feat: more filters (#106)

This commit is contained in:
Julian Sommer
2026-04-01 13:23:04 +10:00
committed by GitHub
parent 853b5bfd32
commit 39e9fa6fb8
8 changed files with 522 additions and 6 deletions
+12 -2
View File
@@ -311,8 +311,10 @@ func mustJS(nc *nats.Conn) nats.JetStreamContext {
}
var describeNatGatewaysValidFilters = map[string]bool{
"vpc-id": true,
"state": true,
"nat-gateway-id": true,
"subnet-id": true,
"vpc-id": true,
"state": true,
}
// DescribeNatGateways lists NAT Gateways, optionally filtered
@@ -402,6 +404,14 @@ func natgwMatchesFilters(record *NatGatewayRecord, filters map[string][]string)
continue
}
switch name {
case "nat-gateway-id":
if !filterutil.MatchesAny(values, record.NatGatewayId) {
return false
}
case "subnet-id":
if !filterutil.MatchesAny(values, record.SubnetId) {
return false
}
case "vpc-id":
if !filterutil.MatchesAny(values, record.VpcId) {
return false
@@ -268,6 +268,61 @@ func TestDescribeNatGateways_DeletedGatewayVisible(t *testing.T) {
assert.EqualError(t, err, awserrors.ErrorInvalidNatGatewayIDNotFound)
}
func TestDescribeNatGateways_FilterByNatGatewayId(t *testing.T) {
svc := setupTestService(t)
createOut, err := svc.CreateNatGateway(&ec2.CreateNatGatewayInput{
SubnetId: aws.String("subnet-pub1"),
AllocationId: aws.String("eipalloc-test1"),
}, testAccountID)
require.NoError(t, err)
natgwID := *createOut.NatGateway.NatGatewayId
// Exact match
out, err := svc.DescribeNatGateways(&ec2.DescribeNatGatewaysInput{
Filter: []*ec2.Filter{{Name: aws.String("nat-gateway-id"), Values: []*string{aws.String(natgwID)}}},
}, testAccountID)
require.NoError(t, err)
require.Len(t, out.NatGateways, 1)
assert.Equal(t, natgwID, *out.NatGateways[0].NatGatewayId)
// Non-match
out, err = svc.DescribeNatGateways(&ec2.DescribeNatGatewaysInput{
Filter: []*ec2.Filter{{Name: aws.String("nat-gateway-id"), Values: []*string{aws.String("nat-000000")}}},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.NatGateways)
// Wildcard
out, err = svc.DescribeNatGateways(&ec2.DescribeNatGatewaysInput{
Filter: []*ec2.Filter{{Name: aws.String("nat-gateway-id"), Values: []*string{aws.String("nat-*")}}},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NatGateways, 1)
}
func TestDescribeNatGateways_FilterBySubnetId(t *testing.T) {
svc := setupTestService(t)
_, err := svc.CreateNatGateway(&ec2.CreateNatGatewayInput{
SubnetId: aws.String("subnet-pub1"),
AllocationId: aws.String("eipalloc-test1"),
}, testAccountID)
require.NoError(t, err)
// Exact match
out, err := svc.DescribeNatGateways(&ec2.DescribeNatGatewaysInput{
Filter: []*ec2.Filter{{Name: aws.String("subnet-id"), Values: []*string{aws.String("subnet-pub1")}}},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NatGateways, 1)
// Non-match
out, err = svc.DescribeNatGateways(&ec2.DescribeNatGatewaysInput{
Filter: []*ec2.Filter{{Name: aws.String("subnet-id"), Values: []*string{aws.String("subnet-000000")}}},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.NatGateways)
}
func TestDescribeNatGateways_FilterByVpcId(t *testing.T) {
svc := setupTestService(t)
_, err := svc.CreateNatGateway(&ec2.CreateNatGatewayInput{
@@ -158,6 +158,7 @@ func (s *PlacementGroupServiceImpl) DeletePlacementGroup(input *ec2.DeletePlacem
}
var describePlacementGroupsValidFilters = map[string]bool{
"group-id": true,
"strategy": true,
"state": true,
"spread-level": true,
@@ -254,6 +255,10 @@ func (s *PlacementGroupServiceImpl) DescribePlacementGroups(input *ec2.DescribeP
func pgMatchesFilters(record *PlacementGroupRecord, filters map[string][]string) bool {
for name, values := range filters {
switch name {
case "group-id":
if !filterutil.MatchesAny(values, record.GroupId) {
return false
}
case "strategy":
if !filterutil.MatchesAny(values, record.Strategy) {
return false
@@ -226,6 +226,40 @@ func TestDescribePlacementGroups_FilterByGroupId(t *testing.T) {
assert.Equal(t, *pg.GroupId, *out.PlacementGroups[0].GroupId)
}
func TestDescribePlacementGroups_FilterByGroupIdFilter(t *testing.T) {
svc := setupTestService(t)
pg := createTestGroup(t, svc, "gid-filter", "spread")
createTestGroup(t, svc, "other-group", "cluster")
// Exact match
out, err := svc.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
Filters: []*ec2.Filter{
{Name: aws.String("group-id"), Values: []*string{pg.GroupId}},
},
}, testAccountID)
require.NoError(t, err)
require.Len(t, out.PlacementGroups, 1)
assert.Equal(t, *pg.GroupId, *out.PlacementGroups[0].GroupId)
// Non-match
out, err = svc.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
Filters: []*ec2.Filter{
{Name: aws.String("group-id"), Values: []*string{aws.String("pg-000000")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.PlacementGroups)
// Wildcard
out, err = svc.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
Filters: []*ec2.Filter{
{Name: aws.String("group-id"), Values: []*string{aws.String("pg-*")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.PlacementGroups, 2)
}
func TestDescribePlacementGroups_FilterByStrategy(t *testing.T) {
svc := setupTestService(t)
createTestGroup(t, svc, "spread1", "spread")
@@ -265,6 +265,10 @@ var describeRouteTablesValidFilters = map[string]bool{
"association.subnet-id": true,
"route.destination-cidr-block": true,
"route.gateway-id": true,
"route.nat-gateway-id": true,
"route.state": true,
"route.origin": true,
"owner-id": true,
}
// DescribeRouteTables lists route tables, optionally filtered
@@ -831,6 +835,43 @@ func rtbMatchesFilters(record *RouteTableRecord, filters map[string][]string) bo
if !found {
return false
}
case "route.nat-gateway-id":
found := false
for _, r := range record.Routes {
if filterutil.MatchesAny(values, r.NatGatewayId) {
found = true
break
}
}
if !found {
return false
}
case "route.state":
found := false
for _, r := range record.Routes {
if filterutil.MatchesAny(values, r.State) {
found = true
break
}
}
if !found {
return false
}
case "route.origin":
found := false
for _, r := range record.Routes {
if filterutil.MatchesAny(values, r.Origin) {
found = true
break
}
}
if !found {
return false
}
case "owner-id":
if !filterutil.MatchesAny(values, record.AccountID) {
return false
}
default:
return false
}
@@ -194,6 +194,87 @@ func TestDescribeRouteTables_FilterByMain(t *testing.T) {
assert.True(t, *out.RouteTables[0].Associations[0].Main)
}
func TestDescribeRouteTables_FilterByRouteState(t *testing.T) {
svc := setupTestService(t)
createTestRtb(t, svc) // has local route with state=active
// Exact match
out, err := svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("route.state"), Values: []*string{aws.String("active")}}},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.RouteTables, 1)
// Non-match
out, err = svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("route.state"), Values: []*string{aws.String("blackhole")}}},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.RouteTables)
}
func TestDescribeRouteTables_FilterByRouteOrigin(t *testing.T) {
svc := setupTestService(t)
createTestRtb(t, svc) // local route has origin=CreateRouteTable
out, err := svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("route.origin"), Values: []*string{aws.String("CreateRouteTable")}}},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.RouteTables, 1)
out, err = svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("route.origin"), Values: []*string{aws.String("CreateRoute")}}},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.RouteTables)
}
func TestDescribeRouteTables_FilterByRouteNatGatewayId(t *testing.T) {
svc := setupTestService(t)
createTestRtb(t, svc) // no NAT GW route
// No match — no routes have a nat-gateway-id
out, err := svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("route.nat-gateway-id"), Values: []*string{aws.String("nat-000000")}}},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.RouteTables)
// Wildcard on empty field should not match
out, err = svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("route.nat-gateway-id"), Values: []*string{aws.String("nat-*")}}},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.RouteTables)
}
func TestDescribeRouteTables_FilterByOwnerId(t *testing.T) {
svc := setupTestService(t)
createTestRtb(t, svc)
// Exact match
out, err := svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("owner-id"), Values: []*string{aws.String(testAccountID)}}},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.RouteTables, 1)
// Non-match
out, err = svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("owner-id"), Values: []*string{aws.String("999999999999")}}},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.RouteTables)
// Wildcard
out, err = svc.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{{Name: aws.String("owner-id"), Values: []*string{aws.String("1234*")}}},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.RouteTables, 1)
}
func TestCreateRoute(t *testing.T) {
svc := setupTestService(t)
rtbID := createTestRtb(t, svc)
+55 -4
View File
@@ -227,10 +227,18 @@ func (s *VPCServiceImpl) ModifyNetworkInterfaceAttribute(input *ec2.ModifyNetwor
}
var describeNetworkInterfacesValidFilters = map[string]bool{
"subnet-id": true,
"vpc-id": true,
"description": true,
"attachment.instance-id": true,
"network-interface-id": true,
"subnet-id": true,
"vpc-id": true,
"status": true,
"private-ip-address": true,
"availability-zone": true,
"group-id": true,
"mac-address": true,
"description": true,
"attachment.attachment-id": true,
"attachment.instance-id": true,
"attachment.status": true,
}
// DescribeNetworkInterfaces lists ENIs with optional filters
@@ -315,6 +323,10 @@ func eniMatchesFilters(record *ENIRecord, filters map[string][]string) bool {
continue
}
switch name {
case "network-interface-id":
if !filterutil.MatchesAny(values, record.NetworkInterfaceId) {
return false
}
case "subnet-id":
if !filterutil.MatchesAny(values, record.SubnetId) {
return false
@@ -323,14 +335,53 @@ func eniMatchesFilters(record *ENIRecord, filters map[string][]string) bool {
if !filterutil.MatchesAny(values, record.VpcId) {
return false
}
case "status":
if !filterutil.MatchesAny(values, record.Status) {
return false
}
case "private-ip-address":
if !filterutil.MatchesAny(values, record.PrivateIpAddress) {
return false
}
case "availability-zone":
if !filterutil.MatchesAny(values, record.AvailabilityZone) {
return false
}
case "group-id":
found := false
for _, sgId := range record.SecurityGroupIds {
if filterutil.MatchesAny(values, sgId) {
found = true
break
}
}
if !found {
return false
}
case "mac-address":
if !filterutil.MatchesAny(values, record.MacAddress) {
return false
}
case "description":
if !filterutil.MatchesAny(values, record.Description) {
return false
}
case "attachment.attachment-id":
if !filterutil.MatchesAny(values, record.AttachmentId) {
return false
}
case "attachment.instance-id":
if !filterutil.MatchesAny(values, record.InstanceId) {
return false
}
case "attachment.status":
status := "detached"
if record.InstanceId != "" {
status = "attached"
}
if !filterutil.MatchesAny(values, status) {
return false
}
default:
return false
}
+239
View File
@@ -524,3 +524,242 @@ func TestCreateNetworkInterface_WithSecurityGroups(t *testing.T) {
assert.Equal(t, "sg-aaa", *out.NetworkInterface.Groups[0].GroupId)
assert.Equal(t, "sg-bbb", *out.NetworkInterface.Groups[1].GroupId)
}
func TestDescribeNetworkInterfaces_FilterByNetworkInterfaceId(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
eniId := createTestENI(t, svc, subnetId)
createTestENI(t, svc, subnetId)
out, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("network-interface-id"), Values: []*string{aws.String(eniId)}},
},
}, testAccountID)
require.NoError(t, err)
require.Len(t, out.NetworkInterfaces, 1)
assert.Equal(t, eniId, *out.NetworkInterfaces[0].NetworkInterfaceId)
// Wildcard
out, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("network-interface-id"), Values: []*string{aws.String("eni-*")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NetworkInterfaces, 2)
}
func TestDescribeNetworkInterfaces_FilterByStatus(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
eni1 := createTestENI(t, svc, subnetId)
createTestENI(t, svc, subnetId) // stays available
_, err := svc.AttachENI(testAccountID, eni1, "i-test", 0)
require.NoError(t, err)
out, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("status"), Values: []*string{aws.String("in-use")}},
},
}, testAccountID)
require.NoError(t, err)
require.Len(t, out.NetworkInterfaces, 1)
assert.Equal(t, eni1, *out.NetworkInterfaces[0].NetworkInterfaceId)
out, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("status"), Values: []*string{aws.String("available")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NetworkInterfaces, 1)
}
func TestDescribeNetworkInterfaces_FilterByPrivateIpAddress(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
createTestENI(t, svc, subnetId) // 10.0.1.4
createTestENI(t, svc, subnetId) // 10.0.1.5
out, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("private-ip-address"), Values: []*string{aws.String("10.0.1.4")}},
},
}, testAccountID)
require.NoError(t, err)
require.Len(t, out.NetworkInterfaces, 1)
assert.Equal(t, "10.0.1.4", *out.NetworkInterfaces[0].PrivateIpAddress)
// Wildcard
out, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("private-ip-address"), Values: []*string{aws.String("10.0.1.*")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NetworkInterfaces, 2)
// Non-match
out, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("private-ip-address"), Values: []*string{aws.String("192.168.0.1")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.NetworkInterfaces)
}
func TestDescribeNetworkInterfaces_FilterByAvailabilityZone(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
// Create subnet with explicit AZ
subnetOut, err := svc.CreateSubnet(&ec2.CreateSubnetInput{
VpcId: aws.String(vpcId),
CidrBlock: aws.String("10.0.1.0/24"),
AvailabilityZone: aws.String("ap-southeast-2a"),
}, testAccountID)
require.NoError(t, err)
subnetId := *subnetOut.Subnet.SubnetId
createTestENI(t, svc, subnetId)
out, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("availability-zone"), Values: []*string{aws.String("ap-southeast-2a")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NetworkInterfaces, 1)
out, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("availability-zone"), Values: []*string{aws.String("us-east-1a")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.NetworkInterfaces)
}
func TestDescribeNetworkInterfaces_FilterByGroupId(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
// Create ENI with security groups
out, err := svc.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{
SubnetId: aws.String(subnetId),
Groups: []*string{aws.String("sg-aaa"), aws.String("sg-bbb")},
}, testAccountID)
require.NoError(t, err)
eniId := *out.NetworkInterface.NetworkInterfaceId
createTestENI(t, svc, subnetId) // no SGs
// Match one of the SGs
desc, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("group-id"), Values: []*string{aws.String("sg-bbb")}},
},
}, testAccountID)
require.NoError(t, err)
require.Len(t, desc.NetworkInterfaces, 1)
assert.Equal(t, eniId, *desc.NetworkInterfaces[0].NetworkInterfaceId)
// Non-match
desc, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("group-id"), Values: []*string{aws.String("sg-zzz")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, desc.NetworkInterfaces)
}
func TestDescribeNetworkInterfaces_FilterByMacAddress(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
outCreate, err := svc.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{
SubnetId: aws.String(subnetId),
}, testAccountID)
require.NoError(t, err)
mac := *outCreate.NetworkInterface.MacAddress
out, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("mac-address"), Values: []*string{aws.String(mac)}},
},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NetworkInterfaces, 1)
out, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("mac-address"), Values: []*string{aws.String("ff:ff:ff:ff:ff:ff")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Empty(t, out.NetworkInterfaces)
}
func TestDescribeNetworkInterfaces_FilterByAttachmentId(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
eniId := createTestENI(t, svc, subnetId)
attachId, err := svc.AttachENI(testAccountID, eniId, "i-test", 0)
require.NoError(t, err)
createTestENI(t, svc, subnetId) // not attached
out, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("attachment.attachment-id"), Values: []*string{aws.String(attachId)}},
},
}, testAccountID)
require.NoError(t, err)
require.Len(t, out.NetworkInterfaces, 1)
assert.Equal(t, eniId, *out.NetworkInterfaces[0].NetworkInterfaceId)
}
func TestDescribeNetworkInterfaces_FilterByAttachmentStatus(t *testing.T) {
svc := setupTestVPCService(t)
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
eni1 := createTestENI(t, svc, subnetId)
createTestENI(t, svc, subnetId) // stays detached
_, err := svc.AttachENI(testAccountID, eni1, "i-test", 0)
require.NoError(t, err)
// Filter attached
out, err := svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("attachment.status"), Values: []*string{aws.String("attached")}},
},
}, testAccountID)
require.NoError(t, err)
require.Len(t, out.NetworkInterfaces, 1)
assert.Equal(t, eni1, *out.NetworkInterfaces[0].NetworkInterfaceId)
// Filter detached
out, err = svc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{Name: aws.String("attachment.status"), Values: []*string{aws.String("detached")}},
},
}, testAccountID)
require.NoError(t, err)
assert.Len(t, out.NetworkInterfaces, 1)
assert.NotEqual(t, eni1, *out.NetworkInterfaces[0].NetworkInterfaceId)
}