Skip to content

Commit ef27db8

Browse files
authored
Merge pull request #8431 from LuBingtan/add-validation-metadata
✨ Add validation to nested ObjectMeta fields
2 parents ab50d0b + f0caf10 commit ef27db8

20 files changed

+473
-4
lines changed

api/v1beta1/common_types.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ package v1beta1
1818

1919
import (
2020
corev1 "k8s.io/api/core/v1"
21+
apivalidation "k8s.io/apimachinery/pkg/api/validation"
2122
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
24+
"k8s.io/apimachinery/pkg/util/validation/field"
2225
)
2326

2427
const (
@@ -301,3 +304,16 @@ type ObjectMeta struct {
301304
// +optional
302305
Annotations map[string]string `json:"annotations,omitempty"`
303306
}
307+
308+
// Validate validates the labels and annotations in ObjectMeta.
309+
func (metadata *ObjectMeta) Validate(parent *field.Path) field.ErrorList {
310+
allErrs := metav1validation.ValidateLabels(
311+
metadata.Labels,
312+
parent.Child("labels"),
313+
)
314+
allErrs = append(allErrs, apivalidation.ValidateAnnotations(
315+
metadata.Annotations,
316+
parent.Child("annotations"),
317+
)...)
318+
return allErrs
319+
}

api/v1beta1/machinedeployment_webhook.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ func (m *MachineDeployment) validate(old *MachineDeployment) error {
266266
}
267267
}
268268

269+
// Validate the metadata of the template.
270+
allErrs = append(allErrs, m.Spec.Template.ObjectMeta.Validate(specPath.Child("template", "metadata"))...)
271+
269272
if len(allErrs) == 0 {
270273
return nil
271274
}

api/v1beta1/machinedeployment_webhook_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1beta1
1818

1919
import (
2020
"context"
21+
"strings"
2122
"testing"
2223

2324
. "github.com/onsi/gomega"
@@ -569,3 +570,56 @@ func defaultValidateTestCustomDefaulter(object admission.Validator, customDefaul
569570
})
570571
}
571572
}
573+
574+
func TestMachineDeploymentTemplateMetadataValidation(t *testing.T) {
575+
tests := []struct {
576+
name string
577+
labels map[string]string
578+
annotations map[string]string
579+
expectErr bool
580+
}{
581+
{
582+
name: "should return error for invalid labels and annotations",
583+
labels: map[string]string{
584+
"foo": "$invalid-key",
585+
"bar": strings.Repeat("a", 64) + "too-long-value",
586+
"/invalid-key": "foo",
587+
},
588+
annotations: map[string]string{
589+
"/invalid-key": "foo",
590+
},
591+
expectErr: true,
592+
},
593+
}
594+
595+
for _, tt := range tests {
596+
t.Run(tt.name, func(t *testing.T) {
597+
g := NewWithT(t)
598+
md := &MachineDeployment{
599+
Spec: MachineDeploymentSpec{
600+
Template: MachineTemplateSpec{
601+
ObjectMeta: ObjectMeta{
602+
Labels: tt.labels,
603+
Annotations: tt.annotations,
604+
},
605+
},
606+
},
607+
}
608+
if tt.expectErr {
609+
warnings, err := md.ValidateCreate()
610+
g.Expect(err).To(HaveOccurred())
611+
g.Expect(warnings).To(BeEmpty())
612+
warnings, err = md.ValidateUpdate(md)
613+
g.Expect(err).To(HaveOccurred())
614+
g.Expect(warnings).To(BeEmpty())
615+
} else {
616+
warnings, err := md.ValidateCreate()
617+
g.Expect(err).ToNot(HaveOccurred())
618+
g.Expect(warnings).To(BeEmpty())
619+
warnings, err = md.ValidateUpdate(md)
620+
g.Expect(err).ToNot(HaveOccurred())
621+
g.Expect(warnings).To(BeEmpty())
622+
}
623+
})
624+
}
625+
}

api/v1beta1/machineset_webhook.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ func (m *MachineSet) validate(old *MachineSet) error {
152152
}
153153
}
154154

155+
// Validate the metadata of the template.
156+
allErrs = append(allErrs, m.Spec.Template.ObjectMeta.Validate(specPath.Child("template", "metadata"))...)
157+
155158
if len(allErrs) == 0 {
156159
return nil
157160
}

api/v1beta1/machineset_webhook_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1beta1
1818

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

