mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
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:
@@ -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.
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user