Merge pull request #50112 from jlz27/multiple-ips

Automatic merge from submit-queue (batch tested with PRs 51301, 50497, 50112, 48184, 50993)

AWS: handle multiple IPs when using more than 1 network interface per ec2 instance

**What this PR does / why we need it**:
Adds support for kubelets running with the AWS cloud provider on ec2 instances with multiple network interfaces. If the active interface is not eth0, the AWS cloud provider currently reports the wrong node IP.

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #44686

**Special notes for your reviewer**:
There is also some work necessary for handling multiple DNS names and such but I didn't fix them in this PR.

**Release note**:

```release-note
Fixed bug in AWS provider to handle multiple IPs when using more than 1 network interface per ec2 instance.
```
This commit is contained in:
Kubernetes Submit Queue 2017-09-02 23:50:03 -07:00 committed by GitHub
commit 9341f22bb6
2 changed files with 85 additions and 23 deletions

View File

@ -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})
}
}
externalIP, err := c.metadata.GetMetadata("public-ipv4")
if err != nil {
@ -1063,14 +1080,23 @@ func extractNodeAddresses(instance *ec2.Instance) ([]v1.NodeAddress, error) {
addresses := []v1.NodeAddress{}
privateIPAddress := aws.StringValue(instance.PrivateIpAddress)
if privateIPAddress != "" {
ip := net.ParseIP(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 (%s)", aws.StringValue(instance.InstanceId), privateIPAddress)
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()})
}
}
}
// TODO: Other IP addresses (multiple ips)?
publicIPAddress := aws.StringValue(instance.PublicIpAddress)

View File

@ -118,6 +118,7 @@ type FakeAWSServices struct {
instances []*ec2.Instance
selfInstance *ec2.Instance
networkInterfacesMacs []string
networkInterfacesPrivateIPs [][]string
networkInterfacesVpcIDs []string
ec2 *FakeEC2
@ -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")
addrs4, err4 := aws4.NodeAddresses(mapInstanceToNodeName(&instance0))
if err4 != nil {
t.Errorf("unexpected error: %v", err4)
}
testHasNodeAddress(t, addrs4, v1.NodeInternalIP, "192.168.0.2")
testHasNodeAddress(t, addrs4, v1.NodeExternalIP, "2.3.4.5")
func TestNodeAddressesWithMetadata(t *testing.T) {
var instance ec2.Instance
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"),
}
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) {