Compare commits
2
Commits
louis-instance
...
v0.6.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c8e95fcd8 | ||
|
|
01f8da94e4 |
No files matched your search
@@ -22,6 +22,11 @@ type CatalogAttributes struct {
|
|||||||
|
|
||||||
// DualStack is used for configuring how the aws client will resolve requests.
|
// DualStack is used for configuring how the aws client will resolve requests.
|
||||||
DualStack bool
|
DualStack bool
|
||||||
|
|
||||||
|
// InstanceAddressesOnly specifies whether only the instance addresses (private IPv4/DNS,
|
||||||
|
// public IPv4/DNS, and IPv6) are synced, or all addresses, including those from
|
||||||
|
// secondary ENIs, are synced.
|
||||||
|
InstanceAddressesOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
|
func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
|
||||||
@@ -40,6 +45,12 @@ func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
|
|||||||
}
|
}
|
||||||
delete(unknownFields, ConstAwsDualStack)
|
delete(unknownFields, ConstAwsDualStack)
|
||||||
|
|
||||||
|
instanceAddressesOnly, err := values.GetBoolValue(in, ConstInstanceAddressesOnly, false)
|
||||||
|
if err != nil {
|
||||||
|
badFields[fmt.Sprintf("attributes.%s", ConstInstanceAddressesOnly)] = err.Error()
|
||||||
|
}
|
||||||
|
delete(unknownFields, ConstInstanceAddressesOnly)
|
||||||
|
|
||||||
for s := range unknownFields {
|
for s := range unknownFields {
|
||||||
switch s {
|
switch s {
|
||||||
// Ignore knownFields from CredentialAttributes
|
// Ignore knownFields from CredentialAttributes
|
||||||
@@ -67,6 +78,7 @@ func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
|
|||||||
return &CatalogAttributes{
|
return &CatalogAttributes{
|
||||||
CredentialAttributes: credAttributes,
|
CredentialAttributes: credAttributes,
|
||||||
DualStack: dualStack,
|
DualStack: dualStack,
|
||||||
|
InstanceAddressesOnly: instanceAddressesOnly,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,22 @@ func TestGetCatalogAttributes(t *testing.T) {
|
|||||||
DualStack: true,
|
DualStack: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "withInstanceAddressesOnly",
|
||||||
|
in: &structpb.Struct{
|
||||||
|
Fields: map[string]*structpb.Value{
|
||||||
|
"region": structpb.NewStringValue("us-west-2"),
|
||||||
|
"instance_addresses_only": structpb.NewBoolValue(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &CatalogAttributes{
|
||||||
|
CredentialAttributes: &credential.CredentialAttributes{
|
||||||
|
Region: "us-west-2",
|
||||||
|
DisableCredentialRotation: false,
|
||||||
|
},
|
||||||
|
InstanceAddressesOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "default",
|
name: "default",
|
||||||
in: &structpb.Struct{
|
in: &structpb.Struct{
|
||||||
|
|||||||
@@ -13,4 +13,9 @@ const (
|
|||||||
|
|
||||||
// ConstAwsDualStack is the key for the dualstack flag in the aws s3 client.
|
// ConstAwsDualStack is the key for the dualstack flag in the aws s3 client.
|
||||||
ConstAwsDualStack = "dual_stack"
|
ConstAwsDualStack = "dual_stack"
|
||||||
|
|
||||||
|
// ConstInstanceAddressesOnly is the key (instance_addresses_only) that specifies whether
|
||||||
|
// only the instance addresses (private IPv4/DNS, public IPv4/DNS, and IPv6) are synced,
|
||||||
|
// or all addresses, including those from secondary ENIs, are synced.
|
||||||
|
ConstInstanceAddressesOnly = "instance_addresses_only"
|
||||||
)
|
)
|
||||||
@@ -550,7 +550,7 @@ func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*
|
|||||||
// set of hosts afterwards (possibly removing duplicates).
|
// set of hosts afterwards (possibly removing duplicates).
|
||||||
for _, reservation := range output.Reservations {
|
for _, reservation := range output.Reservations {
|
||||||
for _, instance := range reservation.Instances {
|
for _, instance := range reservation.Instances {
|
||||||
host, err := awsInstanceToHost(instance)
|
host, err := awsInstanceToHost(instance, catalogAttributes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.BadRequestStatusf("error processing host results for host set id %q: %s", query.Id, err)
|
return nil, errors.BadRequestStatusf("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
|
// awsInstanceToHost processes data from an ec2.Instance and returns
|
||||||
// a ListHostsResponseHost with the ID and network addresses
|
// a ListHostsResponseHost with the ID and network addresses
|
||||||
// populated.
|
// populated.
|
||||||
func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, error) {
|
func awsInstanceToHost(instance types.Instance, catalogAttributes *CatalogAttributes) (*pb.ListHostsResponseHost, error) {
|
||||||
if aws.ToString(instance.InstanceId) == "" {
|
if aws.ToString(instance.InstanceId) == "" {
|
||||||
return nil, fmt.Errorf("response integrity error: missing instance id")
|
return nil, fmt.Errorf("response integrity error: missing instance id")
|
||||||
}
|
}
|
||||||
|
if catalogAttributes == nil {
|
||||||
|
return nil, fmt.Errorf("response integrity error: missing catalog attributes")
|
||||||
|
}
|
||||||
|
|
||||||
result := new(pb.ListHostsResponseHost)
|
result := new(pb.ListHostsResponseHost)
|
||||||
|
|
||||||
@@ -666,10 +669,16 @@ func awsInstanceToHost(instance types.Instance) (*pb.ListHostsResponseHost, erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PrivateIpAddress, instance.PublicIpAddress)
|
// First let's get the instance's Private IPv4/DNS, Public IPv4/DNS, and IPv6 addresses
|
||||||
|
result.IpAddresses = appendDistinct(result.IpAddresses, instance.PrivateIpAddress, instance.PublicIpAddress, instance.Ipv6Address)
|
||||||
result.DnsNames = appendDistinct(result.DnsNames, instance.PrivateDnsName, instance.PublicDnsName)
|
result.DnsNames = appendDistinct(result.DnsNames, instance.PrivateDnsName, instance.PublicDnsName)
|
||||||
|
|
||||||
// Now go through all of the interfaces and log the IP address of
|
if catalogAttributes.InstanceAddressesOnly {
|
||||||
|
// return early as we already have the instance addresses
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now go through all of the interfaces and sync the IP address of
|
||||||
// every interface.
|
// every interface.
|
||||||
for _, iface := range instance.NetworkInterfaces {
|
for _, iface := range instance.NetworkInterfaces {
|
||||||
// Populate default IP addresses/DNS name similar to how we do
|
// Populate default IP addresses/DNS name similar to how we do
|
||||||
|
|||||||
@@ -638,7 +638,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
|||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||||
Attributes: &structpb.Struct{
|
Attributes: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
@@ -664,7 +665,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
|||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||||
Attributes: &structpb.Struct{
|
Attributes: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
@@ -694,7 +696,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
|||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||||
Attributes: &structpb.Struct{
|
Attributes: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
@@ -721,7 +724,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
|||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||||
Attributes: &structpb.Struct{
|
Attributes: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
@@ -2385,14 +2389,24 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
instance types.Instance
|
instance types.Instance
|
||||||
|
catalogAttrs *CatalogAttributes
|
||||||
expected *pb.ListHostsResponseHost
|
expected *pb.ListHostsResponseHost
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing instance id",
|
name: "missing instance id",
|
||||||
instance: types.Instance{},
|
instance: types.Instance{},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expectedErr: "response integrity error: missing instance id",
|
expectedErr: "response integrity error: missing instance id",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "missing catalog attributes",
|
||||||
|
instance: types.Instance{
|
||||||
|
InstanceId: aws.String("foobar"),
|
||||||
|
},
|
||||||
|
catalogAttrs: nil,
|
||||||
|
expectedErr: "response integrity error: missing catalog attributes",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "good, single IP w/public addr",
|
name: "good, single IP w/public addr",
|
||||||
instance: types.Instance{
|
instance: types.Instance{
|
||||||
@@ -2418,6 +2432,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
ExternalId: "foobar",
|
||||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1"},
|
IpAddresses: []string{"10.0.0.1", "1.1.1.1"},
|
||||||
@@ -2443,6 +2458,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
ExternalId: "foobar",
|
||||||
IpAddresses: []string{"10.0.0.1"},
|
IpAddresses: []string{"10.0.0.1"},
|
||||||
@@ -2484,6 +2500,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
ExternalId: "foobar",
|
||||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.2"},
|
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.2"},
|
||||||
@@ -2529,6 +2546,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
ExternalId: "foobar",
|
||||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.2", "1.1.1.2"},
|
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.2", "1.1.1.2"},
|
||||||
@@ -2564,6 +2582,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
ExternalId: "foobar",
|
||||||
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.2"},
|
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "10.0.0.2"},
|
||||||
@@ -2578,6 +2597,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
PrivateDnsName: aws.String("test.example.internal"),
|
PrivateDnsName: aws.String("test.example.internal"),
|
||||||
PublicIpAddress: aws.String("1.1.1.1"),
|
PublicIpAddress: aws.String("1.1.1.1"),
|
||||||
PublicDnsName: aws.String("test.example.com"),
|
PublicDnsName: aws.String("test.example.com"),
|
||||||
|
Ipv6Address: aws.String("instance::ipv6::address"),
|
||||||
NetworkInterfaces: []types.InstanceNetworkInterface{
|
NetworkInterfaces: []types.InstanceNetworkInterface{
|
||||||
{
|
{
|
||||||
PrivateIpAddress: aws.String("10.0.0.1"),
|
PrivateIpAddress: aws.String("10.0.0.1"),
|
||||||
@@ -2596,9 +2616,63 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
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", "instance::ipv6::address", "some::fake::address"},
|
||||||
|
DnsNames: []string{"test.example.internal", "test.example.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "good, instance addresses only excludes interface secondary addresses",
|
||||||
|
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"),
|
||||||
|
Ipv6Address: aws.String("instance::ipv6::address"),
|
||||||
|
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"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PrivateIpAddress: aws.String("10.0.0.2"),
|
||||||
|
PrivateDnsName: aws.String("test2.example.internal"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("iface::ipv6::address")}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PrivateIpAddress: aws.String("10.0.1.1"),
|
||||||
|
PrivateDnsName: aws.String("secondary.example.internal"),
|
||||||
|
PrivateIpAddresses: []types.InstancePrivateIpAddress{
|
||||||
|
{
|
||||||
|
Association: &types.InstanceNetworkInterfaceAssociation{
|
||||||
|
PublicIp: aws.String("2.2.2.2"),
|
||||||
|
PublicDnsName: aws.String("secondary.example.com"),
|
||||||
|
},
|
||||||
|
PrivateIpAddress: aws.String("10.0.1.1"),
|
||||||
|
PrivateDnsName: aws.String("secondary.example.internal"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Ipv6Addresses: []types.InstanceIpv6Address{{Ipv6Address: aws.String("secondary::ipv6::address")}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{InstanceAddressesOnly: true},
|
||||||
|
expected: &pb.ListHostsResponseHost{
|
||||||
|
ExternalId: "foobar",
|
||||||
|
IpAddresses: []string{"10.0.0.1", "1.1.1.1", "instance::ipv6::address"},
|
||||||
DnsNames: []string{"test.example.internal", "test.example.com"},
|
DnsNames: []string{"test.example.internal", "test.example.com"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -2632,6 +2706,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
ExternalId: "foobar",
|
||||||
ExternalName: "test-instance-actual-name",
|
ExternalName: "test-instance-actual-name",
|
||||||
@@ -2669,6 +2744,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
catalogAttrs: &CatalogAttributes{},
|
||||||
expected: &pb.ListHostsResponseHost{
|
expected: &pb.ListHostsResponseHost{
|
||||||
ExternalId: "foobar",
|
ExternalId: "foobar",
|
||||||
ExternalName: "test-instance-actual-name",
|
ExternalName: "test-instance-actual-name",
|
||||||
@@ -2682,7 +2758,7 @@ func TestAwsInstanceToHost(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
actual, err := awsInstanceToHost(tc.instance)
|
actual, err := awsInstanceToHost(tc.instance, tc.catalogAttrs)
|
||||||
if tc.expectedErr != "" {
|
if tc.expectedErr != "" {
|
||||||
require.EqualError(err, tc.expectedErr)
|
require.EqualError(err, tc.expectedErr)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ const (
|
|||||||
ConstAwsDualStack = "dual_stack"
|
ConstAwsDualStack = "dual_stack"
|
||||||
|
|
||||||
// defaultStreamChunkSize is the recommened chunk size for sending data through a stream
|
// defaultStreamChunkSize is the recommened chunk size for sending data through a stream
|
||||||
defaultStreamChunkSize = 65536 // 64 KiB
|
defaultStreamChunkSize = 1 << 20 // 1 MiB
|
||||||
)
|
)
|
||||||
@@ -7,9 +7,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/aws/retry"
|
||||||
|
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
smithyEndpoints "github.com/aws/smithy-go/endpoints"
|
smithyEndpoints "github.com/aws/smithy-go/endpoints"
|
||||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||||
@@ -118,7 +122,10 @@ func (s *awsStoragePersistedState) s3Client(ctx context.Context, opt ...s3Option
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve credentials: %v\n", err)
|
return nil, fmt.Errorf("failed to retrieve credentials: %v\n", err)
|
||||||
}
|
}
|
||||||
|
err = newTransport(awsCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to configure new http transport: %v", err)
|
||||||
|
}
|
||||||
c := s3.NewFromConfig(*awsCfg, s3Opts...)
|
c := s3.NewFromConfig(*awsCfg, s3Opts...)
|
||||||
return &s3Client{Client: c, creds: creds}, nil
|
return &s3Client{Client: c, creds: creds}, nil
|
||||||
}
|
}
|
||||||
@@ -202,3 +209,30 @@ func (e *endpointResolver) ResolveEndpoint(ctx context.Context, params s3.Endpoi
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newTransport returns an http Transport object. The http transport object utilizes the
|
||||||
|
// default transport created by minio. The following transport fields are updated to provide
|
||||||
|
// more reliable connectivity when handling larger objects:
|
||||||
|
// MaxResponseHeaderBytes is set to 1MiB to prevent header reads from stalling.
|
||||||
|
// WriteBufferSize is set to 1MiB, matching the default buffer size used in Boundary storage.
|
||||||
|
// ReadBufferSize is set to 1MiB, matching the default buffer size used in Boundary storage.
|
||||||
|
// ForceAttemptHTTP2 is set to true, supporting more resilient multiplexing and avoids HOL blocking on retries.
|
||||||
|
// IdleConnTimeout is set to 2 minutes.
|
||||||
|
func newTransport(cfg *aws.Config) error {
|
||||||
|
if cfg == nil {
|
||||||
|
return fmt.Errorf("missing aws configuration")
|
||||||
|
}
|
||||||
|
cfg.HTTPClient = awshttp.NewBuildableClient().WithTransportOptions(func(transport *http.Transport) {
|
||||||
|
transport.MaxResponseHeaderBytes = 1 << 20 // 1 MiB prevents header read from stalling
|
||||||
|
transport.WriteBufferSize = 1 << 20 // 1 MiB write buffer (default is 4 KiB)
|
||||||
|
transport.ReadBufferSize = 1 << 20 // 1 MiB read buffer (default is 4 KiB)
|
||||||
|
transport.ForceAttemptHTTP2 = true // enables HTTP/2 where supported more resilient multiplexing, avoids HOL blocking on retries
|
||||||
|
transport.IdleConnTimeout = 2 * time.Minute // double the idle connection timeout, default is 1 minute
|
||||||
|
})
|
||||||
|
cfg.Retryer = func() aws.Retryer {
|
||||||
|
return retry.NewStandard(func(o *retry.StandardOptions) {
|
||||||
|
o.MaxAttempts = 20
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -165,6 +165,9 @@ func TestHostPlugin(t *testing.T) {
|
|||||||
testPluginListHosts(ctx, t, p, region, keyid, secret, tc, expectedTagInstancesMap)
|
testPluginListHosts(ctx, t, p, region, keyid, secret, tc, expectedTagInstancesMap)
|
||||||
// TODO: add OnDeleteSet if it needs to be implemented
|
// TODO: add OnDeleteSet if it needs to be implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finally lets test instance_addresses_only catalog attribute correctly syncs only instance addresses
|
||||||
|
testInstanceAddressesOnlyListHosts(ctx, t, p, tf, region, keyid, secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPluginOnCreateCatalog(ctx context.Context, t *testing.T, p *host.HostPlugin, region, accessKeyId, secretAccessKey string, rotate bool) (string, string) {
|
func testPluginOnCreateCatalog(ctx context.Context, t *testing.T, p *host.HostPlugin, region, accessKeyId, secretAccessKey string, rotate bool) (string, string) {
|
||||||
@@ -450,3 +453,92 @@ func testPluginListHosts(ctx context.Context, t *testing.T, p *host.HostPlugin,
|
|||||||
// Success
|
// Success
|
||||||
t.Logf("testing ListHosts: success (region=%s, tags=%v, expected/actual=(len=%d, ids=%s))", region, tags, len(actualInstances), actualInstances)
|
t.Logf("testing ListHosts: success (region=%s, tags=%v, expected/actual=(len=%d, ids=%s))", region, tags, len(actualInstances), actualInstances)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testInstanceAddressesOnlyListHosts(ctx context.Context, t *testing.T, p *host.HostPlugin, tf *TestTerraformer, region, accessKeyId, secretAccessKey string) {
|
||||||
|
t.Helper()
|
||||||
|
t.Logf("testing testInstanceAddressesOnlyListHosts")
|
||||||
|
|
||||||
|
// Get the instance details from tf output
|
||||||
|
instanceID, err := tf.GetOutputString("multi_address_instance_id")
|
||||||
|
require.NoError(t, err)
|
||||||
|
primaryAddresses, err := tf.GetOutputMap("multi_address_primary_addresses")
|
||||||
|
require.NoError(t, err)
|
||||||
|
secondaryAddresses, err := tf.GetOutputMap("multi_address_secondary_addresses")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var primary, allAddresses []string
|
||||||
|
for _, addr := range primaryAddresses {
|
||||||
|
primary = append(primary, addr.(string))
|
||||||
|
}
|
||||||
|
allAddresses = append(allAddresses, primary...)
|
||||||
|
for _, addr := range secondaryAddresses {
|
||||||
|
allAddresses = append(allAddresses, addr.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
instanceAddressesOnly bool
|
||||||
|
expectedIPs []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all addresses",
|
||||||
|
expectedIPs: allAddresses,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "instance addresses only",
|
||||||
|
instanceAddressesOnly: true,
|
||||||
|
expectedIPs: primary,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
catalogAttrMap := map[string]any{
|
||||||
|
credential.ConstRegion: region,
|
||||||
|
credential.ConstDisableCredentialRotation: true,
|
||||||
|
}
|
||||||
|
if tc.instanceAddressesOnly {
|
||||||
|
catalogAttrMap[host.ConstInstanceAddressesOnly] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
catalogAttrs, err := structpb.NewStruct(catalogAttrMap)
|
||||||
|
require.NoError(err)
|
||||||
|
setAttrs, err := structpb.NewStruct(map[string]any{
|
||||||
|
host.ConstDescribeInstancesFilters: []any{fmt.Sprintf("instance-id=%s", instanceID)},
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
reqPersistedSecrets, err := structpb.NewStruct(map[string]any{
|
||||||
|
credential.ConstAccessKeyId: accessKeyId,
|
||||||
|
credential.ConstSecretAccessKey: secretAccessKey,
|
||||||
|
credential.ConstCredsLastRotatedTime: (time.Time{}).Format(time.RFC3339Nano),
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
request := &pb.ListHostsRequest{
|
||||||
|
Catalog: &hostcatalogs.HostCatalog{
|
||||||
|
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||||
|
Attributes: catalogAttrs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Sets: []*hostsets.HostSet{{
|
||||||
|
Id: "hostset-0",
|
||||||
|
Attrs: &hostsets.HostSet_Attributes{Attributes: setAttrs},
|
||||||
|
}},
|
||||||
|
Persisted: &pb.HostCatalogPersisted{
|
||||||
|
Secrets: reqPersistedSecrets,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := p.ListHosts(ctx, request)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(response.GetHosts(), 1)
|
||||||
|
hostResp := response.GetHosts()[0]
|
||||||
|
|
||||||
|
require.Equal(instanceID, hostResp.ExternalId)
|
||||||
|
require.ElementsMatch(hostResp.IpAddresses, tc.expectedIPs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+117
@@ -21,12 +21,40 @@ data "aws_ami" "ubuntu" {
|
|||||||
|
|
||||||
resource "aws_vpc" "vpc" {
|
resource "aws_vpc" "vpc" {
|
||||||
cidr_block = "10.0.0.0/16"
|
cidr_block = "10.0.0.0/16"
|
||||||
|
assign_generated_ipv6_cidr_block = true
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_subnet" "subnet" {
|
resource "aws_subnet" "subnet" {
|
||||||
vpc_id = aws_vpc.vpc.id
|
vpc_id = aws_vpc.vpc.id
|
||||||
cidr_block = aws_vpc.vpc.cidr_block
|
cidr_block = aws_vpc.vpc.cidr_block
|
||||||
|
ipv6_cidr_block = cidrsubnet(aws_vpc.vpc.ipv6_cidr_block, 8, 0)
|
||||||
availability_zone = data.aws_availability_zones.azs.names[0]
|
availability_zone = data.aws_availability_zones.azs.names[0]
|
||||||
|
map_public_ip_on_launch = true
|
||||||
|
assign_ipv6_address_on_creation = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Need to add a gateway for public IP addr assignments
|
||||||
|
resource "aws_internet_gateway" "igw" {
|
||||||
|
vpc_id = aws_vpc.vpc.id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table" "public" {
|
||||||
|
vpc_id = aws_vpc.vpc.id
|
||||||
|
|
||||||
|
route {
|
||||||
|
cidr_block = "0.0.0.0/0"
|
||||||
|
gateway_id = aws_internet_gateway.igw.id
|
||||||
|
}
|
||||||
|
|
||||||
|
route {
|
||||||
|
ipv6_cidr_block = "::/0"
|
||||||
|
gateway_id = aws_internet_gateway.igw.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table_association" "public" {
|
||||||
|
subnet_id = aws_subnet.subnet.id
|
||||||
|
route_table_id = aws_route_table.public.id
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_instance" "instances" {
|
resource "aws_instance" "instances" {
|
||||||
@@ -38,6 +66,73 @@ resource "aws_instance" "instances" {
|
|||||||
tags = local.instance_tags[count.index]
|
tags = local.instance_tags[count.index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "multi_address_instance" {
|
||||||
|
ami = data.aws_ami.ubuntu.id
|
||||||
|
instance_type = "t3.nano"
|
||||||
|
subnet_id = aws_subnet.subnet.id
|
||||||
|
secondary_private_ips = ["10.0.10.10"]
|
||||||
|
ipv6_address_count = 1
|
||||||
|
associate_public_ip_address = true
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "boundary-multi-address-instance"
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "multi_address_secondary_eni" {
|
||||||
|
subnet_id = aws_subnet.subnet.id
|
||||||
|
private_ips = ["10.0.20.10"]
|
||||||
|
ipv6_addresses = [cidrhost(aws_subnet.subnet.ipv6_cidr_block, 32)]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "boundary-multi-address-secondary-eni"
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface_attachment" "multi_address_secondary_eni" {
|
||||||
|
instance_id = aws_instance.multi_address_instance.id
|
||||||
|
network_interface_id = aws_network_interface.multi_address_secondary_eni.id
|
||||||
|
device_index = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_eip" "multi_address_primary" {
|
||||||
|
domain = "vpc"
|
||||||
|
network_interface = aws_instance.multi_address_instance.primary_network_interface_id
|
||||||
|
associate_with_private_ip = aws_instance.multi_address_instance.private_ip
|
||||||
|
depends_on = [aws_internet_gateway.igw, aws_route_table_association.public]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "boundary-multi-address-primary-eip"
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_eip" "multi_address_primary_secondary_private" {
|
||||||
|
domain = "vpc"
|
||||||
|
network_interface = aws_instance.multi_address_instance.primary_network_interface_id
|
||||||
|
associate_with_private_ip = "10.0.10.10"
|
||||||
|
depends_on = [aws_internet_gateway.igw, aws_route_table_association.public, aws_instance.multi_address_instance]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "boundary-multi-address-primary-secondary-eip"
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_eip" "multi_address_secondary_eni" {
|
||||||
|
domain = "vpc"
|
||||||
|
network_interface = aws_network_interface.multi_address_secondary_eni.id
|
||||||
|
associate_with_private_ip = "10.0.20.10"
|
||||||
|
depends_on = [aws_internet_gateway.igw, aws_route_table_association.public, aws_network_interface_attachment.multi_address_secondary_eni]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "boundary-multi-address-secondary-eni-eip"
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
output "instance_ids" {
|
output "instance_ids" {
|
||||||
value = aws_instance.instances.*.id
|
value = aws_instance.instances.*.id
|
||||||
}
|
}
|
||||||
@@ -61,3 +156,25 @@ output "instance_tag_keys" {
|
|||||||
random_id.baz_key.dec,
|
random_id.baz_key.dec,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output "multi_address_instance_id" {
|
||||||
|
value = aws_instance.multi_address_instance.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "multi_address_primary_addresses" {
|
||||||
|
value = {
|
||||||
|
private_ip = aws_instance.multi_address_instance.private_ip
|
||||||
|
public_ip = aws_eip.multi_address_primary.public_ip
|
||||||
|
ipv6 = tolist(aws_instance.multi_address_instance.ipv6_addresses)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "multi_address_secondary_addresses" {
|
||||||
|
value = {
|
||||||
|
primary_eni_private_ip = "10.0.10.10"
|
||||||
|
primary_eni_public_ip = aws_eip.multi_address_primary_secondary_private.public_ip
|
||||||
|
secondary_eni_private_ip = aws_network_interface.multi_address_secondary_eni.private_ip
|
||||||
|
secondary_eni_public_ip = aws_eip.multi_address_secondary_eni.public_ip
|
||||||
|
secondary_eni_ipv6 = tolist(aws_network_interface.multi_address_secondary_eni.ipv6_addresses)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user