github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/cmd/ctr/commands/shim/shim.go (about)

     1  // +build !windows
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package shim
    20  
    21  import (
    22  	gocontext "context"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net"
    26  	"path/filepath"
    27  
    28  	"github.com/containerd/console"
    29  	"github.com/containerd/containerd/cmd/ctr/commands"
    30  	"github.com/containerd/containerd/namespaces"
    31  	"github.com/containerd/containerd/runtime/v2/shim"
    32  	"github.com/containerd/containerd/runtime/v2/task"
    33  	"github.com/containerd/ttrpc"
    34  	"github.com/containerd/typeurl"
    35  	ptypes "github.com/gogo/protobuf/types"
    36  	"github.com/opencontainers/runtime-spec/specs-go"
    37  	"github.com/pkg/errors"
    38  	"github.com/sirupsen/logrus"
    39  	"github.com/urfave/cli"
    40  )
    41  
    42  var fifoFlags = []cli.Flag{
    43  	cli.StringFlag{
    44  		Name:  "stdin",
    45  		Usage: "specify the path to the stdin fifo",
    46  	},
    47  	cli.StringFlag{
    48  		Name:  "stdout",
    49  		Usage: "specify the path to the stdout fifo",
    50  	},
    51  	cli.StringFlag{
    52  		Name:  "stderr",
    53  		Usage: "specify the path to the stderr fifo",
    54  	},
    55  	cli.BoolFlag{
    56  		Name:  "tty,t",
    57  		Usage: "enable tty support",
    58  	},
    59  }
    60  
    61  // Command is the cli command for interacting with a task
    62  var Command = cli.Command{
    63  	Name:  "shim",
    64  	Usage: "interact with a shim directly",
    65  	Flags: []cli.Flag{
    66  		cli.StringFlag{
    67  			Name:  "id",
    68  			Usage: "container id",
    69  		},
    70  	},
    71  	Subcommands: []cli.Command{
    72  		deleteCommand,
    73  		execCommand,
    74  		startCommand,
    75  		stateCommand,
    76  	},
    77  }
    78  
    79  var startCommand = cli.Command{
    80  	Name:  "start",
    81  	Usage: "start a container with a task",
    82  	Action: func(context *cli.Context) error {
    83  		service, err := getTaskService(context)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		_, err = service.Start(gocontext.Background(), &task.StartRequest{
    88  			ID: context.Args().First(),
    89  		})
    90  		return err
    91  	},
    92  }
    93  
    94  var deleteCommand = cli.Command{
    95  	Name:  "delete",
    96  	Usage: "delete a container with a task",
    97  	Action: func(context *cli.Context) error {
    98  		service, err := getTaskService(context)
    99  		if err != nil {
   100  			return err
   101  		}
   102  		r, err := service.Delete(gocontext.Background(), &task.DeleteRequest{
   103  			ID: context.Args().First(),
   104  		})
   105  		if err != nil {
   106  			return err
   107  		}
   108  		fmt.Printf("container deleted and returned exit status %d\n", r.ExitStatus)
   109  		return nil
   110  	},
   111  }
   112  
   113  var stateCommand = cli.Command{
   114  	Name:  "state",
   115  	Usage: "get the state of all the processes of the task",
   116  	Action: func(context *cli.Context) error {
   117  		service, err := getTaskService(context)
   118  		if err != nil {
   119  			return err
   120  		}
   121  		r, err := service.State(gocontext.Background(), &task.StateRequest{
   122  			ID: context.GlobalString("id"),
   123  		})
   124  		if err != nil {
   125  			return err
   126  		}
   127  		commands.PrintAsJSON(r)
   128  		return nil
   129  	},
   130  }
   131  
   132  var execCommand = cli.Command{
   133  	Name:  "exec",
   134  	Usage: "exec a new process in the task's container",
   135  	Flags: append(fifoFlags,
   136  		cli.BoolFlag{
   137  			Name:  "attach,a",
   138  			Usage: "stay attached to the container and open the fifos",
   139  		},
   140  		cli.StringSliceFlag{
   141  			Name:  "env,e",
   142  			Usage: "add environment vars",
   143  			Value: &cli.StringSlice{},
   144  		},
   145  		cli.StringFlag{
   146  			Name:  "cwd",
   147  			Usage: "current working directory",
   148  		},
   149  		cli.StringFlag{
   150  			Name:  "spec",
   151  			Usage: "runtime spec",
   152  		},
   153  	),
   154  	Action: func(context *cli.Context) error {
   155  		service, err := getTaskService(context)
   156  		if err != nil {
   157  			return err
   158  		}
   159  		var (
   160  			id  = context.Args().First()
   161  			ctx = gocontext.Background()
   162  		)
   163  
   164  		if id == "" {
   165  			return errors.New("exec id must be provided")
   166  		}
   167  
   168  		tty := context.Bool("tty")
   169  		wg, err := prepareStdio(context.String("stdin"), context.String("stdout"), context.String("stderr"), tty)
   170  		if err != nil {
   171  			return err
   172  		}
   173  
   174  		// read spec file and extract Any object
   175  		spec, err := ioutil.ReadFile(context.String("spec"))
   176  		if err != nil {
   177  			return err
   178  		}
   179  		url, err := typeurl.TypeURL(specs.Process{})
   180  		if err != nil {
   181  			return err
   182  		}
   183  
   184  		rq := &task.ExecProcessRequest{
   185  			ID: id,
   186  			Spec: &ptypes.Any{
   187  				TypeUrl: url,
   188  				Value:   spec,
   189  			},
   190  			Stdin:    context.String("stdin"),
   191  			Stdout:   context.String("stdout"),
   192  			Stderr:   context.String("stderr"),
   193  			Terminal: tty,
   194  		}
   195  		if _, err := service.Exec(ctx, rq); err != nil {
   196  			return err
   197  		}
   198  		r, err := service.Start(ctx, &task.StartRequest{
   199  			ID: id,
   200  		})
   201  		if err != nil {
   202  			return err
   203  		}
   204  		fmt.Printf("exec running with pid %d\n", r.Pid)
   205  		if context.Bool("attach") {
   206  			logrus.Info("attaching")
   207  			if tty {
   208  				current := console.Current()
   209  				defer current.Reset()
   210  				if err := current.SetRaw(); err != nil {
   211  					return err
   212  				}
   213  				size, err := current.Size()
   214  				if err != nil {
   215  					return err
   216  				}
   217  				if _, err := service.ResizePty(ctx, &task.ResizePtyRequest{
   218  					ID:     id,
   219  					Width:  uint32(size.Width),
   220  					Height: uint32(size.Height),
   221  				}); err != nil {
   222  					return err
   223  				}
   224  			}
   225  			wg.Wait()
   226  		}
   227  		return nil
   228  	},
   229  }
   230  
   231  func getTaskService(context *cli.Context) (task.TaskService, error) {
   232  	id := context.GlobalString("id")
   233  	if id == "" {
   234  		return nil, fmt.Errorf("container id must be specified")
   235  	}
   236  	ns := context.GlobalString("namespace")
   237  
   238  	// /containerd-shim/ns/id/shim.sock is the old way to generate shim socket,
   239  	// compatible it
   240  	s1 := filepath.Join(string(filepath.Separator), "containerd-shim", ns, id, "shim.sock")
   241  	// this should not error, ctr always get a default ns
   242  	ctx := namespaces.WithNamespace(gocontext.Background(), ns)
   243  	s2, _ := shim.SocketAddress(ctx, id)
   244  
   245  	for _, socket := range []string{s1, s2} {
   246  		conn, err := net.Dial("unix", "\x00"+socket)
   247  		if err == nil {
   248  			client := ttrpc.NewClient(conn)
   249  
   250  			// TODO(stevvooe): This actually leaks the connection. We were leaking it
   251  			// before, so may not be a huge deal.
   252  
   253  			return task.NewTaskClient(client), nil
   254  		}
   255  	}
   256  
   257  	return nil, fmt.Errorf("fail to connect to container %s's shim", id)
   258  }