1
15
16 package content
17
18 import (
19 "bytes"
20 "context"
21 "fmt"
22 "io"
23 "io/ioutil"
24 "strings"
25 "sync"
26 "time"
27
28 "github.com/containerd/containerd/content"
29 "github.com/containerd/containerd/errdefs"
30 "github.com/containerd/containerd/remotes"
31 digest "github.com/opencontainers/go-digest"
32 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
33 "github.com/pkg/errors"
34 )
35
36
37 type Memory struct {
38 descriptor map[digest.Digest]ocispec.Descriptor
39 content map[digest.Digest][]byte
40 nameMap map[string]ocispec.Descriptor
41 refMap map[string]ocispec.Descriptor
42 lock *sync.Mutex
43 }
44
45
46 func NewMemory() *Memory {
47 return &Memory{
48 descriptor: make(map[digest.Digest]ocispec.Descriptor),
49 content: make(map[digest.Digest][]byte),
50 nameMap: make(map[string]ocispec.Descriptor),
51 refMap: make(map[string]ocispec.Descriptor),
52 lock: &sync.Mutex{},
53 }
54 }
55
56 func (s *Memory) Resolver() remotes.Resolver {
57 return s
58 }
59
60 func (s *Memory) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
61 desc, ok := s.refMap[ref]
62 if !ok {
63 return "", ocispec.Descriptor{}, fmt.Errorf("unknown reference: %s", ref)
64 }
65 return ref, desc, nil
66 }
67
68 func (s *Memory) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
69 if _, ok := s.refMap[ref]; !ok {
70 return nil, fmt.Errorf("unknown reference: %s", ref)
71 }
72 return s, nil
73 }
74
75
76 func (s *Memory) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
77 _, content, ok := s.Get(desc)
78 if !ok {
79 return nil, ErrNotFound
80 }
81 return ioutil.NopCloser(bytes.NewReader(content)), nil
82 }
83
84 func (s *Memory) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
85 var tag, hash string
86 parts := strings.SplitN(ref, "@", 2)
87 if len(parts) > 0 {
88 tag = parts[0]
89 }
90 if len(parts) > 1 {
91 hash = parts[1]
92 }
93 return &memoryPusher{
94 store: s,
95 ref: tag,
96 hash: hash,
97 }, nil
98 }
99
100 type memoryPusher struct {
101 store *Memory
102 ref string
103 hash string
104 }
105
106 func (s *memoryPusher) Push(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) {
107 name, _ := ResolveName(desc)
108 now := time.Now()
109
110 if desc.Digest.String() == s.hash {
111 s.store.refMap[s.ref] = desc
112 }
113 return &memoryWriter{
114 store: s.store,
115 buffer: bytes.NewBuffer(nil),
116 desc: desc,
117 digester: digest.Canonical.Digester(),
118 status: content.Status{
119 Ref: name,
120 Total: desc.Size,
121 StartedAt: now,
122 UpdatedAt: now,
123 },
124 }, nil
125 }
126
127
128 func (s *Memory) Add(name, mediaType string, content []byte) (ocispec.Descriptor, error) {
129 var annotations map[string]string
130 if name != "" {
131 annotations = map[string]string{
132 ocispec.AnnotationTitle: name,
133 }
134 }
135
136 if mediaType == "" {
137 mediaType = DefaultBlobMediaType
138 }
139
140 desc := ocispec.Descriptor{
141 MediaType: mediaType,
142 Digest: digest.FromBytes(content),
143 Size: int64(len(content)),
144 Annotations: annotations,
145 }
146
147 s.Set(desc, content)
148 return desc, nil
149 }
150
151
152 func (s *Memory) Set(desc ocispec.Descriptor, content []byte) {
153 s.lock.Lock()
154 defer s.lock.Unlock()
155
156 s.descriptor[desc.Digest] = desc
157 s.content[desc.Digest] = content
158
159 if name, ok := ResolveName(desc); ok && name != "" {
160 s.nameMap[name] = desc
161 }
162 }
163
164
165 func (s *Memory) Get(desc ocispec.Descriptor) (ocispec.Descriptor, []byte, bool) {
166 s.lock.Lock()
167 defer s.lock.Unlock()
168
169 desc, ok := s.descriptor[desc.Digest]
170 if !ok {
171 return ocispec.Descriptor{}, nil, false
172 }
173 content, ok := s.content[desc.Digest]
174 return desc, content, ok
175 }
176
177
178 func (s *Memory) GetByName(name string) (ocispec.Descriptor, []byte, bool) {
179 s.lock.Lock()
180 defer s.lock.Unlock()
181
182 desc, ok := s.nameMap[name]
183 if !ok {
184 return ocispec.Descriptor{}, nil, false
185 }
186 content, ok := s.content[desc.Digest]
187 return desc, content, ok
188 }
189
190
191
192
193
194
195
196 func (s *Memory) StoreManifest(ref string, desc ocispec.Descriptor, manifest []byte) error {
197 s.refMap[ref] = desc
198 s.Add("", desc.MediaType, manifest)
199 return nil
200 }
201
202 func descFromBytes(b []byte, mediaType string) (ocispec.Descriptor, error) {
203 digest, err := digest.FromReader(bytes.NewReader(b))
204 if err != nil {
205 return ocispec.Descriptor{}, err
206 }
207
208 if mediaType == "" {
209 mediaType = DefaultBlobMediaType
210 }
211 return ocispec.Descriptor{
212 MediaType: mediaType,
213 Digest: digest,
214 Size: int64(len(b)),
215 }, nil
216 }
217
218 type memoryWriter struct {
219 store *Memory
220 buffer *bytes.Buffer
221 desc ocispec.Descriptor
222 digester digest.Digester
223 status content.Status
224 }
225
226 func (w *memoryWriter) Status() (content.Status, error) {
227 return w.status, nil
228 }
229
230
231
232
233 func (w *memoryWriter) Digest() digest.Digest {
234 return w.digester.Digest()
235 }
236
237
238 func (w *memoryWriter) Write(p []byte) (n int, err error) {
239 n, err = w.buffer.Write(p)
240 w.digester.Hash().Write(p[:n])
241 w.status.Offset += int64(len(p))
242 w.status.UpdatedAt = time.Now()
243 return n, err
244 }
245
246 func (w *memoryWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error {
247 var base content.Info
248 for _, opt := range opts {
249 if err := opt(&base); err != nil {
250 return err
251 }
252 }
253
254 if w.buffer == nil {
255 return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot commit on closed writer")
256 }
257 content := w.buffer.Bytes()
258 w.buffer = nil
259
260 if size > 0 && size != int64(len(content)) {
261 return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit size %d, expected %d", len(content), size)
262 }
263 if dgst := w.digester.Digest(); expected != "" && expected != dgst {
264 return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit digest %s, expected %s", dgst, expected)
265 }
266
267 w.store.Set(w.desc, content)
268 return nil
269 }
270
271 func (w *memoryWriter) Close() error {
272 w.buffer = nil
273 return nil
274 }
275
276 func (w *memoryWriter) Truncate(size int64) error {
277 if size != 0 {
278 return ErrUnsupportedSize
279 }
280 w.status.Offset = 0
281 w.digester.Hash().Reset()
282 w.buffer.Truncate(0)
283 return nil
284 }
285
View as plain text