From 9e77499c2a8b3dc2734d537b1e33425847661eaa Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Thu, 28 Jan 2021 15:00:46 +0100 Subject: [PATCH] introduce --force-recreate and --no-recreate Signed-off-by: Nicolas De Loof --- api/compose/api.go | 11 +++++++++++ cli/cmd/compose/up.go | 23 ++++++++++++++++++++++- local/compose/convergence.go | 9 +++++++-- local/compose/create.go | 2 +- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/api/compose/api.go b/api/compose/api.go index 3c613939e..079492c1a 100644 --- a/api/compose/api.go +++ b/api/compose/api.go @@ -57,6 +57,8 @@ type Service interface { type CreateOptions struct { // Remove legacy containers for services that are not defined in the project RemoveOrphans bool + // Recreate define the strategy to apply on existing containers + Recreate string } // UpOptions group options of the Up API @@ -137,6 +139,15 @@ const ( FAILED string = "Failed" ) +const ( + // RecreateDiverged to recreate services which configuration diverges from compose model + RecreateDiverged = "diverged" + // RecreateForce to force service container being recreated + RecreateForce = "force" + // RecreateNever to never recreate existing service containers + RecreateNever = "never" +) + // Stack holds the name and state of a compose application/stack type Stack struct { ID string diff --git a/cli/cmd/compose/up.go b/cli/cmd/compose/up.go index 40be90241..089c70f25 100644 --- a/cli/cmd/compose/up.go +++ b/cli/cmd/compose/up.go @@ -45,6 +45,18 @@ type upOptions struct { Detach bool Environment []string removeOrphans bool + forceRecreate bool + noRecreate bool +} + +func (o upOptions) recreateStrategy() string { + if o.noRecreate { + return compose.RecreateNever + } + if o.forceRecreate { + return compose.RecreateForce + } + return compose.RecreateDiverged } func upCommand(p *projectOptions, contextType string) *cobra.Command { @@ -59,6 +71,9 @@ func upCommand(p *projectOptions, contextType string) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { switch contextType { case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType: + if opts.forceRecreate && opts.noRecreate { + return fmt.Errorf("--force-recreate and --no-recreate are incompatible") + } return runCreateStart(cmd.Context(), opts, args) default: return runUp(cmd.Context(), opts, args) @@ -71,8 +86,13 @@ func upCommand(p *projectOptions, contextType string) *cobra.Command { flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.") flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.") - if contextType == store.AciContextType { + switch contextType { + case store.AciContextType: flags.StringVar(&opts.DomainName, "domainname", "", "Container NIS domain name") + case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType: + flags.BoolVar(&opts.forceRecreate, "force-recreate", false, "Recreate containers even if their configuration and image haven't changed.") + flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.") + } return upCmd @@ -101,6 +121,7 @@ func runCreateStart(ctx context.Context, opts upOptions, services []string) erro _, err = progress.Run(ctx, func(ctx context.Context) (string, error) { return "", c.ComposeService().Create(ctx, project, compose.CreateOptions{ RemoveOrphans: opts.removeOrphans, + Recreate: opts.recreateStrategy(), }) }) if err != nil { diff --git a/local/compose/convergence.go b/local/compose/convergence.go index 832273367..c56adfa93 100644 --- a/local/compose/convergence.go +++ b/local/compose/convergence.go @@ -28,6 +28,7 @@ import ( "github.com/docker/docker/api/types/network" "golang.org/x/sync/errgroup" + "github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/progress" status "github.com/docker/compose-cli/local/moby" ) @@ -74,7 +75,7 @@ func (s *composeService) ensureScale(ctx context.Context, actual []moby.Containe return eg, actual, nil } -func (s *composeService) ensureService(ctx context.Context, observedState Containers, project *types.Project, service types.ServiceConfig) error { +func (s *composeService) ensureService(ctx context.Context, observedState Containers, project *types.Project, service types.ServiceConfig, recreate string) error { actual := observedState.filter(isService(service.Name)) scale, err := getScale(service) @@ -87,6 +88,10 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai return err } + if recreate == compose.RecreateNever { + return nil + } + expected, err := jsonHash(service) if err != nil { return err @@ -96,7 +101,7 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai name := getCanonicalContainerName(container) diverged := container.Labels[configHashLabel] != expected - if diverged || service.Extensions[extLifecycle] == forceRecreate { + if diverged || recreate == compose.RecreateForce || service.Extensions[extLifecycle] == forceRecreate { eg.Go(func() error { return s.recreateContainer(ctx, project, service, container) }) diff --git a/local/compose/create.go b/local/compose/create.go index 35cf3632a..5b6e4d622 100644 --- a/local/compose/create.go +++ b/local/compose/create.go @@ -95,7 +95,7 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt } return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error { - return s.ensureService(c, observedState, project, service) + return s.ensureService(c, observedState, project, service, opts.Recreate) }) }