Skip to content

Commit 1f5613a

Browse files
authored
feat(host-cleanup): clean up dangling and regular images, unused volumes, stopped containers, build cache (#6687)
Signed-off-by: Alexandr Zaytsev <alexandr.zaytsev@flant.com>
1 parent 8c9d698 commit 1f5613a

26 files changed

+1204
-434
lines changed

Diff for: pkg/build/import_server/rsync_server.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/werf/logboek"
1616
"github.com/werf/werf/v2/pkg/config"
1717
"github.com/werf/werf/v2/pkg/docker"
18+
"github.com/werf/werf/v2/pkg/image"
1819
"github.com/werf/werf/v2/pkg/stapel"
1920
)
2021

@@ -33,7 +34,7 @@ func RunRsyncServer(ctx context.Context, dockerImageName, tmpDir string) (*Rsync
3334

3435
srv := &RsyncServer{
3536
Port: rsyncServerPort,
36-
DockerContainerName: fmt.Sprintf("import-server-%s", uuid.New().String()),
37+
DockerContainerName: fmt.Sprintf("%s%s", image.ImportServerContainerNamePrefix, uuid.New().String()),
3738
AuthUser: fmt.Sprintf("werf-%s", generateSecureRandomString(4)),
3839
AuthPassword: generateSecureRandomString(16),
3940
}

Diff for: pkg/buildah/common.go

+10
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ type CommitOpts struct {
100100
Image string
101101
}
102102

103+
type PruneImagesOptions struct {
104+
CommonOpts
105+
}
106+
107+
type PruneImagesReport struct {
108+
ItemsDeleted []string
109+
SpaceReclaimed uint64
110+
}
111+
103112
type ConfigOpts struct {
104113
CommonOpts
105114

@@ -184,6 +193,7 @@ type Buildah interface {
184193
Add(ctx context.Context, container string, src []string, dst string, opts AddOpts) error
185194
Images(ctx context.Context, opts ImagesOptions) (image.ImagesList, error)
186195
Containers(ctx context.Context, opts ContainersOptions) (image.ContainerList, error)
196+
PruneImages(ctx context.Context, opts PruneImagesOptions) (PruneImagesReport, error)
187197
}
188198

189199
type Mode string

Diff for: pkg/buildah/native_linux.go

+41-8
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,32 @@ func (b *NativeBuildah) Rmi(ctx context.Context, ref string, opts RmiOpts) error
592592
return errors.Join(rmiErrors...)
593593
}
594594

595+
// PruneImages removes dangling images but not touches containers.
596+
func (b *NativeBuildah) PruneImages(ctx context.Context, opts PruneImagesOptions) (PruneImagesReport, error) {
597+
sysCtx, err := b.getSystemContext(opts.TargetPlatform)
598+
if err != nil {
599+
return PruneImagesReport{}, err
600+
}
601+
602+
runtime, err := b.getRuntime(sysCtx)
603+
if err != nil {
604+
return PruneImagesReport{}, err
605+
}
606+
607+
imageReports, rmiErrors := runtime.RemoveImages(ctx, []string{}, &libimage.RemoveImagesOptions{
608+
Force: false, // not remove containers
609+
Filters: []string{"dangling=true"},
610+
WithSize: true,
611+
NoPrune: false,
612+
})
613+
614+
if len(rmiErrors) > 0 {
615+
return PruneImagesReport{}, errors.Join(rmiErrors...)
616+
}
617+
618+
return mapImageReportsToPruneImagesReport(imageReports), nil
619+
}
620+
595621
func (b *NativeBuildah) Commit(ctx context.Context, container string, opts CommitOpts) (string, error) {
596622
builder, err := b.openContainerBuilder(ctx, container)
597623
if err != nil {
@@ -905,14 +931,6 @@ func (b *NativeBuildah) Images(ctx context.Context, opts ImagesOptions) (image.I
905931
Labels: labels,
906932
Created: img.Created(),
907933
Size: size,
908-
// Current buildah api does not provide a way to calculate SharedSize per image.
909-
// We can get aggregate result only via
910-
// https://pkg.go.dev/github.com/containers/common/libimage@v0.58.1#Runtime.DiskUsage
911-
//
912-
// IMPORTANT NOTES
913-
// We assume that all handled images are unshared ones.
914-
// We set constant `-1` to keep compatibility with docker which uses -1 to indicate unshared images.
915-
SharedSize: -1,
916934
}
917935
}
918936

@@ -1280,3 +1298,18 @@ func getSSHAgentSock(sock string) []string {
12801298
return []string{}
12811299
}
12821300
}
1301+
1302+
func mapImageReportsToPruneImagesReport(reports []*libimage.RemoveImageReport) PruneImagesReport {
1303+
report := PruneImagesReport{
1304+
ItemsDeleted: make([]string, 0, len(reports)),
1305+
}
1306+
1307+
for _, imageReport := range reports {
1308+
if imageReport.Removed {
1309+
report.ItemsDeleted = append(report.ItemsDeleted, imageReport.ID)
1310+
report.SpaceReclaimed += uint64(imageReport.Size)
1311+
}
1312+
}
1313+
1314+
return report
1315+
}

Diff for: pkg/container_backend/buildah_backend.go

+21
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/werf/werf/v2/pkg/buildah"
2828
"github.com/werf/werf/v2/pkg/buildah/thirdparty"
2929
"github.com/werf/werf/v2/pkg/container_backend/info"
30+
"github.com/werf/werf/v2/pkg/container_backend/prune"
3031
"github.com/werf/werf/v2/pkg/image"
3132
"github.com/werf/werf/v2/pkg/path_matcher"
3233
)
@@ -1099,3 +1100,23 @@ func (backend *BuildahBackend) PostManifest(ctx context.Context, ref string, opt
10991100
}
11001101