2223
. "github.com/onsi/gomega"
@@ -307,3 +308,56 @@ func TestValidateSkippedMachineSetPreflightChecks(t *testing.T) {
307308
})
308309
}
309310
}
311+
312+
func TestMachineSetTemplateMetadataValidation(t *testing.T) {
313+
tests := []struct {
314+
name string
315+
labels map[string]string
316+
annotations map[string]string
317+
expectErr bool
318+
}{
319+
{
320+
name: "should return error for invalid labels and annotations",
321+
labels: map[string]string{
322+
"foo": "$invalid-key",
323+
"bar": strings.Repeat("a", 64) + "too-long-value",
324+
"/invalid-key": "foo",
325+
},
326+
annotations: map[string]string{
327+
"/invalid-key": "foo",
328+
},
329+
expectErr: true,
330+
},
331+
}
332+
333+
for _, tt := range tests {
334+
t.Run(tt.name, func(t *testing.T) {
335+
g := NewWithT(t)
336+
ms := &MachineSet{
337+
Spec: MachineSetSpec{
338+
Template: MachineTemplateSpec{
339+
ObjectMeta: ObjectMeta{
340+
Labels: tt.labels,
341+
Annotations: tt.annotations,
342+
},
343+
},
344+
},
345+
}
346+
if tt.expectErr {
347+
warnings, err := ms.ValidateCreate()
348+
g.Expect(err).To(HaveOccurred())
349+
g.Expect(warnings).To(BeEmpty())
350+
warnings, err = ms.ValidateUpdate(ms)
351+
g.Expect(err).To(HaveOccurred())
352+
g.Expect(warnings).To(BeEmpty())
353+
} else {
354+
warnings, err := ms.ValidateCreate()
355+
g.Expect(err).ToNot(HaveOccurred())
356+
g.Expect(warnings).To(BeEmpty())
357+
warnings, err = ms.ValidateUpdate(ms)
358+
g.Expect(err).ToNot(HaveOccurred())
359+
g.Expect(warnings).To(BeEmpty())
360+
}
361+
})
362+
}
363+
}

bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ func (r *KubeadmConfigTemplateSpec) validate(name string) error {
6363
var allErrs field.ErrorList
6464

6565
allErrs = append(allErrs, r.Template.Spec.Validate(field.NewPath("spec", "template", "spec"))...)
66+
// Validate the metadata of the template.
67+
allErrs = append(allErrs, r.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))...)
6668

