1package distribution
2
3import (
4	"context"
5	"errors"
6	"fmt"
7	"io"
8	"net/http"
9	"time"
10
11	"github.com/docker/distribution/reference"
12	"github.com/opencontainers/go-digest"
13	"github.com/opencontainers/image-spec/specs-go/v1"
14)
15
16var (
17	// ErrBlobExists returned when blob already exists
18	ErrBlobExists = errors.New("blob exists")
19
20	// ErrBlobDigestUnsupported when blob digest is an unsupported version.
21	ErrBlobDigestUnsupported = errors.New("unsupported blob digest")
22
23	// ErrBlobUnknown when blob is not found.
24	ErrBlobUnknown = errors.New("unknown blob")
25
26	// ErrBlobUploadUnknown returned when upload is not found.
27	ErrBlobUploadUnknown = errors.New("blob upload unknown")
28
29	// ErrBlobInvalidLength returned when the blob has an expected length on
30	// commit, meaning mismatched with the descriptor or an invalid value.
31	ErrBlobInvalidLength = errors.New("blob invalid length")
32)
33
34// ErrBlobInvalidDigest returned when digest check fails.
35type ErrBlobInvalidDigest struct {
36	Digest digest.Digest
37	Reason error
38}
39
40func (err ErrBlobInvalidDigest) Error() string {
41	return fmt.Sprintf("invalid digest for referenced layer: %v, %v",
42		err.Digest, err.Reason)
43}
44
45// ErrBlobMounted returned when a blob is mounted from another repository
46// instead of initiating an upload session.
47type ErrBlobMounted struct {
48	From       reference.Canonical
49	Descriptor Descriptor
50}
51
52func (err ErrBlobMounted) Error() string {
53	return fmt.Sprintf("blob mounted from: %v to: %v",
54		err.From, err.Descriptor)
55}
56
57// Descriptor describes targeted content. Used in conjunction with a blob
58// store, a descriptor can be used to fetch, store and target any kind of
59// blob. The struct also describes the wire protocol format. Fields should
60// only be added but never changed.
61type Descriptor struct {
62	// MediaType describe the type of the content. All text based formats are
63	// encoded as utf-8.
64	MediaType string `json:"mediaType,omitempty"`
65
66	// Size in bytes of content.
67	Size int64 `json:"size,omitempty"`
68
69	// Digest uniquely identifies the content. A byte stream can be verified
70	// against this digest.
71	Digest digest.Digest `json:"digest,omitempty"`
72
73	// URLs contains the source URLs of this content.
74	URLs []string `json:"urls,omitempty"`
75
76	// Annotations contains arbitrary metadata relating to the targeted content.
77	Annotations map[string]string `json:"annotations,omitempty"`
78
79	// Platform describes the platform which the image in the manifest runs on.
80	// This should only be used when referring to a manifest.
81	Platform *v1.Platform `json:"platform,omitempty"`
82
83	// NOTE: Before adding a field here, please ensure that all
84	// other options have been exhausted. Much of the type relationships
85	// depend on the simplicity of this type.
86}
87
88// Descriptor returns the descriptor, to make it satisfy the Describable
89// interface. Note that implementations of Describable are generally objects
90// which can be described, not simply descriptors; this exception is in place
91// to make it more convenient to pass actual descriptors to functions that
92// expect Describable objects.
93func (d Descriptor) Descriptor() Descriptor {
94	return d
95}
96
97// BlobStatter makes blob descriptors available by digest. The service may
98// provide a descriptor of a different digest if the provided digest is not
99// canonical.
100type BlobStatter interface {
101	// Stat provides metadata about a blob identified by the digest. If the
102	// blob is unknown to the describer, ErrBlobUnknown will be returned.
103	Stat(ctx context.Context, dgst digest.Digest) (Descriptor, error)
104}
105
106// BlobDeleter enables deleting blobs from storage.
107type BlobDeleter interface {
108	Delete(ctx context.Context, dgst digest.Digest) error
109}
110
111// BlobEnumerator enables iterating over blobs from storage
112type BlobEnumerator interface {
113	Enumerate(ctx context.Context, ingester func(dgst digest.Digest) error) error
114}
115
116// BlobDescriptorService manages metadata about a blob by digest. Most
117// implementations will not expose such an interface explicitly. Such mappings
118// should be maintained by interacting with the BlobIngester. Hence, this is
119// left off of BlobService and BlobStore.
120type BlobDescriptorService interface {
121	BlobStatter
122
123	// SetDescriptor assigns the descriptor to the digest. The provided digest and
124	// the digest in the descriptor must map to identical content but they may
125	// differ on their algorithm. The descriptor must have the canonical
126	// digest of the content and the digest algorithm must match the
127	// annotators canonical algorithm.
128	//
129	// Such a facility can be used to map blobs between digest domains, with
130	// the restriction that the algorithm of the descriptor must match the
131	// canonical algorithm (ie sha256) of the annotator.
132	SetDescriptor(ctx context.Context, dgst digest.Digest, desc Descriptor) error
133
134	// Clear enables descriptors to be unlinked
135	Clear(ctx context.Context, dgst digest.Digest) error
136}
137
138// BlobDescriptorServiceFactory creates middleware for BlobDescriptorService.
139type BlobDescriptorServiceFactory interface {
140	BlobAccessController(svc BlobDescriptorService) BlobDescriptorService
141}
142
143// ReadSeekCloser is the primary reader type for blob data, combining
144// io.ReadSeeker with io.Closer.
145type ReadSeekCloser interface {
146	io.ReadSeeker
147	io.Closer
148}
149
150// BlobProvider describes operations for getting blob data.
151type BlobProvider interface {
152	// Get returns the entire blob identified by digest along with the descriptor.
153	Get(ctx context.Context, dgst digest.Digest) ([]byte, error)
154
155	// Open provides a ReadSeekCloser to the blob identified by the provided
156	// descriptor. If the blob is not known to the service, an error will be
157	// returned.
158	Open(ctx context.Context, dgst digest.Digest) (ReadSeekCloser, error)
159}
160
161// BlobServer can serve blobs via http.
162type BlobServer interface {
163	// ServeBlob attempts to serve the blob, identified by dgst, via http. The
164	// service may decide to redirect the client elsewhere or serve the data
165	// directly.
166	//
167	// This handler only issues successful responses, such as 2xx or 3xx,
168	// meaning it serves data or issues a redirect. If the blob is not
169	// available, an error will be returned and the caller may still issue a
170	// response.
171	//
172	// The implementation may serve the same blob from a different digest
173	// domain. The appropriate headers will be set for the blob, unless they
174	// have already been set by the caller.
175	ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error
176}
177
178// BlobIngester ingests blob data.
179type BlobIngester interface {
180	// Put inserts the content p into the blob service, returning a descriptor
181	// or an error.
182	Put(ctx context.Context, mediaType string, p []byte) (Descriptor, error)
183
184	// Create allocates a new blob writer to add a blob to this service. The
185	// returned handle can be written to and later resumed using an opaque
186	// identifier. With this approach, one can Close and Resume a BlobWriter
187	// multiple times until the BlobWriter is committed or cancelled.
188	Create(ctx context.Context, options ...BlobCreateOption) (BlobWriter, error)
189
190	// Resume attempts to resume a write to a blob, identified by an id.
191	Resume(ctx context.Context, id string) (BlobWriter, error)
192}
193
194// BlobCreateOption is a general extensible function argument for blob creation
195// methods. A BlobIngester may choose to honor any or none of the given
196// BlobCreateOptions, which can be specific to the implementation of the
197// BlobIngester receiving them.
198// TODO (brianbland): unify this with ManifestServiceOption in the future
199type BlobCreateOption interface {
200	Apply(interface{}) error
201}
202
203// CreateOptions is a collection of blob creation modifiers relevant to general
204// blob storage intended to be configured by the BlobCreateOption.Apply method.
205type CreateOptions struct {
206	Mount struct {
207		ShouldMount bool
208		From        reference.Canonical
209		// Stat allows to pass precalculated descriptor to link and return.
210		// Blob access check will be skipped if set.
211		Stat *Descriptor
212	}
213}
214
215// BlobWriter provides a handle for inserting data into a blob store.
216// Instances should be obtained from BlobWriteService.Writer and
217// BlobWriteService.Resume. If supported by the store, a writer can be
218// recovered with the id.
219type BlobWriter interface {
220	io.WriteCloser
221	io.ReaderFrom
222
223	// Size returns the number of bytes written to this blob.
224	Size() int64
225
226	// ID returns the identifier for this writer. The ID can be used with the
227	// Blob service to later resume the write.
228	ID() string
229
230	// StartedAt returns the time this blob write was started.
231	StartedAt() time.Time
232
233	// Commit completes the blob writer process. The content is verified
234	// against the provided provisional descriptor, which may result in an
235	// error. Depending on the implementation, written data may be validated
236	// against the provisional descriptor fields. If MediaType is not present,
237	// the implementation may reject the commit or assign "application/octet-
238	// stream" to the blob. The returned descriptor may have a different
239	// digest depending on the blob store, referred to as the canonical
240	// descriptor.
241	Commit(ctx context.Context, provisional Descriptor) (canonical Descriptor, err error)
242
243	// Cancel ends the blob write without storing any data and frees any
244	// associated resources. Any data written thus far will be lost. Cancel
245	// implementations should allow multiple calls even after a commit that
246	// result in a no-op. This allows use of Cancel in a defer statement,
247	// increasing the assurance that it is correctly called.
248	Cancel(ctx context.Context) error
249}
250
251// BlobService combines the operations to access, read and write blobs. This
252// can be used to describe remote blob services.
253type BlobService interface {
254	BlobStatter
255	BlobProvider
256	BlobIngester
257}
258
259// BlobStore represent the entire suite of blob related operations. Such an
260// implementation can access, read, write, delete and serve blobs.
261type BlobStore interface {
262	BlobService
263	BlobServer
264	BlobDeleter
265}
266