forked from TrueCloudLab/distribution
9c88801a12
Back in the before time, the best practices surrounding usage of Context weren't quite worked out. We defined our own type to make usage easier. As this packaged was used elsewhere, it make it more and more challenging to integrate with the forked `Context` type. Now that it is available in the standard library, we can just use that one directly. To make usage more consistent, we now use `dcontext` when referring to the distribution context package. Signed-off-by: Stephen J Day <stephen.day@docker.com>
257 lines
9.4 KiB
Go
257 lines
9.4 KiB
Go
package distribution
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
var (
|
|
// ErrBlobExists returned when blob already exists
|
|
ErrBlobExists = errors.New("blob exists")
|
|
|
|
// ErrBlobDigestUnsupported when blob digest is an unsupported version.
|
|
ErrBlobDigestUnsupported = errors.New("unsupported blob digest")
|
|
|
|
// ErrBlobUnknown when blob is not found.
|
|
ErrBlobUnknown = errors.New("unknown blob")
|
|
|
|
// ErrBlobUploadUnknown returned when upload is not found.
|
|
ErrBlobUploadUnknown = errors.New("blob upload unknown")
|
|
|
|
// ErrBlobInvalidLength returned when the blob has an expected length on
|
|
// commit, meaning mismatched with the descriptor or an invalid value.
|
|
ErrBlobInvalidLength = errors.New("blob invalid length")
|
|
)
|
|
|
|
// ErrBlobInvalidDigest returned when digest check fails.
|
|
type ErrBlobInvalidDigest struct {
|
|
Digest digest.Digest
|
|
Reason error
|
|
}
|
|
|
|
func (err ErrBlobInvalidDigest) Error() string {
|
|
return fmt.Sprintf("invalid digest for referenced layer: %v, %v",
|
|
err.Digest, err.Reason)
|
|
}
|
|
|
|
// ErrBlobMounted returned when a blob is mounted from another repository
|
|
// instead of initiating an upload session.
|
|
type ErrBlobMounted struct {
|
|
From reference.Canonical
|
|
Descriptor Descriptor
|
|
}
|
|
|
|
func (err ErrBlobMounted) Error() string {
|
|
return fmt.Sprintf("blob mounted from: %v to: %v",
|
|
err.From, err.Descriptor)
|
|
}
|
|
|
|
// Descriptor describes targeted content. Used in conjunction with a blob
|
|
// store, a descriptor can be used to fetch, store and target any kind of
|
|
// blob. The struct also describes the wire protocol format. Fields should
|
|
// only be added but never changed.
|
|
type Descriptor struct {
|
|
// MediaType describe the type of the content. All text based formats are
|
|
// encoded as utf-8.
|
|
MediaType string `json:"mediaType,omitempty"`
|
|
|
|
// Size in bytes of content.
|
|
Size int64 `json:"size,omitempty"`
|
|
|
|
// Digest uniquely identifies the content. A byte stream can be verified
|
|
// against against this digest.
|
|
Digest digest.Digest `json:"digest,omitempty"`
|
|
|
|
// URLs contains the source URLs of this content.
|
|
URLs []string `json:"urls,omitempty"`
|
|
|
|
// NOTE: Before adding a field here, please ensure that all
|
|
// other options have been exhausted. Much of the type relationships
|
|
// depend on the simplicity of this type.
|
|
}
|
|
|
|
// Descriptor returns the descriptor, to make it satisfy the Describable
|
|
// interface. Note that implementations of Describable are generally objects
|
|
// which can be described, not simply descriptors; this exception is in place
|
|
// to make it more convenient to pass actual descriptors to functions that
|
|
// expect Describable objects.
|
|
func (d Descriptor) Descriptor() Descriptor {
|
|
return d
|
|
}
|
|
|
|
// BlobStatter makes blob descriptors available by digest. The service may
|
|
// provide a descriptor of a different digest if the provided digest is not
|
|
// canonical.
|
|
type BlobStatter interface {
|
|
// Stat provides metadata about a blob identified by the digest. If the
|
|
// blob is unknown to the describer, ErrBlobUnknown will be returned.
|
|
Stat(ctx context.Context, dgst digest.Digest) (Descriptor, error)
|
|
}
|
|
|
|
// BlobDeleter enables deleting blobs from storage.
|
|
type BlobDeleter interface {
|
|
Delete(ctx context.Context, dgst digest.Digest) error
|
|
}
|
|
|
|
// BlobEnumerator enables iterating over blobs from storage
|
|
type BlobEnumerator interface {
|
|
Enumerate(ctx context.Context, ingester func(dgst digest.Digest) error) error
|
|
}
|
|
|
|
// BlobDescriptorService manages metadata about a blob by digest. Most
|
|
// implementations will not expose such an interface explicitly. Such mappings
|
|
// should be maintained by interacting with the BlobIngester. Hence, this is
|
|
// left off of BlobService and BlobStore.
|
|
type BlobDescriptorService interface {
|
|
BlobStatter
|
|
|
|
// SetDescriptor assigns the descriptor to the digest. The provided digest and
|
|
// the digest in the descriptor must map to identical content but they may
|
|
// differ on their algorithm. The descriptor must have the canonical
|
|
// digest of the content and the digest algorithm must match the
|
|
// annotators canonical algorithm.
|
|
//
|
|
// Such a facility can be used to map blobs between digest domains, with
|
|
// the restriction that the algorithm of the descriptor must match the
|
|
// canonical algorithm (ie sha256) of the annotator.
|
|
SetDescriptor(ctx context.Context, dgst digest.Digest, desc Descriptor) error
|
|
|
|
// Clear enables descriptors to be unlinked
|
|
Clear(ctx context.Context, dgst digest.Digest) error
|
|
}
|
|
|
|
// BlobDescriptorServiceFactory creates middleware for BlobDescriptorService.
|
|
type BlobDescriptorServiceFactory interface {
|
|
BlobAccessController(svc BlobDescriptorService) BlobDescriptorService
|
|
}
|
|
|
|
// ReadSeekCloser is the primary reader type for blob data, combining
|
|
// io.ReadSeeker with io.Closer.
|
|
type ReadSeekCloser interface {
|
|
io.ReadSeeker
|
|
io.Closer
|
|
}
|
|
|
|
// BlobProvider describes operations for getting blob data.
|
|
type BlobProvider interface {
|
|
// Get returns the entire blob identified by digest along with the descriptor.
|
|
Get(ctx context.Context, dgst digest.Digest) ([]byte, error)
|
|
|
|
// Open provides a ReadSeekCloser to the blob identified by the provided
|
|
// descriptor. If the blob is not known to the service, an error will be
|
|
// returned.
|
|
Open(ctx context.Context, dgst digest.Digest) (ReadSeekCloser, error)
|
|
}
|
|
|
|
// BlobServer can serve blobs via http.
|
|
type BlobServer interface {
|
|
// ServeBlob attempts to serve the blob, identified by dgst, via http. The
|
|
// service may decide to redirect the client elsewhere or serve the data
|
|
// directly.
|
|
//
|
|
// This handler only issues successful responses, such as 2xx or 3xx,
|
|
// meaning it serves data or issues a redirect. If the blob is not
|
|
// available, an error will be returned and the caller may still issue a
|
|
// response.
|
|
//
|
|
// The implementation may serve the same blob from a different digest
|
|
// domain. The appropriate headers will be set for the blob, unless they
|
|
// have already been set by the caller.
|
|
ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error
|
|
}
|
|
|
|
// BlobIngester ingests blob data.
|
|
type BlobIngester interface {
|
|
// Put inserts the content p into the blob service, returning a descriptor
|
|
// or an error.
|
|
Put(ctx context.Context, mediaType string, p []byte) (Descriptor, error)
|
|
|
|
// Create allocates a new blob writer to add a blob to this service. The
|
|
// returned handle can be written to and later resumed using an opaque
|
|
// identifier. With this approach, one can Close and Resume a BlobWriter
|
|
// multiple times until the BlobWriter is committed or cancelled.
|
|
Create(ctx context.Context, options ...BlobCreateOption) (BlobWriter, error)
|
|
|
|
// Resume attempts to resume a write to a blob, identified by an id.
|
|
Resume(ctx context.Context, id string) (BlobWriter, error)
|
|
}
|
|
|
|
// BlobCreateOption is a general extensible function argument for blob creation
|
|
// methods. A BlobIngester may choose to honor any or none of the given
|
|
// BlobCreateOptions, which can be specific to the implementation of the
|
|
// BlobIngester receiving them.
|
|
// TODO (brianbland): unify this with ManifestServiceOption in the future
|
|
type BlobCreateOption interface {
|
|
Apply(interface{}) error
|
|
}
|
|
|
|
// CreateOptions is a collection of blob creation modifiers relevant to general
|
|
// blob storage intended to be configured by the BlobCreateOption.Apply method.
|
|
type CreateOptions struct {
|
|
Mount struct {
|
|
ShouldMount bool
|
|
From reference.Canonical
|
|
// Stat allows to pass precalculated descriptor to link and return.
|
|
// Blob access check will be skipped if set.
|
|
Stat *Descriptor
|
|
}
|
|
}
|
|
|
|
// BlobWriter provides a handle for inserting data into a blob store.
|
|
// Instances should be obtained from BlobWriteService.Writer and
|
|
// BlobWriteService.Resume. If supported by the store, a writer can be
|
|
// recovered with the id.
|
|
type BlobWriter interface {
|
|
io.WriteCloser
|
|
io.ReaderFrom
|
|
|
|
// Size returns the number of bytes written to this blob.
|
|
Size() int64
|
|
|
|
// ID returns the identifier for this writer. The ID can be used with the
|
|
// Blob service to later resume the write.
|
|
ID() string
|
|
|
|
// StartedAt returns the time this blob write was started.
|
|
StartedAt() time.Time
|
|
|
|
// Commit completes the blob writer process. The content is verified
|
|
// against the provided provisional descriptor, which may result in an
|
|
// error. Depending on the implementation, written data may be validated
|
|
// against the provisional descriptor fields. If MediaType is not present,
|
|
// the implementation may reject the commit or assign "application/octet-
|
|
// stream" to the blob. The returned descriptor may have a different
|
|
// digest depending on the blob store, referred to as the canonical
|
|
// descriptor.
|
|
Commit(ctx context.Context, provisional Descriptor) (canonical Descriptor, err error)
|
|
|
|
// Cancel ends the blob write without storing any data and frees any
|
|
// associated resources. Any data written thus far will be lost. Cancel
|
|
// implementations should allow multiple calls even after a commit that
|
|
// result in a no-op. This allows use of Cancel in a defer statement,
|
|
// increasing the assurance that it is correctly called.
|
|
Cancel(ctx context.Context) error
|
|
}
|
|
|
|
// BlobService combines the operations to access, read and write blobs. This
|
|
// can be used to describe remote blob services.
|
|
type BlobService interface {
|
|
BlobStatter
|
|
BlobProvider
|
|
BlobIngester
|
|
}
|
|
|
|
// BlobStore represent the entire suite of blob related operations. Such an
|
|
// implementation can access, read, write, delete and serve blobs.
|
|
type BlobStore interface {
|
|
BlobService
|
|
BlobServer
|
|
BlobDeleter
|
|
}
|