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

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  	http://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  See the License for the specific language governing permissions and
    11  limitations under the License.
    12  */
    13  
    14  package cli
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"io"
    20  
    21  	"github.com/kubevela/pkg/util/compression"
    22  	"github.com/pkg/errors"
    23  	"github.com/spf13/cobra"
    24  	apitypes "k8s.io/apimachinery/pkg/types"
    25  	"k8s.io/klog/v2"
    26  	"sigs.k8s.io/yaml"
    27  
    28  	"github.com/oam-dev/kubevela/pkg/velaql"
    29  
    30  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    31  	"github.com/oam-dev/kubevela/apis/types"
    32  	"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/application"
    33  	"github.com/oam-dev/kubevela/pkg/oam"
    34  	"github.com/oam-dev/kubevela/pkg/utils"
    35  	"github.com/oam-dev/kubevela/pkg/utils/common"
    36  )
    37  
    38  const (
    39  	revisionView = "application-revision-view"
    40  )
    41  
    42  // RevisionCommandGroup the commands for managing application revisions
    43  func RevisionCommandGroup(c common.Args, order string) *cobra.Command {
    44  	cmd := &cobra.Command{
    45  		Use:   "revision",
    46  		Short: "Manage Application Revisions.",
    47  		Long:  "Manage KubeVela Application Revisions",
    48  		Annotations: map[string]string{
    49  			types.TagCommandType:  types.TypeApp,
    50  			types.TagCommandOrder: order,
    51  		},
    52  	}
    53  	cmd.AddCommand(
    54  		NewRevisionListCommand(c),
    55  		NewRevisionGetCommand(c),
    56  	)
    57  	return cmd
    58  }
    59  
    60  // NewRevisionListCommand list the revisions for application
    61  func NewRevisionListCommand(c common.Args) *cobra.Command {
    62  	cmd := &cobra.Command{
    63  		Use:     "list",
    64  		Aliases: []string{"ls"},
    65  		Short:   "list application revisions",
    66  		Long:    "list Kubevela application revisions",
    67  		Args:    cobra.ExactArgs(1),
    68  		RunE: func(cmd *cobra.Command, args []string) error {
    69  			namespace, err := GetFlagNamespaceOrEnv(cmd, c)
    70  			if err != nil {
    71  				return err
    72  			}
    73  			cli, err := c.GetClient()
    74  			if err != nil {
    75  				return err
    76  			}
    77  			name := args[0]
    78  			app := &v1beta1.Application{}
    79  			ctx := context.Background()
    80  			if err = cli.Get(ctx, apitypes.NamespacedName{Namespace: namespace, Name: name}, app); err != nil {
    81  				return errors.Wrapf(err, "failed to get application %s/%s", namespace, name)
    82  			}
    83  			revs, err := application.GetSortedAppRevisions(ctx, cli, name, namespace)
    84  			if err != nil {
    85  				return err
    86  			}
    87  			printApprevs(cmd.OutOrStdout(), revs)
    88  			_, _ = cmd.OutOrStdout().Write([]byte("\n"))
    89  			return nil
    90  		},
    91  	}
    92  	addNamespaceAndEnvArg(cmd)
    93  	return cmd
    94  }
    95  
    96  // NewRevisionGetCommand gets specific revision of application
    97  func NewRevisionGetCommand(c common.Args) *cobra.Command {
    98  	var outputFormat string
    99  	ctx := context.Background()
   100  	cmd := &cobra.Command{
   101  		Use:     "get",
   102  		Aliases: []string{"get"},
   103  		Short:   "get specific revision of application",
   104  		Long:    "get specific revision of application",
   105  		Args:    cobra.ExactArgs(1),
   106  		RunE: func(cmd *cobra.Command, args []string) error {
   107  			namespace, err := GetFlagNamespaceOrEnv(cmd, c)
   108  			if err != nil {
   109  				return err
   110  			}
   111  			name := args[0]
   112  			def, err := cmd.Flags().GetString("definition")
   113  			if err != nil {
   114  				return err
   115  			}
   116  
   117  			return getRevision(ctx, c, outputFormat, cmd.OutOrStdout(), name, namespace, def)
   118  		},
   119  	}
   120  	addNamespaceAndEnvArg(cmd)
   121  	cmd.Flags().StringP("definition", "d", "", "component definition")
   122  	cmd.Flags().StringVarP(&outputFormat, "output", "o", "", "raw Application output format. One of: (json, yaml, jsonpath)")
   123  	return cmd
   124  }
   125  
   126  func getRevision(ctx context.Context, c common.Args, format string, out io.Writer, name string, namespace string, def string) error {
   127  
   128  	kubeConfig, err := c.GetConfig()
   129  	if err != nil {
   130  		return err
   131  	}
   132  	cli, err := c.GetClient()
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	pd, err := c.GetPackageDiscover()
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	params := map[string]string{
   143  		"name":      name,
   144  		"namespace": namespace,
   145  	}
   146  	query, err := velaql.ParseVelaQL(MakeVelaQL(revisionView, params, "status"))
   147  	if err != nil {
   148  		klog.Errorf("fail to parse ql string %s", err.Error())
   149  		return fmt.Errorf(fmt.Sprintf("Unable to get application revision %s in namespace %s", name, namespace))
   150  	}
   151  
   152  	queryValue, err := velaql.NewViewHandler(cli, kubeConfig, pd).QueryView(ctx, query)
   153  	if err != nil {
   154  		klog.Errorf("fail to query the view %s", err.Error())
   155  		return fmt.Errorf(fmt.Sprintf("Unable to get application revision %s in namespace %s", name, namespace))
   156  	}
   157  
   158  	apprev := v1beta1.ApplicationRevision{}
   159  	err = queryValue.UnmarshalTo(&apprev)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	if apprev.CreationTimestamp.IsZero() {
   164  		fmt.Fprintf(out, "No such application revision %s in namespace %s", name, namespace)
   165  		return nil
   166  	}
   167  
   168  	if def != "" {
   169  		if cd, exist := apprev.Spec.ComponentDefinitions[def]; exist {
   170  			ba, err := yaml.Marshal(&cd)
   171  			if err != nil {
   172  				return err
   173  			}
   174  			fmt.Fprint(out, string(ba))
   175  		} else {
   176  			fmt.Fprintf(out, "No such definition %s", def)
   177  		}
   178  	} else {
   179  		if format == "" {
   180  			printApprevs(out, []v1beta1.ApplicationRevision{apprev})
   181  		} else {
   182  			output, err := convertApplicationRevisionTo(format, &apprev)
   183  			if err != nil {
   184  				return err
   185  			}
   186  			fmt.Fprint(out, output)
   187  
   188  		}
   189  	}
   190  
   191  	return nil
   192  }
   193  
   194  func printApprevs(out io.Writer, apprevs []v1beta1.ApplicationRevision) {
   195  	table := newUITable().AddRow("NAME", "PUBLISH_VERSION", "SUCCEEDED", "HASH", "BEGIN_TIME", "STATUS", "SIZE")
   196  	for _, apprev := range apprevs {
   197  		var begin, status, hash, size string
   198  		status = "NotStart"
   199  		if apprev.Status.Workflow != nil {
   200  			begin = apprev.Status.Workflow.StartTime.Format("2006-01-02 15:04:05")
   201  			// aggregate workflow result
   202  			switch {
   203  			case apprev.Status.Succeeded:
   204  				status = "Succeeded"
   205  			case apprev.Status.Workflow.Terminated || apprev.Status.Workflow.Suspend || apprev.Status.Workflow.Finished:
   206  				status = "Failed"
   207  			default:
   208  				status = "Executing or Failed"
   209  			}
   210  		}
   211  		if labels := apprev.GetLabels(); labels != nil {
   212  			hash = apprev.GetLabels()[oam.LabelAppRevisionHash]
   213  		}
   214  		//nolint:gosec // apprev is only used here once, implicit memory aliasing is fine. (apprev pointer is required to call custom marshal methods)
   215  		if bs, err := yaml.Marshal(&apprev); err == nil {
   216  			compressedSize := len(bs)
   217  			size = utils.ByteCountIEC(int64(compressedSize))
   218  			// Show how much compressed if compression is enabled.
   219  			if apprev.Spec.Compression.Type != compression.Uncompressed {
   220  				// Get the original size.
   221  				apprev.Spec.Compression.Type = compression.Uncompressed
   222  				var uncompressedSize int
   223  				//nolint:gosec // apprev is only used here once, implicit memory aliasing is fine. (apprev pointer is required to call custom marshal methods)
   224  				if ubs, err := yaml.Marshal(&apprev); err == nil && len(ubs) > 0 {
   225  					uncompressedSize = len(ubs)
   226  				}
   227  				size += fmt.Sprintf(" (Compressed, %.0f%%)", float64(compressedSize)/float64(uncompressedSize)*100)
   228  			}
   229  		}
   230  		table.AddRow(apprev.Name, oam.GetPublishVersion(apprev.DeepCopy()), apprev.Status.Succeeded, hash, begin, status, size)
   231  	}
   232  	fmt.Fprint(out, table.String())
   233  }