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 }