pkg/manifests.List: add SetSubject()/Subject()

Add methods for reading and writing the index-level "subject" field.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai 2024-01-25 16:27:48 -05:00
parent ba247016fa
commit 0f7852ef9b
3 changed files with 92 additions and 0 deletions

View File

@ -0,0 +1,29 @@
package internal
import (
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// DeepCopyDescriptor copies a Descriptor, deeply copying its contents
func DeepCopyDescriptor(original *v1.Descriptor) *v1.Descriptor {
tmp := *original
if original.URLs != nil {
tmp.URLs = slices.Clone(original.URLs)
}
if original.Annotations != nil {
tmp.Annotations = maps.Clone(original.Annotations)
}
if original.Data != nil {
tmp.Data = slices.Clone(original.Data)
}
if original.Platform != nil {
tmpPlatform := *original.Platform
if original.Platform.OSFeatures != nil {
tmpPlatform.OSFeatures = slices.Clone(original.Platform.OSFeatures)
}
tmp.Platform = &tmpPlatform
}
return &tmp
}

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/containers/common/internal"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
imgspec "github.com/opencontainers/image-spec/specs-go" imgspec "github.com/opencontainers/image-spec/specs-go"
@ -39,6 +40,8 @@ type List interface {
MediaType(instanceDigest digest.Digest) (string, error) MediaType(instanceDigest digest.Digest) (string, error)
SetArtifactType(instanceDigest digest.Digest, artifactType string) error SetArtifactType(instanceDigest digest.Digest, artifactType string) error
ArtifactType(instanceDigest digest.Digest) (string, error) ArtifactType(instanceDigest digest.Digest) (string, error)
SetSubject(subject *v1.Descriptor) error
Subject() (*v1.Descriptor, error)
Serialize(mimeType string) ([]byte, error) Serialize(mimeType string) ([]byte, error)
Instances() []digest.Digest Instances() []digest.Digest
OCIv1() *v1.Index OCIv1() *v1.Index
@ -488,6 +491,26 @@ func (l *list) ArtifactType(instanceDigest digest.Digest) (string, error) {
return oci.ArtifactType, nil return oci.ArtifactType, nil
} }
// SetSubject sets the image index's subject.
// The field is specific to the OCI image index format, and is not present in Docker manifest lists.
func (l *list) SetSubject(subject *v1.Descriptor) error {
if subject != nil {
subject = internal.DeepCopyDescriptor(subject)
}
l.oci.Subject = subject
return nil
}
// Subject retrieves the subject which might have been set on the image index.
// The field is specific to the OCI image index format, and is not present in Docker manifest lists.
func (l *list) Subject() (*v1.Descriptor, error) {
s := l.oci.Subject
if s != nil {
s = internal.DeepCopyDescriptor(s)
}
return s, nil
}
// FromBlob builds a list from an encoded manifest list or image index. // FromBlob builds a list from an encoded manifest list or image index.
func FromBlob(manifestBytes []byte) (List, error) { func FromBlob(manifestBytes []byte) (List, error) {
manifestType := manifest.GuessMIMEType(manifestBytes) manifestType := manifest.GuessMIMEType(manifestBytes)

View File

@ -462,3 +462,43 @@ func TestPlatform(t *testing.T) {
assert.NoError(t, list.SetOSFeatures(instanceDigest, []string{})) assert.NoError(t, list.SetOSFeatures(instanceDigest, []string{}))
assert.Nil(t, list.OCIv1().Manifests[0].Platform, "expected platform to be nil") assert.Nil(t, list.OCIv1().Manifests[0].Platform, "expected platform to be nil")
} }
func TestSubject(t *testing.T) {
bytes, err := os.ReadFile(ociFixture)
if err != nil {
t.Fatalf("error loading blob: %v", err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing blob: %v", err)
}
for _, wrote := range []*v1.Descriptor{
nil,
{},
{
MediaType: v1.MediaTypeImageManifest,
Digest: expectedInstance,
Size: 1234,
},
} {
err := list.SetSubject(wrote)
if err != nil {
t.Fatalf("error setting subject: %v", err)
}
b, err := list.Serialize("")
if err != nil {
t.Fatalf("error serializing list: %v", err)
}
list, err = FromBlob(b)
if err != nil {
t.Fatalf("error parsing list: %v", err)
}
read, err := list.Subject()
if err != nil {
t.Fatalf("error retrieving subject: %v", err)
}
if !reflect.DeepEqual(read, wrote) {
t.Fatalf("expected subject %v, got %v", wrote, read)
}
}
}