Skip to content

Commit eb808f1

Browse files
authored
Merge pull request #3316 from austinvazquez/add-builder-prune-all
Add builder prune --all and --force flag support
2 parents ccf6830 + f5d1d6d commit eb808f1

19 files changed

+158
-58
lines changed

cmd/nerdctl/builder.go

+68-14
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ import (
2222
"os/exec"
2323
"strings"
2424

25+
"github.com/docker/go-units"
2526
"github.com/spf13/cobra"
2627

27-
"github.com/containerd/log"
28-
29-
"github.com/containerd/nerdctl/v2/pkg/buildkitutil"
28+
"github.com/containerd/nerdctl/v2/pkg/api/types"
29+
"github.com/containerd/nerdctl/v2/pkg/cmd/builder"
3030
)
3131

3232
func newBuilderCommand() *cobra.Command {
@@ -58,29 +58,83 @@ func newBuilderPruneCommand() *cobra.Command {
5858
}
5959

6060
AddStringFlag(buildPruneCommand, "buildkit-host", nil, "", "BUILDKIT_HOST", "BuildKit address")
61+
62+
buildPruneCommand.Flags().BoolP("all", "a", false, "Remove all unused build cache, not just dangling ones")
63+
buildPruneCommand.Flags().BoolP("force", "f", false, "Do not prompt for confirmation")
6164
return buildPruneCommand
6265
}
6366

6467
func builderPruneAction(cmd *cobra.Command, _ []string) error {
65-
globalOptions, err := processRootCmdFlags(cmd)
68+
options, err := processBuilderPruneOptions(cmd)
6669
if err != nil {
6770
return err
6871
}
69-
buildkitHost, err := getBuildkitHost(cmd, globalOptions.Namespace)
72+
73+
if !options.Force {
74+
var (
75+
confirm string
76+
msg string
77+
)
78+
79+
if options.All {
80+
msg = "This will remove all build cache."
81+
} else {
82+
msg = "This will remove any dangling build cache."
83+
}
84+
msg += " Are you sure you want to continue? [y/N] "
85+
86+
fmt.Fprintf(cmd.OutOrStdout(), "WARNING! %s", msg)
87+
fmt.Fscanf(cmd.InOrStdin(), "%s", &confirm)
88+
89+
if strings.ToLower(confirm) != "y" {
90+
return nil
91+
}
92+
}
93+
94+
prunedObjects, err := builder.Prune(cmd.Context(), options)
7095
if err != nil {
7196
return err
7297
}
73-
buildctlBinary, err := buildkitutil.BuildctlBinary()
98+
99+
var totalReclaimedSpace int64
100+
101+
for _, prunedObject := range prunedObjects {
102+
totalReclaimedSpace += prunedObject.Size
103+
}
104+
105+
fmt.Fprintf(cmd.OutOrStdout(), "Total: %s\n", units.BytesSize(float64(totalReclaimedSpace)))
106+
107+
return nil
108+
}
109+
110+
func processBuilderPruneOptions(cmd *cobra.Command) (types.BuilderPruneOptions, error) {
111+
globalOptions, err := processRootCmdFlags(cmd)
112+
if err != nil {
113+
return types.BuilderPruneOptions{}, err
114+
}
115+
116+
buildkitHost, err := getBuildkitHost(cmd, globalOptions.Namespace)
117+
if err != nil {
118+
return types.BuilderPruneOptions{}, err
119+
}
120+
121+
all, err := cmd.Flags().GetBool("all")
74122
if err != nil {
75-
return err
123+
return types.BuilderPruneOptions{}, err
76124
}
77-
buildctlArgs := buildkitutil.BuildctlBaseArgs(buildkitHost)
78-
buildctlArgs = append(buildctlArgs, "prune")
79-
log.L.Debugf("running %s %v", buildctlBinary, buildctlArgs)
80-
buildctlCmd := exec.Command(buildctlBinary, buildctlArgs...)
81-
buildctlCmd.Env = os.Environ()
82-
buildctlCmd.Stdout = cmd.OutOrStdout()
83-
return buildctlCmd.Run()
125+
126+
force, err := cmd.Flags().GetBool("force")
127+
if err != nil {
128+
return types.BuilderPruneOptions{}, err
129+
}
130+
131+
return types.BuilderPruneOptions{
132+
Stderr: cmd.OutOrStderr(),
133+
GOptions: globalOptions,
134+
BuildKitHost: buildkitHost,
135+
All: all,
136+
Force: force,
137+
}, nil
84138
}
85139

