mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
fix: add and drop network response fields to match AWS, as far as the SDK's shapes allow
This commit is contained in:
@@ -1806,6 +1806,12 @@ func (d *Daemon) startCluster() error {
|
||||
// describe-tags agrees with each resource's own describe from birth and
|
||||
// stops answering for it once it is deleted.
|
||||
d.vpcService.SetCentralTagStore(d.tagsService)
|
||||
// Name interfaces as their instances are named, so the two agree.
|
||||
region, internalDomain := d.config.Region, handlers_dns.ResolveInternalDomain(d.config)
|
||||
d.vpcService.SetPrivateDNSNamer(func(privateIP string) string {
|
||||
_, private := handlers_dns.EC2DNSNames(region, "", internalDomain, "", privateIP)
|
||||
return private
|
||||
})
|
||||
|
||||
d.routeTableService, err = initServiceWithRetry("RouteTable service", func() (*handlers_ec2_routetable.RouteTableServiceImpl, error) {
|
||||
return handlers_ec2_routetable.NewRouteTableServiceImplWithNATS(d.ctx, d.config, d.natsConn)
|
||||
|
||||
@@ -109,8 +109,15 @@ func ec2Handler[In any](handler func(ctx context.Context, input *In, gw *Gateway
|
||||
// ec2ListsAsSet names the list fields AWS omits when empty instead of
|
||||
// rendering them. Each is rendered as its handler set it, so nil omits it.
|
||||
var ec2ListsAsSet = map[reflect.Type][]string{
|
||||
reflect.TypeFor[ec2.CreateKeyPairOutput](): {"Tags"},
|
||||
reflect.TypeFor[ec2.Image](): {"Tags", "ProductCodes"},
|
||||
reflect.TypeFor[ec2.CreateKeyPairOutput](): {"Tags"},
|
||||
reflect.TypeFor[ec2.Image](): {"Tags", "ProductCodes"},
|
||||
reflect.TypeFor[ec2.CreateSecurityGroupOutput](): {"Tags"},
|
||||
reflect.TypeFor[ec2.SecurityGroup](): {"Tags"},
|
||||
reflect.TypeFor[ec2.SecurityGroupRule](): {"Tags"},
|
||||
reflect.TypeFor[ec2.RevokeSecurityGroupIngressOutput](): {"UnknownIpPermissions"},
|
||||
reflect.TypeFor[ec2.RevokeSecurityGroupEgressOutput](): {"UnknownIpPermissions"},
|
||||
reflect.TypeFor[ec2.NetworkInterface](): {"Ipv4Prefixes", "Ipv6Prefixes"},
|
||||
reflect.TypeFor[ec2.Vpc](): {"Ipv6CidrBlockAssociationSet"},
|
||||
}
|
||||
|
||||
// marshalEC2Response renders an EC2 handler's output into the action's XML
|
||||
|
||||
@@ -136,3 +136,48 @@ func TestEC2_ListsAWSOmitsWhenEmpty(t *testing.T) {
|
||||
})
|
||||
assert.Contains(t, tagged, "<tagSet><item><key>k</key><value>v</value></item></tagSet>")
|
||||
}
|
||||
|
||||
// The network lists AWS omits when empty: an untagged security group's and
|
||||
// rule's tagSet, Revoke*'s unknownIpPermissionSet, an interface's prefix
|
||||
// lists, and a described VPC's IPv6 association set.
|
||||
func TestEC2_NetworkListsAWSOmitsWhenEmpty(t *testing.T) {
|
||||
sg := render[ec2.CreateSecurityGroupInput](t, "CreateSecurityGroup", ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-1")})
|
||||
assert.NotContains(t, sg, "tagSet")
|
||||
|
||||
groups := render[ec2.DescribeSecurityGroupsInput](t, "DescribeSecurityGroups", ec2.DescribeSecurityGroupsOutput{
|
||||
SecurityGroups: []*ec2.SecurityGroup{{GroupId: aws.String("sg-1")}},
|
||||
})
|
||||
assert.NotContains(t, groups, "tagSet")
|
||||
assert.Contains(t, groups, "<ipPermissions></ipPermissions>")
|
||||
|
||||
auth := render[ec2.AuthorizeSecurityGroupIngressInput](t, "AuthorizeSecurityGroupIngress", ec2.AuthorizeSecurityGroupIngressOutput{
|
||||
SecurityGroupRules: []*ec2.SecurityGroupRule{{SecurityGroupRuleId: aws.String("sgr-1")}},
|
||||
})
|
||||
assert.NotContains(t, auth, "tagSet")
|
||||
|
||||
rules := render[ec2.DescribeSecurityGroupRulesInput](t, "DescribeSecurityGroupRules", ec2.DescribeSecurityGroupRulesOutput{
|
||||
SecurityGroupRules: []*ec2.SecurityGroupRule{{SecurityGroupRuleId: aws.String("sgr-1"), Tags: []*ec2.Tag{}}},
|
||||
})
|
||||
assert.Contains(t, rules, "<tagSet></tagSet>")
|
||||
|
||||
revoke := render[ec2.RevokeSecurityGroupIngressInput](t, "RevokeSecurityGroupIngress", ec2.RevokeSecurityGroupIngressOutput{Return: aws.Bool(true)})
|
||||
assert.NotContains(t, revoke, "unknownIpPermissionSet")
|
||||
revokeEgress := render[ec2.RevokeSecurityGroupEgressInput](t, "RevokeSecurityGroupEgress", ec2.RevokeSecurityGroupEgressOutput{Return: aws.Bool(true)})
|
||||
assert.NotContains(t, revokeEgress, "unknownIpPermissionSet")
|
||||
|
||||
enis := render[ec2.DescribeNetworkInterfacesInput](t, "DescribeNetworkInterfaces", ec2.DescribeNetworkInterfacesOutput{
|
||||
NetworkInterfaces: []*ec2.NetworkInterface{{NetworkInterfaceId: aws.String("eni-1")}},
|
||||
})
|
||||
assert.NotContains(t, enis, "ipv4PrefixSet")
|
||||
assert.NotContains(t, enis, "ipv6PrefixSet")
|
||||
assert.Contains(t, enis, "<groupSet></groupSet>")
|
||||
|
||||
vpcs := render[ec2.DescribeVpcsInput](t, "DescribeVpcs", ec2.DescribeVpcsOutput{Vpcs: []*ec2.Vpc{{VpcId: aws.String("vpc-1")}}})
|
||||
assert.NotContains(t, vpcs, "ipv6CidrBlockAssociationSet")
|
||||
|
||||
created := render[ec2.CreateVpcInput](t, "CreateVpc", ec2.CreateVpcOutput{Vpc: &ec2.Vpc{
|
||||
VpcId: aws.String("vpc-1"),
|
||||
Ipv6CidrBlockAssociationSet: []*ec2.VpcIpv6CidrBlockAssociation{},
|
||||
}})
|
||||
assert.Contains(t, created, "<ipv6CidrBlockAssociationSet></ipv6CidrBlockAssociationSet>")
|
||||
}
|
||||
|
||||
@@ -551,7 +551,8 @@ func (s *RouteTableServiceImpl) CreateRouteTable(ctx context.Context, input *ec2
|
||||
}
|
||||
|
||||
return &ec2.CreateRouteTableOutput{
|
||||
RouteTable: recordToEC2(record),
|
||||
RouteTable: recordToEC2(record),
|
||||
ClientToken: input.ClientToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -103,9 +103,11 @@ func TestCreateRouteTable(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
out, err := svc.CreateRouteTable(t.Context(), &ec2.CreateRouteTableInput{
|
||||
VpcId: aws.String("vpc-test1"),
|
||||
VpcId: aws.String("vpc-test1"),
|
||||
ClientToken: aws.String("token-1"),
|
||||
}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "token-1", aws.StringValue(out.ClientToken), "AWS echoes the request's ClientToken")
|
||||
|
||||
rtb := out.RouteTable
|
||||
assert.NotEmpty(t, *rtb.RouteTableId)
|
||||
|
||||
@@ -270,7 +270,6 @@ func (s *VPCServiceImpl) buildDefaultVPC(ctx context.Context, accountID string,
|
||||
AZ: s.localAZ(),
|
||||
EnableDnsSupport: true, // AWS default
|
||||
EnableDnsHostnames: true, // AWS default for default VPC
|
||||
Tags: map[string]string{"Name": "default"},
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -270,3 +270,17 @@ func TestEnsureDefaultVPC_UsesBootstrapIDs(t *testing.T) {
|
||||
assert.Equal(t, ids.IgwId, info.InternetGatewayId)
|
||||
}
|
||||
}
|
||||
|
||||
// AWS leaves the default VPC untagged.
|
||||
func TestEnsureDefaultVPC_LeavesVPCUntagged(t *testing.T) {
|
||||
t.Parallel()
|
||||
svcs, _, _ := newNodeServices(t, 1)
|
||||
|
||||
info, err := svcs[0].EnsureDefaultVPC("000000000001", handlers_ec2_vpc.BootstrapIDs{})
|
||||
require.NoError(t, err)
|
||||
|
||||
out, err := svcs[0].DescribeVpcs(t.Context(), &ec2.DescribeVpcsInput{VpcIds: []*string{aws.String(info.VpcId)}}, "000000000001")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, out.Vpcs, 1)
|
||||
assert.Empty(t, out.Vpcs[0].Tags)
|
||||
}
|
||||
|
||||
@@ -216,8 +216,10 @@ func (s *VPCServiceImpl) CreateNetworkInterface(ctx context.Context, input *ec2.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupNames := make(map[string]string)
|
||||
s.fillSGNames(ctx, accountID, record.SecurityGroupIds, groupNames)
|
||||
return &ec2.CreateNetworkInterfaceOutput{
|
||||
NetworkInterface: s.eniRecordToEC2(&record, accountID),
|
||||
NetworkInterface: s.eniRecordToEC2(&record, accountID, groupNames),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -542,6 +544,7 @@ func (s *VPCServiceImpl) DescribeNetworkInterfaces(ctx context.Context, input *e
|
||||
}
|
||||
|
||||
enis := make([]*ec2.NetworkInterface, 0)
|
||||
groupNames := make(map[string]string)
|
||||
|
||||
eniIDs := make(map[string]bool)
|
||||
for _, id := range input.NetworkInterfaceIds {
|
||||
@@ -586,7 +589,8 @@ func (s *VPCServiceImpl) DescribeNetworkInterfaces(ctx context.Context, input *e
|
||||
continue
|
||||
}
|
||||
|
||||
enis = append(enis, s.eniRecordToEC2(&record, accountID))
|
||||
s.fillSGNames(ctx, accountID, record.SecurityGroupIds, groupNames)
|
||||
enis = append(enis, s.eniRecordToEC2(&record, accountID, groupNames))
|
||||
}
|
||||
|
||||
// If specific ENI IDs were requested but not found, return error
|
||||
@@ -1008,8 +1012,32 @@ func (s *VPCServiceImpl) updateENIPublicIP(ctx context.Context, accountID, eniId
|
||||
return nil
|
||||
}
|
||||
|
||||
// eniRecordToEC2 converts an ENI record to an EC2 NetworkInterface.
|
||||
func (s *VPCServiceImpl) eniRecordToEC2(record *ENIRecord, accountID string) *ec2.NetworkInterface {
|
||||
// fillSGNames records in names the name of each group in ids it does not
|
||||
// already hold. A group that cannot be read is recorded as "", so it is listed
|
||||
// without a name and not looked up again.
|
||||
func (s *VPCServiceImpl) fillSGNames(ctx context.Context, accountID string, ids []string, names map[string]string) {
|
||||
for _, id := range ids {
|
||||
if _, ok := names[id]; ok {
|
||||
continue
|
||||
}
|
||||
names[id] = ""
|
||||
entry, err := s.sgKV.Get(ctx, utils.AccountKey(accountID, id))
|
||||
if err != nil {
|
||||
slog.WarnContext(ctx, "fillSGNames: SG read failed", "groupId", id, "err", err)
|
||||
continue
|
||||
}
|
||||
var rec SecurityGroupRecord
|
||||
if err := json.Unmarshal(entry.Value(), &rec); err != nil {
|
||||
slog.WarnContext(ctx, "fillSGNames: SG unmarshal failed", "groupId", id, "err", err)
|
||||
continue
|
||||
}
|
||||
names[id] = rec.GroupName
|
||||
}
|
||||
}
|
||||
|
||||
// eniRecordToEC2 converts an ENI record to an EC2 NetworkInterface, naming
|
||||
// each security group from groupNames.
|
||||
func (s *VPCServiceImpl) eniRecordToEC2(record *ENIRecord, accountID string, groupNames map[string]string) *ec2.NetworkInterface {
|
||||
// ENIs with spinifex:managed-by tag are system-managed (e.g. by ELBv2)
|
||||
requesterManaged := record.Tags["spinifex:managed-by"] != ""
|
||||
|
||||
@@ -1038,13 +1066,22 @@ func (s *VPCServiceImpl) eniRecordToEC2(record *ENIRecord, accountID string) *ec
|
||||
if len(record.SecurityGroupIds) > 0 {
|
||||
groups := make([]*ec2.GroupIdentifier, 0, len(record.SecurityGroupIds))
|
||||
for _, sgId := range record.SecurityGroupIds {
|
||||
groups = append(groups, &ec2.GroupIdentifier{
|
||||
GroupId: aws.String(sgId),
|
||||
})
|
||||
group := &ec2.GroupIdentifier{GroupId: aws.String(sgId)}
|
||||
if name := groupNames[sgId]; name != "" {
|
||||
group.GroupName = aws.String(name)
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
eni.Groups = groups
|
||||
}
|
||||
|
||||
if s.privateDNSName != nil && record.PrivateIpAddress != "" {
|
||||
if name := s.privateDNSName(record.PrivateIpAddress); name != "" {
|
||||
eni.PrivateDnsName = aws.String(name)
|
||||
eni.PrivateIpAddresses[0].PrivateDnsName = aws.String(name)
|
||||
}
|
||||
}
|
||||
|
||||
if record.PublicIpAddress != "" {
|
||||
eni.Association = &ec2.NetworkInterfaceAssociation{
|
||||
PublicIp: aws.String(record.PublicIpAddress),
|
||||
|
||||
@@ -50,6 +50,46 @@ func TestCreateNetworkInterface(t *testing.T) {
|
||||
assert.NotEmpty(t, *eni.MacAddress)
|
||||
}
|
||||
|
||||
func TestNetworkInterface_NamesGroupsAndPrivateAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
sgId := createTestSG(t, svc, vpcId, "web")
|
||||
|
||||
// Without a namer an interface carries no private name.
|
||||
unnamed := createTestENI(t, svc, subnetId)
|
||||
desc, err := svc.DescribeNetworkInterfaces(context.Background(), &ec2.DescribeNetworkInterfacesInput{
|
||||
NetworkInterfaceIds: []*string{aws.String(unnamed)},
|
||||
}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, desc.NetworkInterfaces, 1)
|
||||
assert.Nil(t, desc.NetworkInterfaces[0].PrivateDnsName)
|
||||
|
||||
svc.SetPrivateDNSNamer(func(ip string) string { return "name-of-" + ip })
|
||||
out, err := svc.CreateNetworkInterface(context.Background(), &ec2.CreateNetworkInterfaceInput{
|
||||
SubnetId: aws.String(subnetId),
|
||||
Groups: []*string{aws.String(sgId)},
|
||||
}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
|
||||
desc, err = svc.DescribeNetworkInterfaces(context.Background(), &ec2.DescribeNetworkInterfacesInput{
|
||||
NetworkInterfaceIds: []*string{out.NetworkInterface.NetworkInterfaceId},
|
||||
}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, desc.NetworkInterfaces, 1)
|
||||
|
||||
for name, eni := range map[string]*ec2.NetworkInterface{"create": out.NetworkInterface, "describe": desc.NetworkInterfaces[0]} {
|
||||
want := "name-of-" + aws.StringValue(eni.PrivateIpAddress)
|
||||
assert.Equal(t, want, aws.StringValue(eni.PrivateDnsName), name)
|
||||
require.Len(t, eni.PrivateIpAddresses, 1, name)
|
||||
assert.Equal(t, want, aws.StringValue(eni.PrivateIpAddresses[0].PrivateDnsName), name)
|
||||
require.Len(t, eni.Groups, 1, name)
|
||||
assert.Equal(t, sgId, aws.StringValue(eni.Groups[0].GroupId), name)
|
||||
assert.Equal(t, "web", aws.StringValue(eni.Groups[0].GroupName), name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_SequentialIPs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
@@ -286,6 +286,7 @@ func (s *VPCServiceImpl) CreateSecurityGroup(ctx context.Context, input *ec2.Cre
|
||||
|
||||
return &ec2.CreateSecurityGroupOutput{
|
||||
GroupId: aws.String(groupId),
|
||||
Tags: utils.MapToEC2Tags(record.Tags),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -864,6 +865,13 @@ func (s *VPCServiceImpl) DescribeSecurityGroupRules(ctx context.Context, input *
|
||||
}
|
||||
|
||||
rules, nextToken := paging.EC2Page(rules, func(r *ec2.SecurityGroupRule) string { return *r.SecurityGroupRuleId }, pageReq)
|
||||
// AWS lists an untagged rule with empty Tags here, but omits them from
|
||||
// the Authorize* responses that share the rule shape.
|
||||
for _, r := range rules {
|
||||
if r.Tags == nil {
|
||||
r.Tags = []*ec2.Tag{}
|
||||
}
|
||||
}
|
||||
|
||||
slog.InfoContext(ctx, "DescribeSecurityGroupRules completed", "count", len(rules), "accountID", accountID)
|
||||
|
||||
@@ -982,8 +990,8 @@ func applySGRuleTags(rules []SGRule, specs []*ec2.TagSpecification) []SGRule {
|
||||
}
|
||||
|
||||
// sgRuleToSecurityGroupRule flattens a stored SGRule into the AWS API shape.
|
||||
// accountID supplies GroupOwnerId and ReferencedGroupInfo.UserId; VpcId is
|
||||
// derived from the parent record (same-VPC references are enforced on write).
|
||||
// accountID supplies GroupOwnerId and ReferencedGroupInfo.UserId. AWS names
|
||||
// no VpcId for a same-VPC reference, the only kind Spinifex allows.
|
||||
func sgRuleToSecurityGroupRule(record *SecurityGroupRecord, rule SGRule, isEgress bool, accountID string) *ec2.SecurityGroupRule {
|
||||
// An all-protocol rule has no ports to report. AWS answers -1 for both;
|
||||
// reporting the stored zeroes offers a caller two ports it never sent.
|
||||
@@ -1014,7 +1022,6 @@ func sgRuleToSecurityGroupRule(record *SecurityGroupRecord, rule SGRule, isEgres
|
||||
out.ReferencedGroupInfo = &ec2.ReferencedSecurityGroup{
|
||||
GroupId: aws.String(rule.SourceSG),
|
||||
UserId: aws.String(accountID),
|
||||
VpcId: aws.String(record.VpcId),
|
||||
}
|
||||
}
|
||||
return out
|
||||
@@ -1841,8 +1848,8 @@ func (s *VPCServiceImpl) sgRecordToEC2(record *SecurityGroupRecord, accountID st
|
||||
Description: aws.String(record.Description),
|
||||
VpcId: aws.String(record.VpcId),
|
||||
OwnerId: aws.String(accountID),
|
||||
IpPermissions: sgRulesToIpPermissions(record.IngressRules),
|
||||
IpPermissionsEgress: sgRulesToIpPermissions(record.EgressRules),
|
||||
IpPermissions: sgRulesToIpPermissions(record.IngressRules, accountID),
|
||||
IpPermissionsEgress: sgRulesToIpPermissions(record.EgressRules, accountID),
|
||||
}
|
||||
|
||||
sg.Tags = utils.MapToEC2Tags(record.Tags)
|
||||
@@ -1954,7 +1961,8 @@ func ipPermissionsToSGRules(perms []*ec2.IpPermission, mode sgParseMode) ([]SGRu
|
||||
}
|
||||
|
||||
// sgRulesToIpPermissions converts SGRule slice to AWS IpPermission slice.
|
||||
func sgRulesToIpPermissions(rules []SGRule) []*ec2.IpPermission {
|
||||
// accountID owns every referenced group, since references stay in one VPC.
|
||||
func sgRulesToIpPermissions(rules []SGRule, accountID string) []*ec2.IpPermission {
|
||||
// Group rules by protocol+port range
|
||||
type permKey struct {
|
||||
IpProtocol string
|
||||
@@ -1992,7 +2000,7 @@ func sgRulesToIpPermissions(rules []SGRule) []*ec2.IpPermission {
|
||||
perm.Ipv6Ranges = append(perm.Ipv6Ranges, ipRange)
|
||||
}
|
||||
if rule.SourceSG != "" {
|
||||
pair := &ec2.UserIdGroupPair{GroupId: aws.String(rule.SourceSG)}
|
||||
pair := &ec2.UserIdGroupPair{GroupId: aws.String(rule.SourceSG), UserId: aws.String(accountID)}
|
||||
if rule.Description != "" {
|
||||
pair.Description = aws.String(rule.Description)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ func TestCreateSecurityGroup_Success(t *testing.T) {
|
||||
}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, *out.GroupId)
|
||||
assert.Nil(t, out.Tags, "AWS omits Tags from an untagged CreateSecurityGroup")
|
||||
}
|
||||
|
||||
func TestCreateSecurityGroup_MissingGroupName(t *testing.T) {
|
||||
@@ -930,7 +931,7 @@ func TestSGRulesToIpPermissions_IPv6RoundTrip(t *testing.T) {
|
||||
perms := sgRulesToIpPermissions([]SGRule{
|
||||
{IpProtocol: "-1", CidrIp: "0.0.0.0/0"},
|
||||
{IpProtocol: "-1", CidrIpv6: "::/0"},
|
||||
})
|
||||
}, testAccountID)
|
||||
require.Len(t, perms, 1)
|
||||
require.Len(t, perms[0].IpRanges, 1)
|
||||
require.Len(t, perms[0].Ipv6Ranges, 1)
|
||||
@@ -945,7 +946,7 @@ func TestSGRulesToIpPermissions_Conversion(t *testing.T) {
|
||||
{IpProtocol: "-1", SourceSG: "sg-other"},
|
||||
}
|
||||
|
||||
perms := sgRulesToIpPermissions(rules)
|
||||
perms := sgRulesToIpPermissions(rules, testAccountID)
|
||||
require.Len(t, perms, 2)
|
||||
|
||||
// Order is non-deterministic (map-based), find each by protocol
|
||||
@@ -968,6 +969,7 @@ func TestSGRulesToIpPermissions_Conversion(t *testing.T) {
|
||||
assert.Nil(t, allPerm.ToPort)
|
||||
assert.Len(t, allPerm.UserIdGroupPairs, 1)
|
||||
assert.Equal(t, "sg-other", *allPerm.UserIdGroupPairs[0].GroupId)
|
||||
assert.Equal(t, testAccountID, aws.StringValue(allPerm.UserIdGroupPairs[0].UserId))
|
||||
}
|
||||
|
||||
func TestSGRuleKey(t *testing.T) {
|
||||
@@ -1456,6 +1458,9 @@ func TestDescribeSecurityGroups_FilterByTag(t *testing.T) {
|
||||
},
|
||||
}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, out.Tags, 1, "CreateSecurityGroup echoes the requested tags")
|
||||
assert.Equal(t, "Env", aws.StringValue(out.Tags[0].Key))
|
||||
assert.Equal(t, "prod", aws.StringValue(out.Tags[0].Value))
|
||||
|
||||
createTestSG(t, svc, vpcID, "untagged-sg")
|
||||
|
||||
@@ -2309,7 +2314,7 @@ func TestDescribeSecurityGroupRules_ReferencedGroupInfo(t *testing.T) {
|
||||
dstSG := createTestSG(t, svc, vpcID, "dest")
|
||||
|
||||
proto := "tcp"
|
||||
_, err := svc.AuthorizeSecurityGroupIngress(context.Background(), &ec2.AuthorizeSecurityGroupIngressInput{
|
||||
authOut, err := svc.AuthorizeSecurityGroupIngress(context.Background(), &ec2.AuthorizeSecurityGroupIngressInput{
|
||||
GroupId: aws.String(dstSG),
|
||||
IpPermissions: []*ec2.IpPermission{{
|
||||
IpProtocol: &proto,
|
||||
@@ -2319,6 +2324,8 @@ func TestDescribeSecurityGroupRules_ReferencedGroupInfo(t *testing.T) {
|
||||
}},
|
||||
}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, authOut.SecurityGroupRules, 1)
|
||||
assert.Nil(t, authOut.SecurityGroupRules[0].Tags, "Authorize* omits Tags on an untagged rule")
|
||||
|
||||
out, err := svc.DescribeSecurityGroupRules(context.Background(), &ec2.DescribeSecurityGroupRulesInput{
|
||||
Filters: []*ec2.Filter{{Name: aws.String("group-id"), Values: []*string{aws.String(dstSG)}}},
|
||||
@@ -2330,11 +2337,12 @@ func TestDescribeSecurityGroupRules_ReferencedGroupInfo(t *testing.T) {
|
||||
if r.ReferencedGroupInfo != nil {
|
||||
require.NotNil(t, r.ReferencedGroupInfo.GroupId)
|
||||
require.NotNil(t, r.ReferencedGroupInfo.UserId)
|
||||
require.NotNil(t, r.ReferencedGroupInfo.VpcId)
|
||||
assert.Equal(t, srcSG, *r.ReferencedGroupInfo.GroupId)
|
||||
assert.Equal(t, testAccountID, *r.ReferencedGroupInfo.UserId)
|
||||
assert.Equal(t, vpcID, *r.ReferencedGroupInfo.VpcId)
|
||||
assert.Nil(t, r.ReferencedGroupInfo.VpcId, "AWS names no VpcId for a same-VPC reference")
|
||||
assert.Nil(t, r.CidrIpv4)
|
||||
assert.NotNil(t, r.Tags, "DescribeSecurityGroupRules lists an untagged rule with empty Tags")
|
||||
assert.Empty(t, r.Tags)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,10 @@ type VPCServiceImpl struct {
|
||||
// A nil value leaves both unchanged.
|
||||
centralTags CentralTagStore
|
||||
|
||||
// Optional: injected after construction to name interfaces' private
|
||||
// addresses. Nil leaves them unnamed.
|
||||
privateDNSName func(privateIP string) string
|
||||
|
||||
// disableDefaultPublicIP seeds default subnets with MapPublicIpOnLaunch=false
|
||||
// (non-pool external modes have no public IPs to assign). Zero value keeps
|
||||
// the AWS-faithful default of true.
|
||||
@@ -117,6 +121,13 @@ func (s *VPCServiceImpl) SetExternalIPAM(ipam *ExternalIPAM, eipKV jetstream.Key
|
||||
s.eipKV = eipKV
|
||||
}
|
||||
|
||||
// SetPrivateDNSNamer injects how a private address is named, so an interface
|
||||
// reports the same PrivateDnsName as the instance using it. Nil, or a namer
|
||||
// that answers "", leaves interfaces unnamed.
|
||||
func (s *VPCServiceImpl) SetPrivateDNSNamer(namer func(privateIP string) string) {
|
||||
s.privateDNSName = namer
|
||||
}
|
||||
|
||||
// SetCentralTagStore injects the central tag store so create paths project
|
||||
// their record tags into it and delete paths clear them. Nil leaves both
|
||||
// unchanged.
|
||||
@@ -328,9 +339,11 @@ func (s *VPCServiceImpl) CreateVpc(ctx context.Context, input *ec2.CreateVpcInpu
|
||||
return nil, errors.New(awserrors.ErrorServerInternal)
|
||||
}
|
||||
|
||||
return &ec2.CreateVpcOutput{
|
||||
Vpc: s.vpcRecordToEC2(&record, accountID),
|
||||
}, nil
|
||||
// AWS renders the empty IPv6 association list on CreateVpc only;
|
||||
// DescribeVpcs omits it.
|
||||
vpc := s.vpcRecordToEC2(&record, accountID)
|
||||
vpc.Ipv6CidrBlockAssociationSet = []*ec2.VpcIpv6CidrBlockAssociation{}
|
||||
return &ec2.CreateVpcOutput{Vpc: vpc}, nil
|
||||
}
|
||||
|
||||
// requireVPCExists returns InvalidVpcID.NotFound if the VPC doesn't exist for
|
||||
|
||||
Reference in New Issue
Block a user