d9d84ae269
This changeset integrates context with the storage package. Debug messages have been added to exported methods. Existing log messages will now include contextual details through logger fields to aid in debugging. This integration focuses on logging and may be followed up with a metric-oriented change in the future. 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"
|
|
"github.com/docker/distribution/storagedriver"
|
|
)
|
|
|
|
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
|
|
}
|