github.com/lalkh/containerd@v1.4.3/pkg/process/init_state.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 process 20 21 import ( 22 "context" 23 24 runc "github.com/containerd/go-runc" 25 google_protobuf "github.com/gogo/protobuf/types" 26 "github.com/pkg/errors" 27 "github.com/sirupsen/logrus" 28 ) 29 30 type initState interface { 31 Start(context.Context) error 32 Delete(context.Context) error 33 Pause(context.Context) error 34 Resume(context.Context) error 35 Update(context.Context, *google_protobuf.Any) error 36 Checkpoint(context.Context, *CheckpointConfig) error 37 Exec(context.Context, string, *ExecConfig) (Process, error) 38 Kill(context.Context, uint32, bool) error 39 SetExited(int) 40 Status(context.Context) (string, error) 41 } 42 43 type createdState struct { 44 p *Init 45 } 46 47 func (s *createdState) transition(name string) error { 48 switch name { 49 case "running": 50 s.p.initState = &runningState{p: s.p} 51 case "stopped": 52 s.p.initState = &stoppedState{p: s.p} 53 case "deleted": 54 s.p.initState = &deletedState{} 55 default: 56 return errors.Errorf("invalid state transition %q to %q", stateName(s), name) 57 } 58 return nil 59 } 60 61 func (s *createdState) Pause(ctx context.Context) error { 62 return errors.Errorf("cannot pause task in created state") 63 } 64 65 func (s *createdState) Resume(ctx context.Context) error { 66 return errors.Errorf("cannot resume task in created state") 67 } 68 69 func (s *createdState) Update(ctx context.Context, r *google_protobuf.Any) error { 70 return s.p.update(ctx, r) 71 } 72 73 func (s *createdState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { 74 return errors.Errorf("cannot checkpoint a task in created state") 75 } 76 77 func (s *createdState) Start(ctx context.Context) error { 78 if err := s.p.start(ctx); err != nil { 79 return err 80 } 81 return s.transition("running") 82 } 83 84 func (s *createdState) Delete(ctx context.Context) error { 85 if err := s.p.delete(ctx); err != nil { 86 return err 87 } 88 return s.transition("deleted") 89 } 90 91 func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error { 92 return s.p.kill(ctx, sig, all) 93 } 94 95 func (s *createdState) SetExited(status int) { 96 s.p.setExited(status) 97 98 if err := s.transition("stopped"); err != nil { 99 panic(err) 100 } 101 } 102 103 func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { 104 return s.p.exec(ctx, path, r) 105 } 106 107 func (s *createdState) Status(ctx context.Context) (string, error) { 108 return "created", nil 109 } 110 111 type createdCheckpointState struct { 112 p *Init 113 opts *runc.RestoreOpts 114 } 115 116 func (s *createdCheckpointState) transition(name string) error { 117 switch name { 118 case "running": 119 s.p.initState = &runningState{p: s.p} 120 case "stopped": 121 s.p.initState = &stoppedState{p: s.p} 122 case "deleted": 123 s.p.initState = &deletedState{} 124 default: 125 return errors.Errorf("invalid state transition %q to %q", stateName(s), name) 126 } 127 return nil 128 } 129 130 func (s *createdCheckpointState) Pause(ctx context.Context) error { 131 return errors.Errorf("cannot pause task in created state") 132 } 133 134 func (s *createdCheckpointState) Resume(ctx context.Context) error { 135 return errors.Errorf("cannot resume task in created state") 136 } 137 138 func (s *createdCheckpointState) Update(ctx context.Context, r *google_protobuf.Any) error { 139 return s.p.update(ctx, r) 140 } 141 142 func (s *createdCheckpointState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { 143 return errors.Errorf("cannot checkpoint a task in created state") 144 } 145 146 func (s *createdCheckpointState) Start(ctx context.Context) error { 147 p := s.p 148 sio := p.stdio 149 150 var ( 151 err error 152 socket *runc.Socket 153 ) 154 if sio.Terminal { 155 if socket, err = runc.NewTempConsoleSocket(); err != nil { 156 return errors.Wrap(err, "failed to create OCI runtime console socket") 157 } 158 defer socket.Close() 159 s.opts.ConsoleSocket = socket 160 } 161 162 if _, err := s.p.runtime.Restore(ctx, p.id, p.Bundle, s.opts); err != nil { 163 return p.runtimeError(err, "OCI runtime restore failed") 164 } 165 if sio.Stdin != "" { 166 if err := p.openStdin(sio.Stdin); err != nil { 167 return errors.Wrapf(err, "failed to open stdin fifo %s", sio.Stdin) 168 } 169 } 170 if socket != nil { 171 console, err := socket.ReceiveMaster() 172 if err != nil { 173 return errors.Wrap(err, "failed to retrieve console master") 174 } 175 console, err = p.Platform.CopyConsole(ctx, console, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg) 176 if err != nil { 177 return errors.Wrap(err, "failed to start console copy") 178 } 179 p.console = console 180 } else { 181 if err := p.io.Copy(ctx, &p.wg); err != nil { 182 return errors.Wrap(err, "failed to start io pipe copy") 183 } 184 } 185 pid, err := runc.ReadPidFile(s.opts.PidFile) 186 if err != nil { 187 return errors.Wrap(err, "failed to retrieve OCI runtime container pid") 188 } 189 p.pid = pid 190 return s.transition("running") 191 } 192 193 func (s *createdCheckpointState) Delete(ctx context.Context) error { 194 if err := s.p.delete(ctx); err != nil { 195 return err 196 } 197 return s.transition("deleted") 198 } 199 200 func (s *createdCheckpointState) Kill(ctx context.Context, sig uint32, all bool) error { 201 return s.p.kill(ctx, sig, all) 202 } 203 204 func (s *createdCheckpointState) SetExited(status int) { 205 s.p.setExited(status) 206 207 if err := s.transition("stopped"); err != nil { 208 panic(err) 209 } 210 } 211 212 func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { 213 return nil, errors.Errorf("cannot exec in a created state") 214 } 215 216 func (s *createdCheckpointState) Status(ctx context.Context) (string, error) { 217 return "created", nil 218 } 219 220 type runningState struct { 221 p *Init 222 } 223 224 func (s *runningState) transition(name string) error { 225 switch name { 226 case "stopped": 227 s.p.initState = &stoppedState{p: s.p} 228 case "paused": 229 s.p.initState = &pausedState{p: s.p} 230 default: 231 return errors.Errorf("invalid state transition %q to %q", stateName(s), name) 232 } 233 return nil 234 } 235 236 func (s *runningState) Pause(ctx context.Context) error { 237 s.p.pausing.set(true) 238 // NOTE "pausing" will be returned in the short window 239 // after `transition("paused")`, before `pausing` is reset 240 // to false. That doesn't break the state machine, just 241 // delays the "paused" state a little bit. 242 defer s.p.pausing.set(false) 243 244 if err := s.p.runtime.Pause(ctx, s.p.id); err != nil { 245 return s.p.runtimeError(err, "OCI runtime pause failed") 246 } 247 248 return s.transition("paused") 249 } 250 251 func (s *runningState) Resume(ctx context.Context) error { 252 return errors.Errorf("cannot resume a running process") 253 } 254 255 func (s *runningState) Update(ctx context.Context, r *google_protobuf.Any) error { 256 return s.p.update(ctx, r) 257 } 258 259 func (s *runningState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { 260 return s.p.checkpoint(ctx, r) 261 } 262 263 func (s *runningState) Start(ctx context.Context) error { 264 return errors.Errorf("cannot start a running process") 265 } 266 267 func (s *runningState) Delete(ctx context.Context) error { 268 return errors.Errorf("cannot delete a running process") 269 } 270 271 func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error { 272 return s.p.kill(ctx, sig, all) 273 } 274 275 func (s *runningState) SetExited(status int) { 276 s.p.setExited(status) 277 278 if err := s.transition("stopped"); err != nil { 279 panic(err) 280 } 281 } 282 283 func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { 284 return s.p.exec(ctx, path, r) 285 } 286 287 func (s *runningState) Status(ctx context.Context) (string, error) { 288 return "running", nil 289 } 290 291 type pausedState struct { 292 p *Init 293 } 294 295 func (s *pausedState) transition(name string) error { 296 switch name { 297 case "running": 298 s.p.initState = &runningState{p: s.p} 299 case "stopped": 300 s.p.initState = &stoppedState{p: s.p} 301 default: 302 return errors.Errorf("invalid state transition %q to %q", stateName(s), name) 303 } 304 return nil 305 } 306 307 func (s *pausedState) Pause(ctx context.Context) error { 308 return errors.Errorf("cannot pause a paused container") 309 } 310 311 func (s *pausedState) Resume(ctx context.Context) error { 312 if err := s.p.runtime.Resume(ctx, s.p.id); err != nil { 313 return s.p.runtimeError(err, "OCI runtime resume failed") 314 } 315 316 return s.transition("running") 317 } 318 319 func (s *pausedState) Update(ctx context.Context, r *google_protobuf.Any) error { 320 return s.p.update(ctx, r) 321 } 322 323 func (s *pausedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { 324 return s.p.checkpoint(ctx, r) 325 } 326 327 func (s *pausedState) Start(ctx context.Context) error { 328 return errors.Errorf("cannot start a paused process") 329 } 330 331 func (s *pausedState) Delete(ctx context.Context) error { 332 return errors.Errorf("cannot delete a paused process") 333 } 334 335 func (s *pausedState) Kill(ctx context.Context, sig uint32, all bool) error { 336 return s.p.kill(ctx, sig, all) 337 } 338 339 func (s *pausedState) SetExited(status int) { 340 s.p.setExited(status) 341 342 if err := s.p.runtime.Resume(context.Background(), s.p.id); err != nil { 343 logrus.WithError(err).Error("resuming exited container from paused state") 344 } 345 346 if err := s.transition("stopped"); err != nil { 347 panic(err) 348 } 349 } 350 351 func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { 352 return nil, errors.Errorf("cannot exec in a paused state") 353 } 354 355 func (s *pausedState) Status(ctx context.Context) (string, error) { 356 return "paused", nil 357 } 358 359 type stoppedState struct { 360 p *Init 361 } 362 363 func (s *stoppedState) transition(name string) error { 364 switch name { 365 case "deleted": 366 s.p.initState = &deletedState{} 367 default: 368 return errors.Errorf("invalid state transition %q to %q", stateName(s), name) 369 } 370 return nil 371 } 372 373 func (s *stoppedState) Pause(ctx context.Context) error { 374 return errors.Errorf("cannot pause a stopped container") 375 } 376 377 func (s *stoppedState) Resume(ctx context.Context) error { 378 return errors.Errorf("cannot resume a stopped container") 379 } 380 381 func (s *stoppedState) Update(ctx context.Context, r *google_protobuf.Any) error { 382 return errors.Errorf("cannot update a stopped container") 383 } 384 385 func (s *stoppedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { 386 return errors.Errorf("cannot checkpoint a stopped container") 387 } 388 389 func (s *stoppedState) Start(ctx context.Context) error { 390 return errors.Errorf("cannot start a stopped process") 391 } 392 393 func (s *stoppedState) Delete(ctx context.Context) error { 394 if err := s.p.delete(ctx); err != nil { 395 return err 396 } 397 return s.transition("deleted") 398 } 399 400 func (s *stoppedState) Kill(ctx context.Context, sig uint32, all bool) error { 401 return s.p.kill(ctx, sig, all) 402 } 403 404 func (s *stoppedState) SetExited(status int) { 405 // no op 406 } 407 408 func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { 409 return nil, errors.Errorf("cannot exec in a stopped state") 410 } 411 412 func (s *stoppedState) Status(ctx context.Context) (string, error) { 413 return "stopped", nil 414 }