github.com/oam-dev/kubevela@v1.9.11/references/cli/ls.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cli
    18  
    19  import (
    20  	"context"
    21  	"strings"
    22  
    23  	"github.com/oam-dev/kubevela/pkg/utils"
    24  
    25  	"github.com/gosuri/uitable"
    26  	"github.com/spf13/cobra"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	"k8s.io/apimachinery/pkg/fields"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"sigs.k8s.io/controller-runtime/pkg/client"
    32  
    33  	commontypes "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    34  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    35  	"github.com/oam-dev/kubevela/apis/types"
    36  	"github.com/oam-dev/kubevela/pkg/utils/common"
    37  	cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
    38  )
    39  
    40  // AllNamespace list app in all namespaces
    41  var AllNamespace bool
    42  
    43  // LabelSelector list app using label selector
    44  var LabelSelector string
    45  
    46  // FieldSelector list app using field selector
    47  var FieldSelector string
    48  
    49  // NewListCommand creates `ls` command and its nested children command
    50  func NewListCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
    51  	ctx := context.Background()
    52  	cmd := &cobra.Command{
    53  		Use:                   "ls",
    54  		Aliases:               []string{"list"},
    55  		DisableFlagsInUseLine: true,
    56  		Short:                 "List applications.",
    57  		Long:                  "List all vela applications.",
    58  		Example:               `vela ls`,
    59  		RunE: func(cmd *cobra.Command, args []string) error {
    60  			newClient, err := c.GetClient()
    61  			if err != nil {
    62  				return err
    63  			}
    64  			namespace, err := GetFlagNamespaceOrEnv(cmd, c)
    65  			if err != nil {
    66  				return err
    67  			}
    68  			if AllNamespace {
    69  				namespace = ""
    70  			}
    71  			return printApplicationList(ctx, newClient, namespace, ioStreams)
    72  		},
    73  		Annotations: map[string]string{
    74  			types.TagCommandOrder: order,
    75  			types.TagCommandType:  types.TypeStart,
    76  		},
    77  	}
    78  	addNamespaceAndEnvArg(cmd)
    79  	cmd.Flags().BoolVarP(&AllNamespace, "all-namespaces", "A", false, "If true, check the specified action in all namespaces.")
    80  	cmd.Flags().StringVarP(&LabelSelector, "selector", "l", LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
    81  	cmd.Flags().StringVar(&FieldSelector, "field-selector", FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2).")
    82  	return cmd
    83  }
    84  
    85  func printApplicationList(ctx context.Context, c client.Reader, namespace string, ioStreams cmdutil.IOStreams) error {
    86  	table, err := buildApplicationListTable(ctx, c, namespace)
    87  	if err != nil {
    88  		return err
    89  	}
    90  	ioStreams.Info(table.String())
    91  	return nil
    92  }
    93  
    94  func buildApplicationListTable(ctx context.Context, c client.Reader, namespace string) (*uitable.Table, error) {
    95  	table := newUITable()
    96  	header := []interface{}{"APP", "COMPONENT", "TYPE", "TRAITS", "PHASE", "HEALTHY", "STATUS", "CREATED-TIME"}
    97  	if AllNamespace {
    98  		header = append([]interface{}{"NAMESPACE"}, header...)
    99  	}
   100  	table.AddRow(header...)
   101  
   102  	labelSelector := labels.NewSelector()
   103  	if len(LabelSelector) > 0 {
   104  		selector, err := labels.Parse(LabelSelector)
   105  		if err != nil {
   106  			return nil, err
   107  		}
   108  		labelSelector = selector
   109  	}
   110  
   111  	applist := v1beta1.ApplicationList{}
   112  	if err := c.List(ctx, &applist, client.InNamespace(namespace), &client.ListOptions{LabelSelector: labelSelector}); err != nil {
   113  		if apierrors.IsNotFound(err) {
   114  			return table, nil
   115  		}
   116  		return nil, err
   117  	}
   118  
   119  	if len(FieldSelector) > 0 {
   120  		fieldSelector, err := fields.ParseSelector(FieldSelector)
   121  		if err != nil {
   122  			return nil, err
   123  		}
   124  		var objects []runtime.Object
   125  		for i := range applist.Items {
   126  			objects = append(objects, &applist.Items[i])
   127  		}
   128  		applist.Items = objectsToApps(utils.FilterObjectsByFieldSelector(objects, fieldSelector))
   129  	}
   130  
   131  	for _, a := range applist.Items {
   132  		service := map[string]commontypes.ApplicationComponentStatus{}
   133  		for _, s := range a.Status.Services {
   134  			service[s.Name] = s
   135  		}
   136  
   137  		if len(a.Spec.Components) == 0 {
   138  			if AllNamespace {
   139  				table.AddRow(a.Namespace, a.Name, "", "", "", a.Status.Phase, "", "", a.CreationTimestamp)
   140  			} else {
   141  				table.AddRow(a.Name, "", "", "", a.Status.Phase, "", "", a.CreationTimestamp)
   142  			}
   143  			continue
   144  		}
   145  
   146  		for idx, cmp := range a.Spec.Components {
   147  			var appName = a.Name
   148  			if idx > 0 {
   149  				appName = "├─"
   150  				if idx == len(a.Spec.Components)-1 {
   151  					appName = "└─"
   152  				}
   153  			}
   154  
   155  			var healthy, status string
   156  			if s, ok := service[cmp.Name]; ok {
   157  				healthy = getHealthString(s.Healthy)
   158  				status = s.Message
   159  			}
   160  
   161  			var traits []string
   162  			for _, tr := range cmp.Traits {
   163  				traits = append(traits, tr.Type)
   164  			}
   165  			if AllNamespace {
   166  				table.AddRow(a.Namespace, appName, cmp.Name, cmp.Type, strings.Join(traits, ","), a.Status.Phase, healthy, status, a.CreationTimestamp)
   167  			} else {
   168  				table.AddRow(appName, cmp.Name, cmp.Type, strings.Join(traits, ","), a.Status.Phase, healthy, status, a.CreationTimestamp)
   169  			}
   170  		}
   171  	}
   172  	return table, nil
   173  }
   174  
   175  func getHealthString(healthy bool) string {
   176  	if healthy {
   177  		return "healthy"
   178  	}
   179  	return "unhealthy"
   180  }
   181  
   182  // objectsToApps objects to apps
   183  func objectsToApps(objs []runtime.Object) []v1beta1.Application {
   184  	res := make([]v1beta1.Application, 0)
   185  	for _, obj := range objs {
   186  		obj, ok := obj.(*v1beta1.Application)
   187  		if ok {
   188  			res = append(res, *obj)
   189  		}
   190  	}
   191  	return res
   192  }