Skip to content

Commit 7a05fb4

Browse files
committed
taint propagation: implement validation unit tests
1 parent 39fcb1e commit 7a05fb4

File tree

4 files changed

+370
-0
lines changed

4 files changed

+370
-0
lines changed

internal/webhooks/machine_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ import (
2020
"testing"
2121

2222
. "github.com/onsi/gomega"
23+
corev1 "k8s.io/api/core/v1"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
utilfeature "k8s.io/component-base/featuregate/testing"
2426
"k8s.io/utils/ptr"
2527

2628
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
29+
"sigs.k8s.io/cluster-api/feature"
2730
"sigs.k8s.io/cluster-api/internal/webhooks/util"
31+
"sigs.k8s.io/cluster-api/util/test/builder"
2832
)
2933

3034
func TestMachineDefault(t *testing.T) {
@@ -225,3 +229,119 @@ func TestMachineVersionValidation(t *testing.T) {
225229
})
226230
}
227231
}
232+
233+
func TestMachineTaintValidation(t *testing.T) {
234+
m := builder.Machine("default", "machine1").
235+
WithBootstrapTemplate(builder.BootstrapTemplate("default", "bootstrap-template").Build())
236+
webhook := &Machine{}
237+
238+
tests := []struct {
239+
name string
240+
machine *clusterv1.Machine
241+
featureEnabled bool
242+
expectErr bool
243+
}{
244+
{
245+
name: "should allow empty taints with feature gate disabled",
246+
featureEnabled: false,
247+
machine: m.DeepCopy().Build(),
248+
expectErr: false,
249+
},
250+
{
251+
name: "should allow empty taints with feature gate enabled",
252+
featureEnabled: true,
253+
machine: m.DeepCopy().Build(),
254+
expectErr: false,
255+
},
256+
{
257+
name: "should block taint key node.cluster.x-k8s.io/uninitialized",
258+
featureEnabled: true,
259+
machine: m.DeepCopy().WithTaints(clusterv1.MachineTaint{
260+
Key: "node.cluster.x-k8s.io/uninitialized", Effect: corev1.TaintEffectNoSchedule,
261+
}).Build(),
262+
expectErr: true,
263+
},
264+
{
265+
name: "should block taint key node.cluster.x-k8s.io/outdated-revision",
266+
featureEnabled: true,
267+
machine: m.DeepCopy().WithTaints(clusterv1.MachineTaint{
268+
Key: "node.cluster.x-k8s.io/outdated-revision", Effect: corev1.TaintEffectNoSchedule,
269+
}).Build(),
270+
expectErr: true,
271+
},
272+
{
273+
name: "should block taint with key prefix node.kubernetes.io/, which is not `out-of-service`",
274+
featureEnabled: true,
275+
machine: m.DeepCopy().WithTaints(clusterv1.MachineTaint{
276+
Key: "node.kubernetes.io/some-taint", Effect: corev1.TaintEffectNoSchedule,
277+
}).Build(),
278+
expectErr: true,
279+
},
280+
{
281+
name: "should allow taint node.kubernetes.io/out-of-service",
282+
featureEnabled: true,
283+
machine: m.DeepCopy().WithTaints(clusterv1.MachineTaint{
284+
Key: "node.kubernetes.io/out-of-service", Effect: corev1.TaintEffectNoSchedule,
285+
}).Build(),
286+
expectErr: false,
287+
},
288+
{
289+
name: "should block taint with keyprefix node.cloudprovider.kubernetes.io/",
290+
featureEnabled: true,
291+
machine: m.DeepCopy().WithTaints(clusterv1.MachineTaint{
292+
Key: "node.cloudprovider.kubernetes.io/some-taint", Effect: corev1.TaintEffectNoSchedule,
293+
}).Build(),
294+
expectErr: true,
295+
},
296+
{
297+
name: "should block taint key node-role.kubernetes.io/master",
298+
featureEnabled: true,
299+
machine: m.DeepCopy().WithTaints(clusterv1.MachineTaint{
300+
Key: "node-role.kubernetes.io/master", Effect: corev1.TaintEffectNoSchedule,
301+
}).Build(),
302+
expectErr: true,
303+
},
304+
{
305+
name: "should allow taint key node-role.kubernetes.io/control-plane for control-plane nodes",
306+
featureEnabled: true,
307+
machine: m.DeepCopy().
308+
WithLabels(map[string]string{clusterv1.MachineControlPlaneLabel: "true"}).
309+
WithTaints(clusterv1.MachineTaint{
310+
Key: "node-role.kubernetes.io/control-plane", Effect: corev1.TaintEffectNoSchedule,
311+
}).Build(),
312+
expectErr: false,
313+
},
314+
{
315+
name: "should block taint key node-role.kubernetes.io/control-plane for worker nodes",
316+
featureEnabled: true,
317+
machine: m.DeepCopy().WithTaints(clusterv1.MachineTaint{
318+
Key: "node-role.kubernetes.io/control-plane", Effect: corev1.TaintEffectNoSchedule,
319+
}).Build(),
320+
expectErr: true,
321+
},
322+
}
323+
for i := range tests {
324+
tt := tests[i]
325+
t.Run(tt.name, func(t *testing.T) {
326+
g := NewWithT(t)
327+
328+
utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.MachineTaintPropagation, tt.featureEnabled)
329+
330+
warnings, err := webhook.ValidateCreate(ctx, tt.machine)
331+
if tt.expectErr {
332+
g.Expect(err).To(HaveOccurred())
333+
} else {
334+
g.Expect(err).ToNot(HaveOccurred())
335+
}
336+
g.Expect(warnings).To(BeEmpty())
337+
338+
warnings, err = webhook.ValidateUpdate(ctx, tt.machine, tt.machine)
339+
if tt.expectErr {
340+
g.Expect(err).To(HaveOccurred())
341+
} else {
342+
g.Expect(err).ToNot(HaveOccurred())
343+
}
344+
g.Expect(warnings).To(BeEmpty())
345+
})
346+
}
347+
}

