Parsing and storing of OCI Artifact Manifests

Does not yet make referrers link from the subject to the referrer

Signed-off-by: Bracken Dawson <abdawson@gmail.com>
This commit is contained in:
Bracken Dawson 2023-02-01 16:46:02 +00:00
parent 983358f8e2
commit 30015b3c6d
No known key found for this signature in database
GPG key ID: 7C6C7FA182101826
23 changed files with 543 additions and 25 deletions

2
go.mod
View file

@ -22,7 +22,7 @@ require (
github.com/mitchellh/mapstructure v1.1.2
github.com/ncw/swift v1.0.47
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/prometheus/client_golang v1.12.1 // indirect; updated to latest
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.6.1

4
go.sum
View file

@ -227,8 +227,8 @@ github.com/ncw/swift v1.0.47 h1:4DQRPj35Y41WogBxyhOXlrI37nzGlyEcsforeudyYPQ=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View file

@ -0,0 +1,20 @@
{
"mediaType": "application/vnd.oci.artifact.manifest.v1+json",
"artifactType": "application/vnd.example.sbom.v1",
"blobs": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:b093528a5eabd2ce6c954c0ecad0509f95536744c176f181c2640a8b126cdbcf",
"size": 29876998
}
],
"subject": {
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 343,
"digest": "sha256:dcfff5eb40423f055a4cd0a8d7ed39ff6cb9816868f5766b4088b9e9906961b9"
},
"annotations": {
"org.opencontainers.artifact.created": "2023-01-16T14:40:01Z",
"org.example.sbom.format": "json"
}
}

View file

@ -0,0 +1,20 @@
{
"mediaType": "application/vnd.oci.artifact.manifest.v1+json",
"artifactType": "application/vnd.example.sbom.v1",
"blobs": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:b093528a5eabd2ce6c954c0ecad0509f95536744c176f181c2640a8b126cdbcf",
"size": 29876998
}
],
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 549,
"digest": "sha256:f756842dc7541130d3a327a870a38aa9521233fc076d0ee2cea895c8c0a1e388"
},
"annotations": {
"org.opencontainers.artifact.created": "2023-01-16T14:40:01Z",
"org.example.sbom.format": "json"
}
}

View file

@ -0,0 +1,19 @@
{
"artifactType": "application/vnd.example.sbom.v1",
"blobs": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:b093528a5eabd2ce6c954c0ecad0509f95536744c176f181c2640a8b126cdbcf",
"size": 29876998
}
],
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 549,
"digest": "sha256:f756842dc7541130d3a327a870a38aa9521233fc076d0ee2cea895c8c0a1e388"
},
"annotations": {
"org.opencontainers.artifact.created": "2023-01-16T14:40:01Z",
"org.example.sbom.format": "json"
}
}

View file

@ -0,0 +1,15 @@
{
"mediaType": "application/vnd.oci.artifact.manifest.v1+json",
"artifactType": "application/vnd.example.sbom.v1",
"blobs": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:b093528a5eabd2ce6c954c0ecad0509f95536744c176f181c2640a8b126cdbcf",
"size": 29876998
}
],
"annotations": {
"org.opencontainers.artifact.created": "2023-01-16T14:40:01Z",
"org.example.sbom.format": "json"
}
}

View file

