|
3 | 3 | import os.path |
4 | 4 | import time |
5 | 5 | from operator import itemgetter |
| 6 | +from typing import TYPE_CHECKING, Unpack |
6 | 7 |
|
7 | 8 | import pytest |
8 | 9 | from botocore.config import Config |
9 | 10 | from botocore.exceptions import ClientError |
10 | 11 | from localstack_snapshot.snapshots.transformer import SortingTransformer |
11 | 12 |
|
12 | 13 | from localstack.aws.api.apigateway import PutMode |
| 14 | +from localstack.aws.connect import ServiceLevelClientFactory |
13 | 15 | from localstack.testing.aws.util import is_aws_cloud |
14 | 16 | from localstack.testing.pytest import markers |
15 | 17 | from localstack.utils.files import load_file |
|
24 | 26 | ) |
25 | 27 | from tests.aws.services.apigateway.conftest import is_next_gen_api |
26 | 28 |
|
| 29 | +if TYPE_CHECKING: |
| 30 | + from mypy_boto3_apigateway.type_defs import CreateVpcLinkRequestTypeDef, VpcLinkResponseTypeDef |
| 31 | + |
| 32 | + |
27 | 33 | LOG = logging.getLogger(__name__) |
28 | 34 |
|
29 | 35 | THIS_DIR = os.path.dirname(os.path.abspath(__file__)) |
@@ -98,6 +104,28 @@ def _factory(*args, **kwargs): |
98 | 104 | delete_rest_api_retry(apigateway_client, rest_api_id) |
99 | 105 |
|
100 | 106 |
|
| 107 | +@pytest.fixture |
| 108 | +def apigw_create_vpc_link(aws_client): |
| 109 | + vpc_links: list[tuple[ServiceLevelClientFactory, str]] = [] |
| 110 | + |
| 111 | + def _create_vpc_link( |
| 112 | + client: ServiceLevelClientFactory | None = None, |
| 113 | + **kwargs: Unpack["CreateVpcLinkRequestTypeDef"], |
| 114 | + ) -> "VpcLinkResponseTypeDef": |
| 115 | + client = client or aws_client |
| 116 | + vpc_link = client.apigateway.create_vpc_link(**kwargs) |
| 117 | + vpc_links.append((client, vpc_link["id"])) |
| 118 | + return vpc_link |
| 119 | + |
| 120 | + yield _create_vpc_link |
| 121 | + |
| 122 | + for _client, vpc_link_id in vpc_links: |
| 123 | + try: |
| 124 | + _client.apigateway.delete_vpc_link(vpcLinkId=vpc_link_id) |
| 125 | + except ClientError as e: |
| 126 | + LOG.error("Error deleting VPC link: %s", e) |
| 127 | + |
| 128 | + |
101 | 129 | class TestApiGatewayApiRestApi: |
102 | 130 | @markers.aws.validated |
103 | 131 | def test_list_and_delete_apis(self, apigw_create_rest_api, snapshot, aws_client): |
@@ -2652,6 +2680,135 @@ def test_update_gateway_response( |
2652 | 2680 | ) |
2653 | 2681 |
|
2654 | 2682 |
|
| 2683 | +class TestApiGatewayVpcLink: |
| 2684 | + @markers.aws.validated |
| 2685 | + @markers.snapshot.skip_snapshot_verify(paths=["$.update_vpc_link.tags", "$.get_vpc_link.tags"]) |
| 2686 | + def test_vpc_link_lifecycle( |
| 2687 | + self, |
| 2688 | + aws_client, |
| 2689 | + snapshot, |
| 2690 | + cleanups, |
| 2691 | + default_vpc, |
| 2692 | + region_name, |
| 2693 | + apigw_create_vpc_link, |
| 2694 | + account_id, |
| 2695 | + ): |
| 2696 | + snapshot.add_transformer(snapshot.transform.key_value("nlb-arn")) |
| 2697 | + |
| 2698 | + retries = 240 if is_aws_cloud() else 3 |
| 2699 | + sleep = 3 if is_aws_cloud() else 1 |
| 2700 | + |
| 2701 | + if is_aws_cloud(): |
| 2702 | + vpc_id = default_vpc["VpcId"] |
| 2703 | + subnets = aws_client.ec2.describe_subnets( |
| 2704 | + Filters=[{"Name": "vpc-id", "Values": [vpc_id]}] |
| 2705 | + )["Subnets"] |
| 2706 | + # require at least 2 subnets for the NLB |
| 2707 | + assert len(subnets) >= 2 |
| 2708 | + nlb = aws_client.elbv2.create_load_balancer( |
| 2709 | + Name=f"nlb-{short_uid()}", |
| 2710 | + Type="network", |
| 2711 | + Subnets=[subnets[0]["SubnetId"], subnets[1]["SubnetId"]], |
| 2712 | + )["LoadBalancers"][0] |
| 2713 | + nlb_arn = nlb["LoadBalancerArn"] |
| 2714 | + cleanups.append(lambda: aws_client.elbv2.delete_load_balancer(LoadBalancerArn=nlb_arn)) |
| 2715 | + waiter = aws_client.elbv2.get_waiter("load_balancer_available") |
| 2716 | + waiter.wait( |
| 2717 | + LoadBalancerArns=[nlb_arn], WaiterConfig={"Delay": sleep, "MaxAttempts": retries} |
| 2718 | + ) |
| 2719 | + else: |
| 2720 | + # ElbV2 is not available in community, so we just use a dummy arn |
| 2721 | + nlb_arn = f"arn:aws:elasticloadbalancing:{region_name}:{account_id}:loadbalancer/net/my-load-balancer/50dc6c495c0c9188" |
| 2722 | + |
| 2723 | + snapshot.match("nlb-arn", nlb_arn) |
| 2724 | + |
| 2725 | + # create vpc link |
| 2726 | + vpc_link_name = f"test-vpc-link-{short_uid()}" |
| 2727 | + create_vpc_link_response = apigw_create_vpc_link(name=vpc_link_name, targetArns=[nlb_arn]) |
| 2728 | + snapshot.match("create_vpc_link", create_vpc_link_response) |
| 2729 | + vpc_link_id = create_vpc_link_response["id"] |
| 2730 | + |
| 2731 | + # get vpc link |
| 2732 | + # AWS needs some time to make the VPC link available |
| 2733 | + def _wait_for_vpc_link_available(): |
| 2734 | + get_vpc_link_response = aws_client.apigateway.get_vpc_link(vpcLinkId=vpc_link_id) |
| 2735 | + assert get_vpc_link_response["status"] == "AVAILABLE" |
| 2736 | + return get_vpc_link_response |
| 2737 | + |
| 2738 | + vpc_link_response = retry(_wait_for_vpc_link_available, retries=retries, sleep=sleep) |
| 2739 | + snapshot.match("get_vpc_link", vpc_link_response) |
| 2740 | + |
| 2741 | + # get vpc links |
| 2742 | + get_vpc_links_response = aws_client.apigateway.get_vpc_links() |
| 2743 | + # for the snapshot to be stable, we need to filter for the VPC link we created |
| 2744 | + get_vpc_links_response["items"] = [ |
| 2745 | + item for item in get_vpc_links_response["items"] if item["id"] == vpc_link_id |
| 2746 | + ] |
| 2747 | + snapshot.match("get_vpc_links", get_vpc_links_response) |
| 2748 | + |
| 2749 | + # update vpc link |
| 2750 | + patch_operations = [ |
| 2751 | + {"op": "replace", "path": "/name", "value": f"{vpc_link_name}-updated"}, |
| 2752 | + ] |
| 2753 | + update_vpc_link_response = aws_client.apigateway.update_vpc_link( |
| 2754 | + vpcLinkId=vpc_link_id, patchOperations=patch_operations |
| 2755 | + ) |
| 2756 | + snapshot.match("update_vpc_link", update_vpc_link_response) |
| 2757 | + |
| 2758 | + delete_response = aws_client.apigateway.delete_vpc_link(vpcLinkId=vpc_link_id) |
| 2759 | + snapshot.match("delete_vpc_link", delete_response) |
| 2760 | + |
| 2761 | + def _wait_for_deleted(): |
| 2762 | + try: |
| 2763 | + vpc_link = aws_client.apigateway.get_vpc_link(vpcLinkId=vpc_link_id) |
| 2764 | + # this assertion shouldn't happen, but this will ensure failure if the vpc link is still being deleted |
| 2765 | + assert vpc_link["status"] == "DELETED" |
| 2766 | + except aws_client.apigateway.exceptions.NotFoundException: |
| 2767 | + pass |
| 2768 | + |
| 2769 | + # waiting for delete, as it takes a long time and would prevent NLB deletion |
| 2770 | + retry(_wait_for_deleted, retries=retries, sleep=sleep) |
| 2771 | + |
| 2772 | + @markers.aws.validated |
| 2773 | + def test_create_vpc_link_invalid_parameters(self, aws_client, snapshot): |
| 2774 | + with pytest.raises(ClientError) as e: |
| 2775 | + aws_client.apigateway.create_vpc_link( |
| 2776 | + name=f"test-vpc-link-{short_uid()}", |
| 2777 | + targetArns=["invalid-arn"], |
| 2778 | + ) |
| 2779 | + snapshot.match("create_vpc_link_invalid_target_arn", e.value.response) |
| 2780 | + |
| 2781 | + with pytest.raises(ClientError) as e: |
| 2782 | + aws_client.apigateway.create_vpc_link( |
| 2783 | + name="", |
| 2784 | + targetArns=[], |
| 2785 | + ) |
| 2786 | + snapshot.match("create_vpc_link_empty_name", e.value.response) |
| 2787 | + |
| 2788 | + @markers.aws.validated |
| 2789 | + def test_get_vpc_link_invalid_id(self, aws_client, snapshot): |
| 2790 | + with pytest.raises(ClientError) as e: |
| 2791 | + aws_client.apigateway.get_vpc_link(vpcLinkId="invalid-id") |
| 2792 | + snapshot.match("get_vpc_link_invalid_id", e.value.response) |
| 2793 | + |
| 2794 | + @markers.aws.validated |
| 2795 | + def test_delete_vpc_link_invalid_id(self, aws_client, snapshot): |
| 2796 | + with pytest.raises(ClientError) as e: |
| 2797 | + aws_client.apigateway.delete_vpc_link(vpcLinkId="invalid-id") |
| 2798 | + snapshot.match("delete_vpc_link_invalid_id", e.value.response) |
| 2799 | + |
| 2800 | + @markers.aws.validated |
| 2801 | + def test_update_vpc_link_invalid_id(self, aws_client, snapshot): |
| 2802 | + patch_operations = [ |
| 2803 | + {"op": "replace", "path": "/name", "value": "new-name"}, |
| 2804 | + ] |
| 2805 | + with pytest.raises(ClientError) as e: |
| 2806 | + aws_client.apigateway.update_vpc_link( |
| 2807 | + vpcLinkId="invalid-id", patchOperations=patch_operations |
| 2808 | + ) |
| 2809 | + snapshot.match("update_vpc_link_invalid_id", e.value.response) |
| 2810 | + |
| 2811 | + |
2655 | 2812 | class TestApigatewayTestInvoke: |
2656 | 2813 | @markers.aws.validated |
2657 | 2814 | def test_invoke_test_method( |
|
0 commit comments