vendor: github.com/docker/cli v23.0.0
full diff: https://github.com/docker/cli/compare/v23.0.0-rc.1...v23.0.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>pull/1610/head
parent
260117289b
commit
081447c9b1
@ -0,0 +1,107 @@
|
||||
package ocischema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// Builder is a type for constructing manifests.
|
||||
type Builder struct {
|
||||
// bs is a BlobService used to publish the configuration blob.
|
||||
bs distribution.BlobService
|
||||
|
||||
// configJSON references
|
||||
configJSON []byte
|
||||
|
||||
// layers is a list of layer descriptors that gets built by successive
|
||||
// calls to AppendReference.
|
||||
layers []distribution.Descriptor
|
||||
|
||||
// Annotations contains arbitrary metadata relating to the targeted content.
|
||||
annotations map[string]string
|
||||
|
||||
// For testing purposes
|
||||
mediaType string
|
||||
}
|
||||
|
||||
// NewManifestBuilder is used to build new manifests for the current schema
|
||||
// version. It takes a BlobService so it can publish the configuration blob
|
||||
// as part of the Build process, and annotations.
|
||||
func NewManifestBuilder(bs distribution.BlobService, configJSON []byte, annotations map[string]string) distribution.ManifestBuilder {
|
||||
mb := &Builder{
|
||||
bs: bs,
|
||||
configJSON: make([]byte, len(configJSON)),
|
||||
annotations: annotations,
|
||||
mediaType: v1.MediaTypeImageManifest,
|
||||
}
|
||||
copy(mb.configJSON, configJSON)
|
||||
|
||||
return mb
|
||||
}
|
||||
|
||||
// SetMediaType assigns the passed mediatype or error if the mediatype is not a
|
||||
// valid media type for oci image manifests currently: "" or "application/vnd.oci.image.manifest.v1+json"
|
||||
func (mb *Builder) SetMediaType(mediaType string) error {
|
||||
if mediaType != "" && mediaType != v1.MediaTypeImageManifest {
|
||||
return errors.New("invalid media type for OCI image manifest")
|
||||
}
|
||||
|
||||
mb.mediaType = mediaType
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build produces a final manifest from the given references.
|
||||
func (mb *Builder) Build(ctx context.Context) (distribution.Manifest, error) {
|
||||
m := Manifest{
|
||||
Versioned: manifest.Versioned{
|
||||
SchemaVersion: 2,
|
||||
MediaType: mb.mediaType,
|
||||
},
|
||||
Layers: make([]distribution.Descriptor, len(mb.layers)),
|
||||
Annotations: mb.annotations,
|
||||
}
|
||||
copy(m.Layers, mb.layers)
|
||||
|
||||
configDigest := digest.FromBytes(mb.configJSON)
|
||||
|
||||
var err error
|
||||
m.Config, err = mb.bs.Stat(ctx, configDigest)
|
||||
switch err {
|
||||
case nil:
|
||||
// Override MediaType, since Put always replaces the specified media
|
||||
// type with application/octet-stream in the descriptor it returns.
|
||||
m.Config.MediaType = v1.MediaTypeImageConfig
|
||||
return FromStruct(m)
|
||||
case distribution.ErrBlobUnknown:
|
||||
// nop
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add config to the blob store
|
||||
m.Config, err = mb.bs.Put(ctx, v1.MediaTypeImageConfig, mb.configJSON)
|
||||
// Override MediaType, since Put always replaces the specified media
|
||||
// type with application/octet-stream in the descriptor it returns.
|
||||
m.Config.MediaType = v1.MediaTypeImageConfig
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return FromStruct(m)
|
||||
}
|
||||
|
||||
// AppendReference adds a reference to the current ManifestBuilder.
|
||||
func (mb *Builder) AppendReference(d distribution.Describable) error {
|
||||
mb.layers = append(mb.layers, d.Descriptor())
|
||||
return nil
|
||||
}
|
||||
|
||||
// References returns the current references added to this builder.
|
||||
func (mb *Builder) References() []distribution.Descriptor {
|
||||
return mb.layers
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
package ocischema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemaVersion provides a pre-initialized version structure for this
|
||||
// packages version of the manifest.
|
||||
SchemaVersion = manifest.Versioned{
|
||||
SchemaVersion: 2, // historical value here.. does not pertain to OCI or docker version
|
||||
MediaType: v1.MediaTypeImageManifest,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
|
||||
if err := validateManifest(b); err != nil {
|
||||
return nil, distribution.Descriptor{}, err
|
||||
}
|
||||
m := new(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.MediaTypeImageManifest}, err
|
||||
}
|
||||
err := distribution.RegisterManifestSchema(v1.MediaTypeImageManifest, ocischemaFunc)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Unable to register manifest: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Manifest defines a ocischema manifest.
|
||||
type Manifest struct {
|
||||
manifest.Versioned
|
||||
|
||||
// Config references the image configuration as a blob.
|
||||
Config distribution.Descriptor `json:"config"`
|
||||
|
||||
// Layers lists descriptors for the layers referenced by the
|
||||
// configuration.
|
||||
Layers []distribution.Descriptor `json:"layers"`
|
||||
|
||||
// Annotations contains arbitrary metadata for the image manifest.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// References returns the descriptors of this manifests references.
|
||||
func (m Manifest) References() []distribution.Descriptor {
|
||||
references := make([]distribution.Descriptor, 0, 1+len(m.Layers))
|
||||
references = append(references, m.Config)
|
||||
references = append(references, m.Layers...)
|
||||
return references
|
||||
}
|
||||
|
||||
// Target returns the target of this manifest.
|
||||
func (m Manifest) Target() distribution.Descriptor {
|
||||
return m.Config
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// UnmarshalJSON populates a new Manifest struct from JSON data.
|
||||
func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
|
||||
m.canonical = make([]byte, len(b))
|
||||
// store manifest in canonical
|
||||
copy(m.canonical, b)
|
||||
|
||||
// Unmarshal canonical JSON into Manifest object
|
||||
var manifest Manifest
|
||||
if err := json.Unmarshal(m.canonical, &manifest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if manifest.MediaType != "" && manifest.MediaType != v1.MediaTypeImageManifest {
|
||||
return fmt.Errorf("if present, mediaType in manifest should be '%s' not '%s'",
|
||||
v1.MediaTypeImageManifest, manifest.MediaType)
|
||||
}
|
||||
|
||||
m.Manifest = manifest
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the contents of canonical. If canonical is empty,
|
||||
// marshals the inner contents.
|
||||
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.MediaTypeImageManifest, m.canonical, nil
|
||||
}
|
||||
|
||||
// unknownDocument represents a manifest, manifest list, or index that has not
|
||||
// yet been validated
|
||||
type unknownDocument struct {
|
||||
Manifests interface{} `json:"manifests,omitempty"`
|
||||
}
|
||||
|
||||
// validateManifest returns an error if the byte slice is invalid JSON or if it
|
||||
// contains fields that belong to a index
|
||||
func validateManifest(b []byte) error {
|
||||
var doc unknownDocument
|
||||
if err := json.Unmarshal(b, &doc); err != nil {
|
||||
return err
|
||||
}
|
||||
if doc.Manifests != nil {
|
||||
return errors.New("ocimanifest: expected manifest but found index")
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue