1/*
2Copyright 2018 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package envtest
18
19import (
20	"context"
21	"path/filepath"
22	"time"
23
24	. "github.com/onsi/ginkgo"
25	. "github.com/onsi/gomega"
26	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
27	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
28	apierrors "k8s.io/apimachinery/pkg/api/errors"
29	"k8s.io/apimachinery/pkg/runtime"
30	"k8s.io/apimachinery/pkg/types"
31	"sigs.k8s.io/controller-runtime/pkg/client"
32)
33
34var _ = Describe("Test", func() {
35	var crds []runtime.Object
36	var err error
37	var s *runtime.Scheme
38	var c client.Client
39
40	var validDirectory = filepath.Join(".", "testdata")
41	var invalidDirectory = "fake"
42
43	var teardownTimeoutSeconds float64 = 10
44
45	// Initialize the client
46	BeforeEach(func(done Done) {
47		crds = []runtime.Object{}
48		s = runtime.NewScheme()
49		err = v1beta1.AddToScheme(s)
50		Expect(err).NotTo(HaveOccurred())
51		err = apiextensionsv1.AddToScheme(s)
52		Expect(err).NotTo(HaveOccurred())
53
54		c, err = client.New(env.Config, client.Options{Scheme: s})
55		Expect(err).NotTo(HaveOccurred())
56
57		close(done)
58	})
59
60	// Cleanup CRDs
61	AfterEach(func(done Done) {
62		for _, crd := range runtimeCRDListToUnstructured(crds) {
63			// Delete only if CRD exists.
64			crdObjectKey := client.ObjectKey{
65				Name: crd.GetName(),
66			}
67			var placeholder v1beta1.CustomResourceDefinition
68			err := c.Get(context.TODO(), crdObjectKey, &placeholder)
69			if err != nil && apierrors.IsNotFound(err) {
70				// CRD doesn't need to be deleted.
71				continue
72			}
73			Expect(err).NotTo(HaveOccurred())
74			Expect(c.Delete(context.TODO(), crd)).To(Succeed())
75			Eventually(func() bool {
76				err := c.Get(context.TODO(), crdObjectKey, &placeholder)
77				return apierrors.IsNotFound(err)
78			}, 1*time.Second).Should(BeTrue())
79		}
80		close(done)
81	}, teardownTimeoutSeconds)
82
83	Describe("InstallCRDs", func() {
84		It("should install the CRDs into the cluster using directory", func(done Done) {
85			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
86				Paths: []string{validDirectory},
87			})
88			Expect(err).NotTo(HaveOccurred())
89
90			// Expect to find the CRDs
91
92			crdv1 := &apiextensionsv1.CustomResourceDefinition{}
93			err = c.Get(context.TODO(), types.NamespacedName{Name: "foos.bar.example.com"}, crdv1)
94			Expect(err).NotTo(HaveOccurred())
95			Expect(crdv1.Spec.Names.Kind).To(Equal("Foo"))
96
97			crd := &v1beta1.CustomResourceDefinition{}
98			err = c.Get(context.TODO(), types.NamespacedName{Name: "bazs.qux.example.com"}, crd)
99			Expect(err).NotTo(HaveOccurred())
100			Expect(crd.Spec.Names.Kind).To(Equal("Baz"))
101
102			crd = &v1beta1.CustomResourceDefinition{}
103			err = c.Get(context.TODO(), types.NamespacedName{Name: "captains.crew.example.com"}, crd)
104			Expect(err).NotTo(HaveOccurred())
105			Expect(crd.Spec.Names.Kind).To(Equal("Captain"))
106
107			crd = &v1beta1.CustomResourceDefinition{}
108			err = c.Get(context.TODO(), types.NamespacedName{Name: "firstmates.crew.example.com"}, crd)
109			Expect(err).NotTo(HaveOccurred())
110			Expect(crd.Spec.Names.Kind).To(Equal("FirstMate"))
111
112			crd = &v1beta1.CustomResourceDefinition{}
113			err = c.Get(context.TODO(), types.NamespacedName{Name: "drivers.crew.example.com"}, crd)
114			Expect(err).NotTo(HaveOccurred())
115			Expect(crd.Spec.Names.Kind).To(Equal("Driver"))
116
117			err = WaitForCRDs(env.Config, []runtime.Object{
118				&apiextensionsv1.CustomResourceDefinition{
119					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
120						Group: "bar.example.com",
121						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
122							{
123								Name:    "v1",
124								Storage: true,
125								Served:  true,
126								Schema: &apiextensionsv1.CustomResourceValidation{
127									OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
128										Type: "object",
129									},
130								},
131							},
132						},
133						Names: apiextensionsv1.CustomResourceDefinitionNames{
134							Plural: "foos",
135						}},
136				},
137				&v1beta1.CustomResourceDefinition{
138					Spec: v1beta1.CustomResourceDefinitionSpec{
139						Group:   "qux.example.com",
140						Version: "v1beta1",
141						Names: v1beta1.CustomResourceDefinitionNames{
142							Plural: "bazs",
143						}},
144				},
145				&v1beta1.CustomResourceDefinition{
146					Spec: v1beta1.CustomResourceDefinitionSpec{
147						Group:   "crew.example.com",
148						Version: "v1beta1",
149						Names: v1beta1.CustomResourceDefinitionNames{
150							Plural: "captains",
151						}},
152				},
153				&v1beta1.CustomResourceDefinition{
154					Spec: v1beta1.CustomResourceDefinitionSpec{
155						Group:   "crew.example.com",
156						Version: "v1beta1",
157						Names: v1beta1.CustomResourceDefinitionNames{
158							Plural: "firstmates",
159						}},
160				},
161				&v1beta1.CustomResourceDefinition{
162					Spec: v1beta1.CustomResourceDefinitionSpec{
163						Group: "crew.example.com",
164						Names: v1beta1.CustomResourceDefinitionNames{
165							Plural: "drivers",
166						},
167						Versions: []v1beta1.CustomResourceDefinitionVersion{
168							{
169								Name:    "v1",
170								Storage: true,
171								Served:  true,
172							},
173							{
174								Name:    "v2",
175								Storage: false,
176								Served:  true,
177							},
178						}},
179				},
180			},
181				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
182			)
183			Expect(err).NotTo(HaveOccurred())
184
185			close(done)
186		}, 5)
187
188		It("should install the CRDs into the cluster using file", func(done Done) {
189			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
190				Paths: []string{filepath.Join(".", "testdata", "crds", "examplecrd3.yaml")},
191			})
192			Expect(err).NotTo(HaveOccurred())
193
194			crd := &v1beta1.CustomResourceDefinition{}
195			err = c.Get(context.TODO(), types.NamespacedName{Name: "configs.foo.example.com"}, crd)
196			Expect(err).NotTo(HaveOccurred())
197			Expect(crd.Spec.Names.Kind).To(Equal("Config"))
198
199			err = WaitForCRDs(env.Config, []runtime.Object{
200				&v1beta1.CustomResourceDefinition{
201					Spec: v1beta1.CustomResourceDefinitionSpec{
202						Group:   "foo.example.com",
203						Version: "v1beta1",
204						Names: v1beta1.CustomResourceDefinitionNames{
205							Plural: "configs",
206						}},
207				},
208			},
209				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
210			)
211			Expect(err).NotTo(HaveOccurred())
212
213			close(done)
214		}, 10)
215
216		It("should filter out already existent CRD", func(done Done) {
217			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
218				Paths: []string{
219					filepath.Join(".", "testdata"),
220					filepath.Join(".", "testdata", "examplecrd1.yaml"),
221				},
222			})
223			Expect(err).NotTo(HaveOccurred())
224
225			crd := &apiextensionsv1.CustomResourceDefinition{}
226			err = c.Get(context.TODO(), types.NamespacedName{Name: "foos.bar.example.com"}, crd)
227			Expect(err).NotTo(HaveOccurred())
228			Expect(crd.Spec.Names.Kind).To(Equal("Foo"))
229
230			err = WaitForCRDs(env.Config, []runtime.Object{
231				&apiextensionsv1.CustomResourceDefinition{
232					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
233						Group: "bar.example.com",
234						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
235							{
236								Name:    "v1",
237								Storage: true,
238								Served:  true,
239								Schema: &apiextensionsv1.CustomResourceValidation{
240									OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
241										Type: "object",
242									},
243								},
244							},
245						},
246						Names: apiextensionsv1.CustomResourceDefinitionNames{
247							Plural: "foos",
248						}},
249				},
250			},
251				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
252			)
253			Expect(err).NotTo(HaveOccurred())
254
255			close(done)
256		}, 10)
257
258		It("should not return an not error if the directory doesn't exist", func(done Done) {
259			crds, err = InstallCRDs(env.Config, CRDInstallOptions{Paths: []string{invalidDirectory}})
260			Expect(err).NotTo(HaveOccurred())
261
262			close(done)
263		}, 5)
264
265		It("should return an error if the directory doesn't exist", func(done Done) {
266			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
267				Paths: []string{invalidDirectory}, ErrorIfPathMissing: true,
268			})
269			Expect(err).To(HaveOccurred())
270
271			close(done)
272		}, 5)
273
274		It("should return an error if the file doesn't exist", func(done Done) {
275			crds, err = InstallCRDs(env.Config, CRDInstallOptions{Paths: []string{
276				filepath.Join(".", "testdata", "fake.yaml")}, ErrorIfPathMissing: true,
277			})
278			Expect(err).To(HaveOccurred())
279
280			close(done)
281		}, 5)
282
283		It("should return an error if the resource group version isn't found", func(done Done) {
284			// Wait for a CRD where the Group and Version don't exist
285			err := WaitForCRDs(env.Config,
286				[]runtime.Object{
287					&v1beta1.CustomResourceDefinition{
288						Spec: v1beta1.CustomResourceDefinitionSpec{
289							Version: "v1",
290							Names: v1beta1.CustomResourceDefinitionNames{
291								Plural: "notfound",
292							}},
293					},
294				},
295				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
296			)
297			Expect(err).To(HaveOccurred())
298
299			close(done)
300		}, 5)
301
302		It("should return an error if the resource isn't found in the group version", func(done Done) {
303			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
304				Paths: []string{"."},
305			})
306			Expect(err).NotTo(HaveOccurred())
307
308			// Wait for a CRD that doesn't exist, but the Group and Version do
309			err = WaitForCRDs(env.Config, []runtime.Object{
310				&v1beta1.CustomResourceDefinition{
311					Spec: v1beta1.CustomResourceDefinitionSpec{
312						Group:   "qux.example.com",
313						Version: "v1beta1",
314						Names: v1beta1.CustomResourceDefinitionNames{
315							Plural: "bazs",
316						}},
317				},
318				&v1beta1.CustomResourceDefinition{
319					Spec: v1beta1.CustomResourceDefinitionSpec{
320						Group:   "bar.example.com",
321						Version: "v1beta1",
322						Names: v1beta1.CustomResourceDefinitionNames{
323							Plural: "fake",
324						}},
325				}},
326				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
327			)
328			Expect(err).To(HaveOccurred())
329
330			close(done)
331		}, 5)
332
333		It("should reinstall the CRDs if already present in the cluster", func(done Done) {
334
335			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
336				Paths: []string{filepath.Join(".", "testdata")},
337			})
338			Expect(err).NotTo(HaveOccurred())
339
340			// Expect to find the CRDs
341
342			crd := &v1beta1.CustomResourceDefinition{}
343			err = c.Get(context.TODO(), types.NamespacedName{Name: "foos.bar.example.com"}, crd)
344			Expect(err).NotTo(HaveOccurred())
345			Expect(crd.Spec.Names.Kind).To(Equal("Foo"))
346
347			crd = &v1beta1.CustomResourceDefinition{}
348			err = c.Get(context.TODO(), types.NamespacedName{Name: "bazs.qux.example.com"}, crd)
349			Expect(err).NotTo(HaveOccurred())
350			Expect(crd.Spec.Names.Kind).To(Equal("Baz"))
351
352			crd = &v1beta1.CustomResourceDefinition{}
353			err = c.Get(context.TODO(), types.NamespacedName{Name: "captains.crew.example.com"}, crd)
354			Expect(err).NotTo(HaveOccurred())
355			Expect(crd.Spec.Names.Kind).To(Equal("Captain"))
356
357			crd = &v1beta1.CustomResourceDefinition{}
358			err = c.Get(context.TODO(), types.NamespacedName{Name: "firstmates.crew.example.com"}, crd)
359			Expect(err).NotTo(HaveOccurred())
360			Expect(crd.Spec.Names.Kind).To(Equal("FirstMate"))
361
362			crd = &v1beta1.CustomResourceDefinition{}
363			err = c.Get(context.TODO(), types.NamespacedName{Name: "drivers.crew.example.com"}, crd)
364			Expect(err).NotTo(HaveOccurred())
365			Expect(crd.Spec.Names.Kind).To(Equal("Driver"))
366
367			err = WaitForCRDs(env.Config, []runtime.Object{
368				&apiextensionsv1.CustomResourceDefinition{
369					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
370						Group: "bar.example.com",
371						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
372							{
373								Name:    "v1",
374								Storage: true,
375								Served:  true,
376								Schema: &apiextensionsv1.CustomResourceValidation{
377									OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
378										Type: "object",
379									},
380								},
381							},
382						},
383						Names: apiextensionsv1.CustomResourceDefinitionNames{
384							Plural: "foos",
385						}},
386				},
387				&v1beta1.CustomResourceDefinition{
388					Spec: v1beta1.CustomResourceDefinitionSpec{
389						Group:   "qux.example.com",
390						Version: "v1beta1",
391						Names: v1beta1.CustomResourceDefinitionNames{
392							Plural: "bazs",
393						}},
394				},
395				&v1beta1.CustomResourceDefinition{
396					Spec: v1beta1.CustomResourceDefinitionSpec{
397						Group:   "crew.example.com",
398						Version: "v1beta1",
399						Names: v1beta1.CustomResourceDefinitionNames{
400							Plural: "captains",
401						}},
402				},
403				&v1beta1.CustomResourceDefinition{
404					Spec: v1beta1.CustomResourceDefinitionSpec{
405						Group:   "crew.example.com",
406						Version: "v1beta1",
407						Names: v1beta1.CustomResourceDefinitionNames{
408							Plural: "firstmates",
409						}},
410				},
411				&v1beta1.CustomResourceDefinition{
412					Spec: v1beta1.CustomResourceDefinitionSpec{
413						Group: "crew.example.com",
414						Names: v1beta1.CustomResourceDefinitionNames{
415							Plural: "drivers",
416						},
417						Versions: []v1beta1.CustomResourceDefinitionVersion{
418							{
419								Name:    "v1",
420								Storage: true,
421								Served:  true,
422							},
423							{
424								Name:    "v2",
425								Storage: false,
426								Served:  true,
427							},
428						}},
429				},
430			},
431				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
432			)
433			Expect(err).NotTo(HaveOccurred())
434
435			// Try to re-install the CRDs
436
437			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
438				Paths: []string{filepath.Join(".", "testdata")},
439			})
440			Expect(err).NotTo(HaveOccurred())
441
442			// Expect to find the CRDs
443
444			crd = &v1beta1.CustomResourceDefinition{}
445			err = c.Get(context.TODO(), types.NamespacedName{Name: "foos.bar.example.com"}, crd)
446			Expect(err).NotTo(HaveOccurred())
447			Expect(crd.Spec.Names.Kind).To(Equal("Foo"))
448
449			crd = &v1beta1.CustomResourceDefinition{}
450			err = c.Get(context.TODO(), types.NamespacedName{Name: "bazs.qux.example.com"}, crd)
451			Expect(err).NotTo(HaveOccurred())
452			Expect(crd.Spec.Names.Kind).To(Equal("Baz"))
453
454			crd = &v1beta1.CustomResourceDefinition{}
455			err = c.Get(context.TODO(), types.NamespacedName{Name: "captains.crew.example.com"}, crd)
456			Expect(err).NotTo(HaveOccurred())
457			Expect(crd.Spec.Names.Kind).To(Equal("Captain"))
458
459			crd = &v1beta1.CustomResourceDefinition{}
460			err = c.Get(context.TODO(), types.NamespacedName{Name: "firstmates.crew.example.com"}, crd)
461			Expect(err).NotTo(HaveOccurred())
462			Expect(crd.Spec.Names.Kind).To(Equal("FirstMate"))
463
464			crd = &v1beta1.CustomResourceDefinition{}
465			err = c.Get(context.TODO(), types.NamespacedName{Name: "drivers.crew.example.com"}, crd)
466			Expect(err).NotTo(HaveOccurred())
467			Expect(crd.Spec.Names.Kind).To(Equal("Driver"))
468
469			err = WaitForCRDs(env.Config, []runtime.Object{
470				&apiextensionsv1.CustomResourceDefinition{
471					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
472						Group: "bar.example.com",
473						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
474							{
475								Name:    "v1",
476								Storage: true,
477								Served:  true,
478								Schema: &apiextensionsv1.CustomResourceValidation{
479									OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
480										Type: "object",
481									},
482								},
483							},
484						},
485						Names: apiextensionsv1.CustomResourceDefinitionNames{
486							Plural: "foos",
487						}},
488				},
489				&v1beta1.CustomResourceDefinition{
490					Spec: v1beta1.CustomResourceDefinitionSpec{
491						Group:   "qux.example.com",
492						Version: "v1beta1",
493						Names: v1beta1.CustomResourceDefinitionNames{
494							Plural: "bazs",
495						}},
496				},
497				&v1beta1.CustomResourceDefinition{
498					Spec: v1beta1.CustomResourceDefinitionSpec{
499						Group:   "crew.example.com",
500						Version: "v1beta1",
501						Names: v1beta1.CustomResourceDefinitionNames{
502							Plural: "captains",
503						}},
504				},
505				&v1beta1.CustomResourceDefinition{
506					Spec: v1beta1.CustomResourceDefinitionSpec{
507						Group:   "crew.example.com",
508						Version: "v1beta1",
509						Names: v1beta1.CustomResourceDefinitionNames{
510							Plural: "firstmates",
511						}},
512				},
513				&v1beta1.CustomResourceDefinition{
514					Spec: v1beta1.CustomResourceDefinitionSpec{
515						Group: "crew.example.com",
516						Names: v1beta1.CustomResourceDefinitionNames{
517							Plural: "drivers",
518						},
519						Versions: []v1beta1.CustomResourceDefinitionVersion{
520							{
521								Name:    "v1",
522								Storage: true,
523								Served:  true,
524							},
525							{
526								Name:    "v2",
527								Storage: false,
528								Served:  true,
529							},
530						}},
531				},
532			},
533				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
534			)
535			Expect(err).NotTo(HaveOccurred())
536
537			close(done)
538		}, 5)
539	})
540
541	It("should update CRDs if already present in the cluster", func(done Done) {
542
543		// Install only the CRDv1 multi-version example
544		crds, err = InstallCRDs(env.Config, CRDInstallOptions{
545			Paths: []string{filepath.Join(".", "testdata")},
546		})
547		Expect(err).NotTo(HaveOccurred())
548
549		// Expect to find the CRDs
550
551		crd := &v1beta1.CustomResourceDefinition{}
552		err = c.Get(context.TODO(), types.NamespacedName{Name: "drivers.crew.example.com"}, crd)
553		Expect(err).NotTo(HaveOccurred())
554		Expect(crd.Spec.Names.Kind).To(Equal("Driver"))
555		Expect(len(crd.Spec.Versions)).To(BeEquivalentTo(2))
556
557		// Store resource version for comparison later on
558		firstRV := crd.ResourceVersion
559
560		err = WaitForCRDs(env.Config, []runtime.Object{
561			&v1beta1.CustomResourceDefinition{
562				Spec: v1beta1.CustomResourceDefinitionSpec{
563					Group: "crew.example.com",
564					Names: v1beta1.CustomResourceDefinitionNames{
565						Plural: "drivers",
566					},
567					Versions: []v1beta1.CustomResourceDefinitionVersion{
568						{
569							Name:    "v1",
570							Storage: true,
571							Served:  true,
572						},
573						{
574							Name:    "v2",
575							Storage: false,
576							Served:  true,
577						},
578					}},
579			},
580		},
581			CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
582		)
583		Expect(err).NotTo(HaveOccurred())
584
585		// Add one more version and update
586		_, err = InstallCRDs(env.Config, CRDInstallOptions{
587			Paths: []string{filepath.Join(".", "testdata", "crdv1_updated")},
588		})
589		Expect(err).NotTo(HaveOccurred())
590
591		// Expect to find updated CRD
592
593		crd = &v1beta1.CustomResourceDefinition{}
594		err = c.Get(context.TODO(), types.NamespacedName{Name: "drivers.crew.example.com"}, crd)
595		Expect(err).NotTo(HaveOccurred())
596		Expect(crd.Spec.Names.Kind).To(Equal("Driver"))
597		Expect(len(crd.Spec.Versions)).To(BeEquivalentTo(3))
598		Expect(crd.ResourceVersion).NotTo(BeEquivalentTo(firstRV))
599
600		err = WaitForCRDs(env.Config, []runtime.Object{
601			&v1beta1.CustomResourceDefinition{
602				Spec: v1beta1.CustomResourceDefinitionSpec{
603					Group: "crew.example.com",
604					Names: v1beta1.CustomResourceDefinitionNames{
605						Plural: "drivers",
606					},
607					Versions: []v1beta1.CustomResourceDefinitionVersion{
608						{
609							Name:    "v1",
610							Storage: true,
611							Served:  true,
612						},
613						{
614							Name:    "v2",
615							Storage: false,
616							Served:  true,
617						},
618						{
619							Name:    "v3",
620							Storage: false,
621							Served:  true,
622						},
623					}},
624			},
625		},
626			CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
627		)
628		Expect(err).NotTo(HaveOccurred())
629
630		close(done)
631	}, 5)
632
633	Describe("UninstallCRDs", func() {
634		It("should uninstall the CRDs from the cluster", func(done Done) {
635
636			crds, err = InstallCRDs(env.Config, CRDInstallOptions{
637				Paths: []string{validDirectory},
638			})
639			Expect(err).NotTo(HaveOccurred())
640
641			// Expect to find the CRDs
642
643			crdv1 := &apiextensionsv1.CustomResourceDefinition{}
644			err = c.Get(context.TODO(), types.NamespacedName{Name: "foos.bar.example.com"}, crdv1)
645			Expect(err).NotTo(HaveOccurred())
646			Expect(crdv1.Spec.Names.Kind).To(Equal("Foo"))
647
648			crd := &v1beta1.CustomResourceDefinition{}
649			err = c.Get(context.TODO(), types.NamespacedName{Name: "bazs.qux.example.com"}, crd)
650			Expect(err).NotTo(HaveOccurred())
651			Expect(crd.Spec.Names.Kind).To(Equal("Baz"))
652
653			crd = &v1beta1.CustomResourceDefinition{}
654			err = c.Get(context.TODO(), types.NamespacedName{Name: "captains.crew.example.com"}, crd)
655			Expect(err).NotTo(HaveOccurred())
656			Expect(crd.Spec.Names.Kind).To(Equal("Captain"))
657
658			crd = &v1beta1.CustomResourceDefinition{}
659			err = c.Get(context.TODO(), types.NamespacedName{Name: "firstmates.crew.example.com"}, crd)
660			Expect(err).NotTo(HaveOccurred())
661			Expect(crd.Spec.Names.Kind).To(Equal("FirstMate"))
662
663			crd = &v1beta1.CustomResourceDefinition{}
664			err = c.Get(context.TODO(), types.NamespacedName{Name: "drivers.crew.example.com"}, crd)
665			Expect(err).NotTo(HaveOccurred())
666			Expect(crd.Spec.Names.Kind).To(Equal("Driver"))
667
668			err = WaitForCRDs(env.Config, []runtime.Object{
669				&apiextensionsv1.CustomResourceDefinition{
670					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
671						Group: "bar.example.com",
672						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
673							{
674								Name:    "v1",
675								Storage: true,
676								Served:  true,
677								Schema: &apiextensionsv1.CustomResourceValidation{
678									OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
679										Type: "object",
680									},
681								},
682							},
683						},
684						Names: apiextensionsv1.CustomResourceDefinitionNames{
685							Plural: "foos",
686						}},
687				},
688				&v1beta1.CustomResourceDefinition{
689					Spec: v1beta1.CustomResourceDefinitionSpec{
690						Group:   "qux.example.com",
691						Version: "v1beta1",
692						Names: v1beta1.CustomResourceDefinitionNames{
693							Plural: "bazs",
694						}},
695				},
696				&v1beta1.CustomResourceDefinition{
697					Spec: v1beta1.CustomResourceDefinitionSpec{
698						Group:   "crew.example.com",
699						Version: "v1beta1",
700						Names: v1beta1.CustomResourceDefinitionNames{
701							Plural: "captains",
702						}},
703				},
704				&v1beta1.CustomResourceDefinition{
705					Spec: v1beta1.CustomResourceDefinitionSpec{
706						Group:   "crew.example.com",
707						Version: "v1beta1",
708						Names: v1beta1.CustomResourceDefinitionNames{
709							Plural: "firstmates",
710						}},
711				},
712				&v1beta1.CustomResourceDefinition{
713					Spec: v1beta1.CustomResourceDefinitionSpec{
714						Group: "crew.example.com",
715						Names: v1beta1.CustomResourceDefinitionNames{
716							Plural: "drivers",
717						},
718						Versions: []v1beta1.CustomResourceDefinitionVersion{
719							{
720								Name:    "v1",
721								Storage: true,
722								Served:  true,
723							},
724							{
725								Name:    "v2",
726								Storage: false,
727								Served:  true,
728							},
729						}},
730				},
731			},
732				CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
733			)
734			Expect(err).NotTo(HaveOccurred())
735
736			err = UninstallCRDs(env.Config, CRDInstallOptions{
737				Paths: []string{validDirectory},
738			})
739			Expect(err).NotTo(HaveOccurred())
740
741			// Expect to NOT find the CRDs
742
743			v1crds := []string{
744				"foos.bar.example.com",
745			}
746			v1placeholder := &apiextensionsv1.CustomResourceDefinition{}
747			Eventually(func() bool {
748				for _, crd := range v1crds {
749					err = c.Get(context.TODO(), types.NamespacedName{Name: crd}, v1placeholder)
750					notFound := err != nil && apierrors.IsNotFound(err)
751					if !notFound {
752						return false
753					}
754				}
755				return true
756			}, 20).Should(BeTrue())
757
758			v1beta1crds := []string{
759				"bazs.qux.example.com",
760				"captains.crew.example.com",
761				"firstmates.crew.example.com",
762				"drivers.crew.example.com",
763			}
764			v1beta1placeholder := &v1beta1.CustomResourceDefinition{}
765			Eventually(func() bool {
766				for _, crd := range v1beta1crds {
767					err = c.Get(context.TODO(), types.NamespacedName{Name: crd}, v1beta1placeholder)
768					notFound := err != nil && apierrors.IsNotFound(err)
769					if !notFound {
770						return false
771					}
772				}
773				return true
774			}, 20).Should(BeTrue())
775
776			close(done)
777		}, 30)
778	})
779
780	Describe("Start", func() {
781		It("should raise an error on invalid dir when flag is enabled", func(done Done) {
782			env := &Environment{ErrorIfCRDPathMissing: true, CRDDirectoryPaths: []string{invalidDirectory}}
783			_, err := env.Start()
784			Expect(err).To(HaveOccurred())
785			Expect(env.Stop()).To(Succeed())
786			close(done)
787		}, 30)
788
789		It("should not raise an error on invalid dir when flag is disabled", func(done Done) {
790			env := &Environment{ErrorIfCRDPathMissing: false, CRDDirectoryPaths: []string{invalidDirectory}}
791			_, err := env.Start()
792			Expect(err).NotTo(HaveOccurred())
793			Expect(env.Stop()).To(Succeed())
794			close(done)
795		}, 30)
796	})
797})
798