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