-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinterface.go
More file actions
304 lines (269 loc) · 13.4 KB
/
interface.go
File metadata and controls
304 lines (269 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// Copyright 2023 CUE Labs AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package oci provides an abstraction that represents the
// capabilities provided by an OCI registry.
//
// See the [OCI distribution specification] for more information on OCI registries.
//
// Packages within this module provide the capability to translate to and
// from the HTTP protocol documented in that specification:
// - [github.com/docker/oci/ociclient] provides an [Interface] value
// that acts as an HTTP client.
// - [github.com/docker/oci/ociserver] provides an HTTP server
// that serves the distribution protocol by making calls to an arbitrary
// [Interface] value.
//
// When used together in a stack, the above two packages can be used
// to provide a simple proxy server.
//
// The [github.com/docker/oci/ocimem] package provides a trivial
// in-memory implementation of the interface.
//
// Other packages provide some utilities that manipulate [Interface] values:
// - [github.com/docker/oci/ocifilter] provides functionality for exposing
// modified or restricted views onto a registry.
// - [github.com/docker/oci/ociunify] can combine two registries into one
// unified view across both.
//
// # Notes on [Interface]
//
// In general, the caller cannot assume that the implementation of a given [Interface] value
// is present on the network. For example, [github.com/docker/oci/ocimem]
// doesn't know about the network at all. But there are times when an implementation
// might want to provide information about the location of blobs or manifests so
// that a client can go direct if it wishes. That is, a proxy might not wish
// to ship all the traffic for all blobs through itself, but instead redirect clients
// to talk to some other location on the internet.
//
// When an [Interface] implementation wishes to provide that information, it
// can do so by setting the `URLs` field on the descriptor that it returns for
// a given blob or manifest. Although it is not mandatory for a caller to use
// this, some callers (specifically the ociserver package) can use this information
// to redirect clients appropriately.
//
// [OCI distribution specification]: https://github.com/opencontainers/distribution-spec/blob/main/spec.md
package oci
import (
"context"
"io"
"iter"
"github.com/docker/oci/ociref"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// Interface defines a generic interface to a single OCI registry.
// It does not support cross-registry operations: all methods are
// directed to the receiver only.
type Interface interface {
Writer
Reader
Deleter
Lister
Extension
private()
}
// ReadWriter combines the [Reader] and [Writer] interfaces.
type ReadWriter interface {
Reader
Writer
}
// Type aliases for commonly used OCI types.
type (
// Digest is a content-addressable digest. It is an alias for [ociref.Digest].
Digest = ociref.Digest
// Descriptor describes the disposition of targeted content. It is an alias for [ocispec.Descriptor].
Descriptor = ocispec.Descriptor
// Manifest provides the `application/vnd.oci.image.manifest.v1+json` mediatype structure. It is an alias for [ocispec.Manifest].
Manifest = ocispec.Manifest
)
// Reader defines registry operations that read blobs, manifests and tags.
type Reader interface {
// GetBlob returns the content of the blob with the given digest.
// The context also controls the lifetime of the returned BlobReader.
// Errors:
// - ErrNameUnknown when the repository is not present.
// - ErrBlobUnknown when the blob is not present in the repository.
GetBlob(ctx context.Context, repo string, digest Digest) (BlobReader, error)
// GetBlobRange is like GetBlob but asks to get only the given range of bytes from the blob,
// starting at offset0, up to but not including offset1.
// If offset1 is negative or exceeds the actual size of the blob, GetBlobRange will
// return all the data starting from offset0.
// The context also controls the lifetime of the returned BlobReader.
GetBlobRange(ctx context.Context, repo string, digest Digest, offset0, offset1 int64) (BlobReader, error)
// GetManifest returns the contents of the manifest with the given digest.
// The context also controls the lifetime of the returned BlobReader.
// Errors:
// - ErrNameUnknown when the repository is not present.
// - ErrManifestUnknown when the blob is not present in the repository.
GetManifest(ctx context.Context, repo string, digest Digest) (BlobReader, error)
// GetTag returns the contents of the manifest with the given tag.
// The context also controls the lifetime of the returned BlobReader.
// Errors:
// - ErrNameUnknown when the repository is not present.
// - ErrManifestUnknown when the tag is not present in the repository.
GetTag(ctx context.Context, repo string, tagName string) (BlobReader, error)
// ResolveDigest returns the descriptor for a given blob.
// Only the MediaType, Digest and Size fields will be filled out.
// Errors:
// - ErrNameUnknown when the repository is not present.
// - ErrBlobUnknown when the blob is not present in the repository.
ResolveBlob(ctx context.Context, repo string, digest Digest) (Descriptor, error)
// ResolveManifest returns the descriptor for a given maniifest.
// Only the MediaType, Digest and Size fields will be filled out.
// Errors:
// - ErrNameUnknown when the repository is not present.
// - ErrManifestUnknown when the blob is not present in the repository.
ResolveManifest(ctx context.Context, repo string, digest Digest) (Descriptor, error)
// ResolveTag returns the descriptor for a given tag.
// Only the MediaType, Digest and Size fields will be filled out.
// Errors:
// - ErrNameUnknown when the repository is not present.
// - ErrManifestUnknown when the blob is not present in the repository.
ResolveTag(ctx context.Context, repo string, tagName string) (Descriptor, error)
}
// PushManifestParameters holds optional parameters for the [Writer.PushManifest] method.
type PushManifestParameters struct {
Digest Digest
Tags []string
}
// Writer defines registry actions that write to blobs, manifests and tags.
type Writer interface {
// PushBlob pushes a blob described by desc to the given repository, reading content from r.
// Only the desc.Digest and desc.Size fields are used.
// It returns desc with Digest set to the canonical digest for the blob.
// Errors:
// - ErrNameUnknown when the repository is not present.
// - ErrNameInvalid when the repository name is not valid.
// - ErrDigestInvalid when desc.Digest does not match the content.
// - ErrSizeInvalid when desc.Size does not match the content length.
PushBlob(ctx context.Context, repo string, desc Descriptor, r io.Reader) (Descriptor, error)
// PushBlobChunked starts to push a blob to the given repository.
// The returned [BlobWriter] can be used to stream the upload and resume on temporary errors.
//
// The chunkSize parameter provides a hint for the chunk size to use
// when writing to the registry. If it's zero, a suitable default will be chosen.
// It might be larger if the underlying registry requires that.
//
// The context remains active as long as the BlobWriter is around: if it's
// cancelled, it should cause any blocked BlobWriter operations to terminate.
PushBlobChunked(ctx context.Context, repo string, chunkSize int) (BlobWriter, error)
// PushBlobChunkedResume resumes a previous push of a blob started with PushBlobChunked.
// The id should be the value returned from [BlobWriter.ID] from the previous push.
// and the offset should be the value returned from [BlobWriter.Size].
//
// The offset and chunkSize should similarly be obtained from the previous [BlobWriter]
// via the [BlobWriter.Size] and [BlobWriter.ChunkSize] methods.
// Alternatively, set offset to -1 to continue where the last write left off,
// and to only use chunkSize as a hint like in PushBlobChunked.
//
// The context remains active as long as the BlobWriter is around: if it's
// cancelled, it should cause any blocked BlobWriter operations to terminate.
PushBlobChunkedResume(ctx context.Context, repo, id string, offset int64, chunkSize int) (BlobWriter, error)
// MountBlob makes a blob with the given digest that's in fromRepo available
// in toRepo and returns its canonical descriptor.
//
// This avoids the need to pull content down from fromRepo only to push it to r.
//
// TODO the mount endpoint doesn't return the size of the content,
// so to return a correctly populated descriptor, a client will need to make
// an extra HTTP call to find that out. For now, we'll just say that
// the descriptor returned from MountBlob might have a zero Size.
//
// Errors:
// ErrUnsupported (when the repository does not support mounts).
MountBlob(ctx context.Context, fromRepo, toRepo string, digest Digest) (Descriptor, error)
// PushManifest pushes a manifest with the given media type and contents.
//
// It returns a descriptor suitable for accessing the manfiest.
PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *PushManifestParameters) (Descriptor, error)
}
// Deleter defines registry actions that delete objects from the registry.
type Deleter interface {
// DeleteBlob deletes the blob with the given digest in the given repository.
DeleteBlob(ctx context.Context, repo string, digest Digest) error
// DeleteManifest deletes the manifest with the given digest in the given repository.
DeleteManifest(ctx context.Context, repo string, digest Digest) error
// DeleteTag deletes the manifest with the given tag in the given repository.
// TODO does this delete the tag only, or the manifest too?
DeleteTag(ctx context.Context, repo string, name string) error
}
// TagsParameters holds optional parameters for the [Lister.Tags] method.
type TagsParameters struct {
StartAfter string
Limit int
}
// ReferrersParameters holds optional parameters for the [Lister.Referrers] method.
type ReferrersParameters struct {
ArtifactType string
}
// Lister defines registry operations that enumerate objects within the registry.
// TODO support resumption from a given point.
type Lister interface {
// Tags returns an iterator that can be used to iterate over all
// the tags in the given repository in lexical order. If
// startAfter is non-empty, the tags start lexically after, but
// not including that tag.
//
// If limit is greater than zero, at most that many tags will be returned.
// If limit is less than or equal to zero, all tags will be returned.
Tags(ctx context.Context, repo string, params *TagsParameters) iter.Seq2[string, error]
// Referrers returns an iterator that can be used to iterate over all
// the manifests that have the given digest as their Subject.
// If artifactType is non-zero, the results will be restricted to
// only manifests with that type.
// TODO is it possible to ask for multiple artifact types?
Referrers(ctx context.Context, repo string, digest Digest, params *ReferrersParameters) iter.Seq2[Descriptor, error]
}
// Extension defines registry operations that are not currently part of the spec, but are additional optional operations
type Extension interface {
// Repositories returns an iterator that can be used to iterate
// over all the repositories in the registry in lexical order.
// If startAfter is non-empty, the iteration starts lexically
// after, but not including, that repository.
Repositories(ctx context.Context, startAfter string) iter.Seq2[string, error]
}
// BlobWriter provides a handle for uploading a blob to a registry.
type BlobWriter interface {
// Write writes more data to the blob. When resuming, the
// caller must start writing data from Size bytes into the content.
io.Writer
// Closer closes the writer but does not abort. The blob write
// can later be resumed.
io.Closer
// Size returns the number of bytes written to this blob.
Size() int64
// ChunkSize returns the maximum number of bytes to upload at a single time.
// This number must meet the minimum given by the registry
// and should otherwise follow the hint given by the user.
ChunkSize() int
// ID returns the opaque identifier for this writer. The returned value
// can be passed to PushBlobChunked to resume the write.
// It is only valid before Write has been called or after Close has
// been called.
ID() string
// Commit completes the blob writer process. The content is verified
// against the provided digest, and a canonical descriptor for it is returned.
Commit(digest Digest) (Descriptor, 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() error
}
// BlobReader provides the contents of a given blob or manifest.
type BlobReader interface {
io.ReadCloser
// Descriptor returns the descriptor for the blob.
Descriptor() Descriptor
}