diff options
author | Matthew Heon <mheon@redhat.com> | 2020-06-17 15:31:53 -0400 |
---|---|---|
committer | Matthew Heon <matthew.heon@pm.me> | 2020-06-18 09:34:04 -0400 |
commit | b20619e5b0dfa6c63b25c3fd9a7ae6188bee2b4c (patch) | |
tree | 440ba00f0400d44da8b19024f852978ed67ff2e0 | |
parent | 3eb0ad04a8b1d56866a16f1428bb8019927ccfa3 (diff) | |
download | podman-b20619e5b0dfa6c63b25c3fd9a7ae6188bee2b4c.tar.gz podman-b20619e5b0dfa6c63b25c3fd9a7ae6188bee2b4c.tar.bz2 podman-b20619e5b0dfa6c63b25c3fd9a7ae6188bee2b4c.zip |
Allow recursive dependency start with Init()
As part of APIv2 Attach, we need to be able to attach to freshly
created containers (in ContainerStateConfigured). This isn't
something Libpod is interested in supporting, so we use Init() to
get the container into ContainerStateCreated, in which attach is
possible. Problem: Init() will fail if dependencies are not
started, so a fresh container in a fresh pod will fail. The
simplest solution is to extend the existing recursive start code
from Start() to Init(), allowing dependency containers to be
started when we initialize the container (optionally, controlled
via bool).
Also, update some comments in container_api.go to make it more
clear how some of our major API calls work.
Fixes #6646
Signed-off-by: Matthew Heon <mheon@redhat.com>
-rw-r--r-- | libpod/container_api.go | 58 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_attach.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 2 | ||||
-rw-r--r-- | pkg/varlinkapi/containers.go | 4 |
5 files changed, 41 insertions, 27 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go index d43cb4829..5e79763b6 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -19,8 +19,17 @@ import ( "k8s.io/client-go/tools/remotecommand" ) -// Init creates a container in the OCI runtime -func (c *Container) Init(ctx context.Context) (err error) { +// Init creates a container in the OCI runtime, moving a container from +// ContainerStateConfigured, ContainerStateStopped, or ContainerStateExited to +// ContainerStateCreated. Once in Created state, Conmon will be running, which +// allows the container to be attached to. The container can subsequently +// transition to ContainerStateRunning via Start(), or be transitioned back to +// ContainerStateConfigured by Cleanup() (which will stop conmon and unmount the +// container). +// Init requires that all dependency containers be started (e.g. pod infra +// containers). The `recursive` parameter will, if set to true, start these +// dependency containers before initializing this container. +func (c *Container) Init(ctx context.Context, recursive bool) (err error) { span, _ := opentracing.StartSpanFromContext(ctx, "containerInit") span.SetTag("struct", "container") defer span.Finish() @@ -38,9 +47,14 @@ func (c *Container) Init(ctx context.Context) (err error) { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s has already been created in runtime", c.ID()) } - // don't recursively start - if err := c.checkDependenciesAndHandleError(); err != nil { - return err + if !recursive { + if err := c.checkDependenciesAndHandleError(); err != nil { + return err + } + } else { + if err := c.startDependencies(ctx); err != nil { + return err + } } if err := c.prepare(); err != nil { @@ -59,13 +73,18 @@ func (c *Container) Init(ctx context.Context) (err error) { return c.init(ctx, false) } -// Start starts a container. -// Start can start configured, created or stopped containers. -// For configured containers, the container will be initialized first, then -// started. -// Stopped containers will be deleted and re-created in runc, undergoing a fresh -// Init(). -// If recursive is set, Start will also start all containers this container depends on. +// Start starts the given container. +// Start will accept container in ContainerStateConfigured, +// ContainerStateCreated, ContainerStateStopped, and ContainerStateExited, and +// transition them to ContainerStateRunning (all containers not in +// ContainerStateCreated will make an intermediate stop there via the Init API). +// Once in ContainerStateRunning, the container can be transitioned to +// ContainerStatePaused via Pause(), or to ContainerStateStopped by the process +// stopping (either due to exit, or being forced to stop by the Kill or Stop API +// calls). +// Start requites that all dependency containers (e.g. pod infra containers) be +// running before being run. The recursive parameter, if set, will start all +// dependencies before starting this container. func (c *Container) Start(ctx context.Context, recursive bool) (err error) { span, _ := opentracing.StartSpanFromContext(ctx, "containerStart") span.SetTag("struct", "container") @@ -88,16 +107,11 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) { } // StartAndAttach starts a container and attaches to it. -// StartAndAttach can start configured, created or stopped containers. -// For configured containers, the container will be initialized first, then -// started. -// Stopped containers will be deleted and re-created in runc, undergoing a fresh -// Init(). -// If successful, an error channel will be returned containing the result of the -// attach call. -// The channel will be closed automatically after the result of attach has been -// sent. -// If recursive is set, StartAndAttach will also start all containers this container depends on. +// This acts as a combination of the Start and Attach APIs, ensuring proper +// ordering of the two such that no output from the container is lost (e.g. the +// Attach call occurs before Start). +// In overall functionality, it is identical to the Start call, with the added +// side effect that an attach session will also be started. func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) { if !c.batched { c.lock.Lock() diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go index 990140ee1..aad6e2294 100644 --- a/pkg/api/handlers/compat/containers_attach.go +++ b/pkg/api/handlers/compat/containers_attach.go @@ -89,7 +89,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) { } // For Docker compatibility, we need to re-initialize containers in these states. if state == define.ContainerStateConfigured || state == define.ContainerStateExited { - if err := ctr.Init(r.Context()); err != nil { + if err := ctr.Init(r.Context(), ctr.PodID() != ""); err != nil { utils.Error(w, "Container in wrong state", http.StatusConflict, errors.Wrapf(err, "error preparing container %s for attach", ctr.ID())) return } diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 50f6b1a38..2556cdc2a 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -294,7 +294,7 @@ func InitContainer(w http.ResponseWriter, r *http.Request) { utils.ContainerNotFound(w, name, err) return } - err = ctr.Init(r.Context()) + err = ctr.Init(r.Context(), ctr.PodID() != "") if errors.Cause(err) == define.ErrCtrStateInvalid { utils.Error(w, "container already initialized", http.StatusNotModified, err) return diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 8e0ffc075..22de28a1c 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -999,7 +999,7 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) for _, ctr := range ctrs { report := entities.ContainerInitReport{Id: ctr.ID()} - err := ctr.Init(ctx) + err := ctr.Init(ctx, ctr.PodID() != "") // If we're initializing all containers, ignore invalid state errors if options.All && errors.Cause(err) == define.ErrCtrStateInvalid { diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index 291353cad..a81d19131 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -385,7 +385,7 @@ func (i *VarlinkAPI) InitContainer(call iopodman.VarlinkCall, name string) error if err != nil { return call.ReplyContainerNotFound(name, err.Error()) } - if err := ctr.Init(getContext()); err != nil { + if err := ctr.Init(getContext(), false); err != nil { if errors.Cause(err) == define.ErrCtrStateInvalid { return call.ReplyInvalidState(ctr.ID(), err.Error()) } @@ -557,7 +557,7 @@ func (i *VarlinkAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) er // If the container hasn't been run, we need to run init // so the conmon sockets get created. if status == define.ContainerStateConfigured || status == define.ContainerStateStopped { - if err := ctr.Init(getContext()); err != nil { + if err := ctr.Init(getContext(), false); err != nil { return call.ReplyErrorOccurred(err.Error()) } } |