Change:
<!-- This form is for bug reports and feature requests ONLY! If you're looking for help check and the . If this may be security issue, please disclose it privately via --Is this a BUG REPORT or FEATURE REQUEST?: /kind bug What happened: When using the flag on the kubelet, the node's list no longer reports , , addresses. What you expected to happen: That I can explicitly set to an IP, but the other addresses will still show up. How to reproduce it (as minimally and precisely as possible): Create a new kops cluster, modify the kubelet to use the flag with the eth0 on the node. Run Anything else we need to know?: This change will probably take place in  This came up as a result of Environment: Kubernetes version (use ): 1.9.x Cloud provider or hardware configuration: AWS OS (e.g. from /etc/os-release): any Kernel (e.g. ): any Install tools: kops /sig network /sig node I'll make a PR for this change shortly

Discussion:
glog.Errorf("Specified Node IP not found in cloudprovider") return } <del> nodeAddresses = []v1.NodeAddress{*nodeIP} </del> } newNode := node.DeepCopy() newNode.Status.Addresses = nodeAddresses

assert.Equal(t, 1, len(fnh.UpdatedNodes), "Node was not updated") assert.Equal(t, "node0", fnh.UpdatedNodes[0].Name, "Node was not updated") <del> assert.Equal(t, 1, len(fnh.UpdatedNodes[0].Status.Addresses), "Node status unexpectedly updated") </del> <ins> assert.Equal(t, 3, len(fnh.UpdatedNodes[0].Status.Addresses), "Node status unexpectedly updated") </ins> cloudNodeController.Run()

updatedNodes := fnh.GetUpdatedNodesCopy() <del> assert.Equal(t, 1, len(updatedNodes[0].Status.Addresses), 1, "Node Addresses not correctly updated") </del> <ins> assert.Equal(t, 3, len(updatedNodes[0].Status.Addresses), "Node Addresses not correctly updated") </ins> assert.Equal(t, "10.0.0.1", updatedNodes[0].Status.Addresses[0].Address, "Node Addresses not correctly updated") }

