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 crd
18
19import (
20	"fmt"
21
22	"k8s.io/utils/pointer"
23
24	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
25	crdclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
26	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
27	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28	"k8s.io/apimachinery/pkg/runtime/schema"
29	"k8s.io/client-go/dynamic"
30	"k8s.io/kubernetes/test/e2e/framework"
31)
32
33// CleanCrdFn declares the clean up function needed to remove the CRD
34type CleanCrdFn func() error
35
36// TestCrd holds all the pieces needed to test with the CRD
37type TestCrd struct {
38	APIExtensionClient *crdclientset.Clientset
39	Crd                *apiextensionsv1.CustomResourceDefinition
40	DynamicClients     map[string]dynamic.ResourceInterface
41	CleanUp            CleanCrdFn
42}
43
44// Option is a modifier for a CRD object used to customize CreateMultiVersionTestCRD and CreateTestCRD.
45type Option func(crd *apiextensionsv1.CustomResourceDefinition)
46
47// CreateMultiVersionTestCRD creates a new CRD specifically for the calling test.
48func CreateMultiVersionTestCRD(f *framework.Framework, group string, opts ...Option) (*TestCrd, error) {
49	suffix := framework.RandomSuffix()
50	name := fmt.Sprintf("e2e-test-%s-%s-crd", f.BaseName, suffix)
51	kind := fmt.Sprintf("e2e-test-%s-%s-crd", f.BaseName, suffix)
52	testcrd := &TestCrd{}
53
54	// Creating a custom resource definition for use by assorted tests.
55	config, err := framework.LoadConfig()
56	if err != nil {
57		framework.Failf("failed to load config: %v", err)
58		return nil, err
59	}
60	apiExtensionClient, err := crdclientset.NewForConfig(config)
61	if err != nil {
62		framework.Failf("failed to initialize apiExtensionClient: %v", err)
63		return nil, err
64	}
65	dynamicClient, err := dynamic.NewForConfig(config)
66	if err != nil {
67		framework.Failf("failed to initialize dynamic client: %v", err)
68		return nil, err
69	}
70
71	crd := &apiextensionsv1.CustomResourceDefinition{
72		ObjectMeta: metav1.ObjectMeta{Name: name + "s." + group},
73		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
74			Group: group,
75			Names: apiextensionsv1.CustomResourceDefinitionNames{
76				Plural:   name + "s",
77				Singular: name,
78				Kind:     kind,
79				ListKind: kind + "List",
80			},
81			Scope: apiextensionsv1.NamespaceScoped,
82		},
83	}
84	for _, opt := range opts {
85		opt(crd)
86	}
87	if len(crd.Spec.Versions) == 0 {
88		crd.Spec.Versions = []apiextensionsv1.CustomResourceDefinitionVersion{{
89			Served:  true,
90			Storage: true,
91			Name:    "v1",
92			Schema:  fixtures.AllowAllSchema(),
93		}}
94	}
95
96	//create CRD and waits for the resource to be recognized and available.
97	crd, err = fixtures.CreateNewV1CustomResourceDefinitionWatchUnsafe(crd, apiExtensionClient)
98	if err != nil {
99		framework.Failf("failed to create CustomResourceDefinition: %v", err)
100		return nil, err
101	}
102
103	resourceClients := map[string]dynamic.ResourceInterface{}
104	for _, v := range crd.Spec.Versions {
105		if v.Served {
106			gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: v.Name, Resource: crd.Spec.Names.Plural}
107			resourceClients[v.Name] = dynamicClient.Resource(gvr).Namespace(f.Namespace.Name)
108		}
109	}
110
111	testcrd.APIExtensionClient = apiExtensionClient
112	testcrd.Crd = crd
113	testcrd.DynamicClients = resourceClients
114	testcrd.CleanUp = func() error {
115		err := fixtures.DeleteV1CustomResourceDefinition(crd, apiExtensionClient)
116		if err != nil {
117			framework.Failf("failed to delete CustomResourceDefinition(%s): %v", name, err)
118		}
119		return err
120	}
121	return testcrd, nil
122}
123
124// CreateTestCRD creates a new CRD specifically for the calling test.
125func CreateTestCRD(f *framework.Framework, opts ...Option) (*TestCrd, error) {
126	group := fmt.Sprintf("%s.example.com", f.BaseName)
127	return CreateMultiVersionTestCRD(f, group, append([]Option{func(crd *apiextensionsv1.CustomResourceDefinition) {
128		crd.Spec.Versions = []apiextensionsv1.CustomResourceDefinitionVersion{
129			{
130				Name:    "v1",
131				Served:  true,
132				Storage: true,
133				Schema: &apiextensionsv1.CustomResourceValidation{
134					OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
135						XPreserveUnknownFields: pointer.BoolPtr(true),
136						Type:                   "object",
137					},
138				},
139			},
140		}
141	}}, opts...)...)
142}
143