diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go b/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go index f090889beb1..4b43beb3688 100644 --- a/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go @@ -19,13 +19,19 @@ package upgrade import ( "os" "path/filepath" + "reflect" "regexp" "strings" "testing" "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" errorsutil "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/client-go/kubernetes/fake" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs" "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -169,6 +175,142 @@ func TestWriteKubeletConfigFiles(t *testing.T) { } } +func TestUnupgradedControlPlaneInstances(t *testing.T) { + testCases := []struct { + name string + pods []corev1.Pod + currentNode string + expectedNodes []string + expectError bool + }{ + { + name: "two nodes, one needs upgrade", + pods: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-apiserver-1", + Namespace: metav1.NamespaceSystem, + Labels: map[string]string{ + "component": constants.KubeAPIServer, + }, + }, + Spec: corev1.PodSpec{ + NodeName: "node-1", + Containers: []corev1.Container{ + {Name: constants.KubeAPIServer, Image: "registry.kl8s.io/kube-apiserver:v2"}, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-apiserver-2", + Namespace: metav1.NamespaceSystem, + Labels: map[string]string{ + "component": constants.KubeAPIServer, + }, + }, + Spec: corev1.PodSpec{ + NodeName: "node-2", + Containers: []corev1.Container{ + {Name: constants.KubeAPIServer, Image: "registry.kl8s.io/kube-apiserver:v1"}, + }, + }, + }, + }, + currentNode: "node-1", + expectedNodes: []string{"node-2"}, + expectError: false, + }, + { + name: "one node which is already upgraded", + pods: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-apiserver-1", + Namespace: metav1.NamespaceSystem, + Labels: map[string]string{ + "component": constants.KubeAPIServer, + }, + }, + Spec: corev1.PodSpec{ + NodeName: "node-1", + Containers: []corev1.Container{ + {Name: constants.KubeAPIServer, Image: "registry.kl8s.io/kube-apiserver:v2"}, + }, + }, + }, + }, + currentNode: "node-1", + expectedNodes: nil, + expectError: false, + }, + { + name: "two nodes, both already upgraded", + pods: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-apiserver-1", + Namespace: metav1.NamespaceSystem, + Labels: map[string]string{ + "component": constants.KubeAPIServer, + }, + }, + Spec: corev1.PodSpec{ + NodeName: "node-1", + Containers: []corev1.Container{ + {Name: constants.KubeAPIServer, Image: "registry.kl8s.io/kube-apiserver:v2"}, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-apiserver-2", + Namespace: metav1.NamespaceSystem, + Labels: map[string]string{ + "component": constants.KubeAPIServer, + }, + }, + Spec: corev1.PodSpec{ + NodeName: "node-2", + Containers: []corev1.Container{ + {Name: constants.KubeAPIServer, Image: "registry.kl8s.io/kube-apiserver:v2"}, + }, + }, + }, + }, + currentNode: "node-1", + expectedNodes: nil, + expectError: false, + }, + { + name: "no kube-apiserver pods", + pods: []corev1.Pod{}, + currentNode: "node-1", + expectedNodes: nil, + expectError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var runtimeObjs []runtime.Object + for _, pod := range tc.pods { + runtimeObjs = append(runtimeObjs, &pod) // Use pointer + } + client := fake.NewSimpleClientset(runtimeObjs...) + + nodes, err := UnupgradedControlPlaneInstances(client, tc.currentNode) + if tc.expectError != (err != nil) { + t.Fatalf("expected error: %v, got: %v", tc.expectError, err) + } + + if !reflect.DeepEqual(nodes, tc.expectedNodes) { + t.Fatalf("expected unupgraded control plane instances: %v, got: %v", tc.expectedNodes, nodes) + } + }) + } +} + // Just some stub code, the code could be enriched when necessary. type componentConfig struct { userSupplied bool