86140
func newBuilderDebugCommand() *cobra.Command {

cmd/nerdctl/builder_build_test.go

+19-19
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import (
3232

3333
func TestBuild(t *testing.T) {
3434
testutil.RequiresBuild(t)
35+
testutil.RegisterBuildCacheCleanup(t)
3536
base := testutil.NewBase(t)
36-
defer base.Cmd("builder", "prune").Run()
3737
imageName := testutil.Identifier(t)
3838
defer base.Cmd("rmi", imageName).Run()
3939

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

5858
func TestBuildIsShareableForCompatiblePlatform(t *testing.T) {
5959
testutil.RequiresBuild(t)
60+
testutil.RegisterBuildCacheCleanup(t)
6061
base := testutil.NewBase(t)
61-
defer base.Cmd("builder", "prune").Run()
6262
imageName := testutil.Identifier(t)
6363
defer base.Cmd("rmi", imageName).Run()
6464

@@ -87,8 +87,8 @@ CMD ["echo", "nerdctl-build-test-string"]
8787
// This isn't currently supported by nerdctl with BuildKit OCI worker.
8888
func TestBuildBaseImage(t *testing.T) {
8989
testutil.RequiresBuild(t)
90+
testutil.RegisterBuildCacheCleanup(t)
9091
base := testutil.NewBase(t)
91-
defer base.Cmd("builder", "prune").Run()
9292
imageName := testutil.Identifier(t)
9393
defer base.Cmd("rmi", imageName).Run()
9494
imageName2 := imageName + "-2"
@@ -122,8 +122,8 @@ CMD ["cat", "/hello2"]
122122
func TestBuildFromContainerd(t *testing.T) {
123123
testutil.DockerIncompatible(t)
124124
testutil.RequiresBuild(t)
125+
testutil.RegisterBuildCacheCleanup(t)
125126
base := testutil.NewBase(t)
126-
defer base.Cmd("builder", "prune").Run()
127127
imageName := testutil.Identifier(t)
128128
defer base.Cmd("rmi", imageName).Run()
129129
imageName2 := imageName + "-2"
@@ -152,8 +152,8 @@ CMD ["cat", "/hello2"]
152152

153153
func TestBuildFromStdin(t *testing.T) {
154154
testutil.RequiresBuild(t)
155+
testutil.RegisterBuildCacheCleanup(t)
155156
base := testutil.NewBase(t)
156-
defer base.Cmd("builder", "prune").Run()
157157
imageName := testutil.Identifier(t)
158158
defer base.Cmd("rmi", imageName).Run()
159159

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

167167
func TestBuildWithDockerfile(t *testing.T) {
168168
testutil.RequiresBuild(t)
169+
testutil.RegisterBuildCacheCleanup(t)
169170
base := testutil.NewBase(t)
170-
defer base.Cmd("builder", "prune").Run()
171171
imageName := testutil.Identifier(t)
172172
defer base.Cmd("rmi", imageName).Run()
173173

@@ -198,8 +198,8 @@ CMD ["echo", "nerdctl-build-test-dockerfile"]
198198

199199
func TestBuildLocal(t *testing.T) {
200200
testutil.RequiresBuild(t)
201+
testutil.RegisterBuildCacheCleanup(t)
201202
base := testutil.NewBase(t)
202-
defer base.Cmd("builder", "prune").Run()
203203
const testFileName = "nerdctl-build-test"
204204
const testContent = "nerdctl"
205205
outputDir := t.TempDir()
@@ -243,8 +243,8 @@ func createBuildContext(t *testing.T, dockerfile string) string {
243243

244244
func TestBuildWithBuildArg(t *testing.T) {
245245
testutil.RequiresBuild(t)
246+
testutil.RegisterBuildCacheCleanup(t)
246247
base := testutil.NewBase(t)
247-
defer base.Cmd("builder", "prune").Run()
248248
imageName := testutil.Identifier(t)
249249
defer base.Cmd("rmi", imageName).Run()
250250

@@ -288,8 +288,8 @@ CMD echo $TEST_STRING
288288

289289
func TestBuildWithIIDFile(t *testing.T) {
290290
testutil.RequiresBuild(t)
291+
testutil.RegisterBuildCacheCleanup(t)
291292
base := testutil.NewBase(t)
292-
defer base.Cmd("builder", "prune").Run()
293293
imageName := testutil.Identifier(t)
294294
defer base.Cmd("rmi", imageName).Run()
295295

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

313313
func TestBuildWithLabels(t *testing.T) {
314314
testutil.RequiresBuild(t)
315+
testutil.RegisterBuildCacheCleanup(t)
315316
base := testutil.NewBase(t)
316-
defer base.Cmd("builder", "prune").Run()
317317
imageName := testutil.Identifier(t)
318318

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

331331
func TestBuildMultipleTags(t *testing.T) {
332332
testutil.RequiresBuild(t)
333+
testutil.RegisterBuildCacheCleanup(t)
333334
base := testutil.NewBase(t)
334-
defer base.Cmd("builder", "prune").Run()
335335
img := testutil.Identifier(t)
336336
imgWithNoTag, imgWithCustomTag := fmt.Sprintf("%s%d", img, 2), fmt.Sprintf("%s%d:hello", img, 3)
337337
defer base.Cmd("rmi", img).AssertOK()
@@ -354,10 +354,10 @@ func TestBuildMultipleTags(t *testing.T) {
354354
}
355355

356356
func TestBuildWithContainerfile(t *testing.T) {
357-
testutil.RequiresBuild(t)
358357
testutil.DockerIncompatible(t)
358+
testutil.RequiresBuild(t)
359+
testutil.RegisterBuildCacheCleanup(t)
359360
base := testutil.NewBase(t)
360-
defer base.Cmd("builder", "prune").Run()
361361
imageName := testutil.Identifier(t)
362362
defer base.Cmd("rmi", imageName).Run()
363363

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

376376
func TestBuildWithDockerFileAndContainerfile(t *testing.T) {
377377
testutil.RequiresBuild(t)
378+
testutil.RegisterBuildCacheCleanup(t)
378379
base := testutil.NewBase(t)
379-
defer base.Cmd("builder", "prune").Run()
380380
imageName := testutil.Identifier(t)
381381
defer base.Cmd("rmi", imageName).Run()
382382

@@ -404,8 +404,8 @@ CMD ["echo", "dockerfile"]
404404

405405
func TestBuildNoTag(t *testing.T) {
406406
testutil.RequiresBuild(t)
407+
testutil.RegisterBuildCacheCleanup(t)
407408
base := testutil.NewBase(t)
408-
defer base.Cmd("builder", "prune").AssertOK()
409409
base.Cmd("image", "prune", "--force", "--all").AssertOK()
410410

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

421421
func TestBuildContextDockerImageAlias(t *testing.T) {
422422
testutil.RequiresBuild(t)
423+
testutil.RegisterBuildCacheCleanup(t)
423424
base := testutil.NewBase(t)
424-
defer base.Cmd("builder", "prune").AssertOK()
425425
base.Cmd("image", "prune", "--force", "--all").AssertOK()
426426

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

436436
func TestBuildContextWithCopyFromDir(t *testing.T) {
437437
testutil.RequiresBuild(t)
438+
testutil.RegisterBuildCacheCleanup(t)
438439
base := testutil.NewBase(t)
439-
defer base.Cmd("builder", "prune").AssertOK()
440440
base.Cmd("image", "prune", "--force", "--all").AssertOK()
441441

442442
content := "hello_from_dir_2"
@@ -487,8 +487,8 @@ CMD ["cat", "/source-date-epoch"]
487487

488488
func TestBuildNetwork(t *testing.T) {
489489
testutil.RequiresBuild(t)
490+
testutil.RegisterBuildCacheCleanup(t)
490491
base := testutil.NewBase(t)
491-
defer base.Cmd("builder", "prune").AssertOK()
492492

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

541541
func TestBuildAttestation(t *testing.T) {
542542
testutil.RequiresBuild(t)
543+
testutil.RegisterBuildCacheCleanup(t)
543544
base := testutil.NewBase(t)
544545
builderName := testutil.Identifier(t)
545546
if testutil.GetTarget() == testutil.Docker {
546547
// create named builder for docker
547548
defer base.Cmd("buildx", "rm", builderName).AssertOK()
548549
base.Cmd("buildx", "create", "--name", builderName, "--bootstrap", "--use").AssertOK()
549550
}
550-
defer base.Cmd("builder", "prune").Run()
551551

552552
dockerfile := "FROM " + testutil.NginxAlpineImage
553553
buildCtx := createBuildContext(t, dockerfile)

cmd/nerdctl/builder_linux_test.go

+35-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,39 @@ import (
3030
"github.com/containerd/nerdctl/v2/pkg/testutil"
3131
)
3232

33+
func TestBuilderPrune(t *testing.T) {
34+
testutil.RequiresBuild(t)
35+
testutil.RegisterBuildCacheCleanup(t)
36+
37+
base := testutil.NewBase(t)
38+
39+
dockerfile := fmt.Sprintf(`FROM %s
40+
CMD ["echo", "nerdctl-test-builder-prune"]`, testutil.CommonImage)
41+
42+
buildCtx := createBuildContext(t, dockerfile)
43+
44+
testCases := []struct {
45+
name string
46+
commandArgs []string
47+
}{
48+
{
49+
name: "TestBuilderPruneForce",
50+
commandArgs: []string{"builder", "prune", "--force"},
51+
},
52+
{
53+
name: "TestBuilderPruneForceAll",
54+
commandArgs: []string{"builder", "prune", "--force", "--all"},
55+
},
56+
}
57+
58+
for _, tc := range testCases {
59+
t.Run(tc.name, func(t *testing.T) {
60+
base.Cmd("build", buildCtx).AssertOK()
61+
base.Cmd(tc.commandArgs...).AssertOK()
62+
})
63+
}
64+
}
65+
3366
func TestBuilderDebug(t *testing.T) {
3467
testutil.DockerIncompatible(t)
3568
base := testutil.NewBase(t)
@@ -49,6 +82,7 @@ func TestBuildWithPull(t *testing.T) {
4982
t.Skipf("skipped because the test needs a custom buildkitd config")
5083
}
5184
testutil.RequiresBuild(t)
85+
testutil.RegisterBuildCacheCleanup(t)
5286

5387
oldImage := testutil.BusyboxImage
5488
oldImageSha := "141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47"
@@ -86,8 +120,8 @@ namespace = "%s"`, testutil.Namespace)
86120
for _, tc := range testCases {
87121
tc := tc
88122
t.Run(tc.name, func(t *testing.T) {
123+
testutil.RegisterBuildCacheCleanup(t)
89124
base := testutil.NewBase(t)
90-
defer base.Cmd("builder", "prune").AssertOK()
91125
base.Cmd("image", "prune", "--force", "--all").AssertOK()
92126

93127
base.Cmd("pull", oldImage).Run()

cmd/nerdctl/compose_build_linux_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ services:
4646
dockerfile := fmt.Sprintf(`FROM %s`, testutil.AlpineImage)
4747

4848
testutil.RequiresBuild(t)
49+
testutil.RegisterBuildCacheCleanup(t)
4950
base := testutil.NewBase(t)
50-
defer base.Cmd("builder", "prune").Run()
5151

5252
comp := testutil.NewComposeDir(t, dockerComposeYAML)
5353
defer comp.CleanUp()

cmd/nerdctl/compose_create_linux_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ services:
116116
dockerfile := fmt.Sprintf(`FROM %s`, testutil.AlpineImage)
117117

118118
testutil.RequiresBuild(t)
119+
testutil.RegisterBuildCacheCleanup(t)
119120
base := testutil.NewBase(t)
120-
defer base.Cmd("builder", "prune").Run()
121121

122122
comp := testutil.NewComposeDir(t, dockerComposeYAML)
123123
defer comp.CleanUp()

cmd/nerdctl/compose_run_linux_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ func TestComposePushAndPullWithCosignVerify(t *testing.T) {
426426
testutil.RequireExecutable(t, "cosign")
427427
testutil.DockerIncompatible(t)
428428
testutil.RequiresBuild(t)
429+
testutil.RegisterBuildCacheCleanup(t)
429430
t.Parallel()
430431

431432
base := testutil.NewBase(t)
@@ -435,7 +436,6 @@ func TestComposePushAndPullWithCosignVerify(t *testing.T) {
435436
reg := testregistry.NewWithNoAuth(base, 0, false)
436437
t.Cleanup(func() {
437438
keyPair.cleanup()
438-
base.Cmd("builder", "prune").Run()
439439
reg.Cleanup(nil)
440440
})
441441

0 commit comments

Comments
 (0)