Split task creation and start into two separate method calls in the
libcontainerd API. Clients now have the opportunity to inspect the
freshly-created task and customize its runtime environment before
starting execution of the user-specified binary.
Signed-off-by: Cory Snider <csnider@mirantis.com>
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/api/types/events" |
| 12 | 12 |
"github.com/docker/docker/container" |
| 13 | 13 |
"github.com/docker/docker/errdefs" |
| 14 |
+ "github.com/docker/docker/internal/compatcontext" |
|
| 14 | 15 |
"github.com/docker/docker/libcontainerd" |
| 15 | 16 |
"github.com/pkg/errors" |
| 16 | 17 |
) |
| ... | ... |
@@ -198,16 +199,32 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore |
| 198 | 198 |
if err != nil {
|
| 199 | 199 |
return setExitCodeFromError(container.SetExitCode, err) |
| 200 | 200 |
} |
| 201 |
+ defer func() {
|
|
| 202 |
+ if retErr != nil {
|
|
| 203 |
+ if err := ctr.Delete(compatcontext.WithoutCancel(ctx)); err != nil {
|
|
| 204 |
+ log.G(ctx).WithError(err).WithField("container", container.ID).
|
|
| 205 |
+ Error("failed to delete failed start container")
|
|
| 206 |
+ } |
|
| 207 |
+ } |
|
| 208 |
+ }() |
|
| 201 | 209 |
|
| 202 | 210 |
// TODO(mlaventure): we need to specify checkpoint options here |
| 203 |
- tsk, err := ctr.Start(context.TODO(), // Passing ctx to ctr.Start caused integration tests to be stuck in the cleanup phase |
|
| 211 |
+ tsk, err := ctr.NewTask(context.TODO(), // Passing ctx caused integration tests to be stuck in the cleanup phase |
|
| 204 | 212 |
checkpointDir, container.StreamConfig.Stdin() != nil || container.Config.Tty, |
| 205 | 213 |
container.InitializeStdio) |
| 206 | 214 |
if err != nil {
|
| 207 |
- if err := ctr.Delete(context.Background()); err != nil {
|
|
| 208 |
- log.G(ctx).WithError(err).WithField("container", container.ID).
|
|
| 209 |
- Error("failed to delete failed start container")
|
|
| 215 |
+ return setExitCodeFromError(container.SetExitCode, err) |
|
| 216 |
+ } |
|
| 217 |
+ defer func() {
|
|
| 218 |
+ if retErr != nil {
|
|
| 219 |
+ if err := tsk.ForceDelete(compatcontext.WithoutCancel(ctx)); err != nil {
|
|
| 220 |
+ log.G(ctx).WithError(err).WithField("container", container.ID).
|
|
| 221 |
+ Error("failed to delete task after fail start")
|
|
| 222 |
+ } |
|
| 210 | 223 |
} |
| 224 |
+ }() |
|
| 225 |
+ |
|
| 226 |
+ if err := tsk.Start(context.TODO()); err != nil { // passing ctx caused integration tests to be stuck in the cleanup phase
|
|
| 211 | 227 |
return setExitCodeFromError(container.SetExitCode, err) |
| 212 | 228 |
} |
| 213 | 229 |
|
| ... | ... |
@@ -387,7 +387,7 @@ func (c *client) extractResourcesFromSpec(spec *specs.Spec, configuration *hcssh |
| 387 | 387 |
} |
| 388 | 388 |
} |
| 389 | 389 |
|
| 390 |
-func (ctr *container) Start(_ context.Context, _ string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (_ libcontainerdtypes.Task, retErr error) {
|
|
| 390 |
+func (ctr *container) NewTask(_ context.Context, _ string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (_ libcontainerdtypes.Task, retErr error) {
|
|
| 391 | 391 |
ctr.mu.Lock() |
| 392 | 392 |
defer ctr.mu.Unlock() |
| 393 | 393 |
|
| ... | ... |
@@ -514,6 +514,11 @@ func (ctr *container) Start(_ context.Context, _ string, withStdin bool, attachS |
| 514 | 514 |
return t, nil |
| 515 | 515 |
} |
| 516 | 516 |
|
| 517 |
+func (*task) Start(context.Context) error {
|
|
| 518 |
+ // No-op on Windows. |
|
| 519 |
+ return nil |
|
| 520 |
+} |
|
| 521 |
+ |
|
| 517 | 522 |
func (ctr *container) Task(context.Context) (libcontainerdtypes.Task, error) {
|
| 518 | 523 |
ctr.mu.Lock() |
| 519 | 524 |
defer ctr.mu.Unlock() |
| ... | ... |
@@ -145,8 +145,8 @@ func (c *client) NewContainer(ctx context.Context, id string, ociSpec *specs.Spe |
| 145 | 145 |
return &created, nil |
| 146 | 146 |
} |
| 147 | 147 |
|
| 148 |
-// Start create and start a task for the specified containerd id |
|
| 149 |
-func (c *container) Start(ctx context.Context, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (libcontainerdtypes.Task, error) {
|
|
| 148 |
+// NewTask creates a task for the specified containerd id |
|
| 149 |
+func (c *container) NewTask(ctx context.Context, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (libcontainerdtypes.Task, error) {
|
|
| 150 | 150 |
var ( |
| 151 | 151 |
checkpoint *types.Descriptor |
| 152 | 152 |
t containerd.Task |
| ... | ... |
@@ -236,19 +236,14 @@ func (c *container) Start(ctx context.Context, checkpointDir string, withStdin b |
| 236 | 236 |
// Signal c.createIO that it can call CloseIO |
| 237 | 237 |
stdinCloseSync <- t |
| 238 | 238 |
|
| 239 |
- if err := t.Start(ctx); err != nil {
|
|
| 240 |
- // Only Stopped tasks can be deleted. Created tasks have to be |
|
| 241 |
- // killed first, to transition them to Stopped. |
|
| 242 |
- if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil {
|
|
| 243 |
- c.client.logger.WithError(err).WithField("container", c.c8dCtr.ID()).
|
|
| 244 |
- Error("failed to delete task after fail start")
|
|
| 245 |
- } |
|
| 246 |
- return nil, wrapError(err) |
|
| 247 |
- } |
|
| 248 |
- |
|
| 249 | 239 |
return c.newTask(t), nil |
| 250 | 240 |
} |
| 251 | 241 |
|
| 242 |
+func (t *task) Start(ctx context.Context) error {
|
|
| 243 |
+ return wrapError(t.Task.Start(ctx)) |
|
| 244 |
+ |
|
| 245 |
+} |
|
| 246 |
+ |
|
| 252 | 247 |
// Exec creates exec process. |
| 253 | 248 |
// |
| 254 | 249 |
// The containerd client calls Exec to register the exec config in the shim side. |
| ... | ... |
@@ -64,7 +64,7 @@ type Client interface {
|
| 64 | 64 |
|
| 65 | 65 |
// Container provides access to a containerd container. |
| 66 | 66 |
type Container interface {
|
| 67 |
- Start(ctx context.Context, checkpointDir string, withStdin bool, attachStdio StdioCallback) (Task, error) |
|
| 67 |
+ NewTask(ctx context.Context, checkpointDir string, withStdin bool, attachStdio StdioCallback) (Task, error) |
|
| 68 | 68 |
Task(ctx context.Context) (Task, error) |
| 69 | 69 |
// AttachTask returns the current task for the container and reattaches |
| 70 | 70 |
// to the IO for the running task. If no task exists for the container |
| ... | ... |
@@ -79,6 +79,8 @@ type Container interface {
|
| 79 | 79 |
// Task provides access to a running containerd container. |
| 80 | 80 |
type Task interface {
|
| 81 | 81 |
Process |
| 82 |
+ // Start begins execution of the task |
|
| 83 |
+ Start(context.Context) error |
|
| 82 | 84 |
// Pause suspends the execution of the task |
| 83 | 85 |
Pause(context.Context) error |
| 84 | 86 |
// Resume the execution of the task |
| ... | ... |
@@ -81,11 +81,15 @@ func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteClo |
| 81 | 81 |
} |
| 82 | 82 |
|
| 83 | 83 |
p := c8dPlugin{log: log.G(ctx).WithField("plugin", id), ctr: ctr}
|
| 84 |
- p.tsk, err = ctr.Start(ctx, "", false, attachStreamsFunc(stdout, stderr)) |
|
| 84 |
+ p.tsk, err = ctr.NewTask(ctx, "", false, attachStreamsFunc(stdout, stderr)) |
|
| 85 | 85 |
if err != nil {
|
| 86 | 86 |
p.deleteTaskAndContainer(ctx) |
| 87 | 87 |
return err |
| 88 | 88 |
} |
| 89 |
+ if err := p.tsk.Start(ctx); err != nil {
|
|
| 90 |
+ p.deleteTaskAndContainer(ctx) |
|
| 91 |
+ return err |
|
| 92 |
+ } |
|
| 89 | 93 |
e.mu.Lock() |
| 90 | 94 |
defer e.mu.Unlock() |
| 91 | 95 |
e.plugins[id] = &p |