Skip to content

Commit f468c2e

Browse files
webhook: sort on version if names are equal
1 parent bf7d6b7 commit f468c2e

File tree

8 files changed

+147
-0
lines changed

8 files changed

+147
-0
lines changed

pkg/webhook/parser.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
516516
versionedWebhooks := make(map[string][]interface{}, len(supportedWebhookVersions))
517517
for _, version := range supportedWebhookVersions {
518518
if cfgs, ok := mutatingCfgs[version]; ok {
519+
sort.SliceStable(cfgs, func(i, j int) bool {
520+
return cfgs[i].Name < cfgs[j].Name
521+
})
519522
var objRaw *admissionregv1.MutatingWebhookConfiguration
520523
if mutatingWebhookCfgs.Name != "" {
521524
objRaw = &mutatingWebhookCfgs
@@ -553,6 +556,9 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
553556
}
554557

555558
if cfgs, ok := validatingCfgs[version]; ok {
559+
sort.SliceStable(cfgs, func(i, j int) bool {
560+
return cfgs[i].Name < cfgs[j].Name
561+
})
556562
var objRaw *admissionregv1.ValidatingWebhookConfiguration
557563
if validatingWebhookCfgs.Name != "" {
558564
objRaw = &validatingWebhookCfgs

pkg/webhook/parser_integration_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,77 @@ var _ = Describe("Webhook Generation From Parsing to CustomResourceDefinition",
426426
assertSame(actualValidating, expectedValidating)
427427
})
428428

429+
It("should keep webhook order stable across package traversal orders", func() {
430+
By("switching into testdata to appease go modules")
431+
cwd, err := os.Getwd()
432+
Expect(err).NotTo(HaveOccurred())
433+
Expect(os.Chdir("./testdata/valid-crosspkg-stable")).To(Succeed()) // go modules are directory-sensitive
434+
defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }()
435+
436+
By("loading the roots in one order")
437+
pkgsA, err := loader.LoadRoots("./v1", "./v1alpha1")
438+
Expect(err).NotTo(HaveOccurred())
439+
Expect(pkgsA).To(HaveLen(2))
440+
441+
By("setting up the parser")
442+
reg := &markers.Registry{}
443+
Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed())
444+
Expect(reg.Register(webhook.WebhookConfigDefinition)).To(Succeed())
445+
446+
By("generating manifests for order A")
447+
outputDirA, err := os.MkdirTemp("", "webhook-integration-test-order-a")
448+
Expect(err).NotTo(HaveOccurred())
449+
defer os.RemoveAll(outputDirA)
450+
genCtxA := &genall.GenerationContext{
451+
Collector: &markers.Collector{Registry: reg},
452+
Roots: pkgsA,
453+
OutputRule: genall.OutputToDirectory(outputDirA),
454+
}
455+
Expect(webhook.Generator{}.Generate(genCtxA)).To(Succeed())
456+
for _, r := range genCtxA.Roots {
457+
Expect(r.Errors).To(HaveLen(0))
458+
}
459+
460+
By("loading the generated v1 YAML for order A")
461+
actualFileA, err := os.ReadFile(path.Join(outputDirA, "manifests.yaml"))
462+
Expect(err).NotTo(HaveOccurred())
463+
actualManifestA := &admissionregv1.ValidatingWebhookConfiguration{}
464+
Expect(yaml.UnmarshalStrict(actualFileA, actualManifestA)).To(Succeed())
465+
466+
By("loading the roots in the reverse order")
467+
pkgsB := []*loader.Package{pkgsA[1], pkgsA[0]}
468+
469+
By("generating manifests for order B")
470+
outputDirB, err := os.MkdirTemp("", "webhook-integration-test-order-b")
471+
Expect(err).NotTo(HaveOccurred())
472+
defer os.RemoveAll(outputDirB)
473+
genCtxB := &genall.GenerationContext{
474+
Collector: &markers.Collector{Registry: reg},
475+
Roots: pkgsB,
476+
OutputRule: genall.OutputToDirectory(outputDirB),
477+
}
478+
Expect(webhook.Generator{}.Generate(genCtxB)).To(Succeed())
479+
for _, r := range genCtxB.Roots {
480+
Expect(r.Errors).To(HaveLen(0))
481+
}
482+
483+
By("loading the generated v1 YAML for order B")
484+
actualFileB, err := os.ReadFile(path.Join(outputDirB, "manifests.yaml"))
485+
Expect(err).NotTo(HaveOccurred())
486+
actualManifestB := &admissionregv1.ValidatingWebhookConfiguration{}
487+
Expect(yaml.UnmarshalStrict(actualFileB, actualManifestB)).To(Succeed())
488+
489+
By("loading the desired v1 YAML")
490+
expectedFile, err := os.ReadFile("manifests.yaml")
491+
Expect(err).NotTo(HaveOccurred())
492+
expectedManifest := &admissionregv1.ValidatingWebhookConfiguration{}
493+
Expect(yaml.UnmarshalStrict(expectedFile, expectedManifest)).To(Succeed())
494+
495+
By("comparing manifests across orders and to expected")
496+
assertSame(actualManifestA, actualManifestB)
497+
assertSame(actualManifestA, expectedManifest)
498+
})
499+
429500
It("should fail to generate when there are multiple `kubebuilder:webhookconfiguration` markers of the same mutation type", func() {
430501
By("switching into testdata to appease go modules")
431502
cwd, err := os.Getwd()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module sigs.k8s.io/controller-tools/pkg/webhook/testdata/valid-crosspkg-stable
2+
3+
go 1.22
4+
5+
require (
6+
sigs.k8s.io/controller-runtime v0.18.5
7+
k8s.io/api v0.34.1
8+
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
apiVersion: admissionregistration.k8s.io/v1
3+
kind: ValidatingWebhookConfiguration
4+
metadata:
5+
name: validating-webhook-configuration
6+
webhooks:
7+
- admissionReviewVersions:
8+
- v1
9+
- v1beta1
10+
clientConfig:
11+
service:
12+
name: webhook-service
13+
namespace: system
14+
path: /validate-testdata-kubebuilder-io-v1alpha1-cronjob
15+
failurePolicy: Fail
16+
matchPolicy: Equivalent
17+
name: a.cronjob.testdata.kubebuilder.io
18+
rules:
19+
- apiGroups:
20+
- testdata.kubebuilder.io
21+
apiVersions:
22+
- v1alpha1
23+
operations:
24+
- CREATE
25+
- UPDATE
26+
resources:
27+
- cronjobs
28+
sideEffects: None
29+
- admissionReviewVersions:
30+
- v1
31+
- v1beta1
32+
clientConfig:
33+
service:
34+
name: webhook-service
35+
namespace: system
36+
path: /validate-testdata-kubebuilder-io-v1-cronjob
37+
failurePolicy: Fail
38+
matchPolicy: Equivalent
39+
name: validation.cronjob.testdata.kubebuilder.io
40+
rules:
41+
- apiGroups:
42+
- testdata.kubebuilder.io
43+
apiVersions:
44+
- v1
45+
operations:
46+
- CREATE
47+
- UPDATE
48+
resources:
49+
- cronjobs
50+
sideEffects: None
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package v1
2+
3+
type CronJob struct{}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package v1
2+
3+
// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuilder.io,resources=cronjobs,versions=v1,name=validation.cronjob.testdata.kubebuilder.io,sideEffects=None,admissionReviewVersions=v1;v1beta1,path=/validate-testdata-kubebuilder-io-v1-cronjob
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package v1alpha1
2+
3+
type CronJob struct{}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package v1alpha1
2+
3+
// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuilder.io,resources=cronjobs,versions=v1alpha1,name=a.cronjob.testdata.kubebuilder.io,sideEffects=None,admissionReviewVersions=v1;v1beta1,path=/validate-testdata-kubebuilder-io-v1alpha1-cronjob

0 commit comments

Comments
 (0)