Skip to content

Commit

Permalink
Merge pull request #3316 from austinvazquez/add-builder-prune-all
Browse files Browse the repository at this point in the history
Add builder prune --all and --force flag support
  • Loading branch information
AkihiroSuda authored Aug 16, 2024
2 parents ccf6830 + f5d1d6d commit eb808f1
Show file tree
Hide file tree
Showing 19 changed files with 158 additions and 58 deletions.
82 changes: 68 additions & 14 deletions cmd/nerdctl/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import (
"os/exec"
"strings"

"github.com/docker/go-units"
"github.com/spf13/cobra"

"github.com/containerd/log"

"github.com/containerd/nerdctl/v2/pkg/buildkitutil"
"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/containerd/nerdctl/v2/pkg/cmd/builder"
)

func newBuilderCommand() *cobra.Command {
Expand Down Expand Up @@ -58,29 +58,83 @@ func newBuilderPruneCommand() *cobra.Command {
}

AddStringFlag(buildPruneCommand, "buildkit-host", nil, "", "BUILDKIT_HOST", "BuildKit address")

buildPruneCommand.Flags().BoolP("all", "a", false, "Remove all unused build cache, not just dangling ones")
buildPruneCommand.Flags().BoolP("force", "f", false, "Do not prompt for confirmation")
return buildPruneCommand
}

func builderPruneAction(cmd *cobra.Command, _ []string) error {
globalOptions, err := processRootCmdFlags(cmd)
options, err := processBuilderPruneOptions(cmd)
if err != nil {
return err
}
buildkitHost, err := getBuildkitHost(cmd, globalOptions.Namespace)

if !options.Force {
var (
confirm string
msg string
)

if options.All {
msg = "This will remove all build cache."
} else {
msg = "This will remove any dangling build cache."
}
msg += " Are you sure you want to continue? [y/N] "

fmt.Fprintf(cmd.OutOrStdout(), "WARNING! %s", msg)
fmt.Fscanf(cmd.InOrStdin(), "%s", &confirm)

if strings.ToLower(confirm) != "y" {
return nil
}
}

prunedObjects, err := builder.Prune(cmd.Context(), options)
if err != nil {
return err
}
buildctlBinary, err := buildkitutil.BuildctlBinary()

var totalReclaimedSpace int64

for _, prunedObject := range prunedObjects {
totalReclaimedSpace += prunedObject.Size
}

fmt.Fprintf(cmd.OutOrStdout(), "Total: %s\n", units.BytesSize(float64(totalReclaimedSpace)))

return nil
}

func processBuilderPruneOptions(cmd *cobra.Command) (types.BuilderPruneOptions, error) {
globalOptions, err := processRootCmdFlags(cmd)
if err != nil {
return types.BuilderPruneOptions{}, err
}

buildkitHost, err := getBuildkitHost(cmd, globalOptions.Namespace)
if err != nil {
return types.BuilderPruneOptions{}, err
}

all, err := cmd.Flags().GetBool("all")
if err != nil {
return err
return types.BuilderPruneOptions{}, err
}
buildctlArgs := buildkitutil.BuildctlBaseArgs(buildkitHost)
buildctlArgs = append(buildctlArgs, "prune")
log.L.Debugf("running %s %v", buildctlBinary, buildctlArgs)
buildctlCmd := exec.Command(buildctlBinary, buildctlArgs...)
buildctlCmd.Env = os.Environ()
buildctlCmd.Stdout = cmd.OutOrStdout()
return buildctlCmd.Run()

force, err := cmd.Flags().GetBool("force")
if err != nil {
return types.BuilderPruneOptions{}, err
}

return types.BuilderPruneOptions{
Stderr: cmd.OutOrStderr(),
GOptions: globalOptions,
BuildKitHost: buildkitHost,
All: all,
Force: force,
}, nil
}

