mirror of https://github.com/kubevela/kubevela.git
				
				
				
			
		
			
				
	
	
		
			238 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2021 The KubeVela Authors.
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 	http://www.apache.org/licenses/LICENSE-2.0
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package cli
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 
 | |
| 	"github.com/kubevela/pkg/util/compression"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/spf13/cobra"
 | |
| 	apitypes "k8s.io/apimachinery/pkg/types"
 | |
| 	"k8s.io/klog/v2"
 | |
| 	"sigs.k8s.io/yaml"
 | |
| 
 | |
| 	"github.com/oam-dev/kubevela/pkg/velaql"
 | |
| 
 | |
| 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 | |
| 	"github.com/oam-dev/kubevela/apis/types"
 | |
| 	"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
 | |
| 	"github.com/oam-dev/kubevela/pkg/oam"
 | |
| 	"github.com/oam-dev/kubevela/pkg/utils"
 | |
| 	"github.com/oam-dev/kubevela/pkg/utils/common"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	revisionView = "application-revision-view"
 | |
| )
 | |
| 
 | |
| // RevisionCommandGroup the commands for managing application revisions
 | |
| func RevisionCommandGroup(c common.Args) *cobra.Command {
 | |
| 	cmd := &cobra.Command{
 | |
| 		Use:   "revision",
 | |
| 		Short: "Manage Application Revisions",
 | |
| 		Long:  "Manage KubeVela Application Revisions",
 | |
| 		Annotations: map[string]string{
 | |
| 			types.TagCommandType: types.TypeApp,
 | |
| 		},
 | |
| 	}
 | |
| 	cmd.AddCommand(
 | |
| 		NewRevisionListCommand(c),
 | |
| 		NewRevisionGetCommand(c),
 | |
| 	)
 | |
| 	return cmd
 | |
| }
 | |
| 
 | |
| // NewRevisionListCommand list the revisions for application
 | |
| func NewRevisionListCommand(c common.Args) *cobra.Command {
 | |
| 	cmd := &cobra.Command{
 | |
| 		Use:     "list",
 | |
| 		Aliases: []string{"ls"},
 | |
| 		Short:   "list application revisions",
 | |
| 		Long:    "list Kubevela application revisions",
 | |
| 		Args:    cobra.ExactValidArgs(1),
 | |
| 		RunE: func(cmd *cobra.Command, args []string) error {
 | |
| 			namespace, err := GetFlagNamespaceOrEnv(cmd, c)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			cli, err := c.GetClient()
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			name := args[0]
 | |
| 			app := &v1beta1.Application{}
 | |
| 			ctx := context.Background()
 | |
| 			if err = cli.Get(ctx, apitypes.NamespacedName{Namespace: namespace, Name: name}, app); err != nil {
 | |
| 				return errors.Wrapf(err, "failed to get application %s/%s", namespace, name)
 | |
| 			}
 | |
| 			revs, err := application.GetSortedAppRevisions(ctx, cli, name, namespace)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			printApprevs(cmd.OutOrStdout(), revs)
 | |
| 			_, _ = cmd.OutOrStdout().Write([]byte("\n"))
 | |
| 			return nil
 | |
| 		},
 | |
| 	}
 | |
| 	addNamespaceAndEnvArg(cmd)
 | |
| 	return cmd
 | |
| }
 | |
| 
 | |
| // NewRevisionGetCommand gets specific revision of application
 | |
| func NewRevisionGetCommand(c common.Args) *cobra.Command {
 | |
| 	var outputFormat string
 | |
| 	ctx := context.Background()
 | |
| 	cmd := &cobra.Command{
 | |
| 		Use:     "get",
 | |
| 		Aliases: []string{"get"},
 | |
| 		Short:   "get specific revision of application",
 | |
| 		Long:    "get specific revision of application",
 | |
| 		Args:    cobra.ExactValidArgs(1),
 | |
| 		RunE: func(cmd *cobra.Command, args []string) error {
 | |
| 			namespace, err := GetFlagNamespaceOrEnv(cmd, c)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			name := args[0]
 | |
| 			def, err := cmd.Flags().GetString("definition")
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 
 | |
| 			return getRevision(ctx, c, outputFormat, cmd.OutOrStdout(), name, namespace, def)
 | |
| 		},
 | |
| 	}
 | |
| 	addNamespaceAndEnvArg(cmd)
 | |
| 	cmd.Flags().StringP("definition", "d", "", "component definition")
 | |
| 	cmd.Flags().StringVarP(&outputFormat, "output", "o", "", "raw Application output format. One of: (json, yaml, jsonpath)")
 | |
| 	return cmd
 | |
| }
 | |
| 
 | |
