diff --git a/pkg/application/app.go b/pkg/application/app.go index be250b836..61f5d909f 100644 --- a/pkg/application/app.go +++ b/pkg/application/app.go @@ -9,6 +9,8 @@ import ( "path/filepath" "reflect" "sort" + "strings" + "time" "cuelang.org/go/cue" @@ -38,6 +40,8 @@ type Application struct { Components map[string]map[string]interface{} `json:"components"` Secrets map[string]map[string]interface{} `json:"secrets"` Scopes map[string]map[string]interface{} `json:"appScopes"` + CreateTime time.Time `json:"createTime,omitempty"` + UpdateTime time.Time `json:"updateTime,omitempty"` } func LoadFromFile(fileName string) (*Application, error) { @@ -64,11 +68,56 @@ func Load(envName, appName string) (*Application, error) { return LoadFromFile(filepath.Join(appDir, appName+".yaml")) } +func List(envName string) ([]*Application, error) { + appDir, err := system.GetApplicationDir(envName) + if err != nil { + return nil, fmt.Errorf("get app dir from env %s err %v", envName, err) + } + files, err := ioutil.ReadDir(appDir) + if err != nil { + return nil, fmt.Errorf("list apps from %s err %v", appDir, err) + } + var apps []*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 %v", err) + } + apps = append(apps, app) + } + return apps, nil +} + +func MatchAppByComp(envName, compName string) (*Application, error) { + apps, err := List(envName) + if err != nil { + return nil, err + } + for _, subapp := range apps { + for _, v := range subapp.GetComponents() { + if v == compName { + return subapp, nil + } + } + } + return nil, fmt.Errorf("no app found contains %s in env %s", compName, envName) +} + func (app *Application) Save(envName string) error { appDir, err := system.GetApplicationDir(envName) if err != nil { return fmt.Errorf("get app dir from env %s err %v", envName, err) } + if app.CreateTime.IsZero() { + app.CreateTime = time.Now() + } + app.UpdateTime = time.Now() out, err := yaml.Marshal(app) if err != nil { return err diff --git a/pkg/cmd/workload_run.go b/pkg/cmd/comp.go similarity index 94% rename from pkg/cmd/workload_run.go rename to pkg/cmd/comp.go index d1f2aea61..256f22c8f 100644 --- a/pkg/cmd/workload_run.go +++ b/pkg/cmd/comp.go @@ -37,8 +37,9 @@ func AddCompCommands(c types.Args, ioStreams util.IOStreams) *cobra.Command { types.TagCommandType: types.TypeWorkloads, }, } + compCommands.PersistentFlags().StringP(App, "a", "", "specify application name for component") - compCommands.AddCommand(NewCompRunCommands(c, ioStreams)) + compCommands.AddCommand(NewCompRunCommands(c, ioStreams), NewCompShowCommand(ioStreams)) return compCommands } @@ -74,7 +75,6 @@ func NewCompRunCommands(c types.Args, ioStreams util.IOStreams) *cobra.Command { } runCmd.SetOut(ioStreams.Out) - runCmd.Flags().StringP(App, "a", "", "create or add into an existing application group") runCmd.Flags().BoolP(Staging, "s", false, "only save changes locally without real update application") runCmd.Flags().StringP(WorkloadType, "t", "", "specify workload type for application") @@ -91,8 +91,8 @@ func GetWorkloadNameFromArgs(args []string) (string, error) { } func (o *runOptions) Complete(cmd *cobra.Command, args []string, ctx context.Context) error { - flags := cmd.Flags() + flags.AddFlagSet(cmd.PersistentFlags()) flags.ParseErrorsWhitelist.UnknownFlags = true // First parse, figure out which workloadType it is. diff --git a/pkg/cmd/workload_run_test.go b/pkg/cmd/comp_test.go similarity index 100% rename from pkg/cmd/workload_run_test.go rename to pkg/cmd/comp_test.go diff --git a/pkg/cmd/ls.go b/pkg/cmd/ls.go index d2c7e95be..07c52b666 100644 --- a/pkg/cmd/ls.go +++ b/pkg/cmd/ls.go @@ -13,8 +13,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -var appName string - func NewAppsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "app", @@ -30,7 +28,7 @@ func NewAppsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { cmd.AddCommand(NewAppsListCommand(c, ioStreams), NewDeleteCommand(c, ioStreams), NewAppStatusCommand(c, ioStreams), - NewAppShowCommand(c, ioStreams), + NewAppShowCommand(ioStreams), NewRunCommand(c, ioStreams)) return cmd } @@ -52,6 +50,10 @@ func NewAppsListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comman if err != nil { return err } + appName, err := cmd.Flags().GetString(App) + if err != nil { + return err + } printApplicationList(ctx, newClient, appName, env.Namespace, ioStreams) return nil }, @@ -59,7 +61,7 @@ func NewAppsListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comman types.TagCommandType: types.TypeApp, }, } - cmd.Flags().StringVarP(&appName, "app", "a", "", "Application name") + cmd.Flags().StringP(App, "a", "", "Application name") return cmd } @@ -74,7 +76,6 @@ func printApplicationList(ctx context.Context, c client.Client, appName string, return } else { table := uitable.New() - table.MaxColWidth = 60 table.AddRow("NAME", "WORKLOAD", "TRAITS", "STATUS", "CREATED-TIME") for _, a := range applicationMetaList { traitAlias := strings.Join(a.Traits, ",") diff --git a/pkg/cmd/show.go b/pkg/cmd/show.go index d7e61092c..eb656d55e 100644 --- a/pkg/cmd/show.go +++ b/pkg/cmd/show.go @@ -1,26 +1,24 @@ package cmd import ( - "context" "fmt" "os" + "strings" "github.com/cloud-native-application/rudrx/api/types" + "github.com/cloud-native-application/rudrx/pkg/application" cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util" - corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" - "github.com/ghodss/yaml" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/gosuri/uitable" "github.com/spf13/cobra" - "sigs.k8s.io/controller-runtime/pkg/client" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func NewAppShowCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command { - ctx := context.Background() +func NewAppShowCommand(ioStreams cmdutil.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "show ", - Short: "get detail spec of your app", - Long: "get detail spec of your app, including its workload and trait", + Short: "get details of your app", + Long: "get details of your app, including its workload and trait", Example: `vela app show `, RunE: func(cmd *cobra.Command, args []string) error { argsLength := len(args) @@ -35,12 +33,7 @@ func NewAppShowCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command return err } - newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema}) - if err != nil { - return err - } - - return showApplication(ctx, newClient, cmd, env, appName) + return showApplication(cmd, env, appName) }, Annotations: map[string]string{ types.TagCommandType: types.TypeApp, @@ -57,48 +50,125 @@ type Unkown struct { Status interface{} `json:"status"` } -func showApplication(ctx context.Context, c client.Client, cmd *cobra.Command, env *types.EnvMeta, appName string) error { - var ( - application corev1alpha2.ApplicationConfiguration - ) - namespace := env.Namespace +func showApplication(cmd *cobra.Command, env *types.EnvMeta, appName string) error { - if err := c.Get(ctx, client.ObjectKey{Name: appName, Namespace: namespace}, &application); err != nil { - return fmt.Errorf("Fetch application with Err: %s", err) - } - - if len(application.Spec.Components) == 0 { - cmd.Println("About:") - cmd.Printf(" Appset: %s", appName) - cmd.Printf(" ENV: %s", namespace) - return nil - } - - // current only support one component - componentName := application.Spec.Components[0].ComponentName - component, err := cmdutil.GetComponent(ctx, c, componentName, namespace) + app, err := application.Load(env.Name, appName) if err != nil { - return fmt.Errorf("Fetch component %s with Err: %s", componentName, err) - } - if component.Labels == nil { - return fmt.Errorf("Can't get workloadDef, please check component %s label \"%s\" is correct.", - componentName, types.ComponentWorkloadDefLabel) + return err } - traitDefinitions := cmdutil.ListTraitDefinitionsByApplicationConfiguration(application) - componentOut, _ := yaml.JSONToYAML(component.Spec.Workload.Raw) + cmd.Printf("About:\n\n") + table := uitable.New() + table.AddRow(" Name:", appName) + table.AddRow(" Created at:", app.CreateTime.String()) + table.AddRow(" Updated at:", app.UpdateTime.String()) + cmd.Printf("%s\n\n", table.String()) - cmd.Println("About:") - cmd.Printf(" Appset: %s\n", appName) - cmd.Printf(" ENV: %s\n", namespace) - cmd.Printf("%s", string(componentOut)) + cmd.Println() + cmd.Printf("Environment:\n\n") + cmd.Printf(" Namespace:\t%s\n", env.Namespace) + cmd.Println() - if len(traitDefinitions) != 0 { - cmd.Println("Traits:") + table = uitable.New() + cmd.Printf("Components:\n\n") - traitOut, _ := yaml.Marshal(application.Spec.Components[0].Traits) - cmd.Println(string(traitOut)) + table.AddRow(" Name", "Type", "Traits") + + for compName := range app.Components { + wtype, _ := app.GetWorkload(compName) + var outPutTraits []string + traits, _ := app.GetTraits(compName) + for k := range traits { + outPutTraits = append(outPutTraits, k) + } + table.AddRow(" "+compName, wtype, strings.Join(outPutTraits, ",")) + } + cmd.Println(table.String()) + cmd.Println() + return nil +} + +func NewCompShowCommand(ioStreams cmdutil.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "show ", + Short: "get component detail", + Long: "get component detail, including arguments of workload and trait", + Example: `vela comp show `, + RunE: func(cmd *cobra.Command, args []string) error { + argsLength := len(args) + if argsLength == 0 { + ioStreams.Errorf("Hint: please specify the application 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 + } + + appName, err := cmd.Flags().GetString(App) + if err != nil { + return err + } + + return showComponent(cmd, env, compName, appName) + }, + Annotations: map[string]string{ + types.TagCommandType: types.TypeApp, + }, + } + cmd.SetOut(ioStreams.Out) + return cmd +} + +func showComponent(cmd *cobra.Command, env *types.EnvMeta, compName, appName string) error { + var app *application.Application + var err error + if appName != "" { + app, err = application.Load(env.Name, appName) + } else { + app, err = application.MatchAppByComp(env.Name, compName) + } + if err != nil { + return err + } + + for cname := range app.Components { + if cname != compName { + continue + } + cmd.Printf("About:\n\n") + wtype, data := app.GetWorkload(compName) + table := uitable.New() + table.AddRow(" Name:", compName) + table.AddRow(" WorkloadType:", wtype) + table.AddRow(" Application:", app.Name) + cmd.Printf("%s\n\n", table.String()) + cmd.Printf("Environment:\n\n") + cmd.Printf(" Namespace:\t%s\n\n", env.Namespace) + cmd.Printf("Arguments:\n\n") + table = uitable.New() + for k, v := range data { + table.AddRow(fmt.Sprintf(" %s:", k), v) + } + cmd.Printf("%s\n\n", table.String()) + traits, err := app.GetTraits(compName) + if err != nil || len(traits) == 0 { + continue + } + cmd.Println() + cmd.Printf("Traits:\n\n") + for k, v := range traits { + cmd.Printf(" Type:\t%s\n", k) + cmd.Printf(" Arguments:\n\n") + table = uitable.New() + for kk, vv := range v { + table.AddRow(fmt.Sprintf(" %s:", kk), vv) + } + cmd.Printf("%s\n\n", table.String()) + } + cmd.Println() } - return nil }