...

Source file src/github.com/docker/distribution/registry/storage/registry.go

Documentation: github.com/docker/distribution/registry/storage

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"regexp"
     6  
     7  	"github.com/distribution/reference"
     8  	"github.com/docker/distribution"
     9  	"github.com/docker/distribution/registry/storage/cache"
    10  	storagedriver "github.com/docker/distribution/registry/storage/driver"
    11  	"github.com/docker/libtrust"
    12  )
    13  
    14  // registry is the top-level implementation of Registry for use in the storage
    15  // package. All instances should descend from this object.
    16  type registry struct {
    17  	blobStore                    *blobStore
    18  	blobServer                   *blobServer
    19  	statter                      *blobStatter // global statter service.
    20  	blobDescriptorCacheProvider  cache.BlobDescriptorCacheProvider
    21  	deleteEnabled                bool
    22  	schema1Enabled               bool
    23  	resumableDigestEnabled       bool
    24  	schema1SigningKey            libtrust.PrivateKey
    25  	blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory
    26  	manifestURLs                 manifestURLs
    27  	driver                       storagedriver.StorageDriver
    28  }
    29  
    30  // manifestURLs holds regular expressions for controlling manifest URL whitelisting
    31  type manifestURLs struct {
    32  	allow *regexp.Regexp
    33  	deny  *regexp.Regexp
    34  }
    35  
    36  // RegistryOption is the type used for functional options for NewRegistry.
    37  type RegistryOption func(*registry) error
    38  
    39  // EnableRedirect is a functional option for NewRegistry. It causes the backend
    40  // blob server to attempt using (StorageDriver).URLFor to serve all blobs.
    41  func EnableRedirect(registry *registry) error {
    42  	registry.blobServer.redirect = true
    43  	return nil
    44  }
    45  
    46  // EnableDelete is a functional option for NewRegistry. It enables deletion on
    47  // the registry.
    48  func EnableDelete(registry *registry) error {
    49  	registry.deleteEnabled = true
    50  	return nil
    51  }
    52  
    53  // EnableSchema1 is a functional option for NewRegistry. It enables pushing of
    54  // schema1 manifests.
    55  func EnableSchema1(registry *registry) error {
    56  	registry.schema1Enabled = true
    57  	return nil
    58  }
    59  
    60  // DisableDigestResumption is a functional option for NewRegistry. It should be
    61  // used if the registry is acting as a caching proxy.
    62  func DisableDigestResumption(registry *registry) error {
    63  	registry.resumableDigestEnabled = false
    64  	return nil
    65  }
    66  
    67  // ManifestURLsAllowRegexp is a functional option for NewRegistry.
    68  func ManifestURLsAllowRegexp(r *regexp.Regexp) RegistryOption {
    69  	return func(registry *registry) error {
    70  		registry.manifestURLs.allow = r
    71  		return nil
    72  	}
    73  }
    74  
    75  // ManifestURLsDenyRegexp is a functional option for NewRegistry.
    76  func ManifestURLsDenyRegexp(r *regexp.Regexp) RegistryOption {
    77  	return func(registry *registry) error {
    78  		registry.manifestURLs.deny = r
    79  		return nil
    80  	}
    81  }
    82  
    83  // Schema1SigningKey returns a functional option for NewRegistry. It sets the
    84  // key for signing  all schema1 manifests.
    85  func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption {
    86  	return func(registry *registry) error {
    87  		registry.schema1SigningKey = key
    88  		return nil
    89  	}
    90  }
    91  
    92  // BlobDescriptorServiceFactory returns a functional option for NewRegistry. It sets the
    93  // factory to create BlobDescriptorServiceFactory middleware.
    94  func BlobDescriptorServiceFactory(factory distribution.BlobDescriptorServiceFactory) RegistryOption {
    95  	return func(registry *registry) error {
    96  		registry.blobDescriptorServiceFactory = factory
    97  		return nil
    98  	}
    99  }
   100  
   101  // BlobDescriptorCacheProvider returns a functional option for
   102  // NewRegistry. It creates a cached blob statter for use by the
   103  // registry.
   104  func BlobDescriptorCacheProvider(blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider) RegistryOption {
   105  	// TODO(aaronl): The duplication of statter across several objects is
   106  	// ugly, and prevents us from using interface types in the registry
   107  	// struct. Ideally, blobStore and blobServer should be lazily
   108  	// initialized, and use the current value of
   109  	// blobDescriptorCacheProvider.
   110  	return func(registry *registry) error {
   111  		if blobDescriptorCacheProvider != nil {
   112  			statter := cache.NewCachedBlobStatter(blobDescriptorCacheProvider, registry.statter)
   113  			registry.blobStore.statter = statter
   114  			registry.blobServer.statter = statter
   115  			registry.blobDescriptorCacheProvider = blobDescriptorCacheProvider
   116  		}
   117  		return nil
   118  	}
   119  }
   120  
   121  // NewRegistry creates a new registry instance from the provided driver. The
   122  // resulting registry may be shared by multiple goroutines but is cheap to
   123  // allocate. If the Redirect option is specified, the backend blob server will
   124  // attempt to use (StorageDriver).URLFor to serve all blobs.
   125  func NewRegistry(ctx context.Context, driver storagedriver.StorageDriver, options ...RegistryOption) (distribution.Namespace, error) {
   126  	// create global statter
   127  	statter := &blobStatter{
   128  		driver: driver,
   129  	}
   130  
   131  	bs := &blobStore{
   132  		driver:  driver,
   133  		statter: statter,
   134  	}
   135  
   136  	registry := &registry{
   137  		blobStore: bs,
   138  		blobServer: &blobServer{
   139  			driver:  driver,
   140  			statter: statter,
   141  			pathFn:  bs.path,
   142  		},
   143  		statter:                statter,
   144  		resumableDigestEnabled: true,
   145  		driver:                 driver,
   146  	}
   147  
   148  	for _, option := range options {
   149  		if err := option(registry); err != nil {
   150  			return nil, err
   151  		}
   152  	}
   153  
   154  	return registry, nil
   155  }
   156  
   157  // Scope returns the namespace scope for a registry. The registry
   158  // will only serve repositories contained within this scope.
   159  func (reg *registry) Scope() distribution.Scope {
   160  	return distribution.GlobalScope
   161  }
   162  
   163  // Repository returns an instance of the repository tied to the registry.
   164  // Instances should not be shared between goroutines but are cheap to
   165  // allocate. In general, they should be request scoped.
   166  func (reg *registry) Repository(ctx context.Context, canonicalName reference.Named) (distribution.Repository, error) {
   167  	var descriptorCache distribution.BlobDescriptorService
   168  	if reg.blobDescriptorCacheProvider != nil {
   169  		var err error
   170  		descriptorCache, err = reg.blobDescriptorCacheProvider.RepositoryScoped(canonicalName.Name())
   171  		if err != nil {
   172  			return nil, err
   173  		}
   174  	}
   175  
   176  	return &repository{
   177  		ctx:             ctx,
   178  		registry:        reg,
   179  		name:            canonicalName,
   180  		descriptorCache: descriptorCache,
   181  	}, nil
   182  }
   183  
   184  func (reg *registry) Blobs() distribution.BlobEnumerator {
   185  	return reg.blobStore
   186  }
   187  
   188  func (reg *registry) BlobStatter() distribution.BlobStatter {
   189  	return reg.statter
   190  }
   191  
   192  // repository provides name-scoped access to various services.
   193  type repository struct {
   194  	*registry
   195  	ctx             context.Context
   196  	name            reference.Named
   197  	descriptorCache distribution.BlobDescriptorService
   198  }
   199  
   200  // Name returns the name of the repository.
   201  func (repo *repository) Named() reference.Named {
   202  	return repo.name
   203  }
   204  
   205  func (repo *repository) Tags(ctx context.Context) distribution.TagService {
   206  	tags := &tagStore{
   207  		repository: repo,
   208  		blobStore:  repo.registry.blobStore,
   209  	}
   210  
   211  	return tags
   212  }
   213  
   214  // Manifests returns an instance of ManifestService. Instantiation is cheap and
   215  // may be context sensitive in the future. The instance should be used similar
   216  // to a request local.
   217  func (repo *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {
   218  	manifestLinkPathFns := []linkPathFunc{
   219  		// NOTE(stevvooe): Need to search through multiple locations since
   220  		// 2.1.0 unintentionally linked into  _layers.
   221  		manifestRevisionLinkPath,
   222  		blobLinkPath,
   223  	}
   224  
   225  	manifestDirectoryPathSpec := manifestRevisionsPathSpec{name: repo.name.Name()}
   226  
   227  	var statter distribution.BlobDescriptorService = &linkedBlobStatter{
   228  		blobStore:   repo.blobStore,
   229  		repository:  repo,
   230  		linkPathFns: manifestLinkPathFns,
   231  	}
   232  
   233  	if repo.registry.blobDescriptorServiceFactory != nil {
   234  		statter = repo.registry.blobDescriptorServiceFactory.BlobAccessController(statter)
   235  	}
   236  
   237  	blobStore := &linkedBlobStore{
   238  		ctx:                  ctx,
   239  		blobStore:            repo.blobStore,
   240  		repository:           repo,
   241  		deleteEnabled:        repo.registry.deleteEnabled,
   242  		blobAccessController: statter,
   243  
   244  		// TODO(stevvooe): linkPath limits this blob store to only
   245  		// manifests. This instance cannot be used for blob checks.
   246  		linkPathFns:           manifestLinkPathFns,
   247  		linkDirectoryPathSpec: manifestDirectoryPathSpec,
   248  	}
   249  
   250  	var v1Handler ManifestHandler
   251  	if repo.schema1Enabled {
   252  		v1Handler = &signedManifestHandler{
   253  			ctx:               ctx,
   254  			schema1SigningKey: repo.schema1SigningKey,
   255  			repository:        repo,
   256  			blobStore:         blobStore,
   257  		}
   258  	} else {
   259  		v1Handler = &v1UnsupportedHandler{
   260  			innerHandler: &signedManifestHandler{
   261  				ctx:               ctx,
   262  				schema1SigningKey: repo.schema1SigningKey,
   263  				repository:        repo,
   264  				blobStore:         blobStore,
   265  			},
   266  		}
   267  	}
   268  
   269  	ms := &manifestStore{
   270  		ctx:            ctx,
   271  		repository:     repo,
   272  		blobStore:      blobStore,
   273  		schema1Handler: v1Handler,
   274  		schema2Handler: &schema2ManifestHandler{
   275  			ctx:          ctx,
   276  			repository:   repo,
   277  			blobStore:    blobStore,
   278  			manifestURLs: repo.registry.manifestURLs,
   279  		},
   280  		manifestListHandler: &manifestListHandler{
   281  			ctx:        ctx,
   282  			repository: repo,
   283  			blobStore:  blobStore,
   284  		},
   285  		ocischemaHandler: &ocischemaManifestHandler{
   286  			ctx:          ctx,
   287  			repository:   repo,
   288  			blobStore:    blobStore,
   289  			manifestURLs: repo.registry.manifestURLs,
   290  		},
   291  	}
   292  
   293  	// Apply options
   294  	for _, option := range options {
   295  		err := option.Apply(ms)
   296  		if err != nil {
   297  			return nil, err
   298  		}
   299  	}
   300  
   301  	return ms, nil
   302  }
   303  
   304  // Blobs returns an instance of the BlobStore. Instantiation is cheap and
   305  // may be context sensitive in the future. The instance should be used similar
   306  // to a request local.
   307  func (repo *repository) Blobs(ctx context.Context) distribution.BlobStore {
   308  	var statter distribution.BlobDescriptorService = &linkedBlobStatter{
   309  		blobStore:   repo.blobStore,
   310  		repository:  repo,
   311  		linkPathFns: []linkPathFunc{blobLinkPath},
   312  	}
   313  
   314  	if repo.descriptorCache != nil {
   315  		statter = cache.NewCachedBlobStatter(repo.descriptorCache, statter)
   316  	}
   317  
   318  	if repo.registry.blobDescriptorServiceFactory != nil {
   319  		statter = repo.registry.blobDescriptorServiceFactory.BlobAccessController(statter)
   320  	}
   321  
   322  	return &linkedBlobStore{
   323  		registry:             repo.registry,
   324  		blobStore:            repo.blobStore,
   325  		blobServer:           repo.blobServer,
   326  		blobAccessController: statter,
   327  		repository:           repo,
   328  		ctx:                  ctx,
   329  
   330  		// TODO(stevvooe): linkPath limits this blob store to only layers.
   331  		// This instance cannot be used for manifest checks.
   332  		linkPathFns:            []linkPathFunc{blobLinkPath},
   333  		deleteEnabled:          repo.registry.deleteEnabled,
   334  		resumableDigestEnabled: repo.resumableDigestEnabled,
   335  	}
   336  }
   337  

View as plain text