1/*
2   Copyright The containerd Authors.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17package content
18
19import (
20	"context"
21	"io"
22	"time"
23
24	"github.com/opencontainers/go-digest"
25	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
26)
27
28// ReaderAt extends the standard io.ReaderAt interface with reporting of Size and io.Closer
29type ReaderAt interface {
30	io.ReaderAt
31	io.Closer
32	Size() int64
33}
34
35// Provider provides a reader interface for specific content
36type Provider interface {
37	// ReaderAt only requires desc.Digest to be set.
38	// Other fields in the descriptor may be used internally for resolving
39	// the location of the actual data.
40	ReaderAt(ctx context.Context, dec ocispec.Descriptor) (ReaderAt, error)
41}
42
43// Ingester writes content
44type Ingester interface {
45	// Some implementations require WithRef to be included in opts.
46	Writer(ctx context.Context, opts ...WriterOpt) (Writer, error)
47}
48
49// Info holds content specific information
50//
51// TODO(stevvooe): Consider a very different name for this struct. Info is way
52// to general. It also reads very weird in certain context, like pluralization.
53type Info struct {
54	Digest    digest.Digest
55	Size      int64
56	CreatedAt time.Time
57	UpdatedAt time.Time
58	Labels    map[string]string
59}
60
61// Status of a content operation
62type Status struct {
63	Ref       string
64	Offset    int64
65	Total     int64
66	Expected  digest.Digest
67	StartedAt time.Time
68	UpdatedAt time.Time
69}
70
71// WalkFunc defines the callback for a blob walk.
72type WalkFunc func(Info) error
73
74// Manager provides methods for inspecting, listing and removing content.
75type Manager interface {
76	// Info will return metadata about content available in the content store.
77	//
78	// If the content is not present, ErrNotFound will be returned.
79	Info(ctx context.Context, dgst digest.Digest) (Info, error)
80
81	// Update updates mutable information related to content.
82	// If one or more fieldpaths are provided, only those
83	// fields will be updated.
84	// Mutable fields:
85	//  labels.*
86	Update(ctx context.Context, info Info, fieldpaths ...string) (Info, error)
87
88	// Walk will call fn for each item in the content store which
89	// match the provided filters. If no filters are given all
90	// items will be walked.
91	Walk(ctx context.Context, fn WalkFunc, filters ...string) error
92
93	// Delete removes the content from the store.
94	Delete(ctx context.Context, dgst digest.Digest) error
95}
96
97// IngestManager provides methods for managing ingests.
98type IngestManager interface {
99	// Status returns the status of the provided ref.
100	Status(ctx context.Context, ref string) (Status, error)
101
102	// ListStatuses returns the status of any active ingestions whose ref match the
103	// provided regular expression. If empty, all active ingestions will be
104	// returned.
105	ListStatuses(ctx context.Context, filters ...string) ([]Status, error)
106
107	// Abort completely cancels the ingest operation targeted by ref.
108	Abort(ctx context.Context, ref string) error
109}
110
111// Writer handles the write of content into a content store
112type Writer interface {
113	// Close closes the writer, if the writer has not been
114	// committed this allows resuming or aborting.
115	// Calling Close on a closed writer will not error.
116	io.WriteCloser
117
118	// Digest may return empty digest or panics until committed.
119	Digest() digest.Digest
120
121	// Commit commits the blob (but no roll-back is guaranteed on an error).
122	// size and expected can be zero-value when unknown.
123	// Commit always closes the writer, even on error.
124	// ErrAlreadyExists aborts the writer.
125	Commit(ctx context.Context, size int64, expected digest.Digest, opts ...Opt) error
126
127	// Status returns the current state of write
128	Status() (Status, error)
129
130	// Truncate updates the size of the target blob
131	Truncate(size int64) error
132}
133
134// Store combines the methods of content-oriented interfaces into a set that
135// are commonly provided by complete implementations.
136type Store interface {
137	Manager
138	Provider
139	IngestManager
140	Ingester
141}
142
143// Opt is used to alter the mutable properties of content
144type Opt func(*Info) error
145
146// WithLabels allows labels to be set on content
147func WithLabels(labels map[string]string) Opt {
148	return func(info *Info) error {
149		info.Labels = labels
150		return nil
151	}
152}
153
154// WriterOpts is internally used by WriterOpt.
155type WriterOpts struct {
156	Ref  string
157	Desc ocispec.Descriptor
158}
159
160// WriterOpt is used for passing options to Ingester.Writer.
161type WriterOpt func(*WriterOpts) error
162
163// WithDescriptor specifies an OCI descriptor.
164// Writer may optionally use the descriptor internally for resolving
165// the location of the actual data.
166// Write does not require any field of desc to be set.
167// If the data size is unknown, desc.Size should be set to 0.
168// Some implementations may also accept negative values as "unknown".
169func WithDescriptor(desc ocispec.Descriptor) WriterOpt {
170	return func(opts *WriterOpts) error {
171		opts.Desc = desc
172		return nil
173	}
174}
175
176// WithRef specifies a ref string.
177func WithRef(ref string) WriterOpt {
178	return func(opts *WriterOpts) error {
179		opts.Ref = ref
180		return nil
181	}
182}
183