2637e29e18
This change contains the initial implementation of the LayerService to power layer push and pulls on the storagedriver. The interfaces presented in this package will be used by the http application to drive most features around efficient pulls and resumable pushes. The file storage/layer.go defines the interface interactions. LayerService is the root type and supports methods to access Layer and LayerUpload objects. Pull operations are supported with LayerService.Fetch and push operations are supported with LayerService.Upload and LayerService.Resume. Reads and writes of layers are split between Layer and LayerUpload, respectively. LayerService is implemented internally with the layerStore object, which takes a storagedriver.StorageDriver and a pathMapper instance. LayerUploadState is currently exported and will likely continue to be as the interaction between it and layerUploadStore are better understood. Likely, the layerUploadStore lifecycle and implementation will be deferred to the application. Image pushes pulls will be implemented in a similar manner without the discrete, persistent upload. Much of this change is in place to get something running and working. Caveats of this change include the following: 1. Layer upload state storage is implemented on the local filesystem, separate from the storage driver. This must be replaced with using the proper backend and other state storage. This can be removed when we implement resumable hashing and tarsum calculations to avoid backend roundtrips. 2. Error handling is rather bespoke at this time. The http API implementation should really dictate the error return structure for the future, so we intend to refactor this heavily to support these errors. We'd also like to collect production data to understand how failures happen in the system as a while before moving to a particular edict around error handling. 3. The layerUploadStore, which manages layer upload storage and state is not currently exported. This will likely end up being split, with the file management portion being pointed at the storagedriver and the state storage elsewhere. 4. Access Control provisions are nearly completely missing from this change. There are details around how layerindex lookup works that are related with access controls. As the auth portions of the new API take shape, these provisions will become more clear. Please see TODOs for details and individual recommendations.
96 lines
3.1 KiB
Go
96 lines
3.1 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
)
|
|
|
|
// LayerService provides operations on layer files in a backend storage.
|
|
type LayerService interface {
|
|
// Exists returns true if the layer exists.
|
|
Exists(tarSum string) (bool, error)
|
|
|
|
// Fetch the layer identifed by TarSum.
|
|
Fetch(tarSum string) (Layer, error)
|
|
|
|
// 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.
|
|
Upload(name, tarSum string) (LayerUpload, error)
|
|
|
|
// Resume continues an in progress layer upload, returning the current
|
|
// state of the upload.
|
|
Resume(name, tarSum, uuid string) (LayerUpload, error)
|
|
}
|
|
|
|
// Layer provides a readable and seekable layer object. Typically,
|
|
// implementations are *not* goroutine safe.
|
|
type Layer interface {
|
|
// http.ServeContent requires an efficient implementation of
|
|
// ReadSeeker.Seek(0, os.SEEK_END).
|
|
io.ReadSeeker
|
|
io.Closer
|
|
|
|
// Name returns the repository under which this layer is linked.
|
|
Name() string // TODO(stevvooe): struggling with nomenclature: should this be "repo" or "name"?
|
|
|
|
// TarSum returns the unique tarsum of the layer.
|
|
TarSum() string
|
|
|
|
// CreatedAt returns the time this layer was created. Until we implement
|
|
// Stat call on storagedriver, this just returns the zero time.
|
|
CreatedAt() time.Time
|
|
}
|
|
|
|
// LayerUpload provides a handle for working with in-progress uploads.
|
|
// Instances can be obtained from the LayerService.Upload and
|
|
// LayerService.Resume.
|
|
type LayerUpload interface {
|
|
io.WriteCloser
|
|
|
|
// UUID returns the identifier for this upload.
|
|
UUID() string
|
|
|
|
// Name of the repository under which the layer will be linked.
|
|
Name() string
|
|
|
|
// TarSum identifier of the proposed layer. Resulting data must match this
|
|
// tarsum.
|
|
TarSum() string
|
|
|
|
// Offset returns the position of the last byte written to this layer.
|
|
Offset() int64
|
|
|
|
// Finish marks the upload as completed, returning a valid handle to the
|
|
// uploaded layer. The final size and checksum are validated against the
|
|
// contents of the uploaded layer. The checksum should be provided in the
|
|
// format <algorithm>:<hex digest>.
|
|
Finish(size int64, digest string) (Layer, error)
|
|
|
|
// Cancel the layer upload process.
|
|
Cancel() error
|
|
}
|
|
|
|
var (
|
|
// ErrLayerUnknown returned when layer cannot be found.
|
|
ErrLayerUnknown = fmt.Errorf("unknown layer")
|
|
|
|
// ErrLayerExists returned when layer already exists
|
|
ErrLayerExists = fmt.Errorf("layer exists")
|
|
|
|
// ErrLayerTarSumVersionUnsupported when tarsum is unsupported version.
|
|
ErrLayerTarSumVersionUnsupported = fmt.Errorf("unsupported tarsum version")
|
|
|
|
// ErrLayerUploadUnknown returned when upload is not found.
|
|
ErrLayerUploadUnknown = fmt.Errorf("layer upload unknown")
|
|
|
|
// ErrLayerInvalidChecksum returned when checksum/digest check fails.
|
|
ErrLayerInvalidChecksum = fmt.Errorf("invalid layer checksum")
|
|
|
|
// ErrLayerInvalidTarsum returned when tarsum check fails.
|
|
ErrLayerInvalidTarsum = fmt.Errorf("invalid layer tarsum")
|
|
|
|
// ErrLayerInvalidLength returned when length check fails.
|
|
ErrLayerInvalidLength = fmt.Errorf("invalid layer length")
|
|
)
|