...

Source file src/github.com/containerd/containerd/cmd/ctr/commands/tasks/exec.go

Documentation: github.com/containerd/containerd/cmd/ctr/commands/tasks

     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 tasks
    18  
    19  import (
    20  	"errors"
    21  	"io"
    22  	"net/url"
    23  	"os"
    24  
    25  	"github.com/containerd/console"
    26  	"github.com/containerd/containerd"
    27  	"github.com/containerd/containerd/cio"
    28  	"github.com/containerd/containerd/cmd/ctr/commands"
    29  	"github.com/containerd/containerd/oci"
    30  	"github.com/sirupsen/logrus"
    31  	"github.com/urfave/cli"
    32  )
    33  
    34  var execCommand = cli.Command{
    35  	Name:           "exec",
    36  	Usage:          "Execute additional processes in an existing container",
    37  	ArgsUsage:      "[flags] CONTAINER CMD [ARG...]",
    38  	SkipArgReorder: true,
    39  	Flags: []cli.Flag{
    40  		cli.StringFlag{
    41  			Name:  "cwd",
    42  			Usage: "Working directory of the new process",
    43  		},
    44  		cli.BoolFlag{
    45  			Name:  "tty,t",
    46  			Usage: "Allocate a TTY for the container",
    47  		},
    48  		cli.BoolFlag{
    49  			Name:  "detach,d",
    50  			Usage: "Detach from the task after it has started execution",
    51  		},
    52  		cli.StringFlag{
    53  			Name:     "exec-id",
    54  			Required: true,
    55  			Usage:    "Exec specific id for the process",
    56  		},
    57  		cli.StringFlag{
    58  			Name:  "fifo-dir",
    59  			Usage: "Directory used for storing IO FIFOs",
    60  		},
    61  		cli.StringFlag{
    62  			Name:  "log-uri",
    63  			Usage: "Log uri for custom shim logging",
    64  		},
    65  		cli.StringFlag{
    66  			Name:  "user",
    67  			Usage: "User id or name",
    68  		},
    69  	},
    70  	Action: func(context *cli.Context) error {
    71  		var (
    72  			id     = context.Args().First()
    73  			args   = context.Args().Tail()
    74  			tty    = context.Bool("tty")
    75  			detach = context.Bool("detach")
    76  		)
    77  		if id == "" {
    78  			return errors.New("container id must be provided")
    79  		}
    80  		client, ctx, cancel, err := commands.NewClient(context)
    81  		if err != nil {
    82  			return err
    83  		}
    84  		defer cancel()
    85  		container, err := client.LoadContainer(ctx, id)
    86  		if err != nil {
    87  			return err
    88  		}
    89  		spec, err := container.Spec(ctx)
    90  		if err != nil {
    91  			return err
    92  		}
    93  		if user := context.String("user"); user != "" {
    94  			c, err := container.Info(ctx)
    95  			if err != nil {
    96  				return err
    97  			}
    98  			if err := oci.WithUser(user)(ctx, client, &c, spec); err != nil {
    99  				return err
   100  			}
   101  		}
   102  
   103  		pspec := spec.Process
   104  		pspec.Terminal = tty
   105  		pspec.Args = args
   106  
   107  		if cwd := context.String("cwd"); cwd != "" {
   108  			pspec.Cwd = cwd
   109  		}
   110  
   111  		task, err := container.Task(ctx, nil)
   112  		if err != nil {
   113  			return err
   114  		}
   115  
   116  		var (
   117  			ioCreator cio.Creator
   118  			stdinC    = &stdinCloser{
   119  				stdin: os.Stdin,
   120  			}
   121  			con console.Console
   122  		)
   123  
   124  		fifoDir := context.String("fifo-dir")
   125  		logURI := context.String("log-uri")
   126  		ioOpts := []cio.Opt{cio.WithFIFODir(fifoDir)}
   127  		switch {
   128  		case tty && logURI != "":
   129  			return errors.New("can't use log-uri with tty")
   130  		case logURI != "" && fifoDir != "":
   131  			return errors.New("can't use log-uri with fifo-dir")
   132  
   133  		case tty:
   134  			con = console.Current()
   135  			defer con.Reset()
   136  			if err := con.SetRaw(); err != nil {
   137  				return err
   138  			}
   139  			ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, nil), cio.WithTerminal}, ioOpts...)...)
   140  
   141  		case logURI != "":
   142  			uri, err := url.Parse(logURI)
   143  			if err != nil {
   144  				return err
   145  			}
   146  			ioCreator = cio.LogURI(uri)
   147  
   148  		default:
   149  			ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(stdinC, os.Stdout, os.Stderr)}, ioOpts...)...)
   150  		}
   151  
   152  		process, err := task.Exec(ctx, context.String("exec-id"), pspec, ioCreator)
   153  		if err != nil {
   154  			return err
   155  		}
   156  		stdinC.closer = func() {
   157  			process.CloseIO(ctx, containerd.WithStdinCloser)
   158  		}
   159  		// if detach, we should not call this defer
   160  		if !detach {
   161  			defer process.Delete(ctx)
   162  		}
   163  
   164  		statusC, err := process.Wait(ctx)
   165  		if err != nil {
   166  			return err
   167  		}
   168  
   169  		if err := process.Start(ctx); err != nil {
   170  			return err
   171  		}
   172  		if detach {
   173  			return nil
   174  		}
   175  		if tty {
   176  			if err := HandleConsoleResize(ctx, process, con); err != nil {
   177  				logrus.WithError(err).Error("console resize")
   178  			}
   179  		} else {
   180  			sigc := commands.ForwardAllSignals(ctx, process)
   181  			defer commands.StopCatch(sigc)
   182  		}
   183  		status := <-statusC
   184  		code, _, err := status.Result()
   185  		if err != nil {
   186  			return err
   187  		}
   188  		if code != 0 {
   189  			return cli.NewExitError("", int(code))
   190  		}
   191  		return nil
   192  	},
   193  }
   194  
   195  type stdinCloser struct {
   196  	stdin  *os.File
   197  	closer func()
   198  }
   199  
   200  func (s *stdinCloser) Read(p []byte) (int, error) {
   201  	n, err := s.stdin.Read(p)
   202  	if err == io.EOF {
   203  		if s.closer != nil {
   204  			s.closer()
   205  		}
   206  	}
   207  	return n, err
   208  }
   209  

View as plain text