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 }