github.com/TheSpiritXIII/controller-tools@v0.14.1/pkg/webhook/parser_integration_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package webhook_test 18 19 import ( 20 "bytes" 21 "os" 22 "path" 23 24 "github.com/google/go-cmp/cmp" 25 . "github.com/onsi/ginkgo" 26 . "github.com/onsi/gomega" 27 admissionregv1 "k8s.io/api/admissionregistration/v1" 28 "sigs.k8s.io/yaml" 29 30 "github.com/TheSpiritXIII/controller-tools/pkg/genall" 31 "github.com/TheSpiritXIII/controller-tools/pkg/loader" 32 "github.com/TheSpiritXIII/controller-tools/pkg/markers" 33 "github.com/TheSpiritXIII/controller-tools/pkg/webhook" 34 ) 35 36 // TODO(directxman12): test generation across multiple versions (right 37 // now, we're trusting k/k's conversion code, though, which is probably 38 // fine for the time being) 39 var _ = Describe("Webhook Generation From Parsing to CustomResourceDefinition", func() { 40 assertSame := func(actual, expected interface{}) { 41 ExpectWithOffset(1, actual).To(Equal(expected), "type not as expected, check pkg/webhook/testdata/README.md for more details.\n\nDiff:\n\n%s", cmp.Diff(actual, expected)) 42 } 43 44 It("should fail generating a v1beta1 webhook", func() { 45 By("switching into testdata to appease go modules") 46 cwd, err := os.Getwd() 47 Expect(err).NotTo(HaveOccurred()) 48 Expect(os.Chdir("./testdata/invalid-v1beta1NotSupported")).To(Succeed()) // go modules are directory-sensitive 49 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 50 51 By("loading the roots") 52 pkgs, err := loader.LoadRoots(".") 53 Expect(err).NotTo(HaveOccurred()) 54 Expect(pkgs).To(HaveLen(1)) 55 56 By("setting up the parser") 57 reg := &markers.Registry{} 58 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 59 60 By("requesting that the manifest be generated") 61 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 62 Expect(err).NotTo(HaveOccurred()) 63 defer os.RemoveAll(outputDir) 64 genCtx := &genall.GenerationContext{ 65 Collector: &markers.Collector{Registry: reg}, 66 Roots: pkgs, 67 OutputRule: genall.OutputToDirectory(outputDir), 68 } 69 err = webhook.Generator{}.Generate(genCtx) 70 Expect(err).To(MatchError("unsupported webhook version: v1beta1")) 71 }) 72 73 It("should fail without admissionReviewVersions specified", func() { 74 By("switching into testdata to appease go modules") 75 cwd, err := os.Getwd() 76 Expect(err).NotTo(HaveOccurred()) 77 Expect(os.Chdir("./testdata/invalid-admissionReviewVersionsRequired")).To(Succeed()) // go modules are directory-sensitive 78 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 79 80 By("loading the roots") 81 pkgs, err := loader.LoadRoots(".") 82 Expect(err).NotTo(HaveOccurred()) 83 Expect(pkgs).To(HaveLen(1)) 84 85 By("setting up the parser") 86 reg := &markers.Registry{} 87 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 88 89 By("requesting that the manifest be generated") 90 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 91 Expect(err).NotTo(HaveOccurred()) 92 defer os.RemoveAll(outputDir) 93 genCtx := &genall.GenerationContext{ 94 Collector: &markers.Collector{Registry: reg}, 95 Roots: pkgs, 96 OutputRule: genall.OutputToDirectory(outputDir), 97 } 98 Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed()) 99 Expect(genCtx.Roots).To(HaveLen(1)) 100 Expect(genCtx.Roots[0].Errors).To(HaveLen(1)) 101 Expect(genCtx.Roots[0].Errors[0].Error()).To(ContainSubstring(`missing argument "admissionReviewVersions"`)) 102 }) 103 104 It("should fail with invalid side effects", func() { 105 By("switching into testdata to appease go modules") 106 cwd, err := os.Getwd() 107 Expect(err).NotTo(HaveOccurred()) 108 Expect(os.Chdir("./testdata/invalid-sideEffects")).To(Succeed()) // go modules are directory-sensitive 109 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 110 111 By("loading the roots") 112 pkgs, err := loader.LoadRoots(".") 113 Expect(err).NotTo(HaveOccurred()) 114 Expect(pkgs).To(HaveLen(1)) 115 116 By("setting up the parser") 117 reg := &markers.Registry{} 118 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 119 120 By("requesting that the manifest be generated") 121 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 122 Expect(err).NotTo(HaveOccurred()) 123 defer os.RemoveAll(outputDir) 124 genCtx := &genall.GenerationContext{ 125 Collector: &markers.Collector{Registry: reg}, 126 Roots: pkgs, 127 OutputRule: genall.OutputToDirectory(outputDir), 128 } 129 err = webhook.Generator{}.Generate(genCtx) 130 Expect(err).To(MatchError("SideEffects should not be set to `Some` or `Unknown` for v1 {Mutating,Validating}WebhookConfiguration")) 131 }) 132 133 It("should fail with invalid timeout seconds", func() { 134 By("switching into testdata to appease go modules") 135 cwd, err := os.Getwd() 136 Expect(err).NotTo(HaveOccurred()) 137 Expect(os.Chdir("./testdata/invalid-timeoutSeconds")).To(Succeed()) // go modules are directory-sensitive 138 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 139 140 By("loading the roots") 141 pkgs, err := loader.LoadRoots(".") 142 Expect(err).NotTo(HaveOccurred()) 143 Expect(pkgs).To(HaveLen(1)) 144 145 By("setting up the parser") 146 reg := &markers.Registry{} 147 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 148 149 By("requesting that the manifest be generated") 150 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 151 Expect(err).NotTo(HaveOccurred()) 152 defer os.RemoveAll(outputDir) 153 genCtx := &genall.GenerationContext{ 154 Collector: &markers.Collector{Registry: reg}, 155 Roots: pkgs, 156 OutputRule: genall.OutputToDirectory(outputDir), 157 } 158 err = webhook.Generator{}.Generate(genCtx) 159 Expect(err).To(MatchError("TimeoutSeconds must be between 1 and 30 seconds")) 160 }) 161 162 It("should properly generate the webhook definition", func() { 163 By("switching into testdata to appease go modules") 164 cwd, err := os.Getwd() 165 Expect(err).NotTo(HaveOccurred()) 166 Expect(os.Chdir("./testdata/valid")).To(Succeed()) // go modules are directory-sensitive 167 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 168 169 By("loading the roots") 170 pkgs, err := loader.LoadRoots(".") 171 Expect(err).NotTo(HaveOccurred()) 172 Expect(pkgs).To(HaveLen(1)) 173 174 By("setting up the parser") 175 reg := &markers.Registry{} 176 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 177 178 By("requesting that the manifest be generated") 179 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 180 Expect(err).NotTo(HaveOccurred()) 181 defer os.RemoveAll(outputDir) 182 genCtx := &genall.GenerationContext{ 183 Collector: &markers.Collector{Registry: reg}, 184 Roots: pkgs, 185 OutputRule: genall.OutputToDirectory(outputDir), 186 } 187 Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed()) 188 for _, r := range genCtx.Roots { 189 Expect(r.Errors).To(HaveLen(0)) 190 } 191 192 By("loading the generated v1 YAML") 193 actualFile, err := os.ReadFile(path.Join(outputDir, "manifests.yaml")) 194 Expect(err).NotTo(HaveOccurred()) 195 actualMutating, actualValidating := unmarshalBothV1(actualFile) 196 197 By("loading the desired v1 YAML") 198 expectedFile, err := os.ReadFile("manifests.yaml") 199 Expect(err).NotTo(HaveOccurred()) 200 expectedMutating, expectedValidating := unmarshalBothV1(expectedFile) 201 202 By("comparing the two") 203 assertSame(actualMutating, expectedMutating) 204 assertSame(actualValidating, expectedValidating) 205 }) 206 207 It("should generate the ordered webhook definitions", func() { 208 By("switching into testdata to appease go modules") 209 cwd, err := os.Getwd() 210 Expect(err).NotTo(HaveOccurred()) 211 Expect(os.Chdir("./testdata/valid-ordered")).To(Succeed()) // go modules are directory-sensitive 212 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 213 214 By("loading the roots") 215 pkgs, err := loader.LoadRoots(".") 216 Expect(err).NotTo(HaveOccurred()) 217 Expect(pkgs).To(HaveLen(1)) 218 219 By("setting up the parser") 220 reg := &markers.Registry{} 221 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 222 223 By("requesting that the manifest be generated") 224 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 225 Expect(err).NotTo(HaveOccurred()) 226 defer os.RemoveAll(outputDir) 227 228 for i := 0; i < 10; i++ { 229 genCtx := &genall.GenerationContext{ 230 Collector: &markers.Collector{Registry: reg}, 231 Roots: pkgs, 232 OutputRule: genall.OutputToDirectory(outputDir), 233 } 234 Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed()) 235 for _, r := range genCtx.Roots { 236 Expect(r.Errors).To(HaveLen(0)) 237 } 238 239 By("loading the generated v1 YAML") 240 actualFile, err := os.ReadFile(path.Join(outputDir, "manifests.yaml")) 241 Expect(err).NotTo(HaveOccurred()) 242 actualManifest := &admissionregv1.ValidatingWebhookConfiguration{} 243 Expect(yaml.UnmarshalStrict(actualFile, actualManifest)).To(Succeed()) 244 245 By("loading the desired v1 YAML") 246 expectedFile, err := os.ReadFile("manifests.yaml") 247 Expect(err).NotTo(HaveOccurred()) 248 expectedManifest := &admissionregv1.ValidatingWebhookConfiguration{} 249 Expect(yaml.UnmarshalStrict(expectedFile, expectedManifest)).To(Succeed()) 250 251 By("comparing the manifest") 252 assertSame(actualManifest, expectedManifest) 253 } 254 }) 255 256 It("should properly generate the webhook definition with url instead of service", func() { 257 By("switching into testdata to appease go modules") 258 cwd, err := os.Getwd() 259 Expect(err).NotTo(HaveOccurred()) 260 Expect(os.Chdir("./testdata/valid-url")).To(Succeed()) // go modules are directory-sensitive 261 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 262 263 By("loading the roots") 264 pkgs, err := loader.LoadRoots(".") 265 Expect(err).NotTo(HaveOccurred()) 266 Expect(pkgs).To(HaveLen(1)) 267 268 By("setting up the parser") 269 reg := &markers.Registry{} 270 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 271 272 By("requesting that the manifest be generated") 273 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 274 Expect(err).NotTo(HaveOccurred()) 275 defer os.RemoveAll(outputDir) 276 genCtx := &genall.GenerationContext{ 277 Collector: &markers.Collector{Registry: reg}, 278 Roots: pkgs, 279 OutputRule: genall.OutputToDirectory(outputDir), 280 } 281 Expect(webhook.Generator{}.Generate(genCtx)).To(Succeed()) 282 for _, r := range genCtx.Roots { 283 Expect(r.Errors).To(HaveLen(0)) 284 } 285 286 By("loading the generated v1 YAML") 287 actualFile, err := os.ReadFile(path.Join(outputDir, "manifests.yaml")) 288 Expect(err).NotTo(HaveOccurred()) 289 actualMutating, actualValidating := unmarshalBothV1(actualFile) 290 291 By("loading the desired v1 YAML") 292 expectedFile, err := os.ReadFile("manifests.yaml") 293 Expect(err).NotTo(HaveOccurred()) 294 expectedMutating, expectedValidating := unmarshalBothV1(expectedFile) 295 296 By("comparing the two") 297 assertSame(actualMutating, expectedMutating) 298 assertSame(actualValidating, expectedValidating) 299 }) 300 301 It("should fail to generate when both path and url are set", func() { 302 By("switching into testdata to appease go modules") 303 cwd, err := os.Getwd() 304 Expect(err).NotTo(HaveOccurred()) 305 Expect(os.Chdir("./testdata/invalid-path-and-url")).To(Succeed()) // go modules are directory-sensitive 306 defer func() { Expect(os.Chdir(cwd)).To(Succeed()) }() 307 308 By("loading the roots") 309 pkgs, err := loader.LoadRoots(".") 310 Expect(err).NotTo(HaveOccurred()) 311 Expect(pkgs).To(HaveLen(1)) 312 313 By("setting up the parser") 314 reg := &markers.Registry{} 315 Expect(reg.Register(webhook.ConfigDefinition)).To(Succeed()) 316 317 By("requesting that the manifest be generated") 318 outputDir, err := os.MkdirTemp("", "webhook-integration-test") 319 Expect(err).NotTo(HaveOccurred()) 320 defer os.RemoveAll(outputDir) 321 genCtx := &genall.GenerationContext{ 322 Collector: &markers.Collector{Registry: reg}, 323 Roots: pkgs, 324 OutputRule: genall.OutputToDirectory(outputDir), 325 } 326 err = webhook.Generator{}.Generate(genCtx) 327 Expect(err).To(HaveOccurred()) 328 }) 329 }) 330 331 func unmarshalBothV1(in []byte) (mutating admissionregv1.MutatingWebhookConfiguration, validating admissionregv1.ValidatingWebhookConfiguration) { 332 documents := bytes.Split(in, []byte("\n---\n")) 333 ExpectWithOffset(1, documents).To(HaveLen(2), "expected two documents in file, found %d", len(documents)) 334 335 ExpectWithOffset(1, yaml.UnmarshalStrict(documents[0], &mutating)).To(Succeed(), "expected the first document in the file to be a mutating webhook configuration") 336 ExpectWithOffset(1, yaml.UnmarshalStrict(documents[1], &validating)).To(Succeed(), "expected the second document in the file to be a validating webhook configuration") 337 return 338 }