diff --git a/pkg/appfile/appfile.go b/pkg/appfile/appfile.go index eec3d7da4..ed169d76b 100644 --- a/pkg/appfile/appfile.go +++ b/pkg/appfile/appfile.go @@ -108,13 +108,7 @@ func LoadFromFile(filename string) (*AppFile, error) { return af, nil } -// BuildOAM renders Appfile into AppConfig, Components. It also builds images for services if defined. -func (app *AppFile) BuildOAM(ns string, io cmdutil.IOStreams, tm template.Manager, slience bool) ( - []*v1alpha2.Component, *v1alpha2.ApplicationConfiguration, []oam.Object, error) { - return app.buildOAM(ns, io, tm, slience) -} - -// BuildOAMApplication generate v1alpha2.Application and scopes +// BuildOAMApplication renders Appfile into Application, Scopes and other K8s Resources. func (app *AppFile) BuildOAMApplication(env *types.EnvMeta, io cmdutil.IOStreams, tm template.Manager, silence bool) (*v1alpha2.Application, []oam.Object, error) { // assistantObjects currently include OAM Scope Custom Resources and ConfigMaps var assistantObjects []oam.Object @@ -153,54 +147,6 @@ func (app *AppFile) BuildOAMApplication(env *types.EnvMeta, io cmdutil.IOStreams return servApp, assistantObjects, nil } -func (app *AppFile) buildOAM(ns string, io cmdutil.IOStreams, tm template.Manager, silence bool) ( - []*v1alpha2.Component, *v1alpha2.ApplicationConfiguration, []oam.Object, error) { - - appConfig := &v1alpha2.ApplicationConfiguration{ - ObjectMeta: metav1.ObjectMeta{ - Name: app.Name, - Namespace: ns, - }, - } - - var comps []*v1alpha2.Component - - for sname, svc := range app.GetServices() { - - // Image 这块已经用 task 的模式替代了==== 以下 == - var image string - v, ok := svc["image"] - if ok { - image = v.(string) - } - - if b := svc.GetBuild(); b != nil { - if image == "" { - return nil, nil, nil, ErrImageNotDefined - } - io.Infof("\nBuilding service (%s)...\n", sname) - if err := b.BuildImage(io, image); err != nil { - return nil, nil, nil, err - } - } - // Image 这块已经用 task 的模式替代了==== 以上 == - - if !silence { - io.Infof("\nRendering configs for service (%s)...\n", sname) - } - acComp, comp, err := svc.RenderService(tm, sname, ns, app.configGetter) - if err != nil { - return nil, nil, nil, err - } - appConfig.Spec.Components = append(appConfig.Spec.Components, *acComp) - comps = append(comps, comp) - } - - addWorkloadTypeLabel(comps, app.Services) - health := addHealthScope(appConfig) - return comps, appConfig, []oam.Object{health}, nil -} - func addWorkloadTypeLabel(comps []*v1alpha2.Component, services map[string]Service) { for _, comp := range comps { workloadType := services[comp.Name].GetType() @@ -226,35 +172,13 @@ func addDefaultHealthScopeToApplication(app *v1alpha2.Application) *v1alpha2.Hea health.Namespace = app.Namespace health.Spec.WorkloadReferences = make([]v1alpha1.TypedReference, 0) for i := range app.Spec.Components { + // FIXME(wonderflow): the hardcode health scope should be fixed. data, _ := json.Marshal(map[string]string{"healthscopes.core.oam.dev": health.Name}) app.Spec.Components[i].Scopes = runtime.RawExtension{Raw: data} } return health } -func addHealthScope(appConfig *v1alpha2.ApplicationConfiguration) *v1alpha2.HealthScope { - health := &v1alpha2.HealthScope{ - TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha2.HealthScopeGroupVersionKind.GroupVersion().String(), - Kind: v1alpha2.HealthScopeKind, - }, - } - health.Name = FormatDefaultHealthScopeName(appConfig.Name) - health.Namespace = appConfig.Namespace - health.Spec.WorkloadReferences = make([]v1alpha1.TypedReference, 0) - for i := range appConfig.Spec.Components { - // TODO(wonderflow): Temporarily we add health scope here, should change to use scope framework - appConfig.Spec.Components[i].Scopes = append(appConfig.Spec.Components[i].Scopes, v1alpha2.ComponentScope{ - ScopeReference: v1alpha1.TypedReference{ - APIVersion: v1alpha2.SchemeGroupVersion.String(), - Kind: v1alpha2.HealthScopeKind, - Name: health.Name, - }, - }) - } - return health -} - // FormatDefaultHealthScopeName will create a default health scope name. func FormatDefaultHealthScopeName(appName string) string { return appName + "-default-health" diff --git a/pkg/appfile/appfile_test.go b/pkg/appfile/appfile_test.go index 3e3c4cf48..d75ff71a9 100644 --- a/pkg/appfile/appfile_test.go +++ b/pkg/appfile/appfile_test.go @@ -4,8 +4,6 @@ import ( "os" "testing" - "github.com/oam-dev/kubevela/pkg/appfile/config" - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" @@ -16,6 +14,7 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/apis/types" + "github.com/oam-dev/kubevela/pkg/appfile/config" "github.com/oam-dev/kubevela/pkg/appfile/template" cmdutil "github.com/oam-dev/kubevela/pkg/commands/util" ) @@ -407,7 +406,7 @@ outputs: ingress: { } } - comps, ac, _, err := app.BuildOAM("default", io, tm, false) + comps, ac, _, err := app.BuildOAMApplication("default", io, tm, false) if err != nil { assert.Equal(t, c.want.err, err) return diff --git a/pkg/appfile/service.go b/pkg/appfile/service.go index a3a4ab071..913ca9d87 100644 --- a/pkg/appfile/service.go +++ b/pkg/appfile/service.go @@ -2,21 +2,11 @@ package appfile import ( "encoding/json" - "errors" - "fmt" - "github.com/oam-dev/kubevela/pkg/appfile/config" - - "cuelang.org/go/cue" - cueJson "cuelang.org/go/pkg/encoding/json" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/pkg/appfile/template" - mycue "github.com/oam-dev/kubevela/pkg/cue" - "github.com/oam-dev/kubevela/pkg/oam" ) // Service defines the service spec for AppFile, it will contain all information a service realted including OAM component, traits, source to image, etc... @@ -57,24 +47,7 @@ outerLoop: return config } -// GetBuild will get source to image build info -func (s Service) GetBuild() *Build { - v, ok := s["build"] - if !ok { - return nil - } - b, err := json.Marshal(v) - if err != nil { - panic(err) - } - build := &Build{} - err = json.Unmarshal(b, build) - if err != nil { - panic(err) - } - return build -} - +// RenderServiceToApplicationComponent render all capabilities of a service to CUE values to KubeVela Application. func (s Service) RenderServiceToApplicationComponent(tm template.Manager, serviceName string) (v1alpha2.ApplicationComponent, error) { // sort out configs by workload/trait @@ -126,172 +99,7 @@ func (s Service) RenderServiceToApplicationComponent(tm template.Manager, servic return comp, nil } -// RenderService render all capabilities of a service to CUE values of a Component. -// It outputs a Component which will be marshaled as standalone Component and also returned AppConfig Component section. -func (s Service) RenderService(tm template.Manager, name, ns string, cg config.Store) (*v1alpha2.ApplicationConfigurationComponent, *v1alpha2.Component, error) { - - // sort out configs by workload/trait - workloadKeys := map[string]interface{}{} - traitKeys := map[string]interface{}{} - - wtype := s.GetType() - - for k, v := range s.GetConfig() { - if tm.IsTrait(k) { - traitKeys[k] = v - continue - } - workloadKeys[k] = v - } - - // render component - component := &v1alpha2.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - } - - ctxData := map[string]interface{}{ - "name": name, - } - if cn := s.GetUserConfigName(); cn != "" { - data, err := cg.GetConfigData(cn) - if err != nil { - return nil, nil, err - } - ctxData["config"] = data - } - - // = ======= 以上 Merged ================== - - u, err := evalComponent(tm, wtype, ctxData, intifyValues(workloadKeys)) - if err != nil { - return nil, nil, fmt.Errorf("eval service failed: %w", err) - } - component.Spec.Workload.Object = u - - // render traits - traits := make([]v1alpha2.ComponentTrait, 0) - for traitType, traitData := range traitKeys { - ts, err := evalTraits(tm.LoadTemplate(traitType), ctxData, intifyValues(traitData)) - if err != nil { - return nil, nil, fmt.Errorf("eval traits failed: %w", err) - } - // one capability corresponds to one trait only - if len(ts) == 1 { - ts[0].SetLabels(map[string]string{oam.TraitTypeLabel: traitType}) - } - for _, t := range ts { - traits = append(traits, v1alpha2.ComponentTrait{ - Trait: runtime.RawExtension{ - Object: t, - }}, - ) - } - } - - acComp := &v1alpha2.ApplicationConfigurationComponent{ - ComponentName: component.Name, - Traits: traits, - } - return acComp, component, nil -} - // GetServices will get all services defined in AppFile func (af *AppFile) GetServices() map[string]Service { return af.Services } - -func getValueStruct(raw string, ctxValues, userValues interface{}) (*cue.Struct, error) { - r := &cue.Runtime{} - template, err := r.Compile("", raw+mycue.BaseTemplate) - if err != nil { - return nil, fmt.Errorf("compile CUE template failed: %w", err) - } - // fill values - rootValue := template.Value() - rootValue = rootValue.Fill(ctxValues, "context") - rootValue = rootValue.Fill(intifyValues(userValues), "parameter") - appValue, err := rootValue.Eval().Struct() - if err != nil { - return nil, fmt.Errorf("eval CUE template failed: %w", err) - } - return appValue, nil -} - -func renderOneOutput(appValue *cue.Struct) (*unstructured.Unstructured, error) { - outputField, err := appValue.FieldByName("output", true) - if err != nil { - return nil, fmt.Errorf("FieldByName('output'): %w", err) - } - - final := outputField.Value - data, err := cueJson.Marshal(final) - if err != nil { - return nil, fmt.Errorf("marshal final value failed: %w", err) - } - obj := make(map[string]interface{}) - if err = json.Unmarshal([]byte(data), &obj); err != nil { - return nil, err - } - return &unstructured.Unstructured{ - Object: obj, - }, nil -} - -func renderAllOutputs(field cue.FieldInfo) ([]*unstructured.Unstructured, error) { - iter, err := field.Value.Fields() - if err != nil { - return nil, err - } - us := []*unstructured.Unstructured{} - for iter.Next() { - final := iter.Value() - data, err := cueJson.Marshal(final) - if err != nil { - return nil, fmt.Errorf("marshal final value err %w", err) - } - // need to unmarshal it to a map to get rid of the outer spec name - obj := make(map[string]interface{}) - if err = json.Unmarshal([]byte(data), &obj); err != nil { - return nil, err - } - u := &unstructured.Unstructured{Object: obj} - us = append(us, u) - } - return us, nil -} - -func evalComponent(tm template.Manager, wtype string, ctxValues, userValues interface{}) (*unstructured.Unstructured, error) { - workloadCueTemplate := tm.LoadTemplate(wtype) - if workloadCueTemplate == "" { - return nil, fmt.Errorf("no template found in capability %s", wtype) - } - appValue, err := getValueStruct(workloadCueTemplate, ctxValues, userValues) - if err != nil { - return nil, err - } - return renderOneOutput(appValue) -} - -func evalTraits(raw string, ctxValues, userValues interface{}) ([]*unstructured.Unstructured, error) { - appValue, err := getValueStruct(raw, ctxValues, userValues) - if err != nil { - return nil, err - } - - _, err = appValue.FieldByName("output", true) - if err != nil { - outputField, err := appValue.FieldByName("outputs", true) - if err != nil { - return nil, errors.New("both output and outputs fields not found") - } - return renderAllOutputs(outputField) - } - u, err := renderOneOutput(appValue) - if err != nil { - return nil, err - } - return []*unstructured.Unstructured{u}, nil -} diff --git a/pkg/application/app.go b/pkg/application/app.go index c31d2b11e..ebd7b9b85 100644 --- a/pkg/application/app.go +++ b/pkg/application/app.go @@ -17,7 +17,6 @@ import ( "github.com/oam-dev/kubevela/pkg/appfile/storage/driver" "github.com/oam-dev/kubevela/pkg/appfile/template" cmdutil "github.com/oam-dev/kubevela/pkg/commands/util" - "github.com/oam-dev/kubevela/pkg/oam" ) // NewEmptyApplication new empty application, only set tm @@ -166,16 +165,6 @@ func GetTraitsByType(app *driver.Application, componentName, traitType string) ( return t.(map[string]interface{}), nil } -// OAM will convert an AppFile to OAM objects -// TODO(wonderflow) add scope support here -func OAM(app *driver.Application, env *types.EnvMeta, io cmdutil.IOStreams, silence bool) ([]*v1alpha2.Component, *v1alpha2.ApplicationConfiguration, []oam.Object, error) { - comps, appConfig, scopes, err := app.BuildOAM(env.Namespace, io, app.Tm, silence) - if err != nil { - return nil, nil, nil, err - } - return comps, appConfig, scopes, nil -} - // GetAppConfig will get AppConfig from K8s cluster. func GetAppConfig(ctx context.Context, c client.Client, app *driver.Application, env *types.EnvMeta) (*v1alpha2.ApplicationConfiguration, error) { appConfig := &v1alpha2.ApplicationConfiguration{} diff --git a/pkg/application/run.go b/pkg/application/run.go index f144dbc70..94af727bc 100644 --- a/pkg/application/run.go +++ b/pkg/application/run.go @@ -18,12 +18,12 @@ import ( // BuildRun will build application and deploy from Appfile func BuildRun(ctx context.Context, app *driver.Application, client client.Client, env *types.EnvMeta, io cmdutil.IOStreams) error { - nApp, err := app.InitTasks(io) + nApp, err := InitTasks(app, io) if err != nil { return err } - o, scopes, err := nApp.Object(env.Namespace) + o, scopes, err := nApp.BuildOAMApplication(env, io, nApp.Tm, true) if err != nil { return err }