From b1f4cadf0e6094713216bc43d84ccbae63cd6d8c Mon Sep 17 00:00:00 2001 From: Jon Johnson Date: Mon, 5 Feb 2024 22:26:49 -0800 Subject: [PATCH] Add --rm flag (and options) to Build This instructs melange to clean up after itself. Currently, this only applies to the guest container image, but we can expand the scope over time. Signed-off-by: Jon Johnson --- docs/md/melange_build.md | 1 + pkg/build/build.go | 15 ++++++++++++++ pkg/cli/build.go | 3 +++ pkg/container/bubblewrap_runner.go | 4 ++++ pkg/container/docker_runner.go | 33 +++++++++++++++++++++++++++--- pkg/container/kubernetes_runner.go | 8 ++++++++ pkg/container/runner.go | 1 + 7 files changed, 62 insertions(+), 3 deletions(-) diff --git a/docs/md/melange_build.md b/docs/md/melange_build.md index a4b89f1a9..2f305d7f9 100644 --- a/docs/md/melange_build.md +++ b/docs/md/melange_build.md @@ -55,6 +55,7 @@ melange build [flags] --overlay-binsh string use specified file as /bin/sh overlay in build environment --pipeline-dir string directory used to extend defined built-in pipelines -r, --repository-append strings path to extra repositories to include in the build environment + --rm clean up intermediate artifacts (e.g. container images) --runner string which runner to use to enable running commands, default is based on your platform. Options are ["bubblewrap" "docker" "lima" "kubernetes"] --signing-key string key to use for signing --source-dir string directory used for included sources diff --git a/pkg/build/build.go b/pkg/build/build.go index 959074d94..334e3a832 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -86,6 +86,7 @@ type Build struct { containerConfig *container.Config Debug bool DebugRunner bool + Remove bool LogPolicy []string FailOnLintWarning bool DefaultCPU string @@ -220,6 +221,11 @@ func New(ctx context.Context, opts ...Option) (*Build, error) { } func (b *Build) Close() error { + if b.Remove { + if err := b.Runner.OCIImageLoader().RemoveImage(context.TODO(), b.containerConfig.ImgRef); err != nil { + return err + } + } return b.Runner.Close() } @@ -499,6 +505,15 @@ func WithDebugRunner(debug bool) Option { } } +// WithRemove indicates whether the the build will clean up after itself. +// This includes deleting any intermediate artifacts like container images. +func WithRemove(remove bool) Option { + return func(b *Build) error { + b.Remove = remove + return nil + } +} + // WithLogPolicy sets the logging policy to use during builds. func WithLogPolicy(policy []string) Option { return func(b *Build) error { diff --git a/pkg/cli/build.go b/pkg/cli/build.go index e57fbc89d..30ba42b94 100644 --- a/pkg/cli/build.go +++ b/pkg/cli/build.go @@ -61,6 +61,7 @@ func Build() *cobra.Command { var createBuildLog bool var debug bool var debugRunner bool + var remove bool var runner string var failOnLintWarning bool var cpu, memory string @@ -107,6 +108,7 @@ func Build() *cobra.Command { build.WithCreateBuildLog(createBuildLog), build.WithDebug(debug), build.WithDebugRunner(debugRunner), + build.WithRemove(remove), build.WithLogPolicy(logPolicy), build.WithRunner(runner), build.WithFailOnLintWarning(failOnLintWarning), @@ -160,6 +162,7 @@ func Build() *cobra.Command { cmd.Flags().BoolVar(&createBuildLog, "create-build-log", false, "creates a package.log file containing a list of packages that were built by the command") cmd.Flags().BoolVar(&debug, "debug", false, "enables debug logging of build pipelines") cmd.Flags().BoolVar(&debugRunner, "debug-runner", false, "when enabled, the builder pod will persist after the build succeeds or fails") + cmd.Flags().BoolVar(&remove, "rm", false, "clean up intermediate artifacts (e.g. container images)") cmd.Flags().BoolVar(&failOnLintWarning, "fail-on-lint-warning", false, "turns linter warnings into failures") cmd.Flags().StringVar(&cpu, "cpu", "", "default CPU resources to use for builds") cmd.Flags().StringVar(&memory, "memory", "", "default memory resources to use for builds") diff --git a/pkg/container/bubblewrap_runner.go b/pkg/container/bubblewrap_runner.go index 8262c81e1..1f00e8dec 100644 --- a/pkg/container/bubblewrap_runner.go +++ b/pkg/container/bubblewrap_runner.go @@ -191,3 +191,7 @@ func (b bubblewrapOCILoader) LoadImage(ctx context.Context, layer v1.Layer, arch } return guestDir, nil } + +func (b bubblewrapOCILoader) RemoveImage(ctx context.Context, ref string) error { + return os.RemoveAll(ref) +} diff --git a/pkg/container/docker_runner.go b/pkg/container/docker_runner.go index e94df740d..bfcc140da 100644 --- a/pkg/container/docker_runner.go +++ b/pkg/container/docker_runner.go @@ -160,7 +160,9 @@ func (dk *docker) TestUsability(ctx context.Context) bool { // OCIImageLoader create a loader to load an OCI image into the docker daemon. func (dk *docker) OCIImageLoader() Loader { - return &dockerLoader{} + return &dockerLoader{ + cli: dk.cli, + } } // TempDir returns the base for temporary directory. For docker @@ -241,9 +243,11 @@ func (dk *docker) WorkspaceTar(ctx context.Context, cfg *Config) (io.ReadCloser, return nil, nil } -type dockerLoader struct{} +type dockerLoader struct { + cli *client.Client +} -func (d dockerLoader) LoadImage(ctx context.Context, layer v1.Layer, arch apko_types.Architecture, bc *apko_build.Context) (string, error) { +func (d *dockerLoader) LoadImage(ctx context.Context, layer v1.Layer, arch apko_types.Architecture, bc *apko_build.Context) (string, error) { ctx, span := otel.Tracer("melange").Start(ctx, "docker.LoadImage") defer span.End() @@ -263,3 +267,26 @@ func (d dockerLoader) LoadImage(ctx context.Context, layer v1.Layer, arch apko_t } return ref.String(), nil } + +func (d *dockerLoader) RemoveImage(ctx context.Context, ref string) error { + log := clog.FromContext(ctx) + log.Infof("deleting image %q", ref) + resps, err := d.cli.ImageRemove(ctx, ref, types.ImageRemoveOptions{ + Force: true, + PruneChildren: true, + }) + if err != nil { + return err + } + + for _, resp := range resps { + if resp.Untagged != "" { + log.Infof("untagged %q", resp.Untagged) + } + if resp.Deleted != "" { + log.Infof("deleted %q", resp.Deleted) + } + } + + return nil +} diff --git a/pkg/container/kubernetes_runner.go b/pkg/container/kubernetes_runner.go index 117a639ad..4c9856cba 100644 --- a/pkg/container/kubernetes_runner.go +++ b/pkg/container/kubernetes_runner.go @@ -680,6 +680,14 @@ func (k *k8sLoader) LoadImage(ctx context.Context, layer ggcrv1.Layer, arch apko return ref.String(), nil } +func (k *k8sLoader) RemoveImage(ctx context.Context, str string) error { + ref, err := name.ParseReference(str) + if err != nil { + return err + } + return remote.Delete(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithContext(ctx)) +} + type k8sTarFetcher struct { client kubernetes.Interface restconfig *rest.Config diff --git a/pkg/container/runner.go b/pkg/container/runner.go index 96dd0a373..e0c58a03e 100644 --- a/pkg/container/runner.go +++ b/pkg/container/runner.go @@ -45,6 +45,7 @@ type Runner interface { type Loader interface { LoadImage(ctx context.Context, layer v1.Layer, arch apko_types.Architecture, bc *apko_build.Context) (ref string, err error) + RemoveImage(ctx context.Context, ref string) error } // GetRunner returns the requested runner implementation.