Merge pull request #21 from stevvooe/manifest-package
Move manifest to discrete package
This commit is contained in:
commit
89cd694e51
17 changed files with 297 additions and 168 deletions
12
api_test.go
12
api_test.go
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/docker/distribution/common/testutil"
|
"github.com/docker/distribution/common/testutil"
|
||||||
"github.com/docker/distribution/configuration"
|
"github.com/docker/distribution/configuration"
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
"github.com/docker/distribution/storage"
|
"github.com/docker/distribution/manifest"
|
||||||
_ "github.com/docker/distribution/storagedriver/inmemory"
|
_ "github.com/docker/distribution/storagedriver/inmemory"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
|
@ -268,10 +268,10 @@ func TestManifestAPI(t *testing.T) {
|
||||||
|
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
// Attempt to push unsigned manifest with missing layers
|
// Attempt to push unsigned manifest with missing layers
|
||||||
unsignedManifest := &storage.Manifest{
|
unsignedManifest := &manifest.Manifest{
|
||||||
Name: imageName,
|
Name: imageName,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
FSLayers: []storage.FSLayer{
|
FSLayers: []manifest.FSLayer{
|
||||||
{
|
{
|
||||||
BlobSum: "asdf",
|
BlobSum: "asdf",
|
||||||
},
|
},
|
||||||
|
@ -345,7 +345,7 @@ func TestManifestAPI(t *testing.T) {
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// Push the signed manifest with all layers pushed.
|
// Push the signed manifest with all layers pushed.
|
||||||
signedManifest, err := unsignedManifest.Sign(pk)
|
signedManifest, err := manifest.Sign(unsignedManifest, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error signing manifest: %v", err)
|
t.Fatalf("unexpected error signing manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ func TestManifestAPI(t *testing.T) {
|
||||||
|
|
||||||
checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK)
|
checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK)
|
||||||
|
|
||||||
var fetchedManifest storage.SignedManifest
|
var fetchedManifest manifest.SignedManifest
|
||||||
dec = json.NewDecoder(resp.Body)
|
dec = json.NewDecoder(resp.Body)
|
||||||
if err := dec.Decode(&fetchedManifest); err != nil {
|
if err := dec.Decode(&fetchedManifest); err != nil {
|
||||||
t.Fatalf("error decoding fetched manifest: %v", err)
|
t.Fatalf("error decoding fetched manifest: %v", err)
|
||||||
|
@ -404,7 +404,7 @@ func TestManifestAPI(t *testing.T) {
|
||||||
|
|
||||||
func putManifest(t *testing.T, msg, url string, v interface{}) *http.Response {
|
func putManifest(t *testing.T, msg, url string, v interface{}) *http.Response {
|
||||||
var body []byte
|
var body []byte
|
||||||
if sm, ok := v.(*storage.SignedManifest); ok {
|
if sm, ok := v.(*manifest.SignedManifest); ok {
|
||||||
body = sm.Raw
|
body = sm.Raw
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -12,18 +12,18 @@ import (
|
||||||
|
|
||||||
"github.com/docker/distribution/api/v2"
|
"github.com/docker/distribution/api/v2"
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
"github.com/docker/distribution/storage"
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client implements the client interface to the registry http api
|
// Client implements the client interface to the registry http api
|
||||||
type Client interface {
|
type Client interface {
|
||||||
// GetImageManifest returns an image manifest for the image at the given
|
// GetImageManifest returns an image manifest for the image at the given
|
||||||
// name, tag pair.
|
// name, tag pair.
|
||||||
GetImageManifest(name, tag string) (*storage.SignedManifest, error)
|
GetImageManifest(name, tag string) (*manifest.SignedManifest, error)
|
||||||
|
|
||||||
// PutImageManifest uploads an image manifest for the image at the given
|
// PutImageManifest uploads an image manifest for the image at the given
|
||||||
// name, tag pair.
|
// name, tag pair.
|
||||||
PutImageManifest(name, tag string, imageManifest *storage.SignedManifest) error
|
PutImageManifest(name, tag string, imageManifest *manifest.SignedManifest) error
|
||||||
|
|
||||||
// DeleteImage removes the image at the given name, tag pair.
|
// DeleteImage removes the image at the given name, tag pair.
|
||||||
DeleteImage(name, tag string) error
|
DeleteImage(name, tag string) error
|
||||||
|
@ -91,7 +91,7 @@ type clientImpl struct {
|
||||||
|
|
||||||
// TODO(bbland): use consistent route generation between server and client
|
// TODO(bbland): use consistent route generation between server and client
|
||||||
|
|
||||||
func (r *clientImpl) GetImageManifest(name, tag string) (*storage.SignedManifest, error) {
|
func (r *clientImpl) GetImageManifest(name, tag string) (*manifest.SignedManifest, error) {
|
||||||
manifestURL, err := r.ub.BuildManifestURL(name, tag)
|
manifestURL, err := r.ub.BuildManifestURL(name, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -124,7 +124,7 @@ func (r *clientImpl) GetImageManifest(name, tag string) (*storage.SignedManifest
|
||||||
|
|
||||||
decoder := json.NewDecoder(response.Body)
|
decoder := json.NewDecoder(response.Body)
|
||||||
|
|
||||||
manifest := new(storage.SignedManifest)
|
manifest := new(manifest.SignedManifest)
|
||||||
err = decoder.Decode(manifest)
|
err = decoder.Decode(manifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -132,7 +132,7 @@ func (r *clientImpl) GetImageManifest(name, tag string) (*storage.SignedManifest
|
||||||
return manifest, nil
|
return manifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *clientImpl) PutImageManifest(name, tag string, manifest *storage.SignedManifest) error {
|
func (r *clientImpl) PutImageManifest(name, tag string, manifest *manifest.SignedManifest) error {
|
||||||
manifestURL, err := r.ub.BuildManifestURL(name, tag)
|
manifestURL, err := r.ub.BuildManifestURL(name, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/distribution/common/testutil"
|
"github.com/docker/distribution/common/testutil"
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
"github.com/docker/distribution/storage"
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testBlob struct {
|
type testBlob struct {
|
||||||
|
@ -33,8 +33,8 @@ func TestPush(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
uploadLocations := make([]string, len(testBlobs))
|
uploadLocations := make([]string, len(testBlobs))
|
||||||
blobs := make([]storage.FSLayer, len(testBlobs))
|
blobs := make([]manifest.FSLayer, len(testBlobs))
|
||||||
history := make([]storage.ManifestHistory, len(testBlobs))
|
history := make([]manifest.History, len(testBlobs))
|
||||||
|
|
||||||
for i, blob := range testBlobs {
|
for i, blob := range testBlobs {
|
||||||
// TODO(bbland): this is returning the same location for all uploads,
|
// TODO(bbland): this is returning the same location for all uploads,
|
||||||
|
@ -42,24 +42,24 @@ func TestPush(t *testing.T) {
|
||||||
// It's sort of okay because we're using unique digests, but this needs
|
// It's sort of okay because we're using unique digests, but this needs
|
||||||
// to change at some point.
|
// to change at some point.
|
||||||
uploadLocations[i] = fmt.Sprintf("/v2/%s/blobs/test-uuid", name)
|
uploadLocations[i] = fmt.Sprintf("/v2/%s/blobs/test-uuid", name)
|
||||||
blobs[i] = storage.FSLayer{BlobSum: blob.digest}
|
blobs[i] = manifest.FSLayer{BlobSum: blob.digest}
|
||||||
history[i] = storage.ManifestHistory{V1Compatibility: blob.digest.String()}
|
history[i] = manifest.History{V1Compatibility: blob.digest.String()}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest := &storage.SignedManifest{
|
m := &manifest.SignedManifest{
|
||||||
Manifest: storage.Manifest{
|
Manifest: manifest.Manifest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Architecture: "x86",
|
Architecture: "x86",
|
||||||
FSLayers: blobs,
|
FSLayers: blobs,
|
||||||
History: history,
|
History: history,
|
||||||
Versioned: storage.Versioned{
|
Versioned: manifest.Versioned{
|
||||||
SchemaVersion: 1,
|
SchemaVersion: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
manifest.Raw, err = json.Marshal(manifest)
|
m.Raw, err = json.Marshal(m)
|
||||||
|
|
||||||
blobRequestResponseMappings := make([]testutil.RequestResponseMapping, 2*len(testBlobs))
|
blobRequestResponseMappings := make([]testutil.RequestResponseMapping, 2*len(testBlobs))
|
||||||
for i, blob := range testBlobs {
|
for i, blob := range testBlobs {
|
||||||
|
@ -94,7 +94,7 @@ func TestPush(t *testing.T) {
|
||||||
Request: testutil.Request{
|
Request: testutil.Request{
|
||||||
Method: "PUT",
|
Method: "PUT",
|
||||||
Route: "/v2/" + name + "/manifests/" + tag,
|
Route: "/v2/" + name + "/manifests/" + tag,
|
||||||
Body: manifest.Raw,
|
Body: m.Raw,
|
||||||
},
|
},
|
||||||
Response: testutil.Response{
|
Response: testutil.Response{
|
||||||
StatusCode: http.StatusOK,
|
StatusCode: http.StatusOK,
|
||||||
|
@ -119,7 +119,7 @@ func TestPush(t *testing.T) {
|
||||||
}
|
}
|
||||||
objectStore := &memoryObjectStore{
|
objectStore := &memoryObjectStore{
|
||||||
mutex: new(sync.Mutex),
|
mutex: new(sync.Mutex),
|
||||||
manifestStorage: make(map[string]*storage.SignedManifest),
|
manifestStorage: make(map[string]*manifest.SignedManifest),
|
||||||
layerStorage: make(map[digest.Digest]Layer),
|
layerStorage: make(map[digest.Digest]Layer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ func TestPush(t *testing.T) {
|
||||||
writer.Close()
|
writer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
objectStore.WriteManifest(name, tag, manifest)
|
objectStore.WriteManifest(name, tag, m)
|
||||||
|
|
||||||
err = Push(client, objectStore, name, tag)
|
err = Push(client, objectStore, name, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -160,27 +160,27 @@ func TestPull(t *testing.T) {
|
||||||
contents: []byte("some other contents"),
|
contents: []byte("some other contents"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
blobs := make([]storage.FSLayer, len(testBlobs))
|
blobs := make([]manifest.FSLayer, len(testBlobs))
|
||||||
history := make([]storage.ManifestHistory, len(testBlobs))
|
history := make([]manifest.History, len(testBlobs))
|
||||||
|
|
||||||
for i, blob := range testBlobs {
|
for i, blob := range testBlobs {
|
||||||
blobs[i] = storage.FSLayer{BlobSum: blob.digest}
|
blobs[i] = manifest.FSLayer{BlobSum: blob.digest}
|
||||||
history[i] = storage.ManifestHistory{V1Compatibility: blob.digest.String()}
|
history[i] = manifest.History{V1Compatibility: blob.digest.String()}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest := &storage.SignedManifest{
|
m := &manifest.SignedManifest{
|
||||||
Manifest: storage.Manifest{
|
Manifest: manifest.Manifest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Architecture: "x86",
|
Architecture: "x86",
|
||||||
FSLayers: blobs,
|
FSLayers: blobs,
|
||||||
History: history,
|
History: history,
|
||||||
Versioned: storage.Versioned{
|
Versioned: manifest.Versioned{
|
||||||
SchemaVersion: 1,
|
SchemaVersion: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
manifestBytes, err := json.Marshal(manifest)
|
manifestBytes, err := json.Marshal(m)
|
||||||
|
|
||||||
blobRequestResponseMappings := make([]testutil.RequestResponseMapping, len(testBlobs))
|
blobRequestResponseMappings := make([]testutil.RequestResponseMapping, len(testBlobs))
|
||||||
for i, blob := range testBlobs {
|
for i, blob := range testBlobs {
|
||||||
|
@ -213,7 +213,7 @@ func TestPull(t *testing.T) {
|
||||||
}
|
}
|
||||||
objectStore := &memoryObjectStore{
|
objectStore := &memoryObjectStore{
|
||||||
mutex: new(sync.Mutex),
|
mutex: new(sync.Mutex),
|
||||||
manifestStorage: make(map[string]*storage.SignedManifest),
|
manifestStorage: make(map[string]*manifest.SignedManifest),
|
||||||
layerStorage: make(map[digest.Digest]Layer),
|
layerStorage: make(map[digest.Digest]Layer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ func TestPull(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := objectStore.Manifest(name, tag)
|
m, err = objectStore.Manifest(name, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -272,25 +272,25 @@ func TestPullResume(t *testing.T) {
|
||||||
contents: []byte("some other contents"),
|
contents: []byte("some other contents"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
layers := make([]storage.FSLayer, len(testBlobs))
|
layers := make([]manifest.FSLayer, len(testBlobs))
|
||||||
history := make([]storage.ManifestHistory, len(testBlobs))
|
history := make([]manifest.History, len(testBlobs))
|
||||||
|
|
||||||
for i, layer := range testBlobs {
|
for i, layer := range testBlobs {
|
||||||
layers[i] = storage.FSLayer{BlobSum: layer.digest}
|
layers[i] = manifest.FSLayer{BlobSum: layer.digest}
|
||||||
history[i] = storage.ManifestHistory{V1Compatibility: layer.digest.String()}
|
history[i] = manifest.History{V1Compatibility: layer.digest.String()}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest := &storage.Manifest{
|
m := &manifest.Manifest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Architecture: "x86",
|
Architecture: "x86",
|
||||||
FSLayers: layers,
|
FSLayers: layers,
|
||||||
History: history,
|
History: history,
|
||||||
Versioned: storage.Versioned{
|
Versioned: manifest.Versioned{
|
||||||
SchemaVersion: 1,
|
SchemaVersion: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
manifestBytes, err := json.Marshal(manifest)
|
manifestBytes, err := json.Marshal(m)
|
||||||
|
|
||||||
layerRequestResponseMappings := make([]testutil.RequestResponseMapping, 2*len(testBlobs))
|
layerRequestResponseMappings := make([]testutil.RequestResponseMapping, 2*len(testBlobs))
|
||||||
for i, blob := range testBlobs {
|
for i, blob := range testBlobs {
|
||||||
|
@ -340,7 +340,7 @@ func TestPullResume(t *testing.T) {
|
||||||
}
|
}
|
||||||
objectStore := &memoryObjectStore{
|
objectStore := &memoryObjectStore{
|
||||||
mutex: new(sync.Mutex),
|
mutex: new(sync.Mutex),
|
||||||
manifestStorage: make(map[string]*storage.SignedManifest),
|
manifestStorage: make(map[string]*manifest.SignedManifest),
|
||||||
layerStorage: make(map[digest.Digest]Layer),
|
layerStorage: make(map[digest.Digest]Layer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,12 +355,12 @@ func TestPullResume(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := objectStore.Manifest(name, tag)
|
sm, err := objectStore.Manifest(name, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mBytes, err := json.Marshal(m)
|
mBytes, err := json.Marshal(sm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
"github.com/docker/distribution/storage"
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -26,11 +26,11 @@ var (
|
||||||
type ObjectStore interface {
|
type ObjectStore interface {
|
||||||
// Manifest retrieves the image manifest stored at the given repository name
|
// Manifest retrieves the image manifest stored at the given repository name
|
||||||
// and tag
|
// and tag
|
||||||
Manifest(name, tag string) (*storage.SignedManifest, error)
|
Manifest(name, tag string) (*manifest.SignedManifest, error)
|
||||||
|
|
||||||
// WriteManifest stores an image manifest at the given repository name and
|
// WriteManifest stores an image manifest at the given repository name and
|
||||||
// tag
|
// tag
|
||||||
WriteManifest(name, tag string, manifest *storage.SignedManifest) error
|
WriteManifest(name, tag string, manifest *manifest.SignedManifest) error
|
||||||
|
|
||||||
// Layer returns a handle to a layer for reading and writing
|
// Layer returns a handle to a layer for reading and writing
|
||||||
Layer(dgst digest.Digest) (Layer, error)
|
Layer(dgst digest.Digest) (Layer, error)
|
||||||
|
@ -83,11 +83,11 @@ type LayerWriter interface {
|
||||||
// memoryObjectStore is an in-memory implementation of the ObjectStore interface
|
// memoryObjectStore is an in-memory implementation of the ObjectStore interface
|
||||||
type memoryObjectStore struct {
|
type memoryObjectStore struct {
|
||||||
mutex *sync.Mutex
|
mutex *sync.Mutex
|
||||||
manifestStorage map[string]*storage.SignedManifest
|
manifestStorage map[string]*manifest.SignedManifest
|
||||||
layerStorage map[digest.Digest]Layer
|
layerStorage map[digest.Digest]Layer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (objStore *memoryObjectStore) Manifest(name, tag string) (*storage.SignedManifest, error) {
|
func (objStore *memoryObjectStore) Manifest(name, tag string) (*manifest.SignedManifest, error) {
|
||||||
objStore.mutex.Lock()
|
objStore.mutex.Lock()
|
||||||
defer objStore.mutex.Unlock()
|
defer objStore.mutex.Unlock()
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ func (objStore *memoryObjectStore) Manifest(name, tag string) (*storage.SignedMa
|
||||||
return manifest, nil
|
return manifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (objStore *memoryObjectStore) WriteManifest(name, tag string, manifest *storage.SignedManifest) error {
|
func (objStore *memoryObjectStore) WriteManifest(name, tag string, manifest *manifest.SignedManifest) error {
|
||||||
objStore.mutex.Lock()
|
objStore.mutex.Lock()
|
||||||
defer objStore.mutex.Unlock()
|
defer objStore.mutex.Unlock()
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/docker/distribution/storage"
|
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// simultaneousLayerPullWindow is the size of the parallel layer pull window.
|
// simultaneousLayerPullWindow is the size of the parallel layer pull window.
|
||||||
|
@ -77,7 +77,7 @@ func Pull(c Client, objectStore ObjectStore, name, tag string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pullLayer(c Client, objectStore ObjectStore, name string, fsLayer storage.FSLayer) error {
|
func pullLayer(c Client, objectStore ObjectStore, name string, fsLayer manifest.FSLayer) error {
|
||||||
log.WithField("layer", fsLayer).Info("Pulling layer")
|
log.WithField("layer", fsLayer).Info("Pulling layer")
|
||||||
|
|
||||||
layer, err := objectStore.Layer(fsLayer.BlobSum)
|
layer, err := objectStore.Layer(fsLayer.BlobSum)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/distribution/storage"
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// simultaneousLayerPushWindow is the size of the parallel layer push window.
|
// simultaneousLayerPushWindow is the size of the parallel layer push window.
|
||||||
|
@ -12,7 +12,7 @@ import (
|
||||||
// push window has been successfully pushed.
|
// push window has been successfully pushed.
|
||||||
const simultaneousLayerPushWindow = 4
|
const simultaneousLayerPushWindow = 4
|
||||||
|
|
||||||
type pushFunction func(fsLayer storage.FSLayer) error
|
type pushFunction func(fsLayer manifest.FSLayer) error
|
||||||
|
|
||||||
// Push implements a client push workflow for the image defined by the given
|
// Push implements a client push workflow for the image defined by the given
|
||||||
// name and tag pair, using the given ObjectStore for local manifest and layer
|
// name and tag pair, using the given ObjectStore for local manifest and layer
|
||||||
|
@ -71,7 +71,7 @@ func Push(c Client, objectStore ObjectStore, name, tag string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushLayer(c Client, objectStore ObjectStore, name string, fsLayer storage.FSLayer) error {
|
func pushLayer(c Client, objectStore ObjectStore, name string, fsLayer manifest.FSLayer) error {
|
||||||
log.WithField("layer", fsLayer).Info("Pushing layer")
|
log.WithField("layer", fsLayer).Info("Pushing layer")
|
||||||
|
|
||||||
layer, err := objectStore.Layer(fsLayer.BlobSum)
|
layer, err := objectStore.Layer(fsLayer.BlobSum)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/distribution/api/v2"
|
"github.com/docker/distribution/api/v2"
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/storage"
|
"github.com/docker/distribution/storage"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
)
|
)
|
||||||
|
@ -56,7 +57,7 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
|
||||||
manifests := imh.services.Manifests()
|
manifests := imh.services.Manifests()
|
||||||
dec := json.NewDecoder(r.Body)
|
dec := json.NewDecoder(r.Body)
|
||||||
|
|
||||||
var manifest storage.SignedManifest
|
var manifest manifest.SignedManifest
|
||||||
if err := dec.Decode(&manifest); err != nil {
|
if err := dec.Decode(&manifest); err != nil {
|
||||||
imh.Errors.Push(v2.ErrorCodeManifestInvalid, err)
|
imh.Errors.Push(v2.ErrorCodeManifestInvalid, err)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package storage
|
package manifest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Versioned provides a struct with just the manifest schemaVersion. Incoming
|
// Versioned provides a struct with just the manifest schemaVersion. Incoming
|
||||||
|
@ -36,65 +33,7 @@ type Manifest struct {
|
||||||
FSLayers []FSLayer `json:"fsLayers"`
|
FSLayers []FSLayer `json:"fsLayers"`
|
||||||
|
|
||||||
// History is a list of unstructured historical data for v1 compatibility
|
// History is a list of unstructured historical data for v1 compatibility
|
||||||
History []ManifestHistory `json:"history"`
|
History []History `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
|
// SignedManifest provides an envelope for a signed image manifest, including
|
||||||
|
@ -109,30 +48,6 @@ type SignedManifest struct {
|
||||||
Raw []byte `json:"-"`
|
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.
|
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
|
||||||
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||||
var manifest Manifest
|
var manifest Manifest
|
||||||
|
@ -149,7 +64,7 @@ func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||||
|
|
||||||
// MarshalJSON returns the contents of raw. If Raw is nil, marshals the inner
|
// MarshalJSON returns the contents of raw. If Raw is nil, marshals the inner
|
||||||
// contents. Applications requiring a marshaled signed manifest should simply
|
// contents. Applications requiring a marshaled signed manifest should simply
|
||||||
// use Raw directly, since the the content produced by json.Marshal will
|
// use Raw directly, since the the content produced by json.Marshal will be
|
||||||
// compacted and will fail signature checks.
|
// compacted and will fail signature checks.
|
||||||
func (sm *SignedManifest) MarshalJSON() ([]byte, error) {
|
func (sm *SignedManifest) MarshalJSON() ([]byte, error) {
|
||||||
if len(sm.Raw) > 0 {
|
if len(sm.Raw) > 0 {
|
||||||
|
@ -166,8 +81,8 @@ type FSLayer struct {
|
||||||
BlobSum digest.Digest `json:"blobSum"`
|
BlobSum digest.Digest `json:"blobSum"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestHistory stores unstructured v1 compatibility information
|
// History stores unstructured v1 compatibility information
|
||||||
type ManifestHistory struct {
|
type History struct {
|
||||||
// V1Compatibility is the raw v1 compatibility information
|
// V1Compatibility is the raw v1 compatibility information
|
||||||
V1Compatibility string `json:"v1Compatibility"`
|
V1Compatibility string `json:"v1Compatibility"`
|
||||||
}
|
}
|
110
manifest/manifest_test.go
Normal file
110
manifest/manifest_test.go
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testEnv struct {
|
||||||
|
name, tag string
|
||||||
|
manifest *Manifest
|
||||||
|
signed *SignedManifest
|
||||||
|
pk libtrust.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManifestMarshaling(t *testing.T) {
|
||||||
|
env := genEnv(t)
|
||||||
|
|
||||||
|
// Check that the Raw field is the same as json.MarshalIndent with these
|
||||||
|
// parameters.
|
||||||
|
p, err := json.MarshalIndent(env.signed, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error marshaling manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(p, env.signed.Raw) {
|
||||||
|
t.Fatalf("manifest bytes not equal: %q != %q", string(env.signed.Raw), string(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManifestUnmarshaling(t *testing.T) {
|
||||||
|
env := genEnv(t)
|
||||||
|
|
||||||
|
var signed SignedManifest
|
||||||
|
if err := json.Unmarshal(env.signed.Raw, &signed); err != nil {
|
||||||
|
t.Fatalf("error unmarshaling signed manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(&signed, env.signed) {
|
||||||
|
t.Fatalf("manifests are different after unmarshaling: %v != %v", signed, env.signed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManifestVerification(t *testing.T) {
|
||||||
|
env := genEnv(t)
|
||||||
|
|
||||||
|
publicKeys, err := Verify(env.signed)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error verifying manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(publicKeys) == 0 {
|
||||||
|
t.Fatalf("no public keys found in signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
publicKey := env.pk.PublicKey()
|
||||||
|
// ensure that one of the extracted public keys matches the private key.
|
||||||
|
for _, candidate := range publicKeys {
|
||||||
|
if candidate.KeyID() == publicKey.KeyID() {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("expected public key, %v, not found in verified keys: %v", publicKey, publicKeys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genEnv(t *testing.T) *testEnv {
|
||||||
|
pk, err := libtrust.GenerateECP256PrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error generating test key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
name, tag := "foo/bar", "test"
|
||||||
|
|
||||||
|
m := Manifest{
|
||||||
|
Versioned: Versioned{
|
||||||
|
SchemaVersion: 1,
|
||||||
|
},
|
||||||
|
Name: name,
|
||||||
|
Tag: tag,
|
||||||
|
FSLayers: []FSLayer{
|
||||||
|
{
|
||||||
|
BlobSum: "asdf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlobSum: "qwer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sm, err := Sign(&m, pk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error signing manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &testEnv{
|
||||||
|
name: name,
|
||||||
|
tag: tag,
|
||||||
|
manifest: &m,
|
||||||
|
signed: sm,
|
||||||
|
pk: pk,
|
||||||
|
}
|
||||||
|
}
|
66
manifest/sign.go
Normal file
66
manifest/sign.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 Sign(m *Manifest, 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 SignWithChain(m *Manifest, 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
|
||||||
|
}
|
32
manifest/verify.go
Normal file
32
manifest/verify.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify verifies the signature of the signed manifest returning the public
|
||||||
|
// keys used during signing.
|
||||||
|
func Verify(sm *SignedManifest) ([]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 VerifyChains(sm *SignedManifest, ca *x509.CertPool) ([][]*x509.Certificate, error) {
|
||||||
|
js, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return js.VerifyChains(ca)
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Layer provides a readable and seekable layer object. Typically,
|
// Layer provides a readable and seekable layer object. Typically,
|
||||||
|
@ -74,7 +75,7 @@ var (
|
||||||
|
|
||||||
// ErrUnknownLayer returned when layer cannot be found.
|
// ErrUnknownLayer returned when layer cannot be found.
|
||||||
type ErrUnknownLayer struct {
|
type ErrUnknownLayer struct {
|
||||||
FSLayer FSLayer
|
FSLayer manifest.FSLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err ErrUnknownLayer) Error() string {
|
func (err ErrUnknownLayer) Error() string {
|
||||||
|
@ -83,7 +84,7 @@ func (err ErrUnknownLayer) Error() string {
|
||||||
|
|
||||||
// ErrLayerInvalidDigest returned when tarsum check fails.
|
// ErrLayerInvalidDigest returned when tarsum check fails.
|
||||||
type ErrLayerInvalidDigest struct {
|
type ErrLayerInvalidDigest struct {
|
||||||
FSLayer FSLayer
|
FSLayer manifest.FSLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err ErrLayerInvalidDigest) Error() string {
|
func (err ErrLayerInvalidDigest) Error() string {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/storagedriver"
|
"github.com/docker/distribution/storagedriver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ func (ls *layerStore) Fetch(name string, digest digest.Digest) (Layer, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case storagedriver.PathNotFoundError, *storagedriver.PathNotFoundError:
|
case storagedriver.PathNotFoundError, *storagedriver.PathNotFoundError:
|
||||||
return nil, ErrUnknownLayer{FSLayer{BlobSum: digest}}
|
return nil, ErrUnknownLayer{manifest.FSLayer{BlobSum: digest}}
|
||||||
default:
|
default:
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -43,7 +44,7 @@ func (ls *layerStore) Fetch(name string, digest digest.Digest) (Layer, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case storagedriver.PathNotFoundError, *storagedriver.PathNotFoundError:
|
case storagedriver.PathNotFoundError, *storagedriver.PathNotFoundError:
|
||||||
return nil, ErrUnknownLayer{FSLayer{BlobSum: digest}}
|
return nil, ErrUnknownLayer{manifest.FSLayer{BlobSum: digest}}
|
||||||
default:
|
default:
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"code.google.com/p/go-uuid/uuid"
|
"code.google.com/p/go-uuid/uuid"
|
||||||
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/storagedriver"
|
"github.com/docker/distribution/storagedriver"
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
"github.com/docker/docker/pkg/tarsum"
|
||||||
|
|
||||||
|
@ -284,7 +285,7 @@ func (luc *layerUploadController) validateLayer(fp layerFile, size int64, dgst d
|
||||||
}
|
}
|
||||||
|
|
||||||
if !digestVerifier.Verified() {
|
if !digestVerifier.Verified() {
|
||||||
return "", ErrLayerInvalidDigest{FSLayer{BlobSum: dgst}}
|
return "", ErrLayerInvalidDigest{manifest.FSLayer{BlobSum: dgst}}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dgst, nil
|
return dgst, nil
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/storagedriver"
|
"github.com/docker/distribution/storagedriver"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
)
|
)
|
||||||
|
@ -116,7 +117,7 @@ func (ms *manifestStore) Exists(name, tag string) (bool, error) {
|
||||||
return true, nil
|
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)
|
p, err := ms.path(name, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if err := json.Unmarshal(content, &manifest); err != nil {
|
||||||
// TODO(stevvooe): Corrupted manifest error?
|
// TODO(stevvooe): Corrupted manifest error?
|
||||||
|
@ -144,7 +145,7 @@ func (ms *manifestStore) Get(name, tag string) (*SignedManifest, error) {
|
||||||
return &manifest, nil
|
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)
|
p, err := ms.path(name, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -185,19 +186,19 @@ 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, mnfst *manifest.SignedManifest) error {
|
||||||
// TODO(stevvooe): This verification is present here, but this needs to be
|
// TODO(stevvooe): This verification is present here, but this needs to be
|
||||||
// lifted out of the storage infrastructure and moved into a package
|
// lifted out of the storage infrastructure and moved into a package
|
||||||
// oriented towards defining verifiers and reporting them with
|
// oriented towards defining verifiers and reporting them with
|
||||||
// granularity.
|
// granularity.
|
||||||
|
|
||||||
var errs ErrManifestVerification
|
var errs ErrManifestVerification
|
||||||
if manifest.Name != name {
|
if mnfst.Name != name {
|
||||||
// TODO(stevvooe): This needs to be an exported error
|
// TODO(stevvooe): This needs to be an exported error
|
||||||
errs = append(errs, fmt.Errorf("name does not match manifest name"))
|
errs = append(errs, fmt.Errorf("name does not match manifest name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if manifest.Tag != tag {
|
if mnfst.Tag != tag {
|
||||||
// TODO(stevvooe): This needs to be an exported error.
|
// TODO(stevvooe): This needs to be an exported error.
|
||||||
errs = append(errs, fmt.Errorf("tag does not match manifest tag"))
|
errs = append(errs, fmt.Errorf("tag does not match manifest tag"))
|
||||||
}
|
}
|
||||||
|
@ -206,7 +207,7 @@ func (ms *manifestStore) verifyManifest(name, tag string, manifest *SignedManife
|
||||||
// VerifyWithChains. We need to define the exact source of the CA.
|
// VerifyWithChains. We need to define the exact source of the CA.
|
||||||
// Perhaps, its a configuration value injected into manifest store.
|
// Perhaps, its a configuration value injected into manifest store.
|
||||||
|
|
||||||
if _, err := manifest.Verify(); err != nil {
|
if _, err := manifest.Verify(mnfst); err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey:
|
case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey:
|
||||||
errs = append(errs, ErrManifestUnverified{})
|
errs = append(errs, ErrManifestUnverified{})
|
||||||
|
@ -219,7 +220,7 @@ func (ms *manifestStore) verifyManifest(name, tag string, manifest *SignedManife
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fsLayer := range manifest.FSLayers {
|
for _, fsLayer := range mnfst.FSLayers {
|
||||||
exists, err := ms.layerService.Exists(name, fsLayer.BlobSum)
|
exists, err := ms.layerService.Exists(name, fsLayer.BlobSum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/libtrust"
|
|
||||||
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/storagedriver/inmemory"
|
"github.com/docker/distribution/storagedriver/inmemory"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestManifestStorage(t *testing.T) {
|
func TestManifestStorage(t *testing.T) {
|
||||||
|
@ -42,13 +42,13 @@ func TestManifestStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest := Manifest{
|
m := manifest.Manifest{
|
||||||
Versioned: Versioned{
|
Versioned: manifest.Versioned{
|
||||||
SchemaVersion: 1,
|
SchemaVersion: 1,
|
||||||
},
|
},
|
||||||
Name: name,
|
Name: name,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
FSLayers: []FSLayer{
|
FSLayers: []manifest.FSLayer{
|
||||||
{
|
{
|
||||||
BlobSum: "asdf",
|
BlobSum: "asdf",
|
||||||
},
|
},
|
||||||
|
@ -63,7 +63,7 @@ func TestManifestStorage(t *testing.T) {
|
||||||
t.Fatalf("unexpected error generating private key: %v", err)
|
t.Fatalf("unexpected error generating private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sm, err := manifest.Sign(pk)
|
sm, err := manifest.Sign(&m, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error signing manifest: %v", err)
|
t.Fatalf("error signing manifest: %v", err)
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/storagedriver"
|
"github.com/docker/distribution/storagedriver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,10 +60,10 @@ type ManifestService interface {
|
||||||
Exists(name, tag string) (bool, error)
|
Exists(name, tag string) (bool, error)
|
||||||
|
|
||||||
// Get retrieves the named manifest, if it exists.
|
// 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 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 removes the named manifest, if it exists.
|
||||||
Delete(name, tag string) error
|
Delete(name, tag string) error
|
||||||
|
|
Loading…
Reference in a new issue