const ( // When kubelet is started with the "external" cloud provider, then // it sets this annotation on the node to denote an ip address set from the <del> // cmd line flag. This ip is verified with the cloudprovider as valid by </del> <ins> // cmd line flag (--node-ip). This ip is verified with the cloudprovider as valid by </ins> // the cloud-controller-manager AnnotationProvidedIPAddr = "alpha.kubernetes.io/provided-node-ip" )

containerManager:     kubeDeps.ContainerManager, containerRuntimeName: containerRuntime, nodeIP:               parsedNodeIP, <ins> nodeIPValidator:      validateNodeIP, </ins> clock:                clock.RealClock{}, enableControllerAttachDetach:            kubeCfg.EnableControllerAttachDetach, iptClient:                               utilipt.New(utilexec.New(), utildbus.New(), utilipt.ProtocolIpv4),

// If non-nil, use this IP address for the node nodeIP net.IP <ins> // use this function to validate the kubelet nodeIP nodeIPValidator func(net.IP) error </ins> // If non-nil, this is a unique identifier for the node in an external database, eg. cloudprovider providerID string

// Set IP and hostname addresses for the node. func (kl *Kubelet) setNodeAddress(node *v1.Node) error { if kl.nodeIP != nil { <del> if err := validateNodeIP(kl.nodeIP); err != nil { </del> <ins> if err := kl.nodeIPValidator(kl.nodeIP); err != nil { </ins> return fmt.Errorf("failed to validate nodeIP: %v", err) } glog.V(2).Infof("Using node IP: %q", kl.nodeIP.String())

} if kl.nodeIP != nil { enforcedNodeAddresses := []v1.NodeAddress{} <ins> var nodeIPType v1.NodeAddressType </ins> for _, nodeAddress := range nodeAddresses { if nodeAddress.Address == kl.nodeIP.String() { enforcedNodeAddresses = append(enforcedNodeAddresses, v1.NodeAddress{Type: nodeAddress.Type, Address: nodeAddress.Address}) <ins> nodeIPType = nodeAddress.Type break </ins> } } if len(enforcedNodeAddresses) > 0 { <ins> for _, nodeAddress := range nodeAddresses { if nodeAddress.Type != nodeIPType && nodeAddress.Type != v1.NodeHostName { enforcedNodeAddresses = append(enforcedNodeAddresses, v1.NodeAddress{Type: nodeAddress.Type, Address: nodeAddress.Address}) } } </ins> enforcedNodeAddresses = append(enforcedNodeAddresses, v1.NodeAddress{Type: v1.NodeHostName, Address: kl.GetHostname()}) node.Status.Addresses = enforcedNodeAddresses return nil

var addrs []net.IP addrs, _ = net.LookupIP(node.Name) for _, addr := range addrs { <del> if err = validateNodeIP(addr); err == nil { </del> <ins> if err = kl.nodeIPValidator(addr); err == nil { </ins> if addr.To4() != nil { ipAddr = addr break

kubelet.kubeClient = nil // ensure only the heartbeat client is used kubelet.hostname = testKubeletHostname <del> existingNode := v1.Node{ ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname, Annotations: make(map[string]string)}, Spec:       v1.NodeSpec{}, } // TODO : is it possible to mock validateNodeIP() to avoid relying on the host interface addresses ? addrs, err := net.InterfaceAddrs() assert.NoError(t, err) for _, addr := range addrs { var ip net.IP switch v := addr.(type) { case *net.IPNet: ip = v.IP case *net.IPAddr: ip = v.IP } if ip != nil && !ip.IsLoopback() && ip.To4() != nil { kubelet.nodeIP = ip break } } assert.NotNil(t, kubelet.nodeIP) fakeCloud := &fakecloud.FakeCloud{ Addresses: []v1.NodeAddress{ { Type:    v1.NodeExternalIP, Address: "132.143.154.163", </del> <ins> cases := []struct { name              string nodeIP            net.IP nodeAddresses     []v1.NodeAddress expectedAddresses []v1.NodeAddress shouldError       bool }{ { name:   "A single InternalIP", nodeIP: net.ParseIP("10.1.1.1"), nodeAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, </ins> }, <del> { Type:    v1.NodeExternalIP, Address: kubelet.nodeIP.String(), </del> <ins> expectedAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, </ins> }, <del> { Type:    v1.NodeInternalIP, Address: "132.143.154.164", </del> <ins> shouldError: false, }, { name:   "NodeIP is external", nodeIP: net.ParseIP("55.55.55.55"), nodeAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, </ins> }, <del> { Type:    v1.NodeInternalIP, Address: kubelet.nodeIP.String(), </del> <ins> expectedAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, </ins> }, <del> { Type:    v1.NodeInternalIP, Address: "132.143.154.165", </del> <ins> shouldError: false, }, { // Accommodating #45201 and #49202 name:   "InternalIP and ExternalIP are the same", nodeIP: net.ParseIP("55.55.55.55"), nodeAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "55.55.55.55"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, </ins> }, <del> { Type:    v1.NodeHostName, Address: testKubeletHostname, </del> <ins> expectedAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "55.55.55.55"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, </ins> }, <ins> shouldError: false, </ins> }, <del> Err: nil, } kubelet.cloud = fakeCloud kubelet.cloudproviderRequestParallelism = make(chan int, 1) kubelet.cloudproviderRequestSync = make(chan int) kubelet.cloudproviderRequestTimeout = 10 * time.Second kubelet.setNodeAddress(&existingNode) expectedAddresses := []v1.NodeAddress{ </del> { <del> Type:    v1.NodeExternalIP, Address: kubelet.nodeIP.String(), </del> <ins> name:   "An Internal/ExternalIP, an Internal/ExternalDNS", nodeIP: net.ParseIP("10.1.1.1"), nodeAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeInternalDNS, Address: "ip-10-1-1-1.us-west-2.compute.internal"}, {Type: v1.NodeExternalDNS, Address: "ec2-55-55-55-55.us-west-2.compute.amazonaws.com"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, }, expectedAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeInternalDNS, Address: "ip-10-1-1-1.us-west-2.compute.internal"}, {Type: v1.NodeExternalDNS, Address: "ec2-55-55-55-55.us-west-2.compute.amazonaws.com"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, }, shouldError: false, </ins> }, { <del> Type:    v1.NodeInternalIP, Address: kubelet.nodeIP.String(), </del> <ins> name:   "An Internal with multiple internal IPs", nodeIP: net.ParseIP("10.1.1.1"), nodeAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeInternalIP, Address: "10.2.2.2"}, {Type: v1.NodeInternalIP, Address: "10.3.3.3"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, }, expectedAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, }, shouldError: false, </ins> }, { <del> Type:    v1.NodeHostName, Address: testKubeletHostname, </del> <ins> name:   "An InternalIP that isn't valid: should error", nodeIP: net.ParseIP("10.2.2.2"), nodeAddresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.1.1.1"}, {Type: v1.NodeExternalIP, Address: "55.55.55.55"}, {Type: v1.NodeHostName, Address: testKubeletHostname}, }, expectedAddresses: nil, shouldError:       true, </ins> }, } <del> assert.True(t, apiequality.Semantic.DeepEqual(expectedAddresses, existingNode.Status.Addresses), "%s", diff.ObjectDiff(expectedAddresses, existingNode.Status.Addresses)) </del> <ins> for _, testCase := range cases { // testCase setup existingNode := v1.Node{ ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname, Annotations: make(map[string]string)}, Spec:       v1.NodeSpec{}, } kubelet.nodeIP = testCase.nodeIP fakeCloud := &fakecloud.FakeCloud{ Addresses: testCase.nodeAddresses, Err:       nil, } kubelet.cloud = fakeCloud kubelet.cloudproviderRequestParallelism = make(chan int, 1) kubelet.cloudproviderRequestSync = make(chan int) kubelet.cloudproviderRequestTimeout = 10 * time.Second kubelet.nodeIPValidator = func(nodeIP net.IP) error { return nil } // execute method err := kubelet.setNodeAddress(&existingNode) if err != nil && !testCase.shouldError { t.Errorf("Unexpected error for test %s: %q", testCase.name, err) continue } else if err != nil && testCase.shouldError { // expected an error continue } // Sort both sets for consistent equality sortNodeAddresses(testCase.expectedAddresses) sortNodeAddresses(existingNode.Status.Addresses) assert.True( t, apiequality.Semantic.DeepEqual( testCase.expectedAddresses, existingNode.Status.Addresses, ), fmt.Sprintf("Test %s failed %%s", testCase.name), diff.ObjectDiff(testCase.expectedAddresses, existingNode.Status.Addresses), ) } } // sortableNodeAddress is a type for sorting []v1.NodeAddress type sortableNodeAddress []v1.NodeAddress func (s sortableNodeAddress) Len() int { return len(s) } func (s sortableNodeAddress) Less(i, j int) bool { return (string(s[i].Type) + s[i].Address) < (string(s[j].Type) + s[j].Address) } func (s sortableNodeAddress) Swap(i, j int) { s[j], s[i] = s[i], s[j] } func sortNodeAddresses(addrs sortableNodeAddress) { sort.Sort(addrs) </ins> } func TestUpdateNewNodeStatus(t *testing.T) {