Skip to content

Commit acac5b2

Browse files
committed
taint propagation: implement validation
1 parent 7c9537d commit acac5b2

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

internal/webhooks/machine.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ import (
2222
"strings"
2323

2424
apierrors "k8s.io/apimachinery/pkg/api/errors"
25+
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
2526
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/apimachinery/pkg/util/validation"
2628
"k8s.io/apimachinery/pkg/util/validation/field"
2729
"k8s.io/utils/ptr"
2830
ctrl "sigs.k8s.io/controller-runtime"
2931
"sigs.k8s.io/controller-runtime/pkg/webhook"
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/util/labels"
3437
"sigs.k8s.io/cluster-api/util/version"
3538
)
@@ -136,8 +139,82 @@ func (webhook *Machine) validate(oldM, newM *clusterv1.Machine) error {
136139
}
137140
}
138141

142+
allErrs = append(allErrs, validateMachineTaints(newM.Spec.Taints, specPath.Child("taints"))...)
143+
allErrs = append(allErrs, validateMachineTaintsForWorkers(newM.Spec.Taints, newM, specPath.Child("taints"))...)
144+
139145
if len(allErrs) == 0 {
140146
return nil
141147
}
142148
return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Machine").GroupKind(), newM.Name, allErrs)
143149
}
150+
151+
func validateMachineTaints(taints []clusterv1.MachineTaint, taintsPath *field.Path) field.ErrorList {
152+
var allErrs field.ErrorList
153+
154+
if !feature.Gates.Enabled(feature.MachineTaintPropagation) {
155+
if len(taints) > 0 {
156+
allErrs = append(allErrs, field.Forbidden(taintsPath, "taints are not allowed to be set when the feature gate MachineTaintPropagation is disabled"))
157+
}
158+
}
159+
160+
for i, taint := range taints {
161+
idxPath := taintsPath.Index(i)
162+
163+
// Validate key syntax.
164+
if errs := metav1validation.ValidateLabelName(taint.Key, idxPath); len(errs) > 0 {
165+
allErrs = append(allErrs, errs...)
166+
}
167+
168+
// Validate value syntax.
169+
if errs := validation.IsValidLabelValue(ptr.Deref(taint.Value, "")); len(errs) > 0 {
170+
allErrs = append(allErrs, field.Invalid(idxPath.Child("value"), taint.Value, strings.Join(errs, ";")))
171+
}
172+
173+
// The following validations uses a switch statement, because if one of them matches, then the others won't.
174+
175+
switch {
176+
// Validate for keys which are reserved for usage by the cluster-api or providers.
177+
case taint.Key == "node.cluster.x-k8s.io/uninitialized":
178+
allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key must not be node.cluster.x-k8s.io/uninitialized"))
179+
case taint.Key == "node.cluster.x-k8s.io/outdated-revision":
180+
allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key must not be node.cluster.x-k8s.io/uninitialized or node.cluster.x-k8s.io/outdated-revision"))
181+
// Validate for key's which are reserved for usage by the node or node-lifecycle-controller, but allow `node.kubernetes.io/out-of-service`.
182+
case strings.HasPrefix(taint.Key, "node.kubernetes.io/") && taint.Key != "node.kubernetes.io/out-of-service":
183+
allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key must not have the prefix node.kubernetes.io/, except for node.kubernetes.io/out-of-service"))
184+
// Validate for keys which are reserved for usage by the cloud-controller-manager or kubelet.
185+
case strings.HasPrefix(taint.Key, "node.cloudprovider.kubernetes.io/"):
186+
allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key must not have the prefix node.cloudprovider.kubernetes.io/"))
187+
// Validate for the deprecated kubeadm node-role taint.
188+
case taint.Key == "node-role.kubernetes.io/master":
189+
allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint is deprecated since 1.24 and should not be used anymore"))
190+
}
191+
}
192+
193+
return allErrs
194+
}
195+
196+
func validateMachineTaintsForWorkers(taints []clusterv1.MachineTaint, machine *clusterv1.Machine, taintsPath *field.Path) field.ErrorList {
197+
var allErrs field.ErrorList
198+
199+
if !feature.Gates.Enabled(feature.MachineTaintPropagation) {
200+
return allErrs
201+
}
202+
203+
// Skip for control-plane machines.
204+
if machine != nil && machine.Labels != nil {
205+
if _, ok := machine.Labels[clusterv1.MachineControlPlaneLabel]; ok {
206+
return allErrs
207+
}
208+
}
209+
210+
// Validate taints for worker machines.
211+
for i, taint := range taints {
212+
idxPath := taintsPath.Index(i)
213+
214+
if taint.Key == "node-role.kubernetes.io/control-plane" {
215+
allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint is not allowed for worker machines"))
216+
}
217+
}
218+
219+
return allErrs
220+
}

internal/webhooks/machinedeployment.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ func (webhook *MachineDeployment) validate(oldMD, newMD *clusterv1.MachineDeploy
254254

255255
allErrs = append(allErrs, validateMDMachineNaming(newMD.Spec.MachineNaming, specPath.Child("machineNaming"))...)
256256

257+
allErrs = append(allErrs, validateMachineTaints(newMD.Spec.Template.Spec.Taints, specPath.Child("template", "spec", "taints"))...)
258+
allErrs = append(allErrs, validateMachineTaintsForWorkers(newMD.Spec.Template.Spec.Taints, nil, specPath.Child("template", "spec", "taints"))...)
259+
257260
// Validate the metadata of the template.
258261
allErrs = append(allErrs, newMD.Spec.Template.Validate(specPath.Child("template", "metadata"))...)
259262

internal/webhooks/machinepool.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ func (webhook *MachinePool) validate(oldObj, newObj *clusterv1.MachinePool) erro
189189
}
190190
}
191191

192+
if len(newObj.Spec.Template.Spec.Taints) > 0 {
193+
if !feature.Gates.Enabled(feature.MachineTaintPropagation) {
194+
allErrs = append(allErrs, field.Forbidden(specPath.Child("taints"), "taints are not allowed to be set when the feature gate MachineTaintPropagation is disabled"))
195+
} else {
196+
allErrs = append(allErrs, field.Forbidden(specPath.Child("taints"), "taints feature for MachinePools is not yet implemented"))
197+
}
198+
}
199+
192200
// Validate the metadata of the MachinePool template.
193201
allErrs = append(allErrs, newObj.Spec.Template.Validate(specPath.Child("template", "metadata"))...)
194202

internal/webhooks/machineset.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ func (webhook *MachineSet) validate(oldMS, newMS *clusterv1.MachineSet) error {
229229
}
230230
}
231231

232+
allErrs = append(allErrs, validateMachineTaints(newMS.Spec.Template.Spec.Taints, specPath.Child("template", "spec", "taints"))...)
233+
allErrs = append(allErrs, validateMachineTaintsForWorkers(newMS.Spec.Template.Spec.Taints, nil, specPath.Child("template", "spec", "taints"))...)
234+
232235
allErrs = append(allErrs, validateMSMachineNaming(newMS.Spec.MachineNaming, specPath.Child("machineNaming"))...)
233236

234237
// Validate the metadata of the template.

0 commit comments

Comments
 (0)