func newBuilderDebugCommand() *cobra.Command {
Expand Down
38 changes: 19 additions & 19 deletions cmd/nerdctl/builder_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import (

func TestBuild(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand All @@ -57,8 +57,8 @@ CMD ["echo", "nerdctl-build-test-string"]

func TestBuildIsShareableForCompatiblePlatform(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand Down Expand Up @@ -87,8 +87,8 @@ CMD ["echo", "nerdctl-build-test-string"]
// This isn't currently supported by nerdctl with BuildKit OCI worker.
func TestBuildBaseImage(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()
imageName2 := imageName + "-2"
Expand Down Expand Up @@ -122,8 +122,8 @@ CMD ["cat", "/hello2"]
func TestBuildFromContainerd(t *testing.T) {
testutil.DockerIncompatible(t)
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()
imageName2 := imageName + "-2"
Expand Down Expand Up @@ -152,8 +152,8 @@ CMD ["cat", "/hello2"]

func TestBuildFromStdin(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand All @@ -166,8 +166,8 @@ CMD ["echo", "nerdctl-build-test-stdin"]

func TestBuildWithDockerfile(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand Down Expand Up @@ -198,8 +198,8 @@ CMD ["echo", "nerdctl-build-test-dockerfile"]

func TestBuildLocal(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
const testFileName = "nerdctl-build-test"
const testContent = "nerdctl"
outputDir := t.TempDir()
Expand Down Expand Up @@ -243,8 +243,8 @@ func createBuildContext(t *testing.T, dockerfile string) string {

func TestBuildWithBuildArg(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand Down Expand Up @@ -288,8 +288,8 @@ CMD echo $TEST_STRING

func TestBuildWithIIDFile(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand All @@ -312,8 +312,8 @@ CMD ["echo", "nerdctl-build-test-string"]

func TestBuildWithLabels(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)

dockerfile := fmt.Sprintf(`FROM %s
Expand All @@ -330,8 +330,8 @@ LABEL name=nerdctl-build-test-label

func TestBuildMultipleTags(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
img := testutil.Identifier(t)
imgWithNoTag, imgWithCustomTag := fmt.Sprintf("%s%d", img, 2), fmt.Sprintf("%s%d:hello", img, 3)
defer base.Cmd("rmi", img).AssertOK()
Expand All @@ -354,10 +354,10 @@ func TestBuildMultipleTags(t *testing.T) {
}

func TestBuildWithContainerfile(t *testing.T) {
testutil.RequiresBuild(t)
testutil.DockerIncompatible(t)
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand All @@ -375,8 +375,8 @@ CMD ["echo", "nerdctl-build-test-string"]

func TestBuildWithDockerFileAndContainerfile(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()
imageName := testutil.Identifier(t)
defer base.Cmd("rmi", imageName).Run()

Expand Down Expand Up @@ -404,8 +404,8 @@ CMD ["echo", "dockerfile"]

func TestBuildNoTag(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").AssertOK()
base.Cmd("image", "prune", "--force", "--all").AssertOK()

dockerfile := fmt.Sprintf(`FROM %s
Expand All @@ -420,8 +420,8 @@ CMD ["echo", "nerdctl-build-notag-string"]

func TestBuildContextDockerImageAlias(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").AssertOK()
base.Cmd("image", "prune", "--force", "--all").AssertOK()

dockerfile := `FROM myorg/myapp
Expand All @@ -435,8 +435,8 @@ CMD ["echo", "nerdctl-build-myorg/myapp"]`

func TestBuildContextWithCopyFromDir(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").AssertOK()
base.Cmd("image", "prune", "--force", "--all").AssertOK()

content := "hello_from_dir_2"
Expand Down Expand Up @@ -487,8 +487,8 @@ CMD ["cat", "/source-date-epoch"]

func TestBuildNetwork(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").AssertOK()

dockerfile := fmt.Sprintf(`FROM %s
RUN apk add --no-cache curl
Expand Down Expand Up @@ -540,14 +540,14 @@ func buildWithNamedBuilder(base *testutil.Base, builderName string, args ...stri

func TestBuildAttestation(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
builderName := testutil.Identifier(t)
if testutil.GetTarget() == testutil.Docker {
// create named builder for docker
defer base.Cmd("buildx", "rm", builderName).AssertOK()
base.Cmd("buildx", "create", "--name", builderName, "--bootstrap", "--use").AssertOK()
}
defer base.Cmd("builder", "prune").Run()

dockerfile := "FROM " + testutil.NginxAlpineImage
buildCtx := createBuildContext(t, dockerfile)
Expand Down
36 changes: 35 additions & 1 deletion cmd/nerdctl/builder_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,39 @@ import (
"github.com/containerd/nerdctl/v2/pkg/testutil"
)

func TestBuilderPrune(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)

base := testutil.NewBase(t)

dockerfile := fmt.Sprintf(`FROM %s
CMD ["echo", "nerdctl-test-builder-prune"]`, testutil.CommonImage)

buildCtx := createBuildContext(t, dockerfile)

testCases := []struct {
name string
commandArgs []string
}{
{
name: "TestBuilderPruneForce",
commandArgs: []string{"builder", "prune", "--force"},
},
{
name: "TestBuilderPruneForceAll",
commandArgs: []string{"builder", "prune", "--force", "--all"},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
base.Cmd("build", buildCtx).AssertOK()
base.Cmd(tc.commandArgs...).AssertOK()
})
}
}

func TestBuilderDebug(t *testing.T) {
testutil.DockerIncompatible(t)
base := testutil.NewBase(t)
Expand All @@ -49,6 +82,7 @@ func TestBuildWithPull(t *testing.T) {
t.Skipf("skipped because the test needs a custom buildkitd config")
}
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)

oldImage := testutil.BusyboxImage
oldImageSha := "141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47"
Expand Down Expand Up @@ -86,8 +120,8 @@ namespace = "%s"`, testutil.Namespace)
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").AssertOK()
base.Cmd("image", "prune", "--force", "--all").AssertOK()

base.Cmd("pull", oldImage).Run()
Expand Down
2 changes: 1 addition & 1 deletion cmd/nerdctl/compose_build_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ services:
dockerfile := fmt.Sprintf(`FROM %s`, testutil.AlpineImage)

testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()

comp := testutil.NewComposeDir(t, dockerComposeYAML)
defer comp.CleanUp()
Expand Down
2 changes: 1 addition & 1 deletion cmd/nerdctl/compose_create_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ services:
dockerfile := fmt.Sprintf(`FROM %s`, testutil.AlpineImage)

testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
base := testutil.NewBase(t)
defer base.Cmd("builder", "prune").Run()

comp := testutil.NewComposeDir(t, dockerComposeYAML)
defer comp.CleanUp()
Expand Down
2 changes: 1 addition & 1 deletion cmd/nerdctl/compose_run_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ func TestComposePushAndPullWithCosignVerify(t *testing.T) {
testutil.RequireExecutable(t, "cosign")
testutil.DockerIncompatible(t)
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
t.Parallel()

base := testutil.NewBase(t)
Expand All @@ -435,7 +436,6 @@ func TestComposePushAndPullWithCosignVerify(t *testing.T) {
reg := testregistry.NewWithNoAuth(base, 0, false)
t.Cleanup(func() {
keyPair.cleanup()
base.Cmd("builder", "prune").Run()
reg.Cleanup(nil)
})

Expand Down
Loading

0 comments on commit eb808f1

Please sign in to comment.