@ -0,0 +1,87 @@
package artifact
import (
_ "embed"
"github.com/distribution/distribution/v3"
_ "github.com/distribution/distribution/v3/manifest/ocischema"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
var (
// ManifestBytes is an example of a valid artifact manifest
//go:embed fixtures/manifest.json
ManifestBytes []byte
ManifestDescriptor = distribution.Descriptor{
MediaType: v1.MediaTypeArtifactManifest,
Size: 647,
Digest: "sha256:dadc6d0e6ccdcb629c78d1d13ee4e857b0fad468371e67839e777f2e1b9e33c4",
}
ManifestDeserialized = &DeserializedManifest{
Manifest: Manifest{
MediaType: v1.MediaTypeArtifactManifest,
ArtifactType: "application/vnd.example.sbom.v1",
Blobs: []distribution.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar+gzip",
Digest: "sha256:b093528a5eabd2ce6c954c0ecad0509f95536744c176f181c2640a8b126cdbcf",
Size: 29876998,
},
},
Subject: distribution.Descriptor{
MediaType: v1.MediaTypeImageManifest,
Digest: "sha256:f756842dc7541130d3a327a870a38aa9521233fc076d0ee2cea895c8c0a1e388",
Size: 549,
},
Annotations: map[string]string{
"org.opencontainers.artifact.created": "2023-01-16T14:40:01Z",
"org.example.sbom.format": "json",
},
},
canonical: ManifestBytes,
}
)
var (
// ManifestNoMediaType is an invalid artifact manifest because there is no
// mediaType field, unlike image manifests the fils is mandatory.
//go:embed fixtures/no-media-type.json
ManifestNoMediaType []byte
)
var (
// ManifestNoSubjectBytes is an example of a valid artifact manifest that has no
// subject.
//go:embed fixtures/no-subject.json
ManifestNoSubjectBytes []byte
ManifestNoSubjectDescriptor = distribution.Descriptor{
MediaType: v1.MediaTypeArtifactManifest,
Size: 459,
Digest: "sha256:89d65c08998b0b94515414eb060b6c28e15f76b2c3a51efe0b2b541a53babe55",
}
ManifestNoSubjectDeserialized = &DeserializedManifest{
Manifest: Manifest{
MediaType: v1.MediaTypeArtifactManifest,
ArtifactType: "application/vnd.example.sbom.v1",
Blobs: []distribution.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar+gzip",
Digest: "sha256:b093528a5eabd2ce6c954c0ecad0509f95536744c176f181c2640a8b126cdbcf",
Size: 29876998,
},
},
Annotations: map[string]string{
"org.opencontainers.artifact.created": "2023-01-16T14:40:01Z",
"org.example.sbom.format": "json",
},
},
canonical: ManifestNoSubjectBytes,
}
)
var (
// ManifestBlobSubjectBytes is an example of an invalid artifact manifest
// because the subject must be another manifest.
//go:embed fixtures/blob-subject.json
ManifestBlobSubjectBytes []byte
)

View file

