From 0a05bbc84e8ebaea2157fe24773bf14ded59e7ce Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Tue, 2 Mar 2021 08:49:55 +0100 Subject: [PATCH 1/3] introduce pull --include-deps Signed-off-by: Nicolas De Loof --- cli/cmd/compose/pull.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cli/cmd/compose/pull.go b/cli/cmd/compose/pull.go index d7b8b7ddb..e09e179a4 100644 --- a/cli/cmd/compose/pull.go +++ b/cli/cmd/compose/pull.go @@ -28,7 +28,8 @@ import ( type pullOptions struct { *projectOptions composeOptions - quiet bool + quiet bool + includeDeps bool } func pullCommand(p *projectOptions) *cobra.Command { @@ -43,6 +44,7 @@ func pullCommand(p *projectOptions) *cobra.Command { }, } cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information") + cmd.Flags().BoolVar(&opts.includeDeps, "include-deps", false, "Also pull services declared as dependencies") return cmd } @@ -57,6 +59,15 @@ func runPull(ctx context.Context, opts pullOptions, services []string) error { return err } + if !opts.includeDeps { + enabled, err := project.GetServices(services...) + if err != nil { + return err + } + project.DisabledServices = append(project.DisabledServices, project.Services...) + project.Services = enabled + } + if opts.quiet { return c.ComposeService().Pull(ctx, project) } From fada87ca19da4748318275a9fc719a800ca5a856 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Tue, 2 Mar 2021 09:30:26 +0100 Subject: [PATCH 2/3] don't put enabled services in the disabled slice Signed-off-by: Nicolas De Loof --- cli/cmd/compose/pull.go | 6 ++- cli/cmd/compose/run.go | 6 ++- cli/cmd/compose/up.go | 108 +++++++++++++++++++------------------ cli/cmd/compose/up_test.go | 3 +- 4 files changed, 68 insertions(+), 55 deletions(-) diff --git a/cli/cmd/compose/pull.go b/cli/cmd/compose/pull.go index e09e179a4..0bbd6e418 100644 --- a/cli/cmd/compose/pull.go +++ b/cli/cmd/compose/pull.go @@ -64,7 +64,11 @@ func runPull(ctx context.Context, opts pullOptions, services []string) error { if err != nil { return err } - project.DisabledServices = append(project.DisabledServices, project.Services...) + for _, s := range project.Services { + if !contains(services, s.Name) { + project.DisabledServices = append(project.DisabledServices, s) + } + } project.Services = enabled } diff --git a/cli/cmd/compose/run.go b/cli/cmd/compose/run.go index e3a7af70e..4eacd0708 100644 --- a/cli/cmd/compose/run.go +++ b/cli/cmd/compose/run.go @@ -93,7 +93,11 @@ func runRun(ctx context.Context, opts runOptions) error { if err != nil { return err } - project.DisabledServices = append(project.DisabledServices, project.Services...) + for _, s := range project.Services { + if s.Name != opts.Service { + project.DisabledServices = append(project.DisabledServices, s) + } + } project.Services = types.Services{enabled} } diff --git a/cli/cmd/compose/up.go b/cli/cmd/compose/up.go index 4416f91b4..8d397d46f 100644 --- a/cli/cmd/compose/up.go +++ b/cli/cmd/compose/up.go @@ -19,6 +19,12 @@ package compose import ( "context" "fmt" + "github.com/docker/compose-cli/api/client" + "github.com/docker/compose-cli/api/compose" + "github.com/docker/compose-cli/api/context/store" + "github.com/docker/compose-cli/api/progress" + "github.com/docker/compose-cli/cli/cmd" + "github.com/docker/compose-cli/cli/formatter" "os" "os/signal" "path/filepath" @@ -27,13 +33,6 @@ import ( "syscall" "time" - "github.com/docker/compose-cli/api/client" - "github.com/docker/compose-cli/api/compose" - "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/api/progress" - "github.com/docker/compose-cli/cli/cmd" - "github.com/docker/compose-cli/cli/formatter" - "github.com/compose-spec/compose-go/types" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -89,6 +88,54 @@ func (o upOptions) dependenciesRecreateStrategy() string { return compose.RecreateDiverged } +func (opts upOptions) apply(project *types.Project, services []string) error { + if opts.noDeps { + enabled, err := project.GetServices(services...) + if err != nil { + return err + } + for _, s := range project.Services { + if !contains(services, s.Name) { + project.DisabledServices = append(project.DisabledServices, s) + } + } + project.Services = enabled + } + + if opts.exitCodeFrom != "" { + _, err := project.GetService(opts.exitCodeFrom) + if err != nil { + return err + } + } + + if opts.timeChanged { + timeoutValue := types.Duration(time.Duration(opts.timeout) * time.Second) + for i, s := range project.Services { + s.StopGracePeriod = &timeoutValue + project.Services[i] = s + } + } + + for _, scale := range opts.scale { + split := strings.Split(scale, "=") + if len(split) != 2 { + return fmt.Errorf("invalid --scale option %q. Should be SERVICE=NUM", scale) + } + name := split[0] + replicas, err := strconv.Atoi(split[1]) + if err != nil { + return err + } + err = setServiceScale(project, name, replicas) + if err != nil { + return err + } + } + + return nil +} + func upCommand(p *projectOptions, contextType string) *cobra.Command { opts := upOptions{ composeOptions: &composeOptions{ @@ -157,7 +204,7 @@ func runUp(ctx context.Context, opts upOptions, services []string) error { return err } - err = applyScaleOpt(opts.scale, project) + err = opts.apply(project, services) if err != nil { return err } @@ -176,35 +223,11 @@ func runCreateStart(ctx context.Context, opts upOptions, services []string) erro return err } - if opts.noDeps { - enabled, err := project.GetServices(services...) - if err != nil { - return err - } - project.DisabledServices = append(project.DisabledServices, project.Services...) - project.Services = enabled - } - - err = applyScaleOpt(opts.scale, project) + err = opts.apply(project, services) if err != nil { return err } - if opts.exitCodeFrom != "" { - _, err := project.GetService(opts.exitCodeFrom) - if err != nil { - return err - } - } - - if opts.timeChanged { - timeoutValue := types.Duration(time.Duration(opts.timeout) * time.Second) - for i, s := range project.Services { - s.StopGracePeriod = &timeoutValue - project.Services[i] = s - } - } - _, err = progress.Run(ctx, func(ctx context.Context) (string, error) { err := c.ComposeService().Create(ctx, project, compose.CreateOptions{ Services: services, @@ -282,25 +305,6 @@ func runCreateStart(ctx context.Context, opts upOptions, services []string) erro return err } -func applyScaleOpt(opts []string, project *types.Project) error { - for _, scale := range opts { - split := strings.Split(scale, "=") - if len(split) != 2 { - return fmt.Errorf("invalid --scale option %q. Should be SERVICE=NUM", scale) - } - name := split[0] - replicas, err := strconv.Atoi(split[1]) - if err != nil { - return err - } - err = setServiceScale(project, name, replicas) - if err != nil { - return err - } - } - return nil -} - func setServiceScale(project *types.Project, name string, replicas int) error { for i, s := range project.Services { if s.Name == name { diff --git a/cli/cmd/compose/up_test.go b/cli/cmd/compose/up_test.go index 03dac57e0..566a68476 100644 --- a/cli/cmd/compose/up_test.go +++ b/cli/cmd/compose/up_test.go @@ -34,7 +34,8 @@ func TestApplyScaleOpt(t *testing.T) { }, }, } - err := applyScaleOpt([]string{"foo=2"}, &p) + opt := upOptions{scale: []string{"foo=2"}} + err := opt.apply(&p, nil) assert.NilError(t, err) foo, err := p.GetService("foo") assert.NilError(t, err) From e9c9a1983cf9f8c6eb8e04c356b4e0e9f0cd5d58 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Tue, 2 Mar 2021 14:33:26 +0100 Subject: [PATCH 3/3] pass timeout to "up" backend implementation Signed-off-by: Nicolas De Loof --- api/compose/api.go | 2 ++ cli/cmd/compose/up.go | 42 +++++++++++++++++++----------------- local/compose/convergence.go | 14 ++++++------ local/compose/create.go | 4 ++-- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/api/compose/api.go b/api/compose/api.go index dfd919333..5efc209ee 100644 --- a/api/compose/api.go +++ b/api/compose/api.go @@ -77,6 +77,8 @@ type CreateOptions struct { RecreateDependencies string // Inherit reuse anonymous volumes from previous container Inherit bool + // Timeout set delay to wait for container to gracelfuly stop before sending SIGKILL + Timeout *time.Duration } // StartOptions group options of the Start API diff --git a/cli/cmd/compose/up.go b/cli/cmd/compose/up.go index 8d397d46f..a97e0d09c 100644 --- a/cli/cmd/compose/up.go +++ b/cli/cmd/compose/up.go @@ -19,12 +19,6 @@ package compose import ( "context" "fmt" - "github.com/docker/compose-cli/api/client" - "github.com/docker/compose-cli/api/compose" - "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/api/progress" - "github.com/docker/compose-cli/cli/cmd" - "github.com/docker/compose-cli/cli/formatter" "os" "os/signal" "path/filepath" @@ -37,6 +31,13 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" + + "github.com/docker/compose-cli/api/client" + "github.com/docker/compose-cli/api/compose" + "github.com/docker/compose-cli/api/context/store" + "github.com/docker/compose-cli/api/progress" + "github.com/docker/compose-cli/cli/cmd" + "github.com/docker/compose-cli/cli/formatter" ) // composeOptions hold options common to `up` and `run` to run compose project @@ -68,26 +69,34 @@ type upOptions struct { noInherit bool } -func (o upOptions) recreateStrategy() string { - if o.noRecreate { +func (opts upOptions) recreateStrategy() string { + if opts.noRecreate { return compose.RecreateNever } - if o.forceRecreate { + if opts.forceRecreate { return compose.RecreateForce } return compose.RecreateDiverged } -func (o upOptions) dependenciesRecreateStrategy() string { - if o.noRecreate { +func (opts upOptions) dependenciesRecreateStrategy() string { + if opts.noRecreate { return compose.RecreateNever } - if o.recreateDeps { + if opts.recreateDeps { return compose.RecreateForce } return compose.RecreateDiverged } +func (opts upOptions) GetTimeout() *time.Duration { + if opts.timeChanged { + t := time.Duration(opts.timeout) * time.Second + return &t + } + return nil +} + func (opts upOptions) apply(project *types.Project, services []string) error { if opts.noDeps { enabled, err := project.GetServices(services...) @@ -109,14 +118,6 @@ func (opts upOptions) apply(project *types.Project, services []string) error { } } - if opts.timeChanged { - timeoutValue := types.Duration(time.Duration(opts.timeout) * time.Second) - for i, s := range project.Services { - s.StopGracePeriod = &timeoutValue - project.Services[i] = s - } - } - for _, scale := range opts.scale { split := strings.Split(scale, "=") if len(split) != 2 { @@ -235,6 +236,7 @@ func runCreateStart(ctx context.Context, opts upOptions, services []string) erro Recreate: opts.recreateStrategy(), RecreateDependencies: opts.dependenciesRecreateStrategy(), Inherit: !opts.noInherit, + Timeout: opts.GetTimeout(), }) if err != nil { return "", err diff --git a/local/compose/convergence.go b/local/compose/convergence.go index aaf8bc0a2..fe0571a56 100644 --- a/local/compose/convergence.go +++ b/local/compose/convergence.go @@ -42,7 +42,7 @@ const ( "Remove the custom name to scale the service.\n" ) -func (s *composeService) ensureScale(ctx context.Context, project *types.Project, service types.ServiceConfig) (*errgroup.Group, []moby.Container, error) { +func (s *composeService) ensureScale(ctx context.Context, project *types.Project, service types.ServiceConfig, timeout *time.Duration) (*errgroup.Group, []moby.Container, error) { cState, err := GetContextContainerState(ctx) if err != nil { return nil, nil, err @@ -73,7 +73,7 @@ func (s *composeService) ensureScale(ctx context.Context, project *types.Project for i := scale; i < len(actual); i++ { container := actual[i] eg.Go(func() error { - err := s.apiClient.ContainerStop(ctx, container.ID, nil) + err := s.apiClient.ContainerStop(ctx, container.ID, timeout) if err != nil { return err } @@ -85,8 +85,8 @@ func (s *composeService) ensureScale(ctx context.Context, project *types.Project return eg, actual, nil } -func (s *composeService) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool) error { - eg, actual, err := s.ensureScale(ctx, project, service) +func (s *composeService) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error { + eg, actual, err := s.ensureScale(ctx, project, service, timeout) if err != nil { return err } @@ -106,7 +106,7 @@ func (s *composeService) ensureService(ctx context.Context, project *types.Proje diverged := container.Labels[configHashLabel] != expected if diverged || recreate == compose.RecreateForce || service.Extensions[extLifecycle] == forceRecreate { eg.Go(func() error { - return s.recreateContainer(ctx, project, service, container, inherit) + return s.recreateContainer(ctx, project, service, container, inherit, timeout) }) continue } @@ -209,10 +209,10 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro return nil } -func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, container moby.Container, inherit bool) error { +func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, container moby.Container, inherit bool, timeout *time.Duration) error { w := progress.ContextWriter(ctx) w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Recreate")) - err := s.apiClient.ContainerStop(ctx, container.ID, nil) + err := s.apiClient.ContainerStop(ctx, container.ID, timeout) if err != nil { return err } diff --git a/local/compose/create.go b/local/compose/create.go index 123502030..f35557d9f 100644 --- a/local/compose/create.go +++ b/local/compose/create.go @@ -102,9 +102,9 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error { if contains(opts.Services, service.Name) { - return s.ensureService(c, project, service, opts.Recreate, opts.Inherit) + return s.ensureService(c, project, service, opts.Recreate, opts.Inherit, opts.Timeout) } - return s.ensureService(c, project, service, opts.RecreateDependencies, opts.Inherit) + return s.ensureService(c, project, service, opts.RecreateDependencies, opts.Inherit, opts.Timeout) }) }