github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_exec.go (about) 1 /* 2 Copyright The containerd 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 main 18 19 import ( 20 "errors" 21 22 "github.com/containerd/containerd" 23 "github.com/containerd/nerdctl/pkg/api/types" 24 "github.com/containerd/nerdctl/pkg/clientutil" 25 "github.com/containerd/nerdctl/pkg/cmd/container" 26 "github.com/spf13/cobra" 27 ) 28 29 func newExecCommand() *cobra.Command { 30 var execCommand = &cobra.Command{ 31 Use: "exec [flags] CONTAINER COMMAND [ARG...]", 32 Args: cobra.MinimumNArgs(2), 33 Short: "Run a command in a running container", 34 RunE: execAction, 35 ValidArgsFunction: execShellComplete, 36 SilenceUsage: true, 37 SilenceErrors: true, 38 } 39 execCommand.Flags().SetInterspersed(false) 40 41 execCommand.Flags().BoolP("tty", "t", false, "Allocate a pseudo-TTY") 42 execCommand.Flags().BoolP("interactive", "i", false, "Keep STDIN open even if not attached") 43 execCommand.Flags().BoolP("detach", "d", false, "Detached mode: run command in the background") 44 execCommand.Flags().StringP("workdir", "w", "", "Working directory inside the container") 45 // env needs to be StringArray, not StringSlice, to prevent "FOO=foo1,foo2" from being split to {"FOO=foo1", "foo2"} 46 execCommand.Flags().StringArrayP("env", "e", nil, "Set environment variables") 47 // env-file is defined as StringSlice, not StringArray, to allow specifying "--env-file=FILE1,FILE2" (compatible with Podman) 48 execCommand.Flags().StringSlice("env-file", nil, "Set environment variables from file") 49 execCommand.Flags().Bool("privileged", false, "Give extended privileges to the command") 50 execCommand.Flags().StringP("user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])") 51 return execCommand 52 } 53 54 func processExecCommandOptions(cmd *cobra.Command) (types.ContainerExecOptions, error) { 55 globalOptions, err := processRootCmdFlags(cmd) 56 if err != nil { 57 return types.ContainerExecOptions{}, err 58 } 59 60 flagI, err := cmd.Flags().GetBool("interactive") 61 if err != nil { 62 return types.ContainerExecOptions{}, err 63 } 64 flagT, err := cmd.Flags().GetBool("tty") 65 if err != nil { 66 return types.ContainerExecOptions{}, err 67 } 68 flagD, err := cmd.Flags().GetBool("detach") 69 if err != nil { 70 return types.ContainerExecOptions{}, err 71 } 72 73 if flagI { 74 if flagD { 75 return types.ContainerExecOptions{}, errors.New("currently flag -i and -d cannot be specified together (FIXME)") 76 } 77 } 78 79 if flagT { 80 if flagD { 81 return types.ContainerExecOptions{}, errors.New("currently flag -t and -d cannot be specified together (FIXME)") 82 } 83 } 84 85 workdir, err := cmd.Flags().GetString("workdir") 86 if err != nil { 87 return types.ContainerExecOptions{}, err 88 } 89 90 envFile, err := cmd.Flags().GetStringSlice("env-file") 91 if err != nil { 92 return types.ContainerExecOptions{}, err 93 } 94 env, err := cmd.Flags().GetStringArray("env") 95 if err != nil { 96 return types.ContainerExecOptions{}, err 97 } 98 privileged, err := cmd.Flags().GetBool("privileged") 99 if err != nil { 100 return types.ContainerExecOptions{}, err 101 } 102 user, err := cmd.Flags().GetString("user") 103 if err != nil { 104 return types.ContainerExecOptions{}, err 105 } 106 107 return types.ContainerExecOptions{ 108 GOptions: globalOptions, 109 TTY: flagT, 110 Interactive: flagI, 111 Detach: flagD, 112 Workdir: workdir, 113 Env: env, 114 EnvFile: envFile, 115 Privileged: privileged, 116 User: user, 117 }, nil 118 } 119 120 func execAction(cmd *cobra.Command, args []string) error { 121 options, err := processExecCommandOptions(cmd) 122 if err != nil { 123 return err 124 } 125 // simulate the behavior of double dash 126 newArg := []string{} 127 if len(args) >= 2 && args[1] == "--" { 128 newArg = append(newArg, args[:1]...) 129 newArg = append(newArg, args[2:]...) 130 args = newArg 131 } 132 133 client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), options.GOptions.Namespace, options.GOptions.Address) 134 if err != nil { 135 return err 136 } 137 defer cancel() 138 139 return container.Exec(ctx, client, args, options) 140 } 141 142 func execShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 143 if len(args) == 0 { 144 // show running container names 145 statusFilterFn := func(st containerd.ProcessStatus) bool { 146 return st == containerd.Running 147 } 148 return shellCompleteContainerNames(cmd, statusFilterFn) 149 } 150 return nil, cobra.ShellCompDirectiveNoFileComp 151 }