@ -0,0 +1,117 @@
package artifact
import (
"encoding/json"
"errors"
"fmt"
"github.com/distribution/distribution/v3"
"github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
func init() {
artifactFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
m := &DeserializedManifest{}
err := m.UnmarshalJSON(b)
if err != nil {
return nil, distribution.Descriptor{}, err
}
dgst := digest.FromBytes(b)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeArtifactManifest}, nil
}
if err := distribution.RegisterManifestSchema(v1.MediaTypeArtifactManifest, artifactFunc); err != nil {
panic(fmt.Sprintf("Unable to register manifest: %s", err))
}
}
// Manifest defines an oci artifact manifest.
type Manifest struct {
// MediaType is the media type of this artifact manifest.
MediaType string `json:"mediaType,omitEmpty"`
// ArtifactType is the media type of the artifact referenced by this
// artifact manifest.
ArtifactType string `json:"artifactType,omitempty"`
// Blobs lists the descriptors for the files making up the artifact
// referenced by this this artifact manifest.
Blobs []distribution.Descriptor `json:"blobs,omitempty"`
// Subject is the descriptor of a manifest referred to by this artifact.
Subject distribution.Descriptor
// Annotations contain arbitrary metadata for the image manifest
Annotations map[string]string `json:"annotations,omitempty"`
}
// FromStruct takes a Manifest structure, marshals it to JSON, and returns a
// DeserializedManifest which contains the manifest and its JSON representation.
func FromStruct(m Manifest) (*DeserializedManifest, error) {
var deserialized DeserializedManifest
deserialized.Manifest = m
var err error
deserialized.canonical, err = json.MarshalIndent(&m, "", " ")
return &deserialized, err
}
// DeserializedManifest wraps Manifest with a copy of the original JSON.
// It satisfies the distribution.Manifest interface.
type DeserializedManifest struct {
Manifest
// canonical is the canonical byte representation of the Manifest.
canonical []byte
}
func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
m.canonical = make([]byte, len(b))
copy(m.canonical, b)
var manifest Manifest
if err := json.Unmarshal(m.canonical, &manifest); err != nil {
return err
}
if manifest.MediaType != v1.MediaTypeArtifactManifest {
return fmt.Errorf("mediaType in manifest must be '%q' not '%s'",
v1.MediaTypeArtifactManifest, manifest.MediaType)
}
// The subject if specified must be a must be a manifest. This is validated
// here rather then in the storage manifest Put handler because the subject
// does not have to exist, so there is nothing to validate in the manifest
// store. If a non-compliant client provided the digest of a blob then the
// registry would still indicate that the referred manifest does not exist.
if manifest.Subject.Digest != "" {
if !distribution.ManifestMediaTypeSupported(manifest.Subject.MediaType) {
return fmt.Errorf("subject.mediaType must be a manifest, not '%s'", manifest.Subject.MediaType)
}
}
m.Manifest = manifest
return nil
}
// MarshalJSON returns the contents of canonical. If canonical is empty,
// returns an error.
func (m *DeserializedManifest) MarshalJSON() ([]byte, error) {
if len(m.canonical) > 0 {
return m.canonical, nil
}
return nil, errors.New("JSON representation not initialized in DeserializedManifest")
}
// Payload returns the raw content of the manifest. The contents can be used to
// calculate the content identifier.
func (m *DeserializedManifest) Payload() (string, []byte, error) {
return v1.MediaTypeArtifactManifest, m.canonical, nil
}
// References returns the descriptors of this manifest's blobs only.
func (m *DeserializedManifest) References() []distribution.Descriptor {
return m.Blobs
}

View file