| func getRevision(ctx context.Context, c common.Args, format string, out io.Writer, name string, namespace string, def string) error {
 | |
| 
 | |
| 	kubeConfig, err := c.GetConfig()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	cli, err := c.GetClient()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	dm, err := c.GetDiscoveryMapper()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	pd, err := c.GetPackageDiscover()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	params := map[string]string{
 | |
| 		"name":      name,
 | |
| 		"namespace": namespace,
 | |
| 	}
 | |
| 	query, err := velaql.ParseVelaQL(MakeVelaQL(revisionView, params, "status"))
 | |
| 	if err != nil {
 | |
| 		klog.Errorf("fail to parse ql string %s", err.Error())
 | |
| 		return fmt.Errorf(fmt.Sprintf("Unable to get application revision %s in namespace %s", name, namespace))
 | |
| 	}
 | |
| 
 | |
| 	queryValue, err := velaql.NewViewHandler(cli, kubeConfig, dm, pd).QueryView(ctx, query)
 | |
| 	if err != nil {
 | |
| 		klog.Errorf("fail to query the view %s", err.Error())
 | |
| 		return fmt.Errorf(fmt.Sprintf("Unable to get application revision %s in namespace %s", name, namespace))
 | |
| 	}
 | |
| 
 | |
| 	apprev := v1beta1.ApplicationRevision{}
 | |
| 	err = queryValue.UnmarshalTo(&apprev)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if apprev.CreationTimestamp.IsZero() {
 | |
| 		fmt.Fprintf(out, "No such application revision %s in namespace %s", name, namespace)
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if def != "" {
 | |
| 		if cd, exist := apprev.Spec.ComponentDefinitions[def]; exist {
 | |
| 			ba, err := yaml.Marshal(&cd)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			fmt.Fprint(out, string(ba))
 | |
| 		} else {
 | |
| 			fmt.Fprintf(out, "No such definition %s", def)
 | |
| 		}
 | |
| 	} else {
 | |
| 		if format == "" {
 | |
| 			printApprevs(out, []v1beta1.ApplicationRevision{apprev})
 | |
| 		} else {
 | |
| 			output, err := convertApplicationRevisionTo(format, &apprev)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			fmt.Fprint(out, output)
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func printApprevs(out io.Writer, apprevs []v1beta1.ApplicationRevision) {
 | |
| 	table := newUITable().AddRow("NAME", "PUBLISH_VERSION", "SUCCEEDED", "HASH", "BEGIN_TIME", "STATUS", "SIZE")
 | |
| 	for _, apprev := range apprevs {
 | |
| 		var begin, status, hash, size string
 | |
| 		status = "NotStart"
 | |
| 		if apprev.Status.Workflow != nil {
 | |
| 			begin = apprev.Status.Workflow.StartTime.Format("2006-01-02 15:04:05")
 | |
| 			// aggregate workflow result
 | |
| 			switch {
 | |
| 			case apprev.Status.Succeeded:
 | |
| 				status = "Succeeded"
 | |
| 			case apprev.Status.Workflow.Terminated || apprev.Status.Workflow.Suspend || apprev.Status.Workflow.Finished:
 | |
| 				status = "Failed"
 | |
| 			default:
 | |
| 				status = "Executing or Failed"
 | |
| 			}
 | |
| 		}
 | |
| 		if labels := apprev.GetLabels(); labels != nil {
 | |
| 			hash = apprev.GetLabels()[oam.LabelAppRevisionHash]
 | |
| 		}
 | |
| 		//nolint:gosec // apprev is only used here once, implicit memory aliasing is fine. (apprev pointer is required to call custom marshal methods)
 | |
| 		if bs, err := yaml.Marshal(&apprev); err == nil {
 | |
| 			compressedSize := len(bs)
 | |
| 			size = utils.ByteCountIEC(int64(compressedSize))
 | |
| 			// Show how much compressed if compression is enabled.
 | |
| 			if apprev.Spec.Compression.Type != compression.Uncompressed {
 | |
| 				// Get the original size.
 | |
| 				apprev.Spec.Compression.Type = compression.Uncompressed
 | |
| 				var uncompressedSize int
 | |
| 				//nolint:gosec // apprev is only used here once, implicit memory aliasing is fine. (apprev pointer is required to call custom marshal methods)
 | |
| 				if ubs, err := yaml.Marshal(&apprev); err == nil && len(ubs) > 0 {
 | |
| 					uncompressedSize = len(ubs)
 | |
| 				}
 | |
| 				size += fmt.Sprintf(" (Compressed, %.0f%%)", float64(compressedSize)/float64(uncompressedSize)*100)
 | |
| 			}
 | |
| 		}
 | |
| 		table.AddRow(apprev.Name, oam.GetPublishVersion(apprev.DeepCopy()), apprev.Status.Succeeded, hash, begin, status, size)
 | |
| 	}
 | |
| 	fmt.Fprint(out, table.String())
 | |
| }
 |