From 0c008c6be9666bcd61d6d3874fb6b60fe5b6f917 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 6 Dec 2022 17:01:56 +0100 Subject: [PATCH] odo list component shows components running on podman (#6366) * Enable run-on flag * List all components * Select specific platform with --run-on flag * Make podmanClient nil if no podman client is accessible * integration test * Delay the display of warnings related to experimental features * Add logs displaying podman commands executed * Use None when Running On is empty * odo list --- pkg/api/component-abstract.go | 13 +- pkg/binding/list.go | 26 ++- pkg/component/component.go | 29 ++- pkg/component/component_test.go | 6 +- pkg/dev/podmandev/pod.go | 2 +- pkg/dev/podmandev/pod_test.go | 1 + pkg/odo/cli/cli.go | 2 +- pkg/odo/cli/dev/dev.go | 10 +- pkg/odo/cli/list/component/list.go | 55 ++++- pkg/odo/cli/list/list.go | 55 +++-- pkg/odo/commonflags/context/context.go | 6 +- pkg/odo/commonflags/context/context_test.go | 10 +- pkg/odo/commonflags/run_on.go | 6 +- .../genericclioptions/clientset/clientset.go | 59 ++--- pkg/odo/genericclioptions/runnable.go | 4 +- pkg/podman/component.go | 84 +++++++ pkg/podman/exec.go | 4 +- pkg/podman/interface.go | 3 + pkg/podman/podman.go | 43 +++- pkg/podman/version.go | 42 ++++ tests/integration/cmd_devfile_list_test.go | 216 +++++++++++++----- 21 files changed, 512 insertions(+), 164 deletions(-) create mode 100644 pkg/podman/component.go create mode 100644 pkg/podman/version.go diff --git a/pkg/api/component-abstract.go b/pkg/api/component-abstract.go index e932d624688..bb8a98566cd 100644 --- a/pkg/api/component-abstract.go +++ b/pkg/api/component-abstract.go @@ -2,11 +2,14 @@ package api // ComponentAbstract represents a component as part of a list of components type ComponentAbstract struct { - Name string `json:"name"` - ManagedBy string `json:"managedBy"` - ManagedByVersion string `json:"managedByVersion"` - RunningIn RunningModes `json:"runningIn"` - Type string `json:"projectType"` + Name string `json:"name"` + ManagedBy string `json:"managedBy"` + ManagedByVersion string `json:"managedByVersion"` + // RunningIn are the modes the component is running in, among Dev and Deploy + RunningIn RunningModes `json:"runningIn"` + Type string `json:"projectType"` + // RunningOn is the platform the component is running on, either cluster or podman + RunningOn string `json:"runningOn,omitempty"` } const ( diff --git a/pkg/binding/list.go b/pkg/binding/list.go index 24020080de3..a9f186808c4 100644 --- a/pkg/binding/list.go +++ b/pkg/binding/list.go @@ -61,22 +61,24 @@ func (o *BindingClient) ListAllBindings(devfileObj *parser.DevfileObj, context s } } - specs, bindings, err := o.kubernetesClient.ListServiceBindingsFromAllGroups() - if err != nil { - return nil, nil, err - } - - for i := range specs { - bindingList, err = o.process(bindingList, &specs[i]) + if o.kubernetesClient != nil { + specs, bindings, err := o.kubernetesClient.ListServiceBindingsFromAllGroups() if err != nil { return nil, nil, err } - } - for i := range bindings { - bindingList, err = o.process(bindingList, &bindings[i]) - if err != nil { - return nil, nil, err + for i := range specs { + bindingList, err = o.process(bindingList, &specs[i]) + if err != nil { + return nil, nil, err + } + } + + for i := range bindings { + bindingList, err = o.process(bindingList, &bindings[i]) + if err != nil { + return nil, nil, err + } } } diff --git a/pkg/component/component.go b/pkg/component/component.go index bdc82728781..cdcb142a11a 100644 --- a/pkg/component/component.go +++ b/pkg/component/component.go @@ -19,8 +19,10 @@ import ( "github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/kclient" odolabels "github.com/redhat-developer/odo/pkg/labels" + "github.com/redhat-developer/odo/pkg/odo/commonflags" odocontext "github.com/redhat-developer/odo/pkg/odo/context" "github.com/redhat-developer/odo/pkg/platform" + "github.com/redhat-developer/odo/pkg/podman" "github.com/redhat-developer/odo/pkg/util" corev1 "k8s.io/api/core/v1" @@ -170,6 +172,7 @@ func ListAllClusterComponents(client kclient.ClientInterface, namespace string) ManagedBy: managedBy, Type: componentType, ManagedByVersion: managedByVersion, + RunningOn: commonflags.RunOnCluster, } mode := odolabels.GetMode(labels) componentFound := false @@ -204,14 +207,26 @@ func ListAllClusterComponents(client kclient.ClientInterface, namespace string) return components, nil } -func ListAllComponents(client kclient.ClientInterface, namespace string, devObj *parser.DevfileObj, componentName string) ([]api.ComponentAbstract, string, error) { - var devfileComponents []api.ComponentAbstract - var err error +func ListAllComponents(client kclient.ClientInterface, podmanClient podman.Client, namespace string, devObj *parser.DevfileObj, componentName string) ([]api.ComponentAbstract, string, error) { + var ( + allComponents []api.ComponentAbstract + ) + if client != nil { - devfileComponents, err = ListAllClusterComponents(client, namespace) + clusterComponents, err := ListAllClusterComponents(client, namespace) + if err != nil { + return nil, "", err + } + allComponents = append(allComponents, clusterComponents...) + } + + // PdomanClient can be nil if experimental mode is not active + if podmanClient != nil { + podmanComponents, err := podmanClient.ListAllComponents() if err != nil { return nil, "", err } + allComponents = append(allComponents, podmanComponents...) } localComponent := api.ComponentAbstract{ @@ -225,12 +240,12 @@ func ListAllComponents(client kclient.ClientInterface, namespace string, devObj componentInDevfile := "" if localComponent.Name != "" { - if !Contains(localComponent, devfileComponents) { - devfileComponents = append(devfileComponents, localComponent) + if !Contains(localComponent, allComponents) { + allComponents = append(allComponents, localComponent) } componentInDevfile = localComponent.Name } - return devfileComponents, componentInDevfile, nil + return allComponents, componentInDevfile, nil } func getResourcesForComponent( diff --git a/pkg/component/component_test.go b/pkg/component/component_test.go index 8978051919b..a88f616b4dd 100644 --- a/pkg/component/component_test.go +++ b/pkg/component/component_test.go @@ -85,6 +85,7 @@ func TestListAllClusterComponents(t *testing.T) { ManagedByVersion: "", RunningIn: nil, Type: "Unknown", + RunningOn: "cluster", }}, wantErr: false, }, @@ -125,12 +126,14 @@ func TestListAllClusterComponents(t *testing.T) { ManagedByVersion: "", RunningIn: nil, Type: "Unknown", + RunningOn: "cluster", }, { Name: "svc1", ManagedBy: "odo", ManagedByVersion: "v3.0.0-beta3", RunningIn: nil, Type: "nodejs", + RunningOn: "cluster", }}, wantErr: false, }, @@ -156,7 +159,8 @@ func TestListAllClusterComponents(t *testing.T) { "dev": true, "deploy": true, }, - Type: "nodejs", + Type: "nodejs", + RunningOn: "cluster", }}, wantErr: false, }, diff --git a/pkg/dev/podmandev/pod.go b/pkg/dev/podmandev/pod.go index dfd7c37b1c3..63121b63cbe 100644 --- a/pkg/dev/podmandev/pod.go +++ b/pkg/dev/podmandev/pod.go @@ -80,7 +80,6 @@ func createPodFromComponent( } } - // TODO add labels (for GetRunningPodFromSelector) pod := corev1.Pod{ Spec: corev1.PodSpec{ Containers: containers, @@ -97,6 +96,7 @@ func createPodFromComponent( runtime := component.GetComponentRuntimeFromDevfileMetadata(devfileObj.Data.GetMetadata()) pod.SetLabels(labels.GetLabels(componentName, appName, runtime, labels.ComponentDevMode, true)) + labels.SetProjectType(pod.GetLabels(), component.GetComponentTypeFromDevfileMetadata(devfileObj.Data.GetMetadata())) return &pod, fwPorts, nil } diff --git a/pkg/dev/podmandev/pod_test.go b/pkg/dev/podmandev/pod_test.go index 9bb70fffa15..fa39065bf0a 100644 --- a/pkg/dev/podmandev/pod_test.go +++ b/pkg/dev/podmandev/pod_test.go @@ -58,6 +58,7 @@ var ( "app.kubernetes.io/part-of": appName, "component": devfileName, "odo.dev/mode": labels.ComponentDevMode, + "odo.dev/project-type": "Not available", }, }, Spec: corev1.PodSpec{ diff --git a/pkg/odo/cli/cli.go b/pkg/odo/cli/cli.go index 1e87aebfdb4..f77875f1f7f 100644 --- a/pkg/odo/cli/cli.go +++ b/pkg/odo/cli/cli.go @@ -180,7 +180,7 @@ func odoRootCmd(ctx context.Context, name, fullName string) *cobra.Command { version.NewCmdVersion(version.RecommendedCommandName, util.GetFullName(fullName, version.RecommendedCommandName)), preference.NewCmdPreference(ctx, preference.RecommendedCommandName, util.GetFullName(fullName, preference.RecommendedCommandName)), telemetry.NewCmdTelemetry(telemetry.RecommendedCommandName), - list.NewCmdList(list.RecommendedCommandName, util.GetFullName(fullName, list.RecommendedCommandName)), + list.NewCmdList(ctx, list.RecommendedCommandName, util.GetFullName(fullName, list.RecommendedCommandName)), build_images.NewCmdBuildImages(build_images.RecommendedCommandName, util.GetFullName(fullName, build_images.RecommendedCommandName)), deploy.NewCmdDeploy(deploy.RecommendedCommandName, util.GetFullName(fullName, deploy.RecommendedCommandName)), _init.NewCmdInit(_init.RecommendedCommandName, util.GetFullName(fullName, _init.RecommendedCommandName)), diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index 5b0eb1731fa..a3530fb8f63 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -101,12 +101,16 @@ func (o *DevOptions) Validate(ctx context.Context) error { return clierrors.NewNoCommandInDevfileError("debug") } - platform := fcontext.GetRunOn(ctx) + platform := fcontext.GetRunOn(ctx, commonflags.RunOnCluster) switch platform { case commonflags.RunOnCluster: if o.clientset.KubernetesClient == nil { return errors.New("no connection to cluster defined") } + case commonflags.RunOnPodman: + if o.clientset.PodmanClient == nil { + return errors.New("unable to access podman. Do you have podman client installed?") + } } return nil } @@ -118,7 +122,7 @@ func (o *DevOptions) Run(ctx context.Context) (err error) { path = filepath.Dir(devfilePath) componentName = odocontext.GetComponentName(ctx) variables = fcontext.GetVariables(ctx) - platform = fcontext.GetRunOn(ctx) + platform = fcontext.GetRunOn(ctx, commonflags.RunOnCluster) ) var dest string @@ -226,7 +230,7 @@ It forwards endpoints with any exposure values ('public', 'internal' or 'none') clientset.FILESYSTEM, clientset.INIT, clientset.KUBERNETES_NULLABLE, - clientset.PODMAN, + clientset.PODMAN_NULLABLE, clientset.PORT_FORWARD, clientset.PREFERENCE, clientset.STATE, diff --git a/pkg/odo/cli/list/component/list.go b/pkg/odo/cli/list/component/list.go index e0c1fdd0c4e..bb01e6c3c81 100644 --- a/pkg/odo/cli/list/component/list.go +++ b/pkg/odo/cli/list/component/list.go @@ -10,6 +10,7 @@ import ( "github.com/spf13/cobra" "github.com/redhat-developer/odo/pkg/api" + "github.com/redhat-developer/odo/pkg/odo/cli/feature" "github.com/redhat-developer/odo/pkg/odo/cli/ui" "github.com/redhat-developer/odo/pkg/odo/commonflags" @@ -17,6 +18,7 @@ import ( "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/odo/cmdline" + fcontext "github.com/redhat-developer/odo/pkg/odo/commonflags/context" odocontext "github.com/redhat-developer/odo/pkg/odo/context" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" "github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" @@ -91,7 +93,7 @@ func (lo *ListOptions) Run(ctx context.Context) error { listSpinner.End(true) - HumanReadableOutput(list) + HumanReadableOutput(ctx, list) return nil } @@ -104,20 +106,38 @@ func (lo *ListOptions) run(ctx context.Context) (api.ResourcesList, error) { var ( devfileObj = odocontext.GetDevfileObj(ctx) componentName = odocontext.GetComponentName(ctx) + + kubeClient = lo.clientset.KubernetesClient + podmanClient = lo.clientset.PodmanClient ) - devfileComponents, componentInDevfile, err := component.ListAllComponents( - lo.clientset.KubernetesClient, lo.namespaceFilter, devfileObj, componentName) + + switch fcontext.GetRunOn(ctx, "") { + case commonflags.RunOnCluster: + podmanClient = nil + case commonflags.RunOnPodman: + kubeClient = nil + } + + allComponents, componentInDevfile, err := component.ListAllComponents( + kubeClient, podmanClient, lo.namespaceFilter, devfileObj, componentName) if err != nil { return api.ResourcesList{}, err } + + // RunningOn is displayed only when RunOn is active + if !feature.IsEnabled(ctx, feature.GenericRunOnFlag) { + for i := range allComponents { + allComponents[i].RunningOn = "" + } + } return api.ResourcesList{ ComponentInDevfile: componentInDevfile, - Components: devfileComponents, + Components: allComponents, }, nil } // NewCmdList implements the list odo command -func NewCmdComponentList(name, fullName string) *cobra.Command { +func NewCmdComponentList(ctx context.Context, name, fullName string) *cobra.Command { o := NewListOptions() var listCmd = &cobra.Command{ @@ -133,15 +153,18 @@ func NewCmdComponentList(name, fullName string) *cobra.Command { Aliases: []string{"components"}, } clientset.Add(listCmd, clientset.KUBERNETES_NULLABLE, clientset.FILESYSTEM) - + if feature.IsEnabled(ctx, feature.GenericRunOnFlag) { + clientset.Add(listCmd, clientset.PODMAN_NULLABLE) + } listCmd.Flags().StringVar(&o.namespaceFlag, "namespace", "", "Namespace for odo to scan for components") commonflags.UseOutputFlag(listCmd) + commonflags.UseRunOnFlag(listCmd) return listCmd } -func HumanReadableOutput(list api.ResourcesList) { +func HumanReadableOutput(ctx context.Context, list api.ResourcesList) { components := list.Components if len(components) == 0 { log.Error("There are no components deployed.") @@ -151,7 +174,11 @@ func HumanReadableOutput(list api.ResourcesList) { t := ui.NewTable() // Create the header and then sort accordingly - t.AppendHeader(table.Row{"NAME", "PROJECT TYPE", "RUNNING IN", "MANAGED"}) + headers := table.Row{"NAME", "PROJECT TYPE", "RUNNING IN", "MANAGED"} + if feature.IsEnabled(ctx, feature.GenericRunOnFlag) { + headers = append(headers, "RUNNING ON") + } + t.AppendHeader(headers) t.SortBy([]table.SortBy{ {Name: "MANAGED", Mode: table.Asc}, {Name: "NAME", Mode: table.Dsc}, @@ -190,7 +217,17 @@ func HumanReadableOutput(list api.ResourcesList) { managedBy = text.Colors{text.FgBlue}.Sprintf(managedBy) } - t.AppendRow(table.Row{name, componentType, mode, managedBy}) + row := table.Row{name, componentType, mode, managedBy} + + if feature.IsEnabled(ctx, feature.GenericRunOnFlag) { + runningOn := comp.RunningOn + if runningOn == "" { + runningOn = "None" + } + row = append(row, runningOn) + } + + t.AppendRow(row) } t.Render() diff --git a/pkg/odo/cli/list/list.go b/pkg/odo/cli/list/list.go index de035a7867c..ddbe2c81b5a 100644 --- a/pkg/odo/cli/list/list.go +++ b/pkg/odo/cli/list/list.go @@ -5,26 +5,26 @@ import ( "errors" "fmt" - "github.com/redhat-developer/odo/pkg/odo/cli/list/services" - "github.com/redhat-developer/odo/pkg/odo/commonflags" - "github.com/spf13/cobra" "github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/component" "github.com/redhat-developer/odo/pkg/log" + "github.com/redhat-developer/odo/pkg/odo/cli/feature" "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" - - dfutil "github.com/devfile/library/pkg/util" - + "github.com/redhat-developer/odo/pkg/odo/cli/list/services" "github.com/redhat-developer/odo/pkg/odo/cmdline" + "github.com/redhat-developer/odo/pkg/odo/commonflags" + fcontext "github.com/redhat-developer/odo/pkg/odo/commonflags/context" odocontext "github.com/redhat-developer/odo/pkg/odo/context" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" "github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" odoutil "github.com/redhat-developer/odo/pkg/odo/util" + dfutil "github.com/devfile/library/pkg/util" + ktemplates "k8s.io/kubectl/pkg/util/templates" ) @@ -74,7 +74,7 @@ func (lo *ListOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, ar // if it hasn't, we will search from the default project / namespace. if lo.namespaceFlag != "" { lo.namespaceFilter = lo.namespaceFlag - } else { + } else if lo.clientset.KubernetesClient != nil { lo.namespaceFilter = odocontext.GetNamespace(ctx) } @@ -99,7 +99,7 @@ func (lo *ListOptions) Run(ctx context.Context) error { listSpinner.End(true) fmt.Printf("\nComponents:\n") - clicomponent.HumanReadableOutput(list) + clicomponent.HumanReadableOutput(ctx, list) fmt.Printf("\nBindings:\n") binding.HumanReadableOutput(lo.namespaceFilter, list) return nil @@ -114,29 +114,50 @@ func (lo *ListOptions) run(ctx context.Context) (list api.ResourcesList, err err var ( devfileObj = odocontext.GetDevfileObj(ctx) componentName = odocontext.GetComponentName(ctx) + + kubeClient = lo.clientset.KubernetesClient + podmanClient = lo.clientset.PodmanClient ) - devfileComponents, componentInDevfile, err := component.ListAllComponents( - lo.clientset.KubernetesClient, lo.namespaceFilter, devfileObj, componentName) + + switch fcontext.GetRunOn(ctx, "") { + case commonflags.RunOnCluster: + podmanClient = nil + case commonflags.RunOnPodman: + kubeClient = nil + } + + allComponents, componentInDevfile, err := component.ListAllComponents( + kubeClient, podmanClient, lo.namespaceFilter, devfileObj, componentName) if err != nil { return api.ResourcesList{}, err } + var bindings []api.ServiceBinding + var inDevfile []string + workingDir := odocontext.GetWorkingDirectory(ctx) - bindings, inDevfile, err := lo.clientset.BindingClient.ListAllBindings(devfileObj, workingDir) + bindings, inDevfile, err = lo.clientset.BindingClient.ListAllBindings(devfileObj, workingDir) if err != nil { return api.ResourcesList{}, err } + // RunningOn is displayed only when RunOn is active + if !feature.IsEnabled(ctx, feature.GenericRunOnFlag) { + for i := range allComponents { + allComponents[i].RunningOn = "" + } + } + return api.ResourcesList{ ComponentInDevfile: componentInDevfile, - Components: devfileComponents, + Components: allComponents, BindingsInDevfile: inDevfile, Bindings: bindings, }, nil } // NewCmdList implements the list odo command -func NewCmdList(name, fullName string) *cobra.Command { +func NewCmdList(ctx context.Context, name, fullName string) *cobra.Command { o := NewListOptions() var listCmd = &cobra.Command{ @@ -150,11 +171,14 @@ func NewCmdList(name, fullName string) *cobra.Command { return genericclioptions.GenericRun(o, cmd, args) }, } - clientset.Add(listCmd, clientset.KUBERNETES, clientset.BINDING, clientset.FILESYSTEM) + clientset.Add(listCmd, clientset.KUBERNETES_NULLABLE, clientset.BINDING, clientset.FILESYSTEM) + if feature.IsEnabled(ctx, feature.GenericRunOnFlag) { + clientset.Add(listCmd, clientset.PODMAN_NULLABLE) + } namespaceCmd := namespace.NewCmdNamespaceList(namespace.RecommendedCommandName, odoutil.GetFullName(fullName, namespace.RecommendedCommandName)) bindingCmd := binding.NewCmdBindingList(binding.RecommendedCommandName, odoutil.GetFullName(fullName, binding.RecommendedCommandName)) - componentCmd := clicomponent.NewCmdComponentList(clicomponent.RecommendedCommandName, odoutil.GetFullName(fullName, clicomponent.RecommendedCommandName)) + componentCmd := clicomponent.NewCmdComponentList(ctx, clicomponent.RecommendedCommandName, odoutil.GetFullName(fullName, clicomponent.RecommendedCommandName)) servicesCmd := services.NewCmdServicesList(services.RecommendedCommandName, odoutil.GetFullName(fullName, services.RecommendedCommandName)) listCmd.AddCommand(namespaceCmd, bindingCmd, componentCmd, servicesCmd) @@ -162,6 +186,7 @@ func NewCmdList(name, fullName string) *cobra.Command { listCmd.Flags().StringVar(&o.namespaceFlag, "namespace", "", "Namespace for odo to scan for components") commonflags.UseOutputFlag(listCmd) + commonflags.UseRunOnFlag(listCmd) return listCmd } diff --git a/pkg/odo/commonflags/context/context.go b/pkg/odo/commonflags/context/context.go index 26f3c6f757e..5c5b7d6a600 100644 --- a/pkg/odo/commonflags/context/context.go +++ b/pkg/odo/commonflags/context/context.go @@ -2,8 +2,6 @@ package context import ( "context" - - "github.com/redhat-developer/odo/pkg/odo/commonflags" ) const ( @@ -42,12 +40,12 @@ func WithRunOn(ctx context.Context, val string) context.Context { } // GetRunOn gets value of run-on flag in ctx -func GetRunOn(ctx context.Context) string { +func GetRunOn(ctx context.Context, defaultValue string) string { value := ctx.Value(runOnKey) if cast, ok := value.(string); ok { return cast } - return commonflags.RunOnDefault + return defaultValue } // WithVariables sets the value for the --var-file and --var flags in ctx diff --git a/pkg/odo/commonflags/context/context_test.go b/pkg/odo/commonflags/context/context_test.go index bf5eea58daf..13b5e5bcdac 100644 --- a/pkg/odo/commonflags/context/context_test.go +++ b/pkg/odo/commonflags/context/context_test.go @@ -32,21 +32,21 @@ func TestOutput(t *testing.T) { func TestRunOn(t *testing.T) { ctx := context.TODO() ctx = WithRunOn(ctx, commonflags.RunOnCluster) - res := GetRunOn(ctx) + res := GetRunOn(ctx, commonflags.RunOnCluster) if res != commonflags.RunOnCluster { t.Errorf("GetOutput should return %q but returns %q", commonflags.RunOnCluster, res) } ctx = context.TODO() ctx = WithRunOn(ctx, commonflags.RunOnPodman) - res = GetRunOn(ctx) + res = GetRunOn(ctx, commonflags.RunOnCluster) if res != commonflags.RunOnPodman { t.Errorf("GetOutput should return %q but returns %q", commonflags.RunOnPodman, res) } ctx = context.TODO() - res = GetRunOn(ctx) - if res != commonflags.RunOnDefault { - t.Errorf("GetOutput should return %q (default) but returns %q", commonflags.RunOnDefault, res) + res = GetRunOn(ctx, commonflags.RunOnCluster) + if res != commonflags.RunOnCluster { + t.Errorf("GetOutput should return %q (default) but returns %q", commonflags.RunOnCluster, res) } } diff --git a/pkg/odo/commonflags/run_on.go b/pkg/odo/commonflags/run_on.go index c9dcb6cf086..6485998d996 100644 --- a/pkg/odo/commonflags/run_on.go +++ b/pkg/odo/commonflags/run_on.go @@ -63,9 +63,5 @@ func CheckRunOnCommand(cmd *cobra.Command) error { // GetRunOnValue returns value of --run-on flag or default value func GetRunOnValue(cmd cmdline.Cmdline) string { - val := cmd.FlagValueIfSet(RunOnFlagName) - if val == "" { - val = RunOnDefault - } - return val + return cmd.FlagValueIfSet(RunOnFlagName) } diff --git a/pkg/odo/genericclioptions/clientset/clientset.go b/pkg/odo/genericclioptions/clientset/clientset.go index 83d9c682752..b373e1239db 100644 --- a/pkg/odo/genericclioptions/clientset/clientset.go +++ b/pkg/odo/genericclioptions/clientset/clientset.go @@ -12,8 +12,6 @@ package clientset import ( - "fmt" - "github.com/spf13/cobra" "github.com/redhat-developer/odo/pkg/dev/kubedev" @@ -66,6 +64,8 @@ const ( LOGS = "DEP_LOGS" // PODMAN instantiates client for pkg/podman PODMAN = "DEP_PODMAN" + // PODMAN_NULLABLE instantiates client for pkg/podman, can be nil + PODMAN_NULLABLE = "DEP_PODMAN_NULLABLE" // PORT_FORWARD instantiates client for pkg/portForward PORT_FORWARD = "PORT_FORWARD" // PREFERENCE instantiates client for pkg/preference @@ -89,10 +89,10 @@ var subdeps map[string][]string = map[string][]string{ ALIZER: {REGISTRY}, DELETE_COMPONENT: {KUBERNETES_NULLABLE, EXEC}, DEPLOY: {KUBERNETES, FILESYSTEM}, - DEV: {BINDING, DELETE_COMPONENT, EXEC, FILESYSTEM, KUBERNETES_NULLABLE, PODMAN, PORT_FORWARD, PREFERENCE, STATE, SYNC, WATCH}, + DEV: {BINDING, DELETE_COMPONENT, EXEC, FILESYSTEM, KUBERNETES_NULLABLE, PODMAN_NULLABLE, PORT_FORWARD, PREFERENCE, STATE, SYNC, WATCH}, EXEC: {KUBERNETES_NULLABLE}, INIT: {ALIZER, FILESYSTEM, PREFERENCE, REGISTRY}, - LOGS: {KUBERNETES_NULLABLE, PODMAN}, + LOGS: {KUBERNETES_NULLABLE, PODMAN_NULLABLE}, PORT_FORWARD: {KUBERNETES_NULLABLE, STATE}, PROJECT: {KUBERNETES}, REGISTRY: {FILESYSTEM, PREFERENCE}, @@ -145,8 +145,11 @@ func isDefined(command *cobra.Command, dependency string) bool { } func Fetch(command *cobra.Command, platform string) (*Clientset, error) { - dep := Clientset{} - var err error + var ( + err error + dep = Clientset{} + ctx = command.Context() + ) /* Without sub-dependencies */ if isDefined(command, FILESYSTEM) { @@ -163,11 +166,17 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { } } - if isDefined(command, PODMAN) { - dep.PodmanClient = podman.NewPodmanCli() + if isDefined(command, PODMAN) || isDefined(command, PODMAN_NULLABLE) { + dep.PodmanClient, err = podman.NewPodmanCli(ctx) + if err != nil { + if isDefined(command, PODMAN) { + return nil, err + } + dep.PodmanClient = nil + } } if isDefined(command, PREFERENCE) { - dep.PreferenceClient, err = preference.NewClient(command.Context()) + dep.PreferenceClient, err = preference.NewClient(ctx) if err != nil { return nil, err } @@ -182,12 +191,10 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { } if isDefined(command, EXEC) { switch platform { - case commonflags.RunOnCluster: - dep.ExecClient = exec.NewExecClient(dep.KubernetesClient) case commonflags.RunOnPodman: dep.ExecClient = exec.NewExecClient(dep.PodmanClient) default: - panic(fmt.Sprintf("not implemented yet for platform %q", platform)) + dep.ExecClient = exec.NewExecClient(dep.KubernetesClient) } } if isDefined(command, DELETE_COMPONENT) { @@ -201,12 +208,10 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { } if isDefined(command, LOGS) { switch platform { - case commonflags.RunOnCluster: - dep.LogsClient = logs.NewLogsClient(dep.KubernetesClient) case commonflags.RunOnPodman: dep.LogsClient = logs.NewLogsClient(dep.PodmanClient) default: - panic(fmt.Sprintf("not implemented yet for platform %q", platform)) + dep.LogsClient = logs.NewLogsClient(dep.KubernetesClient) } } if isDefined(command, PROJECT) { @@ -217,12 +222,10 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { } if isDefined(command, SYNC) { switch platform { - case commonflags.RunOnCluster: - dep.SyncClient = sync.NewSyncClient(dep.KubernetesClient, dep.ExecClient) case commonflags.RunOnPodman: dep.SyncClient = sync.NewSyncClient(dep.PodmanClient, dep.ExecClient) default: - panic(fmt.Sprintf("not implemented yet for platform %q", platform)) + dep.SyncClient = sync.NewSyncClient(dep.KubernetesClient, dep.ExecClient) } } if isDefined(command, WATCH) { @@ -236,7 +239,15 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { } if isDefined(command, DEV) { switch platform { - case commonflags.RunOnCluster: + case commonflags.RunOnPodman: + dep.DevClient = podmandev.NewDevClient( + dep.PodmanClient, + dep.SyncClient, + dep.ExecClient, + dep.StateClient, + dep.WatchClient, + ) + default: dep.DevClient = kubedev.NewDevClient( dep.KubernetesClient, dep.PreferenceClient, @@ -248,16 +259,6 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { dep.ExecClient, dep.DeleteClient, ) - case commonflags.RunOnPodman: - dep.DevClient = podmandev.NewDevClient( - dep.PodmanClient, - dep.SyncClient, - dep.ExecClient, - dep.StateClient, - dep.WatchClient, - ) - default: - panic(fmt.Sprintf("not implemented yet for platform %q", platform)) } } diff --git a/pkg/odo/genericclioptions/runnable.go b/pkg/odo/genericclioptions/runnable.go index c53cee9c682..2089afae759 100644 --- a/pkg/odo/genericclioptions/runnable.go +++ b/pkg/odo/genericclioptions/runnable.go @@ -206,7 +206,9 @@ func GenericRun(o Runnable, cmd *cobra.Command, args []string) error { feature.DisplayWarnings() ctx = fcontext.WithJsonOutput(ctx, commonflags.GetJsonOutputValue(cmdLineObj)) - ctx = fcontext.WithRunOn(ctx, platform) + if platform != "" { + ctx = fcontext.WithRunOn(ctx, platform) + } ctx = odocontext.WithApplication(ctx, defaultAppName) if deps.KubernetesClient != nil { diff --git a/pkg/podman/component.go b/pkg/podman/component.go new file mode 100644 index 00000000000..36513abc329 --- /dev/null +++ b/pkg/podman/component.go @@ -0,0 +1,84 @@ +package podman + +import ( + "encoding/json" + "fmt" + "os/exec" + "strings" + + "github.com/redhat-developer/odo/pkg/api" + odolabels "github.com/redhat-developer/odo/pkg/labels" + "github.com/redhat-developer/odo/pkg/odo/commonflags" + "k8s.io/klog" +) + +type ListPodsReport struct { + Name string + Labels map[string]string +} + +func (o *PodmanCli) ListAllComponents() ([]api.ComponentAbstract, error) { + cmd := exec.Command(o.podmanCmd, "pod", "ps", "--format", "json", "--filter", "status=running") + klog.V(3).Infof("executing %v", cmd.Args) + out, err := cmd.Output() + if err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr)) + } + return nil, err + } + + var list []ListPodsReport + if err = json.Unmarshal(out, &list); err != nil { + return nil, err + } + + for _, pod := range list { + klog.V(5).Infof("\npod name: %s", pod.Name) + klog.V(5).Infof("labels:") + for k, v := range pod.Labels { + klog.V(5).Infof(" - %s: %s", k, v) + } + } + + var components []api.ComponentAbstract + + for _, pod := range list { + + labels := pod.Labels + + // Figure out the correct name to use + // if there is no instance label (app.kubernetes.io/instance), + // we SKIP the resource as it is not a component essential for Kubernetes. + name := odolabels.GetComponentName(labels) + if name == "" { + continue + } + + // Get the component type (if there is any..) + componentType, err := odolabels.GetProjectType(labels, nil) + if err != nil || componentType == "" { + componentType = api.TypeUnknown + } + + managedBy := odolabels.GetManagedBy(labels) + managedByVersion := odolabels.GetManagedByVersion(labels) + + // Generate the appropriate "component" with all necessary information + component := api.ComponentAbstract{ + Name: name, + ManagedBy: managedBy, + Type: componentType, + ManagedByVersion: managedByVersion, + RunningOn: commonflags.RunOnPodman, + } + mode := odolabels.GetMode(labels) + if mode != "" { + component.RunningIn = api.NewRunningModes() + component.RunningIn.AddRunningMode(api.RunningMode(strings.ToLower(mode))) + } + components = append(components, component) + } + + return components, nil +} diff --git a/pkg/podman/exec.go b/pkg/podman/exec.go index 5cfdc3d0be1..83ddf034c5e 100644 --- a/pkg/podman/exec.go +++ b/pkg/podman/exec.go @@ -21,10 +21,10 @@ func (o *PodmanCli) ExecCMDInContainer(containerName, podName string, cmd []stri args = append(args, name) args = append(args, cmd...) - command := exec.Command("podman", args...) + command := exec.Command(o.podmanCmd, args...) + klog.V(3).Infof("executing %v", command.Args) command.Stdin = stdin - klog.V(4).Infof("exec podman %v\n", args) out, err := command.Output() if err != nil { return err diff --git a/pkg/podman/interface.go b/pkg/podman/interface.go index a9f1db147c8..195a7461043 100644 --- a/pkg/podman/interface.go +++ b/pkg/podman/interface.go @@ -3,6 +3,7 @@ package podman import ( "io" + "github.com/redhat-developer/odo/pkg/api" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -41,4 +42,6 @@ type Client interface { // GetRunningPodFromSelector returns any pod matching the given label selector. // If multiple pods are found, implementations might have different behavior, by either returning an error or returning any element. GetRunningPodFromSelector(selector string) (*corev1.Pod, error) + + ListAllComponents() ([]api.ComponentAbstract, error) } diff --git a/pkg/podman/podman.go b/pkg/podman/podman.go index b9356ce8037..576159eb299 100644 --- a/pkg/podman/podman.go +++ b/pkg/podman/podman.go @@ -2,20 +2,38 @@ package podman import ( "bufio" + "context" "fmt" "os/exec" "strings" + envcontext "github.com/redhat-developer/odo/pkg/config/context" + corev1 "k8s.io/api/core/v1" jsonserializer "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/klog" "k8s.io/kubectl/pkg/scheme" ) -type PodmanCli struct{} +type PodmanCli struct { + podmanCmd string +} + +// NewPodmanCli returns a new podman client, or nil if the podman command is not accessible in the system +func NewPodmanCli(ctx context.Context) (*PodmanCli, error) { + // Check if podman is available in the system + cli := &PodmanCli{ + podmanCmd: envcontext.GetEnvConfig(ctx).PodmanCmd, + } + version, err := cli.Version() + if err != nil { + return nil, fmt.Errorf("executable %q not found", cli.podmanCmd) + } + if version.Client == nil { + return nil, fmt.Errorf("executable %q not recognized as podman client", cli.podmanCmd) + } -func NewPodmanCli() *PodmanCli { - return &PodmanCli{} + return cli, nil } func (o *PodmanCli) PlayKube(pod *corev1.Pod) error { @@ -28,7 +46,8 @@ func (o *PodmanCli) PlayKube(pod *corev1.Pod) error { }, ) - cmd := exec.Command("podman", "play", "kube", "-") + cmd := exec.Command(o.podmanCmd, "play", "kube", "-") + klog.V(3).Infof("executing %v", cmd.Args) stdin, err := cmd.StdinPipe() if err != nil { return err @@ -71,7 +90,9 @@ func (o *PodmanCli) PlayKube(pod *corev1.Pod) error { } func (o *PodmanCli) PodStop(podname string) error { - out, err := exec.Command("podman", "pod", "stop", podname).Output() + cmd := exec.Command(o.podmanCmd, "pod", "stop", podname) + klog.V(3).Infof("executing %v", cmd.Args) + out, err := cmd.Output() if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr)) @@ -83,7 +104,9 @@ func (o *PodmanCli) PodStop(podname string) error { } func (o *PodmanCli) PodRm(podname string) error { - out, err := exec.Command("podman", "pod", "rm", podname).Output() + cmd := exec.Command(o.podmanCmd, "pod", "rm", podname) + klog.V(3).Infof("executing %v", cmd.Args) + out, err := cmd.Output() if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr)) @@ -95,7 +118,9 @@ func (o *PodmanCli) PodRm(podname string) error { } func (o *PodmanCli) VolumeRm(volumeName string) error { - out, err := exec.Command("podman", "volume", "rm", volumeName).Output() + cmd := exec.Command(o.podmanCmd, "volume", "rm", volumeName) + klog.V(3).Infof("executing %v", cmd.Args) + out, err := cmd.Output() if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr)) @@ -107,7 +132,9 @@ func (o *PodmanCli) VolumeRm(volumeName string) error { } func (o *PodmanCli) VolumeLs() (map[string]bool, error) { - out, err := exec.Command("podman", "volume", "ls", "--format", "{{.Name}}", "--noheading").Output() + cmd := exec.Command(o.podmanCmd, "volume", "ls", "--format", "{{.Name}}", "--noheading") + klog.V(3).Infof("executing %v", cmd.Args) + out, err := cmd.Output() if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr)) diff --git a/pkg/podman/version.go b/pkg/podman/version.go new file mode 100644 index 00000000000..2b1be324cdd --- /dev/null +++ b/pkg/podman/version.go @@ -0,0 +1,42 @@ +package podman + +import ( + "encoding/json" + "fmt" + "os/exec" + + "k8s.io/klog" +) + +type Version struct { + APIVersion string + Version string + GoVersion string + GitCommit string + BuiltTime string + Built int64 + OsArch string + Os string +} + +type SystemVersionReport struct { + Client *Version `json:",omitempty"` +} + +func (o *PodmanCli) Version() (SystemVersionReport, error) { + cmd := exec.Command(o.podmanCmd, "version", "--format", "json") + klog.V(3).Infof("executing %v", cmd.Args) + out, err := cmd.Output() + if err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr)) + } + return SystemVersionReport{}, err + } + var result SystemVersionReport + err = json.Unmarshal(out, &result) + if err != nil { + return SystemVersionReport{}, err + } + return result, nil +} diff --git a/tests/integration/cmd_devfile_list_test.go b/tests/integration/cmd_devfile_list_test.go index d3bdf4eb07d..774a511e6ae 100644 --- a/tests/integration/cmd_devfile_list_test.go +++ b/tests/integration/cmd_devfile_list_test.go @@ -96,83 +96,187 @@ var _ = Describe("odo list with devfile", func() { When("a component created in 'app' application", func() { var devSession helper.DevSession + var componentName = "nodejs-prj1-api-abhz" // from devfile-deploy.yaml + BeforeEach(func() { helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context) helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-deploy.yaml"), path.Join(commonVar.Context, "devfile.yaml")) helper.Chdir(commonVar.Context) - var err error - devSession, _, _, _, err = helper.StartDevMode(helper.DevSessionOpts{}) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - devSession.Stop() - devSession.WaitEnd() }) - var checkList = func(componentType string) { - By("checking the normal output", func() { - stdOut := helper.Cmd("odo", "list", "component").ShouldPass().Out() - Expect(stdOut).To(ContainSubstring(componentType)) - }) - } - Context("verifying the managedBy Version in the odo list output", func() { - var version string + When("dev is running on cluster", func() { BeforeEach(func() { - versionOut := helper.Cmd("odo", "version").ShouldPass().Out() - reOdoVersion := regexp.MustCompile(`v[0-9]+.[0-9]+.[0-9]+(?:-\w+)?`) - version = reOdoVersion.FindString(versionOut) - + var err error + devSession, _, _, _, err = helper.StartDevMode(helper.DevSessionOpts{}) + Expect(err).ToNot(HaveOccurred()) }) - It("should show managedBy Version", func() { + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) + + var checkList = func(componentType string) { By("checking the normal output", func() { - stdout := helper.Cmd("odo", "list", "component").ShouldPass().Out() - Expect(stdout).To(ContainSubstring(version)) + stdOut := helper.Cmd("odo", "list", "component").ShouldPass().Out() + Expect(stdOut).To(ContainSubstring(componentType)) }) - By("checking the JSON output", func() { - stdout := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass().Out() - helper.JsonPathContentContain(stdout, "components.0.managedByVersion", version) - }) - }) - }) + } - It("show an odo deploy or dev in the list", func() { - By("should display the component as 'Dev' in odo list", func() { - checkList("Dev") + It("should display runningOn depending on experimental mode", func() { + for _, cmd := range [][]string{ + {"list", "component"}, + {"list"}, + } { + cmd := cmd + By("returning runningOn when experimental mode is enabled with json output", func() { + args := append(cmd, "-o", "json") + res := helper.Cmd("odo", args...).AddEnv("ODO_EXPERIMENTAL_MODE=true").ShouldPass() + stdout, stderr := res.Out(), res.Err() + Expect(stderr).To(BeEmpty()) + Expect(helper.IsJSON(stdout)).To(BeTrue(), "output should be in JSON format") + helper.JsonPathContentIs(stdout, "components.#", "1") + helper.JsonPathContentIs(stdout, "components.0.runningOn", "cluster") + }) + By("not returning runningOn when experimental mode is not enabled with json output", func() { + args := append(cmd, "-o", "json") + res := helper.Cmd("odo", args...).ShouldPass() + stdout, stderr := res.Out(), res.Err() + Expect(stderr).To(BeEmpty()) + Expect(helper.IsJSON(stdout)).To(BeTrue(), "output should be in JSON format") + helper.JsonPathContentIs(stdout, "components.#", "1") + helper.JsonPathDoesNotExist(stdout, "components.0.runningOn") + }) + By("displaying runningOn when experimental mode is enabled", func() { + stdout := helper.Cmd("odo", cmd...).AddEnv("ODO_EXPERIMENTAL_MODE=true").ShouldPass().Out() + Expect(stdout).To(ContainSubstring("RUNNING ON")) + }) + By("not displaying runningOn when experimental mode is not enabled", func() { + stdout := helper.Cmd("odo", cmd...).ShouldPass().Out() + Expect(stdout).ToNot(ContainSubstring("RUNNING ON")) + }) + } }) - By("should display the component as 'Dev' in odo list -o json", func() { - 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()) - helper.JsonPathContentIs(stdout, "components.#", "1") - helper.JsonPathContentContain(stdout, "components.0.runningIn.dev", "true") - helper.JsonPathContentContain(stdout, "components.0.runningIn.deploy", "") - }) + Context("verifying the managedBy Version in the odo list output", func() { + var version string + BeforeEach(func() { + versionOut := helper.Cmd("odo", "version").ShouldPass().Out() + reOdoVersion := regexp.MustCompile(`v[0-9]+.[0-9]+.[0-9]+(?:-\w+)?`) + version = reOdoVersion.FindString(versionOut) - // Fake the odo deploy image build / push passing in "echo" to PODMAN - stdout := helper.Cmd("odo", "deploy").AddEnv("PODMAN_CMD=echo").ShouldPass().Out() - By("building and pushing image to registry", func() { - Expect(stdout).To(ContainSubstring("build -t quay.io/unknown-account/myimage")) - Expect(stdout).To(ContainSubstring("push quay.io/unknown-account/myimage")) + }) + It("should show managedBy Version", func() { + By("checking the normal output", func() { + stdout := helper.Cmd("odo", "list", "component").ShouldPass().Out() + Expect(stdout).To(ContainSubstring(version)) + }) + By("checking the JSON output", func() { + stdout := helper.Cmd("odo", "list", "component", "-o", "json").ShouldPass().Out() + helper.JsonPathContentContain(stdout, "components.0.managedByVersion", version) + }) + }) }) - By("should display the component as 'Deploy' in odo list", func() { - checkList("Dev, Deploy") + It("show an odo deploy or dev in the list", func() { + By("should display the component as 'Dev' in odo list", func() { + checkList("Dev") + }) + + By("should display the component as 'Dev' in odo list -o json", func() { + 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()) + helper.JsonPathContentIs(stdout, "components.#", "1") + helper.JsonPathContentContain(stdout, "components.0.runningIn.dev", "true") + helper.JsonPathContentContain(stdout, "components.0.runningIn.deploy", "") + }) + + // Fake the odo deploy image build / push passing in "echo" to PODMAN + stdout := helper.Cmd("odo", "deploy").AddEnv("PODMAN_CMD=echo").ShouldPass().Out() + By("building and pushing image to registry", func() { + Expect(stdout).To(ContainSubstring("build -t quay.io/unknown-account/myimage")) + Expect(stdout).To(ContainSubstring("push quay.io/unknown-account/myimage")) + }) + + By("should display the component as 'Deploy' in odo list", func() { + checkList("Dev, Deploy") + }) + + By("should display the component as 'Dev, Deploy' in odo list -o json", func() { + 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()) + helper.JsonPathContentIs(stdout, "components.#", "1") + helper.JsonPathContentContain(stdout, "components.0.runningIn.dev", "true") + helper.JsonPathContentContain(stdout, "components.0.runningIn.deploy", "true") + }) }) + }) - By("should display the component as 'Dev, Deploy' in odo list -o json", func() { - 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()) - helper.JsonPathContentIs(stdout, "components.#", "1") - helper.JsonPathContentContain(stdout, "components.0.runningIn.dev", "true") - helper.JsonPathContentContain(stdout, "components.0.runningIn.deploy", "true") + When("dev is running on podman", Label(helper.LabelPodman), func() { + BeforeEach(func() { + var err error + devSession, _, _, _, err = helper.StartDevMode(helper.DevSessionOpts{ + RunOnPodman: true, + }) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() }) + It("should display component depending on experimental mode and run-on flag", func() { + for _, cmd := range [][]string{ + {"list", "component"}, + {"list"}, + } { + cmd := cmd + By("returning component in dev mode when experimental mode is enabled with json output", func() { + args := append(cmd, "-o", "json") + stdout := helper.Cmd("odo", args...).AddEnv("ODO_EXPERIMENTAL_MODE=true").ShouldPass().Out() + Expect(helper.IsJSON(stdout)).To(BeTrue(), "output should be in JSON format") + helper.JsonPathContentIs(stdout, "components.#", "1") + helper.JsonPathContentIs(stdout, "components.0.name", componentName) + helper.JsonPathContentIs(stdout, "components.0.runningIn.dev", "true") + helper.JsonPathContentIs(stdout, "components.0.runningOn", "podman") + }) + By("returning component not in dev mode when experimental mode is enabled with json output and run-on is cluster", func() { + args := append(cmd, "-o", "json", "--run-on", "cluster") + stdout := helper.Cmd("odo", args...).AddEnv("ODO_EXPERIMENTAL_MODE=true").ShouldPass().Out() + Expect(helper.IsJSON(stdout)).To(BeTrue(), "output should be in JSON format") + helper.JsonPathContentIs(stdout, "components.#", "1") + helper.JsonPathContentIs(stdout, "components.0.name", componentName) + helper.JsonPathContentIs(stdout, "components.0.runningIn.dev", "false") + helper.JsonPathDoesNotExist(stdout, "components.0.runningOn") + }) + By("returning component not in dev mode when experimental mode is not enabled with json output", func() { + args := append(cmd, "-o", "json") + stdout := helper.Cmd("odo", args...).ShouldPass().Out() + Expect(helper.IsJSON(stdout)).To(BeTrue(), "output should be in JSON format") + helper.JsonPathContentIs(stdout, "components.#", "1") + helper.JsonPathContentIs(stdout, "components.0.name", componentName) + helper.JsonPathContentIs(stdout, "components.0.runningIn.dev", "false") + helper.JsonPathDoesNotExist(stdout, "components.0.runningOn") + }) + By("displaying component in dev mode when experimental mode is enabled", func() { + stdout := helper.Cmd("odo", cmd...).AddEnv("ODO_EXPERIMENTAL_MODE=true").ShouldPass().Out() + Expect(stdout).To(ContainSubstring(componentName)) + Expect(stdout).To(ContainSubstring("RUNNING ON")) + Expect(stdout).To(ContainSubstring("podman")) + Expect(stdout).To(ContainSubstring("Dev")) + }) + By("displaying component not in dev mode when experimental mode is not enabled", func() { + stdout := helper.Cmd("odo", cmd...).ShouldPass().Out() + Expect(stdout).To(ContainSubstring(componentName)) + Expect(stdout).ToNot(ContainSubstring("RUNNING ON")) + Expect(stdout).To(ContainSubstring("None")) + }) + } + }) }) - }) Context("devfile has missing metadata", func() {