github.com/azurearcforkubernetes/oras@v0.4.0/pkg/content/memory.go (about) 1 package content 2 3 import ( 4 "bytes" 5 "context" 6 "sync" 7 "time" 8 9 "github.com/containerd/containerd/content" 10 "github.com/containerd/containerd/errdefs" 11 digest "github.com/opencontainers/go-digest" 12 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 13 "github.com/pkg/errors" 14 ) 15 16 // ensure interface 17 var ( 18 _ content.Provider = &Memorystore{} 19 _ content.Ingester = &Memorystore{} 20 ) 21 22 // Memorystore provides content from the memory 23 type Memorystore struct { 24 descriptor map[digest.Digest]ocispec.Descriptor 25 content map[digest.Digest][]byte 26 nameMap map[string]ocispec.Descriptor 27 lock *sync.Mutex 28 } 29 30 // NewMemoryStore creats a new memory store 31 func NewMemoryStore() *Memorystore { 32 return &Memorystore{ 33 descriptor: make(map[digest.Digest]ocispec.Descriptor), 34 content: make(map[digest.Digest][]byte), 35 nameMap: make(map[string]ocispec.Descriptor), 36 lock: &sync.Mutex{}, 37 } 38 } 39 40 // Add adds content 41 func (s *Memorystore) Add(name, mediaType string, content []byte) ocispec.Descriptor { 42 var annotations map[string]string 43 if name != "" { 44 annotations = map[string]string{ 45 ocispec.AnnotationTitle: name, 46 } 47 } 48 49 if mediaType == "" { 50 mediaType = DefaultBlobMediaType 51 } 52 53 desc := ocispec.Descriptor{ 54 MediaType: mediaType, 55 Digest: digest.FromBytes(content), 56 Size: int64(len(content)), 57 Annotations: annotations, 58 } 59 60 s.Set(desc, content) 61 return desc 62 } 63 64 // ReaderAt provides contents 65 func (s *Memorystore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { 66 desc, content, ok := s.Get(desc) 67 if !ok { 68 return nil, ErrNotFound 69 } 70 71 return sizeReaderAt{ 72 readAtCloser: nopCloser{ 73 ReaderAt: bytes.NewReader(content), 74 }, 75 size: desc.Size, 76 }, nil 77 } 78 79 // Writer begins or resumes the active writer identified by desc 80 func (s *Memorystore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { 81 var wOpts content.WriterOpts 82 for _, opt := range opts { 83 if err := opt(&wOpts); err != nil { 84 return nil, err 85 } 86 } 87 desc := wOpts.Desc 88 89 name, _ := ResolveName(desc) 90 now := time.Now() 91 return &memoryWriter{ 92 store: s, 93 buffer: bytes.NewBuffer(nil), 94 desc: desc, 95 digester: digest.Canonical.Digester(), 96 status: content.Status{ 97 Ref: name, 98 Total: desc.Size, 99 StartedAt: now, 100 UpdatedAt: now, 101 }, 102 }, nil 103 } 104 105 // Set adds the content to the store 106 func (s *Memorystore) Set(desc ocispec.Descriptor, content []byte) { 107 s.lock.Lock() 108 defer s.lock.Unlock() 109 110 s.descriptor[desc.Digest] = desc 111 s.content[desc.Digest] = content 112 113 if name, ok := ResolveName(desc); ok && name != "" { 114 s.nameMap[name] = desc 115 } 116 } 117 118 // Get finds the content from the store 119 func (s *Memorystore) Get(desc ocispec.Descriptor) (ocispec.Descriptor, []byte, bool) { 120 s.lock.Lock() 121 defer s.lock.Unlock() 122 123 desc, ok := s.descriptor[desc.Digest] 124 if !ok { 125 return ocispec.Descriptor{}, nil, false 126 } 127 content, ok := s.content[desc.Digest] 128 return desc, content, ok 129 } 130 131 // GetByName finds the content from the store by name (i.e. AnnotationTitle) 132 func (s *Memorystore) GetByName(name string) (ocispec.Descriptor, []byte, bool) { 133 s.lock.Lock() 134 defer s.lock.Unlock() 135 136 desc, ok := s.nameMap[name] 137 if !ok { 138 return ocispec.Descriptor{}, nil, false 139 } 140 content, ok := s.content[desc.Digest] 141 return desc, content, ok 142 } 143 144 type memoryWriter struct { 145 store *Memorystore 146 buffer *bytes.Buffer 147 desc ocispec.Descriptor 148 digester digest.Digester 149 status content.Status 150 } 151 152 func (w *memoryWriter) Status() (content.Status, error) { 153 return w.status, nil 154 } 155 156 // Digest returns the current digest of the content, up to the current write. 157 // 158 // Cannot be called concurrently with `Write`. 159 func (w *memoryWriter) Digest() digest.Digest { 160 return w.digester.Digest() 161 } 162 163 // Write p to the transaction. 164 func (w *memoryWriter) Write(p []byte) (n int, err error) { 165 n, err = w.buffer.Write(p) 166 w.digester.Hash().Write(p[:n]) 167 w.status.Offset += int64(len(p)) 168 w.status.UpdatedAt = time.Now() 169 return n, err 170 } 171 172 func (w *memoryWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { 173 var base content.Info 174 for _, opt := range opts { 175 if err := opt(&base); err != nil { 176 return err 177 } 178 } 179 180 if w.buffer == nil { 181 return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot commit on closed writer") 182 } 183 content := w.buffer.Bytes() 184 w.buffer = nil 185 186 if size > 0 && size != int64(len(content)) { 187 return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit size %d, expected %d", len(content), size) 188 } 189 if dgst := w.digester.Digest(); expected != "" && expected != dgst { 190 return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit digest %s, expected %s", dgst, expected) 191 } 192 193 w.store.Set(w.desc, content) 194 return nil 195 } 196 197 func (w *memoryWriter) Close() error { 198 w.buffer = nil 199 return nil 200 } 201 202 func (w *memoryWriter) Truncate(size int64) error { 203 if size != 0 { 204 return ErrUnsupportedSize 205 } 206 w.status.Offset = 0 207 w.digester.Hash().Reset() 208 w.buffer.Truncate(0) 209 return nil 210 }