6769
if len(allErrs) == 0 {
6870
return nil

bootstrap/kubeadm/api/v1beta1/kubeadmconfigtemplate_webhook_test.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ limitations under the License.
1717
package v1beta1_test
1818

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

2223
. "github.com/onsi/gomega"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2425
"k8s.io/utils/pointer"
2526

27+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2628
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
2729
utildefaulting "sigs.k8s.io/cluster-api/util/defaulting"
2830
)
@@ -46,7 +48,8 @@ func TestKubeadmConfigTemplateDefault(t *testing.T) {
4648

4749
func TestKubeadmConfigTemplateValidation(t *testing.T) {
4850
cases := map[string]struct {
49-
in *bootstrapv1.KubeadmConfigTemplate
51+
in *bootstrapv1.KubeadmConfigTemplate
52+
expectErr bool
5053
}{
5154
"valid configuration": {
5255
in: &bootstrapv1.KubeadmConfigTemplate{
@@ -61,6 +64,21 @@ func TestKubeadmConfigTemplateValidation(t *testing.T) {
6164
},
6265
},
6366
},
67+
"should return error for invalid labels and annotations": {
68+
in: &bootstrapv1.KubeadmConfigTemplate{Spec: bootstrapv1.KubeadmConfigTemplateSpec{
69+
Template: bootstrapv1.KubeadmConfigTemplateResource{ObjectMeta: clusterv1.ObjectMeta{
70+
Labels: map[string]string{
71+
"foo": "$invalid-key",
72+
"bar": strings.Repeat("a", 64) + "too-long-value",
73+
"/invalid-key": "foo",
74+
},
75+
Annotations: map[string]string{
76+
"/invalid-key": "foo",
77+
},
78+
}},
79+
}},
80+
expectErr: true,
81+
},
6482
}
6583

6684
for name, tt := range cases {
@@ -69,10 +87,18 @@ func TestKubeadmConfigTemplateValidation(t *testing.T) {
6987
t.Run(name, func(t *testing.T) {
7088
g := NewWithT(t)
7189
warnings, err := tt.in.ValidateCreate()
72-
g.Expect(err).ToNot(HaveOccurred())
90+
if tt.expectErr {
91+
g.Expect(err).To(HaveOccurred())
92+
} else {
93+
g.Expect(err).ToNot(HaveOccurred())
94+
}
7395
g.Expect(warnings).To(BeEmpty())
7496
warnings, err = tt.in.ValidateUpdate(nil)
75-
g.Expect(err).ToNot(HaveOccurred())
97+
if tt.expectErr {
98+
g.Expect(err).To(HaveOccurred())
99+
} else {
100+
g.Expect(err).ToNot(HaveOccurred())
101+
}
76102
g.Expect(warnings).To(BeEmpty())
77103
})
78104
}

controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ func validateKubeadmControlPlaneSpec(s KubeadmControlPlaneSpec, namespace string
330330
)
331331
}
332332

333+
// Validate the metadata of the MachineTemplate
334+
allErrs = append(allErrs, s.MachineTemplate.ObjectMeta.Validate(pathPrefix.Child("machineTemplate", "metadata"))...)
335+
333336
if !version.KubeSemver.MatchString(s.Version) {
334337
allErrs = append(allErrs, field.Invalid(pathPrefix.Child("version"), s.Version, "must be a valid semantic version"))
335338
}

controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1beta1
1818

1919
import (
20+
"strings"
2021
"testing"
2122
"time"
2223

@@ -152,6 +153,16 @@ func TestKubeadmControlPlaneValidateCreate(t *testing.T) {
152153
validIgnitionConfiguration.Spec.KubeadmConfigSpec.Format = bootstrapv1.Ignition
153154
validIgnitionConfiguration.Spec.KubeadmConfigSpec.Ignition = &bootstrapv1.IgnitionSpec{}
154155

156+
invalidMetadata := valid.DeepCopy()
157+
invalidMetadata.Spec.MachineTemplate.ObjectMeta.Labels = map[string]string{
158+
"foo": "$invalid-key",
159+
"bar": strings.Repeat("a", 64) + "too-long-value",
160+
"/invalid-key": "foo",
161+
}
162+
invalidMetadata.Spec.MachineTemplate.ObjectMeta.Annotations = map[string]string{
163+
"/invalid-key": "foo",
164+
}
165+
155166
tests := []struct {
156167
name string
157168
enableIgnitionFeature bool
@@ -236,6 +247,12 @@ func TestKubeadmControlPlaneValidateCreate(t *testing.T) {
236247
expectErr: false,
237248
kcp: validIgnitionConfiguration,
238249
},
250+
{
251+
name: "should return error for invalid metadata",
252+
enableIgnitionFeature: true,
253+
expectErr: true,
254+
kcp: invalidMetadata,
255+
},
239256
}
240257

241258
for _, tt := range tests {
@@ -671,6 +688,16 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
671688
{"/var/lib/testdir", "/var/lib/etcd/data"},
672689
}
673690

691+
invalidMetadata := before.DeepCopy()
692+
invalidMetadata.Spec.MachineTemplate.ObjectMeta.Labels = map[string]string{
693+
"foo": "$invalid-key",
694+
"bar": strings.Repeat("a", 64) + "too-long-value",
695+
"/invalid-key": "foo",
696+
}
697+
invalidMetadata.Spec.MachineTemplate.ObjectMeta.Annotations = map[string]string{
698+
"/invalid-key": "foo",
699+
}
700+
674701
tests := []struct {
675702
name string
676703
enableIgnitionFeature bool
@@ -1016,6 +1043,13 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
10161043
before: before,
10171044
kcp: switchFromCloudInitToIgnition,
10181045
},
1046+
{
1047+
name: "should return error for invalid metadata",
1048+
enableIgnitionFeature: true,
1049+
expectErr: true,
1050+
before: before,
1051+
kcp: invalidMetadata,
1052+
},
10191053
}
10201054

10211055
for _, tt := range tests {

controlplane/kubeadm/api/v1beta1/kubeadmcontrolplanetemplate_webhook.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ func (r *KubeadmControlPlaneTemplate) ValidateCreate() (admission.Warnings, erro
6969
allErrs := validateKubeadmControlPlaneTemplateResourceSpec(spec, field.NewPath("spec", "template", "spec"))
7070
allErrs = append(allErrs, validateClusterConfiguration(spec.KubeadmConfigSpec.ClusterConfiguration, nil, field.NewPath("spec", "template", "spec", "kubeadmConfigSpec", "clusterConfiguration"))...)
7171
allErrs = append(allErrs, spec.KubeadmConfigSpec.Validate(field.NewPath("spec", "template", "spec", "kubeadmConfigSpec"))...)
72+
// Validate the metadata of the KubeadmControlPlaneTemplateResource
73+
allErrs = append(allErrs, r.Spec.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))...)
7274
if len(allErrs) > 0 {
7375
return nil, apierrors.NewInvalid(GroupVersion.WithKind("KubeadmControlPlaneTemplate").GroupKind(), r.Name, allErrs)
7476
}
@@ -108,5 +110,10 @@ func validateKubeadmControlPlaneTemplateResourceSpec(s KubeadmControlPlaneTempla
108110
allErrs = append(allErrs, validateRolloutBefore(s.RolloutBefore, pathPrefix.Child("rolloutBefore"))...)
109111
allErrs = append(allErrs, validateRolloutStrategy(s.RolloutStrategy, nil, pathPrefix.Child("rolloutStrategy"))...)
110112

113+
if s.MachineTemplate != nil {
114+
// Validate the metadata of the MachineTemplate
115+
allErrs = append(allErrs, s.MachineTemplate.ObjectMeta.Validate(pathPrefix.Child("machineTemplate", "metadata"))...)
116+
}
117+
111118
return allErrs
112119
}

0 commit comments

Comments
 (0)