4decfaa82e
This change implements the first pass at image manifest storage on top of the storagedriver. Very similar to LayerService, its much simpler due to less complexity of pushing and pulling images. Various components are still missing, such as detailed error reporting on missing layers during verification, but the base functionality is present.
134 lines
2.8 KiB
Go
134 lines
2.8 KiB
Go
package storage
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/docker/libtrust"
|
|
|
|
"github.com/docker/docker-registry/storagedriver"
|
|
)
|
|
|
|
type manifestStore struct {
|
|
driver storagedriver.StorageDriver
|
|
pathMapper *pathMapper
|
|
layerService LayerService
|
|
}
|
|
|
|
var _ ManifestService = &manifestStore{}
|
|
|
|
func (ms *manifestStore) Exists(name, tag string) (bool, error) {
|
|
p, err := ms.path(name, tag)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
size, err := ms.driver.CurrentSize(p)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if size == 0 {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (ms *manifestStore) Get(name, tag string) (*SignedManifest, error) {
|
|
p, err := ms.path(name, tag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
content, err := ms.driver.GetContent(p)
|
|
if err != nil {
|
|
switch err := err.(type) {
|
|
case storagedriver.PathNotFoundError, *storagedriver.PathNotFoundError:
|
|
return nil, ErrManifestUnknown
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
var manifest SignedManifest
|
|
|
|
if err := json.Unmarshal(content, &manifest); err != nil {
|
|
// TODO(stevvooe): Corrupted manifest error?
|
|
return nil, err
|
|
}
|
|
|
|
// TODO(stevvooe): Verify the manifest here?
|
|
|
|
return &manifest, nil
|
|
}
|
|
|
|
func (ms *manifestStore) Put(name, tag string, manifest *SignedManifest) error {
|
|
p, err := ms.path(name, tag)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := ms.verifyManifest(name, tag, manifest); err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO(stevvooe): Should we get manifest first?
|
|
|
|
return ms.driver.PutContent(p, manifest.Raw)
|
|
}
|
|
|
|
func (ms *manifestStore) Delete(name, tag string) error {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (ms *manifestStore) path(name, tag string) (string, error) {
|
|
return ms.pathMapper.path(manifestPathSpec{
|
|
name: name,
|
|
tag: tag,
|
|
})
|
|
}
|
|
|
|
func (ms *manifestStore) verifyManifest(name, tag string, manifest *SignedManifest) error {
|
|
if manifest.Name != name {
|
|
return fmt.Errorf("name does not match manifest name")
|
|
}
|
|
|
|
if manifest.Tag != tag {
|
|
return fmt.Errorf("tag does not match manifest tag")
|
|
}
|
|
|
|
var errs []error
|
|
|
|
for _, fsLayer := range manifest.FSLayers {
|
|
exists, err := ms.layerService.Exists(name, fsLayer.BlobSum)
|
|
if err != nil {
|
|
// TODO(stevvooe): Need to store information about missing blob.
|
|
errs = append(errs, err)
|
|
}
|
|
|
|
if !exists {
|
|
errs = append(errs, fmt.Errorf("missing layer %v", fsLayer.BlobSum))
|
|
}
|
|
}
|
|
|
|
if len(errs) != 0 {
|
|
// TODO(stevvooe): These need to be recoverable by a caller.
|
|
return fmt.Errorf("missing layers: %v", errs)
|
|
}
|
|
|
|
js, err := libtrust.ParsePrettySignature(manifest.Raw, "signatures")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = js.Verify() // These pubkeys need to be checked.
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO(sday): Pubkey checks need to go here. This where things get fancy.
|
|
// Perhaps, an injected service would reduce coupling here.
|
|
|
|
return nil
|
|
}
|