From ed8e31df49ceb9ce889a8b649a89331df7843042 Mon Sep 17 00:00:00 2001 From: Yongxiu Cui Date: Thu, 7 Dec 2023 13:27:54 -0800 Subject: [PATCH] Fix controller-tools doesn't support single files as input https://github.com/kubernetes-sigs/controller-tools/issues/837 --- pkg/crd/gen_single_file_integration_test.go | 108 +++++++++++++ pkg/crd/testdata/multiple_files/file_one.go | 45 ++++++ pkg/crd/testdata/multiple_files/file_two.go | 34 ++++ .../multiple_files/file_two_reference.go | 31 ++++ .../multiple_files/one.example.com.yaml | 51 ++++++ .../multiple_files/two.example.com.yaml | 51 ++++++ pkg/loader/loader.go | 20 +-- pkg/loader/loader_test.go | 120 +++++++------- pkg/rbac/parser_integration_test.go | 52 +++++- .../testdata/{ => directory}/controller.go | 0 pkg/rbac/testdata/{ => directory}/role.yaml | 0 pkg/rbac/testdata/file/another_controller.go | 7 + pkg/rbac/testdata/file/controller.go | 13 ++ pkg/rbac/testdata/file/role.yaml | 88 ++++++++++ pkg/webhook/parser_integration_test.go | 135 ++++++++++++++++ .../testdata/valid-single/cronjob_types.go | 71 ++++++++ .../testdata/valid-single/cronjobtwo_types.go | 71 ++++++++ .../testdata/valid-single/manifests_all.yaml | 152 ++++++++++++++++++ .../testdata/valid-single/manifests_one.yaml | 82 ++++++++++ .../testdata/valid-single/manifests_two.yaml | 82 ++++++++++ .../testdata/valid-single/webhook_one.go | 50 ++++++ .../testdata/valid-single/webhook_two.go | 50 ++++++ 22 files changed, 1242 insertions(+), 71 deletions(-) create mode 100644 pkg/crd/gen_single_file_integration_test.go create mode 100644 pkg/crd/testdata/multiple_files/file_one.go create mode 100644 pkg/crd/testdata/multiple_files/file_two.go create mode 100644 pkg/crd/testdata/multiple_files/file_two_reference.go create mode 100644 pkg/crd/testdata/multiple_files/one.example.com.yaml create mode 100644 pkg/crd/testdata/multiple_files/two.example.com.yaml rename pkg/rbac/testdata/{ => directory}/controller.go (100%) rename pkg/rbac/testdata/{ => directory}/role.yaml (100%) create mode 100644 pkg/rbac/testdata/file/another_controller.go create mode 100644 pkg/rbac/testdata/file/controller.go create mode 100644 pkg/rbac/testdata/file/role.yaml create mode 100644 pkg/webhook/testdata/valid-single/cronjob_types.go create mode 100644 pkg/webhook/testdata/valid-single/cronjobtwo_types.go create mode 100644 pkg/webhook/testdata/valid-single/manifests_all.yaml create mode 100644 pkg/webhook/testdata/valid-single/manifests_one.yaml create mode 100644 pkg/webhook/testdata/valid-single/manifests_two.yaml create mode 100644 pkg/webhook/testdata/valid-single/webhook_one.go create mode 100644 pkg/webhook/testdata/valid-single/webhook_two.go diff --git a/pkg/crd/gen_single_file_integration_test.go b/pkg/crd/gen_single_file_integration_test.go new file mode 100644 index 000000000..bec47183f --- /dev/null +++ b/pkg/crd/gen_single_file_integration_test.go @@ -0,0 +1,108 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crd_test + +import ( + "bytes" + "os" + "path/filepath" + + "github.com/google/go-cmp/cmp" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "sigs.k8s.io/controller-tools/pkg/crd" + crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers" + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +var _ = Describe("CRD Generation golang files", func() { + var ( + ctx, ctx2 *genall.GenerationContext + out *outputRule + + genDir = filepath.Join("testdata", "multiple_files") + ) + + BeforeEach(func() { + By("switching into testdata to appease go modules") + cwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + Expect(os.Chdir(genDir)).To(Succeed()) // go modules are directory-sensitive + defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() + + By("loading the roots") + pkgs, err := loader.LoadRoots("file_one.go") + Expect(err).NotTo(HaveOccurred()) + Expect(pkgs).To(HaveLen(1)) + Expect(pkgs[0].GoFiles).To(HaveLen(1)) + pkgs2, err := loader.LoadRoots("file_two.go", "file_two_reference.go") + Expect(err).NotTo(HaveOccurred()) + Expect(pkgs2).To(HaveLen(1)) + Expect(pkgs2[0].GoFiles).To(HaveLen(2)) + + By("setup up the context") + reg := &markers.Registry{} + Expect(crdmarkers.Register(reg)).To(Succeed()) + out = &outputRule{ + buf: &bytes.Buffer{}, + } + ctx = &genall.GenerationContext{ + Collector: &markers.Collector{Registry: reg}, + Roots: pkgs, + Checker: &loader.TypeChecker{}, + OutputRule: out, + } + ctx2 = &genall.GenerationContext{ + Collector: &markers.Collector{Registry: reg}, + Roots: pkgs2, + Checker: &loader.TypeChecker{}, + OutputRule: out, + } + }) + + It("should have deterministic output for single golang file", func() { + By("calling Generate on single golang file") + gen := &crd.Generator{ + CRDVersions: []string{"v1"}, + } + Expect(gen.Generate(ctx)).NotTo(HaveOccurred()) + + By("loading the desired YAML") + expectedFileOne, err := os.ReadFile(filepath.Join(genDir, "one.example.com.yaml")) + Expect(err).NotTo(HaveOccurred()) + expectedFileOne = fixAnnotations(expectedFileOne) + expectedOut := string(expectedFileOne) + Expect(out.buf.String()).To(Equal(expectedOut), cmp.Diff(out.buf.String(), expectedOut)) + }) + It("should have deterministic output for multiple golang files referencing other types", func() { + By("calling Generate on two golang files") + gen := &crd.Generator{ + CRDVersions: []string{"v1"}, + } + Expect(gen.Generate(ctx2)).NotTo(HaveOccurred()) + + By("loading the desired YAML file") + expectedFileTwo, err := os.ReadFile(filepath.Join(genDir, "two.example.com.yaml")) + Expect(err).NotTo(HaveOccurred()) + expectedFileTwo = fixAnnotations(expectedFileTwo) + expectedOut := string(expectedFileTwo) + Expect(out.buf.String()).To(Equal(expectedOut), cmp.Diff(out.buf.String(), expectedOut)) + }) +}) diff --git a/pkg/crd/testdata/multiple_files/file_one.go b/pkg/crd/testdata/multiple_files/file_one.go new file mode 100644 index 000000000..08b5ff259 --- /dev/null +++ b/pkg/crd/testdata/multiple_files/file_one.go @@ -0,0 +1,45 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +groupName=testdata.kubebuilder.io +package multiplefiles + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + multiver "testdata.kubebuilder.io/cronjob/multiple_versions" +) + +type OneResourceSpec struct { + Struct multiver.OuterStruct `json:"struct,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:singular=oneresource + +type OneResource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OneResourceSpec `json:"spec"` +} + +// +kubebuilder:object:root=true + +type OneResourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OneResource `json:"items"` +} diff --git a/pkg/crd/testdata/multiple_files/file_two.go b/pkg/crd/testdata/multiple_files/file_two.go new file mode 100644 index 000000000..137f7d8b7 --- /dev/null +++ b/pkg/crd/testdata/multiple_files/file_two.go @@ -0,0 +1,34 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +groupName=testdata.kubebuilder.io +package multiplefiles + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:singular=tworesource + +type TwoResource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TwoResourceSpec `json:"spec"` +} + +// +kubebuilder:object:root=true diff --git a/pkg/crd/testdata/multiple_files/file_two_reference.go b/pkg/crd/testdata/multiple_files/file_two_reference.go new file mode 100644 index 000000000..699dcf0ed --- /dev/null +++ b/pkg/crd/testdata/multiple_files/file_two_reference.go @@ -0,0 +1,31 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package multiplefiles + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + multiver "testdata.kubebuilder.io/cronjob/multiple_versions" +) + +type TwoResourceSpec struct { + Struct multiver.OuterStruct `json:"struct,omitempty"` +} + +type TwoResourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TwoResource `json:"items"` +} diff --git a/pkg/crd/testdata/multiple_files/one.example.com.yaml b/pkg/crd/testdata/multiple_files/one.example.com.yaml new file mode 100644 index 000000000..54e48cad8 --- /dev/null +++ b/pkg/crd/testdata/multiple_files/one.example.com.yaml @@ -0,0 +1,51 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + name: oneresources.testdata.kubebuilder.io +spec: + group: testdata.kubebuilder.io + names: + kind: OneResource + listKind: OneResourceList + plural: oneresources + singular: oneresource + scope: Namespaced + versions: + - name: multiplefiles + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + struct: + properties: + struct: + properties: + foo: + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/crd/testdata/multiple_files/two.example.com.yaml b/pkg/crd/testdata/multiple_files/two.example.com.yaml new file mode 100644 index 000000000..b3adad85f --- /dev/null +++ b/pkg/crd/testdata/multiple_files/two.example.com.yaml @@ -0,0 +1,51 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + name: tworesources.testdata.kubebuilder.io +spec: + group: testdata.kubebuilder.io + names: + kind: TwoResource + listKind: TwoResourceList + plural: tworesources + singular: tworesource + scope: Namespaced + versions: + - name: multiplefiles + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + struct: + properties: + struct: + properties: + foo: + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go index 7762e53e7..aa8f51bca 100644 --- a/pkg/loader/loader.go +++ b/pkg/loader/loader.go @@ -389,12 +389,6 @@ func LoadRootsWithConfig(cfg *packages.Config, roots ...string) ([]*Package, err } }() - // uniquePkgIDs is used to keep track of the discovered packages to be nice - // and try and prevent packages from showing up twice when nested module - // support is enabled. there is not harm that comes from this per se, but - // it makes testing easier when a known number of modules can be asserted - uniquePkgIDs := sets.String{} - // loadPackages returns the Go packages for the provided roots // // if validatePkgFn is nil, a package will be returned in the slice, @@ -412,10 +406,7 @@ func LoadRootsWithConfig(cfg *packages.Config, roots ...string) ([]*Package, err var pkgs []*Package for _, rp := range rawPkgs { p := l.packageFor(rp) - if !uniquePkgIDs.Has(p.ID) { - pkgs = append(pkgs, p) - uniquePkgIDs.Insert(p.ID) - } + pkgs = append(pkgs, p) } return pkgs, nil } @@ -568,13 +559,14 @@ func LoadRootsWithConfig(cfg *packages.Config, roots ...string) ([]*Package, err for _, r := range fspRoots { b, d := filepath.Base(r), filepath.Dir(r) - // we want the base part of the path to be either "..." or ".", except - // Go's filepath utilities clean paths during manipulation, removing the - // ".". thus, if not "...", let's update the path components so that: + // we want the base part of the path to be either "..." or ".", except Go's + // filepath utilities clean paths during manipulation or go file path, + // removing the ".". thus, if not "..." or go file, let's update the path + // components so that: // // d = r // b = "." - if b != "..." { + if b != "..." && filepath.Ext(b) != ".go" { d = r b = "." } diff --git a/pkg/loader/loader_test.go b/pkg/loader/loader_test.go index a1ba6f339..8e20a2013 100644 --- a/pkg/loader/loader_test.go +++ b/pkg/loader/loader_test.go @@ -33,17 +33,16 @@ var _ = Describe("Loader parsing root module", func() { testmodPkg = loaderPkg + "/testmod" ) - var indexOfPackage = func(pkgID string, pkgs []*loader.Package) int { - for i := range pkgs { - if pkgs[i].ID == pkgID { - return i - } - } - return -1 + var assertPkgExists = func(pkgID string, pkgs map[string]struct{}) { + Expect(pkgs).Should(HaveKey(pkgID)) } - var assertPkgExists = func(pkgID string, pkgs []*loader.Package) { - ExpectWithOffset(1, indexOfPackage(pkgID, pkgs)).Should(BeNumerically(">", -1)) + var dedupPkgs = func(pkgs []*loader.Package) map[string]struct{} { + uniquePkgs := make(map[string]struct{}) + for _, p := range pkgs { + uniquePkgs[p.ID] = struct{}{} + } + return uniquePkgs } Context("with named packages/modules", func() { @@ -67,8 +66,9 @@ var _ = Describe("Loader parsing root module", func() { It("should load one package", func() { pkgs, err := loader.LoadRoots("sigs.k8s.io/controller-tools/pkg/loader/testmod/submod1") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(1)) - assertPkgExists(testmodPkg+"/submod1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(1)) + assertPkgExists(testmodPkg+"/submod1", uniquePkgs) }) }) @@ -76,13 +76,14 @@ var _ = Describe("Loader parsing root module", func() { It("should load six packages", func() { pkgs, err := loader.LoadRoots("sigs.k8s.io/controller-tools/pkg/loader/testmod/...") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(6)) - assertPkgExists(testmodPkg, pkgs) - assertPkgExists(testmodPkg+"/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir2", pkgs) - assertPkgExists(testmodPkg+"/submod1", pkgs) - assertPkgExists(testmodPkg+"/submod1/subdir1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(6)) + assertPkgExists(testmodPkg, uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir2", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1/subdir1", uniquePkgs) }) }) @@ -90,14 +91,15 @@ var _ = Describe("Loader parsing root module", func() { It("should load seven packages", func() { pkgs, err := loader.LoadRoots("sigs.k8s.io/controller-tools/pkg/loader/testmod/...", "./...") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(7)) - assertPkgExists(testmodPkg, pkgs) - assertPkgExists(testmodPkg+"/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir2", pkgs) - assertPkgExists(testmodPkg+"/subdir1/submod1", pkgs) - assertPkgExists(testmodPkg+"/submod1", pkgs) - assertPkgExists(testmodPkg+"/submod1/subdir1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(7)) + assertPkgExists(testmodPkg, uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir2", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/submod1", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1/subdir1", uniquePkgs) }) }) }) @@ -106,8 +108,9 @@ var _ = Describe("Loader parsing root module", func() { It("should load one package", func() { pkgs, err := loader.LoadRoots("../crd/.") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(1)) - assertPkgExists(pkgPkg+"/crd", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(1)) + assertPkgExists(pkgPkg+"/crd", uniquePkgs) }) }) @@ -115,8 +118,9 @@ var _ = Describe("Loader parsing root module", func() { It("should load one package", func() { pkgs, err := loader.LoadRoots("./") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(1)) - assertPkgExists(loaderPkg, pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(1)) + assertPkgExists(loaderPkg, uniquePkgs) }) }) @@ -124,8 +128,9 @@ var _ = Describe("Loader parsing root module", func() { It("should load one package", func() { pkgs, err := loader.LoadRoots("../../pkg/loader") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(1)) - assertPkgExists(loaderPkg, pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(1)) + assertPkgExists(loaderPkg, uniquePkgs) }) }) @@ -135,14 +140,15 @@ var _ = Describe("Loader parsing root module", func() { "../../pkg/loader/../loader/testmod/...", "./testmod/./../testmod//.") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(7)) - assertPkgExists(testmodPkg, pkgs) - assertPkgExists(testmodPkg+"/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir2", pkgs) - assertPkgExists(testmodPkg+"/subdir1/submod1", pkgs) - assertPkgExists(testmodPkg+"/submod1", pkgs) - assertPkgExists(testmodPkg+"/submod1/subdir1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(7)) + assertPkgExists(testmodPkg, uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir2", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/submod1", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1/subdir1", uniquePkgs) }) }) @@ -150,14 +156,15 @@ var _ = Describe("Loader parsing root module", func() { It("should load seven packages", func() { pkgs, err := loader.LoadRoots("./testmod/...") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(7)) - assertPkgExists(testmodPkg, pkgs) - assertPkgExists(testmodPkg+"/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir1", pkgs) - assertPkgExists(testmodPkg+"/subdir1/subdir2", pkgs) - assertPkgExists(testmodPkg+"/subdir1/submod1", pkgs) - assertPkgExists(testmodPkg+"/submod1", pkgs) - assertPkgExists(testmodPkg+"/submod1/subdir1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(7)) + assertPkgExists(testmodPkg, uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir1", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/subdir2", uniquePkgs) + assertPkgExists(testmodPkg+"/subdir1/submod1", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1", uniquePkgs) + assertPkgExists(testmodPkg+"/submod1/subdir1", uniquePkgs) }) }) @@ -165,8 +172,9 @@ var _ = Describe("Loader parsing root module", func() { It("should load one package", func() { pkgs, err := loader.LoadRoots("./testmod/subdir1/submod1/...") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(1)) - assertPkgExists(testmodPkg+"/subdir1/submod1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(1)) + assertPkgExists(testmodPkg+"/subdir1/submod1", uniquePkgs) }) }) @@ -174,9 +182,10 @@ var _ = Describe("Loader parsing root module", func() { It("should load two packages", func() { pkgs, err := loader.LoadRoots("./testmod", "./testmod/submod1") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(2)) - assertPkgExists(testmodPkg, pkgs) - assertPkgExists(testmodPkg+"/submod1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(2)) + assertPkgExists(testmodPkg, uniquePkgs) + assertPkgExists(testmodPkg+"/submod1", uniquePkgs) }) }) @@ -184,8 +193,9 @@ var _ = Describe("Loader parsing root module", func() { It("should load one package", func() { pkgs, err := loader.LoadRoots("./testmod/submod1/subdir1/") Expect(err).ToNot(HaveOccurred()) - Expect(pkgs).To(HaveLen(1)) - assertPkgExists(testmodPkg+"/submod1/subdir1", pkgs) + uniquePkgs := dedupPkgs(pkgs) + Expect(uniquePkgs).To(HaveLen(1)) + assertPkgExists(testmodPkg+"/submod1/subdir1", uniquePkgs) }) }) }) diff --git a/pkg/rbac/parser_integration_test.go b/pkg/rbac/parser_integration_test.go index f4f7bf45a..053ca6051 100644 --- a/pkg/rbac/parser_integration_test.go +++ b/pkg/rbac/parser_integration_test.go @@ -22,11 +22,11 @@ var _ = Describe("ClusterRole generated by the RBAC Generator", func() { // run this test multiple times to make sure the Rule order is stable. const stableTestCount = 5 for i := 0; i < stableTestCount; i++ { - It("should match the expected result", func() { + It("should match the expected result for loading directory", func() { By("switching into testdata to appease go modules") cwd, err := os.Getwd() Expect(err).NotTo(HaveOccurred()) - Expect(os.Chdir("./testdata")).To(Succeed()) // go modules are directory-sensitive + Expect(os.Chdir("./testdata/directory")).To(Succeed()) // go modules are directory-sensitive defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() By("loading the roots") @@ -69,4 +69,52 @@ var _ = Describe("ClusterRole generated by the RBAC Generator", func() { }) } + for i := 0; i < stableTestCount; i++ { + It("should match the expected result for loading single file", func() { + By("switching into testdata to appease go modules") + cwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + Expect(os.Chdir("./testdata/file")).To(Succeed()) // go modules are directory-sensitive + defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() + + By("loading the roots") + pkgs, err := loader.LoadRoots("controller.go") + Expect(err).NotTo(HaveOccurred()) + + By("registering RBAC rule marker") + reg := &markers.Registry{} + Expect(reg.Register(rbac.RuleDefinition)).To(Succeed()) + + By("creating GenerationContext") + ctx := &genall.GenerationContext{ + Collector: &markers.Collector{Registry: reg}, + Roots: pkgs, + } + + By("generating a ClusterRole") + objs, err := rbac.GenerateRoles(ctx, "manager-role") + Expect(err).NotTo(HaveOccurred()) + + By("loading the desired YAML") + expectedFile, err := os.ReadFile("role.yaml") + Expect(err).NotTo(HaveOccurred()) + + By("parsing the desired YAML") + for i, expectedRoleBytes := range bytes.Split(expectedFile, []byte("\n---\n"))[1:] { + By(fmt.Sprintf("comparing the generated Role and expected Role (Pair %d)", i)) + obj := objs[i] + switch obj := obj.(type) { + case rbacv1.ClusterRole: + var expectedClusterRole rbacv1.ClusterRole + Expect(yaml.Unmarshal(expectedRoleBytes, &expectedClusterRole)).To(Succeed()) + Expect(obj).To(Equal(expectedClusterRole), "type not as expected, check pkg/rbac/testdata/README.md for more details.\n\nDiff:\n\n%s", cmp.Diff(obj, expectedClusterRole)) + default: + var expectedRole rbacv1.Role + Expect(yaml.Unmarshal(expectedRoleBytes, &expectedRole)).To(Succeed()) + Expect(obj).To(Equal(expectedRole), "type not as expected, check pkg/rbac/testdata/README.md for more details.\n\nDiff:\n\n%s", cmp.Diff(obj, expectedRole)) + } + } + + }) + } }) diff --git a/pkg/rbac/testdata/controller.go b/pkg/rbac/testdata/directory/controller.go similarity index 100% rename from pkg/rbac/testdata/controller.go rename to pkg/rbac/testdata/directory/controller.go diff --git a/pkg/rbac/testdata/role.yaml b/pkg/rbac/testdata/directory/role.yaml similarity index 100% rename from pkg/rbac/testdata/role.yaml rename to pkg/rbac/testdata/directory/role.yaml diff --git a/pkg/rbac/testdata/file/another_controller.go b/pkg/rbac/testdata/file/another_controller.go new file mode 100644 index 000000000..1afe53018 --- /dev/null +++ b/pkg/rbac/testdata/file/another_controller.go @@ -0,0 +1,7 @@ +package controller + +// +kubebuilder:rbac:groups=ocean,resources=jobs,verbs=get +// +kubebuilder:rbac:groups=land,resources=jobs,verbs=get,namespace=zoo +// +kubebuilder:rbac:groups=ocean,resources=jobs,verbs=get,namespace=zoo +// +kubebuilder:rbac:groups=ocean,resources=jobs,verbs=get,namespace=park +// +kubebuilder:rbac:groups=batch.io,resources=cronjobs,resourceNames=abc;def;xyz,verbs=get;watch diff --git a/pkg/rbac/testdata/file/controller.go b/pkg/rbac/testdata/file/controller.go new file mode 100644 index 000000000..27800d729 --- /dev/null +++ b/pkg/rbac/testdata/file/controller.go @@ -0,0 +1,13 @@ +package controller + +// +kubebuilder:rbac:groups=batch.io,resources=cronjobs,verbs=get;watch;create +// +kubebuilder:rbac:groups=batch.io,resources=cronjobs/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=art,resources=jobs,verbs=get +// +kubebuilder:rbac:groups=wave,resources=jobs,verbs=get,namespace=zoo +// +kubebuilder:rbac:groups=batch;batch;batch,resources=jobs/status,verbs=watch +// +kubebuilder:rbac:groups=batch;cron,resources=jobs/status,verbs=create;get +// +kubebuilder:rbac:groups=art,resources=jobs,verbs=get,namespace=zoo +// +kubebuilder:rbac:groups=cron;batch,resources=jobs/status,verbs=get;create +// +kubebuilder:rbac:groups=batch,resources=jobs/status,verbs=watch;watch +// +kubebuilder:rbac:groups=art,resources=jobs,verbs=get,namespace=park +// +kubebuilder:rbac:groups=batch.io,resources=cronjobs,resourceNames=foo;bar;baz,verbs=get;watch diff --git a/pkg/rbac/testdata/file/role.yaml b/pkg/rbac/testdata/file/role.yaml new file mode 100644 index 000000000..3c3d01a81 --- /dev/null +++ b/pkg/rbac/testdata/file/role.yaml @@ -0,0 +1,88 @@ + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: manager-role +rules: +- apiGroups: + - art + resources: + - jobs + verbs: + - get +- apiGroups: + - batch + resources: + - jobs/status + verbs: + - watch +- apiGroups: + - batch + - cron + resources: + - jobs/status + verbs: + - create + - get +- apiGroups: + - batch.io + resources: + - cronjobs + verbs: + - create + - get + - watch +- apiGroups: + - batch.io + resourceNames: + - bar + - baz + - foo + resources: + - cronjobs + verbs: + - get + - watch +- apiGroups: + - batch.io + resources: + - cronjobs/status + verbs: + - get + - patch + - update + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: manager-role + namespace: park +rules: +- apiGroups: + - art + resources: + - jobs + verbs: + - get + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: manager-role + namespace: zoo +rules: +- apiGroups: + - art + resources: + - jobs + verbs: + - get +- apiGroups: + - wave + resources: + - jobs + verbs: + - get diff --git a/pkg/webhook/parser_integration_test.go b/pkg/webhook/parser_integration_test.go index f020cfc2e..743602c87 100644 --- a/pkg/webhook/parser_integration_test.go +++ b/pkg/webhook/parser_integration_test.go @@ -326,6 +326,141 @@ var _ = Describe("Webhook Generation From Parsing to CustomResourceDefinition", err = webhook.Generator{}.Generate(genCtx) Expect(err).To(HaveOccurred()) }) + + It("should properly generate the webhook definition for single file one", func() { + By("switching into testdata to appease go modules") + cwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + Expect(os.Chdir("./testdata/valid-single")).To(Succeed()) // go modules are directory-sensitive + defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() + + By("loading the golang file") + pkgs, err := loader.LoadRoots("webhook_one.go") + Expect(err).NotTo(HaveOccurred()) + Expect(pkgs).To(HaveLen(1)) + + By("setting up the parser") + reg := &markers.Registry{} + Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) + + By("requesting that the manifest be generated") + outputDir, err := os.MkdirTemp("", "webhook-integration-test") + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(outputDir) + genCtx := &genall.GenerationContext{ + Collector: &markers.Collector{Registry: reg}, + Roots: pkgs, + OutputRule: genall.OutputToDirectory(outputDir), + } + Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed()) + for _, r := range genCtx.Roots { + Expect(r.Errors).To(HaveLen(0)) + } + + By("loading the generated v1 YAML") + actualFile, err := os.ReadFile(path.Join(outputDir, "manifests.yaml")) + Expect(err).NotTo(HaveOccurred()) + actualMutating, actualValidating := unmarshalBothV1(actualFile) + + By("loading the desired v1 YAML") + expectedFile, err := os.ReadFile("manifests_one.yaml") + Expect(err).NotTo(HaveOccurred()) + expectedMutating, expectedValidating := unmarshalBothV1(expectedFile) + + By("comparing the two") + assertSame(actualMutating, expectedMutating) + assertSame(actualValidating, expectedValidating) + }) + + It("should properly generate the webhook definition for single file two", func() { + By("switching into testdata to appease go modules") + cwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + Expect(os.Chdir("./testdata/valid-single")).To(Succeed()) // go modules are directory-sensitive + defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() + + By("loading the golang file") + pkgs, err := loader.LoadRoots("webhook_two.go") + Expect(err).NotTo(HaveOccurred()) + Expect(pkgs).To(HaveLen(1)) + + By("setting up the parser") + reg := &markers.Registry{} + Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) + + By("requesting that the manifest be generated") + outputDir, err := os.MkdirTemp("", "webhook-integration-test") + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(outputDir) + genCtx := &genall.GenerationContext{ + Collector: &markers.Collector{Registry: reg}, + Roots: pkgs, + OutputRule: genall.OutputToDirectory(outputDir), + } + Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed()) + for _, r := range genCtx.Roots { + Expect(r.Errors).To(HaveLen(0)) + } + + By("loading the generated v1 YAML") + actualFile, err := os.ReadFile(path.Join(outputDir, "manifests.yaml")) + Expect(err).NotTo(HaveOccurred()) + actualMutating, actualValidating := unmarshalBothV1(actualFile) + + By("loading the desired v1 YAML") + expectedFile, err := os.ReadFile("manifests_two.yaml") + Expect(err).NotTo(HaveOccurred()) + expectedMutating, expectedValidating := unmarshalBothV1(expectedFile) + + By("comparing the two") + assertSame(actualMutating, expectedMutating) + assertSame(actualValidating, expectedValidating) + }) + + It("should properly generate the webhook definition for multiple files", func() { + By("switching into testdata to appease go modules") + cwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + Expect(os.Chdir("./testdata/valid-single")).To(Succeed()) // go modules are directory-sensitive + defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() + + By("loading the roots") + pkgs, err := loader.LoadRoots(".") + Expect(err).NotTo(HaveOccurred()) + Expect(pkgs).To(HaveLen(1)) + + By("setting up the parser") + reg := &markers.Registry{} + Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) + + By("requesting that the manifest be generated") + outputDir, err := os.MkdirTemp("", "webhook-integration-test") + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(outputDir) + genCtx := &genall.GenerationContext{ + Collector: &markers.Collector{Registry: reg}, + Roots: pkgs, + OutputRule: genall.OutputToDirectory(outputDir), + } + Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed()) + for _, r := range genCtx.Roots { + Expect(r.Errors).To(HaveLen(0)) + } + + By("loading the generated v1 YAML") + actualFile, err := os.ReadFile(path.Join(outputDir, "manifests.yaml")) + Expect(err).NotTo(HaveOccurred()) + actualMutating, actualValidating := unmarshalBothV1(actualFile) + + By("loading the desired v1 YAML") + expectedFile, err := os.ReadFile("manifests_all.yaml") + Expect(err).NotTo(HaveOccurred()) + expectedMutating, expectedValidating := unmarshalBothV1(expectedFile) + + By("comparing the two") + assertSame(actualMutating, expectedMutating) + assertSame(actualValidating, expectedValidating) + }) }) func unmarshalBothV1(in []byte) (mutating admissionregv1.MutatingWebhookConfiguration, validating admissionregv1.ValidatingWebhookConfiguration) { diff --git a/pkg/webhook/testdata/valid-single/cronjob_types.go b/pkg/webhook/testdata/valid-single/cronjob_types.go new file mode 100644 index 000000000..4a95bf52a --- /dev/null +++ b/pkg/webhook/testdata/valid-single/cronjob_types.go @@ -0,0 +1,71 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//go:generate ../../../../.run-controller-gen.sh webhook paths=. output:dir=. + +// +groupName=testdata.kubebuilder.io +// +versionName=v1 +package cronjob + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// CronJobSpec defines the desired state of CronJob +type CronJobSpec struct { + // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + Schedule string `json:"schedule"` +} + +// CronJobStatus defines the observed state of CronJob +type CronJobStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Information when was the last time the job was successfully scheduled. + // +optional + LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:singular=mycronjob + +// CronJob is the Schema for the cronjobs API +type CronJob struct { + /* + */ + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CronJobSpec `json:"spec,omitempty"` + Status CronJobStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// CronJobList contains a list of CronJob +type CronJobList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CronJob `json:"items"` +} + +func init() { + SchemeBuilder.Register(&CronJob{}, &CronJobList{}) +} diff --git a/pkg/webhook/testdata/valid-single/cronjobtwo_types.go b/pkg/webhook/testdata/valid-single/cronjobtwo_types.go new file mode 100644 index 000000000..8bb184302 --- /dev/null +++ b/pkg/webhook/testdata/valid-single/cronjobtwo_types.go @@ -0,0 +1,71 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//go:generate ../../../../.run-controller-gen.sh webhook paths=. output:dir=. + +// +groupName=testdata.kubebuilder.io +// +versionName=v1 +package cronjob + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// CronJobTwoSpec defines the desired state of CronJobTwo +type CronJobTwoSpec struct { + // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + Schedule string `json:"schedule"` +} + +// CronJobTwoStatus defines the observed state of CronJobTwo +type CronJobTwoStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Information when was the last time the job was successfully scheduled. + // +optional + LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:singular=mycronjobtwo + +// CronJobTwo is the Schema for the cronjobtwos API +type CronJobTwo struct { + /* + */ + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CronJobTwoSpec `json:"spec,omitempty"` + Status CronJobTwoStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// CronJobTwoList contains a list of CronJobTwo +type CronJobTwoList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CronJobTwo `json:"items"` +} + +func init() { + SchemeBuilder.Register(&CronJobTwo{}, &CronJobTwoList{}) +} diff --git a/pkg/webhook/testdata/valid-single/manifests_all.yaml b/pkg/webhook/testdata/valid-single/manifests_all.yaml new file mode 100644 index 000000000..ad5f17cd4 --- /dev/null +++ b/pkg/webhook/testdata/valid-single/manifests_all.yaml @@ -0,0 +1,152 @@ +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-testdata-kubebuilder-io-v1-cronjob + failurePolicy: Fail + matchPolicy: Equivalent + name: default.cronjob.testdata.kubebuilder.io + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobs + sideEffects: None + timeoutSeconds: 10 +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-testdata-kubebuilder-io-v1-cronjobtwo + failurePolicy: Fail + matchPolicy: Equivalent + name: default.cronjobtwo.testdata.kubebuilder.io + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobtwos + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjob + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjob.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobs + sideEffects: None + timeoutSeconds: 10 +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjob + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjob.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobs + sideEffects: NoneOnDryRun + timeoutSeconds: 10 +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjobtwo + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjobtwo.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobtwos + sideEffects: None + timeoutSeconds: 10 +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjobtwo + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjobtwo.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobtwos + sideEffects: NoneOnDryRun + timeoutSeconds: 10 diff --git a/pkg/webhook/testdata/valid-single/manifests_one.yaml b/pkg/webhook/testdata/valid-single/manifests_one.yaml new file mode 100644 index 000000000..9a2011559 --- /dev/null +++ b/pkg/webhook/testdata/valid-single/manifests_one.yaml @@ -0,0 +1,82 @@ +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-testdata-kubebuilder-io-v1-cronjob + failurePolicy: Fail + matchPolicy: Equivalent + name: default.cronjob.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobs + sideEffects: None + timeoutSeconds: 10 + reinvocationPolicy: IfNeeded +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjob + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjob.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobs + sideEffects: None + timeoutSeconds: 10 +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjob + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjob.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobs + sideEffects: NoneOnDryRun + timeoutSeconds: 10 diff --git a/pkg/webhook/testdata/valid-single/manifests_two.yaml b/pkg/webhook/testdata/valid-single/manifests_two.yaml new file mode 100644 index 000000000..697763b74 --- /dev/null +++ b/pkg/webhook/testdata/valid-single/manifests_two.yaml @@ -0,0 +1,82 @@ +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-testdata-kubebuilder-io-v1-cronjobtwo + failurePolicy: Fail + matchPolicy: Equivalent + name: default.cronjobtwo.testdata.kubebuilder.io + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobtwos + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjobtwo + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjobtwo.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobtwos + sideEffects: None + timeoutSeconds: 10 +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-testdata-kubebuilder-io-v1-cronjobtwo + failurePolicy: Fail + matchPolicy: Equivalent + name: validation.cronjobtwo.testdata.kubebuilder.io + rules: + - apiGroups: + - testdata.kubebuiler.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - cronjobtwos + sideEffects: NoneOnDryRun + timeoutSeconds: 10 diff --git a/pkg/webhook/testdata/valid-single/webhook_one.go b/pkg/webhook/testdata/valid-single/webhook_one.go new file mode 100644 index 000000000..9e8685278 --- /dev/null +++ b/pkg/webhook/testdata/valid-single/webhook_one.go @@ -0,0 +1,50 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cronjob + +import ( + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +func (c *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(c). + Complete() +} + +// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,path=/validate-testdata-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobs,versions=v1,name=validation.cronjob.testdata.kubebuilder.io,sideEffects=None,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:verbs=create;update,path=/validate-testdata-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobs,versions=v1,name=validation.cronjob.testdata.kubebuilder.io,sideEffects=NoneOnDryRun,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,path=/mutate-testdata-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobs,versions=v1,name=default.cronjob.testdata.kubebuilder.io,sideEffects=None,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1,reinvocationPolicy=IfNeeded + +var _ webhook.Defaulter = &CronJob{} +var _ webhook.Validator = &CronJob{} + +func (c *CronJob) Default() { +} + +func (c *CronJob) ValidateCreate() error { + return nil +} + +func (c *CronJob) ValidateUpdate(_ runtime.Object) error { + return nil +} + +func (c *CronJob) ValidateDelete() error { + return nil +} diff --git a/pkg/webhook/testdata/valid-single/webhook_two.go b/pkg/webhook/testdata/valid-single/webhook_two.go new file mode 100644 index 000000000..1f38aca9b --- /dev/null +++ b/pkg/webhook/testdata/valid-single/webhook_two.go @@ -0,0 +1,50 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cronjob + +import ( + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +func (c *CronJobTwo) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(c). + Complete() +} + +// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,path=/validate-testdata-kubebuilder-io-v1-cronjobtwo,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobtwos,versions=v1,name=validation.cronjobtwo.testdata.kubebuilder.io,sideEffects=None,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:verbs=create;update,path=/validate-testdata-kubebuilder-io-v1-cronjobtwo,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobtwos,versions=v1,name=validation.cronjobtwo.testdata.kubebuilder.io,sideEffects=NoneOnDryRun,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:webhookVersions=v1,verbs=create;update,path=/mutate-testdata-kubebuilder-io-v1-cronjobtwo,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=testdata.kubebuiler.io,resources=cronjobtwos,versions=v1,name=default.cronjobtwo.testdata.kubebuilder.io,sideEffects=None,timeoutSeconds=10,admissionReviewVersions=v1;v1beta1,reinvocationPolicy=IfNeeded + +var _ webhook.Defaulter = &CronJobTwo{} +var _ webhook.Validator = &CronJobTwo{} + +func (c *CronJobTwo) Default() { +} + +func (c *CronJobTwo) ValidateCreate() error { + return nil +} + +func (c *CronJobTwo) ValidateUpdate(_ runtime.Object) error { + return nil +} + +func (c *CronJobTwo) ValidateDelete() error { + return nil +}