diff --git a/pkg/sif/create.go b/pkg/sif/create.go index 0e1f910..22357bb 100644 --- a/pkg/sif/create.go +++ b/pkg/sif/create.go @@ -8,6 +8,7 @@ package sif import ( + "encoding" "encoding/binary" "errors" "fmt" @@ -676,3 +677,41 @@ func (f *FileImage) SetPrimPart(id uint32, opts ...SetOpt) error { return nil } + +// SetMetadata writes the mutated descriptors and returns an error if any. +func (f *FileImage) SetMetadata(id uint32, md encoding.BinaryMarshaler, opts ...SetOpt) error { + so := setOpts{} + + if !f.isDeterministic() { + so.t = time.Now() + } + + for _, opt := range opts { + if err := opt(&so); err != nil { + return fmt.Errorf("%w", err) + } + } + + rd, err := f.getDescriptor(WithID(id)) + if err != nil { + return fmt.Errorf("%w", err) + } + + if err := rd.setExtra(md); err != nil { + return fmt.Errorf("%w", err) + } + + rd.ModifiedAt = so.t.Unix() + + if err := f.writeDescriptors(); err != nil { + return fmt.Errorf("%w", err) + } + + f.h.ModifiedAt = so.t.Unix() + + if err := f.writeHeader(); err != nil { + return fmt.Errorf("%w", err) + } + + return nil +} diff --git a/pkg/sif/create_test.go b/pkg/sif/create_test.go index 917ecee..cca8044 100644 --- a/pkg/sif/create_test.go +++ b/pkg/sif/create_test.go @@ -649,3 +649,84 @@ func TestSetPrimPart(t *testing.T) { }) } } + +func TestSetMetadata(t *testing.T) { + tests := []struct { + name string + createOpts []CreateOpt + id uint32 + opts []SetOpt + wantErr error + }{ + { + name: "Deterministic", + createOpts: []CreateOpt{ + OptCreateWithID("de170c43-36ab-44a8-bca9-1ea1a070a274"), + OptCreateWithDescriptors( + getDescriptorInput(t, DataOCIBlob, []byte{0xfa, 0xce}), + ), + OptCreateWithTime(time.Unix(946702800, 0)), + }, + id: 1, + opts: []SetOpt{ + OptSetDeterministic(), + }, + }, + { + name: "WithTime", + createOpts: []CreateOpt{ + OptCreateDeterministic(), + OptCreateWithDescriptors( + getDescriptorInput(t, DataOCIBlob, []byte{0xfa, 0xce}), + ), + }, + id: 1, + opts: []SetOpt{ + OptSetWithTime(time.Unix(946702800, 0)), + }, + }, + { + name: "One", + createOpts: []CreateOpt{ + OptCreateDeterministic(), + OptCreateWithDescriptors( + getDescriptorInput(t, DataOCIBlob, []byte{0xfa, 0xce}), + ), + }, + id: 1, + }, + { + name: "Two", + createOpts: []CreateOpt{ + OptCreateDeterministic(), + OptCreateWithDescriptors( + getDescriptorInput(t, DataOCIBlob, []byte{0xfa, 0xce}), + getDescriptorInput(t, DataOCIBlob, []byte{0xfe, 0xed}), + ), + }, + id: 2, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var b Buffer + + f, err := CreateContainer(&b, tt.createOpts...) + if err != nil { + t.Fatal(err) + } + + if got, want := f.SetMetadata(tt.id, newOCIBlobDigest(), tt.opts...), tt.wantErr; !errors.Is(got, want) { + t.Errorf("got error %v, want %v", got, want) + } + + if err := f.UnloadContainer(); err != nil { + t.Error(err) + } + + g := goldie.New(t, goldie.WithTestNameForDir(true)) + g.Assert(t, tt.name, b.Bytes()) + }) + } +} diff --git a/pkg/sif/descriptor.go b/pkg/sif/descriptor.go index 3b7a477..62b6de3 100644 --- a/pkg/sif/descriptor.go +++ b/pkg/sif/descriptor.go @@ -194,6 +194,12 @@ func (d rawDescriptor) isPartitionOfType(pt PartType) bool { return t == pt } +// isDeterministic returns true if the timestamps in d are set to +// deterministic values. +func (d rawDescriptor) isDeterministic() bool { + return (time.Unix(d.CreatedAt, 0).IsZero() && time.Unix(d.ModifiedAt, 0).IsZero()) +} + // Descriptor represents the SIF descriptor type. type Descriptor struct { r io.ReaderAt // Backing storage. diff --git a/pkg/sif/testdata/TestSetMetadata/Deterministic.golden b/pkg/sif/testdata/TestSetMetadata/Deterministic.golden new file mode 100644 index 0000000..aab5f44 Binary files /dev/null and b/pkg/sif/testdata/TestSetMetadata/Deterministic.golden differ diff --git a/pkg/sif/testdata/TestSetMetadata/ErrObjectNotFound.golden b/pkg/sif/testdata/TestSetMetadata/ErrObjectNotFound.golden new file mode 100644 index 0000000..01584e2 Binary files /dev/null and b/pkg/sif/testdata/TestSetMetadata/ErrObjectNotFound.golden differ diff --git a/pkg/sif/testdata/TestSetMetadata/One.golden b/pkg/sif/testdata/TestSetMetadata/One.golden new file mode 100644 index 0000000..33e0b15 Binary files /dev/null and b/pkg/sif/testdata/TestSetMetadata/One.golden differ diff --git a/pkg/sif/testdata/TestSetMetadata/Two.golden b/pkg/sif/testdata/TestSetMetadata/Two.golden new file mode 100644 index 0000000..1e26637 Binary files /dev/null and b/pkg/sif/testdata/TestSetMetadata/Two.golden differ diff --git a/pkg/sif/testdata/TestSetMetadata/WithTime.golden b/pkg/sif/testdata/TestSetMetadata/WithTime.golden new file mode 100644 index 0000000..58331df Binary files /dev/null and b/pkg/sif/testdata/TestSetMetadata/WithTime.golden differ