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  })