From 865c52e883ccc89fb981c7124049710004279cde Mon Sep 17 00:00:00 2001 From: zzxwill Date: Fri, 30 Oct 2020 18:44:55 +0800 Subject: [PATCH 1/3] Refactor "vela status" merged `vela app status` and `vela svc status` to `vela status`. To fix #474 --- e2e/application/application_test.go | 4 +- e2e/commonContext.go | 6 +- pkg/commands/app.go | 1 - pkg/commands/cli.go | 1 + pkg/commands/comp.go | 2 - pkg/commands/show.go | 56 +--------- pkg/commands/status.go | 161 +++++----------------------- pkg/oam/application.go | 60 +++++++++++ 8 files changed, 95 insertions(+), 196 deletions(-) diff --git a/e2e/application/application_test.go b/e2e/application/application_test.go index 098177a69..a17d4cabf 100644 --- a/e2e/application/application_test.go +++ b/e2e/application/application_test.go @@ -26,8 +26,8 @@ var _ = ginkgo.Describe("Application", func() { e2e.ComponentListContext("ls", applicationName, "") e2e.TraitManualScalerAttachContext("vela attach scaler trait", traitAlias, applicationName) e2e.ApplicationShowContext("show", applicationName, workloadType) - e2e.ApplicationStatusContext("app status", applicationName, workloadType) - e2e.ApplicationCompStatusContext("svc status", applicationName, workloadType, envName) + e2e.ApplicationStatusContext("status", applicationName, workloadType) + e2e.ApplicationStatusDeeplyContext("status", applicationName, workloadType, envName) e2e.ApplicationExecContext("exec -- COMMAND", applicationName) e2e.ApplicationPortForwardContext("port-forward", applicationName) e2e.ApplicationInitIntercativeCliContext("init", appNameForInit, workloadType) diff --git a/e2e/commonContext.go b/e2e/commonContext.go index 003b8e6f5..76111f9b8 100644 --- a/e2e/commonContext.go +++ b/e2e/commonContext.go @@ -183,7 +183,7 @@ var ( ApplicationStatusContext = func(context string, applicationName string, workloadType string) bool { return ginkgo.Context(context, func() { ginkgo.It("should get status for the application", func() { - cli := fmt.Sprintf("vela app status %s", applicationName) + cli := fmt.Sprintf("vela status %s", applicationName) output, err := Exec(cli) gomega.Expect(err).NotTo(gomega.HaveOccurred()) gomega.Expect(output).To(gomega.ContainSubstring(applicationName)) @@ -192,7 +192,7 @@ var ( }) } - ApplicationCompStatusContext = func(context string, applicationName, workloadType, envName string) bool { + ApplicationStatusDeeplyContext = func(context string, applicationName, workloadType, envName string) bool { return ginkgo.Context(context, func() { ginkgo.It("should get status of the service", func() { ginkgo.By("init new k8s client") @@ -206,7 +206,7 @@ var ( return len(appConfig.Status.Workloads) }, 90*time.Second, 1*time.Second).ShouldNot(gomega.Equal(0)) - cli := fmt.Sprintf("vela svc status %s", applicationName) + cli := fmt.Sprintf("vela status %s", applicationName) output, err := LongTimeExec(cli, 120*time.Second) gomega.Expect(err).NotTo(gomega.HaveOccurred()) gomega.Expect(output).To(gomega.ContainSubstring("Checking health status")) diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 2a705dcd1..b0e3a7e09 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -20,7 +20,6 @@ func NewAppsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { } cmd.AddCommand( - NewAppStatusCommand(c, ioStreams), NewRunCommand(c, ioStreams)) return cmd } diff --git a/pkg/commands/cli.go b/pkg/commands/cli.go index 339ccdccd..08a4721c0 100644 --- a/pkg/commands/cli.go +++ b/pkg/commands/cli.go @@ -73,6 +73,7 @@ func NewCommand() *cobra.Command { NewListCommand(commandArgs, ioStream), NewDeleteCommand(commandArgs, ioStream), NewAppShowCommand(ioStream), + NewAppStatusCommand(commandArgs, ioStream), // Workloads AddCompCommands(commandArgs, ioStream), diff --git a/pkg/commands/comp.go b/pkg/commands/comp.go index 033c99e19..f8fc638c0 100644 --- a/pkg/commands/comp.go +++ b/pkg/commands/comp.go @@ -41,9 +41,7 @@ func AddCompCommands(c types.Args, ioStreams util.IOStreams) *cobra.Command { compCommands.PersistentFlags().StringP(App, "a", "", "specify the name of application containing the services") compCommands.AddCommand( - NewCompDeployCommands(c, ioStreams), - NewCompStatusCommand(c, ioStreams), ) return compCommands } diff --git a/pkg/commands/show.go b/pkg/commands/show.go index dc9d23f2c..aae587fc2 100644 --- a/pkg/commands/show.go +++ b/pkg/commands/show.go @@ -6,7 +6,6 @@ import ( "github.com/oam-dev/kubevela/pkg/oam" - "github.com/AlecAivazis/survey/v2" "github.com/gosuri/uitable" "github.com/oam-dev/kubevela/api/types" "github.com/oam-dev/kubevela/pkg/application" @@ -49,45 +48,7 @@ func showApplication(cmd *cobra.Command, env *types.EnvMeta, appName string) err if err != nil { return err } - - var svcFlag, chosenSvc string - var svcFlagStatus string - // to store the value of flag `--svc` set in Cli, or selected value in survey - var targetServices []string - if svcFlag = cmd.Flag("svc").Value.String(); svcFlag == "" { - svcFlagStatus = oam.FlagNotSet - } else { - svcFlagStatus = oam.FlagIsInvalid - } - // all services name of the application `appName` - var services []string - for svcName := range app.Services { - services = append(services, svcName) - if svcFlag == svcName { - svcFlagStatus = oam.FlagIsValid - targetServices = append(targetServices, svcName) - } - } - totalServices := len(services) - if svcFlagStatus == oam.FlagNotSet && totalServices == 1 { - targetServices = services - } - if svcFlagStatus == oam.FlagIsInvalid || (svcFlagStatus == oam.FlagNotSet && totalServices > 1) { - if svcFlagStatus == oam.FlagIsInvalid { - cmd.Printf("The service name '%s' is not valid\n", svcFlag) - } - chosenSvc, err = chooseSvc(services) - if err != nil { - return err - } - - if chosenSvc == oam.DefaultChosenAllSvc { - targetServices = services - } else { - targetServices = targetServices[:0] - targetServices = append(targetServices, chosenSvc) - } - } + targetServices, err := oam.GetServicesWhenDescribingApplication(cmd, app) cmd.Printf("About:\n\n") table := uitable.New() @@ -160,18 +121,3 @@ func showComponent(cmd *cobra.Command, env *types.EnvMeta, compName, appName str } return nil } - -func chooseSvc(services []string) (string, error) { - var svcName string - services = append(services, oam.DefaultChosenAllSvc) - prompt := &survey.Select{ - Message: "Please choose one service: ", - Options: services, - Default: oam.DefaultChosenAllSvc, - } - err := survey.AskOne(prompt, &svcName) - if err != nil { - return "", fmt.Errorf("failed to retrieve services of the application, err %v", err) - } - return svcName, nil -} diff --git a/pkg/commands/status.go b/pkg/commands/status.go index d4bd967f1..90f167c27 100644 --- a/pkg/commands/status.go +++ b/pkg/commands/status.go @@ -21,7 +21,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/api/types" - "github.com/oam-dev/kubevela/pkg/appfile" "github.com/oam-dev/kubevela/pkg/application" cmdutil "github.com/oam-dev/kubevela/pkg/commands/util" oam2 "github.com/oam-dev/kubevela/pkg/oam" @@ -78,14 +77,7 @@ const ( ErrServiceNotFound = "service %s not found in app" ) -const ( - firstElemPrefix = `├─` - lastElemPrefix = `└─` - pipe = `│ ` -) - var ( - gray = color.New(color.FgHiBlack) red = color.New(color.FgRed) green = color.New(color.FgGreen) yellow = color.New(color.FgYellow) @@ -131,129 +123,45 @@ func NewAppStatusCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comma if err != nil { return err } - return printAppStatus(ctx, newClient, ioStreams, appName, env) + return printAppStatus(ctx, newClient, ioStreams, appName, env, cmd) }, Annotations: map[string]string{ types.TagCommandType: types.TypeApp, }, } + cmd.Flags().StringP("svc", "s", "", "service name") cmd.SetOut(ioStreams.Out) return cmd } -func printAppStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams, appName string, env *types.EnvMeta) error { +func printAppStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams, appName string, env *types.EnvMeta, cmd *cobra.Command) error { app, err := application.Load(env.Name, appName) if err != nil { return err } namespace := env.Name - tbl := uitable.New() - tbl.Separator = " " - tbl.AddRow( - white.Sprint("NAMESPCAE"), - white.Sprint("NAME"), - white.Sprint("INFO")) - tbl.AddRow( - namespace, - fmt.Sprintf("%s/%s", - "Application", - appName)) + targetServices, err := oam2.GetServicesWhenDescribingApplication(cmd, app) - components := app.GetComponents() - // get a map coantaining all workloads health condition - wlConditionsMap, err := getWorkloadHealthConditions(ctx, c, app, namespace) - if err != nil { - return err - } + cmd.Printf("About:\n\n") + table := uitable.New() + table.AddRow(" Name:", appName) + table.AddRow(" Namespace:", namespace) + table.AddRow(" Created at:", app.CreateTime.String()) + table.AddRow(" Updated at:", app.UpdateTime.String()) + cmd.Printf("%s\n\n", table.String()) - for cIndex, compName := range components { - var cPrefix string - switch cIndex { - case len(components) - 1: - cPrefix = lastElemPrefix - default: - cPrefix = firstElemPrefix + cmd.Printf("Services:\n\n") + + for _, svcName := range targetServices { + if printComponentStatus(ctx, c, ioStreams, svcName, appName, env); err != nil { + return err } - - wlHealthCondition := wlConditionsMap[compName] - wlHealthStatus := wlHealthCondition.HealthStatus - healthColor := getHealthStatusColor(wlHealthStatus) - - // print component info - tbl.AddRow("", - fmt.Sprintf("%s%s/%s", - gray.Sprint(printPrefix(cPrefix)), - "Component", - compName), - healthColor.Sprintf("%s %s", wlHealthStatus, wlHealthCondition.Diagnosis)) } - ioStreams.Info(tbl) + return nil } -// map componentName <=> WorkloadHealthCondition -func getWorkloadHealthConditions(ctx context.Context, c client.Client, app *application.Application, ns string) (map[string]*WorkloadHealthCondition, error) { - hs := &v1alpha2.HealthScope{} - // only use default health scope - hsName := appfile.FormatDefaultHealthScopeName(app.Name) - if err := c.Get(ctx, client.ObjectKey{Namespace: ns, Name: hsName}, hs); err != nil { - return nil, err - } - wlConditions := hs.Status.WorkloadHealthConditions - r := map[string]*WorkloadHealthCondition{} - components := app.GetComponents() - for _, compName := range components { - for _, wlhc := range wlConditions { - if wlhc.ComponentName == compName { - r[compName] = wlhc - break - } - } - if r[compName] == nil { - r[compName] = &WorkloadHealthCondition{ - HealthStatus: HealthStatusNotDiagnosed, - } - } - } - - return r, nil -} - -func NewCompStatusCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { - ctx := context.Background() - cmd := &cobra.Command{ - Use: "status ", - Short: "get status of a service", - Long: "get status of a service, including its workload and health status", - Example: `vela svc status `, - RunE: func(cmd *cobra.Command, args []string) error { - argsLength := len(args) - if argsLength == 0 { - ioStreams.Errorf("Hint: please specify the service name") - os.Exit(1) - } - compName := args[0] - env, err := GetEnv(cmd) - if err != nil { - ioStreams.Errorf("Error: failed to get Env: %s", err) - return err - } - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) - if err != nil { - return err - } - appName, _ := cmd.Flags().GetString(App) - return printComponentStatus(ctx, newClient, ioStreams, compName, appName, env) - }, - Annotations: map[string]string{ - types.TagCommandType: types.TypeApp, - }, - } - cmd.SetOut(ioStreams.Out) - return cmd -} - func printComponentStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams, compName, appName string, env *types.EnvMeta) error { app, appConfig, err := getApp(ctx, c, compName, appName, env) if err != nil { @@ -267,20 +175,25 @@ func printComponentStatus(ctx context.Context, c client.Client, ioStreams cmduti return fmt.Errorf(ErrServiceNotFound, compName) } workloadType := svc.GetType() - ioStreams.Infof("Showing status of service(type: %s) %s deployed in Environment %s\n", workloadType, white.Sprint(compName), env.Name) healthStatus, healthInfo, err := healthCheckLoop(ctx, c, compName, appName, env) if err != nil { ioStreams.Info(healthInfo) return err } - ioStreams.Infof(white.Sprintf("Service %s Status:", compName)) + ioStreams.Infof(white.Sprintf(" - Name: %s\n", compName)) + ioStreams.Infof(" Type: %s\n", workloadType) healthColor := getHealthStatusColor(healthStatus) - healthInfo = strings.ReplaceAll(healthInfo, "\n", "\n\t") // formart healthInfo output - ioStreams.Infof("\t %s %s\n", healthColor.Sprint(healthStatus), healthColor.Sprint(healthInfo)) + healthInfo = strings.ReplaceAll(healthInfo, "\n", "\n\t") // format healthInfo output + ioStreams.Infof(" %s %s\n", healthColor.Sprint(healthStatus), healthColor.Sprint(healthInfo)) + + ioStreams.Infof(" Last Deployment:\n") + ioStreams.Infof(" Created at: %v\n", appConfig.CreationTimestamp) + ioStreams.Infof(" Updated at: %v\n", app.UpdateTime.Format(time.RFC3339)) // workload Must found + ioStreams.Infof(" Routes:\n") workloadStatus, _ := getWorkloadStatusFromAppConfig(appConfig, compName) for _, tr := range workloadStatus.Traits { traitType, traitInfo, err := traitCheckLoop(ctx, c, tr.Reference, compName, appConfig, app, 60*time.Second) @@ -288,12 +201,9 @@ func printComponentStatus(ctx context.Context, c client.Client, ioStreams cmduti ioStreams.Infof("%s status: %s", white.Sprint(traitType), traitInfo) return err } - ioStreams.Infof("\t%s: %s", white.Sprint(traitType), traitInfo) + ioStreams.Infof(" - %s: %s", white.Sprint(traitType), traitInfo) } - - ioStreams.Infof(white.Sprint("\nLast Deployment:\n")) - ioStreams.Infof("\tCreated at: %v\n", appConfig.CreationTimestamp) - ioStreams.Infof("\tUpdated at: %v\n", app.UpdateTime.Format(time.RFC3339)) + ioStreams.Info("") return nil } @@ -527,21 +437,6 @@ func applySpinnerNewSuffix(s *spinner.Spinner, suffix string) { s.Suffix = suffixColor.Sprintf(" %s", suffix) } -func printPrefix(p string) string { - if strings.HasSuffix(p, firstElemPrefix) { - p = strings.Replace(p, firstElemPrefix, pipe, strings.Count(p, firstElemPrefix)-1) - } else { - p = strings.ReplaceAll(p, firstElemPrefix, pipe) - } - - if strings.HasSuffix(p, lastElemPrefix) { - p = strings.Replace(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix))), strings.Count(p, lastElemPrefix)-1) - } else { - p = strings.ReplaceAll(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix)))) - } - return p -} - func getHealthStatusColor(s HealthStatus) *color.Color { var c *color.Color switch s { diff --git a/pkg/oam/application.go b/pkg/oam/application.go index 16e6035c0..3cd8d7391 100644 --- a/pkg/oam/application.go +++ b/pkg/oam/application.go @@ -6,11 +6,13 @@ import ( "os" "sort" + "github.com/AlecAivazis/survey/v2" "github.com/oam-dev/kubevela/api/types" "github.com/oam-dev/kubevela/pkg/appfile" "github.com/oam-dev/kubevela/pkg/application" cmdutil "github.com/oam-dev/kubevela/pkg/commands/util" "github.com/oam-dev/kubevela/pkg/server/apis" + "github.com/spf13/cobra" corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -241,3 +243,61 @@ func (o *DeleteOptions) DeleteComponent(io cmdutil.IOStreams) (string, error) { return fmt.Sprintf("delete component succeed %s from %s", o.CompName, o.AppName), nil } + +func chooseSvc(services []string) (string, error) { + var svcName string + services = append(services, DefaultChosenAllSvc) + prompt := &survey.Select{ + Message: "Please choose one service: ", + Options: services, + Default: DefaultChosenAllSvc, + } + err := survey.AskOne(prompt, &svcName) + if err != nil { + return "", fmt.Errorf("failed to retrieve services of the application, err %v", err) + } + return svcName, nil +} + +// GetServicesWhenDescribingApplication gets the target services list either from cli `--svc` flag or from survey +func GetServicesWhenDescribingApplication(cmd *cobra.Command, app *application.Application) ([]string, error) { + var svcFlag string + var svcFlagStatus string + // to store the value of flag `--svc` set in Cli, or selected value in survey + var targetServices []string + if svcFlag = cmd.Flag("svc").Value.String(); svcFlag == "" { + svcFlagStatus = FlagNotSet + } else { + svcFlagStatus = FlagIsInvalid + } + // all services name of the application `appName` + var services []string + for svcName := range app.Services { + services = append(services, svcName) + if svcFlag == svcName { + svcFlagStatus = FlagIsValid + targetServices = append(targetServices, svcName) + } + } + totalServices := len(services) + if svcFlagStatus == FlagNotSet && totalServices == 1 { + targetServices = services + } + if svcFlagStatus == FlagIsInvalid || (svcFlagStatus == FlagNotSet && totalServices > 1) { + if svcFlagStatus == FlagIsInvalid { + cmd.Printf("The service name '%s' is not valid\n", svcFlag) + } + chosenSvc, err := chooseSvc(services) + if err != nil { + return []string{}, err + } + + if chosenSvc == DefaultChosenAllSvc { + targetServices = services + } else { + targetServices = targetServices[:0] + targetServices = append(targetServices, chosenSvc) + } + } + return targetServices, nil +} From c212ac93ff50735cd23f106be9373fe87b05a0f9 Mon Sep 17 00:00:00 2001 From: zzxwill Date: Fri, 30 Oct 2020 23:05:51 +0800 Subject: [PATCH 2/3] revert modification for make manifests --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index f467e587d..e2369b91f 100644 --- a/Makefile +++ b/Makefile @@ -139,6 +139,7 @@ core-uninstall: manifests # Generate manifests e.g. CRD, RBAC etc. manifests: controller-gen $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=charts/vela-core/crds + rm charts/vela-core/crds/_.yaml # Generate code generate: controller-gen From 435ac44cbf30589a15dd47732c79fa0c92649183 Mon Sep 17 00:00:00 2001 From: zzxwill Date: Sun, 1 Nov 2020 21:09:37 +0800 Subject: [PATCH 3/3] fix issue 'ineffectual assignment to err' --- Makefile | 1 - pkg/commands/show.go | 3 +++ pkg/commands/status.go | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e2369b91f..f467e587d 100644 --- a/Makefile +++ b/Makefile @@ -139,7 +139,6 @@ core-uninstall: manifests # Generate manifests e.g. CRD, RBAC etc. manifests: controller-gen $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=charts/vela-core/crds - rm charts/vela-core/crds/_.yaml # Generate code generate: controller-gen diff --git a/pkg/commands/show.go b/pkg/commands/show.go index aae587fc2..26e1307d5 100644 --- a/pkg/commands/show.go +++ b/pkg/commands/show.go @@ -49,6 +49,9 @@ func showApplication(cmd *cobra.Command, env *types.EnvMeta, appName string) err return err } targetServices, err := oam.GetServicesWhenDescribingApplication(cmd, app) + if err != nil { + return err + } cmd.Printf("About:\n\n") table := uitable.New() diff --git a/pkg/commands/status.go b/pkg/commands/status.go index 90f167c27..6d1542135 100644 --- a/pkg/commands/status.go +++ b/pkg/commands/status.go @@ -142,6 +142,9 @@ func printAppStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOSt namespace := env.Name targetServices, err := oam2.GetServicesWhenDescribingApplication(cmd, app) + if err != nil { + return err + } cmd.Printf("About:\n\n") table := uitable.New() @@ -154,7 +157,7 @@ func printAppStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOSt cmd.Printf("Services:\n\n") for _, svcName := range targetServices { - if printComponentStatus(ctx, c, ioStreams, svcName, appName, env); err != nil { + if err := printComponentStatus(ctx, c, ioStreams, svcName, appName, env); err != nil { return err } }