11011102
func (backend *BuildahBackend) ClaimTargetPlatforms(ctx context.Context, targetPlatforms []string) {}
1103+
1104+
func (backend *BuildahBackend) PruneBuildCache(_ context.Context, _ prune.Options) (prune.Report, error) {
1105+
return prune.Report{}, ErrUnsupportedFeature
1106+
}
1107+
1108+
func (backend *BuildahBackend) PruneContainers(_ context.Context, _ prune.Options) (prune.Report, error) {
1109+
return prune.Report{}, ErrUnsupportedFeature
1110+
}
1111+
1112+
func (backend *BuildahBackend) PruneImages(ctx context.Context, options prune.Options) (prune.Report, error) {
1113+
report, err := backend.buildah.PruneImages(ctx, buildah.PruneImagesOptions{})
1114+
if err != nil {
1115+
return prune.Report{}, fmt.Errorf("unable to prune images: %w", err)
1116+
}
1117+
return prune.Report(report), nil
1118+
}
1119+
1120+
func (backend *BuildahBackend) PruneVolumes(_ context.Context, _ prune.Options) (prune.Report, error) {
1121+
return prune.Report{}, ErrUnsupportedFeature
1122+
}

Diff for: pkg/container_backend/docker_server_backend.go

+33-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/werf/common-go/pkg/util"
1818
"github.com/werf/logboek"
1919
"github.com/werf/werf/v2/pkg/container_backend/info"
20+
"github.com/werf/werf/v2/pkg/container_backend/prune"
2021
"github.com/werf/werf/v2/pkg/docker"
2122
"github.com/werf/werf/v2/pkg/image"
2223
"github.com/werf/werf/v2/pkg/ssh_agent"
@@ -337,8 +338,6 @@ func (backend *DockerServerBackend) Images(ctx context.Context, opts ImagesOptio
337338
Labels: img.Labels,
338339
Created: time.Unix(img.Created, 0),
339340
Size: img.Size,
340-
// SharedSize is not calculated by default. `-1` indicates that the value has not been set / calculated.
341-
SharedSize: img.SharedSize,
342341
}
343342
}
344343
return res, nil
@@ -382,3 +381,35 @@ func (backend *DockerServerBackend) Containers(ctx context.Context, opts Contain
382381
func (backend *DockerServerBackend) PostManifest(ctx context.Context, ref string, opts PostManifestOpts) error {
383382
return docker.CreateImage(ctx, ref, docker.CreateImageOptions{Labels: opts.Labels})
384383
}
384+
385+
func (backend *DockerServerBackend) PruneBuildCache(ctx context.Context, options prune.Options) (prune.Report, error) {
386+
report, err := docker.BuildCachePrune(ctx, docker.BuildCachePruneOptions(options))
387+
if err != nil {
388+
return prune.Report{}, fmt.Errorf("unable to prune docker build cache: %w", err)
389+
}
390+
return prune.Report(report), nil
391+
}
392+
393+
func (backend *DockerServerBackend) PruneContainers(ctx context.Context, options prune.Options) (prune.Report, error) {
394+
report, err := docker.ContainersPrune(ctx, docker.ContainersPruneOptions(options))
395+
if err != nil {
396+
return prune.Report{}, fmt.Errorf("unable to prune docker containers: %w", err)
397+
}
398+
return prune.Report(report), nil
399+
}
400+
401+
func (backend *DockerServerBackend) PruneImages(ctx context.Context, options prune.Options) (prune.Report, error) {
402+
report, err := docker.ImagesPrune(ctx, docker.ImagesPruneOptions(options))
403+
if err != nil {
404+
return prune.Report{}, err
405+
}
406+
return prune.Report(report), err
407+
}
408+
409+
func (backend *DockerServerBackend) PruneVolumes(ctx context.Context, options prune.Options) (prune.Report, error) {
410+
report, err := docker.VolumesPrune(ctx, docker.VolumesPruneOptions(options))
411+
if err != nil {
412+
return prune.Report{}, err
413+
}
414+
return prune.Report(report), err
415+
}

Diff for: pkg/container_backend/errors.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package container_backend
2+
3+
import "errors"
4+
5+
var ErrUnsupportedFeature = errors.New("unsupported feature")

Diff for: pkg/container_backend/interface.go

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/werf/common-go/pkg/util"
77
"github.com/werf/werf/v2/pkg/container_backend/info"
8+
"github.com/werf/werf/v2/pkg/container_backend/prune"
89
"github.com/werf/werf/v2/pkg/image"
910
)
1011

