Skip to content

Commit 70e5b8e

Browse files
authored
Merge pull request #2641 from jackfrancis/azuremachinetemplate-webhook-capi-1.2
AzureMachineTemplate webhooks dry-run
2 parents 508812f + e6fa459 commit 70e5b8e

File tree

2 files changed

+65
-22
lines changed

2 files changed

+65
-22
lines changed

api/v1beta1/azuremachinetemplate_webhook.go

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ limitations under the License.
1717
package v1beta1
1818

1919
import (
20+
"context"
21+
"fmt"
2022
"reflect"
2123

2224
apierrors "k8s.io/apimachinery/pkg/api/errors"
2325
"k8s.io/apimachinery/pkg/runtime"
2426
"k8s.io/apimachinery/pkg/util/validation/field"
27+
"sigs.k8s.io/cluster-api/util/topology"
2528
ctrl "sigs.k8s.io/controller-runtime"
2629
"sigs.k8s.io/controller-runtime/pkg/webhook"
30+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2731
)
2832

2933
// AzureMachineTemplateImmutableMsg ...
@@ -36,40 +40,50 @@ const (
3640
func (r *AzureMachineTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error {
3741
return ctrl.NewWebhookManagedBy(mgr).
3842
For(r).
43+
WithValidator(r).
44+
WithDefaulter(r).
3945
Complete()
4046
}
4147

4248
// +kubebuilder:webhook:verbs=create;update,path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-azuremachinetemplate,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=azuremachinetemplates,versions=v1beta1,name=default.azuremachinetemplate.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
4349
// +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-azuremachinetemplate,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=azuremachinetemplates,versions=v1beta1,name=validation.azuremachinetemplate.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
4450

45-
var _ webhook.Defaulter = &AzureMachineTemplate{}
46-
var _ webhook.Validator = &AzureMachineTemplate{}
51+
var _ webhook.CustomDefaulter = &AzureMachineTemplate{}
52+
var _ webhook.CustomValidator = &AzureMachineTemplate{}
4753

48-
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type.
49-
func (r *AzureMachineTemplate) ValidateCreate() error {
50-
spec := r.Spec.Template.Spec
54+
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type.
55+
func (r *AzureMachineTemplate) ValidateCreate(ctx context.Context, obj runtime.Object) error {
56+
t := obj.(*AzureMachineTemplate)
57+
spec := t.Spec.Template.Spec
5158

5259
allErrs := ValidateAzureMachineSpec(spec)
5360

54-
if r.Spec.Template.Spec.RoleAssignmentName != "" {
61+
if spec.RoleAssignmentName != "" {
5562
allErrs = append(allErrs,
56-
field.Invalid(field.NewPath("AzureMachineTemplate", "spec", "template", "spec", "roleAssignmentName"), r, AzureMachineTemplateRoleAssignmentNameMsg),
63+
field.Invalid(field.NewPath("AzureMachineTemplate", "spec", "template", "spec", "roleAssignmentName"), t, AzureMachineTemplateRoleAssignmentNameMsg),
5764
)
5865
}
5966

6067
if len(allErrs) == 0 {
6168
return nil
6269
}
6370

64-
return apierrors.NewInvalid(GroupVersion.WithKind("AzureMachineTemplate").GroupKind(), r.Name, allErrs)
71+
return apierrors.NewInvalid(GroupVersion.WithKind("AzureMachineTemplate").GroupKind(), t.Name, allErrs)
6572
}
6673

6774
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
68-
func (r *AzureMachineTemplate) ValidateUpdate(oldRaw runtime.Object) error {
75+
func (r *AzureMachineTemplate) ValidateUpdate(ctx context.Context, oldRaw runtime.Object, newRaw runtime.Object) error {
6976
var allErrs field.ErrorList
7077
old := oldRaw.(*AzureMachineTemplate)
78+
t := newRaw.(*AzureMachineTemplate)
7179

72-
if !reflect.DeepEqual(r.Spec.Template.Spec, old.Spec.Template.Spec) {
80+
req, err := admission.RequestFromContext(ctx)
81+
if err != nil {
82+
return apierrors.NewBadRequest(fmt.Sprintf("expected a admission.Request inside context: %v", err))
83+
}
84+
85+
if !topology.ShouldSkipImmutabilityChecks(req, t) &&
86+
!reflect.DeepEqual(t.Spec.Template.Spec, old.Spec.Template.Spec) {
7387
// The equality failure could be because of default mismatch between v1alpha3 and v1beta1. This happens because
7488
// the new object `r` will have run through the default webhooks but the old object `old` would not have so.
7589
// This means if the old object was in v1alpha3, it would not get the new defaults set in v1beta1 resulting
@@ -78,35 +92,41 @@ func (r *AzureMachineTemplate) ValidateUpdate(oldRaw runtime.Object) error {
7892

7993
// We need to set ssh key explicitly, otherwise Default() will create a new one.
8094
if old.Spec.Template.Spec.SSHPublicKey == "" {
81-
old.Spec.Template.Spec.SSHPublicKey = r.Spec.Template.Spec.SSHPublicKey
95+
old.Spec.Template.Spec.SSHPublicKey = t.Spec.Template.Spec.SSHPublicKey
8296
}
8397

84-
old.Default()
98+
if err := r.Default(ctx, old); err != nil {
99+
allErrs = append(allErrs,
100+
field.Invalid(field.NewPath("AzureMachineTemplate"), r, fmt.Sprintf("Unable to apply defaults: %v", err)),
101+
)
102+
}
85103

86104
// if it's still not equal, return error.
87-
if !reflect.DeepEqual(r.Spec.Template.Spec, old.Spec.Template.Spec) {
105+
if !reflect.DeepEqual(t.Spec.Template.Spec, old.Spec.Template.Spec) {
88106
allErrs = append(allErrs,
89-
field.Invalid(field.NewPath("AzureMachineTemplate", "spec", "template", "spec"), r, AzureMachineTemplateImmutableMsg),
107+
field.Invalid(field.NewPath("AzureMachineTemplate", "spec", "template", "spec"), t, AzureMachineTemplateImmutableMsg),
90108
)
91109
}
92110
}
93111

94112
if len(allErrs) == 0 {
95113
return nil
96114
}
97-
return apierrors.NewInvalid(GroupVersion.WithKind("AzureMachineTemplate").GroupKind(), r.Name, allErrs)
115+
return apierrors.NewInvalid(GroupVersion.WithKind("AzureMachineTemplate").GroupKind(), t.Name, allErrs)
98116
}
99117

100118
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type.
101-
func (r *AzureMachineTemplate) ValidateDelete() error {
119+
func (r *AzureMachineTemplate) ValidateDelete(ctx context.Context, obj runtime.Object) error {
102120
return nil
103121
}
104122

105123
// Default implements webhookutil.defaulter so a webhook will be registered for the type.
106-
func (r *AzureMachineTemplate) Default() {
107-
if err := r.Spec.Template.Spec.SetDefaultSSHPublicKey(); err != nil {
124+
func (r *AzureMachineTemplate) Default(ctx context.Context, obj runtime.Object) error {
125+
t := obj.(*AzureMachineTemplate)
126+
if err := t.Spec.Template.Spec.SetDefaultSSHPublicKey(); err != nil {
108127
ctrl.Log.WithName("SetDefault").Error(err, "SetDefaultSSHPublicKey failed")
109128
}
110-
r.Spec.Template.Spec.SetDefaultCachingType()
111-
r.Spec.Template.Spec.SetDataDisksDefaults()
129+
t.Spec.Template.Spec.SetDefaultCachingType()
130+
t.Spec.Template.Spec.SetDataDisksDefaults()
131+
return nil
112132
}

api/v1beta1/azuremachinetemplate_webhook_test.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ limitations under the License.
1717
package v1beta1
1818

1919
import (
20+
"context"
2021
"testing"
2122

2223
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute"
2324
"github.com/Azure/go-autorest/autorest/to"
2425
. "github.com/onsi/gomega"
26+
admissionv1 "k8s.io/api/admission/v1"
2527
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/utils/pointer"
29+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2630
)
2731

2832
func TestAzureMachineTemplate_ValidateCreate(t *testing.T) {
@@ -132,8 +136,11 @@ func TestAzureMachineTemplate_ValidateCreate(t *testing.T) {
132136
}
133137

134138
for _, test := range tests {
139+
test := test
135140
t.Run(test.name, func(t *testing.T) {
136-
err := test.machineTemplate.ValidateCreate()
141+
t.Parallel()
142+
ctx := context.Background()
143+
err := test.machineTemplate.ValidateCreate(ctx, test.machineTemplate)
137144
if test.wantErr {
138145
g.Expect(err).To(HaveOccurred())
139146
} else {
@@ -321,11 +328,27 @@ func TestAzureMachineTemplate_ValidateUpdate(t *testing.T) {
321328
},
322329
}
323330

331+
// dry-run=true
332+
for _, amt := range tests {
333+
amt := amt
334+
t.Run(amt.name, func(t *testing.T) {
335+
t.Parallel()
336+
ctx := admission.NewContextWithRequest(context.Background(), admission.Request{AdmissionRequest: admissionv1.AdmissionRequest{DryRun: pointer.Bool(true)}})
337+
err := amt.template.ValidateUpdate(ctx, amt.oldTemplate, amt.template)
338+
if amt.wantErr {
339+
g.Expect(err).To(HaveOccurred())
340+
} else {
341+
g.Expect(err).NotTo(HaveOccurred())
342+
}
343+
})
344+
}
345+
// dry-run=false
324346
for _, amt := range tests {
325347
amt := amt
326348
t.Run(amt.name, func(t *testing.T) {
327349
t.Parallel()
328-
err := amt.template.ValidateUpdate(amt.oldTemplate)
350+
ctx := admission.NewContextWithRequest(context.Background(), admission.Request{AdmissionRequest: admissionv1.AdmissionRequest{DryRun: pointer.Bool(false)}})
351+
err := amt.template.ValidateUpdate(ctx, amt.oldTemplate, amt.template)
329352
if amt.wantErr {
330353
g.Expect(err).To(HaveOccurred())
331354
} else {

0 commit comments

Comments
 (0)