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  }