@ -0,0 +1,95 @@
package artifact_test
import (
"reflect"
"testing"
"github.com/distribution/distribution/v3"
artifact "github.com/distribution/distribution/v3/manifest/artifact"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
func TestArtifactFunc(t *testing.T) {
for name, test := range map[string]struct {
rawManifest []byte
expectError bool
expectedDescriptor distribution.Descriptor
expectedManifest distribution.Manifest
}{
"valid_artifact": {
rawManifest: artifact.ManifestBytes,
expectedDescriptor: artifact.ManifestDescriptor,
expectedManifest: artifact.ManifestDeserialized,
},
"artifact_must_have_mediaType": {
rawManifest: artifact.ManifestNoMediaType,
expectError: true,
},
"artifact_can_have_no_subject": {
rawManifest: artifact.ManifestNoSubjectBytes,
expectedDescriptor: artifact.ManifestNoSubjectDescriptor,
expectedManifest: artifact.ManifestNoSubjectDeserialized,
},
"artifact_subject_must_be_manifest": {
rawManifest: artifact.ManifestBlobSubjectBytes,
expectError: true,
},
} {
t.Run(name, func(t *testing.T) {
test := test
t.Parallel()
manifest, descriptor, err := distribution.UnmarshalManifest(v1.MediaTypeArtifactManifest, test.rawManifest)
if err != nil != test.expectError {
t.Fatalf("Unexpected error value: %s, expected error=%t", err, test.expectError)
}
if !reflect.DeepEqual(descriptor, test.expectedDescriptor) {
t.Errorf("Descriptor incorrect:\n%v\nexpected:\n%v", descriptor, test.expectedDescriptor)
}
if !reflect.DeepEqual(manifest, test.expectedManifest) {
t.Errorf("Manifest incorrect:\n%v\nexpected:\n%v", manifest, test.expectedManifest)
}
})
}
}
func TestPayload(t *testing.T) {
t.Parallel()
manifest, _, err := distribution.UnmarshalManifest(v1.MediaTypeArtifactManifest, artifact.ManifestBytes)
if err != nil {
t.Fatalf("Failed to unmarshal manifest: %s", err)
}
mediaType, payload, err := manifest.Payload()
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if mediaType != v1.MediaTypeArtifactManifest {
t.Errorf("Unexpected mediaType %q, should be %q", mediaType, v1.MediaTypeArtifactManifest)
}
if !reflect.DeepEqual(payload, artifact.ManifestBytes) {
t.Errorf("Unexpected payload, should exactly match inputted manifest.")
}
}
func TestReferences(t *testing.T) {
// References only returns the blobs of this artifact and not also it's
// subject because the subject does not have to exist when the artifact is
// pushed. Unlike when an image index returns the references image manifests
// because those do have to exist before the image index is pushed.
t.Parallel()
manifest, _, err := distribution.UnmarshalManifest(v1.MediaTypeArtifactManifest, artifact.ManifestBytes)
if err != nil {
t.Fatalf("Failed to unmarshal manifest: %s", err)
}
references := manifest.References()
if !reflect.DeepEqual(references, artifact.ManifestDeserialized.Blobs) {
t.Errorf("Unexpected references:\n%v\nexpected:\n%v", references, artifact.ManifestDeserialized.Blobs)
}
}

View file

@ -26,6 +26,14 @@ type Manifest interface {
Payload() (mediaType string, payload []byte, err error)
}
// Referrer represents a Manifest which can refer to a subject.
type Referrer interface {
// Subject returns a pointer to a Descriptor representing the manifest which
// this manifest refers to or nil if this manifest does not refer to a
// subject.
Subject() *Descriptor
}
// ManifestBuilder creates a manifest allowing one to include dependencies.
// Instances can be obtained from a version-specific manifest package. Manifest
// specific data is passed into the function which creates the builder.
@ -84,6 +92,12 @@ func ManifestMediaTypes() (mediaTypes []string) {
return
}
// ManifestMediaTypeSupported returns true if the given mediaType is supported.
func ManifestMediaTypeSupported(mediaType string) bool {
_, ok := mappings[mediaType]
return ok
}
// UnmarshalFunc implements manifest unmarshalling a given MediaType
type UnmarshalFunc func([]byte) (Manifest, Descriptor, error)

View file

@ -9,6 +9,7 @@ import (
"github.com/distribution/distribution/v3"
dcontext "github.com/distribution/distribution/v3/context"
_ "github.com/distribution/distribution/v3/manifest/artifact"
"github.com/distribution/distribution/v3/manifest/manifestlist"
"github.com/distribution/distribution/v3/manifest/ocischema"
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
@ -322,7 +323,8 @@ func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request)
return
}
isAnOCIManifest := mediaType == v1.MediaTypeImageManifest || mediaType == v1.MediaTypeImageIndex
isAnOCIManifest := mediaType == v1.MediaTypeImageManifest || mediaType == v1.MediaTypeImageIndex ||
mediaType == v1.MediaTypeArtifactManifest
if isAnOCIManifest {
dcontext.GetLogger(imh).Debug("Putting an OCI Manifest!")
@ -340,6 +342,8 @@ func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request)
return
}
// TODO create subject link here
_, err = manifests.Put(imh, manifest, options...)
if err != nil {
// TODO(stevvooe): These error handling switches really need to be

View file

@ -8,6 +8,7 @@ import (
"github.com/distribution/distribution/v3"
dcontext "github.com/distribution/distribution/v3/context"
"github.com/distribution/distribution/v3/manifest"
"github.com/distribution/distribution/v3/manifest/artifact"
"github.com/distribution/distribution/v3/manifest/manifestlist"
"github.com/distribution/distribution/v3/manifest/ocischema"
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
@ -134,7 +135,7 @@ func (ms *manifestStore) Put(ctx context.Context, manifest distribution.Manifest
return ms.schema1Handler.Put(ctx, manifest, ms.skipDependencyVerification)
case *schema2.DeserializedManifest:
return ms.schema2Handler.Put(ctx, manifest, ms.skipDependencyVerification)
case *ocischema.DeserializedManifest:
case *ocischema.DeserializedManifest, *artifact.DeserializedManifest:
return ms.ocischemaHandler.Put(ctx, manifest, ms.skipDependencyVerification)
case *manifestlist.DeserializedManifestList:
return ms.manifestListHandler.Put(ctx, manifest, ms.skipDependencyVerification)

View file

@ -7,12 +7,14 @@ import (
"github.com/distribution/distribution/v3"
dcontext "github.com/distribution/distribution/v3/context"
"github.com/distribution/distribution/v3/manifest/artifact"
"github.com/distribution/distribution/v3/manifest/ocischema"
"github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
// ocischemaManifestHandler is a ManifestHandler that covers ocischema manifests.
// ocischemaManifestHandler is a ManifestHandler that covers ocischema manifests
// and OCI Artifact Manifests.
type ocischemaManifestHandler struct {
repository distribution.Repository
blobStore distribution.BlobStore
@ -36,16 +38,11 @@ func (ms *ocischemaManifestHandler) Unmarshal(ctx context.Context, dgst digest.D
func (ms *ocischemaManifestHandler) Put(ctx context.Context, manifest distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error) {
dcontext.GetLogger(ms.ctx).Debug("(*ocischemaManifestHandler).Put")
m, ok := manifest.(*ocischema.DeserializedManifest)
if !ok {
return "", fmt.Errorf("non-ocischema manifest put to ocischemaManifestHandler: %T", manifest)
}
if err := ms.verifyManifest(ms.ctx, *m, skipDependencyVerification); err != nil {
if err := ms.verifyManifest(ms.ctx, manifest, skipDependencyVerification); err != nil {
return "", err
}
mt, payload, err := m.Payload()
mt, payload, err := manifest.Payload()
if err != nil {
return "", err
}
@ -62,11 +59,17 @@ func (ms *ocischemaManifestHandler) Put(ctx context.Context, manifest distributi
// verifyManifest ensures that the manifest content is valid from the
// perspective of the registry. As a policy, the registry only tries to store
// valid content, leaving trust policies of that content up to consumers.
func (ms *ocischemaManifestHandler) verifyManifest(ctx context.Context, mnfst ocischema.DeserializedManifest, skipDependencyVerification bool) error {
func (ms *ocischemaManifestHandler) verifyManifest(ctx context.Context, mnfst distribution.Manifest, skipDependencyVerification bool) error {
var errs distribution.ErrManifestVerification
if mnfst.Manifest.SchemaVersion != 2 {
return fmt.Errorf("unrecognized manifest schema version %d", mnfst.Manifest.SchemaVersion)
switch m := mnfst.(type) {
case *ocischema.DeserializedManifest:
if m.Manifest.SchemaVersion != 2 {
return fmt.Errorf("unrecognized manifest schema version %d", m.Manifest.SchemaVersion)
}
case *artifact.DeserializedManifest:
default:
return fmt.Errorf("non-ocischema manifest put to ocischemaManifestHandler: %T", mnfst)
}
if skipDependencyVerification {

View file

@ -8,6 +8,7 @@ import (
"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/manifest"
"github.com/distribution/distribution/v3/manifest/artifact"
"github.com/distribution/distribution/v3/manifest/ocischema"
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
"github.com/opencontainers/go-digest"
@ -332,3 +333,42 @@ func TestVerifyOCIManifestBlobLayerAndConfig(t *testing.T) {
checkFn(m, c.Err)
}
}
func TestPutArtifact(t *testing.T) {
t.Parallel()
driver := inmemory.New()
registry := createRegistry(t, driver)
repo := makeRepository(t, registry, "test")
manifestService := makeManifestService(t, repo)
ctx := context.Background()
blob, err := repo.Blobs(ctx).Put(ctx, v1.MediaTypeImageLayerGzip, []byte("I am an artifact"))
if err != nil {
t.Fatal(err)
}
template := artifact.Manifest{
MediaType: v1.MediaTypeArtifactManifest,
ArtifactType: "application/vnd.example.sbom.v1",
Blobs: []distribution.Descriptor{
blob,
},
Subject: distribution.Descriptor{
MediaType: v1.MediaTypeImageManifest,
Digest: "sha256:195ce2d6ff471aa95e91f3ea1e95a27d474a452f040d4c18f6eb29f3ca42a821",
Size: 21,
},
}
manifest, err := artifact.FromStruct(template)
if err != nil {
t.Fatalf("Failed to construct artifact manifest: %s", err)
}
_, err = manifestService.Put(ctx, manifest)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
}

View file

@ -53,4 +53,19 @@ const (
// AnnotationDescription is the annotation key for the human-readable description of the software packaged in the image.
AnnotationDescription = "org.opencontainers.image.description"
// AnnotationBaseImageDigest is the annotation key for the digest of the image's base image.
AnnotationBaseImageDigest = "org.opencontainers.image.base.digest"
// AnnotationBaseImageName is the annotation key for the image reference of the image's base image.
AnnotationBaseImageName = "org.opencontainers.image.base.name"
// AnnotationArtifactCreated is the annotation key for the date and time on which the artifact was built, conforming to RFC 3339.
AnnotationArtifactCreated = "org.opencontainers.artifact.created"
// AnnotationArtifactDescription is the annotation key for the human readable description for the artifact.
AnnotationArtifactDescription = "org.opencontainers.artifact.description"
// AnnotationReferrersFiltersApplied is the annotation key for the comma separated list of filters applied by the registry in the referrers listing.
AnnotationReferrersFiltersApplied = "org.opencontainers.referrers.filtersApplied"
)

View file

@ -0,0 +1,34 @@
// Copyright 2022 The Linux Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1
// Artifact describes an artifact manifest.
// This structure provides `application/vnd.oci.artifact.manifest.v1+json` mediatype when marshalled to JSON.
type Artifact struct {
// MediaType is the media type of the object this schema refers to.
MediaType string `json:"mediaType"`
// ArtifactType is the IANA media type of the artifact this schema refers to.
ArtifactType string `json:"artifactType"`
// Blobs is a collection of blobs referenced by this manifest.
Blobs []Descriptor `json:"blobs,omitempty"`
// Subject (reference) is an optional link from the artifact to another manifest forming an association between the artifact and the other manifest.
Subject *Descriptor `json:"subject,omitempty"`
// Annotations contains arbitrary metadata for the artifact manifest.
Annotations map[string]string `json:"annotations,omitempty"`
}

View file

@ -89,9 +89,20 @@ type Image struct {
// Architecture is the CPU architecture which the binaries in this image are built to run on.
Architecture string `json:"architecture"`
// Variant is the variant of the specified CPU architecture which image binaries are intended to run on.
Variant string `json:"variant,omitempty"`
// OS is the name of the operating system which the image is built to run on.
OS string `json:"os"`
// OSVersion is an optional field specifying the operating system
// version, for example on Windows `10.0.14393.1066`.
OSVersion string `json:"os.version,omitempty"`
// OSFeatures is an optional field specifying an array of strings,
// each listing a required OS feature (for example on Windows `win32k`).
OSFeatures []string `json:"os.features,omitempty"`
// Config defines the execution parameters which should be used as a base when running a container using the image.
Config ImageConfig `json:"config,omitempty"`

View file

@ -1,4 +1,4 @@
// Copyright 2016 The Linux Foundation
// Copyright 2016-2022 The Linux Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -35,10 +35,18 @@ type Descriptor struct {
// Annotations contains arbitrary metadata relating to the targeted content.
Annotations map[string]string `json:"annotations,omitempty"`
// Data is an embedding of the targeted content. This is encoded as a base64
// string when marshalled to JSON (automatically, by encoding/json). If
// present, Data can be used directly to avoid fetching the targeted content.
Data []byte `json:"data,omitempty"`
// Platform describes the platform which the image in the manifest runs on.
//
// This should only be used when referring to a manifest.
Platform *Platform `json:"platform,omitempty"`
// ArtifactType is the IANA media type of this artifact.
ArtifactType string `json:"artifactType,omitempty"`
}
// Platform describes the platform which the image in the manifest runs on.

View file

@ -21,7 +21,7 @@ import "github.com/opencontainers/image-spec/specs-go"
type Index struct {
specs.Versioned
// MediaType specificies the type of this document data structure e.g. `application/vnd.oci.image.index.v1+json`
// MediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.index.v1+json`
MediaType string `json:"mediaType,omitempty"`
// Manifests references platform specific manifests.

View file

@ -1,4 +1,4 @@
// Copyright 2016 The Linux Foundation
// Copyright 2016-2022 The Linux Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import "github.com/opencontainers/image-spec/specs-go"
type Manifest struct {
specs.Versioned
// MediaType specificies the type of this document data structure e.g. `application/vnd.oci.image.manifest.v1+json`
// MediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.manifest.v1+json`
MediaType string `json:"mediaType,omitempty"`
// Config references a configuration object for a container, by digest.
@ -30,6 +30,9 @@ type Manifest struct {
// Layers is an indexed list of layers referenced by the manifest.
Layers []Descriptor `json:"layers"`
// Subject is an optional link from the image manifest to another manifest forming an association between the image manifest and the other manifest.
Subject *Descriptor `json:"subject,omitempty"`
// Annotations contains arbitrary metadata for the image manifest.
Annotations map[string]string `json:"annotations,omitempty"`
}

View file

@ -34,6 +34,10 @@ const (
// referenced by the manifest.
MediaTypeImageLayerGzip = "application/vnd.oci.image.layer.v1.tar+gzip"
// MediaTypeImageLayerZstd is the media type used for zstd compressed
// layers referenced by the manifest.
MediaTypeImageLayerZstd = "application/vnd.oci.image.layer.v1.tar+zstd"
// MediaTypeImageLayerNonDistributable is the media type for layers referenced by
// the manifest but with distribution restrictions.
MediaTypeImageLayerNonDistributable = "application/vnd.oci.image.layer.nondistributable.v1.tar"
@ -43,6 +47,14 @@ const (
// restrictions.
MediaTypeImageLayerNonDistributableGzip = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip"
// MediaTypeImageLayerNonDistributableZstd is the media type for zstd
// compressed layers referenced by the manifest but with distribution
// restrictions.
MediaTypeImageLayerNonDistributableZstd = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd"
// MediaTypeImageConfig specifies the media type for the image configuration.
MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json"
// MediaTypeArtifactManifest specifies the media type for a content descriptor.
MediaTypeArtifactManifest = "application/vnd.oci.artifact.manifest.v1+json"
)

View file

@ -20,12 +20,12 @@ const (
// VersionMajor is for an API incompatible changes
VersionMajor = 1
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 0
VersionMinor = 1
// VersionPatch is for backwards-compatible bug fixes
VersionPatch = 2
VersionPatch = 0
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = ""
VersionDev = "-rc2"
)
// Version is the specification version that the package types support.

4
vendor/modules.txt vendored
View file

@ -231,8 +231,8 @@ github.com/ncw/swift/swifttest
## explicit; go 1.13
github.com/opencontainers/go-digest
github.com/opencontainers/go-digest/digestset
# github.com/opencontainers/image-spec v1.0.2
## explicit
# github.com/opencontainers/image-spec v1.1.0-rc2
## explicit; go 1.17
github.com/opencontainers/image-spec/specs-go
github.com/opencontainers/image-spec/specs-go/v1
# github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4