diff --git a/pkg/sif/testdata/TestWrite/SpareDescriptor.golden b/pkg/sif/testdata/TestWrite/SpareDescriptor.golden new file mode 100644 index 0000000..4581e89 Binary files /dev/null and b/pkg/sif/testdata/TestWrite/SpareDescriptor.golden differ diff --git a/pkg/sif/write.go b/pkg/sif/write.go index 71b2d6b..b6bbc12 100644 --- a/pkg/sif/write.go +++ b/pkg/sif/write.go @@ -201,8 +201,38 @@ func numDescriptorsForIndex(ii v1.ImageIndex) (int64, error) { return count + 1, nil } +// writeOpts accumulates write options. +type writeOpts struct { + spareDescriptors int64 +} + +// WriteOpt are used to specify write options. +type WriteOpt func(*writeOpts) error + +// OptWriteWithSpareDescriptorCapacity specifies that the SIF should be created with n spare +// descriptors. +func OptWriteWithSpareDescriptorCapacity(n int64) WriteOpt { + return func(wo *writeOpts) error { + wo.spareDescriptors = n + return nil + } +} + // Write constructs a SIF at path from an ImageIndex. -func Write(path string, ii v1.ImageIndex) error { +// +// By default, the SIF is created with the exact number of descriptors required to represent ii. To +// include spare descriptor capacity, consider using OptWriteWithSpareDescriptorCapacity. +func Write(path string, ii v1.ImageIndex, opts ...WriteOpt) error { + wo := writeOpts{ + spareDescriptors: 0, + } + + for _, opt := range opts { + if err := opt(&wo); err != nil { + return err + } + } + n, err := numDescriptorsForIndex(ii) if err != nil { return err @@ -210,7 +240,7 @@ func Write(path string, ii v1.ImageIndex) error { fi, err := sif.CreateContainerAtPath(path, sif.OptCreateDeterministic(), - sif.OptCreateWithDescriptorCapacity(n), + sif.OptCreateWithDescriptorCapacity(n+wo.spareDescriptors), ) if err != nil { return err diff --git a/pkg/sif/write_test.go b/pkg/sif/write_test.go index 7bcbf08..0f0fae2 100644 --- a/pkg/sif/write_test.go +++ b/pkg/sif/write_test.go @@ -22,6 +22,7 @@ func TestWrite(t *testing.T) { tests := []struct { name string ii v1.ImageIndex + opts []sif.WriteOpt }{ { name: "DockerManifest", @@ -35,12 +36,19 @@ func TestWrite(t *testing.T) { name: "ManyLayers", ii: corpus.ImageIndex(t, "many-layers"), }, + { + name: "SpareDescriptor", + ii: corpus.ImageIndex(t, "hello-world-docker-v2-manifest"), + opts: []sif.WriteOpt{ + sif.OptWriteWithSpareDescriptorCapacity(1), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { path := filepath.Join(t.TempDir(), "image.sif") - if err := sif.Write(path, tt.ii); err != nil { + if err := sif.Write(path, tt.ii, tt.opts...); err != nil { t.Fatal(err) }