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 }