From 3a47d5de73585ea02255ff13a5a29f69ac8622be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E5=85=83?= Date: Mon, 28 Dec 2020 20:11:58 +0800 Subject: [PATCH] refactor application object to have more strict schema --- Makefile | 2 +- .../v1alpha2/application_types.go | 30 ++++- .../v1alpha2/zz_generated.deepcopy.go | 61 ++++++++++ .../crds/core.oam.dev_applications.yaml | 37 +++++- .../{vela-server => application}/Demo.md | 34 +++--- .../{vela-server => application}/README.md | 100 +++++++++------- .../application/application-sample.yaml | 25 ++++ .../template.yaml | 2 +- .../vela-server/application-sample.yaml | 20 ---- go.mod | 1 - .../crds/core.oam.dev_applications.yaml | 37 +++++- .../application/application_controller.go | 7 +- .../v1alpha2/application/apply.go | 2 +- .../v1alpha2/application/builder/build.go | 24 ++-- .../v1alpha2/application/parser/service.go | 113 +++++++----------- .../application/parser/service_test.go | 34 ++++-- .../v1alpha2/application/suite_test.go | 11 +- .../application/validating_handler.go | 7 +- .../application/validating_handler_test.go | 56 ++------- test/e2e-test/component_version_test.go | 13 +- test/e2e-test/health_scope_test.go | 8 ++ 21 files changed, 372 insertions(+), 252 deletions(-) rename config/samples/{vela-server => application}/Demo.md (89%) rename config/samples/{vela-server => application}/README.md (85%) create mode 100644 config/samples/application/application-sample.yaml rename config/samples/{vela-server => application}/template.yaml (99%) delete mode 100644 config/samples/vela-server/application-sample.yaml diff --git a/Makefile b/Makefile index 41f6b0d98..29cf95643 100644 --- a/Makefile +++ b/Makefile @@ -105,7 +105,7 @@ vet: lint: golangci $(GOLANGCILINT) run ./... -reviewable: fmt vet lint manifests +reviewable: manifests fmt vet lint go mod tidy # Execute auto-gen code commands and ensure branch is clean. diff --git a/apis/core.oam.dev/v1alpha2/application_types.go b/apis/core.oam.dev/v1alpha2/application_types.go index dcded5b44..946bc2c0b 100644 --- a/apis/core.oam.dev/v1alpha2/application_types.go +++ b/apis/core.oam.dev/v1alpha2/application_types.go @@ -46,6 +46,31 @@ type AppStatus struct { Phase ApplicationPhase `json:"status,omitempty"` } +// ApplicationTrait defines the trait of application +type ApplicationTrait struct { + Name string `json:"name"` + // +kubebuilder:pruning:PreserveUnknownFields + Properties runtime.RawExtension `json:"properties"` +} + +// ApplicationComponent describe the component of application +type ApplicationComponent struct { + Name string `json:"name"` + WorkloadType string `json:"type"` + // +kubebuilder:pruning:PreserveUnknownFields + Settings runtime.RawExtension `json:"settings"` + + // Traits define the trait of one component, the type must be array to keep the order. + Traits []ApplicationTrait `json:"traits,omitempty"` +} + +// ApplicationSpec is the spec of Application +type ApplicationSpec struct { + Components []ApplicationComponent `json:"components"` + + // TODO(wonderflow): there should have scopes defined here +} + // +kubebuilder:object:root=true // Application is the Schema for the applications API @@ -54,9 +79,8 @@ type Application struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // +kubebuilder:pruning:PreserveUnknownFields - Spec runtime.RawExtension `json:"spec,omitempty"` - Status AppStatus `json:"status,omitempty"` + Spec ApplicationSpec `json:"spec,omitempty"` + Status AppStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true diff --git a/apis/core.oam.dev/v1alpha2/zz_generated.deepcopy.go b/apis/core.oam.dev/v1alpha2/zz_generated.deepcopy.go index b379c63d6..56f5e9148 100644 --- a/apis/core.oam.dev/v1alpha2/zz_generated.deepcopy.go +++ b/apis/core.oam.dev/v1alpha2/zz_generated.deepcopy.go @@ -68,6 +68,29 @@ func (in *Application) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationComponent) DeepCopyInto(out *ApplicationComponent) { + *out = *in + in.Settings.DeepCopyInto(&out.Settings) + if in.Traits != nil { + in, out := &in.Traits, &out.Traits + *out = make([]ApplicationTrait, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationComponent. +func (in *ApplicationComponent) DeepCopy() *ApplicationComponent { + if in == nil { + return nil + } + out := new(ApplicationComponent) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationConfiguration) DeepCopyInto(out *ApplicationConfiguration) { *out = *in @@ -346,6 +369,44 @@ func (in *ApplicationList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) { + *out = *in + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]ApplicationComponent, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSpec. +func (in *ApplicationSpec) DeepCopy() *ApplicationSpec { + if in == nil { + return nil + } + out := new(ApplicationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationTrait) DeepCopyInto(out *ApplicationTrait) { + *out = *in + in.Properties.DeepCopyInto(&out.Properties) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationTrait. +func (in *ApplicationTrait) DeepCopy() *ApplicationTrait { + if in == nil { + return nil + } + out := new(ApplicationTrait) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CPUResources) DeepCopyInto(out *CPUResources) { *out = *in diff --git a/charts/vela-core/crds/core.oam.dev_applications.yaml b/charts/vela-core/crds/core.oam.dev_applications.yaml index 9c087313a..314a5dd0b 100644 --- a/charts/vela-core/crds/core.oam.dev_applications.yaml +++ b/charts/vela-core/crds/core.oam.dev_applications.yaml @@ -30,8 +30,43 @@ spec: metadata: type: object spec: + description: ApplicationSpec is the spec of Application + properties: + components: + items: + description: ApplicationComponent describe the component of application + properties: + name: + type: string + settings: + type: object + x-kubernetes-preserve-unknown-fields: true + traits: + description: Traits define the trait of one component, the type must be array to keep the order. + items: + description: ApplicationTrait defines the trait of application + properties: + name: + type: string + properties: + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - name + - properties + type: object + type: array + type: + type: string + required: + - name + - settings + - type + type: object + type: array + required: + - components type: object - x-kubernetes-preserve-unknown-fields: true status: description: AppStatus defines the observed state of Application properties: diff --git a/config/samples/vela-server/Demo.md b/config/samples/application/Demo.md similarity index 89% rename from config/samples/vela-server/Demo.md rename to config/samples/application/Demo.md index 16b1562c5..4d15bbf4e 100644 --- a/config/samples/vela-server/Demo.md +++ b/config/samples/application/Demo.md @@ -27,24 +27,26 @@ metadata: name: application-sample namespace: oam-test spec: - services: - myweb: - cmd: - - sleep - - "1000" - image: busybox - scaler: - replicas: 10 - service: - http: - server: 80 - sidecar: - command: + components: + - name: myweb + type: worker + settings: + image: "busybox" + cmd: - sleep - "1000" - image: busybox - name: test-sidecar - type: worker + traits: + - name: scaler + properties: + replicas: 10 + - name: sidercar + properties: + name: "sidecar-test" + image: "nginx" + - name: kservice + properties: + http: + server: 80 status: conditions: - lastTransitionTime: "2020-12-02T12:12:52Z" diff --git a/config/samples/vela-server/README.md b/config/samples/application/README.md similarity index 85% rename from config/samples/vela-server/README.md rename to config/samples/application/README.md index 55ad46ea1..ceaf0cc5e 100644 --- a/config/samples/vela-server/README.md +++ b/config/samples/application/README.md @@ -112,13 +112,14 @@ kind: Application metadata: name: application-sample spec: - services: - myweb: + components: + - name: myweb type: worker - image: "busybox" - cmd: - - sleep - - "1000" + settings: + image: "busybox" + cmd: + - sleep + - "1000" ``` we will get a deployment ``` @@ -149,7 +150,7 @@ kind: TraitDefinition metadata: annotations: definition.oam.dev/description: "service the app" - name: service + name: kservice spec: appliesToWorkloads: - webservice @@ -185,16 +186,19 @@ kind: Application metadata: name: application-sample spec: - services: - myweb: + components: + - name: myweb type: worker - image: "busybox" - cmd: - - sleep - - "1000" - service: - http: - server: 80 + settings: + image: "busybox" + cmd: + - sleep + - "1000" + traits: + - name: kservice + properties: + http: + server: 80 ``` we will get a new deployment and service @@ -258,23 +262,22 @@ spec: ``` If add scaler capability to the application, as follows ``` -apiVersion: core.oam.dev/v1alpha2 -kind: Application -metadata: - name: application-sample -spec: - services: - myweb: + components: + - name: myweb type: worker - image: "busybox" - cmd: - - sleep - - "1000" - service: - http: - server: 80 - scaler: - replicas: 10 + settings: + image: "busybox" + cmd: + - sleep + - "1000" + traits: + - name: kservice + properties: + http: + server: 80 + - name: scaler + properties: + replicas: 10 ``` The deployment replicas will be scale to 10 @@ -336,21 +339,26 @@ kind: Application metadata: name: application-sample spec: - services: - myweb: + components: + - name: myweb type: worker - image: "busybox" - cmd: - - sleep - - "1000" - service: - http: - server: 80 - scaler: - replicas: 10 - sidercar: - name: "sidecar-test" - image: "nginx" + settings: + image: "busybox" + cmd: + - sleep + - "1000" + traits: + - name: scaler + properties: + replicas: 10 + - name: sidercar + properties: + name: "sidecar-test" + image: "nginx" + - name: kservice + properties: + http: + server: 80 ``` The deployment updated as follows ``` diff --git a/config/samples/application/application-sample.yaml b/config/samples/application/application-sample.yaml new file mode 100644 index 000000000..7c1cbeaf0 --- /dev/null +++ b/config/samples/application/application-sample.yaml @@ -0,0 +1,25 @@ +apiVersion: core.oam.dev/v1alpha2 +kind: Application +metadata: + name: application-sample +spec: + components: + - name: myweb + type: worker + settings: + image: "busybox" + cmd: + - sleep + - "1000" + traits: + - name: scaler + properties: + replicas: 10 + - name: sidercar + properties: + name: "sidecar-test" + image: "nginx" + - name: kservice + properties: + http: + server: 80 \ No newline at end of file diff --git a/config/samples/vela-server/template.yaml b/config/samples/application/template.yaml similarity index 99% rename from config/samples/vela-server/template.yaml rename to config/samples/application/template.yaml index e7b49024c..766d25220 100644 --- a/config/samples/vela-server/template.yaml +++ b/config/samples/application/template.yaml @@ -95,7 +95,7 @@ kind: TraitDefinition metadata: annotations: definition.oam.dev/description: "service the app" - name: service + name: kservice spec: appliesToWorkloads: - webservice diff --git a/config/samples/vela-server/application-sample.yaml b/config/samples/vela-server/application-sample.yaml deleted file mode 100644 index f0f10ddda..000000000 --- a/config/samples/vela-server/application-sample.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: core.oam.dev/v1alpha2 -kind: Application -metadata: - name: application-sample -spec: - services: - myweb: - type: worker - image: "busybox" - cmd: - - sleep - - "1000" - scaler: - replicas: 10 - sidercar: - name: "sidecar-test" - image: "nginx" - service: - http: - server: 80 \ No newline at end of file diff --git a/go.mod b/go.mod index 4e7110ef9..741d361f6 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,6 @@ require ( golang.org/x/text v0.3.4 // indirect golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8 gotest.tools v2.2.0+incompatible helm.sh/helm/v3 v3.2.4 k8s.io/api v0.18.8 diff --git a/legacy/charts/vela-core-legacy/crds/core.oam.dev_applications.yaml b/legacy/charts/vela-core-legacy/crds/core.oam.dev_applications.yaml index b592c538a..7cb84e104 100644 --- a/legacy/charts/vela-core-legacy/crds/core.oam.dev_applications.yaml +++ b/legacy/charts/vela-core-legacy/crds/core.oam.dev_applications.yaml @@ -30,8 +30,43 @@ spec: metadata: type: object spec: + description: ApplicationSpec is the spec of Application + properties: + components: + items: + description: ApplicationComponent describe the component of application + properties: + name: + type: string + settings: + type: object + + traits: + description: Traits define the trait of one component, the type must be array to keep the order. + items: + description: ApplicationTrait defines the trait of application + properties: + name: + type: string + properties: + type: object + + required: + - name + - properties + type: object + type: array + type: + type: string + required: + - name + - settings + - type + type: object + type: array + required: + - components type: object - status: description: AppStatus defines the observed state of Application properties: diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go index 641bda5ae..cd3f1be3b 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go @@ -76,12 +76,7 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (result ctrl.Result, gerr error // parse template appParser := parser.NewParser(template.GetHanler(fclient.NewDefinitionClient(r.Client))) - expr, err := parser.DecodeJSONMarshaler(app.Spec) - if err != nil { - app.Status.SetConditions(errorCondition("Parsed", err)) - return handler.Err(err) - } - appfile, err := appParser.Parse(app.Name, expr) + appfile, err := appParser.Parse(app.Name, app) if err != nil { app.Status.SetConditions(errorCondition("Parsed", err)) diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/apply.go b/pkg/controller/core.oam.dev/v1alpha2/application/apply.go index d4281b64a..cd97fb4b0 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/apply.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/apply.go @@ -66,7 +66,7 @@ func (ret *reter) apply(ac *v1alpha2.ApplicationConfiguration, comps ...*v1alpha objs = append(objs, ac) listOption := listObjs(client.MatchingLabels{ - builder.OamApplicationLabel: ret.app.Name, + builder.OAMApplicationLabel: ret.app.Name, }, client.InNamespace(ret.app.Namespace)) isController := true diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/builder/build.go b/pkg/controller/core.oam.dev/v1alpha2/application/builder/build.go index 877bbc61c..89a8ae125 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/builder/build.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/builder/build.go @@ -21,8 +21,8 @@ type builder struct { } const ( - // OamApplicationLabel is application's metadata label - OamApplicationLabel = "application.oam.dev" + // OAMApplicationLabel is application's metadata label + OAMApplicationLabel = "application.oam.dev" ) // Build template to applicationConfig & Component @@ -42,9 +42,9 @@ func (b *builder) Complete(ns string) (*v1alpha2.ApplicationConfiguration, []*v1 if appconfig.Labels == nil { appconfig.Labels = map[string]string{} } - appconfig.Labels[OamApplicationLabel] = b.app.Name() + appconfig.Labels[OAMApplicationLabel] = b.app.Name() - componets := []*v1alpha2.Component{} + var componets []*v1alpha2.Component for _, wl := range b.app.Services() { compCtx := map[string]string{"name": wl.Name()} @@ -58,7 +58,7 @@ func (b *builder) Complete(ns string) (*v1alpha2.ApplicationConfiguration, []*v1 if component.Labels == nil { component.Labels = map[string]string{} } - component.Labels[OamApplicationLabel] = b.app.Name() + component.Labels[OAMApplicationLabel] = b.app.Name() component.SetGroupVersionKind(v1alpha2.ComponentGroupVersionKind) componets = append(componets, component) @@ -89,9 +89,9 @@ func (b *builder) CompleteWithContext(ns string) (*v1alpha2.ApplicationConfigura if appconfig.Labels == nil { appconfig.Labels = map[string]string{} } - appconfig.Labels[OamApplicationLabel] = b.app.Name() + appconfig.Labels[OAMApplicationLabel] = b.app.Name() - componets := []*v1alpha2.Component{} + var components []*v1alpha2.Component for _, wl := range b.app.Services() { pCtx := process.NewContext(wl.Name()) if err := wl.EvalContext(pCtx); err != nil { @@ -113,24 +113,24 @@ func (b *builder) CompleteWithContext(ns string) (*v1alpha2.ApplicationConfigura if comp.Labels == nil { comp.Labels = map[string]string{} } - comp.Labels[OamApplicationLabel] = b.app.Name() + comp.Labels[OAMApplicationLabel] = b.app.Name() comp.SetGroupVersionKind(v1alpha2.ComponentGroupVersionKind) - componets = append(componets, comp) + components = append(components, comp) appconfig.Spec.Components = append(appconfig.Spec.Components, *acComp) } - return appconfig, componets, nil + return appconfig, components, nil } func generateOAM(pCtx process.Context) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) { base, assists := pCtx.Output() - componetWorkload, err := base.Object(nil) + componentWorkload, err := base.Object(nil) if err != nil { return nil, nil, err } component := &v1alpha2.Component{} - component.Spec.Workload.Object = componetWorkload + component.Spec.Workload.Object = componentWorkload acComponent := &v1alpha2.ApplicationConfigurationComponent{} acComponent.Traits = []v1alpha2.ComponentTrait{} diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/parser/service.go b/pkg/controller/core.oam.dev/v1alpha2/application/parser/service.go index 5aa18762f..088bf36ad 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/parser/service.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/parser/service.go @@ -1,8 +1,6 @@ package parser import ( - "fmt" - "cuelang.org/go/cue" cueJson "cuelang.org/go/pkg/encoding/json" "github.com/pkg/errors" @@ -94,8 +92,7 @@ func (trait *Trait) Eval(render Render) ([]v1alpha2.ComponentTrait, error) { outputs := inst.Lookup("outputs") iter, err := outputs.List() if err != nil { - return nil, errors.Errorf("output|outputs not found in traitDef %s ", trait.name) - // return nil,errors.WithMessagef(err,"traitDef %s outputs must be list",trait.name) + return nil, errors.Errorf("'output' or 'outputs' not found in trait definition %s ", trait.name) } for iter.Next() { cueValues = append(cueValues, iter.Value()) @@ -164,95 +161,71 @@ func NewParser(handler template.Handler) *Parser { } // Parse convert map to Appfile -func (pser *Parser) Parse(name string, expr map[string]interface{}) (*Appfile, error) { - var svcs interface{} - for _, name := range []string{"Service", "Services", "service", "services"} { - if v, ok := expr[name]; ok { - svcs = v - } - } - if svcs == nil { - return nil, errors.Errorf("services require") - } +func (pser *Parser) Parse(name string, app *v1alpha2.Application) (*Appfile, error) { + appfile := new(Appfile) appfile.name = name - switch v := svcs.(type) { - case map[string]interface{}: - wds := []*Workload{} - for name, wi := range v { - wd, err := pser.parseWorkload(name, wi) - if err != nil { - return nil, err - } - wds = append(wds, wd) + var wds []*Workload + for _, comp := range app.Spec.Components { + wd, err := pser.parseWorkload(comp) + if err != nil { + return nil, err } - appfile.services = wds - default: - return nil, errors.Errorf("services format invalid(must be map)") + wds = append(wds, wd) } + appfile.services = wds + return appfile, nil } -func (pser *Parser) parseWorkload(name string, expr interface{}) (*Workload, error) { +func (pser *Parser) parseWorkload(comp v1alpha2.ApplicationComponent) (*Workload, error) { workload := new(Workload) workload.traits = []*Trait{} - workload.name = name - switch v := expr.(type) { - case map[string]interface{}: - _type, ok := v["type"] - if !ok { - return nil, errors.Errorf("type not specify") + workload.name = comp.Name + workload.typ = comp.WorkloadType + templ, kind, err := pser.templ(workload.typ) + if err != nil && !kerrors.IsNotFound(err) { + return nil, errors.WithMessagef(err, "fetch type of %s", comp.Name) + } + if kind != template.WorkloadKind { + return nil, errors.Errorf("%s type (%s) invalid", comp.Name, workload.typ) + } + workload.template = templ + settings, err := DecodeJSONMarshaler(comp.Settings) + if err != nil { + return nil, errors.Errorf("fail to parse settings for %s", comp.Name) + } + workload.params = settings + for _, traitValue := range comp.Traits { + properties, err := DecodeJSONMarshaler(traitValue.Properties) + if err != nil { + return nil, errors.Errorf("fail to parse properties of %s for %s", traitValue.Name, comp.Name) } - workload.typ = fmt.Sprint(_type) - templ, kind, err := pser.templ(workload.typ) - if err != nil && !kerrors.IsNotFound(err) { - return nil, errors.WithMessagef(err, "fetch %s' type", name) + trait, err := pser.parseTrait(traitValue.Name, properties) + if err != nil { + return nil, errors.WithMessagef(err, "component(%s) parse trait(%s)", comp.Name, traitValue.Name) } - if kind == template.Unkownkind || kind == template.TraitKind { - return nil, errors.Errorf("%s type (%s) invalid", name, workload.typ) - } - workload.template = templ - params := map[string]interface{}{} - for lable, value := range v { - if lable == "type" { - continue - } - trait, err := pser.parseTrait(lable, value) - if err != nil { - return nil, errors.WithMessagef(err, "service(%s) parse trait(%s)", name, lable) - } - if trait == nil { - params[lable] = value - } else { - workload.traits = append(workload.traits, trait) - } - } - workload.params = params - default: - return nil, errors.Errorf("service(%s) format invalid", name) + + workload.traits = append(workload.traits, trait) + } return workload, nil } -func (pser *Parser) parseTrait(label string, expr interface{}) (*Trait, error) { +func (pser *Parser) parseTrait(name string, properties map[string]interface{}) (*Trait, error) { - templ, kind, err := pser.templ(label) + templ, kind, err := pser.templ(name) if err != nil && !kerrors.IsNotFound(err) { return nil, err } if kind != template.TraitKind { - return nil, nil + return nil, errors.Errorf("kind of %s is not trait", name) } trait := new(Trait) trait.template = templ - trait.name = label - switch v := expr.(type) { - case map[string]interface{}: - trait.params = v - default: - return nil, errors.Errorf("trait %s params must be map", label) - } + trait.name = name + trait.params = properties return trait, nil } @@ -310,7 +283,7 @@ var TestExceptApp = &Appfile{ { name: "scaler", params: map[string]interface{}{ - "replicas": 10, + "replicas": float64(10), }, template: ` output: { diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/parser/service_test.go b/pkg/controller/core.oam.dev/v1alpha2/application/parser/service_test.go index 0a63508ce..672ff370d 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/parser/service_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/parser/service_test.go @@ -6,8 +6,9 @@ import ( "testing" "cuelang.org/go/cue" - "gopkg.in/yaml.v3" + "github.com/ghodss/yaml" + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application/defclient" "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application/template" ) @@ -93,26 +94,33 @@ spec: }`) const appfileYaml = ` -services: - myweb: - type: worker - image: "busybox" - cmd: - - sleep - - "1000" - scaler: - replicas: 10 +apiVersion: core.oam.dev/v1alpha2 +kind: Application +metadata: + name: application-sample +spec: + components: + - name: myweb + type: worker + settings: + image: "busybox" + cmd: + - sleep + - "1000" + traits: + - name: scaler + properties: + replicas: 10 ` - o := map[string]interface{}{} + o := v1alpha2.Application{} yaml.Unmarshal([]byte(appfileYaml), &o) - appfile, err := NewParser(template.GetHanler(mock)).Parse("test", o) + appfile, err := NewParser(template.GetHanler(mock)).Parse("test", &o) if err != nil { t.Error(err) return } - if !equal(TestExceptApp, appfile) { t.Error("parser appfile wrong") } diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go b/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go index 2dd248f16..77cebc1f1 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go @@ -112,8 +112,15 @@ var _ = Describe("Test Application Controller", func() { Name: "application-sample", Namespace: ns.Name, }, - Spec: runtime.RawExtension{ - Raw: []byte("{\"services\":{\"myweb\":{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"scaler\":{\"replicas\":10},\"type\":\"worker\"}}}")}, + Spec: v1alpha2.ApplicationSpec{ + Components: []v1alpha2.ApplicationComponent{ + { + Name: "myweb", + WorkloadType: "worker", + Settings: runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")}, + }, + }, + }, } wd := &v1alpha2.WorkloadDefinition{} diff --git a/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler.go b/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler.go index f5eb5e803..8dab58fa5 100644 --- a/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler.go +++ b/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler.go @@ -2,7 +2,6 @@ package application import ( "context" - "encoding/json" "net/http" admissionv1beta1 "k8s.io/api/admission/v1beta1" @@ -61,11 +60,7 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a // try render to validate appParser := parser.NewParser(template.GetHanler(fclient.NewDefinitionClient(h.Client))) - expr := map[string]interface{}{} - if err := json.Unmarshal(app.Spec.Raw, &expr); err != nil { - return admission.Denied(err.Error()) - } - if _, err := appParser.Parse(app.Name, expr); err != nil { + if _, err := appParser.Parse(app.Name, app); err != nil { return admission.Denied(err.Error()) } return admission.ValidationResponse(true, "") diff --git a/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler_test.go b/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler_test.go index 4183c5f63..05d128b6d 100644 --- a/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler_test.go +++ b/pkg/webhook/core.oam.dev/v1alpha2/application/validating_handler_test.go @@ -38,28 +38,13 @@ var _ = Describe("Test Application Validater", func() { Operation: admissionv1beta1.Create, Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha2", Resource: "applications"}, Object: runtime.RawExtension{ - Raw: []byte(`{ - "apiVersion": "core.oam.dev/v1alpha2", - "kind": "Application", - "metadata": { - "name": "application-sample" - }, - "spec": { - "services": { - "myweb": { - "type": "worker", - "image": "busybox", - "cmd": [ - "sleep", - "1000" - ], - "scaler": { - "replicas": 10 - } - } - } - } -}`), + Raw: []byte(` +{"apiVersion":"core.oam.dev/v1alpha2", +"kind":"Application", +"metadata":{"name":"application-sample"}, +"spec":{"components":[{"name":"myweb","settings":{"cmd":["sleep","1000"],"image":"busybox"}, +"traits":[{"name":"scaler","properties":{"replicas":10}}],"type":"worker"}]}} +`), }, }, } @@ -72,28 +57,11 @@ var _ = Describe("Test Application Validater", func() { Operation: admissionv1beta1.Create, Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha2", Resource: "applications"}, Object: runtime.RawExtension{ - Raw: []byte(`{ - "apiVersion": "core.oam.dev/v1alpha2", - "kind": "Application", - "metadata": { - "name": "application-sample" - }, - "spec": { - "services": { - "myweb": { - "type": "worker1", - "image": "busybox", - "cmd": [ - "sleep", - "1000" - ], - "scaler": { - "replicas": 10 - } - } - } - } -}`), + Raw: []byte(`{"apiVersion":"core.oam.dev/v1alpha2", +"kind":"Application", +"metadata":{"name":"application-sample"}, +"spec":{"components":[{"name":"myweb","settings":{"cmd":["sleep","1000"],"image":"busybox"}, +"traits":[{"name":"scaler","properties":{"replicas":10}}],"type":"worker1"}]}}`), }, }, } diff --git a/test/e2e-test/component_version_test.go b/test/e2e-test/component_version_test.go index ba80ce695..1b97011c7 100644 --- a/test/e2e-test/component_version_test.go +++ b/test/e2e-test/component_version_test.go @@ -7,24 +7,21 @@ import ( "io/ioutil" "time" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/ghodss/yaml" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/oam-dev/kubevela/pkg/oam/util" - - "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" - + "github.com/ghodss/yaml" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" + "github.com/oam-dev/kubevela/pkg/oam/util" ) var _ = Describe("Versioning mechanism of components", func() { diff --git a/test/e2e-test/health_scope_test.go b/test/e2e-test/health_scope_test.go index 919c8f3a8..bbebebdc7 100644 --- a/test/e2e-test/health_scope_test.go +++ b/test/e2e-test/health_scope_test.go @@ -181,6 +181,14 @@ var _ = Describe("HealthScope", func() { } logf.Log.Info("Creating component", "Name", comp.Name, "Namespace", comp.Namespace) Expect(k8sClient.Create(ctx, &comp)).Should(BeNil()) + + By("check component successfully created") + Eventually( + func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: componentName, Namespace: comp.Namespace}, &comp) + }, + time.Second*5, time.Millisecond*100).Should(BeNil()) + // Create application configuration workloadInstanceName1 := "example-appconfig-healthscope-a" workloadInstanceName2 := "example-appconfig-healthscope-b"