diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index 4cd29724b2c..a585e9b12a7 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -51,6 +51,7 @@ import ( "k8s.io/kubernetes/pkg/controller" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" "k8s.io/kubernetes/pkg/volume" + "path" ) // ProviderName is the name of this cloud provider. @@ -1010,11 +1011,27 @@ func (c *Cloud) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) { if c.selfAWSInstance.nodeName == name || len(name) == 0 { addresses := []v1.NodeAddress{} - internalIP, err := c.metadata.GetMetadata("local-ipv4") + macs, err := c.metadata.GetMetadata("network/interfaces/macs/") if err != nil { - return nil, fmt.Errorf("error querying AWS metadata for %q: %q", "local-ipv4", err) + return nil, fmt.Errorf("error querying AWS metadata for %q: %q", "network/interfaces/macs", err) + } + + for _, macID := range strings.Split(macs, "\n") { + if macID == "" { + continue + } + macPath := path.Join("network/interfaces/macs/", macID, "local-ipv4s") + internalIPs, err := c.metadata.GetMetadata(macPath) + if err != nil { + return nil, fmt.Errorf("error querying AWS metadata for %q: %q", macPath, err) + } + for _, internalIP := range strings.Split(internalIPs, "\n") { + if internalIP == "" { + continue + } + addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: internalIP}) + } } - addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: internalIP}) externalIP, err := c.metadata.GetMetadata("public-ipv4") if err != nil { @@ -1063,13 +1080,22 @@ func extractNodeAddresses(instance *ec2.Instance) ([]v1.NodeAddress, error) { addresses := []v1.NodeAddress{} - privateIPAddress := aws.StringValue(instance.PrivateIpAddress) - if privateIPAddress != "" { - ip := net.ParseIP(privateIPAddress) - if ip == nil { - return nil, fmt.Errorf("EC2 instance had invalid private address: %s (%s)", aws.StringValue(instance.InstanceId), privateIPAddress) + // handle internal network interfaces + for _, networkInterface := range instance.NetworkInterfaces { + // skip network interfaces that are not currently in use + if aws.StringValue(networkInterface.Status) != ec2.NetworkInterfaceStatusInUse { + continue + } + + for _, internalIP := range networkInterface.PrivateIpAddresses { + if ipAddress := aws.StringValue(internalIP.PrivateIpAddress); ipAddress != "" { + ip := net.ParseIP(ipAddress) + if ip == nil { + return nil, fmt.Errorf("EC2 instance had invalid private address: %s (%q)", aws.StringValue(instance.InstanceId), ipAddress) + } + addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ip.String()}) + } } - addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ip.String()}) } // TODO: Other IP addresses (multiple ips)? diff --git a/pkg/cloudprovider/providers/aws/aws_test.go b/pkg/cloudprovider/providers/aws/aws_test.go index 1fef442cc7e..c922ededaf4 100644 --- a/pkg/cloudprovider/providers/aws/aws_test.go +++ b/pkg/cloudprovider/providers/aws/aws_test.go @@ -114,11 +114,12 @@ func TestReadAWSCloudConfig(t *testing.T) { } type FakeAWSServices struct { - region string - instances []*ec2.Instance - selfInstance *ec2.Instance - networkInterfacesMacs []string - networkInterfacesVpcIDs []string + region string + instances []*ec2.Instance + selfInstance *ec2.Instance + networkInterfacesMacs []string + networkInterfacesPrivateIPs [][]string + networkInterfacesVpcIDs []string ec2 *FakeEC2 elb *FakeELB @@ -366,6 +367,13 @@ func (self *FakeMetadata) GetMetadata(key string) (string, error) { } } } + if len(keySplit) == 5 && keySplit[4] == "local-ipv4s" { + for i, macElem := range self.aws.networkInterfacesMacs { + if macParam == macElem { + return strings.Join(self.aws.networkInterfacesPrivateIPs[i], "/\n"), nil + } + } + } return "", nil } } else { @@ -569,6 +577,16 @@ func TestNodeAddresses(t *testing.T) { instance0.PrivateIpAddress = aws.String("192.168.0.1") instance0.PublicDnsName = aws.String("instance-same.ec2.external") instance0.PublicIpAddress = aws.String("1.2.3.4") + instance0.NetworkInterfaces = []*ec2.InstanceNetworkInterface{ + { + Status: aws.String(ec2.NetworkInterfaceStatusInUse), + PrivateIpAddresses: []*ec2.InstancePrivateIpAddress{ + { + PrivateIpAddress: aws.String("192.168.0.1"), + }, + }, + }, + } instance0.InstanceType = aws.String("c3.large") instance0.Placement = &ec2.Placement{AvailabilityZone: aws.String("us-east-1a")} state0 := ec2.InstanceState{ @@ -614,6 +632,8 @@ func TestNodeAddresses(t *testing.T) { } aws3, _ := mockInstancesResp(&instance0, instances[0:1]) + // change node name so it uses the instance instead of metadata + aws3.selfAWSInstance.nodeName = "foo" addrs3, err3 := aws3.NodeAddresses("instance-same.ec2.internal") if err3 != nil { t.Errorf("Should not error when instance found") @@ -625,18 +645,34 @@ func TestNodeAddresses(t *testing.T) { testHasNodeAddress(t, addrs3, v1.NodeExternalIP, "1.2.3.4") testHasNodeAddress(t, addrs3, v1.NodeExternalDNS, "instance-same.ec2.external") testHasNodeAddress(t, addrs3, v1.NodeInternalDNS, "instance-same.ec2.internal") +} - // Fetch from metadata - aws4, fakeServices := mockInstancesResp(&instance0, []*ec2.Instance{&instance0}) - fakeServices.selfInstance.PublicIpAddress = aws.String("2.3.4.5") - fakeServices.selfInstance.PrivateIpAddress = aws.String("192.168.0.2") +func TestNodeAddressesWithMetadata(t *testing.T) { + var instance ec2.Instance - addrs4, err4 := aws4.NodeAddresses(mapInstanceToNodeName(&instance0)) - if err4 != nil { - t.Errorf("unexpected error: %v", err4) + instanceName := "instance.ec2.internal" + instance.InstanceId = aws.String("i-0") + instance.PrivateDnsName = &instanceName + instance.PublicIpAddress = aws.String("2.3.4.5") + instance.InstanceType = aws.String("c3.large") + instance.Placement = &ec2.Placement{AvailabilityZone: aws.String("us-east-1a")} + state := ec2.InstanceState{ + Name: aws.String("running"), } - testHasNodeAddress(t, addrs4, v1.NodeInternalIP, "192.168.0.2") - testHasNodeAddress(t, addrs4, v1.NodeExternalIP, "2.3.4.5") + instance.State = &state + + instances := []*ec2.Instance{&instance} + awsCloud, awsServices := mockInstancesResp(&instance, instances) + + awsServices.networkInterfacesMacs = []string{"0a:26:89:f3:9c:f6", "0a:77:64:c4:6a:48"} + awsServices.networkInterfacesPrivateIPs = [][]string{{"192.168.0.1"}, {"192.168.0.2"}} + addrs, err := awsCloud.NodeAddresses("") + if err != nil { + t.Errorf("unexpected error: %v", err) + } + testHasNodeAddress(t, addrs, v1.NodeInternalIP, "192.168.0.1") + testHasNodeAddress(t, addrs, v1.NodeInternalIP, "192.168.0.2") + testHasNodeAddress(t, addrs, v1.NodeExternalIP, "2.3.4.5") } func TestGetRegion(t *testing.T) {