Skip to content

Commit 893ac6c

Browse files
TomerNewmank8s-ci-robot
authored andcommitted
Prevent "Exists" tolerations from having a value
This commit introduces a validation check in the webhook, to ensure that tolerations with operator: "Exists" do not specify a value, as this is invalid in k8s.
1 parent d915eb9 commit 893ac6c

File tree

2 files changed

+145
-23
lines changed

2 files changed

+145
-23
lines changed

Diff for: internal/webhook/module.go

+44-9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"errors"
2222
"fmt"
2323
corev1 "k8s.io/api/core/v1"
24+
"k8s.io/apimachinery/pkg/util/validation"
2425
"regexp"
2526
"strings"
2627

@@ -110,7 +111,7 @@ func validateModule(mod *kmmv1beta1.Module) (admission.Warnings, error) {
110111
return nil, fmt.Errorf("failed to validate kernel mappings: %v", err)
111112
}
112113

113-
if err := validateModuleTolerations(mod); err != nil {
114+
if err := validateTolerations(mod.Spec.Tolerations); err != nil {
114115
return nil, fmt.Errorf("failed to validate Module's tolerations: %v", err)
115116
}
116117

@@ -212,17 +213,51 @@ func validateModprobe(modprobe kmmv1beta1.ModprobeSpec) error {
212213

213214
return nil
214215
}
215-
func validateModuleTolerations(mod *kmmv1beta1.Module) error {
216-
for _, toleration := range mod.Spec.Tolerations {
217216

218-
if toleration.Operator != corev1.TolerationOpExists && toleration.Operator != corev1.TolerationOpEqual {
219-
return fmt.Errorf("toleration operator can be only {Exists, Equal} but got %s", toleration.Operator)
217+
func validateTolerations(tolerations []corev1.Toleration) error {
218+
219+
for i, toleration := range tolerations {
220+
tolIndex := fmt.Sprintf("Toleration[%d]", i)
221+
222+
if len(toleration.Key) > 0 {
223+
if errs := validation.IsQualifiedName(toleration.Key); len(errs) > 0 {
224+
return fmt.Errorf("%s invalid key '%s': %s", tolIndex, toleration.Key, strings.Join(errs, "; "))
225+
}
226+
}
227+
228+
if len(toleration.Key) == 0 && toleration.Operator != corev1.TolerationOpExists {
229+
return fmt.Errorf("%s operator must be 'Exists' when key is empty", tolIndex)
230+
}
231+
232+
if toleration.TolerationSeconds != nil && toleration.Effect != corev1.TaintEffectNoExecute {
233+
return fmt.Errorf("%s effect must be 'NoExecute' when tolerationSeconds is set", tolIndex)
220234
}
221235

222-
if toleration.Effect != corev1.TaintEffectNoSchedule &&
223-
toleration.Effect != corev1.TaintEffectNoExecute &&
224-
toleration.Effect != corev1.TaintEffectPreferNoSchedule {
225-
return fmt.Errorf("toleration effect can be only {NoSchedule, NoExecute, PreferNoSchedule} but got %s", toleration.Effect)
236+
switch toleration.Operator {
237+
case corev1.TolerationOpEqual, "":
238+
if errs := validation.IsValidLabelValue(toleration.Value); len(errs) > 0 {
239+
return fmt.Errorf("%s invalid value '%s' for Equal operator %s", tolIndex, toleration.Value, strings.Join(errs, "; "))
240+
}
241+
case corev1.TolerationOpExists:
242+
if len(toleration.Value) > 0 {
243+
return fmt.Errorf("%s value must be empty when operator is 'Exists'", tolIndex)
244+
}
245+
default:
246+
return fmt.Errorf("%s invalid operator '%s', allowed values are ['Equal', 'Exists']", tolIndex, toleration.Operator)
247+
}
248+
249+
if len(toleration.Effect) > 0 {
250+
validEffects := []corev1.TaintEffect{corev1.TaintEffectNoSchedule, corev1.TaintEffectPreferNoSchedule, corev1.TaintEffectNoExecute}
251+
valid := false
252+
for _, v := range validEffects {
253+
if toleration.Effect == v {
254+
valid = true
255+
break
256+
}
257+
}
258+
if !valid {
259+
return fmt.Errorf("%s invalid effect '%s', allowed values are ['NoSchedule', 'PreferNoSchedule', 'NoExecute']", tolIndex, toleration.Effect)
260+
}
226261
}
227262
}
228263
return nil

Diff for: internal/webhook/module_test.go

+101-14
Original file line numberDiff line numberDiff line change
@@ -553,24 +553,23 @@ var _ = Describe("ValidateDelete", func() {
553553
})
554554
})
555555

556-
var _ = Describe("validateModuleTolerarations", func() {
556+
var _ = Describe("validateTolerations", func() {
557557
It("should fail when Module has an invalid toleration effect", func() {
558-
mod := validModule
559-
mod.Spec.Tolerations = []v1.Toleration{
558+
tolerations := []v1.Toleration{
560559
{
561560
Key: "Test-Key1", Operator: v1.TolerationOpEqual, Value: "Test-Value1", Effect: v1.TaintEffectPreferNoSchedule,
562561
},
563562
{
564-
Key: "Test-Key2", Operator: v1.TolerationOpExists, Value: "Test-Value2", Effect: "Invalid-Effect",
563+
Key: "Test-Key2", Operator: v1.TolerationOpExists, Effect: "Invalid-Effect",
565564
},
566565
}
567566

568-
err := validateModuleTolerations(&mod)
567+
err := validateTolerations(tolerations)
569568
Expect(err).To(HaveOccurred())
570569
})
570+
571571
It("should fail when Module has an invalid toleration operator", func() {
572-
mod := validModule
573-
mod.Spec.Tolerations = []v1.Toleration{
572+
tolerations := []v1.Toleration{
574573
{
575574
Key: "Test-Key1", Operator: v1.TolerationOpEqual, Value: "Test-Value1", Effect: v1.TaintEffectPreferNoSchedule,
576575
},
@@ -579,14 +578,28 @@ var _ = Describe("validateModuleTolerarations", func() {
579578
},
580579
}
581580

582-
err := validateModuleTolerations(&mod)
581+
err := validateTolerations(tolerations)
583582
Expect(err).To(HaveOccurred())
584583
})
585-
It("should work when all tolerations have valid effects ", func() {
586-
mod := validModule
587-
mod.Spec.Tolerations = []v1.Toleration{
584+
585+
It("should fail when toleration has non empty value and Exists operator", func() {
586+
tolerations := []v1.Toleration{
587+
{
588+
Key: "Test-Key1", Operator: v1.TolerationOpEqual, Value: "Test-Value1", Effect: v1.TaintEffectPreferNoSchedule,
589+
},
590+
{
591+
Key: "Test-Key2", Operator: v1.TolerationOpExists, Value: "Test-Value2", Effect: v1.TaintEffectNoExecute,
592+
},
593+
}
594+
595+
err := validateTolerations(tolerations)
596+
Expect(err).To(HaveOccurred())
597+
})
598+
599+
It("should work when all tolerations have valid effects", func() {
600+
tolerations := []v1.Toleration{
588601
{
589-
Key: "Test-Key1", Operator: v1.TolerationOpExists, Value: "Test-Value", Effect: v1.TaintEffectPreferNoSchedule,
602+
Key: "Test-Key1", Operator: v1.TolerationOpExists, Effect: v1.TaintEffectPreferNoSchedule,
590603
},
591604
{
592605
Key: "Test-Key2", Operator: v1.TolerationOpEqual, Value: "Test-Value", Effect: v1.TaintEffectNoSchedule,
@@ -596,8 +609,82 @@ var _ = Describe("validateModuleTolerarations", func() {
596609
},
597610
}
598611

599-
err := validateModuleTolerations(&mod)
600-
Expect(err).ToNot(HaveOccurred())
612+
err := validateTolerations(tolerations)
613+
Expect(err).To(Not(HaveOccurred()))
614+
})
615+
616+
It("should fail when toleration has an invalid key format", func() {
617+
tolerations := []v1.Toleration{
618+
{
619+
Key: "Invalid Key!", Operator: v1.TolerationOpEqual, Value: "Test-Value",
620+
},
621+
}
622+
623+
err := validateTolerations(tolerations)
624+
Expect(err).To(HaveOccurred())
625+
})
626+
627+
It("should fail when toleration has empty key with an invalid operator", func() {
628+
tolerations := []v1.Toleration{
629+
{
630+
Key: "", Operator: v1.TolerationOpEqual, Value: "Test-Value",
631+
},
632+
}
633+
634+
err := validateTolerations(tolerations)
635+
Expect(err).To(HaveOccurred())
601636
})
602637

638+
It("should fail when TolerationSeconds is set but Effect is not NoExecute", func() {
639+
tolSecs := int64(30)
640+
tolerations := []v1.Toleration{
641+
{
642+
Key: "Test-Key", Operator: v1.TolerationOpEqual, Value: "Test-Value", Effect: v1.TaintEffectNoSchedule, TolerationSeconds: &tolSecs,
643+
},
644+
}
645+
646+
err := validateTolerations(tolerations)
647+
Expect(err).To(HaveOccurred())
648+
})
649+
650+
It("should pass when TolerationSeconds is set with NoExecute effect", func() {
651+
tolSecs := int64(60)
652+
tolerations := []v1.Toleration{
653+
{
654+
Key: "Test-Key", Operator: v1.TolerationOpEqual, Value: "Test-Value", Effect: v1.TaintEffectNoExecute, TolerationSeconds: &tolSecs,
655+
},
656+
}
657+
658+
err := validateTolerations(tolerations)
659+
Expect(err).To(Not(HaveOccurred()))
660+
})
661+
662+
It("should fail when an invalid effect is provided", func() {
663+
tolerations := []v1.Toleration{
664+
{
665+
Key: "Test-Key", Operator: v1.TolerationOpExists, Effect: "Invalid-Effect",
666+
},
667+
}
668+
669+
err := validateTolerations(tolerations)
670+
Expect(err).To(HaveOccurred())
671+
})
672+
673+
It("should fail when operator is not Equal or Exists", func() {
674+
tolerations := []v1.Toleration{
675+
{
676+
Key: "Test-Key", Operator: "InvalidOperator", Value: "Test-Value",
677+
},
678+
}
679+
680+
err := validateTolerations(tolerations)
681+
Expect(err).To(HaveOccurred())
682+
})
683+
684+
It("should pass with an empty toleration list", func() {
685+
var tolerations []v1.Toleration
686+
687+
err := validateTolerations(tolerations)
688+
Expect(err).To(Not(HaveOccurred()))
689+
})
603690
})

0 commit comments

Comments
 (0)