github.com/oam-dev/kubevela@v1.9.11/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler_test.go (about) 1 /* 2 Copyright 2021. The KubeVela 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 componentdefinition 18 19 import ( 20 "context" 21 "encoding/json" 22 "os" 23 "path/filepath" 24 "testing" 25 "time" 26 27 . "github.com/onsi/ginkgo/v2" 28 . "github.com/onsi/gomega" 29 admissionv1 "k8s.io/api/admission/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/client-go/kubernetes/scheme" 33 "k8s.io/client-go/rest" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 "sigs.k8s.io/controller-runtime/pkg/envtest" 36 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 37 38 core "github.com/oam-dev/kubevela/apis/core.oam.dev" 39 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 40 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 41 ) 42 43 var handler ValidatingHandler 44 var req admission.Request 45 var reqResource metav1.GroupVersionResource 46 var decoder *admission.Decoder 47 var cd v1beta1.ComponentDefinition 48 var cdRaw []byte 49 var testScheme = runtime.NewScheme() 50 var testEnv *envtest.Environment 51 var cfg *rest.Config 52 var validCueTemplate string 53 var inValidCueTemplate string 54 55 func TestComponentdefinition(t *testing.T) { 56 RegisterFailHandler(Fail) 57 RunSpecs(t, "Componentdefinition Suite") 58 } 59 60 var _ = BeforeSuite(func() { 61 62 validCueTemplate = "{hello: 'world'}" 63 inValidCueTemplate = "{hello: world}" 64 65 var yamlPath string 66 if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set { 67 yamlPath = "../../../../../test/compatibility-test/testdata" 68 } else { 69 yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds") 70 } 71 testEnv = &envtest.Environment{ 72 ControlPlaneStartTimeout: time.Minute, 73 ControlPlaneStopTimeout: time.Minute, 74 CRDDirectoryPaths: []string{yamlPath}, 75 } 76 77 err := core.AddToScheme(testScheme) 78 Expect(err).Should(BeNil()) 79 err = scheme.AddToScheme(testScheme) 80 Expect(err).NotTo(HaveOccurred()) 81 82 cfg, err = testEnv.Start() 83 Expect(err).ToNot(HaveOccurred()) 84 Expect(cfg).ToNot(BeNil()) 85 decoder, err = admission.NewDecoder(testScheme) 86 Expect(err).Should(BeNil()) 87 88 cd = v1beta1.ComponentDefinition{} 89 cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind) 90 }) 91 92 var _ = AfterSuite(func() { 93 By("tearing down the test environment") 94 err := testEnv.Stop() 95 Expect(err).ToNot(HaveOccurred()) 96 }) 97 98 var _ = Describe("Test ComponentDefinition validating handler", func() { 99 BeforeEach(func() { 100 cli, err := client.New(cfg, client.Options{}) 101 Expect(err).Should(BeNil()) 102 reqResource = metav1.GroupVersionResource{ 103 Group: v1beta1.Group, 104 Version: v1beta1.Version, 105 Resource: "componentdefinitions"} 106 handler = ValidatingHandler{Client: cli} 107 handler.InjectDecoder(decoder) 108 }) 109 110 It("Test wrong resource of admission request", func() { 111 wrongReqResource := metav1.GroupVersionResource{ 112 Group: v1beta1.Group, 113 Version: v1beta1.Version, 114 Resource: "foos"} 115 req = admission.Request{ 116 AdmissionRequest: admissionv1.AdmissionRequest{ 117 Operation: admissionv1.Create, 118 Resource: wrongReqResource, 119 Object: runtime.RawExtension{Raw: []byte("")}, 120 }, 121 } 122 resp := handler.Handle(context.TODO(), req) 123 Expect(resp.Allowed).Should(BeFalse()) 124 }) 125 126 It("Test bad admission request", func() { 127 req = admission.Request{ 128 AdmissionRequest: admissionv1.AdmissionRequest{ 129 Operation: admissionv1.Create, 130 Resource: reqResource, 131 Object: runtime.RawExtension{Raw: []byte("bad request")}, 132 }, 133 } 134 resp := handler.Handle(context.TODO(), req) 135 Expect(resp.Allowed).Should(BeFalse()) 136 }) 137 138 Context("Test create/update operation admission request", func() { 139 It("Test componentDefinition without type and definition", func() { 140 wrongCd := v1beta1.ComponentDefinition{} 141 wrongCd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind) 142 wrongCd.SetName("wrongCd") 143 wrongCdRaw, _ := json.Marshal(wrongCd) 144 req := admission.Request{ 145 AdmissionRequest: admissionv1.AdmissionRequest{ 146 Operation: admissionv1.Create, 147 Resource: reqResource, 148 Object: runtime.RawExtension{Raw: wrongCdRaw}, 149 }, 150 } 151 resp := handler.Handle(context.TODO(), req) 152 Expect(resp.Allowed).Should(BeFalse()) 153 Expect(resp.Result.Reason).Should(Equal(metav1.StatusReason("neither the type nor the definition of the workload field in the ComponentDefinition wrongCd can be empty"))) 154 }) 155 156 It("Test componentDefinition which type and definition point to different workload type", func() { 157 wrongCd := v1beta1.ComponentDefinition{} 158 wrongCd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind) 159 wrongCd.SetName("wrongCd") 160 wrongCd.Spec.Workload.Type = "jobs.batch" 161 wrongCd.Spec.Workload.Definition = common.WorkloadGVK{ 162 APIVersion: "apps/v1", 163 Kind: "Deployment", 164 } 165 wrongCdRaw, _ := json.Marshal(wrongCd) 166 req := admission.Request{ 167 AdmissionRequest: admissionv1.AdmissionRequest{ 168 Operation: admissionv1.Create, 169 Resource: reqResource, 170 Object: runtime.RawExtension{Raw: wrongCdRaw}, 171 }, 172 } 173 resp := handler.Handle(context.TODO(), req) 174 Expect(resp.Allowed).Should(BeFalse()) 175 Expect(resp.Result.Reason).Should(Equal(metav1.StatusReason("the type and the definition of the workload field in ComponentDefinition wrongCd should represent the same workload"))) 176 }) 177 It("Test cue template validation passed", func() { 178 cd.Spec = v1beta1.ComponentDefinitionSpec{ 179 Workload: common.WorkloadTypeDescriptor{ 180 Type: "deployments.apps", 181 Definition: common.WorkloadGVK{ 182 APIVersion: "apps/v1", 183 Kind: "Deployment", 184 }, 185 }, 186 Schematic: &common.Schematic{ 187 CUE: &common.CUE{ 188 Template: validCueTemplate, 189 }, 190 }, 191 } 192 cdRaw, _ = json.Marshal(cd) 193 194 req = admission.Request{ 195 AdmissionRequest: admissionv1.AdmissionRequest{ 196 Operation: admissionv1.Create, 197 Resource: reqResource, 198 Object: runtime.RawExtension{Raw: cdRaw}, 199 }, 200 } 201 resp := handler.Handle(context.TODO(), req) 202 Expect(resp.Allowed).Should(BeTrue()) 203 }) 204 It("Test cue template validation failed", func() { 205 cd.Spec = v1beta1.ComponentDefinitionSpec{ 206 Workload: common.WorkloadTypeDescriptor{ 207 Type: "deployments.apps", 208 Definition: common.WorkloadGVK{ 209 APIVersion: "apps/v1", 210 Kind: "Deployment", 211 }, 212 }, 213 Schematic: &common.Schematic{ 214 CUE: &common.CUE{ 215 Template: inValidCueTemplate, 216 }, 217 }, 218 } 219 cdRaw, _ = json.Marshal(cd) 220 221 req = admission.Request{ 222 AdmissionRequest: admissionv1.AdmissionRequest{ 223 Operation: admissionv1.Create, 224 Resource: reqResource, 225 Object: runtime.RawExtension{Raw: cdRaw}, 226 }, 227 } 228 resp := handler.Handle(context.TODO(), req) 229 Expect(resp.Allowed).Should(BeFalse()) 230 Expect(string(resp.Result.Reason)).Should(ContainSubstring("hello: reference \"world\" not found")) 231 }) 232 }) 233 })