Skip to content

Commit e3b231e

Browse files
committed
3432: Support --pull flag in nerdctl compose up
Signed-off-by: Manu Gupta <[email protected]>
1 parent 00f6acd commit e3b231e

File tree

7 files changed

+69
-4
lines changed

7 files changed

+69
-4
lines changed

cmd/nerdctl/compose/compose_up.go

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func newComposeUpCommand() *cobra.Command {
5050
composeUpCommand.Flags().Bool("force-recreate", false, "Recreate containers even if their configuration and image haven't changed.")
5151
composeUpCommand.Flags().Bool("no-recreate", false, "Don't recreate containers if they exist, conflict with --force-recreate.")
5252
composeUpCommand.Flags().StringArray("scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.")
53+
composeUpCommand.Flags().String("pull", "", "Pull image before running (\"always\"|\"missing\"|\"never\")")
5354
return composeUpCommand
5455
}
5556

@@ -96,6 +97,10 @@ func composeUpAction(cmd *cobra.Command, services []string) error {
9697
if err != nil {
9798
return err
9899
}
100+
pull, err := cmd.Flags().GetString("pull")
101+
if err != nil {
102+
return err
103+
}
99104
removeOrphans, err := cmd.Flags().GetBool("remove-orphans")
100105
if err != nil {
101106
return err
@@ -154,6 +159,7 @@ func composeUpAction(cmd *cobra.Command, services []string) error {
154159
QuietPull: quietPull,
155160
RemoveOrphans: removeOrphans,
156161
Scale: scale,
162+
Pull: pull,
157163
ForceRecreate: forceRecreate,
158164
NoRecreate: noRecreate,
159165
}

cmd/nerdctl/compose/compose_up_linux_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -579,3 +579,57 @@ services:
579579
}
580580
c.Assert(expected)
581581
}
582+
583+
func TestComposeUpPull(t *testing.T) {
584+
base := testutil.NewBase(t)
585+
586+
var dockerComposeYAML = fmt.Sprintf(`
587+
services:
588+
test:
589+
image: %s
590+
command: sh -euxc "echo hi"
591+
`, testutil.CommonImage)
592+
593+
comp := testutil.NewComposeDir(t, dockerComposeYAML)
594+
defer comp.CleanUp()
595+
596+
// Cases where pull is required
597+
for _, pull := range []string{"missing", "always"} {
598+
t.Run(fmt.Sprintf("pull=%s", pull), func(t *testing.T) {
599+
base.Cmd("rmi", "-f", testutil.CommonImage).Run()
600+
base.Cmd("images").AssertOutNotContains(testutil.CommonImage)
601+
t.Cleanup(func() {
602+
base.ComposeCmd("-f", comp.YAMLFullPath(), "down").AssertOK()
603+
})
604+
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "--pull", pull).AssertOutContains("hi")
605+
})
606+
}
607+
608+
t.Run("pull=never, no pull", func(t *testing.T) {
609+
base.Cmd("rmi", "-f", testutil.CommonImage).Run()
610+
base.Cmd("images").AssertOutNotContains(testutil.CommonImage)
611+
t.Cleanup(func() {
612+
base.ComposeCmd("-f", comp.YAMLFullPath(), "down").AssertOK()
613+
})
614+
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "--pull", "never").AssertExitCode(1)
615+
})
616+
}
617+
618+
func TestComposeUpServicePullPolicy(t *testing.T) {
619+
base := testutil.NewBase(t)
620+
621+
var dockerComposeYAML = fmt.Sprintf(`
622+
services:
623+
test:
624+
image: %s
625+
command: sh -euxc "echo hi"
626+
pull_policy: "never"
627+
`, testutil.CommonImage)
628+
629+
comp := testutil.NewComposeDir(t, dockerComposeYAML)
630+
defer comp.CleanUp()
631+
632+
base.Cmd("rmi", "-f", testutil.CommonImage).Run()
633+
base.Cmd("images").AssertOutNotContains(testutil.CommonImage)
634+
base.ComposeCmd("-f", comp.YAMLFullPath(), "up").AssertExitCode(1)
635+
}

