Skip to content

Commit 155de2c

Browse files
committed
KCPTemplate & DockerClusterTemplate webhook: default before immutability check
Signed-off-by: Stefan Büringer buringerst@vmware.com
1 parent 9dc9c8f commit 155de2c

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

controlplane/kubeadm/internal/webhooks/kubeadmcontrolplanetemplate.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func (webhook *KubeadmControlPlaneTemplate) ValidateCreate(_ context.Context, ob
9595
}
9696

9797
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
98-
func (webhook *KubeadmControlPlaneTemplate) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
98+
func (webhook *KubeadmControlPlaneTemplate) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
9999
var allErrs field.ErrorList
100100

101101
oldK, ok := oldObj.(*controlplanev1.KubeadmControlPlaneTemplate)
@@ -108,6 +108,13 @@ func (webhook *KubeadmControlPlaneTemplate) ValidateUpdate(_ context.Context, ol
108108
return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a KubeadmControlPlaneTemplate but got a %T", newObj))
109109
}
110110

111+
if err := webhook.Default(ctx, oldK); err != nil {
112+
return nil, apierrors.NewBadRequest(fmt.Sprintf("failed to compare old and new KubeadmControlPlaneTemplate: failed to default old object: %v", err))
113+
}
114+
if err := webhook.Default(ctx, newK); err != nil {
115+
return nil, apierrors.NewBadRequest(fmt.Sprintf("failed to compare old and new KubeadmControlPlaneTemplate: failed to default new object: %v", err))
116+
}
117+
111118
if !reflect.DeepEqual(newK.Spec.Template.Spec, oldK.Spec.Template.Spec) {
112119
allErrs = append(allErrs,
113120
field.Invalid(field.NewPath("spec", "template", "spec"), newK, kubeadmControlPlaneTemplateImmutableMsg),

controlplane/kubeadm/internal/webhooks/kubeadmcontrolplanetemplate_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,77 @@ func TestKubeadmControlPlaneTemplateValidationMetadata(t *testing.T) {
156156
g.Expect(warnings).To(BeEmpty())
157157
})
158158
}
159+
160+
func TestKubeadmControlPlaneTemplateUpdateValidation(t *testing.T) {
161+
t.Run("update KubeadmControlPlaneTemplate should pass if only defaulted fields are different", func(t *testing.T) {
162+
g := NewWithT(t)
163+
oldKCPTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
164+
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
165+
Template: controlplanev1.KubeadmControlPlaneTemplateResource{
166+
Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
167+
MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
168+
NodeDrainTimeout: &metav1.Duration{Duration: time.Duration(10) * time.Minute},
169+
},
170+
},
171+
},
172+
},
173+
}
174+
newKCPTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
175+
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
176+
Template: controlplanev1.KubeadmControlPlaneTemplateResource{
177+
Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
178+
KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
179+
// Only this field is different, but defaulting will set it as well, so this should pass the immutability check.
180+
Format: bootstrapv1.CloudConfig,
181+
},
182+
MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
183+
NodeDrainTimeout: &metav1.Duration{Duration: time.Duration(10) * time.Minute},
184+
},
185+
},
186+
},
187+
},
188+
}
189+
webhook := &KubeadmControlPlaneTemplate{}
190+
warnings, err := webhook.ValidateUpdate(ctx, oldKCPTemplate, newKCPTemplate)
191+
g.Expect(err).ToNot(HaveOccurred())
192+
g.Expect(warnings).To(BeEmpty())
193+
})
194+
t.Run("update kubeadmcontrolplanetemplate should not pass if fields are different", func(t *testing.T) {
195+
g := NewWithT(t)
196+
oldKCPTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
197+
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
198+
Template: controlplanev1.KubeadmControlPlaneTemplateResource{
199+
Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
200+
MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
201+
NodeDrainTimeout: &metav1.Duration{Duration: time.Duration(10) * time.Minute},
202+
},
203+
},
204+
},
205+
},
206+
}
207+
newKCPTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
208+
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
209+
Template: controlplanev1.KubeadmControlPlaneTemplateResource{
210+
Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
211+
KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
212+
// Defaulting will set this field as well.
213+
Format: bootstrapv1.CloudConfig,
214+
// This will fail the immutability check.
215+
PreKubeadmCommands: []string{
216+
"new-cmd",
217+
},
218+
},
219+
MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
220+
NodeDrainTimeout: &metav1.Duration{Duration: time.Duration(10) * time.Minute},
221+
},
222+
},
223+
},
224+
},
225+
}
226+
webhook := &KubeadmControlPlaneTemplate{}
227+
warnings, err := webhook.ValidateUpdate(ctx, oldKCPTemplate, newKCPTemplate)
228+
g.Expect(err).To(HaveOccurred())
229+
g.Expect(err.Error()).To(ContainSubstring("KubeadmControlPlaneTemplate spec.template.spec field is immutable"))
230+
g.Expect(warnings).To(BeEmpty())
231+
})
232+
}

test/infrastructure/docker/internal/webhooks/dockerclustertemplate_webhook.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (webhook *DockerClusterTemplate) ValidateCreate(_ context.Context, obj runt
9191
}
9292

9393
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
94-
func (webhook *DockerClusterTemplate) ValidateUpdate(_ context.Context, oldRaw, newRaw runtime.Object) (admission.Warnings, error) {
94+
func (webhook *DockerClusterTemplate) ValidateUpdate(ctx context.Context, oldRaw, newRaw runtime.Object) (admission.Warnings, error) {
9595
var allErrs field.ErrorList
9696
oldTemplate, ok := oldRaw.(*infrav1.DockerClusterTemplate)
9797
if !ok {
@@ -101,6 +101,14 @@ func (webhook *DockerClusterTemplate) ValidateUpdate(_ context.Context, oldRaw,
101101
if !ok {
102102
return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a DockerClusterTemplate but got a %T", newRaw))
103103
}
104+
105+
if err := webhook.Default(ctx, oldTemplate); err != nil {
106+
return nil, apierrors.NewBadRequest(fmt.Sprintf("failed to compare old and new DockerClusterTemplate: failed to default old object: %v", err))
107+
}
108+
if err := webhook.Default(ctx, newTemplate); err != nil {
109+
return nil, apierrors.NewBadRequest(fmt.Sprintf("failed to compare old and new DockerClusterTemplate: failed to default new object: %v", err))
110+
}
111+
104112
if !reflect.DeepEqual(newTemplate.Spec.Template.Spec, oldTemplate.Spec.Template.Spec) {
105113
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "template", "spec"), newTemplate, dockerClusterTemplateImmutableMsg))
106114
}

0 commit comments

Comments
 (0)