From bdf239f1aea289cbb969ec2ad6059315d49feb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E5=85=83?= Date: Thu, 11 Mar 2021 15:34:38 +0800 Subject: [PATCH 1/5] delete health scope when delete app --- references/common/application.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/references/common/application.go b/references/common/application.go index e200c79d5..ffc4abf7f 100644 --- a/references/common/application.go +++ b/references/common/application.go @@ -224,7 +224,21 @@ func (o *DeleteOptions) DeleteApp() (string, error) { return "", fmt.Errorf("delete application err: %w", err) } - // TODO(wonderflow): delete the default health scope here + for _, cmp := range app.Spec.Components { + healthScopeName, ok := cmp.Scopes[api.DefaultHealthScopeKey] + if ok { + var healthScope corev1alpha2.HealthScope + if err := o.Client.Get(ctx, client.ObjectKey{Namespace: o.Env.Namespace, Name: healthScopeName}, &healthScope); err != nil { + if apierrors.IsNotFound(err) { + continue + } + return "", fmt.Errorf("delete health scope %s err: %w", healthScopeName, err) + } + if err = o.Client.Delete(ctx, &healthScope); err != nil { + return "", fmt.Errorf("delete health scope %s err: %w", healthScopeName, err) + } + } + } return fmt.Sprintf("app \"%s\" deleted from env \"%s\"", o.AppName, o.Env.Name), nil } From c47dd8c16bdf3435dd1d2c59c6704c1aa5df9cbd Mon Sep 17 00:00:00 2001 From: wangyike Date: Tue, 9 Mar 2021 17:18:44 +0800 Subject: [PATCH 2/5] fix dryrun use namespace --- references/cli/dryrun.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/references/cli/dryrun.go b/references/cli/dryrun.go index 5f50d55a3..a12867280 100644 --- a/references/cli/dryrun.go +++ b/references/cli/dryrun.go @@ -54,7 +54,13 @@ func NewDryRunCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command parser := appfile.NewApplicationParser(newClient, dm) - ctx := oamutil.SetNamespaceInCtx(context.Background(), app.Namespace) + velaEnv, err := GetEnv(cmd) + if err != nil { + return err + } + + ctx := oamutil.SetNamespaceInCtx(context.Background(), velaEnv.Namespace) + appFile, err := parser.GenerateAppFile(ctx, app.Name, app) if err != nil { return errors.WithMessage(err, "generate appFile") From d84ebd3ae9491b6bbf774ce083f5c68d8d486931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E5=85=83?= Date: Thu, 11 Mar 2021 17:03:49 +0800 Subject: [PATCH 3/5] vela ls will list applications --- e2e/commonContext.go | 2 +- references/cli/common.go | 5 +- references/cli/ls.go | 125 ++++++++++++++------------------------- 3 files changed, 49 insertions(+), 83 deletions(-) diff --git a/e2e/commonContext.go b/e2e/commonContext.go index a399ad18e..59c904b88 100644 --- a/e2e/commonContext.go +++ b/e2e/commonContext.go @@ -136,7 +136,7 @@ var ( ginkgo.It("should list all applications", func() { output, err := Exec("vela ls") gomega.Expect(err).NotTo(gomega.HaveOccurred()) - gomega.Expect(output).To(gomega.ContainSubstring("SERVICE")) + gomega.Expect(output).To(gomega.ContainSubstring("COMPONENT")) gomega.Expect(output).To(gomega.ContainSubstring(applicationName)) gomega.Expect(output).To(gomega.ContainSubstring(workloadType)) if traitAlias != "" { diff --git a/references/cli/common.go b/references/cli/common.go index 63245227b..c8b82d785 100644 --- a/references/cli/common.go +++ b/references/cli/common.go @@ -18,6 +18,7 @@ package cli // constants used in `svc` command const ( - App = "app" - Service = "svc" + App = "app" + Service = "svc" + Namespace = "namespace" ) diff --git a/references/cli/ls.go b/references/cli/ls.go index f72d40e45..384b879f6 100644 --- a/references/cli/ls.go +++ b/references/cli/ls.go @@ -4,15 +4,14 @@ import ( "context" "strings" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "github.com/spf13/cobra" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/apis/types" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" - "github.com/oam-dev/kubevela/references/apiserver/apis" - "github.com/oam-dev/kubevela/references/appfile" - "github.com/oam-dev/kubevela/references/common" ) // NewListCommand creates `ls` command and its nested children command @@ -22,8 +21,8 @@ func NewListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { Use: "ls", Aliases: []string{"list"}, DisableFlagsInUseLine: true, - Short: "List services", - Long: "List services of all applications", + Short: "List applications", + Long: "List all applications in cluster", Example: `vela ls`, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return c.SetConfig() @@ -37,94 +36,60 @@ func NewListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { if err != nil { return err } - appName, err := cmd.Flags().GetString(App) + namespace, err := cmd.Flags().GetString(Namespace) if err != nil { return err } - printComponentList(ctx, newClient, appName, env, ioStreams) - return nil + if namespace == "" { + namespace = env.Namespace + } + return printApplicationList(ctx, newClient, namespace, ioStreams) }, Annotations: map[string]string{ types.TagCommandType: types.TypeApp, }, } - cmd.PersistentFlags().StringP(App, "", "", "specify the name of application") + cmd.PersistentFlags().StringP(Namespace, "n", "", "specify the namespace the application want to list, default is the current env namespace") return cmd } -func printComponentList(ctx context.Context, c client.Reader, appName string, env *types.EnvMeta, - ioStreams cmdutil.IOStreams) { - deployedComponentList, err := common.ListComponents(ctx, c, common.Option{ - AppName: appName, - Namespace: env.Namespace, - }) - if err != nil { - ioStreams.Infof("listing services: %s\n", err) - return +func printApplicationList(ctx context.Context, c client.Reader, namespace string, ioStreams cmdutil.IOStreams) error { + table := newUITable() + table.AddRow("APP", "COMPONENT", "TYPE", "TRAITS", "PHASE", "HEALTHY", "STATUS", "CREATED-TIME") + applist := v1alpha2.ApplicationList{} + if err := c.List(ctx, &applist, client.InNamespace(namespace)); err != nil { + if apierrors.IsNotFound(err) { + ioStreams.Info(table.String()) + return nil + } + return err } - fetcher := func(name string) (*v1alpha2.Application, error) { - var app = new(v1alpha2.Application) - err := c.Get(ctx, client.ObjectKey{Name: name, Namespace: env.Namespace}, app) - return app, err - } - all := mergeStagingComponents(deployedComponentList, env, ioStreams, fetcher) - table := newUITable() - table.AddRow("SERVICE", "APP", "TYPE", "TRAITS", "STATUS", "CREATED-TIME") - for _, a := range all { - traitAlias := strings.Join(a.TraitNames, ",") - table.AddRow(a.Name, a.App, a.WorkloadName, traitAlias, a.Status, a.CreatedTime) + for _, a := range applist.Items { + for idx, cmp := range a.Spec.Components { + var appName = a.Name + if idx > 0 { + appName = "├─" + if idx == len(a.Spec.Components)-1 { + appName = "└─" + } + } + var healthy, status string + if len(a.Status.Services) > idx { + if a.Status.Services[idx].Healthy { + healthy = "healthy" + } else { + healthy = "unhealthy" + } + status = a.Status.Services[idx].Message + } + var traits []string + for _, tr := range cmp.Traits { + traits = append(traits, tr.Name) + } + table.AddRow(appName, cmp.Name, cmp.WorkloadType, strings.Join(traits, ","), a.Status.Phase, healthy, status, a.CreationTimestamp) + } } ioStreams.Info(table.String()) -} - -func mergeStagingComponents(deployed []apis.ComponentMeta, env *types.EnvMeta, ioStreams cmdutil.IOStreams, fetcher func(name string) (*v1alpha2.Application, error)) []apis.ComponentMeta { - localApps, err := appfile.List(env.Name) - if err != nil { - ioStreams.Error("list application err", err) - return deployed - } - var all []apis.ComponentMeta - for _, app := range localApps { - appl, err := fetcher(app.Name) - if err != nil { - ioStreams.Errorf("fetch app %s err %v\n", app.Name, err) - continue - } - for _, c := range appl.Spec.Components { - traits := []string{} - for _, t := range c.Traits { - traits = append(traits, t.Name) - } - compMeta, exist := GetCompMeta(deployed, app.Name, c.Name) - if !exist { - all = append(all, apis.ComponentMeta{ - Name: c.Name, - App: app.Name, - WorkloadName: c.WorkloadType, - TraitNames: traits, - Status: types.StatusStaging, - CreatedTime: app.CreateTime.String(), - }) - continue - } - compMeta.TraitNames = traits - compMeta.WorkloadName = c.WorkloadType - if appl.Status.Phase != v1alpha2.ApplicationRunning { - compMeta.Status = types.StatusStaging - } - all = append(all, compMeta) - } - } - return all -} - -// GetCompMeta gets meta of a component -func GetCompMeta(deployed []apis.ComponentMeta, appName, compName string) (apis.ComponentMeta, bool) { - for _, v := range deployed { - if v.Name == compName && v.App == appName { - return v, true - } - } - return apis.ComponentMeta{}, false + return nil } From 9d5b31be2bb1718b99105bb15cd9c2963563f5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E5=85=83?= Date: Thu, 11 Mar 2021 22:03:04 +0800 Subject: [PATCH 4/5] remove local files, diretly use Definition/Application in Cluster --- apis/types/args.go | 20 ++ apis/types/capability.go | 4 +- .../application/application_controller.go | 1 - pkg/cue/convert.go | 10 +- pkg/cue/convert_test.go | 14 +- pkg/utils/helm/helm.go | 6 +- references/apiserver/api-server.go | 4 +- references/apiserver/appHandlers.go | 2 +- references/apiserver/capabilityHandlers.go | 5 +- references/apiserver/traitHandler.go | 97 +----- references/apiserver/workloadHandler.go | 2 +- references/appfile/addon.go | 6 +- references/appfile/api/driver.go | 4 - references/appfile/app.go | 99 ++---- references/appfile/app_test.go | 17 -- references/appfile/driver/local.go | 66 ---- references/appfile/driver/local_test.go | 86 ------ references/appfile/modify.go | 59 ++-- references/appfile/storage.go | 10 - references/appfile/template/manager.go | 4 +- references/cli/capability.go | 21 +- references/cli/dashboard.go | 2 +- references/cli/delete.go | 7 +- references/cli/dryrun.go | 3 +- references/cli/env.go | 6 +- references/cli/exec.go | 13 +- references/cli/exec_test.go | 43 --- references/cli/export.go | 8 +- references/cli/init.go | 49 +-- references/cli/logs.go | 6 +- references/cli/ls.go | 2 +- references/cli/portforward.go | 9 +- references/cli/refresh.go | 285 ------------------ references/cli/refresh_test.go | 49 --- references/cli/status.go | 40 +-- references/cli/system.go | 7 +- references/cli/testdata/appfile.yaml | 18 ++ references/cli/traits.go | 15 +- references/cli/traits_test.go | 80 ----- references/cli/up.go | 5 +- references/cli/workloads.go | 12 +- references/common/application.go | 42 +-- references/common/capability.go | 28 +- references/common/trait.go | 132 +------- references/common/workload.go | 42 +-- references/plugins/capcenter.go | 8 +- references/plugins/cluster.go | 46 ++- references/plugins/cluster_test.go | 9 +- references/plugins/local.go | 50 ++- references/plugins/references.go | 6 +- 50 files changed, 328 insertions(+), 1231 deletions(-) delete mode 100644 references/cli/refresh.go delete mode 100644 references/cli/refresh_test.go create mode 100644 references/cli/testdata/appfile.yaml diff --git a/apis/types/args.go b/apis/types/args.go index 67df32b17..f4b4c3f08 100644 --- a/apis/types/args.go +++ b/apis/types/args.go @@ -3,6 +3,7 @@ package types import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" ) @@ -10,6 +11,7 @@ import ( type Args struct { Config *rest.Config Schema *runtime.Scheme + Client client.Client } // SetConfig insert kubeconfig into Args @@ -21,3 +23,21 @@ func (a *Args) SetConfig() error { a.Config = restConf return nil } + +// GetClient get client if exist +func (a *Args) GetClient() (client.Client, error) { + if a.Config == nil { + if err := a.SetConfig(); err != nil { + return nil, err + } + } + if a.Client != nil { + return a.Client, nil + } + newClient, err := client.New(a.Config, client.Options{Scheme: a.Schema}) + if err != nil { + return nil, err + } + a.Client = newClient + return a.Client, nil +} diff --git a/apis/types/capability.go b/apis/types/capability.go index e8a447eeb..48797059b 100644 --- a/apis/types/capability.go +++ b/apis/types/capability.go @@ -43,7 +43,6 @@ type Capability struct { CueTemplate string `json:"template,omitempty"` CueTemplateURI string `json:"templateURI,omitempty"` Parameters []Parameter `json:"parameters,omitempty"` - DefinitionPath string `json:"definition"` CrdName string `json:"crdName,omitempty"` Center string `json:"center,omitempty"` Status string `json:"status,omitempty"` @@ -52,6 +51,9 @@ type Capability struct { // trait only AppliesTo []string `json:"appliesTo,omitempty"` + // Namespace represents it's a system-level or user-level capability. + Namespace string `json:"namespace,omitempty"` + // Plugin Source Source *Source `json:"source,omitempty"` Install *Installation `json:"install,omitempty"` 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 a4fe31356..beb4ba74c 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go @@ -83,7 +83,6 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { appParser := appfile.NewApplicationParser(r.Client, r.dm) ctx = oamutil.SetNamespaceInCtx(ctx, app.Namespace) - appfile, err := appParser.GenerateAppFile(ctx, app.Name, app) if err != nil { applog.Error(err, "[Handle Parse]") diff --git a/pkg/cue/convert.go b/pkg/cue/convert.go index 777e9cafc..cd199423e 100644 --- a/pkg/cue/convert.go +++ b/pkg/cue/convert.go @@ -3,8 +3,6 @@ package cue import ( "errors" "fmt" - "io/ioutil" - "path/filepath" "strings" "cuelang.org/go/cue" @@ -16,13 +14,9 @@ import ( const specValue = "parameter" // GetParameters get parameter from cue template -func GetParameters(templatePath string) ([]types.Parameter, error) { +func GetParameters(templateStr string) ([]types.Parameter, error) { r := cue.Runtime{} - b, err := ioutil.ReadFile(filepath.Clean(templatePath)) - if err != nil { - return nil, err - } - template, err := r.Compile("", string(b)+BaseTemplate) + template, err := r.Compile("", templateStr+BaseTemplate) if err != nil { return nil, err } diff --git a/pkg/cue/convert_test.go b/pkg/cue/convert_test.go index cfc20bb79..4e5ec0e6c 100644 --- a/pkg/cue/convert_test.go +++ b/pkg/cue/convert_test.go @@ -1,6 +1,7 @@ package cue import ( + "io/ioutil" "testing" "cuelang.org/go/cue" @@ -10,7 +11,8 @@ import ( ) func TestGetParameter(t *testing.T) { - params, err := GetParameters("testdata/workloads/metrics.cue") + data, _ := ioutil.ReadFile("testdata/workloads/metrics.cue") + params, err := GetParameters(string(data)) assert.NoError(t, err) assert.Equal(t, params, []types.Parameter{ {Name: "format", Required: false, Default: "prometheus", Usage: "format of the metrics, " + @@ -19,8 +21,8 @@ func TestGetParameter(t *testing.T) { {Name: "port", Required: false, Default: int64(8080), Type: cue.IntKind}, {Name: "selector", Required: false, Usage: "the label selector for the pods, default is the workload labels", Type: cue.StructKind}, }) - - params, err = GetParameters("testdata/workloads/deployment.cue") + data, _ = ioutil.ReadFile("testdata/workloads/deployment.cue") + params, err = GetParameters(string(data)) assert.NoError(t, err) assert.Equal(t, []types.Parameter{ {Name: "name", Required: true, Default: "", Type: cue.StringKind}, @@ -31,7 +33,8 @@ func TestGetParameter(t *testing.T) { {Name: "cpu", Short: "", Required: false, Usage: "", Default: "", Type: cue.StringKind}}, params) - params, err = GetParameters("testdata/workloads/test-param.cue") + data, _ = ioutil.ReadFile("testdata/workloads/test-param.cue") + params, err = GetParameters(string(data)) assert.NoError(t, err) assert.Equal(t, []types.Parameter{ {Name: "name", Required: true, Default: "", Type: cue.StringKind}, @@ -41,7 +44,8 @@ func TestGetParameter(t *testing.T) { {Name: "enable", Default: false, Type: cue.BoolKind}, {Name: "fval", Default: 64.3, Type: cue.FloatKind}, {Name: "nval", Default: float64(0), Required: true, Type: cue.NumberKind}}, params) - params, err = GetParameters("testdata/workloads/empty.cue") + data, _ = ioutil.ReadFile("testdata/workloads/empty.cue") + params, err = GetParameters(string(data)) assert.NoError(t, err) var exp []types.Parameter assert.Equal(t, exp, params) diff --git a/pkg/utils/helm/helm.go b/pkg/utils/helm/helm.go index c5f2b019a..e883522c9 100644 --- a/pkg/utils/helm/helm.go +++ b/pkg/utils/helm/helm.go @@ -7,8 +7,6 @@ import ( "os" "strings" - "github.com/oam-dev/kubevela/pkg/utils/common" - "github.com/pkg/errors" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" @@ -17,9 +15,9 @@ import ( "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/types" + "github.com/oam-dev/kubevela/pkg/utils/common" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" ) @@ -39,7 +37,7 @@ func Install(ioStreams cmdutil.IOStreams, repoName, repoURL, chartName, version, if err != nil { return err } - kubeClient, err := client.New(args.Config, client.Options{Scheme: args.Schema}) + kubeClient, err := args.GetClient() if err != nil { return err } diff --git a/references/apiserver/api-server.go b/references/apiserver/api-server.go index c6f10fa58..ae1bef1e1 100644 --- a/references/apiserver/api-server.go +++ b/references/apiserver/api-server.go @@ -18,11 +18,12 @@ type APIServer struct { server *http.Server KubeClient client.Client dm discoverymapper.DiscoveryMapper + c types.Args } // New will create APIServer func New(c types.Args, port, staticPath string) (*APIServer, error) { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return nil, err } @@ -33,6 +34,7 @@ func New(c types.Args, port, staticPath string) (*APIServer, error) { s := &APIServer{ KubeClient: newClient, dm: dm, + c: c, } server := &http.Server{ Addr: port, diff --git a/references/apiserver/appHandlers.go b/references/apiserver/appHandlers.go index 466502465..5fd7da4c8 100644 --- a/references/apiserver/appHandlers.go +++ b/references/apiserver/appHandlers.go @@ -107,7 +107,7 @@ func (s *APIServer) CreateApplication(c *gin.Context) { IO: ioStream, Env: env, } - buildResult, data, err := o.ExportFromAppFile(&body, false) + buildResult, data, err := o.ExportFromAppFile(&body, env.Namespace, false, s.c) if err != nil { util.HandleError(c, util.StatusInternalServerError, err.Error()) return diff --git a/references/apiserver/capabilityHandlers.go b/references/apiserver/capabilityHandlers.go index 62b7a260e..036e8c0dd 100644 --- a/references/apiserver/capabilityHandlers.go +++ b/references/apiserver/capabilityHandlers.go @@ -63,7 +63,8 @@ func (s *APIServer) DeleteCapabilityCenter(c *gin.Context) { // RemoveCapabilityFromCluster remove a specific capability from cluster func (s *APIServer) RemoveCapabilityFromCluster(c *gin.Context) { capabilityCenterName := c.Param("capabilityName") - msg, err := common.RemoveCapabilityFromCluster(s.KubeClient, capabilityCenterName) + // TODO get namespace from env + msg, err := common.RemoveCapabilityFromCluster("default", s.c, s.KubeClient, capabilityCenterName) if err != nil { util.HandleError(c, util.StatusInternalServerError, err.Error()) return @@ -74,7 +75,7 @@ func (s *APIServer) RemoveCapabilityFromCluster(c *gin.Context) { // ListCapabilities lists capabilities of a capability center func (s *APIServer) ListCapabilities(c *gin.Context) { capabilityCenterName := c.Param("capabilityName") - capabilityList, err := common.ListCapabilities(capabilityCenterName) + capabilityList, err := common.ListCapabilities("default", s.c, capabilityCenterName) if err != nil { util.HandleError(c, util.StatusInternalServerError, err.Error()) return diff --git a/references/apiserver/traitHandler.go b/references/apiserver/traitHandler.go index b52450276..3b6da13c1 100644 --- a/references/apiserver/traitHandler.go +++ b/references/apiserver/traitHandler.go @@ -1,22 +1,12 @@ package apiserver import ( - "context" - "os" - "strconv" - "github.com/gin-gonic/gin" - "github.com/spf13/pflag" - ctrl "sigs.k8s.io/controller-runtime" "github.com/oam-dev/kubevela/apis/types" - env2 "github.com/oam-dev/kubevela/pkg/utils/env" - util2 "github.com/oam-dev/kubevela/pkg/utils/util" "github.com/oam-dev/kubevela/references/apiserver/apis" "github.com/oam-dev/kubevela/references/apiserver/util" - "github.com/oam-dev/kubevela/references/appfile/api" "github.com/oam-dev/kubevela/references/common" - "github.com/oam-dev/kubevela/references/plugins" ) // AttachTrait attaches a trait to a component @@ -30,13 +20,8 @@ func (s *APIServer) AttachTrait(c *gin.Context) { util.HandleError(c, util.InvalidArgument, "the trait attach request body is invalid") return } - ctrl.Log.Info("request parameters body:", "body", body) - msg, err := s.DoAttachTrait(c, body) - if err != nil { - util.HandleError(c, util.StatusInternalServerError, err.Error()) - return - } - util.AssembleResponse(c, msg, nil) + + util.AssembleResponse(c, "deprecated, please use appfile to update", nil) } // GetTrait gets a trait by name @@ -46,7 +31,7 @@ func (s *APIServer) GetTrait(c *gin.Context) { var capability types.Capability var err error - if capability, err = common.GetTraitDefinition(&workloadType, traitType); err != nil { + if capability, err = common.GetTraitDefinition("default", s.c, &workloadType, traitType); err != nil { util.HandleError(c, util.StatusInternalServerError, err) return } @@ -58,7 +43,7 @@ func (s *APIServer) ListTrait(c *gin.Context) { var traitList []types.Capability var workloadName string var err error - if traitList, err = common.ListTraitDefinitions(&workloadName); err != nil { + if traitList, err = common.ListTraitDefinitions("default", s.c, &workloadName); err != nil { util.HandleError(c, util.StatusInternalServerError, err) return } @@ -67,77 +52,5 @@ func (s *APIServer) ListTrait(c *gin.Context) { // DetachTrait detaches a trait from a component func (s *APIServer) DetachTrait(c *gin.Context) { - envName := c.Param("envName") - traitType := c.Param("traitName") - componentName := c.Param("compName") - applicationName := c.Param("appName") - - var staging = false - var err error - if stagingStr := c.Param("staging"); stagingStr != "" { - if staging, err = strconv.ParseBool(stagingStr); err != nil { - util.HandleError(c, util.StatusInternalServerError, err.Error()) - return - } - } - msg, err := s.DoDetachTrait(c, envName, traitType, componentName, applicationName, staging) - if err != nil { - util.HandleError(c, util.StatusInternalServerError, err.Error()) - return - } - util.AssembleResponse(c, msg, nil) -} - -// DoAttachTrait executes attaching trait operation -func (s *APIServer) DoAttachTrait(c context.Context, body apis.TraitBody) (string, error) { - // Prepare - var appObj *api.Application - fs := pflag.NewFlagSet("trait", pflag.ContinueOnError) - for _, f := range body.Flags { - fs.String(f.Name, f.Value, "") - } - var staging = false - var err error - if body.Staging != "" { - staging, err = strconv.ParseBool(body.Staging) - if err != nil { - return "", err - } - } - traitAlias := body.Name - template, err := plugins.GetInstalledCapabilityWithCapName(types.TypeTrait, traitAlias) - if err != nil { - return "", err - } - // Run step - env, err := env2.GetEnvByName(body.EnvName) - if err != nil { - return "", err - } - - appObj, err = common.AddOrUpdateTrait(env, body.AppName, body.ComponentName, fs, template) - if err != nil { - return "", err - } - io := util2.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} - return common.TraitOperationRun(c, s.KubeClient, env, appObj, staging, io) -} - -// DoDetachTrait executes detaching trait operation -func (s *APIServer) DoDetachTrait(c context.Context, envName string, traitType string, componentName string, appName string, staging bool) (string, error) { - var appObj *api.Application - var err error - if appName == "" { - appName = componentName - } - if appObj, err = common.PrepareDetachTrait(envName, traitType, componentName, appName); err != nil { - return "", err - } - // Run - env, err := env2.GetEnvByName(envName) - if err != nil { - return "", err - } - io := util2.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} - return common.TraitOperationRun(c, s.KubeClient, env, appObj, staging, io) + util.AssembleResponse(c, "deprecated, please use appfile to update", nil) } diff --git a/references/apiserver/workloadHandler.go b/references/apiserver/workloadHandler.go index 0b7ae8d14..b24e55ec7 100644 --- a/references/apiserver/workloadHandler.go +++ b/references/apiserver/workloadHandler.go @@ -29,7 +29,7 @@ func (s *APIServer) GetWorkload(c *gin.Context) { // ListWorkload lists all workloads in the cluster func (s *APIServer) ListWorkload(c *gin.Context) { var workloadDefinitionList []apis.WorkloadMeta - workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) + workloads, err := plugins.LoadInstalledCapabilityWithType("default", s.c, types.TypeWorkload) if err != nil { util.HandleError(c, util.StatusInternalServerError, err) return diff --git a/references/appfile/addon.go b/references/appfile/addon.go index ab5732d29..635aa5f0d 100644 --- a/references/appfile/addon.go +++ b/references/appfile/addon.go @@ -19,6 +19,7 @@ import ( "github.com/oam-dev/kubevela/pkg/appfile" "github.com/oam-dev/kubevela/pkg/controller/utils" "github.com/oam-dev/kubevela/pkg/oam/discoverymapper" + util2 "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/pkg/utils/common" "github.com/oam-dev/kubevela/pkg/utils/util" ) @@ -36,8 +37,9 @@ func ApplyTerraform(app *v1alpha2.Application, k8sClient client.Client, ioStream var nativeVelaComponents []v1alpha2.ApplicationComponent // parse template appParser := appfile.NewApplicationParser(k8sClient, dm) - // TODO(wangyike) this context only for compiling success, lately mabey surport setting sysNs and appNs in api-server or cli - appFile, err := appParser.GenerateAppFile(context.TODO(), app.Name, app) + + ctx := util2.SetNamespaceInCtx(context.Background(), namespace) + appFile, err := appParser.GenerateAppFile(ctx, app.Name, app) if err != nil { return nil, fmt.Errorf("failed to parse appfile: %w", err) } diff --git a/references/appfile/api/driver.go b/references/appfile/api/driver.go index d2cf8c2e2..73059bdfe 100644 --- a/references/appfile/api/driver.go +++ b/references/appfile/api/driver.go @@ -2,14 +2,10 @@ package api // Driver is mutli implement interface type Driver interface { - // List applications - List(envName string) ([]*Application, error) // Save application Save(app *Application, envName string) error // Delete application Delete(envName, appName string) error - // Get application - Get(envName, appName string) (*Application, error) // Name of storage driver Name() string } diff --git a/references/appfile/app.go b/references/appfile/app.go index 1077c82ed..32c9c0e7a 100644 --- a/references/appfile/app.go +++ b/references/appfile/app.go @@ -2,6 +2,7 @@ package appfile import ( "context" + "encoding/json" "errors" "fmt" "reflect" @@ -16,8 +17,8 @@ import ( ) // NewEmptyApplication new empty application, only set tm -func NewEmptyApplication() (*api.Application, error) { - tm, err := template.Load() +func NewEmptyApplication(namespace string, c types.Args) (*api.Application, error) { + tm, err := template.Load(namespace, c) if err != nil { return nil, err } @@ -52,19 +53,17 @@ func Validate(app *api.Application) error { return nil } -// IsNotFound is application not found error -func IsNotFound(appName string, err error) bool { - return err != nil && err.Error() == fmt.Sprintf(`application "%s" not found`, appName) -} - -// LoadApplication will load application with env and name from default vela home dir. -func LoadApplication(envName, appName string) (*api.Application, error) { - app, err := GetStorage().Get(envName, appName) +// LoadApplication will load application from cluster. +func LoadApplication(namespace, appName string, c types.Args) (*v1alpha2.Application, error) { + newClient, err := c.GetClient() if err != nil { return nil, err } - err = Validate(app) - return app, err + app := &v1alpha2.Application{} + if err := newClient.Get(context.TODO(), client.ObjectKey{Namespace: namespace, Name: appName}, app); err != nil { + return nil, err + } + return app, nil } // Delete will delete an app along with it's appfile. @@ -72,50 +71,16 @@ func Delete(envName, appName string) error { return GetStorage().Delete(envName, appName) } -// List will list all apps -func List(envName string) ([]*api.Application, error) { - respApps, err := GetStorage().List(envName) - if err != nil { - return nil, err - } - var apps []*api.Application - for _, resp := range respApps { - app := NewApplication(resp.AppFile, resp.Tm) - err := Validate(app) - if err != nil { - return nil, err - } - apps = append(apps, app) - } - return apps, nil -} - -// MatchAppByComp will get application with componentName without AppName. -func MatchAppByComp(envName, compName string) (*api.Application, error) { - apps, err := List(envName) - if err != nil { - return nil, err - } - for _, subapp := range apps { - for _, v := range GetComponents(subapp) { - if v == compName { - return subapp, nil - } - } - } - return nil, fmt.Errorf("no app found contains %s in env %s", compName, envName) -} - // Save will save appfile into default dir. func Save(app *api.Application, envName string) error { return GetStorage().Save(app, envName) } // GetComponents will get oam components from Appfile. -func GetComponents(app *api.Application) []string { +func GetComponents(app *v1alpha2.Application) []string { var components []string - for name := range app.Services { - components = append(components, name) + for _, cmp := range app.Spec.Components { + components = append(components, cmp.Name) } sort.Strings(components) return components @@ -130,6 +95,18 @@ func GetServiceConfig(app *api.Application, componentName string) (string, map[s return svc.GetType(), svc.GetApplicationConfig() } +// GetApplicationSettings will get service type and it's configuration +func GetApplicationSettings(app *v1alpha2.Application, componentName string) (string, map[string]interface{}) { + for _, comp := range app.Spec.Components { + if comp.Name == componentName { + data := map[string]interface{}{} + _ = json.Unmarshal(comp.Settings.Raw, &data) + return comp.WorkloadType, data + } + } + return "", make(map[string]interface{}) +} + // GetWorkload will get workload type and it's configuration func GetWorkload(app *api.Application, componentName string) (string, map[string]interface{}) { svcType, config := GetServiceConfig(app, componentName) @@ -163,33 +140,11 @@ func GetTraits(app *api.Application, componentName string) (map[string]map[strin return traitsData, nil } -// GetTraitsByType will get trait configuration with specified component and trait type, we assume one type of trait can only attach to a component once. -func GetTraitsByType(app *api.Application, componentName, traitType string) (map[string]interface{}, error) { - service, ok := app.Services[componentName] - if !ok { - return nil, fmt.Errorf("service name (%s) doesn't exist", componentName) - } - t, ok := service[traitType] - if !ok { - return make(map[string]interface{}), nil - } - return t.(map[string]interface{}), nil -} - // GetAppConfig will get AppConfig from K8s cluster. -func GetAppConfig(ctx context.Context, c client.Client, app *api.Application, env *types.EnvMeta) (*v1alpha2.ApplicationConfiguration, error) { +func GetAppConfig(ctx context.Context, c client.Client, app *v1alpha2.Application, env *types.EnvMeta) (*v1alpha2.ApplicationConfiguration, error) { appConfig := &v1alpha2.ApplicationConfiguration{} if err := c.Get(ctx, client.ObjectKey{Namespace: env.Namespace, Name: app.Name}, appConfig); err != nil { return nil, err } return appConfig, nil } - -// GetApplication will get Application from K8s cluster. -func GetApplication(ctx context.Context, c client.Client, app *api.Application, env *types.EnvMeta) (*v1alpha2.Application, error) { - appl := &v1alpha2.Application{} - if err := c.Get(ctx, client.ObjectKey{Namespace: env.Namespace, Name: app.Name}, appl); err != nil { - return nil, err - } - return appl, nil -} diff --git a/references/appfile/app_test.go b/references/appfile/app_test.go index 0f01f1090..85d5f1c88 100644 --- a/references/appfile/app_test.go +++ b/references/appfile/app_test.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "testing" - "time" "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" @@ -128,7 +127,6 @@ services: continue } assert.Equal(t, c.ExpName, app.Name, caseName) - assert.Equal(t, c.ExpComponents, GetComponents(app), caseName) workloadType, workload := GetWorkload(app, c.WantWorkload) assert.Equal(t, c.ExpWorkload, workload, caseName) assert.Equal(t, c.ExpWorkloadType, workloadType, caseName) @@ -137,18 +135,3 @@ services: assert.Equal(t, c.ExpTraits, traits, caseName) } } - -func TestLoadNotExistsApplication(t *testing.T) { - caseName := "load not exists application" - - now := time.Now().Unix() - appName := fmt.Sprintf("test-app-%d", now) - - app, err := LoadApplication(types.DefaultEnvName, appName) - - assert.Nil(t, app, caseName) - assert.Error(t, err, caseName) - - errString := fmt.Sprintf(`application "%s" not found`, appName) - assert.EqualError(t, err, errString, caseName) -} diff --git a/references/appfile/driver/local.go b/references/appfile/driver/local.go index 276330c1d..33772d843 100644 --- a/references/appfile/driver/local.go +++ b/references/appfile/driver/local.go @@ -5,7 +5,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "time" "github.com/ghodss/yaml" @@ -13,7 +12,6 @@ import ( "github.com/oam-dev/kubevela/pkg/utils/env" "github.com/oam-dev/kubevela/pkg/utils/system" "github.com/oam-dev/kubevela/references/appfile/api" - "github.com/oam-dev/kubevela/references/appfile/template" ) // LocalDriverName is local storage driver name @@ -34,33 +32,6 @@ func (l *Local) Name() string { return LocalDriverName } -// List applications from local storage -func (l *Local) List(envName string) ([]*api.Application, error) { - appDir, err := getApplicationDir(envName) - if err != nil { - return nil, err - } - files, err := ioutil.ReadDir(appDir) - if err != nil { - return nil, fmt.Errorf("list apps from %s err %w", appDir, err) - } - var apps []*api.Application - for _, f := range files { - if f.IsDir() { - continue - } - if !strings.HasSuffix(f.Name(), ".yaml") { - continue - } - app, err := loadFromFile(filepath.Join(appDir, f.Name())) - if err != nil { - return nil, fmt.Errorf("load application err %w", err) - } - apps = append(apps, app) - } - return apps, nil -} - // Save application from local storage func (l *Local) Save(app *api.Application, envName string) error { appDir, err := getApplicationDir(envName) @@ -88,24 +59,6 @@ func (l *Local) Delete(envName, appName string) error { return os.Remove(filepath.Join(appDir, appName+".yaml")) } -// Get application from local storage -func (l *Local) Get(envName, appName string) (*api.Application, error) { - appDir, err := getApplicationDir(envName) - if err != nil { - return nil, err - } - app, err := loadFromFile(filepath.Join(appDir, appName+".yaml")) - - if err != nil { - if os.IsNotExist(err) { - return nil, fmt.Errorf(`application "%s" not found`, appName) - } - return nil, err - } - - return app, nil -} - func getApplicationDir(envName string) (string, error) { appDir := filepath.Join(env.GetEnvDirByName(envName), "applications") _, err := system.CreateIfNotExist(appDir) @@ -114,22 +67,3 @@ func getApplicationDir(envName string) (string, error) { } return appDir, err } - -// LoadFromFile will load application from file -func loadFromFile(fileName string) (*api.Application, error) { - tm, err := template.Load() - if err != nil { - return nil, err - } - _, err = os.Stat(fileName) - if err != nil { - return nil, err - } - - f, err := api.LoadFromFile(fileName) - if err != nil { - return nil, err - } - app := &api.Application{AppFile: f, Tm: tm} - return app, nil -} diff --git a/references/appfile/driver/local_test.go b/references/appfile/driver/local_test.go index 04eca1583..b10806f99 100644 --- a/references/appfile/driver/local_test.go +++ b/references/appfile/driver/local_test.go @@ -10,18 +10,15 @@ import ( "github.com/ghodss/yaml" "github.com/oam-dev/kubevela/references/appfile/api" - "github.com/oam-dev/kubevela/references/appfile/template" ) var dir string -var tm template.Manager var afile *api.AppFile var appName = "testsvc" var envName = "default" func init() { dir, _ = getApplicationDir(envName) - tm, _ = template.Load() afile = api.NewAppFile() afile.Name = appName svcs := make(map[string]api.Service) @@ -37,34 +34,6 @@ func init() { _ = ioutil.WriteFile(filepath.Join(dir, appName+".yaml"), out, 0644) } -func TestLocal_Get(t *testing.T) { - type args struct { - envName string - appName string - } - tests := []struct { - name string - args args - want *api.Application - wantErr bool - }{ - {"TestLocal_Get1", args{envName: envName, appName: appName}, &api.Application{AppFile: afile, Tm: tm}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := &Local{} - got, err := l.Get(tt.args.envName, tt.args.appName) - if (err != nil) != tt.wantErr { - t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Get() got = %v, want %v", got, tt.want) - } - }) - } -} - func TestLocal_Delete(t *testing.T) { type args struct { envName string @@ -110,35 +79,6 @@ func TestLocal_Save(t *testing.T) { } } -func TestLocal_List(t *testing.T) { - type args struct { - envName string - } - want := make([]*api.Application, 0) - want = append(want, &api.Application{AppFile: afile, Tm: tm}) - tests := []struct { - name string - args args - want []*api.Application - wantErr bool - }{ - {"TestLocal_List1", args{envName}, want, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := &Local{} - got, err := l.List(tt.args.envName) - if (err != nil) != tt.wantErr { - t.Errorf("List() error = %v, wantErr %v", err, tt.wantErr) - return - } - if len(got) == 0 { - t.Errorf("List() got = %v, want %v", got, tt.want) - } - }) - } -} - func TestLocal_Name(t *testing.T) { tests := []struct { name string @@ -197,29 +137,3 @@ func Test_getApplicationDir(t *testing.T) { }) } } - -func Test_loadFromFile(t *testing.T) { - type args struct { - fileName string - } - tests := []struct { - name string - args args - want *api.Application - wantErr bool - }{ - {"testRespApp", args{fileName: filepath.Join(dir, appName+".yaml")}, &api.Application{AppFile: afile, Tm: tm}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := loadFromFile(tt.args.fileName) - if (err != nil) != tt.wantErr { - t.Errorf("loadFromFile() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got.Name != tt.want.Name { - t.Errorf("loadFromFile() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/references/appfile/modify.go b/references/appfile/modify.go index eb5b39463..cf47795b2 100644 --- a/references/appfile/modify.go +++ b/references/appfile/modify.go @@ -1,7 +1,13 @@ package appfile import ( + "encoding/json" "errors" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/references/appfile/api" ) @@ -25,30 +31,39 @@ func SetWorkload(app *api.Application, componentName, workloadType string, workl } // SetTrait will set user trait for Appfile -func SetTrait(app *api.Application, componentName, traitType string, traitData map[string]interface{}) error { +func SetTrait(app *v1alpha2.Application, componentName, traitType string, traitData map[string]interface{}) error { if app == nil { return errors.New("app is nil pointer") } if traitData == nil { traitData = make(map[string]interface{}) } - - s, ok := app.Services[componentName] - if !ok { - s = api.Service{} + data, err := json.Marshal(traitData) + if err != nil { + return fmt.Errorf("fail to marshal trait data %w", err) } - - t, ok := s[traitType] - if !ok { - t = make(map[string]interface{}) + var foundComp bool + for idx, comp := range app.Spec.Components { + if comp.Name != componentName { + continue + } + foundComp = true + var added bool + for j, tr := range app.Spec.Components[idx].Traits { + if tr.Name != traitType { + continue + } + added = true + app.Spec.Components[idx].Traits[j].Properties.Raw = data + } + if !added { + app.Spec.Components[idx].Traits = append(app.Spec.Components[idx].Traits, v1alpha2.ApplicationTrait{Name: traitType, Properties: runtime.RawExtension{Raw: data}}) + } } - tm := t.(map[string]interface{}) - for k, v := range traitData { - tm[k] = v + if !foundComp { + return errors.New(componentName + " not found in app " + app.Name) } - s[traitType] = t - app.Services[componentName] = s - return Validate(app) + return nil } // RemoveTrait will remove a trait from Appfile @@ -65,12 +80,18 @@ func RemoveTrait(app *api.Application, componentName, traitType string) error { return nil } -// RemoveComponent will remove component from Appfile -func RemoveComponent(app *api.Application, componentName string) error { +// RemoveComponent will remove component from Application +func RemoveComponent(app *v1alpha2.Application, componentName string) error { if app == nil { return errors.New("app is nil pointer") } - - delete(app.Services, componentName) + var newComps []v1alpha2.ApplicationComponent + for _, comp := range app.Spec.Components { + if comp.Name == componentName { + continue + } + newComps = append(newComps, comp) + } + app.Spec.Components = newComps return nil } diff --git a/references/appfile/storage.go b/references/appfile/storage.go index d3a452097..4f86bc964 100644 --- a/references/appfile/storage.go +++ b/references/appfile/storage.go @@ -33,11 +33,6 @@ func GetStorage() *Storage { return store } -// List applications storage common implement -func (s *Storage) List(envName string) ([]*api.Application, error) { - return s.Driver.List(envName) -} - // Save application storage common implement func (s *Storage) Save(app *api.Application, envName string) error { return s.Driver.Save(app, envName) @@ -47,8 +42,3 @@ func (s *Storage) Save(app *api.Application, envName string) error { func (s *Storage) Delete(envName, appName string) error { return s.Driver.Delete(envName, appName) } - -// Get application storage common implement -func (s *Storage) Get(envName, appName string) (*api.Application, error) { - return s.Driver.Get(envName, appName) -} diff --git a/references/appfile/template/manager.go b/references/appfile/template/manager.go index 22ff1104e..6ccd89d96 100644 --- a/references/appfile/template/manager.go +++ b/references/appfile/template/manager.go @@ -12,8 +12,8 @@ type Manager interface { } // Load will load all installed capabilities and create a manager -func Load() (Manager, error) { - caps, err := plugins.LoadAllInstalledCapability() +func Load(namespace string, c types.Args) (Manager, error) { + caps, err := plugins.LoadAllInstalledCapability(namespace, c) if err != nil { return nil, err } diff --git a/references/cli/capability.go b/references/cli/capability.go index 6ad08de1e..0535f1056 100644 --- a/references/cli/capability.go +++ b/references/cli/capability.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/spf13/cobra" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/oam/discoverymapper" @@ -29,7 +28,7 @@ func CapabilityCommandGroup(c types.Args, ioStream cmdutil.IOStreams) *cobra.Com } cmd.AddCommand( NewCenterCommand(ioStream), - NewCapListCommand(ioStream), + NewCapListCommand(c, ioStream), NewCapInstallCommand(c, ioStream), NewCapUninstallCommand(c, ioStream), ) @@ -94,7 +93,7 @@ func NewCapInstallCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comm if argsLength < 1 { return errors.New("you must specify
/ for capability you want to install") } - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } @@ -126,7 +125,7 @@ func NewCapUninstallCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Co if len(args) < 1 { return errors.New("you must specify for capability you want to uninstall") } - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } @@ -138,7 +137,11 @@ func NewCapUninstallCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Co } name = l[1] } - return common.RemoveCapability(newClient, name, ioStreams) + env, err := GetEnv(cmd) + if err != nil { + return err + } + return common.RemoveCapability(env.Namespace, c, newClient, name, ioStreams) }, } cmd.PersistentFlags().StringP("token", "t", "", "Github Repo token") @@ -168,7 +171,7 @@ func NewCapCenterSyncCommand(ioStreams cmdutil.IOStreams) *cobra.Command { } // NewCapListCommand List capabilities from cap-center -func NewCapListCommand(ioStreams cmdutil.IOStreams) *cobra.Command { +func NewCapListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "ls [cap-center]", Short: "List capabilities from cap-center", @@ -179,7 +182,11 @@ func NewCapListCommand(ioStreams cmdutil.IOStreams) *cobra.Command { if len(args) > 0 { repoName = args[0] } - capabilityList, err := common.ListCapabilities(repoName) + env, err := GetEnv(cmd) + if err != nil { + return err + } + capabilityList, err := common.ListCapabilities(env.Namespace, c, repoName) if err != nil { return err } diff --git a/references/cli/dashboard.go b/references/cli/dashboard.go index 8206a2559..87845b35a 100644 --- a/references/cli/dashboard.go +++ b/references/cli/dashboard.go @@ -45,7 +45,7 @@ func NewDashboardCommand(c types.Args, ioStreams cmdutil.IOStreams, frontendSour return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } diff --git a/references/cli/delete.go b/references/cli/delete.go index ad6a91430..096cd8789 100644 --- a/references/cli/delete.go +++ b/references/cli/delete.go @@ -5,7 +5,6 @@ import ( "github.com/spf13/cobra" apierrors "k8s.io/apimachinery/pkg/api/errors" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/types" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" @@ -30,11 +29,13 @@ func NewDeleteCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command cmd.SetOut(ioStreams.Out) cmd.RunE = func(cmd *cobra.Command, args []string) error { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } - o := &common.DeleteOptions{} + o := &common.DeleteOptions{ + C: c, + } o.Client = newClient o.Env, err = GetEnv(cmd) if err != nil { diff --git a/references/cli/dryrun.go b/references/cli/dryrun.go index a12867280..7c86eb4cf 100644 --- a/references/cli/dryrun.go +++ b/references/cli/dryrun.go @@ -8,7 +8,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/yaml" corev1alpha2 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" @@ -37,7 +36,7 @@ func NewDryRunCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } diff --git a/references/cli/env.go b/references/cli/env.go index 25f06cbdf..f11e2be65 100644 --- a/references/cli/env.go +++ b/references/cli/env.go @@ -64,15 +64,11 @@ func NewEnvInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } - if err := RefreshDefinitions(ctx, c, ioStreams, true, true); err != nil { - return err - } - return CreateOrUpdateEnv(ctx, newClient, &envArgs, args, ioStreams) }, Annotations: map[string]string{ diff --git a/references/cli/exec.go b/references/cli/exec.go index 83667eb65..56cc10ea9 100644 --- a/references/cli/exec.go +++ b/references/cli/exec.go @@ -6,6 +6,8 @@ import ( "strings" "time" + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" + "github.com/spf13/cobra" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -19,7 +21,6 @@ import ( "github.com/oam-dev/kubevela/pkg/utils/common" "github.com/oam-dev/kubevela/pkg/utils/util" "github.com/oam-dev/kubevela/references/appfile" - "github.com/oam-dev/kubevela/references/appfile/api" ) const ( @@ -40,7 +41,7 @@ type VelaExecOptions struct { context.Context VelaC types.Args Env *types.EnvMeta - App *api.Application + App *v1alpha2.Application f k8scmdutil.Factory kcExecOptions *cmdexec.ExecOptions @@ -122,7 +123,7 @@ func (o *VelaExecOptions) Init(ctx context.Context, c *cobra.Command, argsIn []s return err } o.Env = env - app, err := appfile.LoadApplication(env.Name, o.Args[0]) + app, err := appfile.LoadApplication(env.Namespace, o.Args[0], o.VelaC) if err != nil { return err } @@ -167,8 +168,10 @@ func (o *VelaExecOptions) getComponentName() (string, error) { svcName := o.ServiceName if svcName != "" { - if _, exist := o.App.Services[svcName]; exist { - return svcName, nil + for _, cc := range o.App.Spec.Components { + if cc.Name == svcName { + return svcName, nil + } } o.Cmd.Printf("The service name '%s' is not valid\n", svcName) } diff --git a/references/cli/exec_test.go b/references/cli/exec_test.go index 8999485f8..1ea4a08b9 100644 --- a/references/cli/exec_test.go +++ b/references/cli/exec_test.go @@ -6,17 +6,13 @@ import ( "os" "testing" - "github.com/oam-dev/kubevela/references/appfile/api" - "github.com/spf13/cobra" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes/fake" "k8s.io/kubectl/pkg/cmd/exec" cmdtesting "k8s.io/kubectl/pkg/cmd/testing" - k8scmdutil "k8s.io/kubectl/pkg/cmd/util" "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/oam" @@ -52,21 +48,6 @@ func TestExecCommand(t *testing.T) { err := o.Init(context.Background(), cmd, []string{"fakeApp"}) errString := fmt.Sprintf(`application "%s" not found`, "fakeApp") assert.EqualError(t, err, errString) - fakeApp := &api.Application{ - AppFile: &api.AppFile{ - Name: "fakeApp", - Services: map[string]api.Service{ - "fakeComp": map[string]interface{}{}, - }, - }, - } - o.App = fakeApp - - cf := genericclioptions.NewConfigFlags(true) - cf.Namespace = &o.Env.Namespace - o.f = k8scmdutil.NewFactory(k8scmdutil.NewMatchVersionFlags(cf)) - err = o.Complete() - assert.NoError(t, err) } func TestExecCommandPersistentPreRunE(t *testing.T) { @@ -75,27 +56,3 @@ func TestExecCommandPersistentPreRunE(t *testing.T) { cmd := NewExecCommand(fakeC, io) assert.Nil(t, cmd.PersistentPreRunE(new(cobra.Command), []string{})) } - -func TestGetComponent(t *testing.T) { - o := &VelaExecOptions{ - App: &api.Application{ - AppFile: &api.AppFile{ - Name: "fakeApp", - Services: map[string]api.Service{ - "fakeComp1": map[string]interface{}{}, - "fakeComp2": map[string]interface{}{}, - }, - }, - }, - } - - o.ServiceName = "fakeComp1" - svcName, err := o.getComponentName() - assert.NoError(t, err) - assert.Equal(t, o.ServiceName, svcName) - - o.ServiceName = "fakeComp2" - svcName, err = o.getComponentName() - assert.NoError(t, err) - assert.Equal(t, o.ServiceName, svcName) -} diff --git a/references/cli/export.go b/references/cli/export.go index 3ba95ae3f..fd1303954 100644 --- a/references/cli/export.go +++ b/references/cli/export.go @@ -19,15 +19,19 @@ func NewExportCommand(c types.Args, ioStream cmdutil.IOStreams) *cobra.Command { types.TagCommandType: types.TypeStart, }, RunE: func(cmd *cobra.Command, args []string) error { + velaEnv, err := GetEnv(cmd) + if err != nil { + return err + } o := &common.AppfileOptions{ IO: ioStream, - Env: &types.EnvMeta{}, + Env: velaEnv, } filePath, err := cmd.Flags().GetString(appFilePath) if err != nil { return err } - _, data, err := o.Export(filePath, true) + _, data, err := o.Export(filePath, velaEnv.Namespace, true, c) if err != nil { return err } diff --git a/references/cli/init.go b/references/cli/init.go index 7f09577d8..53cbee65a 100644 --- a/references/cli/init.go +++ b/references/cli/init.go @@ -28,6 +28,7 @@ type appInitOptions struct { client client.Client cmdutil.IOStreams Env *types.EnvMeta + c types.Args app *api.Application appName string @@ -38,7 +39,7 @@ type appInitOptions struct { // NewInitCommand creates `init` command func NewInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { - o := &appInitOptions{IOStreams: ioStreams} + o := &appInitOptions{IOStreams: ioStreams, c: c} cmd := &cobra.Command{ Use: "init", DisableFlagsInUseLine: true, @@ -49,7 +50,7 @@ func NewInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } @@ -69,9 +70,6 @@ func NewInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { if err = o.Workload(); err != nil { return err } - if err = o.Traits(); err != nil { - return err - } if err := appfile.Validate(o.app); err != nil { return err @@ -96,14 +94,14 @@ func NewInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { if err != nil { return err } - deployStatus, err := printTrackingDeployStatus(ctx, o.client, o.IOStreams, o.workloadName, o.appName, o.Env) + deployStatus, err := printTrackingDeployStatus(c, o.IOStreams, o.appName, o.Env) if err != nil { return err } if deployStatus != compStatusDeployed { return nil } - return printAppStatus(context.Background(), newClient, ioStreams, o.appName, o.Env, cmd) + return printAppStatus(context.Background(), newClient, ioStreams, o.appName, o.Env, cmd, c) }, Annotations: map[string]string{ types.TagCommandType: types.TypeStart, @@ -184,7 +182,7 @@ func formatAndGetUsage(p *types.Parameter) string { // Workload asks user to choose workload type from installed workloads func (o *appInitOptions) Workload() error { - workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) + workloads, err := plugins.LoadInstalledCapabilityWithType(o.Env.Namespace, o.c, types.TypeWorkload) if err != nil { return err } @@ -306,7 +304,7 @@ func (o *appInitOptions) Workload() error { // other type not supported } } - o.app, err = common.BaseComplete(o.Env.Name, o.workloadName, o.appName, fs, o.workloadType) + o.app, err = common.BaseComplete(o.Env, o.c, o.workloadName, o.appName, fs, o.workloadType) return err } @@ -319,36 +317,3 @@ func GetCapabilityByName(name string, workloads []types.Capability) (types.Capab } return types.Capability{}, fmt.Errorf("%s not found", name) } - -// Traits attaches specific trait to service -func (o *appInitOptions) Traits() error { - traits, err := plugins.LoadInstalledCapabilityWithType(types.TypeTrait) - if err != nil { - return err - } - switch o.workloadType { - case "webservice": - // TODO(wonderflow) this should get from workload definition to know which trait should be suggestions - var suggestTraits = []string{} - if o.Env.Domain != "" { - suggestTraits = append(suggestTraits, "route") - } - for _, tr := range suggestTraits { - trait, err := GetCapabilityByName(tr, traits) - if err != nil { - continue - } - tflags := pflag.NewFlagSet("trait", pflag.ContinueOnError) - for _, pa := range trait.Parameters { - types.SetFlagBy(tflags, pa) - } - // TODO(wonderflow): give a way to add parameter for trait - o.app, err = common.AddOrUpdateTrait(o.Env, o.appName, o.workloadName, tflags, trait) - if err != nil { - return err - } - } - default: - } - return nil -} diff --git a/references/cli/logs.go b/references/cli/logs.go index 14a49b32d..5f8342ac9 100644 --- a/references/cli/logs.go +++ b/references/cli/logs.go @@ -15,11 +15,11 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/utils/common" "github.com/oam-dev/kubevela/pkg/utils/util" "github.com/oam-dev/kubevela/references/appfile" - "github.com/oam-dev/kubevela/references/appfile/api" ) // NewLogsCommand creates `logs` command to tail logs of application @@ -45,7 +45,7 @@ func NewLogsCommand(c types.Args, ioStreams util.IOStreams) *cobra.Command { if err != nil { return err } - app, err := appfile.LoadApplication(env.Name, args[0]) + app, err := appfile.LoadApplication(env.Namespace, args[0], c) if err != nil { return err } @@ -69,7 +69,7 @@ type Args struct { Output string Env *types.EnvMeta C types.Args - App *api.Application + App *v1alpha2.Application } // Run refer to the implementation at https://github.com/oam-dev/stern/blob/master/stern/main.go diff --git a/references/cli/ls.go b/references/cli/ls.go index 384b879f6..4ccd4190f 100644 --- a/references/cli/ls.go +++ b/references/cli/ls.go @@ -32,7 +32,7 @@ func NewListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { if err != nil { return err } - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } diff --git a/references/cli/portforward.go b/references/cli/portforward.go index 0846913b7..15f3f0726 100644 --- a/references/cli/portforward.go +++ b/references/cli/portforward.go @@ -28,7 +28,6 @@ import ( "github.com/oam-dev/kubevela/pkg/utils/common" "github.com/oam-dev/kubevela/pkg/utils/util" "github.com/oam-dev/kubevela/references/appfile" - "github.com/oam-dev/kubevela/references/appfile/api" ) // VelaPortForwardOptions for vela port-forward @@ -40,7 +39,7 @@ type VelaPortForwardOptions struct { context.Context VelaC types.Args Env *types.EnvMeta - App *api.Application + App *v1alpha2.Application f k8scmdutil.Factory kcPortForwardOptions *cmdpf.PortForwardOptions @@ -74,7 +73,7 @@ func NewPortForwardCommand(c types.Args, ioStreams util.IOStreams) *cobra.Comman ioStreams.Error("Please specify application name.") return nil } - newClient, err := client.New(o.VelaC.Config, client.Options{Scheme: o.VelaC.Schema}) + newClient, err := o.VelaC.GetClient() if err != nil { return err } @@ -114,7 +113,7 @@ func (o *VelaPortForwardOptions) Init(ctx context.Context, cmd *cobra.Command, a } o.Env = env - app, err := appfile.LoadApplication(env.Name, o.Args[0]) + app, err := appfile.LoadApplication(env.Namespace, o.Args[0], o.VelaC) if err != nil { return err } @@ -191,7 +190,7 @@ func (o *VelaPortForwardOptions) Complete() error { } if len(o.Args) < 2 { var found bool - _, configs := appfile.GetServiceConfig(o.App, svcName) + _, configs := appfile.GetApplicationSettings(o.App, svcName) for k, v := range configs { if k == "port" { var val string diff --git a/references/cli/refresh.go b/references/cli/refresh.go deleted file mode 100644 index 2a2a78e45..000000000 --- a/references/cli/refresh.go +++ /dev/null @@ -1,285 +0,0 @@ -package cli - -import ( - "context" - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "time" - - "github.com/fatih/color" - "github.com/gosuri/uitable" - "github.com/mitchellh/hashstructure/v2" - - "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/utils/system" - cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" - "github.com/oam-dev/kubevela/references/plugins" -) - -type refreshStatus string - -const ( - added refreshStatus = "Added" - updated refreshStatus = "Updated" - unchanged refreshStatus = "Unchanged" - deleted refreshStatus = "Deleted" -) - -const ( - refreshInterval = 5 * time.Minute -) - -// RefreshDefinitions will sync local capabilities with cluster installed ones -func RefreshDefinitions(ctx context.Context, c types.Args, ioStreams cmdutil.IOStreams, silentOutput, enforceRefresh bool) error { - dir, _ := system.GetCapabilityDir() - oldCaps, err := plugins.LoadAllInstalledCapability() - if err != nil { - return err - } - useCached, err := useCacheInsteadRefresh(dir, refreshInterval) - if err != nil { - return err - } - if !enforceRefresh && useCached { - // use local capabilities instead of fetching from cluster - printRefreshReport(nil, oldCaps, ioStreams, silentOutput, true) - return nil - } - - syncedTemplates, warnings, err := plugins.SyncDefinitionsToLocal(ctx, c, dir) - if err != nil { - return err - } - for _, w := range warnings { - ioStreams.Infof(w) - } - plugins.RemoveLegacyTemps(syncedTemplates, dir) - printRefreshReport(syncedTemplates, oldCaps, ioStreams, silentOutput, false) - return nil -} - -// silent indicates whether output existing caps if no change occurs. If false, output all existing caps. -func printRefreshReport(newCaps, oldCaps []types.Capability, io cmdutil.IOStreams, silent, useCached bool) { - var report map[refreshStatus][]types.Capability - if useCached { - report = map[refreshStatus][]types.Capability{ - added: make([]types.Capability, 0), - updated: make([]types.Capability, 0), - unchanged: oldCaps, - deleted: make([]types.Capability, 0), - } - } else { - report = refreshResultReport(newCaps, oldCaps) - } - table := newUITable() - table.MaxColWidth = 80 - table.AddRow("TYPE", "CATEGORY", "DESCRIPTION") - - if len(report[added]) == 0 && len(report[updated]) == 0 && len(report[deleted]) == 0 { - // no change occurs, just show all existing caps - // always show workload at first - for _, cap := range report[unchanged] { - if cap.Type == types.TypeWorkload { - table.AddRow(cap.Name, cap.Type, cap.Description) - } - } - for _, cap := range report[unchanged] { - if cap.Type == types.TypeTrait { - table.AddRow(cap.Name, cap.Type, cap.Description) - } - } - if !silent { - io.Infof("Automatically discover capabilities successfully %s(no changes)\n\n", emojiSucceed) - io.Info(table.String()) - } - return - } - - io.Infof("Automatically discover capabilities successfully %sAdd(%s) Update(%s) Delete(%s)\n\n", - emojiSucceed, - green.Sprint(len(report[added])), - yellow.Sprint(len(report[updated])), - red.Sprint(len(report[deleted]))) - // show added/updated/deleted cpas - addStsRow(added, report, table) - addStsRow(updated, report, table) - addStsRow(deleted, report, table) - io.Info(table.String()) - io.Info() -} - -func addStsRow(sts refreshStatus, report map[refreshStatus][]types.Capability, t *uitable.Table) { - caps := report[sts] - if len(caps) == 0 { - return - } - var stsIcon string - var stsColor *color.Color - switch sts { - case added: - stsIcon = "+" - stsColor = green - case updated: - stsIcon = "*" - stsColor = yellow - case deleted: - stsIcon = "-" - stsColor = red - case unchanged: - // normal color display - } - for _, cap := range caps { - t.AddRow( - // color.New(color.Bold).Sprint(stsColor.Sprint(stsIcon)), - stsColor.Sprintf("%s%s", stsIcon, cap.Name), - stsColor.Sprint(cap.Type), - stsColor.Sprint(cap.Description)) - } -} - -func refreshResultReport(newCaps, oldCaps []types.Capability) map[refreshStatus][]types.Capability { - dir, _ := system.GetCapabilityDir() - cachedHash := readCapDefHashFromLocal(dir) - newHash := map[string]string{} - for _, newCap := range newCaps { - h, err := hashstructure.Hash(newCap, hashstructure.FormatV2, nil) - if err != nil { - continue - } - newHash[newCap.Name] = strconv.FormatUint(h, 10) - } - - report := map[refreshStatus][]types.Capability{ - added: make([]types.Capability, 0), - updated: make([]types.Capability, 0), - unchanged: make([]types.Capability, 0), - deleted: make([]types.Capability, 0), - } - for _, newCap := range newCaps { - found := false - for _, oldCap := range oldCaps { - if newCap.Name == oldCap.Name { - found = true - break - } - } - if !found { - report[added] = append(report[added], newCap) - } - } - for _, oldCap := range oldCaps { - found := false - for _, newCap := range newCaps { - if oldCap.Name == newCap.Name { - found = true - // use cached hash to determine whether the cap is changed - if h, ok := cachedHash[newCap.Name]; ok { - if h == newHash[newCap.Name] { - report[unchanged] = append(report[unchanged], newCap) - } else { - report[updated] = append(report[updated], newCap) - } - break - } - // in case of missing cache, use Equal func to compare - if types.EqualCapability(oldCap, newCap) { - report[unchanged] = append(report[unchanged], newCap) - } else { - report[updated] = append(report[updated], newCap) - } - break - } - } - if !found { - report[deleted] = append(report[deleted], oldCap) - } - } - _ = writeCapDefHashIntoLocal(dir, newHash) - return report -} - -// useCacheInsteadRefresh checks whether use cached capabilities instead of refresh from cluster -// a timestamp records the time when refresh from cluster last time -// if duration since last time refresh DOES NOT exceed `cacheExpiredDuration` -// use cached capabilities instead of refresh from cluster -// else refresh from cluster and refresh the timestamp -func useCacheInsteadRefresh(capDir string, cacheExpiredDuration time.Duration) (bool, error) { - currentTimestamp := strconv.FormatInt(time.Now().Unix(), 10) - tmpDir := filepath.Join(capDir, ".tmp") - timeFilePath := filepath.Join(tmpDir, ".lasttimerefresh") - exist, _ := system.CreateIfNotExist(tmpDir) - if !exist { - // file saving timestamp is not created yet, create and refresh the timestamp - if err := ioutil.WriteFile(timeFilePath, []byte(currentTimestamp), 0600); err != nil { - return false, err - } - return false, nil - } - r, err := ioutil.ReadFile(filepath.Clean(timeFilePath)) - if err != nil { - if os.IsNotExist(err) { - // tmpDir exists but `.lasttimerefresh` file doesn't - if err := ioutil.WriteFile(timeFilePath, []byte(currentTimestamp), 0600); err != nil { - return false, err - } - return false, nil - } - return false, err - } - i, err := strconv.ParseInt(string(r), 10, 64) - if err != nil { - return false, err - } - lt := time.Unix(i, 0) - if time.Since(lt) > cacheExpiredDuration { - // cache is expired, refresh the timestamp - if err := ioutil.WriteFile(timeFilePath, []byte(currentTimestamp), 0600); err != nil { - return false, err - } - return false, nil - } - // cache is not expired - return true, nil -} - -// each capability has a hash value cached in local capability dir -// hash value is used to compare local capability with one from cluster -// refresh report will show all changed capabilities -func readCapDefHashFromLocal(capDir string) map[string]string { - r := map[string]string{} - tmpDir := filepath.Join(capDir, ".tmp") - hashFilePath := filepath.Join(tmpDir, ".capabilityhash") - if exist, err := system.CreateIfNotExist(tmpDir); !exist || err != nil { - return r - } - if _, err := os.Stat(hashFilePath); os.IsNotExist(err) { - return r - } - hashData, err := ioutil.ReadFile(filepath.Clean(hashFilePath)) - if err != nil { - return r - } - if err := json.Unmarshal(hashData, &r); err != nil { - return r - } - return r -} - -func writeCapDefHashIntoLocal(capDir string, hashData map[string]string) error { - tmpDir := filepath.Join(capDir, ".tmp") - hashFilePath := filepath.Join(tmpDir, ".capabilityhash") - if _, err := system.CreateIfNotExist(tmpDir); err != nil { - return err - } - data, err := json.Marshal(hashData) - if err != nil { - return err - } - if err := ioutil.WriteFile(hashFilePath, data, 0600); err != nil { - return err - } - return nil -} diff --git a/references/cli/refresh_test.go b/references/cli/refresh_test.go deleted file mode 100644 index 4bbdc1b5b..000000000 --- a/references/cli/refresh_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package cli - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestCheckAndUpdateRefreshInterval(t *testing.T) { - testdir := "testdir-refresh-interval" - testMaxInterval := time.Second - - err := os.MkdirAll(testdir, 0755) - assert.NoError(t, err) - defer os.RemoveAll(testdir) - - r, err := useCacheInsteadRefresh(testdir, testMaxInterval) - assert.Equal(t, r, false, "should not use cache for tmp file is not created") - assert.NoError(t, err) - - r, err = useCacheInsteadRefresh(testdir, testMaxInterval) - assert.Equal(t, r, true, "should use cache for interval is not expired") - assert.NoError(t, err) - - time.Sleep(2 * testMaxInterval) - r, err = useCacheInsteadRefresh(testdir, testMaxInterval) - assert.Equal(t, r, false, "should not use cache for interval is already expired") - assert.NoError(t, err) -} - -func TestWriteAndReadLocalCapHash(t *testing.T) { - testdir := "testdir-caphash" - err := os.MkdirAll(testdir, 0755) - assert.NoError(t, err) - defer os.RemoveAll(testdir) - - result := readCapDefHashFromLocal(testdir) - assert.Equal(t, result, map[string]string{}, "capability hash data should be empty") - fakeHashData := map[string]string{ - "a": "test1", - "b": "test2", - } - err = writeCapDefHashIntoLocal(testdir, fakeHashData) - assert.NoError(t, err, "write new hash data successfully") - result = readCapDefHashFromLocal(testdir) - assert.Equal(t, result, fakeHashData, "read hash data successfully") -} diff --git a/references/cli/status.go b/references/cli/status.go index 167853cac..3c9939403 100644 --- a/references/cli/status.go +++ b/references/cli/status.go @@ -92,11 +92,11 @@ func NewAppStatusCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comma ioStreams.Errorf("Error: failed to get Env: %s", err) return err } - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } - return printAppStatus(ctx, newClient, ioStreams, appName, env, cmd) + return printAppStatus(ctx, newClient, ioStreams, appName, env, cmd, c) }, Annotations: map[string]string{ types.TagCommandType: types.TypeApp, @@ -107,8 +107,8 @@ func NewAppStatusCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comma return cmd } -func printAppStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams, appName string, env *types.EnvMeta, cmd *cobra.Command) error { - app, err := appfile.LoadApplication(env.Name, appName) +func printAppStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams, appName string, env *types.EnvMeta, cmd *cobra.Command, velaC types.Args) error { + app, err := appfile.LoadApplication(env.Namespace, appName, velaC) if err != nil { return err } @@ -118,8 +118,7 @@ func printAppStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOSt table := newUITable() table.AddRow(" Name:", appName) table.AddRow(" Namespace:", namespace) - table.AddRow(" Created at:", app.CreateTime.String()) - table.AddRow(" Updated at:", app.UpdateTime.String()) + table.AddRow(" Created at:", app.CreationTimestamp.String()) cmd.Printf("%s\n\n", table.String()) cmd.Printf("Services:\n\n") @@ -216,14 +215,14 @@ HealthCheckLoop: return healthStatus, healthInfo, nil } -func printTrackingDeployStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams, compName, appName string, env *types.EnvMeta) (CompStatus, error) { +func printTrackingDeployStatus(c types.Args, ioStreams cmdutil.IOStreams, appName string, env *types.EnvMeta) (CompStatus, error) { sDeploy := newTrackingSpinnerWithDelay("Checking Status ...", trackingInterval) sDeploy.Start() defer sDeploy.Stop() TrackDeployLoop: for { time.Sleep(trackingInterval) - deployStatus, failMsg, err := TrackDeployStatus(ctx, c, compName, appName, env) + deployStatus, failMsg, err := TrackDeployStatus(c, appName, env) if err != nil { return compStatusUnknown, err } @@ -245,12 +244,12 @@ TrackDeployLoop: } // TrackDeployStatus will only check AppConfig is deployed successfully, -func TrackDeployStatus(ctx context.Context, c client.Client, compName, appName string, env *types.EnvMeta) (CompStatus, string, error) { - app, appObj, err := getApp(ctx, c, compName, appName, env) +func TrackDeployStatus(c types.Args, appName string, env *types.EnvMeta) (CompStatus, string, error) { + appObj, err := appfile.LoadApplication(env.Namespace, appName, c) if err != nil { return compStatusUnknown, "", err } - if app == nil || appObj == nil { + if appObj == nil { return compStatusUnknown, "", errors.New(ErrNotLoadAppConfig) } condition := appObj.Status.Conditions @@ -319,25 +318,6 @@ func trackHealthCheckingStatus(ctx context.Context, c client.Client, compName, a return compStatusHealthCheckDone, HealthStatusNotDiagnosed, "", nil } -func getApp(ctx context.Context, c client.Client, compName, appName string, env *types.EnvMeta) (*api.Application, *v1alpha2.Application, error) { - var app *api.Application - var err error - if appName != "" { - app, err = appfile.LoadApplication(env.Name, appName) - } else { - app, err = appfile.MatchAppByComp(env.Name, compName) - } - if err != nil { - return nil, nil, err - } - - appObj, err := appfile.GetApplication(ctx, c, app, env) - if err != nil { - return nil, nil, err - } - return app, appObj, nil -} - func getWorkloadStatusFromApp(app *v1alpha2.Application, compName string) (v1alpha2.ApplicationComponentStatus, bool) { foundWlStatus := false wlStatus := v1alpha2.ApplicationComponentStatus{} diff --git a/references/cli/system.go b/references/cli/system.go index c684c7871..615cdebae 100644 --- a/references/cli/system.go +++ b/references/cli/system.go @@ -111,7 +111,7 @@ func NewInstallCommand(c types.Args, chartContent string, ioStreams cmdutil.IOSt return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return err } @@ -172,9 +172,6 @@ func (i *initCmd) run(ioStreams cmdutil.IOStreams, chartSource string) error { "try running 'vela workloads' or 'vela traits' to check after a while, details: %v", err) return nil } - if err := RefreshDefinitions(context.Background(), i.c, ioStreams, false, true); err != nil { - return err - } ioStreams.Info("- Finished successfully.") if waitDuration > 0 { @@ -204,7 +201,7 @@ func CheckCapabilityReady(ctx context.Context, c types.Args, timeout time.Durati defer spiner.Stop() for { - _, err = plugins.GetCapabilitiesFromCluster(ctx, types.DefaultKubeVelaNS, c, tmpdir, nil) + _, err = plugins.GetCapabilitiesFromCluster(ctx, types.DefaultKubeVelaNS, c, nil) if err == nil { return nil } diff --git a/references/cli/testdata/appfile.yaml b/references/cli/testdata/appfile.yaml new file mode 100644 index 000000000..b9070263f --- /dev/null +++ b/references/cli/testdata/appfile.yaml @@ -0,0 +1,18 @@ +name: first-vela-app +services: + testsvc: + type: webservice + image: crccheck/hello-world + port: 8000 + ingress: + domain: testsvc.example.com + http: + "/": 8000 + abc: + type: webservice + image: crccheck/hello-world + port: 8000 + ingress: + domain: testsvc.example.com + http: + "/": 8000 diff --git a/references/cli/traits.go b/references/cli/traits.go index 8371aedd1..8f93f7a13 100644 --- a/references/cli/traits.go +++ b/references/cli/traits.go @@ -1,7 +1,6 @@ package cli import ( - "context" "strings" "github.com/spf13/cobra" @@ -14,8 +13,6 @@ import ( // NewTraitsCommand creates `traits` command func NewTraitsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { var workloadName string - var enforceRefresh bool - ctx := context.Background() cmd := &cobra.Command{ Use: "traits [--apply-to WORKLOAD_NAME]", DisableFlagsInUseLine: true, @@ -26,12 +23,11 @@ func NewTraitsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { - - if err := RefreshDefinitions(ctx, c, ioStreams, true, enforceRefresh); err != nil { + env, err := GetEnv(cmd) + if err != nil { return err } - - return printTraitList(&workloadName, ioStreams) + return printTraitList(env.Namespace, c, &workloadName, ioStreams) }, Annotations: map[string]string{ types.TagCommandType: types.TypeCap, @@ -40,15 +36,14 @@ func NewTraitsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command cmd.SetOut(ioStreams.Out) cmd.Flags().StringVar(&workloadName, "apply-to", "", "Workload name") - cmd.Flags().BoolVarP(&enforceRefresh, "", "r", false, "Enforce refresh from cluster even if cache is not expired") return cmd } -func printTraitList(workloadName *string, ioStreams cmdutil.IOStreams) error { +func printTraitList(userNamespace string, c types.Args, workloadName *string, ioStreams cmdutil.IOStreams) error { table := newUITable() table.MaxColWidth = 120 table.Wrap = true - traitDefinitionList, err := common.ListTraitDefinitions(workloadName) + traitDefinitionList, err := common.ListTraitDefinitions(userNamespace, c, workloadName) if err != nil { return err } diff --git a/references/cli/traits_test.go b/references/cli/traits_test.go index 1fd06fd92..c50d9df5b 100644 --- a/references/cli/traits_test.go +++ b/references/cli/traits_test.go @@ -1,11 +1,9 @@ package cli import ( - "bytes" "os" "testing" - "github.com/gosuri/uitable" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" @@ -14,84 +12,6 @@ import ( "github.com/oam-dev/kubevela/references/common" ) -func Test_printTraitList(t *testing.T) { - traits := []types.Capability{ - { - Name: "route", - CrdName: "routes.oam.dev", - // This format is currently OAM spec standard - AppliesTo: []string{"apps/v1.Deployment", "alibaba/v1.Clonset"}, - }, - { - Name: "scaler", - CrdName: "scaler.oam.dev", - // This format is also reasonable, it's align with oam definition name, so we also support here - AppliesTo: []string{"deployments.apps"}, - }, - } - workloads := []types.Capability{ - { - Name: "deployment", - CrdName: "deployments.apps", - }, - { - Name: "clonset", - CrdName: "clonsets.alibaba", - }, - } - newTable := func() *uitable.Table { - table := newUITable() - table.AddRow("NAME", "DEFINITION", "APPLIES TO") - return table - } - tb1 := newTable() - tb1.AddRow("route", "routes.oam.dev", "deployment") - tb1.AddRow("", "", "clonset") - tb1.AddRow("scaler", "scaler.oam.dev", "deployment") - - tb2 := newTable() - tb2.AddRow("route", "routes.oam.dev", "deployment") - tb2.AddRow("scaler", "scaler.oam.dev", "deployment") - - tb3 := newTable() - tb3.AddRow("route", "routes.oam.dev", "clonset") - - cases := map[string]struct { - traits []types.Capability - workloads []types.Capability - workloadName string - iostream cmdutil.IOStreams - ExpectedString string - }{ - "All Workloads": { - traits: traits, - workloads: workloads, - ExpectedString: tb1.String() + "\n", - }, - "Specify Workload Name deployment": { - traits: traits, - workloads: workloads, - workloadName: "deployment", - ExpectedString: tb2.String() + "\n", - }, - "Specify Workload Name clonset": { - traits: traits, - workloads: workloads, - workloadName: "clonset", - ExpectedString: tb3.String() + "\n", - }, - } - // TODO(zzxwill) As the old `func printTraitList(traits, workloads []types.Capability, workloadName *string, ioStreams cmdutil.IOStreams)` - // doesn't exist any more, comment this unit-test for now - //for cname, c := range cases { - for _, c := range cases { - b := bytes.Buffer{} - iostream := cmdutil.IOStreams{Out: &b} - nn := c.workloadName - assert.NoError(t, printTraitList(&nn, iostream)) - } -} - func TestNewTraitsCommandPersistentPreRunE(t *testing.T) { io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} fakeC := types.Args{} diff --git a/references/cli/up.go b/references/cli/up.go index 93c546e8a..01d505b12 100644 --- a/references/cli/up.go +++ b/references/cli/up.go @@ -2,7 +2,6 @@ package cli import ( "github.com/spf13/cobra" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/types" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" @@ -31,7 +30,7 @@ func NewUpCommand(c types.Args, ioStream cmdutil.IOStreams) *cobra.Command { if err != nil { return err } - kubecli, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + kubecli, err := c.GetClient() if err != nil { return err } @@ -45,7 +44,7 @@ func NewUpCommand(c types.Args, ioStream cmdutil.IOStreams) *cobra.Command { if err != nil { return err } - return o.Run(filePath, c.Config) + return o.Run(filePath, velaEnv.Namespace, c) }, } cmd.SetOut(ioStream.Out) diff --git a/references/cli/workloads.go b/references/cli/workloads.go index 71f291e17..acc7e1903 100644 --- a/references/cli/workloads.go +++ b/references/cli/workloads.go @@ -1,8 +1,6 @@ package cli import ( - "context" - "github.com/spf13/cobra" "github.com/oam-dev/kubevela/apis/types" @@ -12,8 +10,6 @@ import ( // NewWorkloadsCommand creates `workloads` command func NewWorkloadsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { - var enforceRefresh bool - ctx := context.Background() cmd := &cobra.Command{ Use: "workloads", DisableFlagsInUseLine: true, @@ -24,12 +20,11 @@ func NewWorkloadsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comma return c.SetConfig() }, RunE: func(cmd *cobra.Command, args []string) error { - - if err := RefreshDefinitions(ctx, c, ioStreams, true, enforceRefresh); err != nil { + env, err := GetEnv(cmd) + if err != nil { return err } - - workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) + workloads, err := plugins.LoadInstalledCapabilityWithType(env.Namespace, c, types.TypeWorkload) if err != nil { return err } @@ -40,7 +35,6 @@ func NewWorkloadsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comma }, } cmd.SetOut(ioStreams.Out) - cmd.Flags().BoolVarP(&enforceRefresh, "", "r", false, "Enforce refresh from cluster even if cache is not expired") return cmd } diff --git a/references/common/application.go b/references/common/application.go index ffc4abf7f..d22a35ee9 100644 --- a/references/common/application.go +++ b/references/common/application.go @@ -19,7 +19,6 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/serializer/json" apitypes "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" corev1alpha2 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" @@ -95,6 +94,7 @@ type DeleteOptions struct { CompName string Client client.Client Env *types.EnvMeta + C types.Args } // ListApplications lists all applications @@ -244,13 +244,11 @@ func (o *DeleteOptions) DeleteApp() (string, error) { // DeleteComponent will delete one component including server side. func (o *DeleteOptions) DeleteComponent(io cmdutil.IOStreams) (string, error) { - var app *api.Application var err error - if o.AppName != "" { - app, err = appfile.LoadApplication(o.Env.Name, o.AppName) - } else { - app, err = appfile.MatchAppByComp(o.Env.Name, o.CompName) + if o.AppName == "" { + return "", errors.New("app name is required") } + app, err := appfile.LoadApplication(o.Env.Namespace, o.AppName, o.C) if err != nil { return "", err } @@ -263,24 +261,15 @@ func (o *DeleteOptions) DeleteComponent(io cmdutil.IOStreams) (string, error) { if err := appfile.RemoveComponent(app, o.CompName); err != nil { return "", err } - if err := appfile.Save(app, o.Env.Name); err != nil { - return "", err - } // Remove component from appConfig in k8s cluster ctx := context.Background() - if err := BuildRun(ctx, app, o.Client, o.Env, io); err != nil { + + if err := o.Client.Update(ctx, app); err != nil { return "", err } - // Remove component in k8s cluster - var c corev1alpha2.Component - c.Name = o.CompName - c.Namespace = o.Env.Namespace - err = o.Client.Delete(context.Background(), &c) - if err != nil && !apierrors.IsNotFound(err) { - return "", fmt.Errorf("delete component err: %w", err) - } + // It's the server responsibility to GC component return fmt.Sprintf("component \"%s\" deleted from \"%s\"", o.CompName, o.AppName), nil } @@ -373,8 +362,8 @@ func saveAndLoadRemoteAppfile(url string) (*api.AppFile, error) { } // ExportFromAppFile exports Application from appfile object -func (o *AppfileOptions) ExportFromAppFile(app *api.AppFile, quiet bool) (*BuildResult, []byte, error) { - tm, err := template.Load() +func (o *AppfileOptions) ExportFromAppFile(app *api.AppFile, namespace string, quiet bool, c types.Args) (*BuildResult, []byte, error) { + tm, err := template.Load(namespace, c) if err != nil { return nil, nil, err } @@ -415,7 +404,7 @@ func (o *AppfileOptions) ExportFromAppFile(app *api.AppFile, quiet bool) (*Build } // Export export Application object from the path of Appfile -func (o *AppfileOptions) Export(filePath string, quiet bool) (*BuildResult, []byte, error) { +func (o *AppfileOptions) Export(filePath, namespace string, quiet bool, c types.Args) (*BuildResult, []byte, error) { var app *api.AppFile var err error if !quiet { @@ -424,9 +413,6 @@ func (o *AppfileOptions) Export(filePath string, quiet bool) (*BuildResult, []by if filePath != "" { if strings.HasPrefix(filePath, "https://") || strings.HasPrefix(filePath, "http://") { app, err = saveAndLoadRemoteAppfile(filePath) - if err != nil { - return nil, nil, err - } } else { app, err = api.LoadFromFile(filePath) } @@ -440,16 +426,16 @@ func (o *AppfileOptions) Export(filePath string, quiet bool) (*BuildResult, []by if !quiet { o.IO.Info("Load Template ...") } - return o.ExportFromAppFile(app, quiet) + return o.ExportFromAppFile(app, namespace, quiet, c) } // Run starts an application according to Appfile -func (o *AppfileOptions) Run(filePath string, config *rest.Config) error { - result, data, err := o.Export(filePath, false) +func (o *AppfileOptions) Run(filePath, namespace string, c types.Args) error { + result, data, err := o.Export(filePath, namespace, false, c) if err != nil { return err } - dm, err := discoverymapper.New(config) + dm, err := discoverymapper.New(c.Config) if err != nil { return err } diff --git a/references/common/capability.go b/references/common/capability.go index 2c4e57062..a412a14e4 100644 --- a/references/common/capability.go +++ b/references/common/capability.go @@ -259,9 +259,9 @@ func SyncCapabilityCenter(capabilityCenterName string) error { // RemoveCapabilityFromCluster will remove a capability from cluster. // 1. remove definition 2. uninstall chart 3. remove local files -func RemoveCapabilityFromCluster(client client.Client, capabilityName string) (string, error) { +func RemoveCapabilityFromCluster(userNamespace string, c types.Args, client client.Client, capabilityName string) (string, error) { ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} - if err := RemoveCapability(client, capabilityName, ioStreams); err != nil { + if err := RemoveCapability(userNamespace, c, client, capabilityName, ioStreams); err != nil { return "", err } msg := fmt.Sprintf("%s removed successfully", capabilityName) @@ -270,9 +270,9 @@ func RemoveCapabilityFromCluster(client client.Client, capabilityName string) (s // RemoveCapability will remove a capability from cluster. // 1. remove definition 2. uninstall chart 3. remove local files -func RemoveCapability(client client.Client, capabilityName string, ioStreams cmdutil.IOStreams) error { +func RemoveCapability(userNamespace string, c types.Args, client client.Client, capabilityName string, ioStreams cmdutil.IOStreams) error { // TODO(wonderflow): make sure no apps is using this capability - caps, err := plugins.LoadAllInstalledCapability() + caps, err := plugins.LoadAllInstalledCapability(userNamespace, c) if err != nil { return err } @@ -329,14 +329,14 @@ func uninstallCap(client client.Client, cap types.Capability, ioStreams cmdutil. } // ListCapabilities will list all caps from specified center -func ListCapabilities(capabilityCenterName string) ([]types.Capability, error) { +func ListCapabilities(userNamespace string, c types.Args, capabilityCenterName string) ([]types.Capability, error) { var capabilityList []types.Capability dir, err := system.GetCapCenterDir() if err != nil { return capabilityList, err } if capabilityCenterName != "" { - return listCenterCapabilities(filepath.Join(dir, capabilityCenterName)) + return listCenterCapabilities(userNamespace, c, filepath.Join(dir, capabilityCenterName)) } dirs, err := ioutil.ReadDir(dir) if err != nil { @@ -346,7 +346,7 @@ func ListCapabilities(capabilityCenterName string) ([]types.Capability, error) { if !dd.IsDir() { continue } - caps, err := listCenterCapabilities(filepath.Join(dir, dd.Name())) + caps, err := listCenterCapabilities(userNamespace, c, filepath.Join(dir, dd.Name())) if err != nil { return capabilityList, err } @@ -355,7 +355,7 @@ func ListCapabilities(capabilityCenterName string) ([]types.Capability, error) { return capabilityList, nil } -func listCenterCapabilities(repoDir string) ([]types.Capability, error) { +func listCenterCapabilities(userNamespace string, c types.Args, repoDir string) ([]types.Capability, error) { templates, err := plugins.LoadCapabilityFromSyncedCenter(repoDir) if err != nil { return templates, err @@ -364,9 +364,9 @@ func listCenterCapabilities(repoDir string) ([]types.Capability, error) { return templates, nil } baseDir := filepath.Base(repoDir) - workloads := gatherWorkloads(templates) + workloads := gatherWorkloads(userNamespace, c, templates) for i, p := range templates { - status := checkInstallStatus(baseDir, p) + status := checkInstallStatus(userNamespace, c, baseDir, p) convertedApplyTo := ConvertApplyTo(p.AppliesTo, workloads) templates[i].Center = baseDir templates[i].Status = status @@ -409,8 +409,8 @@ func RemoveCapabilityCenter(centerName string) (string, error) { return message, err } -func gatherWorkloads(templates []types.Capability) []types.Capability { - workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) +func gatherWorkloads(userNamespace string, c types.Args, templates []types.Capability) []types.Capability { + workloads, err := plugins.LoadInstalledCapabilityWithType(userNamespace, c, types.TypeWorkload) if err != nil { workloads = make([]types.Capability, 0) } @@ -422,9 +422,9 @@ func gatherWorkloads(templates []types.Capability) []types.Capability { return workloads } -func checkInstallStatus(repoName string, tmp types.Capability) string { +func checkInstallStatus(userNamespace string, c types.Args, repoName string, tmp types.Capability) string { var status = "uninstalled" - installed, _ := plugins.LoadInstalledCapabilityWithType(tmp.Type) + installed, _ := plugins.LoadInstalledCapabilityWithType(userNamespace, c, tmp.Type) for _, i := range installed { if i.Source != nil && i.Source.RepoName == repoName && i.Name == tmp.Name && i.CrdName == tmp.CrdName { return "installed" diff --git a/references/common/trait.go b/references/common/trait.go index 704c3aa42..5c558e8d6 100644 --- a/references/common/trait.go +++ b/references/common/trait.go @@ -1,49 +1,42 @@ package common import ( - "context" "fmt" "strings" - "cuelang.org/go/cue" plur "github.com/gertd/go-pluralize" - "github.com/spf13/pflag" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/types" - cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" - "github.com/oam-dev/kubevela/references/appfile" - "github.com/oam-dev/kubevela/references/appfile/api" "github.com/oam-dev/kubevela/references/plugins" ) // ListTraitDefinitions will list all definition include traits and workloads -func ListTraitDefinitions(workloadName *string) ([]types.Capability, error) { +func ListTraitDefinitions(userNamespace string, c types.Args, workloadName *string) ([]types.Capability, error) { var traitList []types.Capability - traits, err := plugins.LoadInstalledCapabilityWithType(types.TypeTrait) + traits, err := plugins.LoadInstalledCapabilityWithType(userNamespace, c, types.TypeTrait) if err != nil { return traitList, err } - workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) + workloads, err := plugins.LoadInstalledCapabilityWithType(userNamespace, c, types.TypeWorkload) if err != nil { return traitList, err } - traitList = convertAllAppliyToList(traits, workloads, workloadName) + traitList = convertAllApplyToList(traits, workloads, workloadName) return traitList, nil } // GetTraitDefinition will get trait capability with applyTo converted -func GetTraitDefinition(workloadName *string, traitType string) (types.Capability, error) { +func GetTraitDefinition(userNamespace string, c types.Args, workloadName *string, traitType string) (types.Capability, error) { var traitDef types.Capability traitCap, err := plugins.GetInstalledCapabilityWithCapName(types.TypeTrait, traitType) if err != nil { return traitDef, err } - workloadsCap, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload) + workloadsCap, err := plugins.LoadInstalledCapabilityWithType(userNamespace, c, types.TypeWorkload) if err != nil { return traitDef, err } - traitList := convertAllAppliyToList([]types.Capability{traitCap}, workloadsCap, workloadName) + traitList := convertAllApplyToList([]types.Capability{traitCap}, workloadsCap, workloadName) if len(traitList) != 1 { return traitDef, fmt.Errorf("could not get installed capability by %s", traitType) } @@ -51,7 +44,7 @@ func GetTraitDefinition(workloadName *string, traitType string) (types.Capabilit return traitDef, nil } -func convertAllAppliyToList(traits []types.Capability, workloads []types.Capability, workloadName *string) []types.Capability { +func convertAllApplyToList(traits []types.Capability, workloads []types.Capability, workloadName *string) []types.Capability { var traitList []types.Capability for _, t := range traits { convertedApplyTo := ConvertApplyTo(t.AppliesTo, workloads) @@ -118,112 +111,3 @@ func Parse(applyTo string) string { } return plur.NewClient().Plural(strings.ToLower(l[1])) + "." + apigroup } - -// ValidateAndMutateForCore was built in validate and mutate function for core workloads and traits -func ValidateAndMutateForCore(traitType, workloadName string, flags *pflag.FlagSet, env *types.EnvMeta) error { - switch traitType { - case "route": - domain, _ := flags.GetString("domain") - if domain == "" { - if env.Domain == "" { - return fmt.Errorf("--domain is required if not contain in environment") - } - if strings.HasPrefix(env.Domain, "https://") { - env.Domain = strings.TrimPrefix(env.Domain, "https://") - } - if strings.HasPrefix(env.Domain, "http://") { - env.Domain = strings.TrimPrefix(env.Domain, "http://") - } - if err := flags.Set("domain", workloadName+"."+env.Domain); err != nil { - return fmt.Errorf("set flag for vela-core trait('route') err %w, please make sure your template is right", err) - } - } - issuer, _ := flags.GetString("issuer") - if issuer == "" && env.Issuer != "" { - if err := flags.Set("issuer", env.Issuer); err != nil { - return fmt.Errorf("set flag for vela-core trait('route') err %w, please make sure your template is right", err) - } - } - default: - // extend other trait here in the future - } - return nil -} - -// AddOrUpdateTrait attach trait to workload -func AddOrUpdateTrait(env *types.EnvMeta, appName string, componentName string, flagSet *pflag.FlagSet, template types.Capability) (*api.Application, error) { - err := ValidateAndMutateForCore(template.Name, componentName, flagSet, env) - if err != nil { - return nil, err - } - if appName == "" { - appName = componentName - } - app, err := appfile.LoadApplication(env.Name, appName) - if err != nil { - return app, err - } - traitAlias := template.Name - traitData, err := appfile.GetTraitsByType(app, componentName, traitAlias) - if err != nil { - return app, err - } - for _, v := range template.Parameters { - name := v.Name - if v.Alias != "" { - name = v.Alias - } - // nolint:exhaustive - switch v.Type { - case cue.IntKind: - traitData[v.Name], err = flagSet.GetInt64(name) - case cue.StringKind: - traitData[v.Name], err = flagSet.GetString(name) - case cue.BoolKind: - traitData[v.Name], err = flagSet.GetBool(name) - case cue.NumberKind, cue.FloatKind: - traitData[v.Name], err = flagSet.GetFloat64(name) - default: - // Currently we don't support get value from complex type - continue - } - - if err != nil { - return nil, fmt.Errorf("get flag(s) \"%s\" err %w", name, err) - } - } - if err = appfile.SetTrait(app, componentName, traitAlias, traitData); err != nil { - return app, err - } - return app, appfile.Save(app, env.Name) -} - -// TraitOperationRun will check if it's a stage operation before run -func TraitOperationRun(ctx context.Context, c client.Client, env *types.EnvMeta, appObj *api.Application, - staging bool, io cmdutil.IOStreams) (string, error) { - if staging { - return "Staging saved", nil - } - err := BuildRun(ctx, appObj, c, env, io) - if err != nil { - return "", err - } - return "Deployed!", nil -} - -// PrepareDetachTrait will detach trait in local AppFile -func PrepareDetachTrait(envName string, traitType string, componentName string, appName string) (*api.Application, error) { - var appObj *api.Application - var err error - if appName == "" { - appName = componentName - } - if appObj, err = appfile.LoadApplication(envName, appName); err != nil { - return appObj, err - } - - if err = appfile.RemoveTrait(appObj, componentName, traitType); err != nil { - return appObj, err - } - return appObj, appfile.Save(appObj, envName) -} diff --git a/references/common/workload.go b/references/common/workload.go index 9cde486fa..3eca9cde9 100644 --- a/references/common/workload.go +++ b/references/common/workload.go @@ -1,7 +1,6 @@ package common import ( - "context" "fmt" "strconv" "strings" @@ -28,27 +27,20 @@ type RunOptions struct { util.IOStreams } -// LoadIfExist will load Application from local dir -func LoadIfExist(envName string, workloadName string, appGroup string) (*api.Application, error) { +// InitApplication will load Application from cluster +func InitApplication(env *types.EnvMeta, c types.Args, workloadName string, appGroup string) (*api.Application, error) { var appName string if appGroup != "" { appName = appGroup } else { appName = workloadName } - app, err := appfile.LoadApplication(envName, appName) - - // can't handle - if err != nil && !appfile.IsNotFound(appName, err) { - return nil, err - } - + // TODO(wonderflow): we should load the existing application from cluster and convert to appfile + // app, err := appfile.LoadApplication(env.Namespace, appName, c) // compatible application not found - if app == nil { - app, err = appfile.NewEmptyApplication() - if err != nil { - return nil, err - } + app, err := appfile.NewEmptyApplication(env.Namespace, c) + if err != nil { + return nil, err } app.Name = appName @@ -56,8 +48,8 @@ func LoadIfExist(envName string, workloadName string, appGroup string) (*api.App } // BaseComplete will construct an Application from cli parameters. -func BaseComplete(envName string, workloadName string, appName string, flagSet *pflag.FlagSet, workloadType string) (*api.Application, error) { - app, err := LoadIfExist(envName, workloadName, appName) +func BaseComplete(env *types.EnvMeta, c types.Args, workloadName string, appName string, flagSet *pflag.FlagSet, workloadType string) (*api.Application, error) { + app, err := InitApplication(env, c, workloadName, appName) if err != nil { return nil, err } @@ -69,7 +61,7 @@ func BaseComplete(envName string, workloadName string, appName string, flagSet * // Not exist tp = workloadType } - template, err := plugins.LoadCapabilityByName(tp) + template, err := plugins.LoadCapabilityByName(tp, env.Namespace, c) if err != nil { return nil, err } @@ -129,17 +121,5 @@ func BaseComplete(envName string, workloadName string, appName string, flagSet * if err = appfile.SetWorkload(app, workloadName, tp, workloadData); err != nil { return app, err } - return app, appfile.Save(app, envName) -} - -// BaseRun will check if it's a stating operation before run -func BaseRun(staging bool, app *api.Application, kubeClient client.Client, env *types.EnvMeta, io util.IOStreams) (string, error) { - if staging { - return "Staging saved", nil - } - if err := BuildRun(context.Background(), app, kubeClient, env, io); err != nil { - err = fmt.Errorf("create app err: %w", err) - return "", err - } - return fmt.Sprintf("App %s deployed", app.Name), nil + return app, appfile.Save(app, env.Name) } diff --git a/references/plugins/capcenter.go b/references/plugins/capcenter.go index f28f2197b..cf9a0bbe3 100644 --- a/references/plugins/capcenter.go +++ b/references/plugins/capcenter.go @@ -162,7 +162,7 @@ func StoreRepos(repos []CapCenterConfig) error { } // ParseAndSyncCapability will convert config from remote center to capability -func ParseAndSyncCapability(data []byte, syncDir string) (types.Capability, error) { +func ParseAndSyncCapability(data []byte) (types.Capability, error) { var obj = unstructured.Unstructured{Object: make(map[string]interface{})} err := yaml.Unmarshal(data, &obj.Object) if err != nil { @@ -175,14 +175,14 @@ func ParseAndSyncCapability(data []byte, syncDir string) (types.Capability, erro if err != nil { return types.Capability{}, err } - return HandleDefinition(rd.Name, syncDir, rd.Spec.Reference.Name, rd.Annotations, rd.Spec.Extension, types.TypeWorkload, nil, rd.Spec.Schematic) + return HandleDefinition(rd.Name, rd.Spec.Reference.Name, rd.Annotations, rd.Spec.Extension, types.TypeWorkload, nil, rd.Spec.Schematic) case "TraitDefinition": var td v1alpha2.TraitDefinition err = yaml.Unmarshal(data, &td) if err != nil { return types.Capability{}, err } - return HandleDefinition(td.Name, syncDir, td.Spec.Reference.Name, td.Annotations, td.Spec.Extension, types.TypeTrait, td.Spec.AppliesToWorkloads, td.Spec.Schematic) + return HandleDefinition(td.Name, td.Spec.Reference.Name, td.Annotations, td.Spec.Extension, types.TypeTrait, td.Spec.AppliesToWorkloads, td.Spec.Schematic) case "ScopeDefinition": // TODO(wonderflow): support scope definition here. } @@ -241,7 +241,7 @@ func (g *GithubCenter) SyncCapabilityFromCenter() error { return fmt.Errorf("decode github content %s err %w", *fileContent.Path, err) } } - tmp, err := ParseAndSyncCapability(data, filepath.Join(dir, ".tmp")) + tmp, err := ParseAndSyncCapability(data) if err != nil { fmt.Printf("parse definition of %s err %v\n", *fileContent.Name, err) continue diff --git a/references/plugins/cluster.go b/references/plugins/cluster.go index e1dbad075..2b0d7ef12 100644 --- a/references/plugins/cluster.go +++ b/references/plugins/cluster.go @@ -3,9 +3,7 @@ package plugins import ( "context" "fmt" - "io/ioutil" "os" - "path/filepath" "strings" "github.com/pkg/errors" @@ -20,7 +18,6 @@ import ( "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/pkg/utils/common" "github.com/oam-dev/kubevela/pkg/utils/helm" - "github.com/oam-dev/kubevela/pkg/utils/system" util2 "github.com/oam-dev/kubevela/pkg/utils/util" ) @@ -28,12 +25,12 @@ import ( const DescriptionUndefined = "description not defined" // GetCapabilitiesFromCluster will get capability from K8s cluster -func GetCapabilitiesFromCluster(ctx context.Context, namespace string, c types.Args, syncDir string, selector labels.Selector) ([]types.Capability, error) { - workloads, _, err := GetWorkloadsFromCluster(ctx, namespace, c, syncDir, selector) +func GetCapabilitiesFromCluster(ctx context.Context, namespace string, c types.Args, selector labels.Selector) ([]types.Capability, error) { + workloads, _, err := GetWorkloadsFromCluster(ctx, namespace, c, selector) if err != nil { return nil, err } - traits, _, err := GetTraitsFromCluster(ctx, namespace, c, syncDir, selector) + traits, _, err := GetTraitsFromCluster(ctx, namespace, c, selector) if err != nil { return nil, err } @@ -42,8 +39,8 @@ func GetCapabilitiesFromCluster(ctx context.Context, namespace string, c types.A } // GetWorkloadsFromCluster will get capability from K8s cluster -func GetWorkloadsFromCluster(ctx context.Context, namespace string, c types.Args, syncDir string, selector labels.Selector) ([]types.Capability, []error, error) { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) +func GetWorkloadsFromCluster(ctx context.Context, namespace string, c types.Args, selector labels.Selector) ([]types.Capability, []error, error) { + newClient, err := c.GetClient() if err != nil { return nil, nil, err } @@ -61,11 +58,12 @@ func GetWorkloadsFromCluster(ctx context.Context, namespace string, c types.Args var templateErrors []error for _, wd := range workloadDefs.Items { - tmp, err := HandleDefinition(wd.Name, syncDir, wd.Spec.Reference.Name, wd.Annotations, wd.Spec.Extension, types.TypeWorkload, nil, wd.Spec.Schematic) + tmp, err := HandleDefinition(wd.Name, wd.Spec.Reference.Name, wd.Annotations, wd.Spec.Extension, types.TypeWorkload, nil, wd.Spec.Schematic) if err != nil { templateErrors = append(templateErrors, errors.Wrapf(err, "handle workload template `%s` failed", wd.Name)) continue } + tmp.Namespace = namespace if tmp, err = validateCapabilities(tmp, dm, wd.Name, wd.Spec.Reference); err != nil { return nil, nil, err } @@ -75,8 +73,8 @@ func GetWorkloadsFromCluster(ctx context.Context, namespace string, c types.Args } // GetTraitsFromCluster will get capability from K8s cluster -func GetTraitsFromCluster(ctx context.Context, namespace string, c types.Args, syncDir string, selector labels.Selector) ([]types.Capability, []error, error) { - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) +func GetTraitsFromCluster(ctx context.Context, namespace string, c types.Args, selector labels.Selector) ([]types.Capability, []error, error) { + newClient, err := c.GetClient() if err != nil { return nil, nil, err } @@ -93,11 +91,12 @@ func GetTraitsFromCluster(ctx context.Context, namespace string, c types.Args, s var templateErrors []error for _, td := range traitDefs.Items { - tmp, err := HandleDefinition(td.Name, syncDir, td.Spec.Reference.Name, td.Annotations, td.Spec.Extension, types.TypeTrait, td.Spec.AppliesToWorkloads, td.Spec.Schematic) + tmp, err := HandleDefinition(td.Name, td.Spec.Reference.Name, td.Annotations, td.Spec.Extension, types.TypeTrait, td.Spec.AppliesToWorkloads, td.Spec.Schematic) if err != nil { templateErrors = append(templateErrors, errors.Wrapf(err, "handle trait template `%s` failed", td.Name)) continue } + tmp.Namespace = namespace if tmp, err = validateCapabilities(tmp, dm, td.Name, td.Spec.Reference); err != nil { return nil, nil, err } @@ -134,9 +133,9 @@ func validateCapabilities(tmp types.Capability, dm discoverymapper.DiscoveryMapp } // HandleDefinition will handle definition to capability -func HandleDefinition(name, syncDir, crdName string, annotation map[string]string, extension *runtime.RawExtension, tp types.CapType, applyTo []string, schematic *corev1alpha2.Schematic) (types.Capability, error) { +func HandleDefinition(name, crdName string, annotation map[string]string, extension *runtime.RawExtension, tp types.CapType, applyTo []string, schematic *corev1alpha2.Schematic) (types.Capability, error) { var tmp types.Capability - tmp, err := HandleTemplate(extension, schematic, name, syncDir) + tmp, err := HandleTemplate(extension, schematic, name) if err != nil { return types.Capability{}, err } @@ -162,7 +161,7 @@ func GetDescription(annotation map[string]string) string { } // HandleTemplate will handle definition template to capability -func HandleTemplate(in *runtime.RawExtension, schematic *corev1alpha2.Schematic, name, syncDir string) (types.Capability, error) { +func HandleTemplate(in *runtime.RawExtension, schematic *corev1alpha2.Schematic, name string) (types.Capability, error) { tmp, err := util.ConvertTemplateJSON2Object(name, in, schematic) if err != nil { return types.Capability{}, err @@ -183,15 +182,10 @@ func HandleTemplate(in *runtime.RawExtension, schematic *corev1alpha2.Schematic, if tmp.CueTemplate == "" { return types.Capability{}, errors.New("template not exist in definition") } - _, _ = system.CreateIfNotExist(syncDir) - filePath := filepath.Join(syncDir, name+".cue") - //nolint:gosec - err = ioutil.WriteFile(filePath, []byte(tmp.CueTemplate), 0644) if err != nil { return types.Capability{}, err } - tmp.DefinitionPath = filePath - tmp.Parameters, err = cue.GetParameters(filePath) + tmp.Parameters, err = cue.GetParameters(tmp.CueTemplate) if err != nil { return types.Capability{}, err } @@ -203,7 +197,7 @@ func SyncDefinitionsToLocal(ctx context.Context, c types.Args, localDefinitionDi var syncedTemplates []types.Capability var warnings []string - templates, templateErrors, err := GetWorkloadsFromCluster(ctx, types.DefaultKubeVelaNS, c, localDefinitionDir, nil) + templates, templateErrors, err := GetWorkloadsFromCluster(ctx, types.DefaultKubeVelaNS, c, nil) if err != nil { return nil, nil, err } @@ -215,7 +209,7 @@ func SyncDefinitionsToLocal(ctx context.Context, c types.Args, localDefinitionDi syncedTemplates = append(syncedTemplates, templates...) SinkTemp2Local(templates, localDefinitionDir) - templates, templateErrors, err = GetTraitsFromCluster(ctx, types.DefaultKubeVelaNS, c, localDefinitionDir, nil) + templates, templateErrors, err = GetTraitsFromCluster(ctx, types.DefaultKubeVelaNS, c, nil) if err != nil { return nil, warnings, err } @@ -233,7 +227,7 @@ func SyncDefinitionsToLocal(ctx context.Context, c types.Args, localDefinitionDi func SyncDefinitionToLocal(ctx context.Context, c types.Args, localDefinitionDir string, capabilityName string) (*types.Capability, error) { var foundCapability bool - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) + newClient, err := c.GetClient() if err != nil { return nil, err } @@ -244,7 +238,7 @@ func SyncDefinitionToLocal(ctx context.Context, c types.Args, localDefinitionDir foundCapability = true } if foundCapability { - template, err := HandleDefinition(capabilityName, localDefinitionDir, workloadDef.Spec.Reference.Name, + template, err := HandleDefinition(capabilityName, workloadDef.Spec.Reference.Name, workloadDef.Annotations, workloadDef.Spec.Extension, types.TypeWorkload, nil, workloadDef.Spec.Schematic) if err == nil { return &template, nil @@ -258,7 +252,7 @@ func SyncDefinitionToLocal(ctx context.Context, c types.Args, localDefinitionDir foundCapability = true } if foundCapability { - template, err := HandleDefinition(capabilityName, localDefinitionDir, traitDef.Spec.Reference.Name, + template, err := HandleDefinition(capabilityName, traitDef.Spec.Reference.Name, traitDef.Annotations, traitDef.Spec.Extension, types.TypeTrait, nil, workloadDef.Spec.Schematic) if err == nil { return &template, nil diff --git a/references/plugins/cluster_test.go b/references/plugins/cluster_test.go index 2ee1cedcb..3d99a467d 100644 --- a/references/plugins/cluster_test.go +++ b/references/plugins/cluster_test.go @@ -108,7 +108,7 @@ var _ = Describe("DefinitionFiles", func() { // Notice!! DefinitionPath Object is Cluster Scope object // which means objects created in other DefinitionNamespace will also affect here. It("gettrait", func() { - traitDefs, _, err := GetTraitsFromCluster(context.Background(), DefinitionNamespace, types.Args{Config: cfg, Schema: scheme}, definitionDir, selector) + traitDefs, _, err := GetTraitsFromCluster(context.Background(), DefinitionNamespace, types.Args{Config: cfg, Schema: scheme}, selector) Expect(err).Should(BeNil()) logf.Log.Info(fmt.Sprintf("Getting trait definitions %v", traitDefs)) for i := range traitDefs { @@ -116,7 +116,6 @@ var _ = Describe("DefinitionFiles", func() { By("check CueTemplate is fulfilled") Expect(traitDefs[i].CueTemplate).ShouldNot(BeEmpty()) traitDefs[i].CueTemplate = "" - traitDefs[i].DefinitionPath = "" } Expect(traitDefs).Should(Equal([]types.Capability{route})) }) @@ -124,7 +123,7 @@ var _ = Describe("DefinitionFiles", func() { // Notice!! DefinitionPath Object is Cluster Scope object // which means objects created in other DefinitionNamespace will also affect here. It("getworkload", func() { - workloadDefs, _, err := GetWorkloadsFromCluster(context.Background(), DefinitionNamespace, types.Args{Config: cfg, Schema: scheme}, definitionDir, selector) + workloadDefs, _, err := GetWorkloadsFromCluster(context.Background(), DefinitionNamespace, types.Args{Config: cfg, Schema: scheme}, selector) Expect(err).Should(BeNil()) logf.Log.Info(fmt.Sprintf("Getting workload definitions %v", workloadDefs)) for i := range workloadDefs { @@ -132,17 +131,15 @@ var _ = Describe("DefinitionFiles", func() { By("check CueTemplate is fulfilled") Expect(workloadDefs[i].CueTemplate).ShouldNot(BeEmpty()) workloadDefs[i].CueTemplate = "" - workloadDefs[i].DefinitionPath = "" } Expect(workloadDefs).Should(Equal([]types.Capability{deployment, websvc})) }) It("getall", func() { - alldef, err := GetCapabilitiesFromCluster(context.Background(), DefinitionNamespace, types.Args{Config: cfg, Schema: scheme}, definitionDir, selector) + alldef, err := GetCapabilitiesFromCluster(context.Background(), DefinitionNamespace, types.Args{Config: cfg, Schema: scheme}, selector) Expect(err).Should(BeNil()) logf.Log.Info(fmt.Sprintf("Getting all definitions %v", alldef)) for i := range alldef { alldef[i].CueTemplate = "" - alldef[i].DefinitionPath = "" } Expect(alldef).Should(Equal([]types.Capability{deployment, websvc, route})) }) diff --git a/references/plugins/local.go b/references/plugins/local.go index 06504f926..53fca848e 100644 --- a/references/plugins/local.go +++ b/references/plugins/local.go @@ -2,6 +2,7 @@ package plugins import ( "bytes" + "context" "encoding/json" "fmt" "io/ioutil" @@ -14,8 +15,8 @@ import ( ) // LoadCapabilityByName will load capability from local by name -func LoadCapabilityByName(name string) (types.Capability, error) { - caps, err := LoadAllInstalledCapability() +func LoadCapabilityByName(name string, userNamespace string, c types.Args) (types.Capability, error) { + caps, err := LoadAllInstalledCapability(userNamespace, c) if err != nil { return types.Capability{}, err } @@ -28,26 +29,49 @@ func LoadCapabilityByName(name string) (types.Capability, error) { } // LoadAllInstalledCapability will list all capability -func LoadAllInstalledCapability() ([]types.Capability, error) { - workloads, err := LoadInstalledCapabilityWithType(types.TypeWorkload) +func LoadAllInstalledCapability(userNamespace string, c types.Args) ([]types.Capability, error) { + caps, err := GetCapabilitiesFromCluster(context.TODO(), userNamespace, c, nil) if err != nil { return nil, err } - traits, err := LoadInstalledCapabilityWithType(types.TypeTrait) + systemCaps, err := GetCapabilitiesFromCluster(context.TODO(), types.DefaultKubeVelaNS, c, nil) if err != nil { return nil, err } - workloads = append(workloads, traits...) - return workloads, nil + caps = append(caps, systemCaps...) + return caps, nil } // LoadInstalledCapabilityWithType will load cap list by type -func LoadInstalledCapabilityWithType(capT types.CapType) ([]types.Capability, error) { - dir, err := system.GetCapabilityDir() - if err != nil { - return nil, err +func LoadInstalledCapabilityWithType(userNamespace string, c types.Args, capT types.CapType) ([]types.Capability, error) { + switch capT { + case types.TypeWorkload: + caps, _, err := GetWorkloadsFromCluster(context.TODO(), userNamespace, c, nil) + if err != nil { + return nil, err + } + systemCaps, _, err := GetWorkloadsFromCluster(context.TODO(), types.DefaultKubeVelaNS, c, nil) + if err != nil { + return nil, err + } + caps = append(caps, systemCaps...) + return caps, nil + case types.TypeTrait: + caps, _, err := GetTraitsFromCluster(context.TODO(), userNamespace, c, nil) + if err != nil { + return nil, err + } + systemCaps, _, err := GetTraitsFromCluster(context.TODO(), types.DefaultKubeVelaNS, c, nil) + if err != nil { + return nil, err + } + caps = append(caps, systemCaps...) + return caps, nil + case types.TypeScope: + } - return loadInstalledCapabilityWithType(dir, capT) + + return nil, nil } // GetInstalledCapabilityWithCapName will get cap by alias @@ -210,7 +234,7 @@ func LoadCapabilityFromSyncedCenter(dir string) ([]types.Capability, error) { fmt.Printf("read file %s err %v\n", f.Name(), err) continue } - tmp, err := ParseAndSyncCapability(data, filepath.Join(dir, ".tmp")) + tmp, err := ParseAndSyncCapability(data) if err != nil { fmt.Printf("get definition of %s err %v\n", f.Name(), err) continue diff --git a/references/plugins/references.go b/references/plugins/references.go index bd24bc4d0..f350bcaac 100644 --- a/references/plugins/references.go +++ b/references/plugins/references.go @@ -183,7 +183,11 @@ func setDisplayFormat(format string) { // GenerateReferenceDocs generates reference docs func (ref *MarkdownReference) GenerateReferenceDocs(baseRefPath string) error { - caps, err := LoadAllInstalledCapability() + c, err := common.InitBaseRestConfig() + if err != nil { + return err + } + caps, err := LoadAllInstalledCapability("default", c) if err != nil { return fmt.Errorf("failed to generate reference docs for all capabilities: %w", err) } From 2d7cbf94678877804a7ed0c30d68dd0cbeda87db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E5=85=83?= Date: Fri, 12 Mar 2021 12:23:58 +0800 Subject: [PATCH 5/5] faster vela traits/workloads --- references/appfile/modify.go | 1 - references/cli/exec.go | 3 +- references/cli/exec_test.go | 58 ---------------------------- references/cli/ls.go | 3 +- references/cli/portforward_test.go | 62 ------------------------------ references/cli/traits.go | 17 ++++---- references/cli/workloads.go | 20 +++++----- references/common/trait.go | 41 ++++++++++++++++++++ references/plugins/cluster.go | 1 + 9 files changed, 62 insertions(+), 144 deletions(-) delete mode 100644 references/cli/exec_test.go delete mode 100644 references/cli/portforward_test.go diff --git a/references/appfile/modify.go b/references/appfile/modify.go index cf47795b2..20d7b70a9 100644 --- a/references/appfile/modify.go +++ b/references/appfile/modify.go @@ -8,7 +8,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" - "github.com/oam-dev/kubevela/references/appfile/api" ) diff --git a/references/cli/exec.go b/references/cli/exec.go index 56cc10ea9..72d14b9d2 100644 --- a/references/cli/exec.go +++ b/references/cli/exec.go @@ -6,8 +6,6 @@ import ( "strings" "time" - "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" - "github.com/spf13/cobra" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -16,6 +14,7 @@ import ( cmdexec "k8s.io/kubectl/pkg/cmd/exec" k8scmdutil "k8s.io/kubectl/pkg/cmd/util" + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/utils/common" diff --git a/references/cli/exec_test.go b/references/cli/exec_test.go deleted file mode 100644 index 1ea4a08b9..000000000 --- a/references/cli/exec_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package cli - -import ( - "context" - "fmt" - "os" - "testing" - - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/kubectl/pkg/cmd/exec" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" - - "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/oam" - cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" -) - -func TestExecCommand(t *testing.T) { - tf := cmdtesting.NewTestFactory() - defer tf.Cleanup() - io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} - fakeC := types.Args{ - Config: tf.ClientConfigVal, - } - cmd := NewExecCommand(fakeC, io) - cmd.PersistentFlags().StringP("env", "e", "", "") - o := &VelaExecOptions{ - kcExecOptions: &exec.ExecOptions{}, - f: tf, - ClientSet: fake.NewSimpleClientset(&corev1.PodList{ - Items: []corev1.Pod{ - { - ObjectMeta: v1.ObjectMeta{ - Name: "fakePod", - Namespace: "default", - Labels: map[string]string{ - oam.LabelAppName: "fakeApp", - oam.LabelAppComponent: "fakeComp", - }}, - }, - }, - }), - } - err := o.Init(context.Background(), cmd, []string{"fakeApp"}) - errString := fmt.Sprintf(`application "%s" not found`, "fakeApp") - assert.EqualError(t, err, errString) -} - -func TestExecCommandPersistentPreRunE(t *testing.T) { - io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} - fakeC := types.Args{} - cmd := NewExecCommand(fakeC, io) - assert.Nil(t, cmd.PersistentPreRunE(new(cobra.Command), []string{})) -} diff --git a/references/cli/ls.go b/references/cli/ls.go index 4ccd4190f..515f3d93b 100644 --- a/references/cli/ls.go +++ b/references/cli/ls.go @@ -4,9 +4,8 @@ import ( "context" "strings" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "github.com/spf13/cobra" + apierrors "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" diff --git a/references/cli/portforward_test.go b/references/cli/portforward_test.go deleted file mode 100644 index ca7bdd831..000000000 --- a/references/cli/portforward_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package cli - -import ( - "context" - "fmt" - "os" - "testing" - - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8sfake "k8s.io/client-go/kubernetes/fake" - "k8s.io/kubectl/pkg/cmd/portforward" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" - - "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/oam" - cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" -) - -func TestPortForwardCommand(t *testing.T) { - fakePod := corev1.Pod{ - ObjectMeta: v1.ObjectMeta{ - Name: "fakePod", - Namespace: "default", - ResourceVersion: "10", - Labels: map[string]string{ - oam.LabelAppComponent: "fakeComp", - }}, - } - tf := cmdtesting.NewTestFactory() - defer tf.Cleanup() - - io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} - fakeC := types.Args{ - Config: tf.ClientConfigVal, - } - cmd := NewPortForwardCommand(fakeC, io) - cmd.PersistentFlags().StringP("env", "e", "", "") - fakeClientSet := k8sfake.NewSimpleClientset(&corev1.PodList{ - Items: []corev1.Pod{fakePod}, - }) - - o := &VelaPortForwardOptions{ - ioStreams: io, - kcPortForwardOptions: &portforward.PortForwardOptions{}, - f: tf, - ClientSet: fakeClientSet, - VelaC: fakeC, - } - err := o.Init(context.Background(), cmd, []string{"fakeApp", "8081:8080"}) - errString := fmt.Sprintf(`application "%s" not found`, "fakeApp") - assert.EqualError(t, err, errString) -} - -func TestNewPortForwardCommandPersistentPreRunE(t *testing.T) { - io := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} - fakeC := types.Args{} - cmd := NewPortForwardCommand(fakeC, io) - assert.Nil(t, cmd.PersistentPreRunE(new(cobra.Command), []string{})) -} diff --git a/references/cli/traits.go b/references/cli/traits.go index 8f93f7a13..d9f606b69 100644 --- a/references/cli/traits.go +++ b/references/cli/traits.go @@ -8,13 +8,13 @@ import ( "github.com/oam-dev/kubevela/apis/types" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" "github.com/oam-dev/kubevela/references/common" + "github.com/oam-dev/kubevela/references/plugins" ) // NewTraitsCommand creates `traits` command func NewTraitsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { - var workloadName string cmd := &cobra.Command{ - Use: "traits [--apply-to WORKLOAD_NAME]", + Use: "traits", DisableFlagsInUseLine: true, Short: "List traits", Long: "List traits", @@ -27,7 +27,7 @@ func NewTraitsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command if err != nil { return err } - return printTraitList(env.Namespace, c, &workloadName, ioStreams) + return printTraitList(env.Namespace, c, ioStreams) }, Annotations: map[string]string{ types.TagCommandType: types.TypeCap, @@ -35,21 +35,20 @@ func NewTraitsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command } cmd.SetOut(ioStreams.Out) - cmd.Flags().StringVar(&workloadName, "apply-to", "", "Workload name") return cmd } -func printTraitList(userNamespace string, c types.Args, workloadName *string, ioStreams cmdutil.IOStreams) error { +func printTraitList(userNamespace string, c types.Args, ioStreams cmdutil.IOStreams) error { table := newUITable() - table.MaxColWidth = 120 table.Wrap = true - traitDefinitionList, err := common.ListTraitDefinitions(userNamespace, c, workloadName) + + traitDefinitionList, err := common.ListRawTraitDefinitions(userNamespace, c) if err != nil { return err } - table.AddRow("NAME", "DESCRIPTION", "APPLIES TO") + table.AddRow("NAME", "NAMESPACE", "APPLIES-TO", "CONFLICTS-WITH", "DESCRIPTION") for _, t := range traitDefinitionList { - table.AddRow(t.Name, t.Description, strings.Join(t.AppliesTo, "\n")) + table.AddRow(t.Name, t.Namespace, strings.Join(t.Spec.AppliesToWorkloads, ","), strings.Join(t.Spec.ConflictsWith, ","), plugins.GetDescription(t.Annotations)) } ioStreams.Info(table.String()) return nil diff --git a/references/cli/workloads.go b/references/cli/workloads.go index acc7e1903..d1f82452f 100644 --- a/references/cli/workloads.go +++ b/references/cli/workloads.go @@ -5,6 +5,7 @@ import ( "github.com/oam-dev/kubevela/apis/types" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" + "github.com/oam-dev/kubevela/references/common" "github.com/oam-dev/kubevela/references/plugins" ) @@ -24,11 +25,7 @@ func NewWorkloadsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comma if err != nil { return err } - workloads, err := plugins.LoadInstalledCapabilityWithType(env.Namespace, c, types.TypeWorkload) - if err != nil { - return err - } - return printWorkloadList(workloads, ioStreams) + return printWorkloadList(env.Namespace, c, ioStreams) }, Annotations: map[string]string{ types.TagCommandType: types.TypeCap, @@ -38,12 +35,15 @@ func NewWorkloadsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comma return cmd } -func printWorkloadList(workloadList []types.Capability, ioStreams cmdutil.IOStreams) error { +func printWorkloadList(userNamespace string, c types.Args, ioStreams cmdutil.IOStreams) error { + def, err := common.ListRawWorkloadDefinitions(userNamespace, c) + if err != nil { + return err + } table := newUITable() - table.MaxColWidth = 120 - table.AddRow("NAME", "DESCRIPTION") - for _, r := range workloadList { - table.AddRow(r.Name, r.Description) + table.AddRow("NAME", "NAMESPACE", "WORKLOAD", "DESCRIPTION") + for _, r := range def { + table.AddRow(r.Name, r.Namespace, r.Spec.Reference.Name, plugins.GetDescription(r.Annotations)) } ioStreams.Info(table.String()) return nil diff --git a/references/common/trait.go b/references/common/trait.go index 5c558e8d6..5ed3d9cd4 100644 --- a/references/common/trait.go +++ b/references/common/trait.go @@ -1,12 +1,17 @@ package common import ( + "context" "fmt" "strings" plur "github.com/gertd/go-pluralize" + client2 "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/apis/types" + "github.com/oam-dev/kubevela/pkg/oam" + "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/references/plugins" ) @@ -25,6 +30,42 @@ func ListTraitDefinitions(userNamespace string, c types.Args, workloadName *stri return traitList, nil } +// ListRawTraitDefinitions will list raw definition +func ListRawTraitDefinitions(userNamespace string, c types.Args) ([]v1alpha2.TraitDefinition, error) { + client, err := c.GetClient() + if err != nil { + return nil, err + } + ctx := util.SetNamespaceInCtx(context.Background(), userNamespace) + traitList := v1alpha2.TraitDefinitionList{} + if err = client.List(ctx, &traitList, client2.InNamespace(userNamespace)); err != nil { + return nil, err + } + sysTraitList := v1alpha2.TraitDefinitionList{} + if err = client.List(ctx, &sysTraitList, client2.InNamespace(oam.SystemDefinitonNamespace)); err != nil { + return nil, err + } + return append(traitList.Items, sysTraitList.Items...), nil +} + +// ListRawWorkloadDefinitions will list raw definition +func ListRawWorkloadDefinitions(userNamespace string, c types.Args) ([]v1alpha2.WorkloadDefinition, error) { + client, err := c.GetClient() + if err != nil { + return nil, err + } + ctx := util.SetNamespaceInCtx(context.Background(), userNamespace) + workloadList := v1alpha2.WorkloadDefinitionList{} + if err = client.List(ctx, &workloadList); err != nil { + return nil, err + } + sysWorkloadList := v1alpha2.WorkloadDefinitionList{} + if err = client.List(ctx, &sysWorkloadList, client2.InNamespace(oam.SystemDefinitonNamespace)); err != nil { + return nil, err + } + return append(workloadList.Items, sysWorkloadList.Items...), nil +} + // GetTraitDefinition will get trait capability with applyTo converted func GetTraitDefinition(userNamespace string, c types.Args, workloadName *string, traitType string) (types.Capability, error) { var traitDef types.Capability diff --git a/references/plugins/cluster.go b/references/plugins/cluster.go index 2b0d7ef12..e9b192e20 100644 --- a/references/plugins/cluster.go +++ b/references/plugins/cluster.go @@ -157,6 +157,7 @@ func GetDescription(annotation map[string]string) string { if !ok { return DescriptionUndefined } + desc = strings.ReplaceAll(desc, "\n", " ") return desc }