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 := &registry{
    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  }