docs/command-reference.md

+1
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,7 @@ Flags:
14131413
- :whale: `--remove-orphans`: Remove containers for services not defined in the Compose file
14141414
- :whale: `--force-recreate`: force Compose to stop and recreate all containers
14151415
- :whale: `--no-recreate`: force Compose to reuse existing containers
1416+
- :whale: `--pull`: Pull image before running ("always"|"missing"|"never")
14161417

14171418
Unimplemented `docker-compose up` (V1) flags: `--no-deps`, `--always-recreate-deps`,
14181419
`--no-start`, `--abort-on-container-exit`, `--attach-dependencies`, `--timeout`, `--renew-anon-volumes`, `--exit-code-from`

pkg/composer/create.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (c *Composer) Create(ctx context.Context, opt CreateOptions, services []str
119119
return err
120120
}
121121
for _, ps := range parsedServices {
122-
if err := c.ensureServiceImage(ctx, ps, !opt.NoBuild, opt.Build, BuildOptions{}, false); err != nil {
122+
if err := c.ensureServiceImage(ctx, ps, !opt.NoBuild, opt.Build, BuildOptions{}, false, ""); err != nil {
123123
return err
124124
}
125125
}

pkg/composer/run.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func (c *Composer) runServices(ctx context.Context, parsedServices []*servicepar
215215

216216
// TODO: parallelize loop for ensuring images (make sure not to mess up tty)
217217
for _, ps := range parsedServices {
218-
if err := c.ensureServiceImage(ctx, ps, !ro.NoBuild, ro.ForceBuild, BuildOptions{}, ro.QuietPull); err != nil {
218+
if err := c.ensureServiceImage(ctx, ps, !ro.NoBuild, ro.ForceBuild, BuildOptions{}, ro.QuietPull, ""); err != nil {
219219
return err
220220
}
221221
}

pkg/composer/up.go

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type UpOptions struct {
4242
ForceRecreate bool
4343
NoRecreate bool
4444
Scale map[string]int // map of service name to replicas
45+
Pull string
4546
}
4647

4748
func (opts UpOptions) recreateStrategy() string {

pkg/composer/up_service.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func (c *Composer) upServices(ctx context.Context, parsedServices []*servicepars
4141

4242
// TODO: parallelize loop for ensuring images (make sure not to mess up tty)
4343
for _, ps := range parsedServices {
44-
if err := c.ensureServiceImage(ctx, ps, !uo.NoBuild, uo.ForceBuild, BuildOptions{}, uo.QuietPull); err != nil {
44+
if err := c.ensureServiceImage(ctx, ps, !uo.NoBuild, uo.ForceBuild, BuildOptions{}, uo.QuietPull, uo.Pull); err != nil {
4545
return err
4646
}
4747
}
@@ -101,7 +101,7 @@ func (c *Composer) upServices(ctx context.Context, parsedServices []*servicepars
101101
return nil
102102
}
103103

104-
func (c *Composer) ensureServiceImage(ctx context.Context, ps *serviceparser.Service, allowBuild, forceBuild bool, bo BuildOptions, quiet bool) error {
104+
func (c *Composer) ensureServiceImage(ctx context.Context, ps *serviceparser.Service, allowBuild, forceBuild bool, bo BuildOptions, quiet bool, pullModeArg string) error {
105105
if ps.Build != nil && allowBuild {
106106
if ps.Build.Force || forceBuild {
107107
return c.buildServiceImage(ctx, ps.Image, ps.Build, ps.Unparsed.Platform, bo)
@@ -117,6 +117,9 @@ func (c *Composer) ensureServiceImage(ctx context.Context, ps *serviceparser.Ser
117117
}
118118

119119
log.G(ctx).Infof("Ensuring image %s", ps.Image)
120+
if pullModeArg != "" {
121+
return c.EnsureImage(ctx, ps.Image, pullModeArg, ps.Unparsed.Platform, ps, quiet)
122+
}
120123
return c.EnsureImage(ctx, ps.Image, ps.PullMode, ps.Unparsed.Platform, ps, quiet)
121124
}
122125

0 commit comments

Comments
 (0)