From cd3697acfddd458604403908598d2f5b0782d49c Mon Sep 17 00:00:00 2001 From: David Trudgian Date: Fri, 27 Sep 2024 14:59:19 +0100 Subject: [PATCH] feat: AppendImage/Index with optional name.Reference Adds `f.AppendImage` and `f.AppendIndex`, which append a `v1.Image` or a `v1.ImageIndex` to the SIF, respectively. The `OptAppendReference` functional option can be used to provide a `name.Reference` that will be stored as an annotation against the new descriptor in the RootIndex, with the key `org.opencontainers.image.ref.name`, as is convention for associating references with items stored in an OCI layout. Fixes #83 Fixes #81 --- pkg/sif/update.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/pkg/sif/update.go b/pkg/sif/update.go index 5634a49..f3815f0 100644 --- a/pkg/sif/update.go +++ b/pkg/sif/update.go @@ -11,7 +11,9 @@ import ( "path/filepath" "slices" + "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/types" "github.com/sylabs/sif/v2/pkg/sif" ) @@ -330,3 +332,69 @@ func selectBlobsExcept(keep []v1.Hash) sif.DescriptorSelectorFunc { return false, nil } } + +// appendOpts accumulates append options. +type appendOpts struct { + tempDir string + ref name.Reference +} + +// AppendOpt are used to specify options to apply when appending to a SIF. +type AppendOpt func(*appendOpts) error + +// OptAppendTempDir sets the directory to use for temporary files. If not set, the +// directory returned by os.TempDir is used. +func OptAppendTempDir(d string) AppendOpt { + return func(c *appendOpts) error { + c.tempDir = d + return nil + } +} + +// OptAppendReference sets the reference to be set for the appended item in the +// RootIndex. The reference is added as an `org.opencontainers.image.ref.name` +// in the RootIndex. +func OptAppendReference(r name.Reference) AppendOpt { + return func(c *appendOpts) error { + c.ref = r + return nil + } +} + +// AppendImage appends an image to the SIF f, updating the RootIndex to +// reference it. +func (f *OCIFileImage) AppendImage(img v1.Image, opts ...AppendOpt) error { + return f.append(img, opts...) +} + +// AppendIndex appends an index to the SIF f, updating the RootIndex to +// reference it. +func (f *OCIFileImage) AppendIndex(ii v1.ImageIndex, opts ...AppendOpt) error { + return f.append(ii, opts...) +} + +func (f *OCIFileImage) append(add mutate.Appendable, opts ...AppendOpt) error { + ao := appendOpts{ + tempDir: os.TempDir(), + } + for _, opt := range opts { + if err := opt(&ao); err != nil { + return err + } + } + + ri, err := f.RootIndex() + if err != nil { + return err + } + + ia := mutate.IndexAddendum{Add: add} + if ao.ref != nil { + ia.Annotations = map[string]string{ + "org.opencontainers.image.ref.name": ao.ref.Name(), + } + } + ri = mutate.AppendManifests(ri, ia) + + return f.UpdateRootIndex(ri, OptUpdateTempDir(ao.tempDir)) +}