github.com/oam-dev/kubevela@v1.9.11/references/cli/exec.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 "fmt" 22 "time" 23 24 "github.com/spf13/cobra" 25 "k8s.io/cli-runtime/pkg/genericclioptions" 26 "k8s.io/client-go/kubernetes" 27 "k8s.io/client-go/rest" 28 cmdexec "k8s.io/kubectl/pkg/cmd/exec" 29 k8scmdutil "k8s.io/kubectl/pkg/cmd/util" 30 31 pkgmulticluster "github.com/kubevela/pkg/multicluster" 32 33 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 34 "github.com/oam-dev/kubevela/apis/types" 35 "github.com/oam-dev/kubevela/pkg/multicluster" 36 "github.com/oam-dev/kubevela/pkg/utils/common" 37 "github.com/oam-dev/kubevela/pkg/utils/util" 38 querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types" 39 "github.com/oam-dev/kubevela/references/appfile" 40 ) 41 42 const ( 43 podRunningTimeoutFlag = "pod-running-timeout" 44 defaultPodExecTimeout = 60 * time.Second 45 defaultStdin = true 46 defaultTTY = true 47 ) 48 49 // VelaExecOptions creates options for `exec` command 50 type VelaExecOptions struct { 51 Cmd *cobra.Command 52 Args []string 53 Stdin bool 54 TTY bool 55 56 ComponentName string 57 PodName string 58 ClusterName string 59 ContainerName string 60 61 Ctx context.Context 62 VelaC common.Args 63 Env *types.EnvMeta 64 App *v1beta1.Application 65 66 namespace string 67 podName string 68 podNamespace string 69 f k8scmdutil.Factory 70 kcExecOptions *cmdexec.ExecOptions 71 ClientSet kubernetes.Interface 72 } 73 74 // NewExecCommand creates `exec` command 75 func NewExecCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command { 76 o := &VelaExecOptions{ 77 kcExecOptions: &cmdexec.ExecOptions{ 78 StreamOptions: cmdexec.StreamOptions{ 79 IOStreams: genericclioptions.IOStreams{ 80 In: ioStreams.In, 81 Out: ioStreams.Out, 82 ErrOut: ioStreams.ErrOut, 83 }, 84 }, 85 Executor: &cmdexec.DefaultRemoteExecutor{}, 86 }, 87 } 88 cmd := &cobra.Command{ 89 Use: "exec", 90 Short: "Execute command in a container.", 91 Long: "Execute command inside container based vela application.", 92 PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 93 o.VelaC = c 94 return nil 95 }, 96 RunE: func(cmd *cobra.Command, args []string) error { 97 if len(args) < 1 { 98 ioStreams.Error("Please specify an application name.") 99 return nil 100 } 101 if len(args) == 1 { 102 ioStreams.Error("Please specify at least one command for the container.") 103 return nil 104 } 105 argsLenAtDash := cmd.ArgsLenAtDash() 106 if argsLenAtDash != 1 { 107 ioStreams.Error("vela exec APP_NAME COMMAND is not supported. Use vela exec APP_NAME -- COMMAND instead.") 108 return nil 109 } 110 var err error 111 o.namespace, err = GetFlagNamespaceOrEnv(cmd, c) 112 if err != nil { 113 return err 114 } 115 if err := o.Init(context.Background(), cmd, args); err != nil { 116 return err 117 } 118 if err := o.Complete(); err != nil { 119 return err 120 } 121 if err := o.Run(); err != nil { 122 return err 123 } 124 return nil 125 }, 126 Annotations: map[string]string{ 127 types.TagCommandOrder: order, 128 types.TagCommandType: types.TypeApp, 129 }, 130 Example: ` 131 exec [flags] APP_NAME -- COMMAND [args...] 132 133 # Get output from running 'date' command from app pod, using the first container by default 134 vela exec my-app -- date 135 136 # Switch to raw terminal mode, sends stdin to 'bash' in containers of application my-app 137 # and sends stdout/stderr from 'bash' back to the client 138 vela exec my-app -i -t -- bash -il 139 `, 140 } 141 cmd.Flags().BoolVarP(&o.Stdin, "stdin", "i", defaultStdin, "Pass stdin to the container") 142 cmd.Flags().BoolVarP(&o.TTY, "tty", "t", defaultTTY, "Stdin is a TTY") 143 cmd.Flags().Duration(podRunningTimeoutFlag, defaultPodExecTimeout, 144 "The length of time (like 5s, 2m, or 3h, higher than zero) to wait until at least one pod is running", 145 ) 146 cmd.Flags().StringVarP(&o.ComponentName, "component", "c", "", "filter the pod by the component name") 147 cmd.Flags().StringVarP(&o.ClusterName, "cluster", "", "", "filter the pod by the cluster name") 148 cmd.Flags().StringVarP(&o.PodName, "pod", "p", "", "specify the pod name") 149 cmd.Flags().StringVarP(&o.ContainerName, "container", "", "", "specify the container name") 150 addNamespaceAndEnvArg(cmd) 151 152 return cmd 153 } 154 155 // Init prepares the arguments accepted by the Exec command 156 func (o *VelaExecOptions) Init(ctx context.Context, c *cobra.Command, argsIn []string) error { 157 o.Cmd = c 158 o.Args = argsIn 159 160 app, err := appfile.LoadApplication(o.namespace, o.Args[0], o.VelaC) 161 if err != nil { 162 return err 163 } 164 o.App = app 165 166 pods, err := GetApplicationPods(ctx, app.Name, app.Namespace, o.VelaC, Filter{ 167 Component: o.ComponentName, 168 Cluster: o.ClusterName, 169 }) 170 if err != nil { 171 return err 172 } 173 var selectPod *querytypes.PodBase 174 if o.PodName != "" { 175 for i, pod := range pods { 176 if pod.Metadata.Name == o.PodName { 177 selectPod = &pods[i] 178 break 179 } 180 } 181 if selectPod == nil { 182 fmt.Println("The Pod you specified does not exist, please select it from the list.") 183 } 184 } 185 if selectPod == nil { 186 selectPod, err = AskToChooseOnePod(pods) 187 if err != nil { 188 return err 189 } 190 } 191 192 if selectPod == nil { 193 return nil 194 } 195 196 cf := genericclioptions.NewConfigFlags(true) 197 var namespace = selectPod.Metadata.Namespace 198 cf.Namespace = &namespace 199 cf.WrapConfigFn = func(cfg *rest.Config) *rest.Config { 200 cfg.Wrap(pkgmulticluster.NewTransportWrapper(pkgmulticluster.ForCluster(selectPod.Cluster))) 201 return cfg 202 } 203 o.f = k8scmdutil.NewFactory(k8scmdutil.NewMatchVersionFlags(cf)) 204 o.podName = selectPod.Metadata.Name 205 o.Ctx = multicluster.ContextWithClusterName(ctx, selectPod.Cluster) 206 o.podNamespace = namespace 207 config, err := o.VelaC.GetConfig() 208 if err != nil { 209 return err 210 } 211 config.Wrap(pkgmulticluster.NewTransportWrapper()) 212 k8sClient, err := kubernetes.NewForConfig(config) 213 if err != nil { 214 return err 215 } 216 o.ClientSet = k8sClient 217 218 o.kcExecOptions.In = c.InOrStdin() 219 o.kcExecOptions.Out = c.OutOrStdout() 220 o.kcExecOptions.ErrOut = c.OutOrStderr() 221 return nil 222 } 223 224 // Complete loads data from the command environment 225 func (o *VelaExecOptions) Complete() error { 226 o.kcExecOptions.StreamOptions.Stdin = o.Stdin 227 o.kcExecOptions.StreamOptions.TTY = o.TTY 228 o.kcExecOptions.StreamOptions.ContainerName = o.ContainerName 229 230 args := make([]string, len(o.Args)) 231 copy(args, o.Args) 232 // args for kcExecOptions MUST be in such format: 233 // [podName, COMMAND...] 234 args[0] = o.podName 235 return o.kcExecOptions.Complete(o.f, o.Cmd, args, 1) 236 } 237 238 // Run executes a validated remote execution against a pod 239 func (o *VelaExecOptions) Run() error { 240 return o.kcExecOptions.Run() 241 }