From b78840192096fb6ea2be68ab328dbf9e6bc7499c Mon Sep 17 00:00:00 2001 From: yangsoon Date: Tue, 16 Mar 2021 17:52:51 +0800 Subject: [PATCH] fix e2e_test --- .../templates/defwithtemplate/task.yaml | 8 +- .../templates/defwithtemplate/webservice.yaml | 8 +- .../templates/defwithtemplate/worker.yaml | 8 +- hack/vela-templates/definitions/task.yaml | 8 +- .../definitions/webservice.yaml | 8 +- hack/vela-templates/definitions/worker.yaml | 8 +- pkg/appfile/parser.go | 9 ++- pkg/appfile/parser_test.go | 6 +- .../componentdefinition_controller.go | 54 +++++-------- .../componentdefinition_controller_test.go | 63 +++++++++++++-- .../components/componentdefinition/handler.go | 78 +++++++++++++++++++ pkg/controller/utils/capability.go | 20 ++++- pkg/oam/discoverymapper/mapper.go | 12 +++ pkg/oam/mock/mapper.go | 23 +++++- pkg/oam/util/helper.go | 35 ++++++--- pkg/oam/util/helper_test.go | 46 ++--------- pkg/oam/util/template.go | 5 +- pkg/oam/util/template_test.go | 18 +++-- pkg/oam/util/test_utils.go | 13 ---- test/e2e-test/rollout_plan_test.go | 8 +- test/e2e-test/suite_test.go | 6 +- .../rollout/cloneset/clonesetDefinition.yaml | 8 +- .../cloneset/clonesetDefinitionModified.yaml | 8 +- 23 files changed, 300 insertions(+), 160 deletions(-) create mode 100644 pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/handler.go diff --git a/charts/vela-core/templates/defwithtemplate/task.yaml b/charts/vela-core/templates/defwithtemplate/task.yaml index 2e9f82cce..b9e606aec 100644 --- a/charts/vela-core/templates/defwithtemplate/task.yaml +++ b/charts/vela-core/templates/defwithtemplate/task.yaml @@ -1,14 +1,16 @@ # Code generated by KubeVela templates. DO NOT EDIT. apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: task namespace: {{.Values.systemDefinitionNamespace}} annotations: definition.oam.dev/description: "Describes jobs that run code or a script to completion." spec: - definitionRef: - name: jobs.batch + workload: + definition: + apiVersion: batch/v1 + kind: Job schematic: cue: template: | diff --git a/charts/vela-core/templates/defwithtemplate/webservice.yaml b/charts/vela-core/templates/defwithtemplate/webservice.yaml index 8d52423b8..9c8f327ba 100644 --- a/charts/vela-core/templates/defwithtemplate/webservice.yaml +++ b/charts/vela-core/templates/defwithtemplate/webservice.yaml @@ -1,14 +1,16 @@ # Code generated by KubeVela templates. DO NOT EDIT. apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: webservice namespace: {{.Values.systemDefinitionNamespace}} annotations: definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers." spec: - definitionRef: - name: deployments.apps + workload: + definition: + apiVersion: apps/v1 + kind: Deployment schematic: cue: template: | diff --git a/charts/vela-core/templates/defwithtemplate/worker.yaml b/charts/vela-core/templates/defwithtemplate/worker.yaml index a3be591ff..2e52f1e1d 100644 --- a/charts/vela-core/templates/defwithtemplate/worker.yaml +++ b/charts/vela-core/templates/defwithtemplate/worker.yaml @@ -1,14 +1,16 @@ # Code generated by KubeVela templates. DO NOT EDIT. apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: worker namespace: {{.Values.systemDefinitionNamespace}} annotations: definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic." spec: - definitionRef: - name: deployments.apps + workload: + definition: + apiVersion: apps/v1 + kind: Deployment schematic: cue: template: | diff --git a/hack/vela-templates/definitions/task.yaml b/hack/vela-templates/definitions/task.yaml index 90c7e257c..852f1391e 100644 --- a/hack/vela-templates/definitions/task.yaml +++ b/hack/vela-templates/definitions/task.yaml @@ -1,13 +1,15 @@ apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: task namespace: {{.Values.systemDefinitionNamespace}} annotations: definition.oam.dev/description: "Describes jobs that run code or a script to completion." spec: - definitionRef: - name: jobs.batch + workload: + definition: + apiVersion: batch/v1 + kind: Job schematic: cue: template: | diff --git a/hack/vela-templates/definitions/webservice.yaml b/hack/vela-templates/definitions/webservice.yaml index 7efb39e0a..4eee1a9a4 100644 --- a/hack/vela-templates/definitions/webservice.yaml +++ b/hack/vela-templates/definitions/webservice.yaml @@ -1,13 +1,15 @@ apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: webservice namespace: {{.Values.systemDefinitionNamespace}} annotations: definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers." spec: - definitionRef: - name: deployments.apps + workload: + definition: + apiVersion: apps/v1 + kind: Deployment schematic: cue: template: | diff --git a/hack/vela-templates/definitions/worker.yaml b/hack/vela-templates/definitions/worker.yaml index 71be4079e..659038b9d 100644 --- a/hack/vela-templates/definitions/worker.yaml +++ b/hack/vela-templates/definitions/worker.yaml @@ -1,13 +1,15 @@ apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: worker namespace: {{.Values.systemDefinitionNamespace}} annotations: definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic." spec: - definitionRef: - name: deployments.apps + workload: + definition: + apiVersion: apps/v1 + kind: Deployment schematic: cue: template: | diff --git a/pkg/appfile/parser.go b/pkg/appfile/parser.go index f97a8e41e..360379e25 100644 --- a/pkg/appfile/parser.go +++ b/pkg/appfile/parser.go @@ -43,7 +43,7 @@ type Workload struct { CustomStatusFormat string Helm *v1alpha2.Helm - DefinitionReference v1alpha2.DefinitionReference + DefinitionReference v1alpha2.WorkloadGVK } // GetUserConfigName get user config from AppFile, it will contain config file in it. @@ -238,7 +238,7 @@ func (p *Parser) GenerateApplicationConfiguration(app *Appfile, ns string) (*v1a switch wl.CapabilityCategory { case types.HelmCategory: - comp, acComp, err = generateComponentFromHelmModule(p.client, p.dm, wl, app.Name, app.RevisionName, ns) + comp, acComp, err = generateComponentFromHelmModule(p.client, wl, app.Name, app.RevisionName, ns) if err != nil { return nil, nil, err } @@ -291,11 +291,12 @@ func generateComponentFromCUEModule(c client.Client, wl *Workload, appName, revi return comp, acComp, nil } -func generateComponentFromHelmModule(c client.Client, dm discoverymapper.DiscoveryMapper, wl *Workload, appName, revision, ns string) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) { - targetWokrloadGVK, err := util.GetGVKFromDefinition(dm, wl.DefinitionReference) +func generateComponentFromHelmModule(c client.Client, wl *Workload, appName, revision, ns string) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) { + gv, err := schema.ParseGroupVersion(wl.DefinitionReference.APIVersion) if err != nil { return nil, nil, err } + targetWokrloadGVK := gv.WithKind(wl.DefinitionReference.Kind) // NOTE this is a hack way to enable using CUE module capabilities on Helm module workload // construct an empty base workload according to its GVK diff --git a/pkg/appfile/parser_test.go b/pkg/appfile/parser_test.go index 8a611e723..7b48d7c8a 100644 --- a/pkg/appfile/parser_test.go +++ b/pkg/appfile/parser_test.go @@ -544,9 +544,9 @@ var _ = Describe("Test appfile parser to parse helm module", func() { "url": "http://oam.dev/catalog/", }), }, - DefinitionReference: v1alpha2.DefinitionReference{ - Name: "deployments.apps", - Version: "v1", + DefinitionReference: v1alpha2.WorkloadGVK{ + APIVersion: "apps/v1", + Kind: "Deployment", }, }, }, diff --git a/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller.go b/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller.go index 7c5dcb508..330fe223b 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller.go +++ b/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller.go @@ -26,10 +26,8 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/event" "github.com/crossplane/crossplane-runtime/pkg/logging" kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" - "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -67,14 +65,28 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { return ctrl.Result{}, nil } - // if Workload.Type is not empty, means componentdefinition refer to an already existing workloaddefinition - if componentDefinition.Spec.Workload.Type != "" { - return ctrl.Result{}, nil + handler := handler{ + Client: r.Client, + dm: r.dm, + cd: &componentDefinition, } + workloadType, err := handler.CreateWorkloadDefinition(ctx) + if err != nil { + klog.ErrorS(err, "cannot create converted WorkloadDefinition") + r.record.Event(&componentDefinition, event.Warning("cannot store capability in ConfigMap", err)) + return ctrl.Result{}, util.PatchCondition(ctx, r, &componentDefinition, + cpv1alpha1.ReconcileError(fmt.Errorf(util.ErrCreateConvertedWorklaodDefinition, componentDefinition.Name, err))) + } + klog.InfoS("Successfully create WorkloadDefinition", "name", componentDefinition.Name) + var def utils.CapabilityComponentDefinition def.Name = req.NamespacedName.Name - err := def.StoreOpenAPISchema(ctx, r, req.Namespace, req.Name) + def.WorkloadType = workloadType + if workloadType == util.ReferWorkload { + def.WorkloadDefName = componentDefinition.Spec.Workload.Type + } + err = def.StoreOpenAPISchema(ctx, r, req.Namespace, req.Name) if err != nil { klog.ErrorS(err, "cannot store capability in ConfigMap") r.record.Event(&(def.ComponentDefinition), event.Warning("cannot store capability in ConfigMap", err)) @@ -89,36 +101,6 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { } klog.Info("Successfully stored Capability Schema in ConfigMap") - // if Workload.Type is empty, we need create a WorkloadDefinition - if err := r.Get(ctx, req.NamespacedName, &v1alpha2.WorkloadDefinition{}); err == nil { - klog.Infof("WorkloadDefinition: %s already exists", componentDefinition.Name) - return ctrl.Result{}, nil - } - - workloadDefinition := new(v1alpha2.WorkloadDefinition) - newCd := componentDefinition.DeepCopy() - if err := util.ConvertComponentDef2WorkloadDef(newCd, workloadDefinition); err != nil { - klog.ErrorS(err, "cannot convert ComponentDefinition") - r.record.Event(&componentDefinition, event.Warning("cannot convert ComponentDefinition", err)) - return ctrl.Result{}, util.PatchCondition(ctx, r, &componentDefinition, - cpv1alpha1.ReconcileError(fmt.Errorf(util.ErrConvertComponentDefinition, componentDefinition.Name, err))) - } - owners := []metav1.OwnerReference{{ - APIVersion: v1alpha2.SchemeGroupVersion.String(), - Kind: v1alpha2.ComponentDefinitionKind, - Name: componentDefinition.Name, - UID: componentDefinition.UID, - Controller: pointer.BoolPtr(true), - }} - workloadDefinition.SetOwnerReferences(owners) - if err := r.Create(ctx, workloadDefinition); err != nil { - klog.ErrorS(err, "cannot create converted WorkloadDefinition") - r.record.Event(&componentDefinition, event.Warning("cannot create converted Workload", err)) - return ctrl.Result{}, util.PatchCondition(ctx, r, &componentDefinition, - cpv1alpha1.ReconcileError(fmt.Errorf(util.ErrCreateConvertedWorklaodDefinition, workloadDefinition.Name, err))) - } - - klog.InfoS("Successfully create WorkloadDefinition", "name", workloadDefinition.Name) return ctrl.Result{}, nil } diff --git a/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller_test.go b/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller_test.go index f2d7cf030..e786b0a55 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/componentdefinition_controller_test.go @@ -46,7 +46,7 @@ var _ = Describe("Test ComponentDefinition Controller", func() { apiVersion: core.oam.dev/v1alpha2 kind: ComponentDefinition metadata: - name: wd-without-ready-ns + name: cd-without-ready-ns namespace: ns-def annotations: definition.oam.dev/description: "test" @@ -410,9 +410,6 @@ spec: Expect(wd.Namespace).Should(Equal(def.Namespace)) Expect(wd.Annotations).Should(Equal(def.Annotations)) Expect(wd.Spec.Schematic).Should(Equal(def.Spec.Schematic)) - convertRef, err := util.ConvertWorkloadGVK2Definition(def.Spec.Workload.Definition) - Expect(err).Should(BeNil()) - Expect(wd.Spec.Reference).Should(Equal(convertRef)) }) }) @@ -422,17 +419,69 @@ spec: req := reconcile.Request{NamespacedName: client.ObjectKey{Name: componentDefinitionName, Namespace: namespace}} It("Applying ComponentDefinition with Workload.Type", func() { + By("Apply WorkloadDefinition") + var taskWorkloadDefinition = ` +apiVersion: core.oam.dev/v1alpha2 +kind: WorkloadDefinition +metadata: + name: worker + annotations: + definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic." +spec: + definitionRef: + name: deployments.apps + schematic: + cue: + template: | + output: { + apiVersion: "apps/v1" + kind: "Deployment" + spec: { + selector: matchLabels: { + "app.oam.dev/component": context.name + } + + template: { + metadata: labels: { + "app.oam.dev/component": context.name + } + + spec: { + containers: [{ + name: context.name + image: parameter.image + + if parameter["cmd"] != _|_ { + command: parameter.cmd + } + }] + } + } + } + } + + parameter: { + // +usage=Which image would you like to use for your service + // +short=i + image: string + // +usage=Commands to run in the container + cmd?: [...string] + } +` + var task v1alpha2.WorkloadDefinition + Expect(yaml.Unmarshal([]byte(taskWorkloadDefinition), &task)).Should(BeNil()) + task.Namespace = namespace + Expect(k8sClient.Create(ctx, &task)).Should(Succeed()) + By("Apply ComponentDefinition") var validComponentDefinition = ` apiVersion: core.oam.dev/v1alpha2 kind: ComponentDefinition metadata: name: cd-with-workload-type - annotations: - definition.oam.dev/description: "test" spec: workload: - type: deployments.app + type: worker ` var def v1alpha2.ComponentDefinition Expect(yaml.Unmarshal([]byte(validComponentDefinition), &def)).Should(BeNil()) diff --git a/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/handler.go b/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/handler.go new file mode 100644 index 000000000..becb972ca --- /dev/null +++ b/pkg/controller/core.oam.dev/v1alpha2/core/components/componentdefinition/handler.go @@ -0,0 +1,78 @@ +/* + Copyright 2021. The KubeVela Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package componentdefinition + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog/v2" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" + "github.com/oam-dev/kubevela/pkg/oam/discoverymapper" + "github.com/oam-dev/kubevela/pkg/oam/util" +) + +type handler struct { + client.Client + dm discoverymapper.DiscoveryMapper + cd *v1alpha2.ComponentDefinition +} + +func (h *handler) CreateWorkloadDefinition(ctx context.Context) (util.WorkloadType, error) { + var workloadType = util.ComponentDef + var workloadName = h.cd.Name + if h.cd.Spec.Workload.Type != "" { + workloadType = util.ReferWorkload + workloadName = h.cd.Spec.Workload.Type + } + if h.cd.Spec.Schematic != nil && h.cd.Spec.Schematic.HELM != nil { + workloadType = util.HELMDef + } + wd := new(v1alpha2.WorkloadDefinition) + err := h.Get(ctx, client.ObjectKey{Namespace: h.cd.Namespace, Name: workloadName}, wd) + if workloadType == util.ReferWorkload { + if err != nil { + klog.Infof("ComponentDefinition %s refer to wrong Workload", h.cd.Name) + return workloadType, err + } + } + if workloadType == util.ComponentDef || workloadType == util.HELMDef { + if err == nil { + return workloadType, nil + } + newCd := h.cd.DeepCopy() + if err := util.ConvertComponentDef2WorkloadDef(h.dm, newCd, wd); err != nil { + return workloadType, fmt.Errorf("convert WorkloadDefinition %s error %w", h.cd.Name, err) + } + owners := []metav1.OwnerReference{{ + APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha2.ComponentDefinitionKind, + Name: h.cd.Name, + UID: h.cd.UID, + Controller: pointer.BoolPtr(true), + }} + wd.SetOwnerReferences(owners) + if err := h.Create(ctx, wd); err != nil { + return workloadType, fmt.Errorf("create converted WorkloadDefinition %s error %w", h.cd.Name, err) + } + } + return workloadType, nil +} diff --git a/pkg/controller/utils/capability.go b/pkg/controller/utils/capability.go index 4d98b9da0..da076e185 100644 --- a/pkg/controller/utils/capability.go +++ b/pkg/controller/utils/capability.go @@ -55,6 +55,8 @@ type CapabilityDefinitionInterface interface { type CapabilityComponentDefinition struct { Name string `json:"name"` ComponentDefinition v1alpha2.ComponentDefinition `json:"componentDefinition"` + WorkloadType util.WorkloadType `json:"workloadType"` + WorkloadDefName string `json:"workloadDefName"` CapabilityBaseDefinition } @@ -62,7 +64,6 @@ type CapabilityComponentDefinition struct { func (def *CapabilityComponentDefinition) GetCapabilityObject(ctx context.Context, k8sClient client.Client, namespace, name string) (*types.Capability, error) { var componentDefinition v1alpha2.ComponentDefinition var capability types.Capability - capability.Name = def.Name objectKey := client.ObjectKey{ Namespace: namespace, Name: name, @@ -72,9 +73,20 @@ func (def *CapabilityComponentDefinition) GetCapabilityObject(ctx context.Contex return nil, fmt.Errorf("failed to get ComponentDefinition %s: %w", def.Name, err) } def.ComponentDefinition = componentDefinition - capability, err = util.ConvertTemplateJSON2Object(name, componentDefinition.Spec.Extension, componentDefinition.Spec.Schematic) - if err != nil { - return nil, fmt.Errorf("failed to convert ComponentDefinition to Capability Object") + + switch def.WorkloadType { + case util.ReferWorkload: + var wd = new(v1alpha2.WorkloadDefinition) + objectKey.Name = def.WorkloadDefName + if err := k8sClient.Get(ctx, objectKey, wd); err != nil { + return nil, fmt.Errorf("failed to get WorkloadDefinition that ComponentDefinition refers to") + } + capability, err = util.ConvertTemplateJSON2Object(name, wd.Spec.Extension, wd.Spec.Schematic) + default: + capability, err = util.ConvertTemplateJSON2Object(name, componentDefinition.Spec.Extension, componentDefinition.Spec.Schematic) + if err != nil { + return nil, fmt.Errorf("failed to convert ComponentDefinition to Capability Object") + } } return &capability, err } diff --git a/pkg/oam/discoverymapper/mapper.go b/pkg/oam/discoverymapper/mapper.go index 9ffd600b2..fe8d17539 100644 --- a/pkg/oam/discoverymapper/mapper.go +++ b/pkg/oam/discoverymapper/mapper.go @@ -14,6 +14,7 @@ type DiscoveryMapper interface { Refresh() (meta.RESTMapper, error) RESTMapping(gk schema.GroupKind, version ...string) (*meta.RESTMapping, error) KindsFor(input schema.GroupVersionResource) ([]schema.GroupVersionKind, error) + ResourcesFor(input schema.GroupVersionKind) (schema.GroupVersionResource, error) } var _ DiscoveryMapper = &DefaultDiscoveryMapper{} @@ -89,3 +90,14 @@ func (d *DefaultDiscoveryMapper) KindsFor(input schema.GroupVersionResource) ([] } return mapping, err } + +// ResourcesFor will get a resource from GroupVersionKind +func (d *DefaultDiscoveryMapper) ResourcesFor(input schema.GroupVersionKind) (schema.GroupVersionResource, error) { + var gvr schema.GroupVersionResource + mapping, err := d.RESTMapping(input.GroupKind(), input.Version) + if err != nil { + return gvr, err + } + gvr = mapping.Resource + return gvr, nil +} diff --git a/pkg/oam/mock/mapper.go b/pkg/oam/mock/mapper.go index 6f7f92040..bf763e6bf 100644 --- a/pkg/oam/mock/mapper.go +++ b/pkg/oam/mock/mapper.go @@ -21,6 +21,9 @@ type RESTMapping func(gk schema.GroupKind, versions ...string) (*meta.RESTMappin // KindsFor is func type for mock convenience type KindsFor func(input schema.GroupVersionResource) ([]schema.GroupVersionKind, error) +// ResourcesFor is func type for mock convenience +type ResourcesFor func(input schema.GroupVersionKind) (schema.GroupVersionResource, error) + // NewMockDiscoveryMapper for unit test only func NewMockDiscoveryMapper() *DiscoveryMapper { return &DiscoveryMapper{ @@ -56,10 +59,11 @@ func NewMockKindsFor(kind string, version ...string) KindsFor { // DiscoveryMapper for unit test only, use GetMapper and refresh will panic type DiscoveryMapper struct { - MockGetMapper GetMapper - MockRefresh Refresh - MockRESTMapping RESTMapping - MockKindsFor KindsFor + MockGetMapper GetMapper + MockRefresh Refresh + MockRESTMapping RESTMapping + MockKindsFor KindsFor + MockResourcesFor ResourcesFor } // GetMapper for mock @@ -81,3 +85,14 @@ func (m *DiscoveryMapper) RESTMapping(gk schema.GroupKind, versions ...string) ( func (m *DiscoveryMapper) KindsFor(input schema.GroupVersionResource) ([]schema.GroupVersionKind, error) { return m.MockKindsFor(input) } + +// ResourcesFor for mock +func (m *DiscoveryMapper) ResourcesFor(input schema.GroupVersionKind) (schema.GroupVersionResource, error) { + var gvr schema.GroupVersionResource + mapping, err := m.RESTMapping(input.GroupKind(), input.Version) + if err != nil { + return gvr, err + } + gvr = mapping.Resource + return gvr, nil +} diff --git a/pkg/oam/util/helper.go b/pkg/oam/util/helper.go index 323a44d8e..3f91e2d17 100644 --- a/pkg/oam/util/helper.go +++ b/pkg/oam/util/helper.go @@ -78,12 +78,24 @@ const ( // ErrUpdateCapabilityInConfigMap is the error while creating or updating a capability ErrUpdateCapabilityInConfigMap = "cannot create or update capability %s in ConfigMap: %v" - // ErrConvertComponentDefinition is the error while convert a workloadDefinition from componentDefinition - ErrConvertComponentDefinition = "cannot convert ComponentDefinition %s to WorkloadDefinition %v" // ErrCreateConvertedWorklaodDefinition is the error while apply a WorkloadDefinition ErrCreateConvertedWorklaodDefinition = "cannot create converted WorkloadDefinition %s: %v" ) +// WorkloadType describe the workload type of ComponentDefinition +type WorkloadType string + +const ( + // ComponentDef describe a workload of Defined by ComponentDefinition + ComponentDef WorkloadType = "ComponentDef" + + // HELMDef describe a workload refer to HELM + HELMDef WorkloadType = "HELMDef" + + // ReferWorkload describe an existing workload + ReferWorkload WorkloadType = "ReferWorkload" +) + type namespaceContextKey int const ( @@ -421,19 +433,19 @@ func GetGVKFromDefinition(dm discoverymapper.DiscoveryMapper, definitionRef v1al } // ConvertWorkloadGVK2Definition help convert a GVK to DefinitionReference -func ConvertWorkloadGVK2Definition(def v1alpha2.WorkloadGVK) (v1alpha2.DefinitionReference, error) { +func ConvertWorkloadGVK2Definition(dm discoverymapper.DiscoveryMapper, def v1alpha2.WorkloadGVK) (v1alpha2.DefinitionReference, error) { var reference v1alpha2.DefinitionReference gv, err := schema.ParseGroupVersion(def.APIVersion) if err != nil { return reference, err } - resource := strings.ToLower(def.Kind) + "s" - if gv.Group == "" { - reference.Name = resource + "." + gv.Version - } else { - reference.Name = resource + "." + gv.Group - reference.Version = gv.Version + gvk := gv.WithKind(def.Kind) + gvr, err := dm.ResourcesFor(gvk) + if err != nil { + return reference, err } + reference.Version = gvr.Version + reference.Name = gvr.GroupResource().String() return reference, nil } @@ -635,12 +647,13 @@ func MergeMapOverrideWithDst(src, dst map[string]string) map[string]string { } // ConvertComponentDef2WorkloadDef help convert a ComponentDefinition to WorkloadDefinition -func ConvertComponentDef2WorkloadDef(componentDef *v1alpha2.ComponentDefinition, workloadDef *v1alpha2.WorkloadDefinition) error { +func ConvertComponentDef2WorkloadDef(dm discoverymapper.DiscoveryMapper, componentDef *v1alpha2.ComponentDefinition, + workloadDef *v1alpha2.WorkloadDefinition) error { if len(componentDef.Spec.Workload.Type) > 1 { return errors.New("No need to convert ComponentDefinition") } var reference v1alpha2.DefinitionReference - reference, err := ConvertWorkloadGVK2Definition(componentDef.Spec.Workload.Definition) + reference, err := ConvertWorkloadGVK2Definition(dm, componentDef.Spec.Workload.Definition) if err != nil { return fmt.Errorf("create DefinitionReference fail %w", err) } diff --git a/pkg/oam/util/helper_test.go b/pkg/oam/util/helper_test.go index 903f04084..7bd81266e 100644 --- a/pkg/oam/util/helper_test.go +++ b/pkg/oam/util/helper_test.go @@ -812,44 +812,14 @@ func TestGetGVKFromDef(t *testing.T) { } func TestConvertWorkloadGVK2Def(t *testing.T) { - type want struct { - ref v1alpha2.DefinitionReference - err error - } - convertErr := fmt.Errorf("unexpected GroupVersion string: %v", "apps/v1/") - - cases := []struct { - testName string - workloadGVK v1alpha2.WorkloadGVK - want want - }{{ - testName: "expected GVK with version", - workloadGVK: v1alpha2.WorkloadGVK{APIVersion: "apps/v1", Kind: "Deployment"}, - want: want{ - ref: v1alpha2.DefinitionReference{Name: "deployments.apps", Version: "v1"}, - err: nil, - }, - }, { - testName: "expected GVK without version", - workloadGVK: v1alpha2.WorkloadGVK{APIVersion: "apps", Kind: "Deployment"}, - want: want{ - ref: v1alpha2.DefinitionReference{Name: "deployments.apps", Version: ""}, - err: nil, - }, - }, { - testName: "unexpected GVK", - workloadGVK: v1alpha2.WorkloadGVK{APIVersion: "apps/v1/", Kind: "Deployment"}, - want: want{ - ref: v1alpha2.DefinitionReference{}, - err: convertErr, - }, - }} - for _, tc := range cases { - ref, err := util.ConvertWorkloadGVK2Definition(tc.workloadGVK) - t.Log(fmt.Sprint("Running test: ", tc.testName)) - assert.Equal(t, tc.want.err, err) - assert.Equal(t, tc.want.ref, ref) - } + mapper := mock.NewMockDiscoveryMapper() + mapper.MockRESTMapping = mock.NewMockRESTMapping("deployments") + ref, err := util.ConvertWorkloadGVK2Definition(mapper, v1alpha2.WorkloadGVK{APIVersion: "apps/v1", Kind: "Deployment"}) + assert.NoError(t, err) + assert.Equal(t, v1alpha2.DefinitionReference{ + Name: "deployments.apps", + Version: "v1", + }, ref) } func TestGenTraitName(t *testing.T) { diff --git a/pkg/oam/util/template.go b/pkg/oam/util/template.go index 2cc4a5402..38bd77522 100644 --- a/pkg/oam/util/template.go +++ b/pkg/oam/util/template.go @@ -21,7 +21,7 @@ type Template struct { Health string CustomStatus string CapabilityCategory types.CapabilityCategory - Reference v1alpha2.DefinitionReference + Reference v1alpha2.WorkloadGVK Helm *v1alpha2.Helm } @@ -54,7 +54,7 @@ func LoadTemplate(ctx context.Context, cli client.Reader, key string, kd types.C if tmpl == nil { return nil, errors.New("no template found in definition") } - tmpl.Reference, err = ConvertWorkloadGVK2Definition(cd.Spec.Workload.Definition) + tmpl.Reference = cd.Spec.Workload.Definition if err != nil { return nil, fmt.Errorf("get DefinitionReference error %w", err) } @@ -80,7 +80,6 @@ func LoadTemplate(ctx context.Context, cli client.Reader, key string, kd types.C if tmpl == nil { return nil, errors.New("no template found in definition") } - tmpl.Reference = td.Spec.Reference tmpl.CapabilityCategory = capabilityCategory return tmpl, nil case types.TypeScope: diff --git a/pkg/oam/util/template_test.go b/pkg/oam/util/template_test.go index a6b443f57..4bc8be693 100644 --- a/pkg/oam/util/template_test.go +++ b/pkg/oam/util/template_test.go @@ -15,7 +15,7 @@ import ( "github.com/oam-dev/kubevela/apis/types" ) -func TestLoadWorkloadTemplate(t *testing.T) { +func TestLoadComponentTemplate(t *testing.T) { cueTemplate := ` context: { name: "test" @@ -60,17 +60,19 @@ func TestLoadWorkloadTemplate(t *testing.T) { } ` - var workloadDefintion = ` + var componentDefintion = ` apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: worker namespace: default annotations: definition.oam.dev/description: "Long-running scalable backend worker without network endpoint" spec: - definitionRef: - name: deployments.apps + workload: + definition: + apiVersion: apps/v1 + kind: Deployment extension: template: | ` + cueTemplate @@ -79,12 +81,12 @@ spec: tclient := test.MockClient{ MockGet: func(ctx context.Context, key ktypes.NamespacedName, obj runtime.Object) error { switch o := obj.(type) { - case *v1alpha2.WorkloadDefinition: - wd, err := UnMarshalStringToWorkloadDefinition(workloadDefintion) + case *v1alpha2.ComponentDefinition: + cd, err := UnMarshalStringToComponentDefinition(componentDefintion) if err != nil { return err } - *o = *wd + *o = *cd } return nil }, diff --git a/pkg/oam/util/test_utils.go b/pkg/oam/util/test_utils.go index 92b4af6ec..a5549d063 100644 --- a/pkg/oam/util/test_utils.go +++ b/pkg/oam/util/test_utils.go @@ -145,19 +145,6 @@ func UnMarshalStringToComponentDefinition(s string) (*v1alpha2.ComponentDefiniti return obj, nil } -// UnMarshalStringToWorkloadDefinition parse a string to a workloadDefinition object -func UnMarshalStringToWorkloadDefinition(s string) (*v1alpha2.WorkloadDefinition, error) { - obj := &v1alpha2.WorkloadDefinition{} - _body, err := yaml.YAMLToJSON([]byte(s)) - if err != nil { - return nil, err - } - if err := json.Unmarshal(_body, obj); err != nil { - return nil, err - } - return obj, nil -} - // UnMarshalStringToTraitDefinition parse a string to a traitDefinition object func UnMarshalStringToTraitDefinition(s string) (*v1alpha2.TraitDefinition, error) { obj := &v1alpha2.TraitDefinition{} diff --git a/test/e2e-test/rollout_plan_test.go b/test/e2e-test/rollout_plan_test.go index d936a4e32..fadeaee2b 100644 --- a/test/e2e-test/rollout_plan_test.go +++ b/test/e2e-test/rollout_plan_test.go @@ -62,10 +62,10 @@ var _ = Describe("Cloneset based rollout tests", func() { } CreateClonesetDef := func() { - By("Install CloneSet based workloadDefinition") - var cd v1alpha2.WorkloadDefinition + By("Install CloneSet based componentDefinition") + var cd v1alpha2.ComponentDefinition Expect(readYaml("testdata/rollout/cloneset/clonesetDefinition.yaml", &cd)).Should(BeNil()) - // create the workloadDefinition if not exist + // create the componentDefinition if not exist Eventually( func() error { return k8sClient.Create(ctx, &cd) @@ -564,7 +564,7 @@ var _ = Describe("Cloneset based rollout tests", func() { ApplySourceApp() MarkAppRolling(1) By("Apply the definition change") - var cd, newCD v1alpha2.WorkloadDefinition + var cd, newCD v1alpha2.ComponentDefinition Expect(readYaml("testdata/rollout/cloneset/clonesetDefinitionModified.yaml.yaml", &newCD)).Should(BeNil()) Eventually( func() error { diff --git a/test/e2e-test/suite_test.go b/test/e2e-test/suite_test.go index da312a219..763aef19a 100644 --- a/test/e2e-test/suite_test.go +++ b/test/e2e-test/suite_test.go @@ -98,7 +98,11 @@ var _ = BeforeSuite(func(done Done) { } // TODO: Remove this after we get rid of the integration test dir - By("Applying CRD of WorkloadDefinition and TraitDefinition") + By("Applying CRD of ComponentDefinition, WorkloadDefinition and TraitDefinition") + var componentDefinitionCRD crdv1.CustomResourceDefinition + Expect(readYaml("../../charts/vela-core/crds/core.oam.dev_componentdefinitions.yaml", &componentDefinitionCRD)).Should(BeNil()) + Expect(k8sClient.Create(context.Background(), &componentDefinitionCRD)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + var workloadDefinitionCRD crdv1.CustomResourceDefinition Expect(readYaml("../../charts/vela-core/crds/core.oam.dev_workloaddefinitions.yaml", &workloadDefinitionCRD)).Should(BeNil()) Expect(k8sClient.Create(context.Background(), &workloadDefinitionCRD)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) diff --git a/test/e2e-test/testdata/rollout/cloneset/clonesetDefinition.yaml b/test/e2e-test/testdata/rollout/cloneset/clonesetDefinition.yaml index ccfae7431..9c2949923 100644 --- a/test/e2e-test/testdata/rollout/cloneset/clonesetDefinition.yaml +++ b/test/e2e-test/testdata/rollout/cloneset/clonesetDefinition.yaml @@ -1,6 +1,6 @@ # Code generated by KubeVela templates. DO NOT EDIT. apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: clonesetservice namespace: vela-system @@ -8,8 +8,10 @@ metadata: definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers. If workload type is skipped for any service defined in Appfile, it will be defaulted to `webservice` type." spec: - definitionRef: - name: clonesets.apps.kruise.io + workload: + definition: + apiVersion: apps.kruise.io/v1alpha1 + kind: CloneSet schematic: cue: template: | diff --git a/test/e2e-test/testdata/rollout/cloneset/clonesetDefinitionModified.yaml b/test/e2e-test/testdata/rollout/cloneset/clonesetDefinitionModified.yaml index a374b34c2..471390388 100644 --- a/test/e2e-test/testdata/rollout/cloneset/clonesetDefinitionModified.yaml +++ b/test/e2e-test/testdata/rollout/cloneset/clonesetDefinitionModified.yaml @@ -1,6 +1,6 @@ # Code generated by KubeVela templates. DO NOT EDIT. apiVersion: core.oam.dev/v1alpha2 -kind: WorkloadDefinition +kind: ComponentDefinition metadata: name: clonesetservice namespace: vela-system @@ -8,8 +8,10 @@ metadata: definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers. If workload type is skipped for any service defined in Appfile, it will be defaulted to `webservice` type." spec: - definitionRef: - name: clonesets.apps.kruise.io + workload: + definition: + apiVersion: apps.kruise.io/v1alpha1 + kind: CloneSet schematic: cue: template: |