github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/registry.go (about) 1 package storage 2 3 import ( 4 "github.com/docker/distribution" 5 "github.com/docker/distribution/context" 6 "github.com/docker/distribution/reference" 7 "github.com/docker/distribution/registry/storage/cache" 8 storagedriver "github.com/docker/distribution/registry/storage/driver" 9 ) 10 11 // registry is the top-level implementation of Registry for use in the storage 12 // package. All instances should descend from this object. 13 type registry struct { 14 blobStore *blobStore 15 blobServer *blobServer 16 statter *blobStatter // global statter service. 17 blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider 18 deleteEnabled bool 19 resumableDigestEnabled bool 20 } 21 22 // RegistryOption is the type used for functional options for NewRegistry. 23 type RegistryOption func(*registry) error 24 25 // EnableRedirect is a functional option for NewRegistry. It causes the backend 26 // blob server to attempt using (StorageDriver).URLFor to serve all blobs. 27 func EnableRedirect(registry *registry) error { 28 registry.blobServer.redirect = true 29 return nil 30 } 31 32 // EnableDelete is a functional option for NewRegistry. It enables deletion on 33 // the registry. 34 func EnableDelete(registry *registry) error { 35 registry.deleteEnabled = true 36 return nil 37 } 38 39 // DisableDigestResumption is a functional option for NewRegistry. It should be 40 // used if the registry is acting as a caching proxy. 41 func DisableDigestResumption(registry *registry) error { 42 registry.resumableDigestEnabled = false 43 return nil 44 } 45 46 // BlobDescriptorCacheProvider returns a functional option for 47 // NewRegistry. It creates a cached blob statter for use by the 48 // registry. 49 func BlobDescriptorCacheProvider(blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider) RegistryOption { 50 // TODO(aaronl): The duplication of statter across several objects is 51 // ugly, and prevents us from using interface types in the registry 52 // struct. Ideally, blobStore and blobServer should be lazily 53 // initialized, and use the current value of 54 // blobDescriptorCacheProvider. 55 return func(registry *registry) error { 56 if blobDescriptorCacheProvider != nil { 57 statter := cache.NewCachedBlobStatter(blobDescriptorCacheProvider, registry.statter) 58 registry.blobStore.statter = statter 59 registry.blobServer.statter = statter 60 registry.blobDescriptorCacheProvider = blobDescriptorCacheProvider 61 } 62 return nil 63 } 64 } 65 66 // NewRegistry creates a new registry instance from the provided driver. The 67 // resulting registry may be shared by multiple goroutines but is cheap to 68 // allocate. If the Redirect option is specified, the backend blob server will 69 // attempt to use (StorageDriver).URLFor to serve all blobs. 70 func NewRegistry(ctx context.Context, driver storagedriver.StorageDriver, options ...RegistryOption) (distribution.Namespace, error) { 71 // create global statter 72 statter := &blobStatter{ 73 driver: driver, 74 } 75 76 bs := &blobStore{ 77 driver: driver, 78 statter: statter, 79 } 80 81 registry := ®istry{ 82 blobStore: bs, 83 blobServer: &blobServer{ 84 driver: driver, 85 statter: statter, 86 pathFn: bs.path, 87 }, 88 statter: statter, 89 resumableDigestEnabled: true, 90 } 91 92 for _, option := range options { 93 if err := option(registry); err != nil { 94 return nil, err 95 } 96 } 97 98 return registry, nil 99 } 100 101 // Scope returns the namespace scope for a registry. The registry 102 // will only serve repositories contained within this scope. 103 func (reg *registry) Scope() distribution.Scope { 104 return distribution.GlobalScope 105 } 106 107 // Repository returns an instance of the repository tied to the registry. 108 // Instances should not be shared between goroutines but are cheap to 109 // allocate. In general, they should be request scoped. 110 func (reg *registry) Repository(ctx context.Context, canonicalName string) (distribution.Repository, error) { 111 if _, err := reference.ParseNamed(canonicalName); err != nil { 112 return nil, distribution.ErrRepositoryNameInvalid{ 113 Name: canonicalName, 114 Reason: err, 115 } 116 } 117 118 var descriptorCache distribution.BlobDescriptorService 119 if reg.blobDescriptorCacheProvider != nil { 120 var err error 121 descriptorCache, err = reg.blobDescriptorCacheProvider.RepositoryScoped(canonicalName) 122 if err != nil { 123 return nil, err 124 } 125 } 126 127 return &repository{ 128 ctx: ctx, 129 registry: reg, 130 name: canonicalName, 131 descriptorCache: descriptorCache, 132 }, nil 133 } 134 135 // repository provides name-scoped access to various services. 136 type repository struct { 137 *registry 138 ctx context.Context 139 name string 140 descriptorCache distribution.BlobDescriptorService 141 } 142 143 // Name returns the name of the repository. 144 func (repo *repository) Name() string { 145 return repo.name 146 } 147 148 func (repo *repository) Tags(ctx context.Context) distribution.TagService { 149 tags := &tagStore{ 150 repository: repo, 151 blobStore: repo.registry.blobStore, 152 } 153 154 return tags 155 } 156 157 // Manifests returns an instance of ManifestService. Instantiation is cheap and 158 // may be context sensitive in the future. The instance should be used similar 159 // to a request local. 160 func (repo *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) { 161 manifestLinkPathFns := []linkPathFunc{ 162 // NOTE(stevvooe): Need to search through multiple locations since 163 // 2.1.0 unintentionally linked into _layers. 164 manifestRevisionLinkPath, 165 blobLinkPath, 166 } 167 168 blobStore := &linkedBlobStore{ 169 ctx: ctx, 170 blobStore: repo.blobStore, 171 repository: repo, 172 deleteEnabled: repo.registry.deleteEnabled, 173 blobAccessController: &linkedBlobStatter{ 174 blobStore: repo.blobStore, 175 repository: repo, 176 linkPathFns: manifestLinkPathFns, 177 }, 178 179 // TODO(stevvooe): linkPath limits this blob store to only 180 // manifests. This instance cannot be used for blob checks. 181 linkPathFns: manifestLinkPathFns, 182 } 183 184 ms := &manifestStore{ 185 ctx: ctx, 186 repository: repo, 187 blobStore: blobStore, 188 schema1Handler: &signedManifestHandler{ 189 ctx: ctx, 190 repository: repo, 191 blobStore: blobStore, 192 signatures: &signatureStore{ 193 ctx: ctx, 194 repository: repo, 195 blobStore: repo.blobStore, 196 }, 197 }, 198 schema2Handler: &schema2ManifestHandler{ 199 ctx: ctx, 200 repository: repo, 201 blobStore: blobStore, 202 }, 203 manifestListHandler: &manifestListHandler{ 204 ctx: ctx, 205 repository: repo, 206 blobStore: blobStore, 207 }, 208 } 209 210 // Apply options 211 for _, option := range options { 212 err := option.Apply(ms) 213 if err != nil { 214 return nil, err 215 } 216 } 217 218 return ms, nil 219 } 220 221 // Blobs returns an instance of the BlobStore. Instantiation is cheap and 222 // may be context sensitive in the future. The instance should be used similar 223 // to a request local. 224 func (repo *repository) Blobs(ctx context.Context) distribution.BlobStore { 225 var statter distribution.BlobDescriptorService = &linkedBlobStatter{ 226 blobStore: repo.blobStore, 227 repository: repo, 228 linkPathFns: []linkPathFunc{blobLinkPath}, 229 } 230 231 if repo.descriptorCache != nil { 232 statter = cache.NewCachedBlobStatter(repo.descriptorCache, statter) 233 } 234 235 return &linkedBlobStore{ 236 blobStore: repo.blobStore, 237 blobServer: repo.blobServer, 238 blobAccessController: statter, 239 repository: repo, 240 ctx: ctx, 241 242 // TODO(stevvooe): linkPath limits this blob store to only layers. 243 // This instance cannot be used for manifest checks. 244 linkPathFns: []linkPathFunc{blobLinkPath}, 245 deleteEnabled: repo.registry.deleteEnabled, 246 resumableDigestEnabled: repo.resumableDigestEnabled, 247 } 248 }