internal/webhooks/machinedeployment_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ import (
2323

2424
. "github.com/onsi/gomega"
2525
admissionv1 "k8s.io/api/admission/v1"
26+
corev1 "k8s.io/api/core/v1"
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"k8s.io/apimachinery/pkg/runtime"
2829
"k8s.io/apimachinery/pkg/util/intstr"
30+
utilfeature "k8s.io/component-base/featuregate/testing"
2931
"k8s.io/utils/ptr"
3032
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
3133

3234
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
35+
"sigs.k8s.io/cluster-api/feature"
3336
"sigs.k8s.io/cluster-api/internal/webhooks/util"
37+
"sigs.k8s.io/cluster-api/util/test/builder"
3438
)
3539

3640
func TestMachineDeploymentDefault(t *testing.T) {
@@ -838,3 +842,109 @@ func TestMachineDeploymentTemplateMetadataValidation(t *testing.T) {
838842
})
839843
}
840844
}
845+
846+
func TestMachineDeploymentTaintValidation(t *testing.T) {
847+
md := builder.MachineDeployment("default", "md").
848+
WithBootstrapTemplate(builder.BootstrapTemplate("default", "bootstrap-template").Build())
849+
webhook := &MachineDeployment{}
850+
851+
tests := []struct {
852+
name string
853+
machineDeployment *clusterv1.MachineDeployment
854+
featureEnabled bool
855+
expectErr bool
856+
}{
857+
{
858+
name: "should allow empty taints with feature gate disabled",
859+
featureEnabled: false,
860+
machineDeployment: md.DeepCopy().Build(),
861+
expectErr: false,
862+
},
863+
{
864+
name: "should allow empty taints with feature gate enabled",
865+
featureEnabled: true,
866+
machineDeployment: md.DeepCopy().Build(),
867+
expectErr: false,
868+
},
869+
{
870+
name: "should block taint key node.cluster.x-k8s.io/uninitialized",
871+
featureEnabled: true,
872+
machineDeployment: md.DeepCopy().WithTaints(clusterv1.MachineTaint{
873+
Key: "node.cluster.x-k8s.io/uninitialized", Effect: corev1.TaintEffectNoSchedule,
874+
}).Build(),
875+
expectErr: true,
876+
},
877+
{
878+
name: "should block taint key node.cluster.x-k8s.io/outdated-revision",
879+
featureEnabled: true,
880+
machineDeployment: md.DeepCopy().WithTaints(clusterv1.MachineTaint{
881+
Key: "node.cluster.x-k8s.io/outdated-revision", Effect: corev1.TaintEffectNoSchedule,
882+
}).Build(),
883+
expectErr: true,
884+
},
885+
{
886+
name: "should block taint with key prefix node.kubernetes.io/, which is not `out-of-service`",
887+
featureEnabled: true,
888+
machineDeployment: md.DeepCopy().WithTaints(clusterv1.MachineTaint{
889+
Key: "node.kubernetes.io/some-taint", Effect: corev1.TaintEffectNoSchedule,
890+
}).Build(),
891+
expectErr: true,
892+
},
893+
{
894+
name: "should allow taint node.kubernetes.io/out-of-service",
895+
featureEnabled: true,
896+
machineDeployment: md.DeepCopy().WithTaints(clusterv1.MachineTaint{
897+
Key: "node.kubernetes.io/out-of-service", Effect: corev1.TaintEffectNoSchedule,
898+
}).Build(),
899+
expectErr: false,
900+
},
901+
{
902+
name: "should block taint with key prefix node.cloudprovider.kubernetes.io/",
903+
featureEnabled: true,
904+
machineDeployment: md.DeepCopy().WithTaints(clusterv1.MachineTaint{
905+
Key: "node.cloudprovider.kubernetes.io/some-taint", Effect: corev1.TaintEffectNoSchedule,
906+
}).Build(),
907+
expectErr: true,
908+
},
909+
{
910+
name: "should block taint key node-role.kubernetes.io/master",
911+
featureEnabled: true,
912+
machineDeployment: md.DeepCopy().WithTaints(clusterv1.MachineTaint{
913+
Key: "node-role.kubernetes.io/master", Effect: corev1.TaintEffectNoSchedule,
914+
}).Build(),
915+
expectErr: true,
916+
},
917+
{
918+
name: "should block taint key node-role.kubernetes.io/control-plane for worker nodes",
919+
featureEnabled: true,
920+
machineDeployment: md.DeepCopy().WithTaints(clusterv1.MachineTaint{
921+
Key: "node-role.kubernetes.io/control-plane", Effect: corev1.TaintEffectNoSchedule,
922+
}).Build(),
923+
expectErr: true,
924+
},
925+
}
926+
for i := range tests {
927+
tt := tests[i]
928+
t.Run(tt.name, func(t *testing.T) {
929+
g := NewWithT(t)
930+
931+
utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.MachineTaintPropagation, tt.featureEnabled)
932+
933+
warnings, err := webhook.ValidateCreate(ctx, tt.machineDeployment)
934+
if tt.expectErr {
935+
g.Expect(err).To(HaveOccurred())
936+
} else {
937+
g.Expect(err).ToNot(HaveOccurred())
938+
}
939+
g.Expect(warnings).To(BeEmpty())
940+
941+
warnings, err = webhook.ValidateUpdate(ctx, tt.machineDeployment, tt.machineDeployment)
942+
if tt.expectErr {
943+
g.Expect(err).To(HaveOccurred())
944+
} else {
945+
g.Expect(err).ToNot(HaveOccurred())
946+
}
947+
g.Expect(warnings).To(BeEmpty())
948+
})
949+
}
950+
}

