github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/blobstore.go (about)

     1  package storage
     2  
     3  import (
     4  	"github.com/docker/distribution"
     5  	"github.com/docker/distribution/context"
     6  	"github.com/docker/distribution/digest"
     7  	"github.com/docker/distribution/registry/storage/driver"
     8  )
     9  
    10  // blobStore implements the read side of the blob store interface over a
    11  // driver without enforcing per-repository membership. This object is
    12  // intentionally a leaky abstraction, providing utility methods that support
    13  // creating and traversing backend links.
    14  type blobStore struct {
    15  	driver  driver.StorageDriver
    16  	statter distribution.BlobStatter
    17  }
    18  
    19  var _ distribution.BlobProvider = &blobStore{}
    20  
    21  // Get implements the BlobReadService.Get call.
    22  func (bs *blobStore) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
    23  	bp, err := bs.path(dgst)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	p, err := bs.driver.GetContent(ctx, bp)
    29  	if err != nil {
    30  		switch err.(type) {
    31  		case driver.PathNotFoundError:
    32  			return nil, distribution.ErrBlobUnknown
    33  		}
    34  
    35  		return nil, err
    36  	}
    37  
    38  	return p, err
    39  }
    40  
    41  func (bs *blobStore) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
    42  	desc, err := bs.statter.Stat(ctx, dgst)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	path, err := bs.path(desc.Digest)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	return newFileReader(ctx, bs.driver, path, desc.Size)
    53  }
    54  
    55  // Put stores the content p in the blob store, calculating the digest. If the
    56  // content is already present, only the digest will be returned. This should
    57  // only be used for small objects, such as manifests. This implemented as a convenience for other Put implementations
    58  func (bs *blobStore) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
    59  	dgst := digest.FromBytes(p)
    60  	desc, err := bs.statter.Stat(ctx, dgst)
    61  	if err == nil {
    62  		// content already present
    63  		return desc, nil
    64  	} else if err != distribution.ErrBlobUnknown {
    65  		context.GetLogger(ctx).Errorf("blobStore: error stating content (%v): %#v", dgst, err)
    66  		// real error, return it
    67  		return distribution.Descriptor{}, err
    68  	}
    69  
    70  	bp, err := bs.path(dgst)
    71  	if err != nil {
    72  		return distribution.Descriptor{}, err
    73  	}
    74  
    75  	// TODO(stevvooe): Write out mediatype here, as well.
    76  
    77  	return distribution.Descriptor{
    78  		Size: int64(len(p)),
    79  
    80  		// NOTE(stevvooe): The central blob store firewalls media types from
    81  		// other users. The caller should look this up and override the value
    82  		// for the specific repository.
    83  		MediaType: "application/octet-stream",
    84  		Digest:    dgst,
    85  	}, bs.driver.PutContent(ctx, bp, p)
    86  }
    87  
    88  // path returns the canonical path for the blob identified by digest. The blob
    89  // may or may not exist.
    90  func (bs *blobStore) path(dgst digest.Digest) (string, error) {
    91  	bp, err := pathFor(blobDataPathSpec{
    92  		digest: dgst,
    93  	})
    94  
    95  	if err != nil {
    96  		return "", err
    97  	}
    98  
    99  	return bp, nil
   100  }
   101  
   102  // link links the path to the provided digest by writing the digest into the
   103  // target file. Caller must ensure that the blob actually exists.
   104  func (bs *blobStore) link(ctx context.Context, path string, dgst digest.Digest) error {
   105  	// The contents of the "link" file are the exact string contents of the
   106  	// digest, which is specified in that package.
   107  	return bs.driver.PutContent(ctx, path, []byte(dgst))
   108  }
   109  
   110  // readlink returns the linked digest at path.
   111  func (bs *blobStore) readlink(ctx context.Context, path string) (digest.Digest, error) {
   112  	content, err := bs.driver.GetContent(ctx, path)
   113  	if err != nil {
   114  		return "", err
   115  	}
   116  
   117  	linked, err := digest.ParseDigest(string(content))
   118  	if err != nil {
   119  		return "", err
   120  	}
   121  
   122  	return linked, nil
   123  }
   124  
   125  // resolve reads the digest link at path and returns the blob store path.
   126  func (bs *blobStore) resolve(ctx context.Context, path string) (string, error) {
   127  	dgst, err := bs.readlink(ctx, path)
   128  	if err != nil {
   129  		return "", err
   130  	}
   131  
   132  	return bs.path(dgst)
   133  }
   134  
   135  type blobStatter struct {
   136  	driver driver.StorageDriver
   137  }
   138  
   139  var _ distribution.BlobDescriptorService = &blobStatter{}
   140  
   141  // Stat implements BlobStatter.Stat by returning the descriptor for the blob
   142  // in the main blob store. If this method returns successfully, there is
   143  // strong guarantee that the blob exists and is available.
   144  func (bs *blobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
   145  	path, err := pathFor(blobDataPathSpec{
   146  		digest: dgst,
   147  	})
   148  
   149  	if err != nil {
   150  		return distribution.Descriptor{}, err
   151  	}
   152  
   153  	fi, err := bs.driver.Stat(ctx, path)
   154  	if err != nil {
   155  		switch err := err.(type) {
   156  		case driver.PathNotFoundError:
   157  			return distribution.Descriptor{}, distribution.ErrBlobUnknown
   158  		default:
   159  			return distribution.Descriptor{}, err
   160  		}
   161  	}
   162  
   163  	if fi.IsDir() {
   164  		// NOTE(stevvooe): This represents a corruption situation. Somehow, we
   165  		// calculated a blob path and then detected a directory. We log the
   166  		// error and then error on the side of not knowing about the blob.
   167  		context.GetLogger(ctx).Warnf("blob path should not be a directory: %q", path)
   168  		return distribution.Descriptor{}, distribution.ErrBlobUnknown
   169  	}
   170  
   171  	// TODO(stevvooe): Add method to resolve the mediatype. We can store and
   172  	// cache a "global" media type for the blob, even if a specific repo has a
   173  	// mediatype that overrides the main one.
   174  
   175  	return distribution.Descriptor{
   176  		Size: fi.Size(),
   177  
   178  		// NOTE(stevvooe): The central blob store firewalls media types from
   179  		// other users. The caller should look this up and override the value
   180  		// for the specific repository.
   181  		MediaType: "application/octet-stream",
   182  		Digest:    dgst,
   183  	}, nil
   184  }
   185  
   186  func (bs *blobStatter) Clear(ctx context.Context, dgst digest.Digest) error {
   187  	return distribution.ErrUnsupported
   188  }
   189  
   190  func (bs *blobStatter) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
   191  	return distribution.ErrUnsupported
   192  }