github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/compose_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/nerdctl/pkg/clientutil" 23 "github.com/containerd/nerdctl/pkg/cmd/compose" 24 "github.com/containerd/nerdctl/pkg/composer" 25 "github.com/spf13/cobra" 26 ) 27 28 func newComposeExecCommand() *cobra.Command { 29 var composeExecCommand = &cobra.Command{ 30 Use: "exec [flags] SERVICE COMMAND [ARGS...]", 31 Short: "Execute a command in a running container of the service", 32 Args: cobra.MinimumNArgs(2), 33 RunE: composeExecAction, 34 SilenceUsage: true, 35 SilenceErrors: true, 36 } 37 composeExecCommand.Flags().SetInterspersed(false) 38 39 composeExecCommand.Flags().BoolP("no-TTY", "T", false, "Disable pseudo-TTY allocation. By default nerdctl compose exec allocates a TTY.") 40 composeExecCommand.Flags().BoolP("detach", "d", false, "Detached mode: Run containers in the background") 41 composeExecCommand.Flags().StringP("workdir", "w", "", "Working directory inside the container") 42 // env needs to be StringArray, not StringSlice, to prevent "FOO=foo1,foo2" from being split to {"FOO=foo1", "foo2"} 43 composeExecCommand.Flags().StringArrayP("env", "e", nil, "Set environment variables") 44 composeExecCommand.Flags().Bool("privileged", false, "Give extended privileges to the command") 45 composeExecCommand.Flags().StringP("user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])") 46 composeExecCommand.Flags().Int("index", 1, "index of the container if the service has multiple instances.") 47 48 composeExecCommand.Flags().BoolP("interactive", "i", true, "Keep STDIN open even if not attached") 49 composeExecCommand.Flags().MarkHidden("interactive") 50 // The -t does not has effect to keep the compatibility with docker. 51 // The proposal of -t is to keep "muscle memory" with compose v1: https://github.com/docker/compose/issues/9207 52 // FYI: https://github.com/docker/compose/blob/v2.23.1/cmd/compose/exec.go#L77 53 composeExecCommand.Flags().BoolP("tty", "t", true, "Allocate a pseudo-TTY") 54 composeExecCommand.Flags().MarkHidden("tty") 55 56 return composeExecCommand 57 } 58 59 func composeExecAction(cmd *cobra.Command, args []string) error { 60 globalOptions, err := processRootCmdFlags(cmd) 61 if err != nil { 62 return err 63 } 64 interactive, err := cmd.Flags().GetBool("interactive") 65 if err != nil { 66 return err 67 } 68 noTty, err := cmd.Flags().GetBool("no-TTY") 69 if err != nil { 70 return err 71 } 72 detach, err := cmd.Flags().GetBool("detach") 73 if err != nil { 74 return err 75 } 76 workdir, err := cmd.Flags().GetString("workdir") 77 if err != nil { 78 return err 79 } 80 env, err := cmd.Flags().GetStringArray("env") 81 if err != nil { 82 return err 83 } 84 privileged, err := cmd.Flags().GetBool("privileged") 85 if err != nil { 86 return err 87 } 88 user, err := cmd.Flags().GetString("user") 89 if err != nil { 90 return err 91 } 92 index, err := cmd.Flags().GetInt("index") 93 if err != nil { 94 return err 95 } 96 97 if index < 1 { 98 return errors.New("index starts from 1 and should be equal or greater than 1") 99 } 100 // https://github.com/containerd/nerdctl/blob/v1.0.0/cmd/nerdctl/exec.go#L116 101 if interactive && detach { 102 return errors.New("currently flag -i and -d cannot be specified together (FIXME)") 103 } 104 // https://github.com/containerd/nerdctl/blob/v1.0.0/cmd/nerdctl/exec.go#L122 105 if !noTty && detach { 106 return errors.New("currently flag -d should be specified with --no-TTY (FIXME)") 107 } 108 109 client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address) 110 if err != nil { 111 return err 112 } 113 defer cancel() 114 options, err := getComposeOptions(cmd, globalOptions.DebugFull, globalOptions.Experimental) 115 if err != nil { 116 return err 117 } 118 c, err := compose.New(client, globalOptions, options, cmd.OutOrStdout(), cmd.ErrOrStderr()) 119 if err != nil { 120 return err 121 } 122 123 eo := composer.ExecOptions{ 124 ServiceName: args[0], 125 Index: index, 126 127 Interactive: interactive, 128 Tty: !noTty, 129 Detach: detach, 130 WorkDir: workdir, 131 Env: env, 132 Privileged: privileged, 133 User: user, 134 Args: args[1:], 135 } 136 137 return c.Exec(ctx, eo) 138 }