internal/webhooks/machineset_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@ import (
2222
"testing"
2323

2424
. "github.com/onsi/gomega"
25+
corev1 "k8s.io/api/core/v1"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2627
"k8s.io/apimachinery/pkg/runtime"
28+
utilfeature "k8s.io/component-base/featuregate/testing"
2729
"k8s.io/utils/ptr"
2830
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2931

3032
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
33+
"sigs.k8s.io/cluster-api/feature"
3134
"sigs.k8s.io/cluster-api/internal/webhooks/util"
35+
"sigs.k8s.io/cluster-api/util/test/builder"
3236
)
3337

3438
func TestMachineSetDefault(t *testing.T) {
@@ -742,3 +746,109 @@ func TestMachineSetMachineNamingValidation(t *testing.T) {
742746
})
743747
}
744748
}
749+
750+
func TestMachineSetTaintValidation(t *testing.T) {
751+
ms := builder.MachineSet("default", "machineset1").
752+
WithBootstrapTemplate(builder.BootstrapTemplate("default", "bootstrap-template").Build())
753+
webhook := &MachineSet{}
754+
755+
tests := []struct {
756+
name string
757+
machineSet *clusterv1.MachineSet
758+
featureEnabled bool
759+
expectErr bool
760+
}{
761+
{
762+
name: "should allow empty taints with feature gate disabled",
763+
featureEnabled: false,
764+
machineSet: ms.DeepCopy().Build(),
765+
expectErr: false,
766+
},
767+
{
768+
name: "should allow empty taints with feature gate enabled",
769+
featureEnabled: true,
770+
machineSet: ms.DeepCopy().Build(),
771+
expectErr: false,
772+
},
773+
{
774+
name: "should block taint key node.cluster.x-k8s.io/uninitialized",
775+
featureEnabled: true,
776+
machineSet: ms.DeepCopy().WithTaints(clusterv1.MachineTaint{
777+
Key: "node.cluster.x-k8s.io/uninitialized", Effect: corev1.TaintEffectNoSchedule,
778+
}).Build(),
779+
expectErr: true,
780+
},
781+
{
782+
name: "should block taint key node.cluster.x-k8s.io/outdated-revision",
783+
featureEnabled: true,
784+
machineSet: ms.DeepCopy().WithTaints(clusterv1.MachineTaint{
785+
Key: "node.cluster.x-k8s.io/outdated-revision", Effect: corev1.TaintEffectNoSchedule,
786+
}).Build(),
787+
expectErr: true,
788+
},
789+
{
790+
name: "should block taint with key prefix node.kubernetes.io/, which is not `out-of-service`",
791+
featureEnabled: true,
792+
machineSet: ms.DeepCopy().WithTaints(clusterv1.MachineTaint{
793+
Key: "node.kubernetes.io/some-taint", Effect: corev1.TaintEffectNoSchedule,
794+
}).Build(),
795+
expectErr: true,
796+
},
797+
{
798+
name: "should allow taint node.kubernetes.io/out-of-service",
799+
featureEnabled: true,
800+
machineSet: ms.DeepCopy().WithTaints(clusterv1.MachineTaint{
801+
Key: "node.kubernetes.io/out-of-service", Effect: corev1.TaintEffectNoSchedule,
802+
}).Build(),
803+
expectErr: false,
804+
},
805+
{
806+
name: "should block taint with keyprefix node.cloudprovider.kubernetes.io/",
807+
featureEnabled: true,
808+
machineSet: ms.DeepCopy().WithTaints(clusterv1.MachineTaint{
809+
Key: "node.cloudprovider.kubernetes.io/some-taint", Effect: corev1.TaintEffectNoSchedule,
810+
}).Build(),
811+
expectErr: true,
812+
},
813+
{
814+
name: "should block taint key node-role.kubernetes.io/master",
815+
featureEnabled: true,
816+
machineSet: ms.DeepCopy().WithTaints(clusterv1.MachineTaint{
817+
Key: "node-role.kubernetes.io/master", Effect: corev1.TaintEffectNoSchedule,
818+
}).Build(),
819+
expectErr: true,
820+
},
821+
{
822+
name: "should block taint key node-role.kubernetes.io/control-plane for worker nodes",
823+
featureEnabled: true,
824+
machineSet: ms.DeepCopy().WithTaints(clusterv1.MachineTaint{
825+
Key: "node-role.kubernetes.io/control-plane", Effect: corev1.TaintEffectNoSchedule,
826+
}).Build(),
827+
expectErr: true,
828+
},
829+
}
830+
for i := range tests {
831+
tt := tests[i]
832+
t.Run(tt.name, func(t *testing.T) {
833+
g := NewWithT(t)
834+
835+
utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.MachineTaintPropagation, tt.featureEnabled)
836+
837+
warnings, err := webhook.ValidateCreate(ctx, tt.machineSet)
838+
if tt.expectErr {
839+
g.Expect(err).To(HaveOccurred())
840+
} else {
841+
g.Expect(err).ToNot(HaveOccurred())
842+
}
843+
g.Expect(warnings).To(BeEmpty())
844+
845+
warnings, err = webhook.ValidateUpdate(ctx, tt.machineSet, tt.machineSet)
846+
if tt.expectErr {
847+
g.Expect(err).To(HaveOccurred())
848+
} else {
849+
g.Expect(err).ToNot(HaveOccurred())
850+
}
851+
g.Expect(warnings).To(BeEmpty())
852+
})
853+
}
854+
}

0 commit comments

Comments
 (0)