diff --git a/docs/website/docs/command-reference/list-binding.md b/docs/website/docs/command-reference/list-binding.md index f0d477d4797..b896e5b4e6b 100644 --- a/docs/website/docs/command-reference/list-binding.md +++ b/docs/website/docs/command-reference/list-binding.md @@ -16,6 +16,11 @@ The name of the service binding is prefixed with `*` when the service binding is To get more information about a specific service binding, you can run the command `odo describe binding --name ` (see [`odo describe binding` command reference](./describe-binding.md)). +## Available flags + +* `--namespace` - Namespace to list the bindings from (optional). By default, the current namespace defined in kubeconfig is used +* `-o json` - Outputs the list in JSON format. See [JSON output](json-output.md) for more information + ## Running the Command To list all the service bindings, you can run `odo list binding`: diff --git a/docs/website/docs/command-reference/list-component.md b/docs/website/docs/command-reference/list-component.md new file mode 100644 index 00000000000..ff858361f01 --- /dev/null +++ b/docs/website/docs/command-reference/list-component.md @@ -0,0 +1,35 @@ +--- +title: odo list component +--- + +`odo list component` command is useful for getting information about components running on a specific namespace. + +If the command is executed from a directory containing a Devfile, it also displays the component +defined in the Devfile as part of the list, prefixed with a star(*). + +For each component, the command displays: +- its name, +- its project type, +- on which mode it is running (None, Dev, Deploy, or both), note that None is only applicable to the component +defined in the local Devfile, +- by which application the component has been deployed. + +## Available flags + +* `--namespace` - Namespace to list the components from (optional). By default, the current namespace defined in kubeconfig is used +* `-o json` - Outputs the list in JSON format. See [JSON output](json-output.md) for more information + +:::tip use of cache + +`odo list component` makes use of cache for performance reasons. This is the same cache that is referred by `kubectl` command +when you do `kubectl api-resources --cached=true`. As a result, if you were to install an Operator/CRD on the +Kubernetes cluster, and create a resource from it using odo, you might not see it in the `odo list component` output. This +would be the case for 10 minutes timeframe for which the cache is considered valid. Beyond this 10 minutes, the +cache is updated anyway. + +If you would like to invalidate the cache before the 10 minutes timeframe, you could manually delete it by doing: +```shell +rm -rf ~/.kube/cache/discovery/api.crc.testing_6443/ +``` +Above example shows how to invalidate the cache for a CRC cluster. Note that you will have to modify the `api.crc. +testing_6443` part based on the cluster you are working against. \ No newline at end of file diff --git a/docs/website/docs/command-reference/list.md b/docs/website/docs/command-reference/list.md index e2017c1fffa..1047378e4d5 100644 --- a/docs/website/docs/command-reference/list.md +++ b/docs/website/docs/command-reference/list.md @@ -2,34 +2,9 @@ title: odo list --- -`odo list` command is useful for getting information about components running on a specific namespace. - -If the command is executed from a directory containing a Devfile, it also displays the command -defined in the Devfile as part of the list, prefixed with a star(*). - -For each component, the command displays: -- its name, -- its project type, -- on which mode it is running (None, Dev, Deploy, or both), not that None is only applicable to the component -defined in the local Devfile, -- by which application the component has been deployed. +`odo list` command combines the `odo list binding` and `odo list component` commands. ## Available flags -* `--namespace` - Namespace to list the components from (optional). By default, the current namespace defined in kubeconfig is used +* `--namespace` - Namespace to list the resources from (optional). By default, the current namespace defined in kubeconfig is used * `-o json` - Outputs the list in JSON format. See [JSON output](json-output.md) for more information - -:::tip use of cache - -`odo list` makes use of cache for performance reasons. This is the same cache that is referred by `kubectl` command -when you do `kubectl api-resources --cached=true`. As a result, if you were to install an Operator/CRD on the -Kubernetes cluster, and create a resource from it using odo, you might not see it in the `odo list` output. This -would be the case for 10 minutes timeframe for which the cache is considered valid. Beyond this 10 minutes, the -cache is updated anyway. - -If you would like to invalidate the cache before the 10 minutes timeframe, you could manually delete it by doing: -```shell -rm -rf ~/.kube/cache/discovery/api.crc.testing_6443/ -``` -Above example shows how to invalidate the cache for a CRC cluster. Note that you will have to modify the `api.crc. -testing_6443` part based on the cluster you are working against. \ No newline at end of file diff --git a/pkg/component/component.go b/pkg/component/component.go index 5cd01ec5a85..b0e2a21d07f 100644 --- a/pkg/component/component.go +++ b/pkg/component/component.go @@ -195,6 +195,32 @@ func ListAllClusterComponents(client kclient.ClientInterface, namespace string) return components, nil } +func ListAllComponents(client kclient.ClientInterface, namespace string, devObj parser.DevfileObj) ([]api.ComponentAbstract, string, error) { + devfileComponents, err := ListAllClusterComponents(client, namespace) + if err != nil { + return nil, "", err + } + + var localComponent api.ComponentAbstract + if devObj.Data != nil { + localComponent = api.ComponentAbstract{ + Name: devObj.Data.GetMetadata().Name, + ManagedBy: "", + RunningIn: []api.RunningMode{}, + Type: GetComponentTypeFromDevfileMetadata(devObj.Data.GetMetadata()), + } + } + + componentInDevfile := "" + if localComponent.Name != "" { + if !Contains(localComponent, devfileComponents) { + devfileComponents = append(devfileComponents, localComponent) + } + componentInDevfile = localComponent.Name + } + return devfileComponents, componentInDevfile, nil +} + func getResourcesForComponent(client kclient.ClientInterface, name string, namespace string) ([]unstructured.Unstructured, error) { selector := labels.GetSelector(name, "app", labels.ComponentAnyMode, false) resourceList, err := client.GetAllResourcesFromSelector(selector, namespace) diff --git a/pkg/odo/cli/list/binding/binding.go b/pkg/odo/cli/list/binding/binding.go index 87336ffe847..4386cf2fe8e 100644 --- a/pkg/odo/cli/list/binding/binding.go +++ b/pkg/odo/cli/list/binding/binding.go @@ -42,6 +42,12 @@ type BindingListOptions struct { // working directory contextDir string + + // Local variables + namespaceFilter string + + // Flags + namespaceFlag string } var _ genericclioptions.Runnable = (*BindingListOptions)(nil) @@ -69,8 +75,13 @@ func (o *BindingListOptions) Complete(cmdline cmdline.Cmdline, args []string) (e return err } - // this ensures that the current namespace is used - o.clientset.KubernetesClient.SetNamespace(o.GetProject()) + if o.namespaceFlag != "" { + o.namespaceFilter = o.namespaceFlag + } else { + o.namespaceFilter = o.GetProject() + } + + o.clientset.KubernetesClient.SetNamespace(o.namespaceFilter) return nil } @@ -125,6 +136,7 @@ func NewCmdBindingList(name, fullName string) *cobra.Command { }, } clientset.Add(bindingListCmd, clientset.KUBERNETES, clientset.BINDING) + bindingListCmd.Flags().StringVar(&o.namespaceFlag, "namespace", "", "Namespace for odo to scan for bindings") machineoutput.UsedByCommand(bindingListCmd) return bindingListCmd } diff --git a/pkg/odo/cli/list/component/list.go b/pkg/odo/cli/list/component/list.go new file mode 100644 index 00000000000..a3902be865b --- /dev/null +++ b/pkg/odo/cli/list/component/list.go @@ -0,0 +1,212 @@ +package component + +import ( + "context" + "errors" + "fmt" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" + "github.com/spf13/cobra" + + "github.com/redhat-developer/odo/pkg/api" + "github.com/redhat-developer/odo/pkg/machineoutput" + "github.com/redhat-developer/odo/pkg/odo/cli/ui" + + dfutil "github.com/devfile/library/pkg/util" + + "github.com/redhat-developer/odo/pkg/component" + + "github.com/redhat-developer/odo/pkg/log" + "github.com/redhat-developer/odo/pkg/odo/cmdline" + "github.com/redhat-developer/odo/pkg/odo/genericclioptions" + "github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" + "github.com/redhat-developer/odo/pkg/odo/util/completion" + + ktemplates "k8s.io/kubectl/pkg/util/templates" +) + +// RecommendedCommandName is the recommended list name +const RecommendedCommandName = "component" + +var listExample = ktemplates.Examples(` # List all components in the application +%[1]s + `) + +// ListOptions ... +type ListOptions struct { + // Context + *genericclioptions.Context + + // Clients + clientset *clientset.Clientset + + // Local variables + namespaceFilter string + + // Flags + namespaceFlag string +} + +var _ genericclioptions.Runnable = (*ListOptions)(nil) +var _ genericclioptions.JsonOutputter = (*ListOptions)(nil) + +// NewListOptions ... +func NewListOptions() *ListOptions { + return &ListOptions{} +} + +func (o *ListOptions) SetClientset(clientset *clientset.Clientset) { + o.clientset = clientset +} + +// Complete ... +func (lo *ListOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) { + + // Check to see if KUBECONFIG exists, and if not, error the user that we would not be able to get cluster information + // Do this before anything else, or else we will just error out with the: + // invalid configuration: no configuration has been provided, try setting KUBERNETES_MASTER environment variable + // instead + if !dfutil.CheckKubeConfigExist() { + return errors.New("KUBECONFIG not found. Unable to retrieve cluster information. Please set your Kubernetes configuration via KUBECONFIG env variable or ~/.kube/config") + } + + // Create the local context and initial Kubernetes client configuration + lo.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile("")) + // The command must work without Devfile + if err != nil && !genericclioptions.IsNoDevfileError(err) { + return err + } + + // If the namespace flag has been passed, we will search there. + // if it hasn't, we will search from the default project / namespace. + if lo.namespaceFlag != "" { + lo.namespaceFilter = lo.namespaceFlag + } else { + lo.namespaceFilter = lo.GetProject() + } + + return nil +} + +// Validate ... +func (lo *ListOptions) Validate() (err error) { + return nil +} + +// Run has the logic to perform the required actions as part of command +func (lo *ListOptions) Run(ctx context.Context) error { + listSpinner := log.Spinnerf("Listing components from namespace '%s'", lo.namespaceFilter) + defer listSpinner.End(false) + + list, err := lo.run(ctx) + if err != nil { + return err + } + + listSpinner.End(true) + + HumanReadableOutput(list) + return nil +} + +// Run contains the logic for the odo command +func (lo *ListOptions) RunForJsonOutput(ctx context.Context) (out interface{}, err error) { + return lo.run(ctx) +} + +func (lo *ListOptions) run(ctx context.Context) (api.ResourcesList, error) { + devfileComponents, componentInDevfile, err := component.ListAllComponents(lo.clientset.KubernetesClient, lo.namespaceFilter, lo.EnvSpecificInfo.GetDevfileObj()) + if err != nil { + return api.ResourcesList{}, err + } + return api.ResourcesList{ + ComponentInDevfile: componentInDevfile, + Components: devfileComponents, + }, nil +} + +// NewCmdList implements the list odo command +func NewCmdComponentList(name, fullName string) *cobra.Command { + o := NewListOptions() + + var listCmd = &cobra.Command{ + Use: name, + Short: "List all components in the current namespace", + Long: "List all components in the current namespace.", + Example: fmt.Sprintf(listExample, fullName), + Args: cobra.NoArgs, + Annotations: map[string]string{"command": "management"}, + Run: func(cmd *cobra.Command, args []string) { + genericclioptions.GenericRun(o, cmd, args) + }, + } + clientset.Add(listCmd, clientset.KUBERNETES) + + listCmd.Flags().StringVar(&o.namespaceFlag, "namespace", "", "Namespace for odo to scan for components") + + completion.RegisterCommandFlagHandler(listCmd, "path", completion.FileCompletionHandler) + machineoutput.UsedByCommand(listCmd) + + return listCmd +} + +func HumanReadableOutput(list api.ResourcesList) { + components := list.Components + if len(components) == 0 { + log.Error("There are no components deployed.") + return + } + + t := ui.NewTable() + + // Create the header and then sort accordingly + t.AppendHeader(table.Row{"NAME", "PROJECT TYPE", "RUNNING IN", "MANAGED"}) + t.SortBy([]table.SortBy{ + {Name: "MANAGED", Mode: table.Asc}, + {Name: "NAME", Mode: table.Dsc}, + }) + + // Go through each component and add it to the table + for _, comp := range components { + + // Mark the name as yellow in the index to it's easier to see. + name := text.Colors{text.FgHiYellow}.Sprint(comp.Name) + + // Get the managed by label + managedBy := comp.ManagedBy + if managedBy == "" { + managedBy = api.TypeUnknown + } + + // Get the mode (dev or deploy) + mode := comp.RunningIn.String() + + // Get the type of the component + componentType := comp.Type + if componentType == "" { + componentType = api.TypeUnknown + } + + // If we find our local unpushed component, let's change the output appropriately. + if list.ComponentInDevfile == comp.Name { + name = fmt.Sprintf("* %s", name) + + if comp.ManagedBy == "" { + managedBy = "odo" + } + } + + // If we are managing that component, output it as blue (our logo colour) to indicate it's used by odo + if managedBy == "odo" { + managedBy = text.Colors{text.FgBlue}.Sprintf("odo (%s)", comp.ManagedByVersion) + } else if managedBy != "" && comp.ManagedByVersion != "" { + // this is done to maintain the color of the output + managedBy += fmt.Sprintf("(%s)", comp.ManagedByVersion) + } + + t.AppendRow(table.Row{name, componentType, mode, managedBy}) + } + t.Render() + +} diff --git a/pkg/odo/cli/list/list.go b/pkg/odo/cli/list/list.go index ecc3da53fa1..f0ad135b4d5 100644 --- a/pkg/odo/cli/list/list.go +++ b/pkg/odo/cli/list/list.go @@ -4,25 +4,20 @@ import ( "context" "errors" "fmt" + "os" - "github.com/jedib0t/go-pretty/v6/table" - "github.com/jedib0t/go-pretty/v6/text" "github.com/spf13/cobra" "github.com/redhat-developer/odo/pkg/api" - "github.com/redhat-developer/odo/pkg/devfile" - "github.com/redhat-developer/odo/pkg/devfile/location" + "github.com/redhat-developer/odo/pkg/component" + "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/machineoutput" "github.com/redhat-developer/odo/pkg/odo/cli/list/binding" + clicomponent "github.com/redhat-developer/odo/pkg/odo/cli/list/component" "github.com/redhat-developer/odo/pkg/odo/cli/list/namespace" - "github.com/redhat-developer/odo/pkg/odo/cli/ui" - "github.com/redhat-developer/odo/pkg/util" dfutil "github.com/devfile/library/pkg/util" - "github.com/redhat-developer/odo/pkg/component" - - "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/odo/cmdline" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" "github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" @@ -48,10 +43,8 @@ type ListOptions struct { clientset *clientset.Clientset // Local variables - project string + contextDir string namespaceFilter string - devfilePath string - localComponent api.ComponentAbstract // Flags namespaceFlag string @@ -71,6 +64,10 @@ func (o *ListOptions) SetClientset(clientset *clientset.Clientset) { // Complete ... func (lo *ListOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) { + lo.contextDir, err = os.Getwd() + if err != nil { + return err + } // Check to see if KUBECONFIG exists, and if not, error the user that we would not be able to get cluster information // Do this before anything else, or else we will just error out with the: @@ -81,50 +78,21 @@ func (lo *ListOptions) Complete(cmdline cmdline.Cmdline, args []string) (err err } // Create the local context and initial Kubernetes client configuration - lo.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline)) - if err != nil { + lo.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile("")) + // The command must work without Devfile + if err != nil && !genericclioptions.IsNoDevfileError(err) { return err } - // Check for the Devfile and then retrieve all information regarding the local Devfile - lo.devfilePath = location.DevfileLocation("") - if util.CheckPathExists(lo.devfilePath) { - - // Set the project / namespace based on the devfile context - lo.project = lo.Context.GetProject() - - // Parse the devfile - devObj, parseErr := devfile.ParseAndValidateFromFile(lo.devfilePath) - if parseErr != nil { - return parseErr - } - - // Create a local component from the parse devfile - localComponent := api.ComponentAbstract{ - Name: devObj.Data.GetMetadata().Name, - ManagedBy: "", - RunningIn: []api.RunningMode{}, - Type: component.GetComponentTypeFromDevfileMetadata(devObj.Data.GetMetadata()), - } - - lo.localComponent = localComponent - - } - - // If the context is "" (devfile.yaml not found..), we get the active one from KUBECONFIG. - if lo.project == "" { - lo.project = lo.clientset.KubernetesClient.GetCurrentNamespace() - } - // If the namespace flag has been passed, we will search there. // if it hasn't, we will search from the default project / namespace. if lo.namespaceFlag != "" { lo.namespaceFilter = lo.namespaceFlag } else { - lo.namespaceFilter = lo.project + lo.namespaceFilter = lo.GetProject() } - return + return nil } // Validate ... @@ -134,49 +102,44 @@ func (lo *ListOptions) Validate() (err error) { // Run has the logic to perform the required actions as part of command func (lo *ListOptions) Run(ctx context.Context) error { - list, err := lo.run(ctx) + listSpinner := log.Spinnerf("Listing resources from the namespace %q", lo.namespaceFilter) + defer listSpinner.End(false) + + list, err := lo.run() if err != nil { return err } - humanReadableOutput(list) + + listSpinner.End(true) + + fmt.Printf("\nComponents:\n") + clicomponent.HumanReadableOutput(list) + fmt.Printf("\nBindings:\n") + binding.HumanReadableOutput(lo.namespaceFilter, list) return nil } // Run contains the logic for the odo command func (lo *ListOptions) RunForJsonOutput(ctx context.Context) (out interface{}, err error) { - list, err := lo.run(ctx) - if err != nil { - return nil, err - } - return list, nil + return lo.run() } -func (lo *ListOptions) run(cts context.Context) (api.ResourcesList, error) { - listSpinner := log.Spinnerf("Listing components from namespace '%s'", lo.namespaceFilter) - defer listSpinner.End(false) - - // Step 1. - // Retrieve all related components from the Kubernetes cluster, from the given namespace - devfileComponents, err := component.ListAllClusterComponents(lo.clientset.KubernetesClient, lo.namespaceFilter) +func (lo *ListOptions) run() (list api.ResourcesList, err error) { + devfileComponents, componentInDevfile, err := component.ListAllComponents(lo.clientset.KubernetesClient, lo.namespaceFilter, lo.EnvSpecificInfo.GetDevfileObj()) if err != nil { return api.ResourcesList{}, err } - listSpinner.End(true) - // Step 2. - // If we have a local component, let's add it to the list of Devfiles - // This checks lo.localComponent.Name. If it's empty, we didn't parse one in the Complete() function, so there is no local devfile. - // We will only append the local component to the devfile if it doesn't exist in the list. - componentInDevfile := "" - if lo.localComponent.Name != "" { - if !component.Contains(lo.localComponent, devfileComponents) { - devfileComponents = append(devfileComponents, lo.localComponent) - } - componentInDevfile = lo.localComponent.Name + bindings, inDevfile, err := lo.clientset.BindingClient.ListAllBindings(lo.EnvSpecificInfo.GetDevfileObj(), lo.contextDir) + if err != nil { + return api.ResourcesList{}, err } + return api.ResourcesList{ ComponentInDevfile: componentInDevfile, Components: devfileComponents, + BindingsInDevfile: inDevfile, + Bindings: bindings, }, nil } @@ -195,11 +158,12 @@ func NewCmdList(name, fullName string) *cobra.Command { genericclioptions.GenericRun(o, cmd, args) }, } - clientset.Add(listCmd, clientset.KUBERNETES) + clientset.Add(listCmd, clientset.KUBERNETES, clientset.BINDING) namespaceCmd := namespace.NewCmdNamespaceList(namespace.RecommendedCommandName, odoutil.GetFullName(fullName, namespace.RecommendedCommandName)) bindingCmd := binding.NewCmdBindingList(binding.RecommendedCommandName, odoutil.GetFullName(fullName, binding.RecommendedCommandName)) - listCmd.AddCommand(namespaceCmd, bindingCmd) + componentCmd := clicomponent.NewCmdComponentList(clicomponent.RecommendedCommandName, odoutil.GetFullName(fullName, clicomponent.RecommendedCommandName)) + listCmd.AddCommand(namespaceCmd, bindingCmd, componentCmd) listCmd.SetUsageTemplate(odoutil.CmdUsageTemplate) listCmd.Flags().StringVar(&o.namespaceFlag, "namespace", "", "Namespace for odo to scan for components") @@ -209,63 +173,3 @@ func NewCmdList(name, fullName string) *cobra.Command { return listCmd } - -func humanReadableOutput(list api.ResourcesList) { - components := list.Components - if len(components) == 0 { - log.Error("There are no components deployed.") - return - } - - t := ui.NewTable() - - // Create the header and then sort accordingly - t.AppendHeader(table.Row{"NAME", "PROJECT TYPE", "RUNNING IN", "MANAGED"}) - t.SortBy([]table.SortBy{ - {Name: "MANAGED", Mode: table.Asc}, - {Name: "NAME", Mode: table.Dsc}, - }) - - // Go through each component and add it to the table - for _, comp := range components { - - // Mark the name as yellow in the index to it's easier to see. - name := text.Colors{text.FgHiYellow}.Sprint(comp.Name) - - // Get the managed by label - managedBy := comp.ManagedBy - if managedBy == "" { - managedBy = api.TypeUnknown - } - - // Get the mode (dev or deploy) - mode := comp.RunningIn.String() - - // Get the type of the component - componentType := comp.Type - if componentType == "" { - componentType = api.TypeUnknown - } - - // If we find our local unpushed component, let's change the output appropriately. - if list.ComponentInDevfile == comp.Name { - name = fmt.Sprintf("* %s", name) - - if comp.ManagedBy == "" { - managedBy = "odo" - } - } - - // If we are managing that component, output it as blue (our logo colour) to indicate it's used by odo - if managedBy == "odo" { - managedBy = text.Colors{text.FgBlue}.Sprintf("odo (%s)", comp.ManagedByVersion) - } else if managedBy != "" && comp.ManagedByVersion != "" { - // this is done to maintain the color of the output - managedBy += fmt.Sprintf("(%s)", comp.ManagedByVersion) - } - - t.AppendRow(table.Row{name, componentType, mode, managedBy}) - } - t.Render() - -} diff --git a/tests/e2escenarios/e2e_test.go b/tests/e2escenarios/e2e_test.go index 8096f669006..ce130f623dd 100644 --- a/tests/e2escenarios/e2e_test.go +++ b/tests/e2escenarios/e2e_test.go @@ -92,7 +92,7 @@ var _ = Describe("E2E Test", func() { checkIfDevEnvIsUp(ports["3000"], "Hello from Node.js app v2 Starter Application!") // "running odo list" - stdout := helper.Cmd("odo", "list").ShouldPass().Out() + stdout := helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(stdout, []string{componentName, "nodejs", "Dev"}) // "exit dev mode and run odo deploy" @@ -115,7 +115,7 @@ var _ = Describe("E2E Test", func() { Expect(stdout).To(ContainSubstring("Your Devfile has been successfully deployed")) // should deploy new changes - stdout = helper.Cmd("odo", "list").ShouldPass().Out() + stdout = helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(stdout, []string{componentName, "nodejs", "Deploy"}) // start dev mode again @@ -130,7 +130,7 @@ var _ = Describe("E2E Test", func() { checkIfDevEnvIsUp(ports["3000"], "Hello from Node.js app v3 Starter Application!") // should list both dev,deploy - stdout = helper.Cmd("odo", "list").ShouldPass().Out() + stdout = helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(stdout, []string{componentName, "nodejs", "Dev", "Deploy"}) // "exit dev mode and run odo deploy" @@ -213,7 +213,7 @@ var _ = Describe("E2E Test", func() { checkIfDevEnvIsUp(ports["3000"], "Hello from Node.js app v2 Starter Application!") // "running odo list" - stdout := helper.Cmd("odo", "list").ShouldPass().Out() + stdout := helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(stdout, []string{componentName, "nodejs", "Dev"}) // "exit dev mode and run odo deploy" @@ -235,7 +235,7 @@ var _ = Describe("E2E Test", func() { Expect(stdout).To(ContainSubstring("Your Devfile has been successfully deployed")) // should deploy new changes - stdout = helper.Cmd("odo", "list").ShouldPass().Out() + stdout = helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(stdout, []string{componentName, "nodejs", "Deploy"}) // start dev mode again @@ -249,7 +249,7 @@ var _ = Describe("E2E Test", func() { checkIfDevEnvIsUp(ports["3000"], "Hello from Node.js app v3 Starter Application!") // should list both dev,deploy - stdout = helper.Cmd("odo", "list").ShouldPass().Out() + stdout = helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(stdout, []string{componentName, "nodejs", "Dev", "Deploy"}) // "exit dev mode and run odo deploy" diff --git a/tests/integration/cmd_describe_list_binding_test.go b/tests/integration/cmd_describe_list_binding_test.go index 87c60b4bf88..634a810379a 100644 --- a/tests/integration/cmd_describe_list_binding_test.go +++ b/tests/integration/cmd_describe_list_binding_test.go @@ -83,48 +83,61 @@ var _ = Describe("odo describe/list binding command tests", func() { }) }) - It("should list the binding without running odo dev", func() { - By("JSON output", func() { - res := helper.Cmd("odo", "list", "binding", "-o", "json").ShouldPass() - stdout, stderr := res.Out(), res.Err() - Expect(stderr).To(BeEmpty()) - Expect(helper.IsJSON(stdout)).To(BeTrue()) - helper.JsonPathContentIs(stdout, "bindings.0.name", "my-nodejs-app-cluster-sample") - helper.JsonPathContentIs(stdout, "bindings.0.spec.application.kind", "Deployment") - helper.JsonPathContentIs(stdout, "bindings.0.spec.application.name", "my-nodejs-app-app") - helper.JsonPathContentIs(stdout, "bindings.0.spec.application.apiVersion", "apps/v1") - helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.apiVersion", "postgresql.k8s.enterprisedb.io/v1") - helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.kind", "Cluster") - helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.name", "cluster-sample") - if ns != "" { - helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.namespace", ns) - } else { - helper.JsonPathDoesNotExist(stdout, "bindings.0.spec.services.0.namespace") - } - helper.JsonPathContentIs(stdout, "bindings.0.spec.detectBindingResources", "true") - helper.JsonPathContentIs(stdout, "bindings.0.spec.bindAsFiles", "true") - helper.JsonPathContentIs(stdout, "bindings.0.spec.namingStrategy", "lowercase") - helper.JsonPathContentIs(stdout, "bindings.0.status", "") - helper.JsonPathContentIs(stdout, "bindingsInDevfile.#", "1") - helper.JsonPathContentIs(stdout, "bindingsInDevfile.0", "my-nodejs-app-cluster-sample") - }) - By("human readable output", func() { - res := helper.Cmd("odo", "list", "binding").ShouldPass() - stdout, _ := res.Out(), res.Err() - lines := strings.Split(stdout, "\n") - Expect(lines[0]).To(ContainSubstring(fmt.Sprintf("Listing ServiceBindings from the namespace %q", commonVar.Project))) - Expect(lines[3]).To(ContainSubstring("* ")) - Expect(lines[3]).To(ContainSubstring("my-nodejs-app-cluster-sample")) - Expect(lines[3]).To(ContainSubstring("my-nodejs-app-app (Deployment)")) - if ns != "" { - Expect(lines[3]).To(ContainSubstring(fmt.Sprintf("cluster-sample (Cluster.postgresql.k8s.enterprisedb.io) (namespace: %s)", ns))) - } else { - Expect(lines[3]).To(ContainSubstring("cluster-sample (Cluster.postgresql.k8s.enterprisedb.io)")) - Expect(lines[3]).ToNot(ContainSubstring("cluster-sample (Cluster.postgresql.k8s.enterprisedb.io) (namespace: ")) - } - Expect(lines[3]).To(ContainSubstring("None")) + for _, command := range [][]string{ + {"list", "binding"}, + {"list"}, + } { + command := command + It("should list the binding without running odo dev", func() { + By("JSON output", func() { + res := helper.Cmd("odo", append(command, "-o", "json")...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + Expect(stderr).To(BeEmpty()) + Expect(helper.IsJSON(stdout)).To(BeTrue()) + helper.JsonPathContentIs(stdout, "bindings.0.name", "my-nodejs-app-cluster-sample") + helper.JsonPathContentIs(stdout, "bindings.0.spec.application.kind", "Deployment") + helper.JsonPathContentIs(stdout, "bindings.0.spec.application.name", "my-nodejs-app-app") + helper.JsonPathContentIs(stdout, "bindings.0.spec.application.apiVersion", "apps/v1") + helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.apiVersion", "postgresql.k8s.enterprisedb.io/v1") + helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.kind", "Cluster") + helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.name", "cluster-sample") + if ns != "" { + helper.JsonPathContentIs(stdout, "bindings.0.spec.services.0.namespace", ns) + } else { + helper.JsonPathDoesNotExist(stdout, "bindings.0.spec.services.0.namespace") + } + helper.JsonPathContentIs(stdout, "bindings.0.spec.detectBindingResources", "true") + helper.JsonPathContentIs(stdout, "bindings.0.spec.bindAsFiles", "true") + helper.JsonPathContentIs(stdout, "bindings.0.spec.namingStrategy", "lowercase") + helper.JsonPathContentIs(stdout, "bindings.0.status", "") + helper.JsonPathContentIs(stdout, "bindingsInDevfile.#", "1") + helper.JsonPathContentIs(stdout, "bindingsInDevfile.0", "my-nodejs-app-cluster-sample") + }) + By("human readable output", func() { + res := helper.Cmd("odo", command...).ShouldPass() + stdout, _ := res.Out(), res.Err() + lines := strings.Split(stdout, "\n") + + if len(command) == 1 { + Expect(lines[0]).To(ContainSubstring(fmt.Sprintf("Listing resources from the namespace %q", commonVar.Project))) + lines = lines[6:] + } else { + Expect(lines[0]).To(ContainSubstring(fmt.Sprintf("Listing ServiceBindings from the namespace %q", commonVar.Project))) + } + Expect(lines[3]).To(ContainSubstring("* ")) + Expect(lines[3]).To(ContainSubstring("my-nodejs-app-cluster-sample")) + Expect(lines[3]).To(ContainSubstring("my-nodejs-app-app (Deployment)")) + if ns != "" { + Expect(lines[3]).To(ContainSubstring(fmt.Sprintf("cluster-sample (Cluster.postgresql.k8s.enterprisedb.io) (namespace: %s)", ns))) + } else { + Expect(lines[3]).To(ContainSubstring("cluster-sample (Cluster.postgresql.k8s.enterprisedb.io)")) + Expect(lines[3]).ToNot(ContainSubstring("cluster-sample (Cluster.postgresql.k8s.enterprisedb.io) (namespace: ")) + } + Expect(lines[3]).To(ContainSubstring("None")) + }) }) - }) + } + }) for _, ctx := range []struct { @@ -561,41 +574,77 @@ var _ = Describe("odo describe/list binding command tests", func() { }) - It("should list the binding", func() { - By("JSON output", func() { - res := helper.Cmd("odo", "list", "binding", "-o", "json").ShouldPass() - stdout, stderr := res.Out(), res.Err() - if ctx.assertListJsonOutput != nil { - ctx.assertListJsonOutput(true, stdout, stderr) - } - }) - By("human readable output", func() { - res := helper.Cmd("odo", "list", "binding").ShouldPass() - stdout, stderr := res.Out(), res.Err() - if ctx.assertListHumanReadableOutput != nil { - ctx.assertListHumanReadableOutput(true, stdout, stderr) - } - }) + for _, command := range [][]string{ + {"list"}, + {"list", "binding"}, + } { + It("should list the binding", func() { + By("JSON output", func() { + res := helper.Cmd("odo", append(command, "-o", "json")...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + if ctx.assertListJsonOutput != nil { + ctx.assertListJsonOutput(true, stdout, stderr) + } + }) + By("human readable output", func() { + res := helper.Cmd("odo", command...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + if ctx.assertListHumanReadableOutput != nil { + ctx.assertListHumanReadableOutput(true, stdout, stderr) + } + }) - By("JSON output from another directory", func() { - err := os.Chdir("/") - Expect(err).ToNot(HaveOccurred()) - res := helper.Cmd("odo", "list", "binding", "-o", "json").ShouldPass() - stdout, stderr := res.Out(), res.Err() - if ctx.assertListJsonOutput != nil { - ctx.assertListJsonOutput(false, stdout, stderr) - } + By("JSON output from another directory", func() { + err := os.Chdir("/") + Expect(err).ToNot(HaveOccurred()) + res := helper.Cmd("odo", append(command, "-o", "json")...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + if ctx.assertListJsonOutput != nil { + ctx.assertListJsonOutput(false, stdout, stderr) + } + }) + By("human readable output from another directory with name flag", func() { + err := os.Chdir("/") + Expect(err).ToNot(HaveOccurred()) + res := helper.Cmd("odo", command...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + if ctx.assertListHumanReadableOutput != nil { + ctx.assertListHumanReadableOutput(false, stdout, stderr) + } + }) }) - By("human readable output from another directory with name flag", func() { - err := os.Chdir("/") - Expect(err).ToNot(HaveOccurred()) - res := helper.Cmd("odo", "list", "binding").ShouldPass() - stdout, stderr := res.Out(), res.Err() - if ctx.assertListHumanReadableOutput != nil { - ctx.assertListHumanReadableOutput(false, stdout, stderr) - } + + When("changing the current namespace", func() { + BeforeEach(func() { + commonVar.CliRunner.SetProject("default") + }) + + AfterEach(func() { + commonVar.CliRunner.SetProject(commonVar.Project) + }) + + It("should list the binding with --namespace flag", func() { + By("JSON output from another directory", func() { + err := os.Chdir("/") + Expect(err).ToNot(HaveOccurred()) + res := helper.Cmd("odo", append(command, "-o", "json", "--namespace", commonVar.Project)...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + if ctx.assertListJsonOutput != nil { + ctx.assertListJsonOutput(false, stdout, stderr) + } + }) + By("human readable output from another directory with name flag", func() { + err := os.Chdir("/") + Expect(err).ToNot(HaveOccurred()) + res := helper.Cmd("odo", append(command, "--namespace", commonVar.Project)...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + if ctx.assertListHumanReadableOutput != nil { + ctx.assertListHumanReadableOutput(false, stdout, stderr) + } + }) + }) }) - }) + } }) }) }) diff --git a/tests/integration/cmd_devfile_list_test.go b/tests/integration/cmd_devfile_list_test.go index 552cd6e62e2..c271419c15b 100644 --- a/tests/integration/cmd_devfile_list_test.go +++ b/tests/integration/cmd_devfile_list_test.go @@ -41,11 +41,11 @@ var _ = Describe("odo list with devfile", func() { commonVar.CliRunner.Run("delete", "-f", helper.GetExamplePath("manifests", "deployment-app-label.yaml")) }) It("should list the component with odo list", func() { - output := helper.Cmd("odo", "list").ShouldPass().Out() + output := helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(output, []string{deploymentName, "Unknown", "None", managedBy}) }) It("should list the component in JSON", func() { - output := helper.Cmd("odo", "list", "-o", "json").ShouldPass().Out() + output := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass().Out() helper.JsonPathContentIs(output, "components.0.name", deploymentName) Expect(gjson.Get(output, "components.0.runningIn").String()).To(BeEmpty()) helper.JsonPathContentIs(output, "components.0.projectType", "Unknown") @@ -64,11 +64,11 @@ var _ = Describe("odo list with devfile", func() { commonVar.CliRunner.Run("delete", "-f", helper.GetExamplePath("manifests", "deployment-without-managed-by-label.yaml")) }) It("should list the component with odo list", func() { - output := helper.Cmd("odo", "list").ShouldPass().Out() + output := helper.Cmd("odo", "list", "component").ShouldPass().Out() helper.MatchAllInOutput(output, []string{deploymentName, "Unknown", "None", "Unknown"}) }) It("should list the component in JSON", func() { - output := helper.Cmd("odo", "list", "-o", "json").ShouldPass().Out() + output := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass().Out() helper.JsonPathContentContain(output, "components.0.name", deploymentName) Expect(gjson.Get(output, "components.0.runningIn").String()).To(BeEmpty()) helper.JsonPathContentContain(output, "components.0.projectType", "Unknown") @@ -84,7 +84,7 @@ var _ = Describe("odo list with devfile", func() { commonVar.CliRunner.Run("delete", "deployment", deploymentName) }) It("should not be listed in the odo list output", func() { - output := helper.Cmd("odo", "list").ShouldRun().Out() + output := helper.Cmd("odo", "list", "component").ShouldRun().Out() Expect(output).ToNot(ContainSubstring(deploymentName)) }) @@ -109,7 +109,7 @@ var _ = Describe("odo list with devfile", func() { var checkList = func(componentType string) { By("checking the normal output", func() { - stdOut := helper.Cmd("odo", "list").ShouldPass().Out() + stdOut := helper.Cmd("odo", "list", "component").ShouldPass().Out() Expect(stdOut).To(ContainSubstring(componentType)) }) } @@ -123,11 +123,11 @@ var _ = Describe("odo list with devfile", func() { }) It("should show managedBy Version", func() { By("checking the normal output", func() { - stdout := helper.Cmd("odo", "list").ShouldPass().Out() + stdout := helper.Cmd("odo", "list", "component").ShouldPass().Out() Expect(stdout).To(ContainSubstring(version)) }) By("checking the JSON output", func() { - stdout := helper.Cmd("odo", "list", "-o", "json").ShouldPass().Out() + stdout := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass().Out() helper.JsonPathContentContain(stdout, "components.0.managedByVersion", version) }) }) @@ -139,7 +139,7 @@ var _ = Describe("odo list with devfile", func() { }) By("should display the component as 'Dev' in odo list -o json", func() { - res := helper.Cmd("odo", "list", "-o", "json").ShouldPass() + res := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass() stdout, stderr := res.Out(), res.Err() Expect(stderr).To(BeEmpty()) Expect(helper.IsJSON(stdout)).To(BeTrue()) @@ -159,7 +159,7 @@ var _ = Describe("odo list with devfile", func() { }) By("should display the component as 'Dev, Deploy' in odo list -o json", func() { - res := helper.Cmd("odo", "list", "-o", "json").ShouldPass() + res := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass() stdout, stderr := res.Out(), res.Err() Expect(stderr).To(BeEmpty()) Expect(helper.IsJSON(stdout)).To(BeTrue()) @@ -183,12 +183,12 @@ var _ = Describe("odo list with devfile", func() { // checkList checks the list output (both normal and json) to see if it contains the expected componentType var checkList = func(componentType string) { By("checking the normal output", func() { - stdOut := helper.Cmd("odo", "list").ShouldPass().Out() + stdOut := helper.Cmd("odo", "list", "component").ShouldPass().Out() Expect(stdOut).To(ContainSubstring(componentType)) }) By("checking the JSON output", func() { - res := helper.Cmd("odo", "list", "-o", "json").ShouldPass() + res := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass() stdout, stderr := res.Out(), res.Err() Expect(stderr).To(BeEmpty()) Expect(helper.IsJSON(stdout)).To(BeTrue())