github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/internal/pkg/containerized/pauseandrun.go (about) 1 package containerized 2 3 import ( 4 "context" 5 6 "github.com/containerd/containerd" 7 "github.com/containerd/containerd/errdefs" 8 "github.com/pkg/errors" 9 "github.com/sirupsen/logrus" 10 ) 11 12 // AtomicImageUpdate will perform an update of the given container with the new image 13 // and verify success via the provided healthcheckFn. If the healthcheck fails, the 14 // container will be reverted to the prior image 15 func AtomicImageUpdate(ctx context.Context, container containerd.Container, image containerd.Image, healthcheckFn func() error) error { 16 updateCompleted := false 17 err := pauseAndRun(ctx, container, func() error { 18 if err := container.Update(ctx, WithUpgrade(image)); err != nil { 19 return errors.Wrap(err, "failed to update to new image") 20 } 21 updateCompleted = true 22 task, err := container.Task(ctx, nil) 23 if err != nil { 24 if errdefs.IsNotFound(err) { 25 return nil 26 } 27 return errors.Wrap(err, "failed to lookup task") 28 } 29 return task.Kill(ctx, sigTERM) 30 }) 31 if err != nil { 32 if updateCompleted { 33 logrus.WithError(err).Error("failed to update, rolling back") 34 return rollBack(ctx, container) 35 } 36 return err 37 } 38 if err := healthcheckFn(); err != nil { 39 logrus.WithError(err).Error("failed health check, rolling back") 40 return rollBack(ctx, container) 41 } 42 return nil 43 } 44 45 func rollBack(ctx context.Context, container containerd.Container) error { 46 return pauseAndRun(ctx, container, func() error { 47 if err := container.Update(ctx, WithRollback); err != nil { 48 return err 49 } 50 task, err := container.Task(ctx, nil) 51 if err != nil { 52 if errdefs.IsNotFound(err) { 53 return nil 54 } 55 return errors.Wrap(err, "failed to lookup task") 56 } 57 return task.Kill(ctx, sigTERM) 58 }) 59 } 60 61 func pauseAndRun(ctx context.Context, container containerd.Container, fn func() error) error { 62 task, err := container.Task(ctx, nil) 63 if err != nil { 64 if errdefs.IsNotFound(err) { 65 return fn() 66 } 67 return errors.Wrap(err, "failed to lookup task") 68 } 69 if err := task.Pause(ctx); err != nil { 70 return errors.Wrap(err, "failed to pause task") 71 } 72 defer task.Resume(ctx) 73 return fn() 74 }