Skip to content

Commit 55721c4

Browse files
committed
Add docker compose deploy command
Implements a production-oriented deploy command that builds, pushes, and applies a Compose project to a Docker server with force-recreate semantics and optional health-check-based wait for zero-downtime deployments. Signed-off-by: Eric Curtin <eric.curtin@docker.com>
1 parent b043368 commit 55721c4

4 files changed

Lines changed: 174 additions & 0 deletions

File tree

cmd/compose/compose.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
615615
statsCommand(&opts, dockerCli),
616616
watchCommand(&opts, dockerCli, backendOptions),
617617
publishCommand(&opts, dockerCli, backendOptions),
618+
deployCommand(&opts, dockerCli, backendOptions),
618619
alphaCommand(&opts, dockerCli, backendOptions),
619620
bridgeCommand(&opts, dockerCli),
620621
volumesCommand(&opts, dockerCli, backendOptions),

cmd/compose/deploy.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
"time"
22+
23+
"github.com/docker/cli/cli/command"
24+
"github.com/spf13/cobra"
25+
26+
"github.com/docker/compose/v5/pkg/api"
27+
"github.com/docker/compose/v5/pkg/compose"
28+
)
29+
30+
type deployOptions struct {
31+
*ProjectOptions
32+
composeOptions
33+
build bool
34+
noBuild bool
35+
push bool
36+
quiet bool
37+
removeOrphans bool
38+
wait bool
39+
waitTimeout int
40+
}
41+
42+
func deployCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *BackendOptions) *cobra.Command {
43+
opts := deployOptions{
44+
ProjectOptions: p,
45+
}
46+
cmd := &cobra.Command{
47+
Use: "deploy [OPTIONS] [SERVICE...]",
48+
Short: "Deploy a Compose application to a Docker server",
49+
Long: `Deploy a Compose application to a Docker server.
50+
51+
This command applies the Compose project to the target Docker server,
52+
recreating containers with updated configuration and images. Images are
53+
pulled from the registry unless --build is specified.
54+
55+
Use health checks defined in the Compose file to ensure zero-downtime
56+
deployments by passing --wait.`,
57+
RunE: Adapt(func(ctx context.Context, args []string) error {
58+
return runDeploy(ctx, dockerCli, backendOptions, opts, args)
59+
}),
60+
ValidArgsFunction: completeServiceNames(dockerCli, p),
61+
}
62+
flags := cmd.Flags()
63+
flags.BoolVar(&opts.build, "build", false, "Build images before deploying")
64+
flags.BoolVar(&opts.noBuild, "no-build", false, "Do not build images even if build configuration is defined")
65+
flags.BoolVar(&opts.push, "push", false, "Push images to registry before deploying")
66+
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress pull/push progress output")
67+
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
68+
flags.BoolVar(&opts.wait, "wait", false, "Wait for services to be healthy before returning")
69+
flags.IntVar(&opts.waitTimeout, "wait-timeout", 0, "Maximum duration in seconds to wait for services to be healthy (0 = no timeout)")
70+
return cmd
71+
}
72+
73+
func runDeploy(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts deployOptions, services []string) error {
74+
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
75+
if err != nil {
76+
return err
77+
}
78+
79+
project, _, err := opts.ToProject(ctx, dockerCli, backend, services)
80+
if err != nil {
81+
return err
82+
}
83+
84+
deployOpts := api.DeployOptions{
85+
Push: opts.push,
86+
Quiet: opts.quiet,
87+
RemoveOrphans: opts.removeOrphans,
88+
Wait: opts.wait,
89+
}
90+
91+
if opts.waitTimeout > 0 {
92+
deployOpts.WaitTimeout = time.Duration(opts.waitTimeout) * time.Second
93+
}
94+
95+
if opts.build && !opts.noBuild {
96+
deployOpts.Build = &api.BuildOptions{
97+
Services: services,
98+
}
99+
}
100+
101+
return backend.Deploy(ctx, project, deployOpts)
102+
}

pkg/api/api.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,24 @@ type Compose interface {
149149
Volumes(ctx context.Context, project string, options VolumesOptions) ([]VolumesSummary, error)
150150
// LoadProject loads and validates a Compose project from configuration files.
151151
LoadProject(ctx context.Context, options ProjectLoadOptions) (*types.Project, error)
152+
// Deploy executes the equivalent to a `compose deploy`
153+
Deploy(ctx context.Context, project *types.Project, options DeployOptions) error
154+
}
155+
156+
// DeployOptions group options of the Deploy API
157+
type DeployOptions struct {
158+
// Build rebuilds service images before deploying
159+
Build *BuildOptions
160+
// Push pushes images to the registry before deploying
161+
Push bool
162+
// Quiet suppresses pull/push progress output
163+
Quiet bool
164+
// RemoveOrphans removes containers for services not defined in the project
165+
RemoveOrphans bool
166+
// Wait waits for services to be healthy after deploy
167+
Wait bool
168+
// WaitTimeout is the maximum time to wait for services to become healthy
169+
WaitTimeout time.Duration
152170
}
153171

154172
type VolumesOptions struct {

pkg/compose/deploy.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
22+
"github.com/compose-spec/compose-go/v2/types"
23+
24+
"github.com/docker/compose/v5/pkg/api"
25+
)
26+
27+
func (s *composeService) Deploy(ctx context.Context, project *types.Project, options api.DeployOptions) error {
28+
if options.Build != nil {
29+
if err := s.Build(ctx, project, *options.Build); err != nil {
30+
return err
31+
}
32+
}
33+
34+
if options.Push {
35+
if err := s.Push(ctx, project, api.PushOptions{
36+
Quiet: options.Quiet,
37+
}); err != nil {
38+
return err
39+
}
40+
}
41+
42+
return s.Up(ctx, project, api.UpOptions{
43+
Create: api.CreateOptions{
44+
Recreate: api.RecreateForce,
45+
RemoveOrphans: options.RemoveOrphans,
46+
QuietPull: options.Quiet,
47+
},
48+
Start: api.StartOptions{
49+
Wait: options.Wait,
50+
WaitTimeout: options.WaitTimeout,
51+
},
52+
})
53+
}

0 commit comments

Comments
 (0)