1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #25043 from stevvooe/forked-pull-context

container/controller: avoid cancellation with forked pull context
This commit is contained in:
Tibor Vass 2016-07-26 01:01:09 -07:00 committed by GitHub
commit 7bb9676a2b

View file

@ -24,6 +24,10 @@ type controller struct {
adapter *containerAdapter adapter *containerAdapter
closed chan struct{} closed chan struct{}
err error err error
pulled chan struct{} // closed after pull
cancelPull func() // cancels pull context if not nil
pullErr error // pull error, only read after pulled closed
} }
var _ exec.Controller = &controller{} var _ exec.Controller = &controller{}
@ -86,12 +90,27 @@ func (r *controller) Prepare(ctx context.Context) error {
} }
if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" { if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
if err := r.adapter.pullImage(ctx); err != nil { if r.pulled == nil {
cause := errors.Cause(err) // Fork the pull to a different context to allow pull to continue
if cause == context.Canceled || cause == context.DeadlineExceeded { // on re-entrant calls to Prepare. This ensures that Prepare can be
return err // idempotent and not incur the extra cost of pulling when
// cancelled on updates.
var pctx context.Context
r.pulled = make(chan struct{})
pctx, r.cancelPull = context.WithCancel(context.Background()) // TODO(stevvooe): Bind a context to the entire controller.
go func() {
defer close(r.pulled)
r.pullErr = r.adapter.pullImage(pctx) // protected by closing r.pulled
}()
} }
select {
case <-ctx.Done():
return ctx.Err()
case <-r.pulled:
if r.pullErr != nil {
// NOTE(stevvooe): We always try to pull the image to make sure we have // NOTE(stevvooe): We always try to pull the image to make sure we have
// the most up to date version. This will return an error, but we only // the most up to date version. This will return an error, but we only
// log it. If the image truly doesn't exist, the create below will // log it. If the image truly doesn't exist, the create below will
@ -103,7 +122,8 @@ func (r *controller) Prepare(ctx context.Context) error {
// //
// If you don't want this behavior, lock down your image to an // If you don't want this behavior, lock down your image to an
// immutable tag or digest. // immutable tag or digest.
log.G(ctx).WithError(err).Error("pulling image failed") log.G(ctx).WithError(r.pullErr).Error("pulling image failed")
}
} }
} }
@ -252,6 +272,10 @@ func (r *controller) Shutdown(ctx context.Context) error {
return err return err
} }
if r.cancelPull != nil {
r.cancelPull()
}
if err := r.adapter.shutdown(ctx); err != nil { if err := r.adapter.shutdown(ctx); err != nil {
if isUnknownContainer(err) || isStoppedContainer(err) { if isUnknownContainer(err) || isStoppedContainer(err) {
return nil return nil
@ -269,6 +293,10 @@ func (r *controller) Terminate(ctx context.Context) error {
return err return err
} }
if r.cancelPull != nil {
r.cancelPull()
}
if err := r.adapter.terminate(ctx); err != nil { if err := r.adapter.terminate(ctx); err != nil {
if isUnknownContainer(err) { if isUnknownContainer(err) {
return nil return nil
@ -286,6 +314,10 @@ func (r *controller) Remove(ctx context.Context) error {
return err return err
} }
if r.cancelPull != nil {
r.cancelPull()
}
// It may be necessary to shut down the task before removing it. // It may be necessary to shut down the task before removing it.
if err := r.Shutdown(ctx); err != nil { if err := r.Shutdown(ctx); err != nil {
if isUnknownContainer(err) { if isUnknownContainer(err) {
@ -320,6 +352,10 @@ func (r *controller) Close() error {
case <-r.closed: case <-r.closed:
return r.err return r.err
default: default:
if r.cancelPull != nil {
r.cancelPull()
}
r.err = exec.ErrControllerClosed r.err = exec.ErrControllerClosed
close(r.closed) close(r.closed)
} }