From 21c5c2ec5cbd44a62593df974f62fdc695768942 Mon Sep 17 00:00:00 2001 From: ravisantoshgudimetla Date: Tue, 26 Oct 2021 10:43:44 -0400 Subject: [PATCH] [kubelet][podadmission]: Validate and reject pods with mismatching labels --- pkg/kubelet/lifecycle/predicate.go | 29 +++++++++++++ pkg/kubelet/lifecycle/predicate_test.go | 55 +++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/pkg/kubelet/lifecycle/predicate.go b/pkg/kubelet/lifecycle/predicate.go index 6ca98aa8518..16fb432b8d0 100644 --- a/pkg/kubelet/lifecycle/predicate.go +++ b/pkg/kubelet/lifecycle/predicate.go @@ -18,6 +18,7 @@ package lifecycle import ( "fmt" + "runtime" v1 "k8s.io/api/core/v1" "k8s.io/apiserver/pkg/util/feature" @@ -153,11 +154,39 @@ func (w *predicateAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult Message: message, } } + if rejectPodAdmissionBasedOnOSSelector(admitPod, node) { + return PodAdmitResult{ + Admit: false, + Reason: "PodOSSelectorNodeLabelDoesNotMatch", + Message: "Failed to admit pod as the `kubernetes.io/os` label doesn't match node label", + } + } return PodAdmitResult{ Admit: true, } } +// rejectPodAdmissionBasedOnOSSelector rejects pod if it's nodeSelector doesn't match +// We expect the kubelet status reconcile which happens every 10sec to update the node labels if there is a mismatch. +func rejectPodAdmissionBasedOnOSSelector(pod *v1.Pod, node *v1.Node) bool { + labels := node.Labels + osName, osLabelExists := labels[v1.LabelOSStable] + if !osLabelExists || osName != runtime.GOOS { + if len(labels) == 0 { + labels = make(map[string]string) + } + labels[v1.LabelOSStable] = runtime.GOOS + } + podLabelSelector, podOSLabelExists := pod.Labels[v1.LabelOSStable] + if !podOSLabelExists { + // If the labelselector didn't exist, let's keep the current behavior as is + return false + } else if podOSLabelExists && podLabelSelector != labels[v1.LabelOSStable] { + return true + } + return false +} + func removeMissingExtendedResources(pod *v1.Pod, nodeInfo *schedulerframework.NodeInfo) *v1.Pod { podCopy := pod.DeepCopy() for i, c := range pod.Spec.Containers { diff --git a/pkg/kubelet/lifecycle/predicate_test.go b/pkg/kubelet/lifecycle/predicate_test.go index 2f16c5a6dca..bd4d3337986 100644 --- a/pkg/kubelet/lifecycle/predicate_test.go +++ b/pkg/kubelet/lifecycle/predicate_test.go @@ -18,6 +18,7 @@ package lifecycle import ( "reflect" + goruntime "runtime" "testing" v1 "k8s.io/api/core/v1" @@ -267,3 +268,57 @@ func TestGeneralPredicates(t *testing.T) { }) } } + +func TestRejectPodAdmissionBasedOnOSSelector(t *testing.T) { + tests := []struct { + name string + pod *v1.Pod + node *v1.Node + expectRejection bool + }{ + { + name: "OS label match", + pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: goruntime.GOOS}}}, + node: &v1.Node{Spec: v1.NodeSpec{}, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: goruntime.GOOS}}}, + expectRejection: false, + }, + { + name: "dummyOS label, but the underlying OS matches", + pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: goruntime.GOOS}}}, + node: &v1.Node{Spec: v1.NodeSpec{}, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + expectRejection: false, + }, + { + name: "dummyOS label, but the underlying OS doesn't match", + pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + node: &v1.Node{Spec: v1.NodeSpec{}, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + expectRejection: true, + }, + { + name: "dummyOS label, but the underlying OS doesn't match", + pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + node: &v1.Node{Spec: v1.NodeSpec{}, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + expectRejection: true, + }, + { + name: "OS field mismatch, OS label on node object would be reset to correct value", + pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + node: &v1.Node{Spec: v1.NodeSpec{}, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + expectRejection: true, + }, + { + name: "No label selector on the pod, should be admitted", + pod: &v1.Pod{}, + node: &v1.Node{Spec: v1.NodeSpec{}, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{v1.LabelOSStable: "dummyOS"}}}, + expectRejection: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actualResult := rejectPodAdmissionBasedOnOSSelector(test.pod, test.node) + if test.expectRejection != actualResult { + t.Errorf("unexpected result, expected %v but got %v", test.expectRejection, actualResult) + } + }) + } +}