diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index c51e606175..624600714f 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -1351,14 +1351,17 @@ func (c *Cloud) NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.No addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: externalIP}) } - internalDNS, err := c.metadata.GetMetadata("local-hostname") - if err != nil || len(internalDNS) == 0 { + localHostname, err := c.metadata.GetMetadata("local-hostname") + if err != nil || len(localHostname) == 0 { //TODO: It would be nice to be able to determine the reason for the failure, // but the AWS client masks all failures with the same error description. klog.V(4).Info("Could not determine private DNS from AWS metadata.") } else { - addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalDNS, Address: internalDNS}) - addresses = append(addresses, v1.NodeAddress{Type: v1.NodeHostName, Address: internalDNS}) + hostname, internalDNS := parseMetadataLocalHostname(localHostname) + addresses = append(addresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname}) + for _, d := range internalDNS { + addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalDNS, Address: d}) + } } externalDNS, err := c.metadata.GetMetadata("public-hostname") @@ -1380,6 +1383,26 @@ func (c *Cloud) NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.No return extractNodeAddresses(instance) } +// parseMetadataLocalHostname parses the output of "local-hostname" metadata. +// If a DHCP option set is configured for a VPC and it has multiple domain names, GetMetadata +// returns a string containing first the hostname followed by additional domain names, +// space-separated. For example, if the DHCP option set has: +// domain-name = us-west-2.compute.internal a.a b.b c.c d.d; +// $ curl http://169.254.169.254/latest/meta-data/local-hostname +// ip-192-168-111-51.us-west-2.compute.internal a.a b.b c.c d.d +func parseMetadataLocalHostname(metadata string) (string, []string) { + localHostnames := strings.Fields(metadata) + hostname := localHostnames[0] + internalDNS := []string{hostname} + + privateAddress := strings.Split(hostname, ".")[0] + for _, h := range localHostnames[1:] { + internalDNSAddress := privateAddress + "." + h + internalDNS = append(internalDNS, internalDNSAddress) + } + return hostname, internalDNS +} + // extractNodeAddresses maps the instance information from EC2 to an array of NodeAddresses func extractNodeAddresses(instance *ec2.Instance) ([]v1.NodeAddress, error) { // Not clear if the order matters here, but we might as well indicate a sensible preference order diff --git a/pkg/cloudprovider/providers/aws/aws_test.go b/pkg/cloudprovider/providers/aws/aws_test.go index d37630e837..84db7aeba7 100644 --- a/pkg/cloudprovider/providers/aws/aws_test.go +++ b/pkg/cloudprovider/providers/aws/aws_test.go @@ -681,6 +681,41 @@ func TestNodeAddressesWithMetadata(t *testing.T) { testHasNodeAddress(t, addrs, v1.NodeExternalIP, "2.3.4.5") } +func TestParseMetadataLocalHostname(t *testing.T) { + tests := []struct { + name string + metadata string + hostname string + internalDNS []string + }{ + { + "single hostname", + "ip-172-31-16-168.us-west-2.compute.internal", + "ip-172-31-16-168.us-west-2.compute.internal", + []string{"ip-172-31-16-168.us-west-2.compute.internal"}, + }, + { + "dhcp options set with three additional domain names", + "ip-172-31-16-168.us-west-2.compute.internal example.com example.ca example.org", + "ip-172-31-16-168.us-west-2.compute.internal", + []string{"ip-172-31-16-168.us-west-2.compute.internal", "ip-172-31-16-168.example.com", "ip-172-31-16-168.example.ca", "ip-172-31-16-168.example.org"}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + hostname, internalDNS := parseMetadataLocalHostname(test.metadata) + if hostname != test.hostname { + t.Errorf("got hostname %v, expected %v", hostname, test.hostname) + } + for i, v := range internalDNS { + if v != test.internalDNS[i] { + t.Errorf("got an internalDNS %v, expected %v", v, test.internalDNS[i]) + } + } + }) + } +} + func TestGetRegion(t *testing.T) { aws := mockAvailabilityZone("us-west-2e") zones, ok := aws.Zones()