@@ -99,6 +100,15 @@ type ContainerBackend interface {
99100

100101
ClaimTargetPlatforms(ctx context.Context, targetPlatforms []string)
101102

103+
// PruneBuildCache removes all unused cache
104+
PruneBuildCache(ctx context.Context, options prune.Options) (prune.Report, error)
105+
// PruneContainers removes all stopped containers
106+
PruneContainers(ctx context.Context, options prune.Options) (prune.Report, error)
107+
// PruneImages removes all dangling images
108+
PruneImages(ctx context.Context, options prune.Options) (prune.Report, error)
109+
// PruneVolumes removes all anonymous volumes not used by at least one container
110+
PruneVolumes(ctx context.Context, options prune.Options) (prune.Report, error)
111+
102112
String() string
103113

104114
// TODO: Util method for cleanup, which possibly should be avoided in the future

Diff for: pkg/container_backend/perf_check_container_backend.go

+33
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/werf/logboek"
77
"github.com/werf/werf/v2/pkg/container_backend/info"
8+
"github.com/werf/werf/v2/pkg/container_backend/prune"
89
"github.com/werf/werf/v2/pkg/image"
910
)
1011

@@ -196,3 +197,35 @@ func (runtime *PerfCheckContainerBackend) ClaimTargetPlatforms(ctx context.Conte
196197
logboek.Context(ctx).Default().LogProcess("ContainerBackend.ClaimTargetPlatforms %v", targetPlatforms).
197198
Do(func() { runtime.ContainerBackend.ClaimTargetPlatforms(ctx, targetPlatforms) })
198199
}
200+
201+
func (runtime *PerfCheckContainerBackend) PruneBuildCache(ctx context.Context, options prune.Options) (report prune.Report, err error) {
202+
logboek.Context(ctx).Default().LogProcess("ContainerBackend.PruneBuildCache %v", options).
203+
Do(func() {
204+
report, err = runtime.ContainerBackend.PruneBuildCache(ctx, options)
205+
})
206+
return
207+
}
208+
209+
func (runtime *PerfCheckContainerBackend) PruneContainers(ctx context.Context, options prune.Options) (report prune.Report, err error) {
210+
logboek.Context(ctx).Default().LogProcess("ContainerBackend.PruneContainers %v", options).
211+
Do(func() {
212+
report, err = runtime.ContainerBackend.PruneContainers(ctx, options)
213+
})
214+
return
215+
}
216+
217+
func (runtime *PerfCheckContainerBackend) PruneImages(ctx context.Context, options prune.Options) (report prune.Report, err error) {
218+
logboek.Context(ctx).Default().LogProcess("ContainerBackend.PruneImages %v", options).
219+
Do(func() {
220+
report, err = runtime.ContainerBackend.PruneImages(ctx, options)
221+
})
222+
return
223+
}
224+
225+
func (runtime *PerfCheckContainerBackend) PruneVolumes(ctx context.Context, options prune.Options) (report prune.Report, err error) {
226+
logboek.Context(ctx).Default().LogProcess("ContainerBackend.PruneVolumes %v", options).
227+
Do(func() {
228+
report, err = runtime.ContainerBackend.PruneVolumes(ctx, options)
229+
})
230+
return
231+
}

Diff for: pkg/container_backend/prune/prune.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package prune
2+
3+
type Options struct{}
4+
5+
type Report struct {
6+
ItemsDeleted []string
7+
SpaceReclaimed uint64
8+
}

