6e4f9a2e3e
This change is slightly more complex than previous package maves in that the package name changed. To address this, we simply always reference the package driver as storagedriver to avoid compatbility issues with existing code. While unfortunate, this can be cleaned up over time. Signed-off-by: Stephen J Day <stephen.day@docker.com>
168 lines
4 KiB
Go
168 lines
4 KiB
Go
package storage
|
|
|
|
import (
|
|
"time"
|
|
|
|
"code.google.com/p/go-uuid/uuid"
|
|
ctxu "github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/digest"
|
|
"github.com/docker/distribution/manifest"
|
|
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
|
)
|
|
|
|
type layerStore struct {
|
|
repository *repository
|
|
}
|
|
|
|
func (ls *layerStore) Exists(digest digest.Digest) (bool, error) {
|
|
ctxu.GetLogger(ls.repository.ctx).Debug("(*layerStore).Exists")
|
|
|
|
// Because this implementation just follows blob links, an existence check
|
|
// is pretty cheap by starting and closing a fetch.
|
|
_, err := ls.Fetch(digest)
|
|
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case ErrUnknownLayer:
|
|
return false, nil
|
|
}
|
|
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (ls *layerStore) Fetch(dgst digest.Digest) (Layer, error) {
|
|
ctxu.GetLogger(ls.repository.ctx).Debug("(*layerStore).Fetch")
|
|
bp, err := ls.path(dgst)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fr, err := newFileReader(ls.repository.driver, bp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &layerReader{
|
|
fileReader: *fr,
|
|
name: ls.repository.Name(),
|
|
digest: dgst,
|
|
}, nil
|
|
}
|
|
|
|
// Upload begins a layer upload, returning a handle. If the layer upload
|
|
// is already in progress or the layer has already been uploaded, this
|
|
// will return an error.
|
|
func (ls *layerStore) Upload() (LayerUpload, error) {
|
|
ctxu.GetLogger(ls.repository.ctx).Debug("(*layerStore).Upload")
|
|
|
|
// NOTE(stevvooe): Consider the issues with allowing concurrent upload of
|
|
// the same two layers. Should it be disallowed? For now, we allow both
|
|
// parties to proceed and the the first one uploads the layer.
|
|
|
|
uuid := uuid.New()
|
|
startedAt := time.Now().UTC()
|
|
|
|
path, err := ls.repository.registry.pm.path(uploadDataPathSpec{
|
|
name: ls.repository.Name(),
|
|
uuid: uuid,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
startedAtPath, err := ls.repository.registry.pm.path(uploadStartedAtPathSpec{
|
|
name: ls.repository.Name(),
|
|
uuid: uuid,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Write a startedat file for this upload
|
|
if err := ls.repository.driver.PutContent(startedAtPath, []byte(startedAt.Format(time.RFC3339))); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ls.newLayerUpload(uuid, path, startedAt)
|
|
}
|
|
|
|
// Resume continues an in progress layer upload, returning the current
|
|
// state of the upload.
|
|
func (ls *layerStore) Resume(uuid string) (LayerUpload, error) {
|
|
ctxu.GetLogger(ls.repository.ctx).Debug("(*layerStore).Resume")
|
|
startedAtPath, err := ls.repository.registry.pm.path(uploadStartedAtPathSpec{
|
|
name: ls.repository.Name(),
|
|
uuid: uuid,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
startedAtBytes, err := ls.repository.driver.GetContent(startedAtPath)
|
|
if err != nil {
|
|
switch err := err.(type) {
|
|
case storagedriver.PathNotFoundError:
|
|
return nil, ErrLayerUploadUnknown
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
startedAt, err := time.Parse(time.RFC3339, string(startedAtBytes))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
path, err := ls.repository.pm.path(uploadDataPathSpec{
|
|
name: ls.repository.Name(),
|
|
uuid: uuid,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ls.newLayerUpload(uuid, path, startedAt)
|
|
}
|
|
|
|
// newLayerUpload allocates a new upload controller with the given state.
|
|
func (ls *layerStore) newLayerUpload(uuid, path string, startedAt time.Time) (LayerUpload, error) {
|
|
fw, err := newFileWriter(ls.repository.driver, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &layerUploadController{
|
|
layerStore: ls,
|
|
uuid: uuid,
|
|
startedAt: startedAt,
|
|
fileWriter: *fw,
|
|
}, nil
|
|
}
|
|
|
|
func (ls *layerStore) path(dgst digest.Digest) (string, error) {
|
|
// We must traverse this path through the link to enforce ownership.
|
|
layerLinkPath, err := ls.repository.registry.pm.path(layerLinkPathSpec{name: ls.repository.Name(), digest: dgst})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
blobPath, err := ls.repository.blobStore.resolve(layerLinkPath)
|
|
|
|
if err != nil {
|
|
switch err := err.(type) {
|
|
case storagedriver.PathNotFoundError:
|
|
return "", ErrUnknownLayer{manifest.FSLayer{BlobSum: dgst}}
|
|
default:
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
return blobPath, nil
|
|
}
|