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  }