mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-20 11:21:49 +00:00
test(e2e): remove single/ tests superseded by daemon-lite ports
TestKeyPairs, TestTagManagement, TestRouteTableValidation, and TestReplaceRouteConvergence now have proven-equivalent ports in tests/integration/ (mutation testing confirmed parity). Delete the e2e originals and their runX helpers, along with keypair_test.go's and tags_test.go's file-local helpers left with no caller. generateImportPubKey is shared by other single/ tests so it moves to helpers_test.go instead of being deleted. Slims the manifest-lint baseline for the removed create-call entries.
This commit is contained in:
@@ -76,7 +76,6 @@ create tests/e2e/single/baseline_test.go CreateVpc #1
|
||||
create tests/e2e/single/baseline_test.go RunInstances #1
|
||||
create tests/e2e/single/datapath_test.go CreateSecurityGroup #1
|
||||
create tests/e2e/single/datapath_test.go RunInstances #1
|
||||
create tests/e2e/single/keypair_test.go ImportKeyPair #1
|
||||
create tests/e2e/single/lifecycle_test.go CreateVolume #1
|
||||
create tests/e2e/single/lifecycle_test.go RunInstances #1
|
||||
create tests/e2e/single/natgw_test.go AllocateAddress #1
|
||||
@@ -98,8 +97,6 @@ create tests/e2e/single/volume_durability_test.go CreateVolume #1
|
||||
create tests/e2e/single/volume_test.go CreateVolume #1
|
||||
create tests/e2e/single/vpc_test.go CreateInternetGateway #1
|
||||
create tests/e2e/single/vpc_test.go CreateRouteTable #1
|
||||
create tests/e2e/single/vpc_test.go CreateRouteTable #2
|
||||
create tests/e2e/single/vpc_test.go CreateRouteTable #3
|
||||
create tests/e2e/single/vpc_test.go CreateSubnet #1
|
||||
create tests/e2e/single/vpc_test.go CreateSubnet #2
|
||||
create tests/e2e/single/vpc_test.go CreateVpc #1
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
package single
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/tests/e2e/harness"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// Package-local bootstrappers wrapping harness.Discover*/Ensure* so each Test*
|
||||
@@ -111,3 +114,15 @@ func needInstance(t *testing.T, fix *Fixture) (inst *ec2.Instance, rootVolumeID
|
||||
require.NotEmpty(t, rootVolumeID, "could not resolve root volume from BlockDeviceMappings")
|
||||
return inst, rootVolumeID
|
||||
}
|
||||
|
||||
// generateImportPubKey returns an OpenSSH-formatted public key (matching the
|
||||
// `ssh-keygen -t rsa` output the bash script feeds into import-key-pair).
|
||||
// 2048-bit to match the bash key length.
|
||||
func generateImportPubKey(t *testing.T) []byte {
|
||||
t.Helper()
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err, "generate RSA key")
|
||||
pub, err := ssh.NewPublicKey(&priv.PublicKey)
|
||||
require.NoError(t, err, "ssh.NewPublicKey")
|
||||
return ssh.MarshalAuthorizedKey(pub)
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
//go:build e2e
|
||||
|
||||
package single
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/tests/e2e/harness"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// runKeyPairs exercises CreateKeyPair / ImportKeyPair / DescribeKeyPairs /
|
||||
// DeleteKeyPair. The primary key pair is materialised via harness.EnsureKeyPair
|
||||
// — it owns the create path's assertions, memoizes for downstream Phase 5+
|
||||
// callers, and registers TestSingleNode-scoped cleanup. test-key-2 is created
|
||||
// via import then deleted within this phase. Maps to run-e2e.sh ~204–231.
|
||||
func runKeyPairs(t *testing.T, fix *Fixture) {
|
||||
harness.Phase(t, "Single — SSH Key Management")
|
||||
|
||||
const key2 = "test-key-2"
|
||||
|
||||
// Clean up leftover import-test key from a prior failed run.
|
||||
_, _ = fix.AWS.EC2.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: aws.String(key2)})
|
||||
|
||||
harness.Step(t, "ensure primary key pair (Phase 5+ prereq)")
|
||||
keyName, pemPath := needKeyPair(t, fix)
|
||||
material, rerr := os.ReadFile(pemPath)
|
||||
require.NoError(t, rerr, "read PEM %s", pemPath)
|
||||
require.NotEmpty(t, material, "EnsureKeyPair PEM empty")
|
||||
require.True(t, strings.HasPrefix(string(material), "-----BEGIN"),
|
||||
"PEM must start with -----BEGIN (got prefix %q)", firstN(string(material), 32))
|
||||
harness.Detail(t, "key", keyName, "pem", pemPath)
|
||||
|
||||
harness.Step(t, "import-key-pair %q (local-generated RSA)", key2)
|
||||
pubMaterial := generateImportPubKey(t)
|
||||
_, err := fix.AWS.EC2.ImportKeyPair(&ec2.ImportKeyPairInput{
|
||||
KeyName: aws.String(key2),
|
||||
PublicKeyMaterial: pubMaterial,
|
||||
})
|
||||
require.NoError(t, err, "import-key-pair %s", key2)
|
||||
harness.Detail(t, "imported", key2)
|
||||
|
||||
harness.Step(t, "describe-key-pairs (both present)")
|
||||
listed := describeKeyNames(t, fix)
|
||||
assert.Contains(t, listed, keyName, "describe-key-pairs missing %s (got %v)", keyName, listed)
|
||||
assert.Contains(t, listed, key2, "describe-key-pairs missing %s (got %v)", key2, listed)
|
||||
|
||||
harness.Step(t, "delete %q", key2)
|
||||
_, err = fix.AWS.EC2.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: aws.String(key2)})
|
||||
require.NoError(t, err, "delete-key-pair %s", key2)
|
||||
|
||||
harness.Step(t, "describe-key-pairs (only %s remains)", keyName)
|
||||
remaining := describeKeyNames(t, fix)
|
||||
assert.Contains(t, remaining, keyName, "describe-key-pairs lost %s after deleting %s", keyName, key2)
|
||||
assert.NotContains(t, remaining, key2, "describe-key-pairs still lists %s after delete", key2)
|
||||
}
|
||||
|
||||
// generateImportPubKey returns an OpenSSH-formatted public key (matching the
|
||||
// `ssh-keygen -t rsa` output the bash script feeds into import-key-pair).
|
||||
// 2048-bit to match the bash key length.
|
||||
func generateImportPubKey(t *testing.T) []byte {
|
||||
t.Helper()
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err, "generate RSA key")
|
||||
pub, err := ssh.NewPublicKey(&priv.PublicKey)
|
||||
require.NoError(t, err, "ssh.NewPublicKey")
|
||||
return ssh.MarshalAuthorizedKey(pub)
|
||||
}
|
||||
|
||||
func describeKeyNames(t *testing.T, fix *Fixture) []string {
|
||||
t.Helper()
|
||||
out, err := fix.AWS.EC2.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{})
|
||||
require.NoError(t, err, "describe-key-pairs")
|
||||
names := make([]string, 0, len(out.KeyPairs))
|
||||
for _, kp := range out.KeyPairs {
|
||||
names = append(names, aws.StringValue(kp.KeyName))
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func firstN(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
return s
|
||||
}
|
||||
return s[:n]
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
//go:build e2e
|
||||
|
||||
package single
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/tests/e2e/harness"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// runTagManagement exercises CreateTags / DescribeTags / DeleteTags
|
||||
// against the Phase 5 instance and its root volume. Maps to run-e2e.sh
|
||||
// ~930–1026. Sub-steps mirror the bash 6a–6j checks; we additionally
|
||||
// assert in 6i that the value-conditional DeleteTags scoped to the
|
||||
// instance does NOT touch the matching tag on the root volume, which the
|
||||
// bash driver only implies via per-resource targeting.
|
||||
func runTagManagement(t *testing.T, fix *Fixture) {
|
||||
harness.Phase(t, "Single — Tag Management")
|
||||
|
||||
inst, rootVolumeID := needInstance(t, fix)
|
||||
instanceID := aws.StringValue(inst.InstanceId)
|
||||
|
||||
// Cleanup runs regardless of which sub-step failed so later phases see
|
||||
// a tag-free instance/volume. DeleteTags with no Value drops the tag
|
||||
// unconditionally — exactly what we want here.
|
||||
t.Cleanup(func() {
|
||||
_, _ = fix.AWS.EC2.DeleteTags(&ec2.DeleteTagsInput{
|
||||
Resources: []*string{aws.String(instanceID), aws.String(rootVolumeID)},
|
||||
Tags: []*ec2.Tag{
|
||||
{Key: aws.String("Name")},
|
||||
{Key: aws.String("Environment")},
|
||||
{Key: aws.String("DeleteMe")},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// 6a: create three tags on the instance.
|
||||
harness.Step(t, "6a create-tags instance=%s (Name, Environment, DeleteMe)", instanceID)
|
||||
_, err := fix.AWS.EC2.CreateTags(&ec2.CreateTagsInput{
|
||||
Resources: []*string{aws.String(instanceID)},
|
||||
Tags: []*ec2.Tag{
|
||||
{Key: aws.String("Name"), Value: aws.String("e2e-test")},
|
||||
{Key: aws.String("Environment"), Value: aws.String("testing")},
|
||||
{Key: aws.String("DeleteMe"), Value: aws.String("please")},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "create-tags instance")
|
||||
|
||||
// 6b: filter by resource-id should return all three.
|
||||
harness.Step(t, "6b describe-tags resource-id=%s want=3", instanceID)
|
||||
tags, err := describeTags(fix, &ec2.Filter{
|
||||
Name: aws.String("resource-id"),
|
||||
Values: []*string{aws.String(instanceID)},
|
||||
})
|
||||
require.NoError(t, err, "describe-tags resource-id")
|
||||
require.Lenf(t, tags, 3, "instance tag count after 6a: %v", tagSummary(tags))
|
||||
|
||||
// 6c: tag the root volume with two tags. Bash uses VOLUME_ID (the now-
|
||||
// deleted Phase 5b volume), which is a bug in the bash driver; the Go
|
||||
// port targets the persistent rootVolumeID so the assertion in 6d
|
||||
// reflects real cross-resource state.
|
||||
harness.Step(t, "6c create-tags volume=%s (Name, Environment)", rootVolumeID)
|
||||
_, err = fix.AWS.EC2.CreateTags(&ec2.CreateTagsInput{
|
||||
Resources: []*string{aws.String(rootVolumeID)},
|
||||
Tags: []*ec2.Tag{
|
||||
{Key: aws.String("Name"), Value: aws.String("e2e-root-vol")},
|
||||
{Key: aws.String("Environment"), Value: aws.String("testing")},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "create-tags volume")
|
||||
|
||||
// 6d: filter by key=Environment should return exactly 2 (one per resource).
|
||||
harness.Step(t, "6d describe-tags key=Environment want=2")
|
||||
tags, err = describeTags(fix, &ec2.Filter{
|
||||
Name: aws.String("key"),
|
||||
Values: []*string{aws.String("Environment")},
|
||||
})
|
||||
require.NoError(t, err, "describe-tags key=Environment")
|
||||
require.Lenf(t, tags, 2, "Environment tag count across resources: %v", tagSummary(tags))
|
||||
|
||||
// 6e: filter by resource-type=instance should return 3 (all instance tags).
|
||||
harness.Step(t, "6e describe-tags resource-type=instance want=3")
|
||||
tags, err = describeTags(fix, &ec2.Filter{
|
||||
Name: aws.String("resource-type"),
|
||||
Values: []*string{aws.String("instance")},
|
||||
})
|
||||
require.NoError(t, err, "describe-tags resource-type=instance")
|
||||
require.Lenf(t, tags, 3, "instance-typed tag count: %v", tagSummary(tags))
|
||||
|
||||
// 6f: overwrite Name on the instance — CreateTags with the same Key
|
||||
// must replace the prior Value.
|
||||
harness.Step(t, "6f overwrite instance Name -> e2e-test-updated")
|
||||
_, err = fix.AWS.EC2.CreateTags(&ec2.CreateTagsInput{
|
||||
Resources: []*string{aws.String(instanceID)},
|
||||
Tags: []*ec2.Tag{
|
||||
{Key: aws.String("Name"), Value: aws.String("e2e-test-updated")},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "create-tags overwrite Name")
|
||||
|
||||
tags, err = describeTags(fix,
|
||||
&ec2.Filter{Name: aws.String("resource-id"), Values: []*string{aws.String(instanceID)}},
|
||||
&ec2.Filter{Name: aws.String("key"), Values: []*string{aws.String("Name")}},
|
||||
)
|
||||
require.NoError(t, err, "describe-tags resource-id + key=Name")
|
||||
require.Lenf(t, tags, 1, "expected exactly one Name tag on instance: %v", tagSummary(tags))
|
||||
assert.Equal(t, "e2e-test-updated", aws.StringValue(tags[0].Value), "Name tag value after overwrite")
|
||||
|
||||
// 6g: delete-tags with no Value drops the tag regardless of stored
|
||||
// value. Instance should be down to {Name, Environment}.
|
||||
harness.Step(t, "6g delete-tags Key=DeleteMe (no value)")
|
||||
_, err = fix.AWS.EC2.DeleteTags(&ec2.DeleteTagsInput{
|
||||
Resources: []*string{aws.String(instanceID)},
|
||||
Tags: []*ec2.Tag{{Key: aws.String("DeleteMe")}},
|
||||
})
|
||||
require.NoError(t, err, "delete-tags DeleteMe")
|
||||
|
||||
tags, err = describeTags(fix, &ec2.Filter{
|
||||
Name: aws.String("resource-id"),
|
||||
Values: []*string{aws.String(instanceID)},
|
||||
})
|
||||
require.NoError(t, err, "describe-tags after unconditional delete")
|
||||
require.Lenf(t, tags, 2, "instance tags after deleting DeleteMe: %v", tagSummary(tags))
|
||||
|
||||
// 6h: delete-tags with a non-matching Value must be a no-op — the
|
||||
// Environment tag on the instance still holds "testing".
|
||||
harness.Step(t, "6h delete-tags Key=Environment,Value=production (no-op)")
|
||||
_, err = fix.AWS.EC2.DeleteTags(&ec2.DeleteTagsInput{
|
||||
Resources: []*string{aws.String(instanceID)},
|
||||
Tags: []*ec2.Tag{{Key: aws.String("Environment"), Value: aws.String("production")}},
|
||||
})
|
||||
require.NoError(t, err, "delete-tags Environment=production")
|
||||
|
||||
tags, err = describeTags(fix,
|
||||
&ec2.Filter{Name: aws.String("resource-id"), Values: []*string{aws.String(instanceID)}},
|
||||
&ec2.Filter{Name: aws.String("key"), Values: []*string{aws.String("Environment")}},
|
||||
)
|
||||
require.NoError(t, err, "describe-tags instance Environment after no-op delete")
|
||||
require.Lenf(t, tags, 1, "Environment tag must survive non-matching delete: %v", tagSummary(tags))
|
||||
assert.Equal(t, "testing", aws.StringValue(tags[0].Value), "Environment value after no-op delete")
|
||||
|
||||
// 6i: delete-tags with the correct Value removes the tag from the
|
||||
// targeted resource only. The volume's Environment tag must survive
|
||||
// because DeleteTags was scoped to the instance.
|
||||
harness.Step(t, "6i delete-tags Key=Environment,Value=testing (instance only)")
|
||||
_, err = fix.AWS.EC2.DeleteTags(&ec2.DeleteTagsInput{
|
||||
Resources: []*string{aws.String(instanceID)},
|
||||
Tags: []*ec2.Tag{{Key: aws.String("Environment"), Value: aws.String("testing")}},
|
||||
})
|
||||
require.NoError(t, err, "delete-tags Environment=testing")
|
||||
|
||||
tags, err = describeTags(fix,
|
||||
&ec2.Filter{Name: aws.String("resource-id"), Values: []*string{aws.String(instanceID)}},
|
||||
&ec2.Filter{Name: aws.String("key"), Values: []*string{aws.String("Environment")}},
|
||||
)
|
||||
require.NoError(t, err, "describe-tags instance Environment after match-delete")
|
||||
assert.Lenf(t, tags, 0, "Environment tag must be gone from instance: %v", tagSummary(tags))
|
||||
|
||||
tags, err = describeTags(fix,
|
||||
&ec2.Filter{Name: aws.String("resource-id"), Values: []*string{aws.String(rootVolumeID)}},
|
||||
&ec2.Filter{Name: aws.String("key"), Values: []*string{aws.String("Environment")}},
|
||||
)
|
||||
require.NoError(t, err, "describe-tags volume Environment after instance-scoped delete")
|
||||
require.Lenf(t, tags, 1, "volume Environment tag must survive instance-scoped delete: %v", tagSummary(tags))
|
||||
assert.Equal(t, rootVolumeID, aws.StringValue(tags[0].ResourceId), "Environment tag still attached to root volume")
|
||||
|
||||
// 6j: only Name should remain on the instance.
|
||||
harness.Step(t, "6j final describe-tags resource-id=%s want=1", instanceID)
|
||||
tags, err = describeTags(fix, &ec2.Filter{
|
||||
Name: aws.String("resource-id"),
|
||||
Values: []*string{aws.String(instanceID)},
|
||||
})
|
||||
require.NoError(t, err, "describe-tags final instance")
|
||||
require.Lenf(t, tags, 1, "final instance tag count: %v", tagSummary(tags))
|
||||
assert.Equal(t, "Name", aws.StringValue(tags[0].Key), "final remaining instance tag key")
|
||||
}
|
||||
|
||||
// describeTags is a thin wrapper that builds a DescribeTagsInput from the
|
||||
// supplied filters — the test reaches for it once per sub-step so the
|
||||
// assertion lines stay readable.
|
||||
func describeTags(fix *Fixture, filters ...*ec2.Filter) ([]*ec2.TagDescription, error) {
|
||||
out, err := fix.AWS.EC2.DescribeTags(&ec2.DescribeTagsInput{Filters: filters})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Tags, nil
|
||||
}
|
||||
|
||||
// tagSummary formats a TagDescription slice as resource=key=value triples
|
||||
// for assertion failure messages — bare counts hide which tag is wrong.
|
||||
func tagSummary(tags []*ec2.TagDescription) []string {
|
||||
out := make([]string, 0, len(tags))
|
||||
for _, td := range tags {
|
||||
out = append(out, aws.StringValue(td.ResourceId)+"="+aws.StringValue(td.Key)+"="+aws.StringValue(td.Value))
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -30,21 +30,11 @@ func TestSerialConsoleAccess(t *testing.T) {
|
||||
runSerialConsoleAccess(t, requireSingleNodeFixture(t))
|
||||
}
|
||||
|
||||
func TestKeyPairs(t *testing.T) {
|
||||
t.Parallel()
|
||||
runKeyPairs(t, requireSingleNodeFixture(t))
|
||||
}
|
||||
|
||||
func TestImage(t *testing.T) {
|
||||
t.Parallel()
|
||||
runImage(t, requireSingleNodeFixture(t))
|
||||
}
|
||||
|
||||
func TestTagManagement(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTagManagement(t, requireSingleNodeFixture(t))
|
||||
}
|
||||
|
||||
func TestAccountScoping(t *testing.T) {
|
||||
t.Parallel()
|
||||
runAccountScoping(t, requireSingleNodeFixture(t))
|
||||
@@ -62,18 +52,6 @@ func TestVPCSubnetE2E(t *testing.T) {
|
||||
runVPCSubnetE2E(t, requireSingleNodeFixture(t))
|
||||
}
|
||||
|
||||
func TestRouteTableValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
runRouteTableValidation(t, requireSingleNodeFixture(t))
|
||||
}
|
||||
|
||||
// TestReplaceRouteConvergence owns its own scratch VPCs/IGWs end to end and
|
||||
// never touches the singleton or default VPC, so it is parallel-safe.
|
||||
func TestReplaceRouteConvergence(t *testing.T) {
|
||||
t.Parallel()
|
||||
runReplaceRouteConvergence(t, requireSingleNodeFixture(t))
|
||||
}
|
||||
|
||||
// --- Sequential: singleton VM lifecycle ---
|
||||
|
||||
// TestClusterStatsCLI exercises the spx cluster CLI; its get-vms baseline is
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
package single
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mulgadc/spinifex/tests/e2e/harness"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -292,472 +290,3 @@ func runVPCSubnetE2E(t *testing.T, fix *Fixture) {
|
||||
t.Logf("WARN: external ping inconclusive — may depend on WAN gateway")
|
||||
}
|
||||
}
|
||||
|
||||
// runRouteTableValidation ports run-e2e.sh Phase 8c (~2626-2745).
|
||||
//
|
||||
// Validates route-table CRUD + invariants against the *default* VPC's main
|
||||
// route table (always runs — no pool-mode gating). Three sub-tests:
|
||||
//
|
||||
// Step1_DefaultMainRouteTable — DescribeRouteTables filter by vpc-id +
|
||||
// association.main asserts the local route is present. The IGW route
|
||||
// is verified only when the default VPC has an attached IGW (some
|
||||
// bootstraps skip it on private-only fixtures).
|
||||
// Step2_CustomRouteTableLifecycle — CreateRouteTable, verify the implicit
|
||||
// local route, AssociateRouteTable to a default subnet, DisassociateRouteTable,
|
||||
// DeleteRoute on a manually-added route, DeleteRouteTable.
|
||||
// Step3_ErrorPaths — DeleteRoute on the local route + DeleteRouteTable on
|
||||
// the main route table must both fail.
|
||||
//
|
||||
// Each step registers its own cleanup so a partial run still releases the
|
||||
// scratch route table / association regardless of which assertion fired.
|
||||
func runRouteTableValidation(t *testing.T, fix *Fixture) {
|
||||
harness.Phase(t, "Single — Route Table Validation")
|
||||
|
||||
c := fix.AWS
|
||||
|
||||
// Default VPC + subnet + SG are owned by the harness Ensure* memo, so
|
||||
// repeated calls across phases (and post-migration: across sibling
|
||||
// tests) hit the same cached IDs without re-discovering.
|
||||
def := harness.EnsureDefaultVPC(t, fix.Harness)
|
||||
require.NotEmpty(t, def.VPCID, "default VPC ID required")
|
||||
require.NotEmpty(t, def.SubnetID, "default subnet ID required")
|
||||
harness.Detail(t, "vpc", def.VPCID, "subnet", def.SubnetID)
|
||||
|
||||
// --- Step 1: Default VPC main route table -----------------------------
|
||||
var mainRTBID string
|
||||
t.Run("Step1_DefaultMainRouteTable", func(t *testing.T) {
|
||||
harness.Step(t, "describe-route-tables filter=vpc-id,association.main")
|
||||
out, err := c.EC2.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
|
||||
Filters: []*ec2.Filter{
|
||||
{Name: aws.String("vpc-id"), Values: []*string{aws.String(def.VPCID)}},
|
||||
{Name: aws.String("association.main"), Values: []*string{aws.String("true")}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "describe-route-tables (main)")
|
||||
require.NotEmptyf(t, out.RouteTables,
|
||||
"no main route table found for default VPC %s", def.VPCID)
|
||||
mainRTBID = aws.StringValue(out.RouteTables[0].RouteTableId)
|
||||
require.NotEmpty(t, mainRTBID, "main RouteTableId empty")
|
||||
harness.Detail(t, "main_rtb", mainRTBID)
|
||||
|
||||
// GetRouteTableAssociations equivalent: confirm at least one
|
||||
// association of the main RT is flagged Main=true. The aws-sdk-go
|
||||
// surface folds this into RouteTable.Associations[].Main, so a
|
||||
// separate API call isn't needed — but assert it explicitly so a
|
||||
// regression that drops the Main flag would fail here, not silently.
|
||||
mainFlagSeen := false
|
||||
for _, a := range out.RouteTables[0].Associations {
|
||||
if aws.BoolValue(a.Main) {
|
||||
mainFlagSeen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Truef(t, mainFlagSeen,
|
||||
"main route table %s has no Association with Main=true", mainRTBID)
|
||||
|
||||
// Local route is mandatory on every route table — the gateway
|
||||
// auto-creates it at CreateRouteTable / CreateVpc time.
|
||||
localFound := false
|
||||
for _, r := range out.RouteTables[0].Routes {
|
||||
if aws.StringValue(r.GatewayId) == "local" {
|
||||
localFound = true
|
||||
harness.Detail(t, "local_route", aws.StringValue(r.DestinationCidrBlock))
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Truef(t, localFound,
|
||||
"main route table %s missing local route", mainRTBID)
|
||||
|
||||
// IGW route (0.0.0.0/0 -> igw-...) — soft check. Default VPCs that
|
||||
// admin init wires with an IGW must have it; private-only bootstraps
|
||||
// legitimately don't, so just log when missing rather than fail.
|
||||
igwRoute := ""
|
||||
for _, r := range out.RouteTables[0].Routes {
|
||||
if aws.StringValue(r.DestinationCidrBlock) == "0.0.0.0/0" {
|
||||
igwRoute = aws.StringValue(r.GatewayId)
|
||||
break
|
||||
}
|
||||
}
|
||||
if igwRoute != "" {
|
||||
harness.Detail(t, "default_route", "0.0.0.0/0->"+igwRoute)
|
||||
} else {
|
||||
t.Logf("note: default VPC main RT has no 0.0.0.0/0 route (no IGW attached)")
|
||||
}
|
||||
})
|
||||
|
||||
// Bail if Step 1 didn't seat mainRTBID — Steps 2/3 dereference it.
|
||||
if mainRTBID == "" {
|
||||
t.Fatalf("Step 1 did not resolve the main route table; cannot continue")
|
||||
}
|
||||
|
||||
// --- Step 2: Custom route table CRUD lifecycle ------------------------
|
||||
t.Run("Step2_CustomRouteTableLifecycle", func(t *testing.T) {
|
||||
harness.Step(t, "create-route-table (vpc=%s)", def.VPCID)
|
||||
ctOut, err := c.EC2.CreateRouteTable(&ec2.CreateRouteTableInput{
|
||||
VpcId: aws.String(def.VPCID),
|
||||
})
|
||||
require.NoError(t, err, "create custom route table")
|
||||
require.NotNil(t, ctOut.RouteTable, "create-route-table returned nil RouteTable")
|
||||
customRTB := aws.StringValue(ctOut.RouteTable.RouteTableId)
|
||||
require.NotEmpty(t, customRTB, "custom RouteTableId empty")
|
||||
rtbDeleted := false
|
||||
t.Cleanup(func() {
|
||||
if !rtbDeleted {
|
||||
_, _ = c.EC2.DeleteRouteTable(&ec2.DeleteRouteTableInput{
|
||||
RouteTableId: aws.String(customRTB),
|
||||
})
|
||||
}
|
||||
})
|
||||
harness.Detail(t, "custom_rtb", customRTB)
|
||||
|
||||
// Local route must be present on the freshly-created table.
|
||||
descCustom, err := c.EC2.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
|
||||
RouteTableIds: []*string{aws.String(customRTB)},
|
||||
})
|
||||
require.NoError(t, err, "describe custom route table")
|
||||
require.NotEmpty(t, descCustom.RouteTables, "custom RT not visible after create")
|
||||
customLocal := ""
|
||||
for _, r := range descCustom.RouteTables[0].Routes {
|
||||
if aws.StringValue(r.GatewayId) == "local" {
|
||||
customLocal = aws.StringValue(r.DestinationCidrBlock)
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotEmptyf(t, customLocal,
|
||||
"custom route table %s missing local route", customRTB)
|
||||
harness.Detail(t, "custom_local_route", customLocal)
|
||||
|
||||
// Find an attached IGW for the default VPC so we can add a real
|
||||
// 0.0.0.0/0 route. If none is attached the test still exercises
|
||||
// associate/disassociate + delete on the local route only.
|
||||
igwForVPC := ""
|
||||
igws, err := c.EC2.DescribeInternetGateways(&ec2.DescribeInternetGatewaysInput{
|
||||
Filters: []*ec2.Filter{
|
||||
{Name: aws.String("attachment.vpc-id"), Values: []*string{aws.String(def.VPCID)}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "describe-internet-gateways")
|
||||
if len(igws.InternetGateways) > 0 {
|
||||
igwForVPC = aws.StringValue(igws.InternetGateways[0].InternetGatewayId)
|
||||
harness.Detail(t, "vpc_igw", igwForVPC)
|
||||
}
|
||||
|
||||
routeAdded := false
|
||||
if igwForVPC != "" {
|
||||
harness.Step(t, "create-route 0.0.0.0/0 -> %s", igwForVPC)
|
||||
_, err = c.EC2.CreateRoute(&ec2.CreateRouteInput{
|
||||
RouteTableId: aws.String(customRTB),
|
||||
DestinationCidrBlock: aws.String("0.0.0.0/0"),
|
||||
GatewayId: aws.String(igwForVPC),
|
||||
})
|
||||
require.NoError(t, err, "create-route 0.0.0.0/0 -> IGW (custom RT)")
|
||||
routeAdded = true
|
||||
t.Cleanup(func() {
|
||||
_, _ = c.EC2.DeleteRoute(&ec2.DeleteRouteInput{
|
||||
RouteTableId: aws.String(customRTB),
|
||||
DestinationCidrBlock: aws.String("0.0.0.0/0"),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Associate with the default subnet, then disassociate. The bash
|
||||
// driver uses `default-for-az` to find a subnet; def.SubnetID
|
||||
// is already that subnet (DiscoverDefaultVPC prefers DefaultForAz).
|
||||
harness.Step(t, "associate-route-table %s <- %s", customRTB, def.SubnetID)
|
||||
assocOut, err := c.EC2.AssociateRouteTable(&ec2.AssociateRouteTableInput{
|
||||
RouteTableId: aws.String(customRTB),
|
||||
SubnetId: aws.String(def.SubnetID),
|
||||
})
|
||||
require.NoError(t, err, "associate-route-table custom")
|
||||
assocID := aws.StringValue(assocOut.AssociationId)
|
||||
require.NotEmpty(t, assocID, "custom AssociationId empty")
|
||||
assocReleased := false
|
||||
t.Cleanup(func() {
|
||||
if !assocReleased {
|
||||
_, _ = c.EC2.DisassociateRouteTable(&ec2.DisassociateRouteTableInput{
|
||||
AssociationId: aws.String(assocID),
|
||||
})
|
||||
}
|
||||
})
|
||||
harness.Detail(t, "custom_assoc", assocID)
|
||||
|
||||
harness.Step(t, "disassociate-route-table %s", assocID)
|
||||
_, err = c.EC2.DisassociateRouteTable(&ec2.DisassociateRouteTableInput{
|
||||
AssociationId: aws.String(assocID),
|
||||
})
|
||||
require.NoError(t, err, "disassociate-route-table")
|
||||
assocReleased = true
|
||||
|
||||
if routeAdded {
|
||||
harness.Step(t, "delete-route 0.0.0.0/0")
|
||||
_, err = c.EC2.DeleteRoute(&ec2.DeleteRouteInput{
|
||||
RouteTableId: aws.String(customRTB),
|
||||
DestinationCidrBlock: aws.String("0.0.0.0/0"),
|
||||
})
|
||||
require.NoError(t, err, "delete-route 0.0.0.0/0")
|
||||
}
|
||||
|
||||
harness.Step(t, "delete-route-table %s", customRTB)
|
||||
_, err = c.EC2.DeleteRouteTable(&ec2.DeleteRouteTableInput{
|
||||
RouteTableId: aws.String(customRTB),
|
||||
})
|
||||
require.NoError(t, err, "delete-route-table")
|
||||
rtbDeleted = true
|
||||
})
|
||||
|
||||
// --- Step 3: Error paths -----------------------------------------------
|
||||
t.Run("Step3_ErrorPaths", func(t *testing.T) {
|
||||
// Scratch RT for the "cannot delete local route" assertion. The bash
|
||||
// version uses a fresh table so the failure doesn't poison the main
|
||||
// table or the Step 2 lifecycle table.
|
||||
harness.Step(t, "create-route-table (scratch for error paths)")
|
||||
ctOut, err := c.EC2.CreateRouteTable(&ec2.CreateRouteTableInput{
|
||||
VpcId: aws.String(def.VPCID),
|
||||
})
|
||||
require.NoError(t, err, "create scratch RT")
|
||||
require.NotNil(t, ctOut.RouteTable, "create-route-table returned nil RouteTable")
|
||||
errRTB := aws.StringValue(ctOut.RouteTable.RouteTableId)
|
||||
require.NotEmpty(t, errRTB, "scratch RouteTableId empty")
|
||||
t.Cleanup(func() {
|
||||
_, _ = c.EC2.DeleteRouteTable(&ec2.DeleteRouteTableInput{
|
||||
RouteTableId: aws.String(errRTB),
|
||||
})
|
||||
})
|
||||
|
||||
// Discover the scratch table's local CIDR (the VPC's CIDR block).
|
||||
descScratch, err := c.EC2.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
|
||||
RouteTableIds: []*string{aws.String(errRTB)},
|
||||
})
|
||||
require.NoError(t, err, "describe scratch RT")
|
||||
require.NotEmpty(t, descScratch.RouteTables, "scratch RT not visible")
|
||||
scratchLocal := ""
|
||||
for _, r := range descScratch.RouteTables[0].Routes {
|
||||
if aws.StringValue(r.GatewayId) == "local" {
|
||||
scratchLocal = aws.StringValue(r.DestinationCidrBlock)
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotEmptyf(t, scratchLocal,
|
||||
"scratch RT %s missing local route", errRTB)
|
||||
|
||||
// DeleteRoute on the local CIDR must be rejected. Bash just asserts
|
||||
// non-zero exit; we assert *any* error here rather than pin a code,
|
||||
// because the gateway might surface this as InvalidParameterValue,
|
||||
// OperationNotPermitted, or a custom "cannot delete local route" code.
|
||||
// If the gateway settles on a single code in future we can tighten this
|
||||
// to harness.AssertAWSError.
|
||||
harness.Step(t, "delete-route local=%s (must fail)", scratchLocal)
|
||||
_, err = c.EC2.DeleteRoute(&ec2.DeleteRouteInput{
|
||||
RouteTableId: aws.String(errRTB),
|
||||
DestinationCidrBlock: aws.String(scratchLocal),
|
||||
})
|
||||
require.Errorf(t, err,
|
||||
"delete-route on local route %s succeeded — must be rejected", scratchLocal)
|
||||
// Sanity: the rejection should be an AWS-shaped error, not a transport
|
||||
// error. Surface the code in the test log so a future tightening to
|
||||
// AssertAWSError has a known target.
|
||||
var aerr awserr.Error
|
||||
if errors.As(err, &aerr) {
|
||||
harness.Detail(t, "delete_local_err", aerr.Code())
|
||||
}
|
||||
|
||||
// DeleteRouteTable on the main RT must be rejected — same shape.
|
||||
harness.Step(t, "delete-route-table %s (main, must fail)", mainRTBID)
|
||||
_, err = c.EC2.DeleteRouteTable(&ec2.DeleteRouteTableInput{
|
||||
RouteTableId: aws.String(mainRTBID),
|
||||
})
|
||||
require.Errorf(t, err,
|
||||
"delete-route-table on main RT %s succeeded — must be rejected", mainRTBID)
|
||||
if errors.As(err, &aerr) {
|
||||
harness.Detail(t, "delete_main_err", aerr.Code())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// runReplaceRouteConvergence validates ReplaceRoute's control-plane contract:
|
||||
// a route installed against one IGW target re-points to a different IGW, with
|
||||
// the route record converging to the new gateway. It then walks the negative
|
||||
// matrix (no-such-route, local-route, missing-target, unknown-IGW, cross-VPC
|
||||
// IGW) so each rejection path stays pinned to its AWS error code.
|
||||
//
|
||||
// V1 scope note: ReplaceRoute updates the route record but publishes no vpc
|
||||
// event, so it does NOT reprogram the OVN datapath. There is no datapath to
|
||||
// assert here — the contract under test is control-plane convergence only.
|
||||
// When ReplaceRoute begins emitting add/delete-igw-route events, extend this
|
||||
// with a guest-egress assertion mirroring runNATGateway.
|
||||
//
|
||||
// Owns two scratch VPCs + three IGWs end to end, so it never touches the
|
||||
// singleton or default VPC and is safe in the parallel bucket. No pool mode or
|
||||
// OVN needed — every step is pure KV control plane.
|
||||
func runReplaceRouteConvergence(t *testing.T, fix *Fixture) {
|
||||
harness.Phase(t, "Single — ReplaceRoute Convergence (control plane)")
|
||||
|
||||
c := fix.AWS
|
||||
|
||||
// --- Primary VPC + two attached IGWs -----------------------------------
|
||||
vpcID := createScratchVPC(t, c, "10.77.0.0/16")
|
||||
igw1 := createAttachedIGW(t, c, vpcID)
|
||||
igw2 := createAttachedIGW(t, c, vpcID)
|
||||
|
||||
// --- Secondary VPC + its own IGW (cross-VPC rejection target) ----------
|
||||
otherVPCID := createScratchVPC(t, c, "10.88.0.0/16")
|
||||
igwOther := createAttachedIGW(t, c, otherVPCID)
|
||||
|
||||
// --- Custom route table in the primary VPC -----------------------------
|
||||
harness.Step(t, "create-route-table (vpc=%s)", vpcID)
|
||||
// e2e:allow-create — scratch route table owned by this ReplaceRoute test.
|
||||
rtOut, err := c.EC2.CreateRouteTable(&ec2.CreateRouteTableInput{VpcId: aws.String(vpcID)})
|
||||
require.NoError(t, err, "create-route-table")
|
||||
require.NotNil(t, rtOut.RouteTable, "create-route-table returned nil RouteTable")
|
||||
rtbID := aws.StringValue(rtOut.RouteTable.RouteTableId)
|
||||
require.NotEmpty(t, rtbID, "RouteTableId empty")
|
||||
t.Cleanup(func() {
|
||||
_, _ = c.EC2.DeleteRouteTable(&ec2.DeleteRouteTableInput{RouteTableId: aws.String(rtbID)})
|
||||
})
|
||||
|
||||
const dest = "192.0.2.0/24"
|
||||
|
||||
// --- CreateRoute -> igw1 -----------------------------------------------
|
||||
harness.Step(t, "create-route %s -> %s", dest, igw1)
|
||||
_, err = c.EC2.CreateRoute(&ec2.CreateRouteInput{
|
||||
RouteTableId: aws.String(rtbID),
|
||||
DestinationCidrBlock: aws.String(dest),
|
||||
GatewayId: aws.String(igw1),
|
||||
})
|
||||
require.NoError(t, err, "create-route %s -> igw1", dest)
|
||||
require.Equalf(t, igw1, routeGateway(t, c, rtbID, dest),
|
||||
"route %s should target igw1 after create", dest)
|
||||
|
||||
// --- ReplaceRoute -> igw2 (genuine target convergence) -----------------
|
||||
harness.Step(t, "replace-route %s -> %s", dest, igw2)
|
||||
_, err = c.EC2.ReplaceRoute(&ec2.ReplaceRouteInput{
|
||||
RouteTableId: aws.String(rtbID),
|
||||
DestinationCidrBlock: aws.String(dest),
|
||||
GatewayId: aws.String(igw2),
|
||||
})
|
||||
require.NoError(t, err, "replace-route %s -> igw2", dest)
|
||||
require.Equalf(t, igw2, routeGateway(t, c, rtbID, dest),
|
||||
"route %s did not converge to igw2 after replace-route", dest)
|
||||
harness.Detail(t, "convergence", dest+": "+igw1+" -> "+igw2)
|
||||
|
||||
// --- Negative matrix ---------------------------------------------------
|
||||
// Unknown destination CIDR — route doesn't exist in the table.
|
||||
harness.Step(t, "replace-route on absent route (must fail)")
|
||||
harness.ExpectError(t, "InvalidRoute.NotFound", func() error {
|
||||
_, e := c.EC2.ReplaceRoute(&ec2.ReplaceRouteInput{
|
||||
RouteTableId: aws.String(rtbID),
|
||||
DestinationCidrBlock: aws.String("198.51.100.0/24"),
|
||||
GatewayId: aws.String(igw2),
|
||||
})
|
||||
return e
|
||||
})
|
||||
|
||||
// Local route is immutable.
|
||||
harness.Step(t, "replace-route on local route (must fail)")
|
||||
harness.ExpectError(t, "InvalidParameterValue", func() error {
|
||||
_, e := c.EC2.ReplaceRoute(&ec2.ReplaceRouteInput{
|
||||
RouteTableId: aws.String(rtbID),
|
||||
DestinationCidrBlock: aws.String("10.77.0.0/16"),
|
||||
GatewayId: aws.String(igw2),
|
||||
})
|
||||
return e
|
||||
})
|
||||
|
||||
// Missing target — V1 requires a GatewayId.
|
||||
harness.Step(t, "replace-route with no target (must fail)")
|
||||
harness.ExpectError(t, "InvalidParameterValue", func() error {
|
||||
_, e := c.EC2.ReplaceRoute(&ec2.ReplaceRouteInput{
|
||||
RouteTableId: aws.String(rtbID),
|
||||
DestinationCidrBlock: aws.String(dest),
|
||||
})
|
||||
return e
|
||||
})
|
||||
|
||||
// Unknown IGW target.
|
||||
harness.Step(t, "replace-route to non-existent IGW (must fail)")
|
||||
harness.ExpectError(t, "InvalidInternetGatewayID.NotFound", func() error {
|
||||
_, e := c.EC2.ReplaceRoute(&ec2.ReplaceRouteInput{
|
||||
RouteTableId: aws.String(rtbID),
|
||||
DestinationCidrBlock: aws.String(dest),
|
||||
GatewayId: aws.String("igw-00000000000000000"),
|
||||
})
|
||||
return e
|
||||
})
|
||||
|
||||
// IGW attached to a different VPC than the route table.
|
||||
harness.Step(t, "replace-route to cross-VPC IGW %s (must fail)", igwOther)
|
||||
harness.ExpectError(t, "InvalidParameterValue", func() error {
|
||||
_, e := c.EC2.ReplaceRoute(&ec2.ReplaceRouteInput{
|
||||
RouteTableId: aws.String(rtbID),
|
||||
DestinationCidrBlock: aws.String(dest),
|
||||
GatewayId: aws.String(igwOther),
|
||||
})
|
||||
return e
|
||||
})
|
||||
|
||||
// Each rejection left the route untouched at igw2.
|
||||
require.Equalf(t, igw2, routeGateway(t, c, rtbID, dest),
|
||||
"route %s drifted off igw2 after the rejection matrix", dest)
|
||||
}
|
||||
|
||||
// createScratchVPC creates a VPC with the given CIDR and registers its deletion.
|
||||
func createScratchVPC(t *testing.T, c *harness.AWSClient, cidr string) string {
|
||||
t.Helper()
|
||||
harness.Step(t, "create-vpc %s", cidr)
|
||||
// e2e:allow-create — scratch VPC owned end to end by the caller.
|
||||
out, err := c.EC2.CreateVpc(&ec2.CreateVpcInput{CidrBlock: aws.String(cidr)})
|
||||
require.NoError(t, err, "create-vpc %s", cidr)
|
||||
require.NotNil(t, out.Vpc, "create-vpc %s returned nil Vpc", cidr)
|
||||
id := aws.StringValue(out.Vpc.VpcId)
|
||||
require.NotEmpty(t, id, "VpcId empty for %s", cidr)
|
||||
t.Cleanup(func() {
|
||||
_, _ = c.EC2.DeleteVpc(&ec2.DeleteVpcInput{VpcId: aws.String(id)})
|
||||
})
|
||||
return id
|
||||
}
|
||||
|
||||
// createAttachedIGW creates an internet gateway, attaches it to vpcID, and
|
||||
// registers detach+delete cleanup (LIFO-safe: detach runs before the owning
|
||||
// VPC's delete).
|
||||
func createAttachedIGW(t *testing.T, c *harness.AWSClient, vpcID string) string {
|
||||
t.Helper()
|
||||
// e2e:allow-create — scratch IGW owned by the caller's ReplaceRoute test.
|
||||
out, err := c.EC2.CreateInternetGateway(&ec2.CreateInternetGatewayInput{})
|
||||
require.NoError(t, err, "create-internet-gateway")
|
||||
require.NotNil(t, out.InternetGateway, "create-internet-gateway returned nil IGW")
|
||||
igwID := aws.StringValue(out.InternetGateway.InternetGatewayId)
|
||||
require.NotEmpty(t, igwID, "InternetGatewayId empty")
|
||||
_, err = c.EC2.AttachInternetGateway(&ec2.AttachInternetGatewayInput{
|
||||
InternetGatewayId: aws.String(igwID),
|
||||
VpcId: aws.String(vpcID),
|
||||
})
|
||||
require.NoError(t, err, "attach-internet-gateway %s -> %s", igwID, vpcID)
|
||||
t.Cleanup(func() {
|
||||
_, _ = c.EC2.DetachInternetGateway(&ec2.DetachInternetGatewayInput{
|
||||
InternetGatewayId: aws.String(igwID),
|
||||
VpcId: aws.String(vpcID),
|
||||
})
|
||||
_, _ = c.EC2.DeleteInternetGateway(&ec2.DeleteInternetGatewayInput{
|
||||
InternetGatewayId: aws.String(igwID),
|
||||
})
|
||||
})
|
||||
return igwID
|
||||
}
|
||||
|
||||
// routeGateway returns the GatewayId of the route matching destCidr in rtbID,
|
||||
// failing the test if the route is absent.
|
||||
func routeGateway(t *testing.T, c *harness.AWSClient, rtbID, destCidr string) string {
|
||||
t.Helper()
|
||||
out, err := c.EC2.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
|
||||
RouteTableIds: []*string{aws.String(rtbID)},
|
||||
})
|
||||
require.NoError(t, err, "describe-route-tables %s", rtbID)
|
||||
require.NotEmpty(t, out.RouteTables, "route table %s not found", rtbID)
|
||||
for _, r := range out.RouteTables[0].Routes {
|
||||
if aws.StringValue(r.DestinationCidrBlock) == destCidr {
|
||||
return aws.StringValue(r.GatewayId)
|
||||
}
|
||||
}
|
||||
t.Fatalf("route %s not found in table %s", destCidr, rtbID)
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user