Kubernetes 1.20 deprecated the node-role.kubernetes.io/master label and replaced it with node-role.kubernetes.io/control-plane, and 1.24 dropped the legacy label entirely. The scheduler-k3s:cluster:add join path still filtered control-plane nodes with master=true, so on any k3s release built from Kubernetes 1.24+ the lookup matched zero nodes and aborted with "no nodes found in the cluster" before the worker could join.
119 lines
3.1 KiB
Go
119 lines
3.1 KiB
Go
package scheduler_k3s
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
)
|
|
|
|
func newNode(name string, labels map[string]string, kubeletVersion string) *corev1.Node {
|
|
return &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
Labels: labels,
|
|
},
|
|
Status: corev1.NodeStatus{
|
|
NodeInfo: corev1.NodeSystemInfo{
|
|
KubeletVersion: kubeletVersion,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestGetLowestNodeVersion(t *testing.T) {
|
|
controlPlaneLabels := map[string]string{
|
|
"node-role.kubernetes.io/control-plane": "true",
|
|
}
|
|
dualLabels := map[string]string{
|
|
"node-role.kubernetes.io/control-plane": "true",
|
|
"node-role.kubernetes.io/master": "true",
|
|
}
|
|
workerLabels := map[string]string{
|
|
"node-role.kubernetes.io/worker": "true",
|
|
}
|
|
legacyMasterLabels := map[string]string{
|
|
"node-role.kubernetes.io/master": "true",
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
nodes []*corev1.Node
|
|
selector string
|
|
want string
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "single control-plane node returns its kubelet version",
|
|
nodes: []*corev1.Node{
|
|
newNode("cp-0", controlPlaneLabels, "v1.35.5+k3s1"),
|
|
},
|
|
selector: "node-role.kubernetes.io/control-plane=true",
|
|
want: "v1.35.5+k3s1",
|
|
},
|
|
{
|
|
name: "mixed cluster returns lowest control-plane version, ignoring workers",
|
|
nodes: []*corev1.Node{
|
|
newNode("cp-0", controlPlaneLabels, "v1.36.0+k3s1"),
|
|
newNode("cp-1", controlPlaneLabels, "v1.35.5+k3s1"),
|
|
newNode("worker-0", workerLabels, "v1.30.0+k3s1"),
|
|
},
|
|
selector: "node-role.kubernetes.io/control-plane=true",
|
|
want: "v1.35.5+k3s1",
|
|
},
|
|
{
|
|
name: "dual-labeled node (k8s 1.20-1.23 era) is matched by control-plane selector",
|
|
nodes: []*corev1.Node{
|
|
newNode("cp-0", dualLabels, "v1.23.0+k3s1"),
|
|
},
|
|
selector: "node-role.kubernetes.io/control-plane=true",
|
|
want: "v1.23.0+k3s1",
|
|
},
|
|
{
|
|
name: "cluster with no control-plane label returns no nodes found",
|
|
nodes: []*corev1.Node{
|
|
newNode("legacy-cp", legacyMasterLabels, "v1.19.0+k3s1"),
|
|
newNode("worker-0", workerLabels, "v1.19.0+k3s1"),
|
|
},
|
|
selector: "node-role.kubernetes.io/control-plane=true",
|
|
wantErr: "no nodes found in the cluster",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
objs := make([]runtime.Object, 0, len(tc.nodes))
|
|
for _, n := range tc.nodes {
|
|
objs = append(objs, n)
|
|
}
|
|
clientset := fake.NewSimpleClientset(objs...)
|
|
k := KubernetesClient{Client: clientset}
|
|
|
|
got, err := k.GetLowestNodeVersion(context.Background(), ListNodesInput{
|
|
LabelSelector: tc.selector,
|
|
})
|
|
|
|
if tc.wantErr != "" {
|
|
if err == nil {
|
|
t.Fatalf("expected error containing %q, got nil (returned version %q)", tc.wantErr, got)
|
|
}
|
|
if !strings.Contains(err.Error(), tc.wantErr) {
|
|
t.Fatalf("expected error containing %q, got %q", tc.wantErr, err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != tc.want {
|
|
t.Fatalf("expected version %q, got %q", tc.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|