mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-27 16:17:34 +00:00
refactor application object to have more strict schema
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
@@ -95,7 +95,7 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "service the app"
|
||||
name: service
|
||||
name: kservice
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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, "")
|
||||
|
||||
@@ -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"}]}}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user