Move manifest to discrete package
Because manifests and their signatures are a discrete component of the registry, we are moving the definitions into a separate package. This causes us to lose some test coverage, but we can fill this in shortly. No changes have been made to the external interfaces, but they are likely to come. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
parent
cd748f92ab
commit
a4024b2f90
14 changed files with 80 additions and 74 deletions
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
)
|
||||
|
||||
// Layer provides a readable and seekable layer object. Typically,
|
||||
|
@ -74,7 +75,7 @@ var (
|
|||
|
||||
// ErrUnknownLayer returned when layer cannot be found.
|
||||
type ErrUnknownLayer struct {
|
||||
FSLayer FSLayer
|
||||
FSLayer manifest.FSLayer
|
||||
}
|
||||
|
||||
func (err ErrUnknownLayer) Error() string {
|
||||
|
@ -83,7 +84,7 @@ func (err ErrUnknownLayer) Error() string {
|
|||
|
||||
// ErrLayerInvalidDigest returned when tarsum check fails.
|
||||
type ErrLayerInvalidDigest struct {
|
||||
FSLayer FSLayer
|
||||
FSLayer manifest.FSLayer
|
||||
}
|
||||
|
||||
func (err ErrLayerInvalidDigest) Error() string {
|
||||
|
|
|
@ -2,6 +2,7 @@ package storage
|
|||
|
||||
import (
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/storagedriver"
|
||||
)
|
||||
|
||||
|
@ -33,7 +34,7 @@ func (ls *layerStore) Fetch(name string, digest digest.Digest) (Layer, error) {
|
|||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case storagedriver.PathNotFoundError, *storagedriver.PathNotFoundError:
|
||||
return nil, ErrUnknownLayer{FSLayer{BlobSum: digest}}
|
||||
return nil, ErrUnknownLayer{manifest.FSLayer{BlobSum: digest}}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
@ -43,7 +44,7 @@ func (ls *layerStore) Fetch(name string, digest digest.Digest) (Layer, error) {
|
|||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case storagedriver.PathNotFoundError, *storagedriver.PathNotFoundError:
|
||||
return nil, ErrUnknownLayer{FSLayer{BlobSum: digest}}
|
||||
return nil, ErrUnknownLayer{manifest.FSLayer{BlobSum: digest}}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"code.google.com/p/go-uuid/uuid"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/storagedriver"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
|
||||
|
@ -284,7 +285,7 @@ func (luc *layerUploadController) validateLayer(fp layerFile, size int64, dgst d
|
|||
}
|
||||
|
||||
if !digestVerifier.Verified() {
|
||||
return "", ErrLayerInvalidDigest{FSLayer{BlobSum: dgst}}
|
||||
return "", ErrLayerInvalidDigest{manifest.FSLayer{BlobSum: dgst}}
|
||||
}
|
||||
|
||||
return dgst, nil
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
// Versioned provides a struct with just the manifest schemaVersion. Incoming
|
||||
// content with unknown schema version can be decoded against this struct to
|
||||
// check the version.
|
||||
type Versioned struct {
|
||||
// SchemaVersion is the image manifest schema that this image follows
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
}
|
||||
|
||||
// Manifest provides the base accessible fields for working with V2 image
|
||||
// format in the registry.
|
||||
type Manifest struct {
|
||||
Versioned
|
||||
|
||||
// Name is the name of the image's repository
|
||||
Name string `json:"name"`
|
||||
|
||||
// Tag is the tag of the image specified by this manifest
|
||||
Tag string `json:"tag"`
|
||||
|
||||
// Architecture is the host architecture on which this image is intended to
|
||||
// run
|
||||
Architecture string `json:"architecture"`
|
||||
|
||||
// FSLayers is a list of filesystem layer blobSums contained in this image
|
||||
FSLayers []FSLayer `json:"fsLayers"`
|
||||
|
||||
// History is a list of unstructured historical data for v1 compatibility
|
||||
History []ManifestHistory `json:"history"`
|
||||
}
|
||||
|
||||
// Sign signs the manifest with the provided private key, returning a
|
||||
// SignedManifest. This typically won't be used within the registry, except
|
||||
// for testing.
|
||||
func (m *Manifest) Sign(pk libtrust.PrivateKey) (*SignedManifest, error) {
|
||||
p, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
js, err := libtrust.NewJSONSignature(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := js.Sign(pk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pretty, err := js.PrettySignature("signatures")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SignedManifest{
|
||||
Manifest: *m,
|
||||
Raw: pretty,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SignWithChain signs the manifest with the given private key and x509 chain.
|
||||
// The public key of the first element in the chain must be the public key
|
||||
// corresponding with the sign key.
|
||||
func (m *Manifest) SignWithChain(key libtrust.PrivateKey, chain []*x509.Certificate) (*SignedManifest, error) {
|
||||
p, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
js, err := libtrust.NewJSONSignature(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := js.SignWithChain(key, chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pretty, err := js.PrettySignature("signatures")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SignedManifest{
|
||||
Manifest: *m,
|
||||
Raw: pretty,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SignedManifest provides an envelope for a signed image manifest, including
|
||||
// the format sensitive raw bytes. It contains fields to
|
||||
type SignedManifest struct {
|
||||
Manifest
|
||||
|
||||
// Raw is the byte representation of the ImageManifest, used for signature
|
||||
// verification. The value of Raw must be used directly during
|
||||
// serialization, or the signature check will fail. The manifest byte
|
||||
// representation cannot change or it will have to be re-signed.
|
||||
Raw []byte `json:"-"`
|
||||
}
|
||||
|
||||
// Verify verifies the signature of the signed manifest returning the public
|
||||
// keys used during signing.
|
||||
func (sm *SignedManifest) Verify() ([]libtrust.PublicKey, error) {
|
||||
js, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
|
||||
if err != nil {
|
||||
logrus.WithField("err", err).Debugf("(*SignedManifest).Verify")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return js.Verify()
|
||||
}
|
||||
|
||||
// VerifyChains verifies the signature of the signed manifest against the
|
||||
// certificate pool returning the list of verified chains. Signatures without
|
||||
// an x509 chain are not checked.
|
||||
func (sm *SignedManifest) VerifyChains(ca *x509.CertPool) ([][]*x509.Certificate, error) {
|
||||
js, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return js.VerifyChains(ca)
|
||||
}
|
||||
|
||||
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
|
||||
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||
var manifest Manifest
|
||||
if err := json.Unmarshal(b, &manifest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm.Manifest = manifest
|
||||
sm.Raw = make([]byte, len(b), len(b))
|
||||
copy(sm.Raw, b)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the contents of raw. If Raw is nil, marshals the inner
|
||||
// contents. Applications requiring a marshaled signed manifest should simply
|
||||
// use Raw directly, since the the content produced by json.Marshal will
|
||||
// compacted and will fail signature checks.
|
||||
func (sm *SignedManifest) MarshalJSON() ([]byte, error) {
|
||||
if len(sm.Raw) > 0 {
|
||||
return sm.Raw, nil
|
||||
}
|
||||
|
||||
// If the raw data is not available, just dump the inner content.
|
||||
return json.Marshal(&sm.Manifest)
|
||||
}
|
||||
|
||||
// FSLayer is a container struct for BlobSums defined in an image manifest
|
||||
type FSLayer struct {
|
||||
// BlobSum is the tarsum of the referenced filesystem image layer
|
||||
BlobSum digest.Digest `json:"blobSum"`
|
||||
}
|
||||
|
||||
// ManifestHistory stores unstructured v1 compatibility information
|
||||
type ManifestHistory struct {
|
||||
// V1Compatibility is the raw v1 compatibility information
|
||||
V1Compatibility string `json:"v1Compatibility"`
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/storagedriver"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
@ -116,7 +117,7 @@ func (ms *manifestStore) Exists(name, tag string) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (ms *manifestStore) Get(name, tag string) (*SignedManifest, error) {
|
||||
func (ms *manifestStore) Get(name, tag string) (*manifest.SignedManifest, error) {
|
||||
p, err := ms.path(name, tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -132,7 +133,7 @@ func (ms *manifestStore) Get(name, tag string) (*SignedManifest, error) {
|
|||
}
|
||||
}
|
||||
|
||||
var manifest SignedManifest
|
||||
var manifest manifest.SignedManifest
|
||||
|
||||
if err := json.Unmarshal(content, &manifest); err != nil {
|
||||
// TODO(stevvooe): Corrupted manifest error?
|
||||
|
@ -144,7 +145,7 @@ func (ms *manifestStore) Get(name, tag string) (*SignedManifest, error) {
|
|||
return &manifest, nil
|
||||
}
|
||||
|
||||
func (ms *manifestStore) Put(name, tag string, manifest *SignedManifest) error {
|
||||
func (ms *manifestStore) Put(name, tag string, manifest *manifest.SignedManifest) error {
|
||||
p, err := ms.path(name, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -185,7 +186,7 @@ func (ms *manifestStore) path(name, tag string) (string, error) {
|
|||
})
|
||||
}
|
||||
|
||||
func (ms *manifestStore) verifyManifest(name, tag string, manifest *SignedManifest) error {
|
||||
func (ms *manifestStore) verifyManifest(name, tag string, manifest *manifest.SignedManifest) error {
|
||||
// TODO(stevvooe): This verification is present here, but this needs to be
|
||||
// lifted out of the storage infrastructure and moved into a package
|
||||
// oriented towards defining verifiers and reporting them with
|
||||
|
|
|
@ -4,10 +4,10 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/storagedriver/inmemory"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
func TestManifestStorage(t *testing.T) {
|
||||
|
@ -42,13 +42,13 @@ func TestManifestStorage(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
manifest := Manifest{
|
||||
Versioned: Versioned{
|
||||
manifest := manifest.Manifest{
|
||||
Versioned: manifest.Versioned{
|
||||
SchemaVersion: 1,
|
||||
},
|
||||
Name: name,
|
||||
Tag: tag,
|
||||
FSLayers: []FSLayer{
|
||||
FSLayers: []manifest.FSLayer{
|
||||
{
|
||||
BlobSum: "asdf",
|
||||
},
|
|
@ -2,6 +2,7 @@ package storage
|
|||
|
||||
import (
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/storagedriver"
|
||||
)
|
||||
|
||||
|
@ -59,10 +60,10 @@ type ManifestService interface {
|
|||
Exists(name, tag string) (bool, error)
|
||||
|
||||
// Get retrieves the named manifest, if it exists.
|
||||
Get(name, tag string) (*SignedManifest, error)
|
||||
Get(name, tag string) (*manifest.SignedManifest, error)
|
||||
|
||||
// Put creates or updates the named manifest.
|
||||
Put(name, tag string, manifest *SignedManifest) error
|
||||
Put(name, tag string, manifest *manifest.SignedManifest) error
|
||||
|
||||
// Delete removes the named manifest, if it exists.
|
||||
Delete(name, tag string) error
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue