#!/usr/bin/env bats # EC2 tests setup() { load 'test_helper/common-setup' PREFIX_LIST_NAME="bats-prefix-list-$(unique_name)" PREFIX_LIST_ID="" SG_VPC_ID="" SG_SOURCE_ID="" SG_TARGET_ID="" TRANSIT_GATEWAY_ID="" TGW_ATTACHMENT_ID="" TGW_VPC_ID="" TGW_ROUTE_TABLE_ID="" MONITOR_INSTANCE_ID="" } teardown() { if [ -n "$MONITOR_INSTANCE_ID" ]; then aws_cmd ec2 terminate-instances --instance-ids "$MONITOR_INSTANCE_ID" >/dev/null 2>&1 || true fi if [ -n "$PREFIX_LIST_ID" ]; then aws_cmd ec2 delete-managed-prefix-list --prefix-list-id "$PREFIX_LIST_ID" >/dev/null 2>&1 || true fi if [ -n "$TGW_ROUTE_TABLE_ID" ]; then aws_cmd ec2 delete-transit-gateway-route-table \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" >/dev/null 2>&1 || true fi if [ -n "$TGW_ATTACHMENT_ID" ]; then aws_cmd ec2 delete-transit-gateway-vpc-attachment \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" >/dev/null 2>&1 || true fi if [ -n "$TRANSIT_GATEWAY_ID" ]; then aws_cmd ec2 delete-transit-gateway --transit-gateway-id "$TRANSIT_GATEWAY_ID" >/dev/null 2>&1 || true fi if [ -n "$TGW_VPC_ID" ]; then aws_cmd ec2 delete-vpc --vpc-id "$TGW_VPC_ID" >/dev/null 2>&1 || true fi for sg in "$SG_TARGET_ID" "$SG_SOURCE_ID"; do if [ -n "$sg" ]; then aws_cmd ec2 delete-security-group --group-id "$sg" >/dev/null 2>&1 || true fi done if [ -n "$SG_VPC_ID" ]; then aws_cmd ec2 delete-vpc --vpc-id "$SG_VPC_ID" >/dev/null 2>&1 || true fi } # Creates a prefix list holding 10.0.0.0/8 and sets PREFIX_LIST_ID. create_prefix_list() { local out out=$(aws_cmd ec2 create-managed-prefix-list \ --prefix-list-name "$PREFIX_LIST_NAME" \ --address-family IPv4 \ --max-entries 5 \ --entries 'Cidr=10.0.0.0/8,Description=corporate') PREFIX_LIST_ID=$(json_get "$out" '.PrefixList.PrefixListId') } @test "EC2: create managed prefix list" { run aws_cmd ec2 create-managed-prefix-list \ --prefix-list-name "$PREFIX_LIST_NAME" \ --address-family IPv4 \ --max-entries 5 \ --entries 'Cidr=10.0.0.0/8,Description=corporate' assert_success PREFIX_LIST_ID=$(json_get "$output" '.PrefixList.PrefixListId') [ -n "$PREFIX_LIST_ID" ] state=$(json_get "$output" '.PrefixList.State') [ "$state" = "create-complete" ] version=$(json_get "$output" '.PrefixList.Version') [ "$version" = "1" ] } @test "EC2: describe managed prefix list by id" { create_prefix_list run aws_cmd ec2 describe-managed-prefix-lists --prefix-list-ids "$PREFIX_LIST_ID" assert_success name=$(json_get "$output" '.PrefixLists[0].PrefixListName') [ "$name" = "$PREFIX_LIST_NAME" ] } @test "EC2: describe managed prefix lists exposes the AWS-managed S3 list" { run aws_cmd ec2 describe-managed-prefix-lists \ --filters "Name=prefix-list-name,Values=com.amazonaws.${AWS_DEFAULT_REGION}.s3" assert_success id=$(json_get "$output" '.PrefixLists[0].PrefixListId') [ "$id" = "pl-63a5400a" ] owner=$(json_get "$output" '.PrefixLists[0].OwnerId') [ "$owner" = "AWS" ] } @test "EC2: get managed prefix list entries" { create_prefix_list run aws_cmd ec2 get-managed-prefix-list-entries --prefix-list-id "$PREFIX_LIST_ID" assert_success cidr=$(json_get "$output" '.Entries[0].Cidr') [ "$cidr" = "10.0.0.0/8" ] desc=$(json_get "$output" '.Entries[0].Description') [ "$desc" = "corporate" ] } @test "EC2: modify managed prefix list bumps version and keeps history" { create_prefix_list run aws_cmd ec2 modify-managed-prefix-list \ --prefix-list-id "$PREFIX_LIST_ID" \ --add-entries 'Cidr=192.168.0.0/16,Description=lab' assert_success version=$(json_get "$output" '.PrefixList.Version') [ "$version" = "2" ] run aws_cmd ec2 get-managed-prefix-list-entries --prefix-list-id "$PREFIX_LIST_ID" assert_success count=$(json_get "$output" '.Entries | length') [ "$count" = "2" ] run aws_cmd ec2 get-managed-prefix-list-entries --prefix-list-id "$PREFIX_LIST_ID" --target-version 1 assert_success count=$(json_get "$output" '.Entries | length') [ "$count" = "1" ] } @test "EC2: modify with a stale current version is rejected" { create_prefix_list aws_cmd ec2 modify-managed-prefix-list --prefix-list-id "$PREFIX_LIST_ID" \ --add-entries 'Cidr=192.168.0.0/16' >/dev/null run aws_cmd ec2 modify-managed-prefix-list \ --prefix-list-id "$PREFIX_LIST_ID" \ --current-version 1 \ --add-entries 'Cidr=172.16.0.0/12' assert_failure assert_output --partial "PrefixListVersionMismatch" } @test "EC2: AWS-managed prefix list cannot be modified" { run aws_cmd ec2 modify-managed-prefix-list \ --prefix-list-id pl-63a5400a \ --add-entries 'Cidr=10.1.0.0/16' assert_failure assert_output --partial "UnsupportedOperation" } @test "EC2: delete managed prefix list" { create_prefix_list local created_id="$PREFIX_LIST_ID" run aws_cmd ec2 delete-managed-prefix-list --prefix-list-id "$created_id" assert_success state=$(json_get "$output" '.PrefixList.State') [ "$state" = "delete-complete" ] PREFIX_LIST_ID="" run aws_cmd ec2 describe-managed-prefix-lists --prefix-list-ids "$created_id" assert_failure assert_output --partial "InvalidPrefixListID.NotFound" } @test "EC2: legacy describe-prefix-lists still serves the gateway lists" { run aws_cmd ec2 describe-prefix-lists \ --filters "Name=prefix-list-name,Values=com.amazonaws.${AWS_DEFAULT_REGION}.s3" assert_success id=$(json_get "$output" '.PrefixLists[0].PrefixListId') [ "$id" = "pl-63a5400a" ] } # ─── security group rules sourced from a prefix list ──────────────────────── @test "EC2: authorize a security group rule from a prefix list" { create_prefix_list local sg sg=$(aws_cmd ec2 create-security-group --group-name "$(unique_name bats-sg)" \ --description "prefix list source" --query 'GroupId' --output text) run aws_cmd ec2 authorize-security-group-ingress --group-id "$sg" \ --ip-permissions "IpProtocol=tcp,FromPort=5432,ToPort=5432,PrefixListIds=[{PrefixListId=$PREFIX_LIST_ID,Description=from-corp}]" assert_success pl=$(json_get "$output" '.SecurityGroupRules[0].PrefixListId') [ "$pl" = "$PREFIX_LIST_ID" ] run aws_cmd ec2 describe-security-groups --group-ids "$sg" assert_success nested=$(json_get "$output" '.SecurityGroups[0].IpPermissions[0].PrefixListIds[0].PrefixListId') [ "$nested" = "$PREFIX_LIST_ID" ] run aws_cmd ec2 describe-security-group-rules --filters "Name=group-id,Values=$sg" assert_success found=$(echo "$output" | jq --arg pl "$PREFIX_LIST_ID" '.SecurityGroupRules | any(.PrefixListId == $pl)') [ "$found" = "true" ] aws_cmd ec2 delete-security-group --group-id "$sg" >/dev/null 2>&1 || true } @test "EC2: authorizing from an unknown prefix list is rejected" { local sg sg=$(aws_cmd ec2 create-security-group --group-name "$(unique_name bats-sg)" \ --description "unknown prefix list" --query 'GroupId' --output text) run aws_cmd ec2 authorize-security-group-ingress --group-id "$sg" \ --ip-permissions 'IpProtocol=tcp,FromPort=1,ToPort=1,PrefixListIds=[{PrefixListId=pl-doesnotexist}]' assert_failure assert_output --partial "InvalidPrefixListID.NotFound" aws_cmd ec2 delete-security-group --group-id "$sg" >/dev/null 2>&1 || true } # Creates a VPC holding a source and a target security group, and sets SG_VPC_ID, # SG_SOURCE_ID and SG_TARGET_ID. create_sg_pair() { local out out=$(aws_cmd ec2 create-vpc --cidr-block 10.0.0.0/16) SG_VPC_ID=$(json_get "$out" '.Vpc.VpcId') out=$(aws_cmd ec2 create-security-group \ --group-name "$(unique_name bats-sg-source)" \ --description "traffic source" --vpc-id "$SG_VPC_ID") SG_SOURCE_ID=$(json_get "$out" '.GroupId') out=$(aws_cmd ec2 create-security-group \ --group-name "$(unique_name bats-sg-target)" \ --description "traffic target" --vpc-id "$SG_VPC_ID") SG_TARGET_ID=$(json_get "$out" '.GroupId') } @test "EC2: ingress rule keeps its source security group" { create_sg_pair # The CLI serializes UserIdGroupPairs under the wire name "Groups", so this exercises the # form the reporter of #2190 actually sent. run aws_cmd ec2 authorize-security-group-ingress --group-id "$SG_TARGET_ID" \ --ip-permissions "IpProtocol=tcp,FromPort=443,ToPort=443,UserIdGroupPairs=[{GroupId=$SG_SOURCE_ID,Description=from-source-sg}]" assert_success ref=$(json_get "$output" '.SecurityGroupRules[0].ReferencedGroupInfo.GroupId') [ "$ref" = "$SG_SOURCE_ID" ] # Control: the CIDR form of the same rule. aws_cmd ec2 authorize-security-group-ingress --group-id "$SG_TARGET_ID" \ --ip-permissions 'IpProtocol=tcp,FromPort=8443,ToPort=8443,IpRanges=[{CidrIp=10.0.0.0/8,Description=control}]' >/dev/null run aws_cmd ec2 describe-security-groups --group-ids "$SG_TARGET_ID" assert_success gid=$(json_get "$output" '.SecurityGroups[0].IpPermissions[] | select(.FromPort==443) | .UserIdGroupPairs[0].GroupId') [ "$gid" = "$SG_SOURCE_ID" ] desc=$(json_get "$output" '.SecurityGroups[0].IpPermissions[] | select(.FromPort==443) | .UserIdGroupPairs[0].Description') [ "$desc" = "from-source-sg" ] cidr=$(json_get "$output" '.SecurityGroups[0].IpPermissions[] | select(.FromPort==8443) | .IpRanges[0].CidrIp') [ "$cidr" = "10.0.0.0/8" ] run aws_cmd ec2 describe-security-group-rules --filters "Name=group-id,Values=$SG_TARGET_ID" assert_success ref=$(json_get "$output" '.SecurityGroupRules[] | select(.FromPort==443) | .ReferencedGroupInfo.GroupId') [ "$ref" = "$SG_SOURCE_ID" ] desc=$(json_get "$output" '.SecurityGroupRules[] | select(.FromPort==443) | .Description') [ "$desc" = "from-source-sg" ] cidr=$(json_get "$output" '.SecurityGroupRules[] | select(.FromPort==8443) | .CidrIpv4') [ "$cidr" = "10.0.0.0/8" ] } # ─── transit gateways ─────────────────────────────────────────────────────── @test "EC2: create a transit gateway and read it back" { run aws_cmd ec2 create-transit-gateway \ --description "bats hub" \ --tag-specifications 'ResourceType=transit-gateway,Tags=[{Key=Name,Value=bats-tgw}]' assert_success TRANSIT_GATEWAY_ID=$(json_get "$output" '.TransitGateway.TransitGatewayId') [ "$(json_get "$output" '.TransitGateway.State')" = "available" ] [ "$(json_get "$output" '.TransitGateway.Options.AmazonSideAsn')" = "64512" ] [ "$(json_get "$output" '.TransitGateway.Options.SecurityGroupReferencingSupport')" = "disable" ] # The default route table is minted with the gateway, so both ids are already set and equal. assoc=$(json_get "$output" '.TransitGateway.Options.AssociationDefaultRouteTableId') prop=$(json_get "$output" '.TransitGateway.Options.PropagationDefaultRouteTableId') [ "$assoc" = "$prop" ] case "$assoc" in tgw-rtb-*) ;; *) return 1 ;; esac run aws_cmd ec2 describe-transit-gateways --transit-gateway-ids "$TRANSIT_GATEWAY_ID" assert_success [ "$(json_get "$output" '.TransitGateways[0].TransitGatewayId')" = "$TRANSIT_GATEWAY_ID" ] [ "$(json_get "$output" '.TransitGateways[0].Tags[0].Value')" = "bats-tgw" ] } @test "EC2: create a transit gateway with CIDR blocks" { run aws_cmd ec2 create-transit-gateway \ --options 'TransitGatewayCidrBlocks=[10.99.0.0/16]' assert_success TRANSIT_GATEWAY_ID=$(json_get "$output" '.TransitGateway.TransitGatewayId') [ "$(json_get "$output" '.TransitGateway.Options.TransitGatewayCidrBlocks[0]')" = "10.99.0.0/16" ] run aws_cmd ec2 describe-transit-gateways --transit-gateway-ids "$TRANSIT_GATEWAY_ID" assert_success [ "$(json_get "$output" '.TransitGateways[0].Options.TransitGatewayCidrBlocks[0]')" = "10.99.0.0/16" ] } # Tags are changed after creation with create-tags rather than a tag specification, and read back # from describe-transit-gateways, which is how a Terraform tag update converges. @test "EC2: transit gateway tags changed after creation are visible on describe" { local out out=$(aws_cmd ec2 create-transit-gateway --tag-specifications \ 'ResourceType=transit-gateway,Tags=[{Key=Name,Value=bats-tgw-tags}]') TRANSIT_GATEWAY_ID=$(json_get "$out" '.TransitGateway.TransitGatewayId') aws_cmd ec2 create-tags --resources "$TRANSIT_GATEWAY_ID" --tags 'Key=env,Value=prod' >/dev/null run aws_cmd ec2 describe-transit-gateways --transit-gateway-ids "$TRANSIT_GATEWAY_ID" assert_success count=$(json_get "$output" '.TransitGateways[0].Tags | length') [ "$count" = "2" ] env_value=$(json_get "$output" '.TransitGateways[0].Tags[] | select(.Key=="env") | .Value') [ "$env_value" = "prod" ] aws_cmd ec2 delete-tags --resources "$TRANSIT_GATEWAY_ID" --tags 'Key=env' >/dev/null run aws_cmd ec2 describe-transit-gateways --transit-gateway-ids "$TRANSIT_GATEWAY_ID" assert_success count=$(json_get "$output" '.TransitGateways[0].Tags | length') [ "$count" = "1" ] } @test "EC2: modify a transit gateway" { local out out=$(aws_cmd ec2 create-transit-gateway --description "before") TRANSIT_GATEWAY_ID=$(json_get "$out" '.TransitGateway.TransitGatewayId') run aws_cmd ec2 modify-transit-gateway \ --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --description "after" \ --options 'DnsSupport=disable,AddTransitGatewayCidrBlocks=[10.100.0.0/16]' assert_success [ "$(json_get "$output" '.TransitGateway.Description')" = "after" ] [ "$(json_get "$output" '.TransitGateway.Options.DnsSupport')" = "disable" ] [ "$(json_get "$output" '.TransitGateway.Options.TransitGatewayCidrBlocks[0]')" = "10.100.0.0/16" ] # Describe serializes the same list, so read it back through the CLI's own parser too. run aws_cmd ec2 describe-transit-gateways --transit-gateway-ids "$TRANSIT_GATEWAY_ID" assert_success [ "$(json_get "$output" '.TransitGateways[0].Options.TransitGatewayCidrBlocks[0]')" = "10.100.0.0/16" ] } @test "EC2: delete a transit gateway" { local out tgw out=$(aws_cmd ec2 create-transit-gateway --description "short lived") tgw=$(json_get "$out" '.TransitGateway.TransitGatewayId') run aws_cmd ec2 delete-transit-gateway --transit-gateway-id "$tgw" assert_success run aws_cmd ec2 describe-transit-gateways --transit-gateway-ids "$tgw" assert_failure assert_output --partial "InvalidTransitGatewayID.NotFound" } @test "EC2: describing an unknown transit gateway is rejected" { run aws_cmd ec2 describe-transit-gateways --transit-gateway-ids tgw-0123456789abcdef0 assert_failure assert_output --partial "InvalidTransitGatewayID.NotFound" } # ─── transit gateway VPC attachments ──────────────────────────────────────── # Creates a gateway, a VPC and one subnet, setting TRANSIT_GATEWAY_ID, TGW_VPC_ID and TGW_SUBNET_ID. create_attachment_fixture() { local out out=$(aws_cmd ec2 create-transit-gateway --description "bats attachment host") TRANSIT_GATEWAY_ID=$(json_get "$out" '.TransitGateway.TransitGatewayId') out=$(aws_cmd ec2 create-vpc --cidr-block 10.80.0.0/16) TGW_VPC_ID=$(json_get "$out" '.Vpc.VpcId') out=$(aws_cmd ec2 create-subnet --vpc-id "$TGW_VPC_ID" --cidr-block 10.80.1.0/24 \ --availability-zone "${AWS_DEFAULT_REGION}a") TGW_SUBNET_ID=$(json_get "$out" '.Subnet.SubnetId') } @test "EC2: attach a VPC to a transit gateway and read it back both ways" { create_attachment_fixture run aws_cmd ec2 create-transit-gateway-vpc-attachment \ --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --vpc-id "$TGW_VPC_ID" \ --subnet-ids "$TGW_SUBNET_ID" \ --tag-specifications 'ResourceType=transit-gateway-attachment,Tags=[{Key=Name,Value=bats-attach}]' assert_success TGW_ATTACHMENT_ID=$(json_get "$output" '.TransitGatewayVpcAttachment.TransitGatewayAttachmentId') [ "$(json_get "$output" '.TransitGatewayVpcAttachment.State')" = "available" ] [ "$(json_get "$output" '.TransitGatewayVpcAttachment.SubnetIds[0]')" = "$TGW_SUBNET_ID" ] # The attachment's own default, which differs from the gateway's. [ "$(json_get "$output" '.TransitGatewayVpcAttachment.Options.SecurityGroupReferencingSupport')" = "enable" ] run aws_cmd ec2 describe-transit-gateway-vpc-attachments \ --transit-gateway-attachment-ids "$TGW_ATTACHMENT_ID" assert_success [ "$(json_get "$output" '.TransitGatewayVpcAttachments[0].VpcId')" = "$TGW_VPC_ID" ] [ "$(json_get "$output" '.TransitGatewayVpcAttachments[0].Tags[0].Value')" = "bats-attach" ] # The resource-agnostic form is a different shape: typed resource plus the association. run aws_cmd ec2 describe-transit-gateway-attachments \ --transit-gateway-attachment-ids "$TGW_ATTACHMENT_ID" assert_success [ "$(json_get "$output" '.TransitGatewayAttachments[0].ResourceType')" = "vpc" ] [ "$(json_get "$output" '.TransitGatewayAttachments[0].ResourceId')" = "$TGW_VPC_ID" ] [ "$(json_get "$output" '.TransitGatewayAttachments[0].Association.State')" = "associated" ] rtb=$(json_get "$output" '.TransitGatewayAttachments[0].Association.TransitGatewayRouteTableId') case "$rtb" in tgw-rtb-*) ;; *) return 1 ;; esac } @test "EC2: modify a transit gateway VPC attachment" { create_attachment_fixture local out subnet_b out=$(aws_cmd ec2 create-transit-gateway-vpc-attachment --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --vpc-id "$TGW_VPC_ID" --subnet-ids "$TGW_SUBNET_ID") TGW_ATTACHMENT_ID=$(json_get "$out" '.TransitGatewayVpcAttachment.TransitGatewayAttachmentId') out=$(aws_cmd ec2 create-subnet --vpc-id "$TGW_VPC_ID" --cidr-block 10.80.2.0/24 \ --availability-zone "${AWS_DEFAULT_REGION}b") subnet_b=$(json_get "$out" '.Subnet.SubnetId') run aws_cmd ec2 modify-transit-gateway-vpc-attachment \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" \ --add-subnet-ids "$subnet_b" \ --options 'DnsSupport=disable' assert_success [ "$(json_get "$output" '.TransitGatewayVpcAttachment.Options.DnsSupport')" = "disable" ] count=$(json_get "$output" '.TransitGatewayVpcAttachment.SubnetIds | length') [ "$count" = "2" ] run aws_cmd ec2 modify-transit-gateway-vpc-attachment \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" \ --remove-subnet-ids "$TGW_SUBNET_ID" "$subnet_b" assert_failure assert_output --partial "InsufficientSubnetsException" } @test "EC2: a transit gateway with an attachment cannot be deleted" { create_attachment_fixture local out out=$(aws_cmd ec2 create-transit-gateway-vpc-attachment --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --vpc-id "$TGW_VPC_ID" --subnet-ids "$TGW_SUBNET_ID") TGW_ATTACHMENT_ID=$(json_get "$out" '.TransitGatewayVpcAttachment.TransitGatewayAttachmentId') run aws_cmd ec2 delete-transit-gateway --transit-gateway-id "$TRANSIT_GATEWAY_ID" assert_failure assert_output --partial "IncorrectState" run aws_cmd ec2 delete-transit-gateway-vpc-attachment \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_success TGW_ATTACHMENT_ID="" run aws_cmd ec2 delete-transit-gateway --transit-gateway-id "$TRANSIT_GATEWAY_ID" assert_success TRANSIT_GATEWAY_ID="" } # ─── transit gateway route tables, associations, propagations and routes ──── @test "EC2: route table associations move between tables" { create_attachment_fixture local out out=$(aws_cmd ec2 create-transit-gateway-vpc-attachment --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --vpc-id "$TGW_VPC_ID" --subnet-ids "$TGW_SUBNET_ID") TGW_ATTACHMENT_ID=$(json_get "$out" '.TransitGatewayVpcAttachment.TransitGatewayAttachmentId') out=$(aws_cmd ec2 describe-transit-gateways --transit-gateway-ids "$TRANSIT_GATEWAY_ID") local default_rtb default_rtb=$(json_get "$out" '.TransitGateways[0].Options.AssociationDefaultRouteTableId') run aws_cmd ec2 create-transit-gateway-route-table --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --tag-specifications 'ResourceType=transit-gateway-route-table,Tags=[{Key=Name,Value=bats-rtb}]' assert_success TGW_ROUTE_TABLE_ID=$(json_get "$output" '.TransitGatewayRouteTable.TransitGatewayRouteTableId') [ "$(json_get "$output" '.TransitGatewayRouteTable.State')" = "available" ] [ "$(json_get "$output" '.TransitGatewayRouteTable.DefaultAssociationRouteTable')" = "false" ] # The attachment starts on the gateway's default table, so a second association is refused. run aws_cmd ec2 associate-transit-gateway-route-table \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_failure assert_output --partial "Resource.AlreadyAssociated" run aws_cmd ec2 disassociate-transit-gateway-route-table \ --transit-gateway-route-table-id "$default_rtb" \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_success [ "$(json_get "$output" '.Association.State')" = "disassociating" ] run aws_cmd ec2 associate-transit-gateway-route-table \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_success [ "$(json_get "$output" '.Association.State')" = "associating" ] run aws_cmd ec2 get-transit-gateway-route-table-associations \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" assert_success [ "$(json_get "$output" '.Associations[0].TransitGatewayAttachmentId')" = "$TGW_ATTACHMENT_ID" ] [ "$(json_get "$output" '.Associations[0].ResourceType')" = "vpc" ] } @test "EC2: propagation produces a route for the attached VPC" { create_attachment_fixture local out out=$(aws_cmd ec2 create-transit-gateway-vpc-attachment --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --vpc-id "$TGW_VPC_ID" --subnet-ids "$TGW_SUBNET_ID") TGW_ATTACHMENT_ID=$(json_get "$out" '.TransitGatewayVpcAttachment.TransitGatewayAttachmentId') out=$(aws_cmd ec2 create-transit-gateway-route-table --transit-gateway-id "$TRANSIT_GATEWAY_ID") TGW_ROUTE_TABLE_ID=$(json_get "$out" '.TransitGatewayRouteTable.TransitGatewayRouteTableId') run aws_cmd ec2 enable-transit-gateway-route-table-propagation \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_success [ "$(json_get "$output" '.Propagation.State')" = "enabled" ] run aws_cmd ec2 get-transit-gateway-route-table-propagations \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" assert_success [ "$(json_get "$output" '.TransitGatewayRouteTablePropagations[0].State')" = "enabled" ] run aws_cmd ec2 search-transit-gateway-routes \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --filters 'Name=type,Values=propagated' assert_success [ "$(json_get "$output" '.Routes[0].DestinationCidrBlock')" = "10.80.0.0/16" ] [ "$(json_get "$output" '.Routes[0].Type')" = "propagated" ] [ "$(json_get "$output" '.Routes[0].State')" = "active" ] run aws_cmd ec2 enable-transit-gateway-route-table-propagation \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_failure assert_output --partial "TransitGatewayRouteTablePropagation.Duplicate" } @test "EC2: static and blackhole transit gateway routes" { create_attachment_fixture local out out=$(aws_cmd ec2 create-transit-gateway-vpc-attachment --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --vpc-id "$TGW_VPC_ID" --subnet-ids "$TGW_SUBNET_ID") TGW_ATTACHMENT_ID=$(json_get "$out" '.TransitGatewayVpcAttachment.TransitGatewayAttachmentId') out=$(aws_cmd ec2 create-transit-gateway-route-table --transit-gateway-id "$TRANSIT_GATEWAY_ID") TGW_ROUTE_TABLE_ID=$(json_get "$out" '.TransitGatewayRouteTable.TransitGatewayRouteTableId') run aws_cmd ec2 create-transit-gateway-route \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --destination-cidr-block 10.60.0.0/16 \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_success [ "$(json_get "$output" '.Route.Type')" = "static" ] [ "$(json_get "$output" '.Route.State')" = "active" ] [ "$(json_get "$output" '.Route.TransitGatewayAttachments[0].TransitGatewayAttachmentId')" = "$TGW_ATTACHMENT_ID" ] run aws_cmd ec2 create-transit-gateway-route \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --destination-cidr-block 10.61.0.0/16 --blackhole assert_success [ "$(json_get "$output" '.Route.State')" = "blackhole" ] # A blackhole carries no attachment, so the key is absent rather than empty. count=$(json_get "$output" '.Route.TransitGatewayAttachments // [] | length') [ "$count" = "0" ] run aws_cmd ec2 search-transit-gateway-routes \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --filters 'Name=type,Values=static' assert_success count=$(json_get "$output" '.Routes | length') [ "$count" = "2" ] run aws_cmd ec2 delete-transit-gateway-route \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --destination-cidr-block 10.61.0.0/16 assert_success [ "$(json_get "$output" '.Route.State')" = "deleted" ] } @test "EC2: replacing a transit gateway route moves its target and upserts" { create_attachment_fixture local out out=$(aws_cmd ec2 create-transit-gateway-vpc-attachment --transit-gateway-id "$TRANSIT_GATEWAY_ID" \ --vpc-id "$TGW_VPC_ID" --subnet-ids "$TGW_SUBNET_ID") TGW_ATTACHMENT_ID=$(json_get "$out" '.TransitGatewayVpcAttachment.TransitGatewayAttachmentId') out=$(aws_cmd ec2 create-transit-gateway-route-table --transit-gateway-id "$TRANSIT_GATEWAY_ID") TGW_ROUTE_TABLE_ID=$(json_get "$out" '.TransitGatewayRouteTable.TransitGatewayRouteTableId') aws_cmd ec2 create-transit-gateway-route --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --destination-cidr-block 10.88.0.0/16 --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" >/dev/null run aws_cmd ec2 replace-transit-gateway-route \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --destination-cidr-block 10.88.0.0/16 --blackhole assert_success [ "$(json_get "$output" '.Route.State')" = "blackhole" ] count=$(json_get "$output" '.Route.TransitGatewayAttachments // [] | length') [ "$count" = "0" ] run aws_cmd ec2 replace-transit-gateway-route \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --destination-cidr-block 10.88.0.0/16 \ --transit-gateway-attachment-id "$TGW_ATTACHMENT_ID" assert_success [ "$(json_get "$output" '.Route.State')" = "active" ] # Replacing a destination the table never held writes it. run aws_cmd ec2 replace-transit-gateway-route \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --destination-cidr-block 10.89.0.0/16 --blackhole assert_success run aws_cmd ec2 search-transit-gateway-routes \ --transit-gateway-route-table-id "$TGW_ROUTE_TABLE_ID" \ --filters 'Name=type,Values=static' assert_success count=$(json_get "$output" '.Routes | length') [ "$count" = "2" ] } # Monitoring goes through the SDK, not just the raw Query wire, because that is the # path aws_instance takes: `monitoring = true` calls MonitorInstances, and the SDK # parses the response rather than reading the XML we happen to emit. AGENTS.md asks # for management APIs to be validated with SDK clients and not only handcrafted HTTP. launch_monitor_instance() { run aws_cmd ec2 run-instances \ --image-id ami-0abcdef1234567890 --instance-type t3.micro \ --count 1 assert_success MONITOR_INSTANCE_ID=$(json_get "$output" '.Instances[0].InstanceId') [ -n "$MONITOR_INSTANCE_ID" ] } @test "EC2: monitor-instances reports enabled and the state survives to describe" { launch_monitor_instance run aws_cmd ec2 monitor-instances --instance-ids "$MONITOR_INSTANCE_ID" assert_success [ "$(json_get "$output" '.InstanceMonitorings[0].InstanceId')" = "$MONITOR_INSTANCE_ID" ] [ "$(json_get "$output" '.InstanceMonitorings[0].Monitoring.State')" = "enabled" ] # Read it back rather than trusting the mutating call's own response: the bug this # guards against was answering "enabled" while every later read still said disabled. run aws_cmd ec2 describe-instances --instance-ids "$MONITOR_INSTANCE_ID" assert_success [ "$(json_get "$output" '.Reservations[0].Instances[0].Monitoring.State')" = "enabled" ] } @test "EC2: unmonitor-instances reports disabled and the state survives to describe" { launch_monitor_instance run aws_cmd ec2 monitor-instances --instance-ids "$MONITOR_INSTANCE_ID" assert_success run aws_cmd ec2 unmonitor-instances --instance-ids "$MONITOR_INSTANCE_ID" assert_success [ "$(json_get "$output" '.InstanceMonitorings[0].Monitoring.State')" = "disabled" ] run aws_cmd ec2 describe-instances --instance-ids "$MONITOR_INSTANCE_ID" assert_success [ "$(json_get "$output" '.Reservations[0].Instances[0].Monitoring.State')" = "disabled" ] } @test "EC2: monitor-instances rejects an instance that does not exist" { run aws_cmd ec2 monitor-instances --instance-ids i-1234567890abcdef0 assert_failure [[ "$output" == *"InvalidInstanceID.NotFound"* ]] }