ba6b774aea
To smooth initial implementation, uploads were spooled to local file storage, validated, then pushed to remote storage. That approach was flawed in that it present easy clustering of registry services that share a remote storage backend. The original plan was to implement resumable hashes then implement remote upload storage. After some thought, it was found to be better to get remote spooling working, then optimize with resumable hashes. Moving to this approach has tradeoffs: after storing the complete upload remotely, the node must fetch the content and validate it before moving it to the final location. This can double bandwidth usage to the remote backend. Modifying the verification and upload code to store intermediate hashes should be trivial once the layer digest format has settled. The largest changes for users of the storage package (mostly the registry app) are the LayerService interface and the LayerUpload interface. The LayerService now takes qualified repository names to start and resume uploads. In corallry, the concept of LayerUploadState has been complete removed, exposing all aspects of that state as part of the LayerUpload object. The LayerUpload object has been modified to work as an io.WriteSeeker and includes a StartedAt time, to allow for upload timeout policies. Finish now only requires a digest, eliding the requirement for a size parameter. Resource cleanup has taken a turn for the better. Resources are cleaned up after successful uploads and during a cancel call. Admittedly, this is probably not completely where we want to be. It's recommend that we bolster this with a periodic driver utility script that scans for partial uploads and deletes the underlying data. As a small benefit, we can leave these around to better understand how and why these uploads are failing, at the cost of some extra disk space. Many other changes follow from the changes above. The webapp needs to be updated to meet the new interface requirements. Signed-off-by: Stephen J Day <stephen.day@docker.com>
76 lines
2.5 KiB
Go
76 lines
2.5 KiB
Go
package storage
|
|
|
|
import (
|
|
"github.com/docker/distribution/digest"
|
|
"github.com/docker/distribution/manifest"
|
|
"github.com/docker/distribution/storagedriver"
|
|
)
|
|
|
|
// Services provides various services with application-level operations for
|
|
// use across backend storage drivers.
|
|
type Services struct {
|
|
driver storagedriver.StorageDriver
|
|
pathMapper *pathMapper
|
|
}
|
|
|
|
// NewServices creates a new Services object to access docker objects stored
|
|
// in the underlying driver.
|
|
func NewServices(driver storagedriver.StorageDriver) *Services {
|
|
|
|
return &Services{
|
|
driver: driver,
|
|
// TODO(sday): This should be configurable.
|
|
pathMapper: defaultPathMapper,
|
|
}
|
|
}
|
|
|
|
// Layers returns an instance of the LayerService. Instantiation is cheap and
|
|
// may be context sensitive in the future. The instance should be used similar
|
|
// to a request local.
|
|
func (ss *Services) Layers() LayerService {
|
|
return &layerStore{driver: ss.driver, pathMapper: ss.pathMapper}
|
|
}
|
|
|
|
// Manifests returns an instance of ManifestService. Instantiation is cheap and
|
|
// may be context sensitive in the future. The instance should be used similar
|
|
// to a request local.
|
|
func (ss *Services) Manifests() ManifestService {
|
|
return &manifestStore{driver: ss.driver, pathMapper: ss.pathMapper, layerService: ss.Layers()}
|
|
}
|
|
|
|
// ManifestService provides operations on image manifests.
|
|
type ManifestService interface {
|
|
// Tags lists the tags under the named repository.
|
|
Tags(name string) ([]string, error)
|
|
|
|
// Exists returns true if the layer exists.
|
|
Exists(name, tag string) (bool, error)
|
|
|
|
// Get retrieves the named manifest, if it exists.
|
|
Get(name, tag string) (*manifest.SignedManifest, error)
|
|
|
|
// Put creates or updates the named manifest.
|
|
Put(name, tag string, manifest *manifest.SignedManifest) error
|
|
|
|
// Delete removes the named manifest, if it exists.
|
|
Delete(name, tag string) error
|
|
}
|
|
|
|
// LayerService provides operations on layer files in a backend storage.
|
|
type LayerService interface {
|
|
// Exists returns true if the layer exists.
|
|
Exists(name string, digest digest.Digest) (bool, error)
|
|
|
|
// Fetch the layer identifed by TarSum.
|
|
Fetch(name string, digest digest.Digest) (Layer, error)
|
|
|
|
// Upload begins a layer upload to repository identified by name,
|
|
// returning a handle.
|
|
Upload(name string) (LayerUpload, error)
|
|
|
|
// Resume continues an in progress layer upload, returning a handle to the
|
|
// upload. The caller should seek to the latest desired upload location
|
|
// before proceeding.
|
|
Resume(name, uuid string) (LayerUpload, error)
|
|
}
|