...

Source file src/oras.land/oras-go/pkg/content/memory.go

Documentation: oras.land/oras-go/pkg/content

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    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  // Memory provides content from the memory
    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  // NewMemory creats a new memory store
    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  // Fetch get an io.ReadCloser for the specific content
    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  	// is this the root?
   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  // Add adds content, generating a descriptor and returning it.
   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  // Set adds the content to the store
   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  // Get finds the content from the store
   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  // GetByName finds the content from the store by name (i.e. AnnotationTitle)
   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  // StoreManifest stores a manifest linked to by the provided ref. The children of the
   191  // manifest, such as layers and config, should already exist in the file store, either
   192  // as files linked via Add(), or via Set(). If they do not exist, then a typical
   193  // Fetcher that walks the manifest will hit an unresolved hash.
   194  //
   195  // StoreManifest does *not* validate their presence.
   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  // Digest returns the current digest of the content, up to the current write.
   231  //
   232  // Cannot be called concurrently with `Write`.
   233  func (w *memoryWriter) Digest() digest.Digest {
   234  	return w.digester.Digest()
   235  }
   236  
   237  // Write p to the transaction.
   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