Diff for: pkg/docker/build_cache.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package docker
2+
3+
import (
4+
"context"
5+
6+
"github.com/docker/docker/api/types"
7+
8+
"github.com/werf/werf/v2/pkg/container_backend/prune"
9+
)
10+
11+
type (
12+
BuildCachePruneOptions prune.Options
13+
BuildCachePruneReport prune.Report
14+
)
15+
16+
func BuildCachePrune(ctx context.Context, _ BuildCachePruneOptions) (BuildCachePruneReport, error) {
17+
report, err := apiCli(ctx).BuildCachePrune(ctx, types.BuildCachePruneOptions{})
18+
if err != nil {
19+
return BuildCachePruneReport{}, err
20+
}
21+
return BuildCachePruneReport{
22+
ItemsDeleted: report.CachesDeleted,
23+
SpaceReclaimed: report.SpaceReclaimed,
24+
}, err
25+
}

Diff for: pkg/docker/container.go

+17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/docker/cli/cli/command"
77
"github.com/docker/cli/cli/command/container"
88
"github.com/docker/docker/api/types"
9+
"github.com/docker/docker/api/types/filters"
910
"github.com/docker/docker/client"
1011
"golang.org/x/net/context"
1112
)
@@ -45,6 +46,22 @@ func ContainerRemove(ctx context.Context, ref string, options types.ContainerRem
4546
return apiCli(ctx).ContainerRemove(ctx, ref, options)
4647
}
4748

49+
type (
50+
ContainersPruneOptions BuildCachePruneOptions
51+
ContainersPruneReport BuildCachePruneReport
52+
)
53+
54+
func ContainersPrune(ctx context.Context, _ ContainersPruneOptions) (ContainersPruneReport, error) {
55+
report, err := apiCli(ctx).ContainersPrune(ctx, filters.NewArgs())
56+
if err != nil {
57+
return ContainersPruneReport{}, err
58+
}
59+
return ContainersPruneReport{
60+
ItemsDeleted: report.ContainersDeleted,
61+
SpaceReclaimed: report.SpaceReclaimed,
62+
}, nil
63+
}
64+
4865
func doCliCreate(c command.Cli, args ...string) error {
4966
return prepareCliCmd(container.NewCreateCommand(c), args...).Execute()
5067
}

Diff for: pkg/docker/image.go

+22
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import (
1313
"github.com/docker/cli/cli/command/image"
1414
"github.com/docker/cli/cli/streams"
1515
"github.com/docker/docker/api/types"
16+
"github.com/docker/docker/api/types/filters"
17+
dockerImage "github.com/docker/docker/api/types/image"
1618
"github.com/docker/docker/client"
19+
"github.com/samber/lo"
1720
"github.com/spf13/cobra"
1821
"golang.org/x/net/context"
1922

@@ -65,6 +68,25 @@ func ImageInspect(ctx context.Context, ref string) (*types.ImageInspect, error)
6568
return &inspect, nil
6669
}
6770

71+
type (
72+
ImagesPruneOptions BuildCachePruneOptions
73+
ImagesPruneReport BuildCachePruneReport
74+
)
75+
76+
func ImagesPrune(ctx context.Context, _ ImagesPruneOptions) (ImagesPruneReport, error) {
77+
report, err := apiCli(ctx).ImagesPrune(ctx, filters.NewArgs())
78+
if err != nil {
79+
return ImagesPruneReport{}, err
80+
}
81+
itemsDeleted := lo.Map(report.ImagesDeleted, func(item dockerImage.DeleteResponse, _ int) string {
82+
return item.Deleted
83+
})
84+
return ImagesPruneReport{
85+
ItemsDeleted: itemsDeleted,
86+
SpaceReclaimed: report.SpaceReclaimed,
87+
}, err
88+
}
89+
6890
func doCliPull(c command.Cli, args ...string) error {
6991
return prepareCliCmd(image.NewPullCommand(c), args...).Execute()
7092
}

Diff for: pkg/docker/volume.go

+17
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
package docker
22

33
import (
4+
"github.com/docker/docker/api/types/filters"
45
"golang.org/x/net/context"
56
)
67

78
func VolumeRm(ctx context.Context, volumeName string, force bool) error {
89
return apiCli(ctx).VolumeRemove(ctx, volumeName, force)
910
}
11+
12+
type (
13+
VolumesPruneOptions BuildCachePruneOptions
14+
VolumesPruneReport BuildCachePruneReport
15+
)
16+
17+
func VolumesPrune(ctx context.Context, _ VolumesPruneOptions) (VolumesPruneReport, error) {
18+
report, err := apiCli(ctx).VolumesPrune(ctx, filters.NewArgs())
19+
if err != nil {
20+
return VolumesPruneReport{}, err
21+
}
22+
return VolumesPruneReport{
23+
ItemsDeleted: report.VolumesDeleted,
24+
SpaceReclaimed: report.SpaceReclaimed,
25+
}, err
26+
}

0 commit comments

Comments
 (0)