diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index ba0ab34b5c7..1530815214e 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -1520,10 +1520,16 @@ func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.Po klog.V(4).InfoS("Cannot get host IPs", "err", err) } else { s.HostIP = hostIPs[0].String() - if kubecontainer.IsHostNetworkPod(pod) && s.PodIP == "" { - s.PodIP = hostIPs[0].String() - s.PodIPs = []v1.PodIP{{IP: s.PodIP}} - if len(hostIPs) == 2 { + // HostNetwork Pods inherit the node IPs as PodIPs. They are immutable once set, + // other than that if the node becomes dual-stack, we add the secondary IP. + if kubecontainer.IsHostNetworkPod(pod) { + // Primary IP is not set + if s.PodIP == "" { + s.PodIP = hostIPs[0].String() + s.PodIPs = []v1.PodIP{{IP: s.PodIP}} + } + // Secondary IP is not set #105320 + if len(hostIPs) == 2 && len(s.PodIPs) == 1 { s.PodIPs = append(s.PodIPs, v1.PodIP{IP: hostIPs[1].String()}) } } diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 495434bca8f..78263de16e9 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -3349,6 +3349,119 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) { } } +func TestNodeAddressUpdatesGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) { + testcases := []struct { + name string + nodeIPs []string + nodeAddresses []v1.NodeAddress + expectedPodIPs []v1.PodIP + }{ + + { + name: "Immutable after update node addresses single-stack", + nodeIPs: []string{"10.0.0.1"}, + nodeAddresses: []v1.NodeAddress{ + {Type: v1.NodeInternalIP, Address: "1.1.1.1"}, + }, + expectedPodIPs: []v1.PodIP{ + {IP: "10.0.0.1"}, + }, + }, + { + name: "Immutable after update node addresses dual-stack - primary address", + nodeIPs: []string{"10.0.0.1", "2001:db8::2"}, + nodeAddresses: []v1.NodeAddress{ + {Type: v1.NodeInternalIP, Address: "1.1.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:db8::2"}, + }, + expectedPodIPs: []v1.PodIP{ + {IP: "10.0.0.1"}, + {IP: "2001:db8::2"}, + }, + }, + { + name: "Immutable after update node addresses dual-stack - secondary address", + nodeIPs: []string{"10.0.0.1", "2001:db8::2"}, + nodeAddresses: []v1.NodeAddress{ + {Type: v1.NodeInternalIP, Address: "10.0.0.1"}, + {Type: v1.NodeInternalIP, Address: "2001:db8:1:2:3::2"}, + }, + expectedPodIPs: []v1.PodIP{ + {IP: "10.0.0.1"}, + {IP: "2001:db8::2"}, + }, + }, + { + name: "Immutable after update node addresses dual-stack - primary and secondary address", + nodeIPs: []string{"10.0.0.1", "2001:db8::2"}, + nodeAddresses: []v1.NodeAddress{ + {Type: v1.NodeInternalIP, Address: "1.1.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:db8:1:2:3::2"}, + }, + expectedPodIPs: []v1.PodIP{ + {IP: "10.0.0.1"}, + {IP: "2001:db8::2"}, + }, + }, + { + name: "Update secondary after new secondary address dual-stack", + nodeIPs: []string{"10.0.0.1"}, + nodeAddresses: []v1.NodeAddress{ + {Type: v1.NodeInternalIP, Address: "10.0.0.1"}, + {Type: v1.NodeInternalIP, Address: "2001:db8::2"}, + }, + expectedPodIPs: []v1.PodIP{ + {IP: "10.0.0.1"}, + {IP: "2001:db8::2"}, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) + defer testKubelet.Cleanup() + kl := testKubelet.kubelet + for _, ip := range tc.nodeIPs { + kl.nodeIPs = append(kl.nodeIPs, netutils.ParseIPSloppy(ip)) + } + kl.nodeLister = testNodeLister{nodes: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{Name: string(kl.nodeName)}, + Status: v1.NodeStatus{ + Addresses: tc.nodeAddresses, + }, + }, + }} + + pod := podWithUIDNameNs("12345", "test-pod", "test-namespace") + pod.Spec.HostNetwork = true + for _, ip := range tc.nodeIPs { + pod.Status.PodIPs = append(pod.Status.PodIPs, v1.PodIP{IP: ip}) + } + if len(pod.Status.PodIPs) > 0 { + pod.Status.PodIP = pod.Status.PodIPs[0].IP + } + + // set old status + podStatus := &kubecontainer.PodStatus{ + ID: pod.UID, + Name: pod.Name, + Namespace: pod.Namespace, + } + podStatus.IPs = tc.nodeIPs + + status := kl.generateAPIPodStatus(pod, podStatus) + if !reflect.DeepEqual(status.PodIPs, tc.expectedPodIPs) { + t.Fatalf("Expected PodIPs %#v, got %#v", tc.expectedPodIPs, status.PodIPs) + } + if kl.nodeIPs[0].String() != status.PodIPs[0].IP { + t.Fatalf("Expected HostIP %q to equal PodIPs[0].IP %q", status.HostIP, status.PodIPs[0].IP) + } + }) + } +} + func TestGenerateAPIPodStatusPodIPs(t *testing.T) { testcases := []struct { name string