Skip to content

Commit

Permalink
Add --pull option to build command
Browse files Browse the repository at this point in the history
Signed-off-by: David Son <[email protected]>
  • Loading branch information
sondavidb committed Jun 19, 2024
1 parent 57ff86d commit bb9e7f0
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 0 deletions.
10 changes: 10 additions & 0 deletions cmd/nerdctl/builder_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ If Dockerfile is not present and -f is not specified, it will look for Container
buildCommand.Flags().Bool("no-cache", false, "Do not use cache when building the image")
buildCommand.Flags().StringP("output", "o", "", "Output destination (format: type=local,dest=path)")
buildCommand.Flags().String("progress", "auto", "Set type of progress output (auto, plain, tty). Use plain to show container output")
buildCommand.Flags().Bool("pull", false, "On true, always attempt to pull latest image version from remote. Default uses buildkit's default.")
buildCommand.Flags().StringArray("secret", nil, "Secret file to expose to the build: id=mysecret,src=/local/secret")
buildCommand.Flags().StringArray("allow", nil, "Allow extra privileged entitlement, e.g. network.host, security.insecure")
buildCommand.RegisterFlagCompletionFunc("allow", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
Expand Down Expand Up @@ -129,6 +130,14 @@ func processBuildCommandFlag(cmd *cobra.Command, args []string) (types.BuilderBu
if err != nil {
return types.BuilderBuildOptions{}, err
}
var pull *bool
if cmd.Flags().Changed("pull") {
pullFlag, err := cmd.Flags().GetBool("pull")
if err != nil {
return types.BuilderBuildOptions{}, err
}
pull = &pullFlag
}
secret, err := cmd.Flags().GetStringArray("secret")
if err != nil {
return types.BuilderBuildOptions{}, err
Expand Down Expand Up @@ -177,6 +186,7 @@ func processBuildCommandFlag(cmd *cobra.Command, args []string) (types.BuilderBu
BuildArgs: buildArgs,
Label: label,
NoCache: noCache,
Pull: pull,
Secret: secret,
Allow: allow,
SSH: ssh,
Expand Down
91 changes: 91 additions & 0 deletions cmd/nerdctl/builder_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/containerd/nerdctl/pkg/testutil"
Expand All @@ -40,3 +42,92 @@ CMD ["echo", "nerdctl-builder-debug-test-string"]

base.Cmd("builder", "debug", buildCtx).CmdOption(testutil.WithStdin(bytes.NewReader([]byte("c\n")))).AssertOK()
}

func TestBuildWithPull(t *testing.T) {
testutil.DockerIncompatible(t)
testutil.RequiresBuild(t)

oldImage := testutil.BusyboxImage
oldImageSha := "141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47"
newImage := testutil.AlpineImage

buildkitConfig := fmt.Sprintf(`[worker.oci]
enabled = false
[worker.containerd]
enabled = true
namespace = "%s"`, testutil.Namespace)

cleanup := useBuildkitConfig(t, buildkitConfig)
defer cleanup()

testCases := []struct {
name string
pull string
}{
{
name: "build with local image",
pull: "false",
},
{
name: "build with newest image",
pull: "true",
},
{
name: "build with buildkit default",
// buildkit default pulls from remote
pull: "default",
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").AssertOK()
base.Cmd("image", "prune", "--force", "--all").AssertOK()

base.Cmd("pull", oldImage).Run()
base.Cmd("tag", oldImage, newImage).Run()

dockerfile := fmt.Sprintf(`FROM %s`, newImage)
tmpDir := t.TempDir()
err := os.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644)
assert.NilError(t, err)

buildCtx, err := createBuildContext(dockerfile)
if err != nil {
t.Fatal(err)
}

buildCmd := []string{"build", buildCtx}
switch tc.pull {
case "false":
buildCmd = append(buildCmd, "--pull=false")
base.Cmd(buildCmd...).AssertErrContains(oldImageSha)
case "true":
buildCmd = append(buildCmd, "--pull=true")
base.Cmd(buildCmd...).AssertErrNotContains(oldImageSha)
case "default":
base.Cmd(buildCmd...).AssertErrNotContains(oldImageSha)
}
})
}
}

func useBuildkitConfig(t *testing.T, config string) (cleanup func()) {
buildkitConfigPath := "/etc/buildkit/buildkitd.toml"

currConfig, err := exec.Command("cat", buildkitConfigPath).Output()
assert.NilError(t, err)

os.WriteFile(buildkitConfigPath, []byte(config), 0644)
_, err = exec.Command("systemctl", "restart", "buildkit").Output()
assert.NilError(t, err)

return func() {
assert.NilError(t, os.WriteFile(buildkitConfigPath, currConfig, 0644))
_, err = exec.Command("systemctl", "restart", "buildkit").Output()
assert.NilError(t, err)
}
}
1 change: 1 addition & 0 deletions docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ Flags:
- :whale: `type=tar[,dest=path/to/output.tar]`: Raw tar ball
- :whale: `type=image,name=example.com/image,push=true`: Push to a registry (see [`buildctl build`](https://github.com/moby/buildkit/tree/v0.9.0#imageregistry) documentation)
- :whale: `--progress=(auto|plain|tty)`: Set type of progress output (auto, plain, tty). Use plain to show container output
- :whale: `--pull=(true|false)`: On true, always attempt to pull latest image version from remote. Default uses buildkit's default.
- :whale: `--secret`: Secret file to expose to the build: id=mysecret,src=/local/secret
- :whale: `--allow`: Allow extra privileged entitlement, e.g. network.host, security.insecure (It’s required to configure the buildkitd to enable the feature, see [`buildkitd.toml`](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md) documentation)
- :whale: `--ssh`: SSH agent socket or keys to expose to the build (format: `default|<id>[=<socket>|<key>[,<key>]]`)
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/types/builder_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ type BuilderBuildOptions struct {
BuildContext string
// NetworkMode mode for the build context
NetworkMode string
// Pull determines if we should try to pull latest image from remote. Default is buildkit's default.
Pull *bool
}

// BuilderPruneOptions specifies options for `nerdctl builder prune`.
Expand Down
9 changes: 9 additions & 0 deletions pkg/cmd/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,15 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
buildctlArgs = append(buildctlArgs, "--no-cache")
}

if options.Pull != nil {
switch *options.Pull {
case true:
buildctlArgs = append(buildctlArgs, "--opt=image-resolve-mode=pull")
case false:
buildctlArgs = append(buildctlArgs, "--opt=image-resolve-mode=local")
}
}

for _, s := range strutil.DedupeStrSlice(options.Secret) {
buildctlArgs = append(buildctlArgs, "--secret="+s)
}
Expand Down
17 changes: 17 additions & 0 deletions pkg/testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,14 @@ func (c *Cmd) AssertOutContains(s string) {
c.Assert(expected)
}

func (c *Cmd) AssertErrContains(s string) {
c.Base.T.Helper()
expected := icmd.Expected{
Err: s,
}
c.Assert(expected)
}

func (c *Cmd) AssertCombinedOutContains(s string) {
c.Base.T.Helper()
res := c.Run()
Expand Down Expand Up @@ -430,6 +438,15 @@ func (c *Cmd) AssertOutNotContains(s string) {
})
}

func (c *Cmd) AssertErrNotContains(s string) {
c.AssertOutWithFunc(func(stderr string) error {
if strings.Contains(stderr, s) {
return fmt.Errorf("expected stdout to not contain %q", s)
}
return nil
})
}

func (c *Cmd) AssertOutExactly(s string) {
c.Base.T.Helper()
fn := func(stdout string) error {
Expand Down

0 comments on commit bb9e7f0

Please sign in to comment.