Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
716052077b |
No files matched your search
@@ -16,6 +16,36 @@ boundary host-catalogs create plugin \
|
||||
-secret secret_access_key='SECRET'
|
||||
```
|
||||
|
||||
To create a host catalog that returns addresses only from the primary network
|
||||
interface:
|
||||
|
||||
```
|
||||
boundary host-catalogs create plugin \
|
||||
-scope-id p_1234567890 \
|
||||
-name "Example Plugin-Based Host Catalog" \
|
||||
-description "Description for plugin-based host catalog" \
|
||||
-plugin-name aws \
|
||||
-attr region=REGION \
|
||||
-attr primary_interface_only=true \
|
||||
-secret access_key_id='KEY' \
|
||||
-secret secret_access_key='SECRET'
|
||||
```
|
||||
|
||||
To create a host catalog that excludes public IPv4 addresses while keeping
|
||||
private IPv4 addresses:
|
||||
|
||||
```
|
||||
boundary host-catalogs create plugin \
|
||||
-scope-id p_1234567890 \
|
||||
-name "Example Plugin-Based Host Catalog" \
|
||||
-description "Description for plugin-based host catalog" \
|
||||
-plugin-name aws \
|
||||
-attr region=REGION \
|
||||
-attr exclude_public_ips=true \
|
||||
-secret access_key_id='KEY' \
|
||||
-secret secret_access_key='SECRET'
|
||||
```
|
||||
|
||||
To create a host set, filtering the host set based on tag keys `foo` or `bar`
|
||||
(either tag can be present), ensuring that any targets set to this host set only
|
||||
connect to external addresses in the `54.0.0.0/8` class A subnet:
|
||||
@@ -75,7 +105,6 @@ a configured IAM user for this provider:
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
* `iam:GetUser`, `iam:CreateAccessKey`, and `iam:DeleteAccessKey`, configured to
|
||||
@@ -116,6 +145,16 @@ The following `attributes` are valid on an AWS host catalog resource:
|
||||
provider.
|
||||
- `role_tags` (object): The key-value pair tags configured for the `AssumeRole`
|
||||
provider.
|
||||
- `primary_interface_only` (bool): If `true`, only addresses from the primary
|
||||
ENI are returned. When `false` or omitted, addresses from all attached ENIs
|
||||
are considered.
|
||||
- `exclude_private_ips` (bool): If `true`, private IPv4 addresses are omitted.
|
||||
- `exclude_public_ips` (bool): If `true`, public IPv4 addresses are omitted.
|
||||
|
||||
If none of the exclusion flags are set, the plugin preserves the historical
|
||||
behavior and returns all discovered address classes. The exclusion flags apply
|
||||
to both IPv4 and IPv6 addresses based on whether the IP is classified as public
|
||||
or private.
|
||||
|
||||
The following `secrets` are required on an AWS host catalog resource:
|
||||
|
||||
|
||||
@@ -22,6 +22,18 @@ type CatalogAttributes struct {
|
||||
|
||||
// DualStack is used for configuring how the aws client will resolve requests.
|
||||
DualStack bool
|
||||
|
||||
// PrimaryInterfaceOnly restricts discovery to the instance's primary ENI.
|
||||
PrimaryInterfaceOnly bool
|
||||
|
||||
// ExcludePrivateIps controls whether private IPv4 addresses are omitted.
|
||||
ExcludePrivateIps bool
|
||||
|
||||
// ExcludePublicIps controls whether public IPv4 addresses are omitted.
|
||||
ExcludePublicIps bool
|
||||
|
||||
// ExcludeIpv6 controls whether IPv6 addresses are omitted.
|
||||
ExcludeIpv6 bool
|
||||
}
|
||||
|
||||
func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
|
||||
@@ -40,6 +52,36 @@ func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
|
||||
}
|
||||
delete(unknownFields, ConstAwsDualStack)
|
||||
|
||||
primaryInterfaceOnly, err := values.GetBoolValue(in, ConstPrimaryInterfaceOnly, false)
|
||||
if err != nil {
|
||||
badFields[fmt.Sprintf("attributes.%s", ConstPrimaryInterfaceOnly)] = err.Error()
|
||||
}
|
||||
delete(unknownFields, ConstPrimaryInterfaceOnly)
|
||||
|
||||
excludePrivateIPs, err := values.GetBoolValue(in, ConstExcludePrivateIps, false)
|
||||
if err != nil {
|
||||
badFields[fmt.Sprintf("attributes.%s", ConstExcludePrivateIps)] = err.Error()
|
||||
}
|
||||
delete(unknownFields, ConstExcludePrivateIps)
|
||||
|
||||
excludePublicIPs, err := values.GetBoolValue(in, ConstExcludePublicIps, false)
|
||||
if err != nil {
|
||||
badFields[fmt.Sprintf("attributes.%s", ConstExcludePublicIps)] = err.Error()
|
||||
}
|
||||
delete(unknownFields, ConstExcludePublicIps)
|
||||
|
||||
excludeIpv6, err := values.GetBoolValue(in, ConstExcludeIpv6, false)
|
||||
if err != nil {
|
||||
badFields[fmt.Sprintf("attributes.%s", ConstExcludeIpv6)] = err.Error()
|
||||
}
|
||||
delete(unknownFields, ConstExcludeIpv6)
|
||||
|
||||
if excludePrivateIPs && excludePublicIPs && excludeIpv6 {
|
||||
badFields[fmt.Sprintf("attributes.%s", ConstExcludePrivateIps)] = "cannot be combined with exclude_public_ips and exclude_ipv6"
|
||||
badFields[fmt.Sprintf("attributes.%s", ConstExcludePublicIps)] = "cannot be combined with exclude_private_ips and exclude_ipv6"
|
||||
badFields[fmt.Sprintf("attributes.%s", ConstExcludeIpv6)] = "cannot be combined with exclude_private_ips and exclude_public_ips"
|
||||
}
|
||||
|
||||
for s := range unknownFields {
|
||||
switch s {
|
||||
// Ignore knownFields from CredentialAttributes
|
||||
@@ -67,6 +109,10 @@ func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
|
||||
return &CatalogAttributes{
|
||||
CredentialAttributes: credAttributes,
|
||||
DualStack: dualStack,
|
||||
PrimaryInterfaceOnly: primaryInterfaceOnly,
|
||||
ExcludePrivateIps: excludePrivateIPs,
|
||||
ExcludePublicIps: excludePublicIPs,
|
||||
ExcludeIpv6: excludeIpv6,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,106 @@ func TestGetCatalogAttributes(t *testing.T) {
|
||||
DualStack: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with primary interface only",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"primary_interface_only": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
PrimaryInterfaceOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with exclude public ip",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_public_ips": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
ExcludePublicIps: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with exclude private ip",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_private_ips": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
ExcludePrivateIps: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with mixed attributes",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"dual_stack": structpb.NewBoolValue(true),
|
||||
"exclude_private_ips": structpb.NewBoolValue(true),
|
||||
"exclude_ipv6": structpb.NewBoolValue(true),
|
||||
"primary_interface_only": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
DualStack: true,
|
||||
ExcludePrivateIps: true,
|
||||
ExcludeIpv6: true,
|
||||
PrimaryInterfaceOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "can exclude public and private IPs when IPv6 remains enabled",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_private_ips": structpb.NewBoolValue(true),
|
||||
"exclude_public_ips": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
ExcludePrivateIps: true,
|
||||
ExcludePublicIps: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cannot exclude public private and IPv6 together",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_private_ips": structpb.NewBoolValue(true),
|
||||
"exclude_public_ips": structpb.NewBoolValue(true),
|
||||
"exclude_ipv6": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expectedErrContains: "attributes.exclude_ipv6: cannot be combined with exclude_private_ips and exclude_public_ips, attributes.exclude_private_ips: cannot be combined with exclude_public_ips and exclude_ipv6, attributes.exclude_public_ips: cannot be combined with exclude_private_ips and exclude_ipv6",
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
in: &structpb.Struct{
|
||||
|
||||
@@ -13,4 +13,16 @@ const (
|
||||
|
||||
// ConstAwsDualStack is the key for the dualstack flag in the aws s3 client.
|
||||
ConstAwsDualStack = "dual_stack"
|
||||
|
||||
// ConstPrimaryInterfaceOnly limits host discovery to the primary ENI.
|
||||
ConstPrimaryInterfaceOnly = "primary_interface_only"
|
||||
|
||||
// ConstExcludePrivateIps excludes private IPv4 addresses.
|
||||
ConstExcludePrivateIps = "exclude_private_ips"
|
||||
|
||||
// ConstExcludePublicIps excludes public IPv4 addresses associated to ENI IPs.
|
||||
ConstExcludePublicIps = "exclude_public_ips"
|
||||
|
||||
// ConstExcludeIpv6 excludes IPv6 addresses.
|
||||
ConstExcludeIpv6 = "exclude_ipv6"
|
||||
)
|
||||
@@ -1,8 +1,8 @@
|
||||
package host
|
||||
|
||||
// Copyright IBM Corp. 2021, 2026
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
@@ -550,7 +550,7 @@ func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*
|
||||
// set of hosts afterwards (possibly removing duplicates).
|
||||
for _, reservation := range output.Reservations {
|
||||
for _, instance := range reservation.Instances {
|
||||
host, err := awsInstanceToHost(instance)
|
||||
host, err := awsInstanceToHost(instance, catalogAttributes)
|
||||
if err != nil {
|
||||
return nil, errors.BadRequestStatus("error processing host results for host set id %q: %s", query.Id, err)
|
||||
}
|
||||
@@ -648,10 +648,13 @@ func buildDescribeInstancesInput(attrs *SetAttributes, dryRun bool) (*ec2.Descri
|
||||
// awsInstanceToHost processes data from an ec2.Instance and returns
|
||||
// a ListHostsResponseHost with the ID and network addresses
|
||||
// populated.
|
||||
func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, error) {
|
||||
func awsInstanceToHost(instance types.Instance, catalogAttributes *CatalogAttributes) (*pb.ListHostsResponseHost, error) {
|
||||
if aws.ToString(instance.InstanceId) == "" {
|
||||
return nil, fmt.Errorf("response integrity error: missing instance id")
|
||||
}
|
||||
if catalogAttributes == nil {
|
||||
return nil, fmt.Errorf("catalog attributes are required")
|
||||
}
|
||||
|
||||
result := new(pb.ListHostsResponseHost)
|
||||
|
||||
@@ -666,22 +669,46 @@ func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, erro
|
||||
}
|
||||
}
|
||||
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PrivateIpAddress, instance.PublicIpAddress)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, instance.PrivateDnsName, instance.PublicDnsName)
|
||||
if !catalogAttributes.ExcludePrivateIps {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PrivateIpAddress)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, instance.PrivateDnsName)
|
||||
}
|
||||
if !catalogAttributes.ExcludePublicIps {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PublicIpAddress)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, instance.PublicDnsName)
|
||||
}
|
||||
|
||||
// Now go through all of the interfaces and log the IP address of
|
||||
// every interface.
|
||||
for _, iface := range instance.NetworkInterfaces {
|
||||
if catalogAttributes.PrimaryInterfaceOnly {
|
||||
if iface.Attachment == nil || aws.ToInt32(iface.Attachment.DeviceIndex) != 0 {
|
||||
// We only want to sync the primary interface so skip any others
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Populate default IP addresses/DNS name similar to how we do
|
||||
// for the entire instance.
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, iface.PrivateIpAddress)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, iface.PrivateDnsName)
|
||||
if !catalogAttributes.ExcludePrivateIps {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, iface.PrivateIpAddress)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, iface.PrivateDnsName)
|
||||
}
|
||||
|
||||
// Iterate through the private IP addresses and log the
|
||||
// information.
|
||||
for _, addr := range iface.PrivateIpAddresses {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, addr.PrivateIpAddress)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, addr.PrivateDnsName)
|
||||
if !catalogAttributes.ExcludePrivateIps {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, addr.PrivateIpAddress)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, addr.PrivateDnsName)
|
||||
}
|
||||
|
||||
if catalogAttributes.ExcludePublicIps {
|
||||
// skip adding public addresses if we are excluding them
|
||||
continue
|
||||
}
|
||||
|
||||
// PublicIP for iface is only available via an association to the private IP
|
||||
if addr.Association != nil {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, addr.Association.PublicIp)
|
||||
result.DnsNames = appendDistinct(result.DnsNames, addr.Association.PublicDnsName)
|
||||
@@ -689,8 +716,10 @@ func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, erro
|
||||
}
|
||||
|
||||
// Add the IPv6 addresses.
|
||||
for _, addr := range iface.Ipv6Addresses {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, addr.Ipv6Address)
|
||||
if !catalogAttributes.ExcludeIpv6 {
|
||||
for _, addr := range iface.Ipv6Addresses {
|
||||
result.IpAddresses = appendDistinct(result.IpAddresses, addr.Ipv6Address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -638,7 +638,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
},
|
||||
},
|
||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
@@ -664,7 +665,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
},
|
||||
},
|
||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
@@ -694,7 +696,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
},
|
||||
},
|
||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
@@ -721,7 +724,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
},
|
||||
},
|
||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
@@ -2383,10 +2387,11 @@ func TestBuildDescribeInstancesInput(t *testing.T) {
|
||||
|
||||
func TestAwsInstanceToHost(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
instance types.Instance
|
||||
expected *pb.ListHostsResponseHost
|
||||
expectedErr string
|
||||
name string
|
||||
instance types.Instance
|
||||
catalogAttributes *CatalogAttributes
|
||||
expected *pb.ListHostsResponseHost
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "missing instance id",
|
||||
@@ -2591,14 +2596,45 @@ func TestAwsInstanceToHost(t *testing.T) {
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{
|
||||
{Ipv6Address: nil}, // Just coverage for nil assertion which is skipped
|
||||
{Ipv6Address: aws.String("some::fake::address")},
|
||||
{Ipv6Address: aws.String("2001:db8::1")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "foobar",
|
||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "some::fake::address"},
|
||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "2001:db8::1"},
|
||||
DnsNames: []string{"test.example.internal", "test.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "good, single IP w/public addr",
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("foobar"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
PublicIpAddress: aws.String("1.1.1.1"),
|
||||
PublicDnsName: aws.String("test.example.com"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.1.1.1"),
|
||||
PublicDnsName: aws.String("test.example.com"),
|
||||
},
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "foobar",
|
||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1"},
|
||||
DnsNames: []string{"test.example.internal", "test.example.com"},
|
||||
},
|
||||
},
|
||||
@@ -2676,18 +2712,663 @@ func TestAwsInstanceToHost(t *testing.T) {
|
||||
DnsNames: []string{"test.example.internal", "test.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "primary_interface_only keeps all addresses on primary ENI",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
PrimaryInterfaceOnly: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("foobar"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
PublicIpAddress: aws.String("1.1.1.1"),
|
||||
PublicDnsName: aws.String("test.example.com"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
Attachment: &types.InstanceNetworkInterfaceAttachment{
|
||||
DeviceIndex: aws.Int32(0),
|
||||
},
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
Primary: aws.Bool(true),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
},
|
||||
// Secondary IPs on primary ENI (e.g. VPC CNI pod IPs).
|
||||
{
|
||||
Primary: aws.Bool(false),
|
||||
PrivateIpAddress: aws.String("10.0.0.42"),
|
||||
PrivateDnsName: aws.String("pod-a.example.internal"),
|
||||
},
|
||||
{
|
||||
Primary: aws.Bool(false),
|
||||
PrivateIpAddress: aws.String("10.0.0.43"),
|
||||
PrivateDnsName: aws.String("pod-b.example.internal"),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Secondary ENI attached by VPC CNI for additional pod IPs.
|
||||
{
|
||||
Attachment: &types.InstanceNetworkInterfaceAttachment{
|
||||
DeviceIndex: aws.Int32(1),
|
||||
},
|
||||
PrivateIpAddress: aws.String("10.0.0.99"),
|
||||
PrivateDnsName: aws.String("eni1.example.internal"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "foobar",
|
||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.42", "10.0.0.43"},
|
||||
DnsNames: []string{"test.example.internal", "test.example.com", "pod-a.example.internal", "pod-b.example.internal"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "primary_interface_only keeps IPv6 on primary ENI",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
PrimaryInterfaceOnly: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("foobar"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
// Secondary ENI listed first to verify ordering does not matter.
|
||||
{
|
||||
Attachment: &types.InstanceNetworkInterfaceAttachment{
|
||||
DeviceIndex: aws.Int32(1),
|
||||
},
|
||||
PrivateIpAddress: aws.String("10.0.0.99"),
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{
|
||||
{Ipv6Address: aws.String("2001:db8::2")},
|
||||
},
|
||||
},
|
||||
{
|
||||
Attachment: &types.InstanceNetworkInterfaceAttachment{
|
||||
DeviceIndex: aws.Int32(0),
|
||||
},
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{
|
||||
{Ipv6Address: aws.String("2001:db8::3")},
|
||||
{Ipv6Address: aws.String("2001:db8::4")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "foobar",
|
||||
IpAddresses: []string{"10.0.0.1", "2001:db8::3", "2001:db8::4"},
|
||||
DnsNames: []string{"test.example.internal"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "primary_interface_only without interfaces still returns top-level IPs",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
PrimaryInterfaceOnly: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("foobar"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
PublicIpAddress: aws.String("1.1.1.1"),
|
||||
PublicDnsName: aws.String("test.example.com"),
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "foobar",
|
||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1"},
|
||||
DnsNames: []string{"test.example.internal", "test.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_public_ips excludes public IPv4 sources but keeps IPv6",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludePublicIps: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"10.0.0.1", "2001:db8::1"},
|
||||
DnsNames: []string{"host.internal"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_private_ips excludes private IPv4 sources but keeps IPv6",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludePrivateIps: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{Association: &types.InstanceNetworkInterfaceAssociation{PublicIp: aws.String("1.2.3.4"), PublicDnsName: aws.String("host.public")}},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
|
||||
},
|
||||
{
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{Association: &types.InstanceNetworkInterfaceAssociation{PublicIp: aws.String("1.2.3.5"), PublicDnsName: aws.String("svc.public")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"1.2.3.4", "2001:db8::1", "1.2.3.5"},
|
||||
DnsNames: []string{"host.public", "svc.public"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_public_ips keeps IPv6 addresses",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludePublicIps: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("fd00::1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"10.0.0.1", "fd00::1"},
|
||||
DnsNames: []string{"host.internal"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_ipv6 omits IPv6 while preserving IPv4 and DNS",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"10.0.0.1", "1.2.3.4"},
|
||||
DnsNames: []string{"host.internal", "host.public"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_public_ips with exclude_ipv6 keeps only private IPv4 sources",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludePublicIps: true,
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
},
|
||||
},
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.2"),
|
||||
PrivateDnsName: aws.String("svc.internal"),
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"10.0.0.1", "10.0.0.2"},
|
||||
DnsNames: []string{"host.internal", "svc.internal"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_private_ips with exclude_ipv6 keeps only public IPv4 sources",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludePrivateIps: true,
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.5"),
|
||||
PublicDnsName: aws.String("svc.public"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("fd00::1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"1.2.3.4", "1.2.3.5"},
|
||||
DnsNames: []string{"host.public", "svc.public"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "primary_interface_only with exclude_ipv6 drops IPv6 from primary ENI",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
PrimaryInterfaceOnly: true,
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("foobar"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
PublicIpAddress: aws.String("1.1.1.1"),
|
||||
PublicDnsName: aws.String("test.example.com"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
Attachment: &types.InstanceNetworkInterfaceAttachment{
|
||||
DeviceIndex: aws.Int32(0),
|
||||
},
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("test.example.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.42"),
|
||||
PrivateDnsName: aws.String("pod-a.example.internal"),
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::3")}},
|
||||
},
|
||||
{
|
||||
Attachment: &types.InstanceNetworkInterfaceAttachment{
|
||||
DeviceIndex: aws.Int32(1),
|
||||
},
|
||||
PrivateIpAddress: aws.String("10.0.0.99"),
|
||||
PrivateDnsName: aws.String("eni1.example.internal"),
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::4")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "foobar",
|
||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.42"},
|
||||
DnsNames: []string{"test.example.internal", "test.example.com", "pod-a.example.internal"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_ipv6 on IPv6-only interface leaves no addresses",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{
|
||||
{Ipv6Address: aws.String("2001:db8::1")},
|
||||
{Ipv6Address: aws.String("fd00::1")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deduplicates repeated addresses and DNS names across instance and interfaces",
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{
|
||||
{Ipv6Address: aws.String("2001:db8::1")},
|
||||
{Ipv6Address: aws.String("2001:db8::1")},
|
||||
},
|
||||
},
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{
|
||||
{Ipv6Address: aws.String("2001:db8::1")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"10.0.0.1", "1.2.3.4", "2001:db8::1"},
|
||||
DnsNames: []string{"host.internal", "host.public"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "primary_interface_only skips interfaces without attachment metadata",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
PrimaryInterfaceOnly: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.99"),
|
||||
PrivateDnsName: aws.String("missing-attachment.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.99"),
|
||||
PrivateDnsName: aws.String("missing-attachment.internal"),
|
||||
},
|
||||
},
|
||||
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("2001:db8::99")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"10.0.0.1", "1.2.3.4"},
|
||||
DnsNames: []string{"host.internal", "host.public"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_private_ips removes private DNS names even when public addresses remain",
|
||||
catalogAttributes: &CatalogAttributes{
|
||||
ExcludePrivateIps: true,
|
||||
},
|
||||
instance: types.Instance{
|
||||
InstanceId: aws.String("i-1234"),
|
||||
PublicIpAddress: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||
PrivateDnsName: aws.String("host.internal"),
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.4"),
|
||||
PublicDnsName: aws.String("host.public"),
|
||||
},
|
||||
},
|
||||
{
|
||||
PrivateIpAddress: aws.String("10.0.0.2"),
|
||||
PrivateDnsName: aws.String("svc.internal"),
|
||||
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||
PublicIp: aws.String("1.2.3.5"),
|
||||
PublicDnsName: aws.String("svc.public"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &pb.ListHostsResponseHost{
|
||||
ExternalId: "i-1234",
|
||||
IpAddresses: []string{"1.2.3.4", "1.2.3.5"},
|
||||
DnsNames: []string{"host.public", "svc.public"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
actual, err := awsInstanceToHost(tc.instance)
|
||||
attrs := tc.catalogAttributes
|
||||
if attrs == nil {
|
||||
attrs = &CatalogAttributes{}
|
||||
}
|
||||
actual, err := awsInstanceToHost(tc.instance, attrs)
|
||||
if tc.expectedErr != "" {
|
||||
require.EqualError(err, tc.expectedErr)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
require.Equal(tc.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCatalogAttributesAddressSelection(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in *structpb.Struct
|
||||
expectedErr string
|
||||
expected *CatalogAttributes
|
||||
}{
|
||||
{
|
||||
name: "valid selectors",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"primary_interface_only": structpb.NewBoolValue(true),
|
||||
"exclude_public_ips": structpb.NewBoolValue(true),
|
||||
"exclude_ipv6": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
PrimaryInterfaceOnly: true,
|
||||
ExcludePublicIps: true,
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_private_ips can be combined with exclude_ipv6",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_private_ips": structpb.NewBoolValue(true),
|
||||
"exclude_ipv6": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
ExcludePrivateIps: true,
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_public_ips can be combined with exclude_ipv6",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_public_ips": structpb.NewBoolValue(true),
|
||||
"exclude_ipv6": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
ExcludePublicIps: true,
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_private_ips and exclude_public_ips can be combined when IPv6 remains enabled",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_private_ips": structpb.NewBoolValue(true),
|
||||
"exclude_public_ips": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
ExcludePrivateIps: true,
|
||||
ExcludePublicIps: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_private_ips and exclude_public_ips cannot also exclude_ipv6",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_private_ips": structpb.NewBoolValue(true),
|
||||
"exclude_public_ips": structpb.NewBoolValue(true),
|
||||
"exclude_ipv6": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expectedErr: "attributes.exclude_ipv6: cannot be combined with exclude_private_ips and exclude_public_ips, attributes.exclude_private_ips: cannot be combined with exclude_public_ips and exclude_ipv6, attributes.exclude_public_ips: cannot be combined with exclude_private_ips and exclude_ipv6",
|
||||
},
|
||||
{
|
||||
name: "exclude_ipv6 alone is accepted",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_ipv6": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
ExcludeIpv6: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude_ipv6 requires a boolean",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"exclude_ipv6": structpb.NewStringValue("true"),
|
||||
},
|
||||
},
|
||||
expectedErr: "attributes.exclude_ipv6",
|
||||
},
|
||||
{
|
||||
name: "no selectors configured preserves historical behavior",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
},
|
||||
},
|
||||
expected: &CatalogAttributes{
|
||||
CredentialAttributes: &credential.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
actual, err := getCatalogAttributes(tc.in)
|
||||
if tc.expectedErr != "" {
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), tc.expectedErr)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
require.Equal(tc.expected, actual)
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user