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  }