github.com/demonoid81/containerd@v1.3.4/runtime/v1/linux/task.go (about) 1 // +build linux 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 linux 20 21 import ( 22 "context" 23 "sync" 24 25 "github.com/containerd/cgroups" 26 eventstypes "github.com/containerd/containerd/api/events" 27 "github.com/containerd/containerd/api/types/task" 28 "github.com/containerd/containerd/errdefs" 29 "github.com/containerd/containerd/events/exchange" 30 "github.com/containerd/containerd/identifiers" 31 "github.com/containerd/containerd/log" 32 "github.com/containerd/containerd/runtime" 33 "github.com/containerd/containerd/runtime/v1/shim/client" 34 "github.com/containerd/containerd/runtime/v1/shim/v1" 35 "github.com/containerd/ttrpc" 36 "github.com/containerd/typeurl" 37 "github.com/gogo/protobuf/types" 38 "github.com/pkg/errors" 39 ) 40 41 // Task on a linux based system 42 type Task struct { 43 mu sync.Mutex 44 id string 45 pid int 46 shim *client.Client 47 namespace string 48 cg cgroups.Cgroup 49 events *exchange.Exchange 50 tasks *runtime.TaskList 51 bundle *bundle 52 } 53 54 func newTask(id, namespace string, pid int, shim *client.Client, events *exchange.Exchange, list *runtime.TaskList, bundle *bundle) (*Task, error) { 55 var ( 56 err error 57 cg cgroups.Cgroup 58 ) 59 if pid > 0 { 60 cg, err = cgroups.Load(cgroups.V1, cgroups.PidPath(pid)) 61 if err != nil && err != cgroups.ErrCgroupDeleted { 62 return nil, err 63 } 64 } 65 return &Task{ 66 id: id, 67 pid: pid, 68 shim: shim, 69 namespace: namespace, 70 cg: cg, 71 events: events, 72 tasks: list, 73 bundle: bundle, 74 }, nil 75 } 76 77 // ID of the task 78 func (t *Task) ID() string { 79 return t.id 80 } 81 82 // Namespace of the task 83 func (t *Task) Namespace() string { 84 return t.namespace 85 } 86 87 // PID of the task 88 func (t *Task) PID() uint32 { 89 return uint32(t.pid) 90 } 91 92 // Delete the task and return the exit status 93 func (t *Task) Delete(ctx context.Context) (*runtime.Exit, error) { 94 rsp, shimErr := t.shim.Delete(ctx, empty) 95 if shimErr != nil { 96 shimErr = errdefs.FromGRPC(shimErr) 97 if !errdefs.IsNotFound(shimErr) { 98 return nil, shimErr 99 } 100 } 101 t.tasks.Delete(ctx, t.id) 102 if err := t.shim.KillShim(ctx); err != nil { 103 log.G(ctx).WithError(err).Error("failed to kill shim") 104 } 105 if err := t.bundle.Delete(); err != nil { 106 log.G(ctx).WithError(err).Error("failed to delete bundle") 107 } 108 if shimErr != nil { 109 return nil, shimErr 110 } 111 t.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventstypes.TaskDelete{ 112 ContainerID: t.id, 113 ExitStatus: rsp.ExitStatus, 114 ExitedAt: rsp.ExitedAt, 115 Pid: rsp.Pid, 116 }) 117 return &runtime.Exit{ 118 Status: rsp.ExitStatus, 119 Timestamp: rsp.ExitedAt, 120 Pid: rsp.Pid, 121 }, nil 122 } 123 124 // Start the task 125 func (t *Task) Start(ctx context.Context) error { 126 t.mu.Lock() 127 hasCgroup := t.cg != nil 128 t.mu.Unlock() 129 r, err := t.shim.Start(ctx, &shim.StartRequest{ 130 ID: t.id, 131 }) 132 if err != nil { 133 return errdefs.FromGRPC(err) 134 } 135 t.pid = int(r.Pid) 136 if !hasCgroup { 137 cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(t.pid)) 138 if err != nil && err != cgroups.ErrCgroupDeleted { 139 return err 140 } 141 t.mu.Lock() 142 if err == cgroups.ErrCgroupDeleted { 143 t.cg = nil 144 } else { 145 t.cg = cg 146 } 147 t.mu.Unlock() 148 } 149 t.events.Publish(ctx, runtime.TaskStartEventTopic, &eventstypes.TaskStart{ 150 ContainerID: t.id, 151 Pid: uint32(t.pid), 152 }) 153 return nil 154 } 155 156 // State returns runtime information for the task 157 func (t *Task) State(ctx context.Context) (runtime.State, error) { 158 response, err := t.shim.State(ctx, &shim.StateRequest{ 159 ID: t.id, 160 }) 161 if err != nil { 162 if errors.Cause(err) != ttrpc.ErrClosed { 163 return runtime.State{}, errdefs.FromGRPC(err) 164 } 165 return runtime.State{}, errdefs.ErrNotFound 166 } 167 var status runtime.Status 168 switch response.Status { 169 case task.StatusCreated: 170 status = runtime.CreatedStatus 171 case task.StatusRunning: 172 status = runtime.RunningStatus 173 case task.StatusStopped: 174 status = runtime.StoppedStatus 175 case task.StatusPaused: 176 status = runtime.PausedStatus 177 case task.StatusPausing: 178 status = runtime.PausingStatus 179 } 180 return runtime.State{ 181 Pid: response.Pid, 182 Status: status, 183 Stdin: response.Stdin, 184 Stdout: response.Stdout, 185 Stderr: response.Stderr, 186 Terminal: response.Terminal, 187 ExitStatus: response.ExitStatus, 188 ExitedAt: response.ExitedAt, 189 }, nil 190 } 191 192 // Pause the task and all processes 193 func (t *Task) Pause(ctx context.Context) error { 194 if _, err := t.shim.Pause(ctx, empty); err != nil { 195 return errdefs.FromGRPC(err) 196 } 197 t.events.Publish(ctx, runtime.TaskPausedEventTopic, &eventstypes.TaskPaused{ 198 ContainerID: t.id, 199 }) 200 return nil 201 } 202 203 // Resume the task and all processes 204 func (t *Task) Resume(ctx context.Context) error { 205 if _, err := t.shim.Resume(ctx, empty); err != nil { 206 return errdefs.FromGRPC(err) 207 } 208 t.events.Publish(ctx, runtime.TaskResumedEventTopic, &eventstypes.TaskResumed{ 209 ContainerID: t.id, 210 }) 211 return nil 212 } 213 214 // Kill the task using the provided signal 215 // 216 // Optionally send the signal to all processes that are a child of the task 217 func (t *Task) Kill(ctx context.Context, signal uint32, all bool) error { 218 if _, err := t.shim.Kill(ctx, &shim.KillRequest{ 219 ID: t.id, 220 Signal: signal, 221 All: all, 222 }); err != nil { 223 return errdefs.FromGRPC(err) 224 } 225 return nil 226 } 227 228 // Exec creates a new process inside the task 229 func (t *Task) Exec(ctx context.Context, id string, opts runtime.ExecOpts) (runtime.Process, error) { 230 if err := identifiers.Validate(id); err != nil { 231 return nil, errors.Wrapf(err, "invalid exec id") 232 } 233 request := &shim.ExecProcessRequest{ 234 ID: id, 235 Stdin: opts.IO.Stdin, 236 Stdout: opts.IO.Stdout, 237 Stderr: opts.IO.Stderr, 238 Terminal: opts.IO.Terminal, 239 Spec: opts.Spec, 240 } 241 if _, err := t.shim.Exec(ctx, request); err != nil { 242 return nil, errdefs.FromGRPC(err) 243 } 244 return &Process{ 245 id: id, 246 t: t, 247 }, nil 248 } 249 250 // Pids returns all system level process ids running inside the task 251 func (t *Task) Pids(ctx context.Context) ([]runtime.ProcessInfo, error) { 252 resp, err := t.shim.ListPids(ctx, &shim.ListPidsRequest{ 253 ID: t.id, 254 }) 255 if err != nil { 256 return nil, errdefs.FromGRPC(err) 257 } 258 var processList []runtime.ProcessInfo 259 for _, p := range resp.Processes { 260 processList = append(processList, runtime.ProcessInfo{ 261 Pid: p.Pid, 262 Info: p.Info, 263 }) 264 } 265 return processList, nil 266 } 267 268 // ResizePty changes the side of the task's PTY to the provided width and height 269 func (t *Task) ResizePty(ctx context.Context, size runtime.ConsoleSize) error { 270 _, err := t.shim.ResizePty(ctx, &shim.ResizePtyRequest{ 271 ID: t.id, 272 Width: size.Width, 273 Height: size.Height, 274 }) 275 if err != nil { 276 err = errdefs.FromGRPC(err) 277 } 278 return err 279 } 280 281 // CloseIO closes the provided IO on the task 282 func (t *Task) CloseIO(ctx context.Context) error { 283 _, err := t.shim.CloseIO(ctx, &shim.CloseIORequest{ 284 ID: t.id, 285 Stdin: true, 286 }) 287 if err != nil { 288 err = errdefs.FromGRPC(err) 289 } 290 return err 291 } 292 293 // Checkpoint creates a system level dump of the task and process information that can be later restored 294 func (t *Task) Checkpoint(ctx context.Context, path string, options *types.Any) error { 295 r := &shim.CheckpointTaskRequest{ 296 Path: path, 297 Options: options, 298 } 299 if _, err := t.shim.Checkpoint(ctx, r); err != nil { 300 return errdefs.FromGRPC(err) 301 } 302 t.events.Publish(ctx, runtime.TaskCheckpointedEventTopic, &eventstypes.TaskCheckpointed{ 303 ContainerID: t.id, 304 }) 305 return nil 306 } 307 308 // Update changes runtime information of a running task 309 func (t *Task) Update(ctx context.Context, resources *types.Any) error { 310 if _, err := t.shim.Update(ctx, &shim.UpdateTaskRequest{ 311 Resources: resources, 312 }); err != nil { 313 return errdefs.FromGRPC(err) 314 } 315 return nil 316 } 317 318 // Process returns a specific process inside the task by the process id 319 func (t *Task) Process(ctx context.Context, id string) (runtime.Process, error) { 320 p := &Process{ 321 id: id, 322 t: t, 323 } 324 if _, err := p.State(ctx); err != nil { 325 return nil, err 326 } 327 return p, nil 328 } 329 330 // Stats returns runtime specific system level metric information for the task 331 func (t *Task) Stats(ctx context.Context) (*types.Any, error) { 332 t.mu.Lock() 333 defer t.mu.Unlock() 334 if t.cg == nil { 335 return nil, errors.Wrap(errdefs.ErrNotFound, "cgroup does not exist") 336 } 337 stats, err := t.cg.Stat(cgroups.IgnoreNotExist) 338 if err != nil { 339 return nil, err 340 } 341 return typeurl.MarshalAny(stats) 342 } 343 344 // Cgroup returns the underlying cgroup for a linux task 345 func (t *Task) Cgroup() (cgroups.Cgroup, error) { 346 t.mu.Lock() 347 defer t.mu.Unlock() 348 if t.cg == nil { 349 return nil, errors.Wrap(errdefs.ErrNotFound, "cgroup does not exist") 350 } 351 return t.cg, nil 352 } 353 354 // Wait for the task to exit returning the status and timestamp 355 func (t *Task) Wait(ctx context.Context) (*runtime.Exit, error) { 356 r, err := t.shim.Wait(ctx, &shim.WaitRequest{ 357 ID: t.id, 358 }) 359 if err != nil { 360 return nil, err 361 } 362 return &runtime.Exit{ 363 Timestamp: r.ExitedAt, 364 Status: r.ExitStatus, 365 }, nil 366 }