odo describe component (#5725)

* odo describe component

* More fields on named describe

* Doc

* Update pkg/odo/cli/describe/component.go

Co-authored-by: Parthvi Vala <pvala@redhat.com>

* Update pkg/odo/cli/describe/component.go

Co-authored-by: Parthvi Vala <pvala@redhat.com>

* Update pkg/odo/cli/describe/component.go

Co-authored-by: Parthvi Vala <pvala@redhat.com>

* Add Describef

* Parthvi review

* Fix rebase

Co-authored-by: Parthvi Vala <pvala@redhat.com>
This commit is contained in:
Philippe Martin
2022-05-12 12:30:47 +02:00
committed by GitHub
parent 312faae108
commit cb1546a67e
10 changed files with 355 additions and 99 deletions

View File

@@ -8,15 +8,25 @@ sidebar_position: 3
There are 2 ways to describe a component:
- [Describe with access to Devfile](#describe-with-access-to-devfile)
- [Describe without access to Devfile](#describe-without-access-to-devfile)
- [Available Flags](#available-flags)
## Describe with access to Devfile
```shell
odo describe component
```
This command returns information extracted from the Devfile:
- metadata (name, display name, project type, language, version, description and tags)
- supported odo features, indicating if the Devfile defines necessary information to run `odo dev`, `odo dev --debug` and `odo deploy`
- the list of container components,
- the list of Kubernetes components.
The command also displays if the component is currently running in the cluster on Dev and/or Deploy mode.
## Describe without access to Devfile
```shell
odo describe component --name <component_name> [--namespace <namespace>]
```
The command extracts information from the labels and annotations attached to the deployed component to display the known metadata of the Devfile used to deploy the component.
The command also displays if the component is currently running in the cluster on Dev and/or Deploy mode.

View File

@@ -1,5 +1,7 @@
package api
import "strings"
type RunningMode string
type RunningModeList []RunningMode
@@ -9,6 +11,17 @@ const (
RunningModeUnknown RunningMode = "Unknown"
)
func (o RunningModeList) String() string {
if len(o) == 0 {
return "None"
}
strs := make([]string, 0, len(o))
for _, s := range o {
strs = append(strs, string(s))
}
return strings.Join(strs, ", ")
}
func (u RunningModeList) Len() int {
return len(u)
}
@@ -25,7 +38,7 @@ type Component struct {
DevfilePath string `json:"devfilePath,omitempty"`
DevfileData *DevfileData `json:"devfileData,omitempty"`
DevForwardedPorts []ForwardedPort `json:"devForwardedPorts,omitempty"`
RunningIn []RunningMode `json:"runningIn"`
RunningIn RunningModeList `json:"runningIn"`
ManagedBy string `json:"managedBy"`
}

View File

@@ -4,8 +4,8 @@ import "github.com/devfile/library/pkg/devfile/parser/data"
// DevfileData describes a devfile content
type DevfileData struct {
Devfile data.DevfileData `json:"devfile"`
SupportedOdoFeatures SupportedOdoFeatures `json:"supportedOdoFeatures"`
Devfile data.DevfileData `json:"devfile"`
SupportedOdoFeatures *SupportedOdoFeatures `json:"supportedOdoFeatures,omitempty"`
}
// SupportedOdoFeatures indicates the support of high-level (odo) features by a devfile component

View File

@@ -13,8 +13,8 @@ func GetDevfileData(devfileObj parser.DevfileObj) *DevfileData {
}
}
func getSupportedOdoFeatures(devfileData data.DevfileData) SupportedOdoFeatures {
return SupportedOdoFeatures{
func getSupportedOdoFeatures(devfileData data.DevfileData) *SupportedOdoFeatures {
return &SupportedOdoFeatures{
Dev: libdevfile.HasRunCommand(devfileData),
Deploy: libdevfile.HasDeployCommand(devfileData),
Debug: libdevfile.HasDebugCommand(devfileData),

View File

@@ -10,6 +10,7 @@ import (
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/api/v2/pkg/devfile"
"github.com/devfile/library/pkg/devfile/parser"
"github.com/devfile/library/pkg/devfile/parser/data"
dfutil "github.com/devfile/library/pkg/util"
"github.com/redhat-developer/odo/pkg/api"
@@ -22,7 +23,10 @@ import (
"k8s.io/klog"
)
const NotAvailable = "Not available"
const (
NotAvailable = "Not available"
UnknownValue = "Unknown"
)
// GetComponentTypeFromDevfileMetadata returns component type from the devfile metadata;
// it could either be projectType or language, if neither of them are set, return 'Not available'
@@ -211,14 +215,11 @@ func ListAllClusterComponents(client kclient.ClientInterface, namespace string)
return components, nil
}
// GetRunningModes returns the list of modes on which a "name" component is deployed, by looking into namespace
// the resources deployed with matching labels, based on the "odo.dev/mode" label
func GetRunningModes(client kclient.ClientInterface, name string, namespace string) ([]api.RunningMode, error) {
mapResult := map[string]bool{}
func getResourcesForComponent(client kclient.ClientInterface, name string, namespace string) ([]unstructured.Unstructured, error) {
selector := labels.GetSelector(name, "app", labels.ComponentAnyMode)
resourceList, err := client.GetAllResourcesFromSelector(selector, namespace)
if err != nil {
return []api.RunningMode{api.RunningModeUnknown}, nil
return nil, err
}
filteredList := []unstructured.Unstructured{}
for _, resource := range resourceList {
@@ -228,12 +229,23 @@ func GetRunningModes(client kclient.ClientInterface, name string, namespace stri
}
filteredList = append(filteredList, resource)
}
return filteredList, nil
}
if len(filteredList) == 0 {
return nil, NewNoComponentFoundError(name, namespace)
// GetRunningModes returns the list of modes on which a "name" component is deployed, by looking into namespace
// the resources deployed with matching labels, based on the "odo.dev/mode" label
func GetRunningModes(client kclient.ClientInterface, name string) ([]api.RunningMode, error) {
list, err := getResourcesForComponent(client, name, client.GetCurrentNamespace())
if err != nil {
return []api.RunningMode{api.RunningModeUnknown}, nil
}
for _, resource := range filteredList {
if len(list) == 0 {
return nil, NewNoComponentFoundError(name, client.GetCurrentNamespace())
}
mapResult := map[string]bool{}
for _, resource := range list {
resourceLabels := resource.GetLabels()
mode := labels.GetMode(resourceLabels)
if mode != "" {
@@ -258,3 +270,47 @@ func Contains(component OdoComponent, components []OdoComponent) bool {
}
return false
}
// GetDevfileInfoFromCluster extracts information from the labels and annotations of resources to rebuild a Devfile
func GetDevfileInfoFromCluster(client kclient.ClientInterface, name string) (parser.DevfileObj, error) {
list, err := getResourcesForComponent(client, name, client.GetCurrentNamespace())
if err != nil {
return parser.DevfileObj{}, nil
}
if len(list) == 0 {
return parser.DevfileObj{}, nil
}
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
return parser.DevfileObj{}, err
}
metadata := devfileData.GetMetadata()
metadata.Name = UnknownValue
metadata.DisplayName = UnknownValue
metadata.ProjectType = UnknownValue
metadata.Language = UnknownValue
metadata.Version = UnknownValue
metadata.Description = UnknownValue
for _, resource := range list {
labels := resource.GetLabels()
annotations := resource.GetAnnotations()
name := odolabels.GetComponentName(labels)
if len(name) > 0 && metadata.Name == UnknownValue {
metadata.Name = name
}
typ, err := odolabels.GetProjectType(labels, annotations)
if err != nil {
continue
}
if len(typ) > 0 && metadata.ProjectType == UnknownValue {
metadata.ProjectType = typ
}
}
devfileData.SetMetadata(metadata)
return parser.DevfileObj{
Data: devfileData,
}, nil
}

View File

@@ -171,9 +171,8 @@ func TestGetRunningModes(t *testing.T) {
packageManifestResource.SetLabels(labels.Builder().WithMode(labels.ComponentDevMode).Labels())
type args struct {
client func(ctrl *gomock.Controller) kclient.ClientInterface
name string
namespace string
client func(ctrl *gomock.Controller) kclient.ClientInterface
name string
}
tests := []struct {
name string
@@ -186,11 +185,11 @@ func TestGetRunningModes(t *testing.T) {
args: args{
client: func(ctrl *gomock.Controller) kclient.ClientInterface {
c := kclient.NewMockClientInterface(ctrl)
c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{}, nil)
return c
},
name: "aname",
namespace: "anamespace",
name: "aname",
},
want: nil,
wantErr: true,
@@ -200,11 +199,11 @@ func TestGetRunningModes(t *testing.T) {
args: args{
client: func(ctrl *gomock.Controller) kclient.ClientInterface {
c := kclient.NewMockClientInterface(ctrl)
c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource}, nil)
return c
},
name: "aname",
namespace: "anamespace",
name: "aname",
},
want: nil,
wantErr: true,
@@ -214,11 +213,11 @@ func TestGetRunningModes(t *testing.T) {
args: args{
client: func(ctrl *gomock.Controller) kclient.ClientInterface {
c := kclient.NewMockClientInterface(ctrl)
c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource, otherResource}, nil)
return c
},
name: "aname",
namespace: "anamespace",
name: "aname",
},
want: []api.RunningMode{},
},
@@ -227,11 +226,11 @@ func TestGetRunningModes(t *testing.T) {
args: args{
client: func(ctrl *gomock.Controller) kclient.ClientInterface {
c := kclient.NewMockClientInterface(ctrl)
c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource, otherResource, resourceDev1, resourceDev2}, nil)
return c
},
name: "aname",
namespace: "anamespace",
name: "aname",
},
want: []api.RunningMode{api.RunningModeDev},
},
@@ -240,11 +239,11 @@ func TestGetRunningModes(t *testing.T) {
args: args{
client: func(ctrl *gomock.Controller) kclient.ClientInterface {
c := kclient.NewMockClientInterface(ctrl)
c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource, otherResource, resourceDeploy1, resourceDeploy2}, nil)
return c
},
name: "aname",
namespace: "anamespace",
name: "aname",
},
want: []api.RunningMode{api.RunningModeDeploy},
},
@@ -253,11 +252,11 @@ func TestGetRunningModes(t *testing.T) {
args: args{
client: func(ctrl *gomock.Controller) kclient.ClientInterface {
c := kclient.NewMockClientInterface(ctrl)
c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource, otherResource, resourceDev1, resourceDev2, resourceDeploy1, resourceDeploy2}, nil)
return c
},
name: "aname",
namespace: "anamespace",
name: "aname",
},
want: []api.RunningMode{api.RunningModeDev, api.RunningModeDeploy},
},
@@ -266,11 +265,11 @@ func TestGetRunningModes(t *testing.T) {
args: args{
client: func(ctrl *gomock.Controller) kclient.ClientInterface {
c := kclient.NewMockClientInterface(ctrl)
c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(nil, errors.New("error"))
return c
},
name: "aname",
namespace: "anamespace",
name: "aname",
},
want: []api.RunningMode{api.RunningModeUnknown},
},
@@ -278,7 +277,7 @@ func TestGetRunningModes(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
got, err := GetRunningModes(tt.args.client(ctrl), tt.args.name, tt.args.namespace)
got, err := GetRunningModes(tt.args.client(ctrl), tt.args.name)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
return

View File

@@ -386,6 +386,16 @@ func Sbold(s string) string {
return bold(fmt.Sprint(s))
}
// Describef will print out the first variable as BOLD and then the second not..
// this is intended to be used with `odo describe` and other outputs that list
// a lot of information
func Describef(title string, format string, a ...interface{}) {
if !IsJSON() {
bold := color.New(color.Bold).SprintFunc()
fmt.Fprintf(GetStdout(), "%s%s\n", bold(title), fmt.Sprintf(format, a...))
}
}
// Spinner creates a spinner, sets the prefix then returns it.
// Remember to use .End(bool) to stop the spin / when you're done.
// For example: defer s.End(false)

View File

@@ -5,12 +5,17 @@ import (
"errors"
"fmt"
"path/filepath"
"strings"
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/library/pkg/devfile/parser"
"github.com/devfile/library/pkg/devfile/parser/data/v2/common"
"github.com/spf13/cobra"
ktemplates "k8s.io/kubectl/pkg/util/templates"
"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/machineoutput"
"github.com/redhat-developer/odo/pkg/odo/cmdline"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
@@ -81,20 +86,20 @@ func (o *ComponentOptions) Validate() (err error) {
}
func (o *ComponentOptions) Run(ctx context.Context) error {
result, err := o.run(ctx)
result, devfileObj, err := o.run(ctx)
if err != nil {
return err
}
printHumanReadableOutput(result)
return nil
return printHumanReadableOutput(result, devfileObj)
}
// Run contains the logic for the odo command
func (o *ComponentOptions) RunForJsonOutput(ctx context.Context) (out interface{}, err error) {
return o.run(ctx)
result, _, err := o.run(ctx)
return result, err
}
func (o *ComponentOptions) run(ctx context.Context) (result api.Component, err error) {
func (o *ComponentOptions) run(ctx context.Context) (result api.Component, devfileObj *parser.DevfileObj, err error) {
if o.nameFlag != "" {
return o.describeNamedComponent(o.nameFlag)
}
@@ -102,35 +107,42 @@ func (o *ComponentOptions) run(ctx context.Context) (result api.Component, err e
}
// describeNamedComponent describes a component given its name
func (o *ComponentOptions) describeNamedComponent(name string) (result api.Component, err error) {
runningIn, err := component.GetRunningModes(o.clientset.KubernetesClient, name, o.clientset.KubernetesClient.GetCurrentNamespace())
func (o *ComponentOptions) describeNamedComponent(name string) (result api.Component, devfileObj *parser.DevfileObj, err error) {
runningIn, err := component.GetRunningModes(o.clientset.KubernetesClient, name)
if err != nil {
return api.Component{}, err
return api.Component{}, nil, err
}
devfile, err := component.GetDevfileInfoFromCluster(o.clientset.KubernetesClient, name)
if err != nil {
return api.Component{}, nil, err
}
return api.Component{
DevfileData: &api.DevfileData{
Devfile: devfile.Data,
},
RunningIn: runningIn,
ManagedBy: "odo",
}, nil
}, &devfile, nil
}
// describeDevfileComponent describes the component defined by the devfile in the current directory
func (o *ComponentOptions) describeDevfileComponent() (result api.Component, err error) {
func (o *ComponentOptions) describeDevfileComponent() (result api.Component, devfile *parser.DevfileObj, err error) {
devfileObj := o.EnvSpecificInfo.GetDevfileObj()
path, err := filepath.Abs(".")
if err != nil {
return api.Component{}, err
return api.Component{}, nil, err
}
forwardedPorts, err := o.clientset.StateClient.GetForwardedPorts()
if err != nil {
return api.Component{}, err
return api.Component{}, nil, err
}
runningIn, err := component.GetRunningModes(o.clientset.KubernetesClient, devfileObj.GetMetadataName(), o.clientset.KubernetesClient.GetCurrentNamespace())
runningIn, err := component.GetRunningModes(o.clientset.KubernetesClient, devfileObj.GetMetadataName())
if err != nil {
if !errors.As(err, &component.NoComponentFoundError{}) {
return api.Component{}, err
return api.Component{}, nil, err
} else {
// it is ok if the component is not deployed
forwardedPorts = nil
runningIn = nil
}
}
return api.Component{
@@ -139,11 +151,76 @@ func (o *ComponentOptions) describeDevfileComponent() (result api.Component, err
DevForwardedPorts: forwardedPorts,
RunningIn: runningIn,
ManagedBy: "odo",
}, nil
}, &devfileObj, nil
}
func printHumanReadableOutput(component api.Component) {
// TODO(feloy) #5661
func printHumanReadableOutput(cmp api.Component, devfileObj *parser.DevfileObj) error {
if cmp.DevfileData != nil {
log.Describef("Name: ", cmp.DevfileData.Devfile.GetMetadata().Name)
log.Describef("Display Name: ", cmp.DevfileData.Devfile.GetMetadata().DisplayName)
log.Describef("Project Type: ", cmp.DevfileData.Devfile.GetMetadata().ProjectType)
log.Describef("Language: ", cmp.DevfileData.Devfile.GetMetadata().Language)
log.Describef("Version: ", cmp.DevfileData.Devfile.GetMetadata().Version)
log.Describef("Description: ", cmp.DevfileData.Devfile.GetMetadata().Description)
log.Describef("Tags: ", strings.Join(cmp.DevfileData.Devfile.GetMetadata().Tags, ", "))
fmt.Println()
}
log.Describef("Running in: ", cmp.RunningIn.String())
fmt.Println()
if len(cmp.DevForwardedPorts) > 0 {
log.Info("Forwarded ports:")
for _, port := range cmp.DevForwardedPorts {
log.Printf("%s:%d -> %s:%d", port.LocalAddress, port.LocalPort, port.ContainerName, port.ContainerPort)
}
fmt.Println()
}
log.Info("Supported odo features:")
if cmp.DevfileData != nil && cmp.DevfileData.SupportedOdoFeatures != nil {
log.Printf("Dev: %v", cmp.DevfileData.SupportedOdoFeatures.Dev)
log.Printf("Deploy: %v", cmp.DevfileData.SupportedOdoFeatures.Deploy)
log.Printf("Debug: %v", cmp.DevfileData.SupportedOdoFeatures.Debug)
} else {
log.Printf("Dev: Unknown")
log.Printf("Deploy: Unknown")
log.Printf("Debug: Unknown")
}
fmt.Println()
err := listComponentsNames("Container components:", devfileObj, v1alpha2.ContainerComponentType)
if err != nil {
return err
}
err = listComponentsNames("Kubernetes components:", devfileObj, v1alpha2.KubernetesComponentType)
if err != nil {
return err
}
return nil
}
func listComponentsNames(title string, devfileObj *parser.DevfileObj, typ v1alpha2.ComponentType) error {
if devfileObj == nil {
log.Describef(title, " Unknown")
return nil
}
containers, err := devfileObj.Data.GetComponents(common.DevfileOptions{
ComponentOptions: common.ComponentOptions{ComponentType: typ},
})
if err != nil {
return err
}
if len(containers) == 0 {
return nil
}
log.Info(title)
for _, container := range containers {
log.Printf("%s", container.Name)
}
fmt.Println()
return nil
}
// NewCmdComponent implements the component odo sub-command

View File

@@ -165,7 +165,7 @@ func (o RegistryClient) ListDevfileStacks(registryName, devfileFlag, filterFlag
if err != nil {
return *catalogDevfileList, err
}
devfile.SupportedOdoFeatures = devfileData.SupportedOdoFeatures
devfile.SupportedOdoFeatures = *devfileData.SupportedOdoFeatures
}
devfiles = append(devfiles, devfile)

View File

@@ -27,7 +27,7 @@ var _ = Describe("odo describe command tests", func() {
})
It("should fail", func() {
By("running odo describe component with namespace flag without name flag", func() {
By("running odo describe component -o json with namespace flag without name flag", func() {
res := helper.Cmd("odo", "describe", "component", "--namespace", "default", "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
@@ -35,7 +35,7 @@ var _ = Describe("odo describe command tests", func() {
helper.JsonPathContentContain(stderr, "message", "--namespace can be used only with --name")
})
By("running odo describe component without name and without devfile in the current directory", func() {
By("running odo describe component -o json without name and without devfile in the current directory", func() {
res := helper.Cmd("odo", "describe", "component", "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
@@ -43,13 +43,34 @@ var _ = Describe("odo describe command tests", func() {
helper.JsonPathContentContain(stderr, "message", "no devfile found")
})
By("running odo describe component with an unknown name", func() {
By("running odo describe component -o json with an unknown name", func() {
res := helper.Cmd("odo", "describe", "component", "--name", "unknown-name", "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
Expect(stdout).To(BeEmpty())
helper.JsonPathContentContain(stderr, "message", "no component found with name \"unknown-name\" in the namespace \""+commonVar.Project+"\"")
})
By("running odo describe component with namespace flag without name flag", func() {
res := helper.Cmd("odo", "describe", "component", "--namespace", "default").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
Expect(stderr).To(ContainSubstring("--namespace can be used only with --name"))
})
By("running odo describe component without name and without devfile in the current directory", func() {
res := helper.Cmd("odo", "describe", "component").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
Expect(stderr).To(ContainSubstring("no devfile found"))
})
By("running odo describe component with an unknown name", func() {
res := helper.Cmd("odo", "describe", "component", "--name", "unknown-name").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
Expect(stderr).To(ContainSubstring("no component found with name \"unknown-name\" in the namespace \"" + commonVar.Project + "\""))
})
})
When("creating a component", func() {
@@ -57,7 +78,7 @@ var _ = Describe("odo describe command tests", func() {
helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-registry.yaml")).ShouldPass()
})
checkDevfileDescription := func(jsonContent string, devfileName string) {
checkDevfileJSONDescription := func(jsonContent string, devfileName string) {
helper.JsonPathContentIs(jsonContent, "devfilePath", filepath.Join(commonVar.Context, devfileName))
helper.JsonPathContentIs(jsonContent, "devfileData.devfile.metadata.name", cmpName)
helper.JsonPathContentIs(jsonContent, "devfileData.supportedOdoFeatures.dev", "true")
@@ -66,24 +87,55 @@ var _ = Describe("odo describe command tests", func() {
helper.JsonPathContentIs(jsonContent, "managedBy", "odo")
}
checkDevfileDescription := func(content string, withUnknown bool) {
Expect(content).To(ContainSubstring("Name: " + cmpName))
Expect(content).To(ContainSubstring("Project Type: nodejs"))
if withUnknown {
for _, v := range []string{"Version", "Display Name", "Description", "Language"} {
Expect(content).To(ContainSubstring(v + ": Unknown"))
}
}
}
It("should describe the component in the current directory", func() {
res := helper.Cmd("odo", "describe", "component", "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
checkDevfileDescription(stdout, "devfile.yaml")
helper.JsonPathContentIs(stdout, "runningIn", "")
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
By("running with json output", func() {
res := helper.Cmd("odo", "describe", "component", "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
checkDevfileJSONDescription(stdout, "devfile.yaml")
helper.JsonPathContentIs(stdout, "runningIn", "")
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
})
By("running with default output", func() {
res := helper.Cmd("odo", "describe", "component").ShouldPass()
stdout := res.Out()
checkDevfileDescription(stdout, false)
Expect(stdout).To(ContainSubstring("Running in: None"))
Expect(stdout).ToNot(ContainSubstring("Forwarded ports"))
})
})
It("should not describe the component from another directory", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
res := helper.Cmd("odo", "describe", "component", "--name", cmpName, "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
Expect(stdout).To(BeEmpty())
helper.JsonPathContentContain(stderr, "message", "no component found with name \""+cmpName+"\" in the namespace \""+commonVar.Project+"\"")
By("running with json output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
res := helper.Cmd("odo", "describe", "component", "--name", cmpName, "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
Expect(stdout).To(BeEmpty())
helper.JsonPathContentContain(stderr, "message", "no component found with name \""+cmpName+"\" in the namespace \""+commonVar.Project+"\"")
})
By("running with default output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
res := helper.Cmd("odo", "describe", "component", "--name", cmpName).ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
Expect(stderr).To(ContainSubstring("no component found with name \"" + cmpName + "\" in the namespace \"" + commonVar.Project + "\""))
})
})
When("renaming to hide devfile.yaml file", func() {
@@ -93,13 +145,23 @@ var _ = Describe("odo describe command tests", func() {
})
It("should describe the component in the current directory using the hidden devfile", func() {
res := helper.Cmd("odo", "describe", "component", "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
checkDevfileDescription(stdout, ".devfile.yaml")
helper.JsonPathContentIs(stdout, "runningIn", "")
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
By("running with json output", func() {
res := helper.Cmd("odo", "describe", "component", "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
checkDevfileJSONDescription(stdout, ".devfile.yaml")
helper.JsonPathContentIs(stdout, "runningIn", "")
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
})
By("running with default output", func() {
res := helper.Cmd("odo", "describe", "component").ShouldPass()
stdout := res.Out()
checkDevfileDescription(stdout, false)
Expect(stdout).To(ContainSubstring("Running in: None"))
Expect(stdout).ToNot(ContainSubstring("Forwarded ports"))
})
})
})
@@ -119,31 +181,60 @@ var _ = Describe("odo describe command tests", func() {
})
It("should describe the component in dev mode", func() {
res := helper.Cmd("odo", "describe", "component", "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
checkDevfileDescription(stdout, "devfile.yaml")
helper.JsonPathContentIs(stdout, "devForwardedPorts.#", "1")
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.containerName", "runtime")
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.localAddress", "127.0.0.1")
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.localPort", ports["3000"][len("127.0.0.1:"):])
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.containerPort", "3000")
By("running with json output", func() {
res := helper.Cmd("odo", "describe", "component", "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
checkDevfileJSONDescription(stdout, "devfile.yaml")
helper.JsonPathContentIs(stdout, "devForwardedPorts.#", "1")
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.containerName", "runtime")
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.localAddress", "127.0.0.1")
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.localPort", ports["3000"][len("127.0.0.1:"):])
helper.JsonPathContentIs(stdout, "devForwardedPorts.0.containerPort", "3000")
})
By("running with default output", func() {
res := helper.Cmd("odo", "describe", "component").ShouldPass()
stdout := res.Out()
checkDevfileDescription(stdout, false)
Expect(stdout).To(ContainSubstring("Forwarded ports"))
Expect(stdout).To(ContainSubstring("127.0.0.1:" + ports["3000"][len("127.0.0.1:"):] + " -> runtime:3000"))
})
})
It("should describe the component from another directory", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
res := helper.Cmd("odo", "describe", "component", "--name", cmpName, "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
helper.JsonPathContentIs(stdout, "devfilePath", "")
helper.JsonPathContentIs(stdout, "devfileData", "")
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
helper.JsonPathContentIs(stdout, "runningIn.#", "1")
helper.JsonPathContentIs(stdout, "runningIn.0", "Dev")
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
By("running with json output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
res := helper.Cmd("odo", "describe", "component", "--name", cmpName, "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stdout)).To(BeTrue())
Expect(stderr).To(BeEmpty())
helper.JsonPathContentIs(stdout, "devfilePath", "")
helper.JsonPathContentIs(stdout, "devfileData.devfile.metadata.name", cmpName)
helper.JsonPathContentIs(stdout, "devfileData.devfile.metadata.projectType", "nodejs")
for _, v := range []string{"version", "displayName", "description", "language"} {
helper.JsonPathContentIs(stdout, "devfileData.devfile.metadata."+v, "Unknown")
}
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
helper.JsonPathContentIs(stdout, "runningIn.#", "1")
helper.JsonPathContentIs(stdout, "runningIn.0", "Dev")
helper.JsonPathContentIs(stdout, "devForwardedPorts", "")
})
By("running with default output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
res := helper.Cmd("odo", "describe", "component", "--name", cmpName).ShouldPass()
stdout := res.Out()
checkDevfileDescription(stdout, true)
Expect(stdout).ToNot(ContainSubstring("Forwarded ports"))
Expect(stdout).To(ContainSubstring("Running in: Dev"))
Expect(stdout).To(ContainSubstring("Dev: Unknown"))
Expect(stdout).To(ContainSubstring("Deploy: Unknown"))
Expect(stdout).To(ContainSubstring("Debug: Unknown"))
})
})
})