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

     1  package storage
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"strings"
     7  
     8  	"github.com/docker/distribution/digest"
     9  )
    10  
    11  const (
    12  	storagePathVersion = "v2"                // fixed storage layout version
    13  	storagePathRoot    = "/docker/registry/" // all driver paths have a prefix
    14  
    15  	// TODO(stevvooe): Get rid of the "storagePathRoot". Initially, we though
    16  	// storage path root would configurable for all drivers through this
    17  	// package. In reality, we've found it simpler to do this on a per driver
    18  	// basis.
    19  )
    20  
    21  // pathFor maps paths based on "object names" and their ids. The "object
    22  // names" mapped by are internal to the storage system.
    23  //
    24  // The path layout in the storage backend is roughly as follows:
    25  //
    26  //		<root>/v2
    27  //			-> repositories/
    28  // 				-><name>/
    29  // 					-> _manifests/
    30  // 						revisions
    31  //							-> <manifest digest path>
    32  //								-> link
    33  //								-> signatures
    34  // 									<algorithm>/<digest>/link
    35  // 						tags/<tag>
    36  //							-> current/link
    37  // 							-> index
    38  //								-> <algorithm>/<hex digest>/link
    39  // 					-> _layers/
    40  // 						<layer links to blob store>
    41  // 					-> _uploads/<id>
    42  // 						data
    43  // 						startedat
    44  // 						hashstates/<algorithm>/<offset>
    45  //			-> blob/<algorithm>
    46  //				<split directory content addressable storage>
    47  //
    48  // The storage backend layout is broken up into a content-addressable blob
    49  // store and repositories. The content-addressable blob store holds most data
    50  // throughout the backend, keyed by algorithm and digests of the underlying
    51  // content. Access to the blob store is controled through links from the
    52  // repository to blobstore.
    53  //
    54  // A repository is made up of layers, manifests and tags. The layers component
    55  // is just a directory of layers which are "linked" into a repository. A layer
    56  // can only be accessed through a qualified repository name if it is linked in
    57  // the repository. Uploads of layers are managed in the uploads directory,
    58  // which is key by upload id. When all data for an upload is received, the
    59  // data is moved into the blob store and the upload directory is deleted.
    60  // Abandoned uploads can be garbage collected by reading the startedat file
    61  // and removing uploads that have been active for longer than a certain time.
    62  //
    63  // The third component of the repository directory is the manifests store,
    64  // which is made up of a revision store and tag store. Manifests are stored in
    65  // the blob store and linked into the revision store. Signatures are separated
    66  // from the manifest payload data and linked into the blob store, as well.
    67  // While the registry can save all revisions of a manifest, no relationship is
    68  // implied as to the ordering of changes to a manifest. The tag store provides
    69  // support for name, tag lookups of manifests, using "current/link" under a
    70  // named tag directory. An index is maintained to support deletions of all
    71  // revisions of a given manifest tag.
    72  //
    73  // We cover the path formats implemented by this path mapper below.
    74  //
    75  //	Manifests:
    76  //
    77  // 	manifestRevisionPathSpec:      <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/
    78  // 	manifestRevisionLinkPathSpec:  <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/link
    79  // 	manifestSignaturesPathSpec:    <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/signatures/
    80  // 	manifestSignatureLinkPathSpec: <root>/v2/repositories/<name>/_manifests/revisions/<algorithm>/<hex digest>/signatures/<algorithm>/<hex digest>/link
    81  //
    82  //	Tags:
    83  //
    84  // 	manifestTagsPathSpec:                  <root>/v2/repositories/<name>/_manifests/tags/
    85  // 	manifestTagPathSpec:                   <root>/v2/repositories/<name>/_manifests/tags/<tag>/
    86  // 	manifestTagCurrentPathSpec:            <root>/v2/repositories/<name>/_manifests/tags/<tag>/current/link
    87  // 	manifestTagIndexPathSpec:              <root>/v2/repositories/<name>/_manifests/tags/<tag>/index/
    88  // 	manifestTagIndexEntryPathSpec:         <root>/v2/repositories/<name>/_manifests/tags/<tag>/index/<algorithm>/<hex digest>/
    89  // 	manifestTagIndexEntryLinkPathSpec:     <root>/v2/repositories/<name>/_manifests/tags/<tag>/index/<algorithm>/<hex digest>/link
    90  //
    91  // 	Blobs:
    92  //
    93  // 	layerLinkPathSpec:            <root>/v2/repositories/<name>/_layers/<algorithm>/<hex digest>/link
    94  //
    95  //	Uploads:
    96  //
    97  // 	uploadDataPathSpec:             <root>/v2/repositories/<name>/_uploads/<id>/data
    98  // 	uploadStartedAtPathSpec:        <root>/v2/repositories/<name>/_uploads/<id>/startedat
    99  // 	uploadHashStatePathSpec:        <root>/v2/repositories/<name>/_uploads/<id>/hashstates/<algorithm>/<offset>
   100  //
   101  //	Blob Store:
   102  //
   103  // 	blobPathSpec:                   <root>/v2/blobs/<algorithm>/<first two hex bytes of digest>/<hex digest>
   104  // 	blobDataPathSpec:               <root>/v2/blobs/<algorithm>/<first two hex bytes of digest>/<hex digest>/data
   105  // 	blobMediaTypePathSpec:               <root>/v2/blobs/<algorithm>/<first two hex bytes of digest>/<hex digest>/data
   106  //
   107  // For more information on the semantic meaning of each path and their
   108  // contents, please see the path spec documentation.
   109  func pathFor(spec pathSpec) (string, error) {
   110  
   111  	// Switch on the path object type and return the appropriate path. At
   112  	// first glance, one may wonder why we don't use an interface to
   113  	// accomplish this. By keep the formatting separate from the pathSpec, we
   114  	// keep separate the path generation componentized. These specs could be
   115  	// passed to a completely different mapper implementation and generate a
   116  	// different set of paths.
   117  	//
   118  	// For example, imagine migrating from one backend to the other: one could
   119  	// build a filesystem walker that converts a string path in one version,
   120  	// to an intermediate path object, than can be consumed and mapped by the
   121  	// other version.
   122  
   123  	rootPrefix := []string{storagePathRoot, storagePathVersion}
   124  	repoPrefix := append(rootPrefix, "repositories")
   125  
   126  	switch v := spec.(type) {
   127  
   128  	case manifestRevisionPathSpec:
   129  		components, err := digestPathComponents(v.revision, false)
   130  		if err != nil {
   131  			return "", err
   132  		}
   133  
   134  		return path.Join(append(append(repoPrefix, v.name, "_manifests", "revisions"), components...)...), nil
   135  	case manifestRevisionLinkPathSpec:
   136  		root, err := pathFor(manifestRevisionPathSpec{
   137  			name:     v.name,
   138  			revision: v.revision,
   139  		})
   140  
   141  		if err != nil {
   142  			return "", err
   143  		}
   144  
   145  		return path.Join(root, "link"), nil
   146  	case manifestSignaturesPathSpec:
   147  		root, err := pathFor(manifestRevisionPathSpec{
   148  			name:     v.name,
   149  			revision: v.revision,
   150  		})
   151  
   152  		if err != nil {
   153  			return "", err
   154  		}
   155  
   156  		return path.Join(root, "signatures"), nil
   157  	case manifestSignatureLinkPathSpec:
   158  		root, err := pathFor(manifestSignaturesPathSpec{
   159  			name:     v.name,
   160  			revision: v.revision,
   161  		})
   162  
   163  		if err != nil {
   164  			return "", err
   165  		}
   166  
   167  		signatureComponents, err := digestPathComponents(v.signature, false)
   168  		if err != nil {
   169  			return "", err
   170  		}
   171  
   172  		return path.Join(root, path.Join(append(signatureComponents, "link")...)), nil
   173  	case manifestTagsPathSpec:
   174  		return path.Join(append(repoPrefix, v.name, "_manifests", "tags")...), nil
   175  	case manifestTagPathSpec:
   176  		root, err := pathFor(manifestTagsPathSpec{
   177  			name: v.name,
   178  		})
   179  
   180  		if err != nil {
   181  			return "", err
   182  		}
   183  
   184  		return path.Join(root, v.tag), nil
   185  	case manifestTagCurrentPathSpec:
   186  		root, err := pathFor(manifestTagPathSpec{
   187  			name: v.name,
   188  			tag:  v.tag,
   189  		})
   190  
   191  		if err != nil {
   192  			return "", err
   193  		}
   194  
   195  		return path.Join(root, "current", "link"), nil
   196  	case manifestTagIndexPathSpec:
   197  		root, err := pathFor(manifestTagPathSpec{
   198  			name: v.name,
   199  			tag:  v.tag,
   200  		})
   201  
   202  		if err != nil {
   203  			return "", err
   204  		}
   205  
   206  		return path.Join(root, "index"), nil
   207  	case manifestTagIndexEntryLinkPathSpec:
   208  		root, err := pathFor(manifestTagIndexEntryPathSpec{
   209  			name:     v.name,
   210  			tag:      v.tag,
   211  			revision: v.revision,
   212  		})
   213  
   214  		if err != nil {
   215  			return "", err
   216  		}
   217  
   218  		return path.Join(root, "link"), nil
   219  	case manifestTagIndexEntryPathSpec:
   220  		root, err := pathFor(manifestTagIndexPathSpec{
   221  			name: v.name,
   222  			tag:  v.tag,
   223  		})
   224  
   225  		if err != nil {
   226  			return "", err
   227  		}
   228  
   229  		components, err := digestPathComponents(v.revision, false)
   230  		if err != nil {
   231  			return "", err
   232  		}
   233  
   234  		return path.Join(root, path.Join(components...)), nil
   235  	case layerLinkPathSpec:
   236  		components, err := digestPathComponents(v.digest, false)
   237  		if err != nil {
   238  			return "", err
   239  		}
   240  
   241  		// TODO(stevvooe): Right now, all blobs are linked under "_layers". If
   242  		// we have future migrations, we may want to rename this to "_blobs".
   243  		// A migration strategy would simply leave existing items in place and
   244  		// write the new paths, commit a file then delete the old files.
   245  
   246  		blobLinkPathComponents := append(repoPrefix, v.name, "_layers")
   247  
   248  		return path.Join(path.Join(append(blobLinkPathComponents, components...)...), "link"), nil
   249  	case blobDataPathSpec:
   250  		components, err := digestPathComponents(v.digest, true)
   251  		if err != nil {
   252  			return "", err
   253  		}
   254  
   255  		components = append(components, "data")
   256  		blobPathPrefix := append(rootPrefix, "blobs")
   257  		return path.Join(append(blobPathPrefix, components...)...), nil
   258  
   259  	case uploadDataPathSpec:
   260  		return path.Join(append(repoPrefix, v.name, "_uploads", v.id, "data")...), nil
   261  	case uploadStartedAtPathSpec:
   262  		return path.Join(append(repoPrefix, v.name, "_uploads", v.id, "startedat")...), nil
   263  	case uploadHashStatePathSpec:
   264  		offset := fmt.Sprintf("%d", v.offset)
   265  		if v.list {
   266  			offset = "" // Limit to the prefix for listing offsets.
   267  		}
   268  		return path.Join(append(repoPrefix, v.name, "_uploads", v.id, "hashstates", string(v.alg), offset)...), nil
   269  	case repositoriesRootPathSpec:
   270  		return path.Join(repoPrefix...), nil
   271  	default:
   272  		// TODO(sday): This is an internal error. Ensure it doesn't escape (panic?).
   273  		return "", fmt.Errorf("unknown path spec: %#v", v)
   274  	}
   275  }
   276  
   277  // pathSpec is a type to mark structs as path specs. There is no
   278  // implementation because we'd like to keep the specs and the mappers
   279  // decoupled.
   280  type pathSpec interface {
   281  	pathSpec()
   282  }
   283  
   284  // manifestRevisionPathSpec describes the components of the directory path for
   285  // a manifest revision.
   286  type manifestRevisionPathSpec struct {
   287  	name     string
   288  	revision digest.Digest
   289  }
   290  
   291  func (manifestRevisionPathSpec) pathSpec() {}
   292  
   293  // manifestRevisionLinkPathSpec describes the path components required to look
   294  // up the data link for a revision of a manifest. If this file is not present,
   295  // the manifest blob is not available in the given repo. The contents of this
   296  // file should just be the digest.
   297  type manifestRevisionLinkPathSpec struct {
   298  	name     string
   299  	revision digest.Digest
   300  }
   301  
   302  func (manifestRevisionLinkPathSpec) pathSpec() {}
   303  
   304  // manifestSignaturesPathSpec decribes the path components for the directory
   305  // containing all the signatures for the target blob. Entries are named with
   306  // the underlying key id.
   307  type manifestSignaturesPathSpec struct {
   308  	name     string
   309  	revision digest.Digest
   310  }
   311  
   312  func (manifestSignaturesPathSpec) pathSpec() {}
   313  
   314  // manifestSignatureLinkPathSpec decribes the path components used to look up
   315  // a signature file by the hash of its blob.
   316  type manifestSignatureLinkPathSpec struct {
   317  	name      string
   318  	revision  digest.Digest
   319  	signature digest.Digest
   320  }
   321  
   322  func (manifestSignatureLinkPathSpec) pathSpec() {}
   323  
   324  // manifestTagsPathSpec describes the path elements required to point to the
   325  // manifest tags directory.
   326  type manifestTagsPathSpec struct {
   327  	name string
   328  }
   329  
   330  func (manifestTagsPathSpec) pathSpec() {}
   331  
   332  // manifestTagPathSpec describes the path elements required to point to the
   333  // manifest tag links files under a repository. These contain a blob id that
   334  // can be used to look up the data and signatures.
   335  type manifestTagPathSpec struct {
   336  	name string
   337  	tag  string
   338  }
   339  
   340  func (manifestTagPathSpec) pathSpec() {}
   341  
   342  // manifestTagCurrentPathSpec describes the link to the current revision for a
   343  // given tag.
   344  type manifestTagCurrentPathSpec struct {
   345  	name string
   346  	tag  string
   347  }
   348  
   349  func (manifestTagCurrentPathSpec) pathSpec() {}
   350  
   351  // manifestTagCurrentPathSpec describes the link to the index of revisions
   352  // with the given tag.
   353  type manifestTagIndexPathSpec struct {
   354  	name string
   355  	tag  string
   356  }
   357  
   358  func (manifestTagIndexPathSpec) pathSpec() {}
   359  
   360  // manifestTagIndexEntryPathSpec contains the entries of the index by revision.
   361  type manifestTagIndexEntryPathSpec struct {
   362  	name     string
   363  	tag      string
   364  	revision digest.Digest
   365  }
   366  
   367  func (manifestTagIndexEntryPathSpec) pathSpec() {}
   368  
   369  // manifestTagIndexEntryLinkPathSpec describes the link to a revisions of a
   370  // manifest with given tag within the index.
   371  type manifestTagIndexEntryLinkPathSpec struct {
   372  	name     string
   373  	tag      string
   374  	revision digest.Digest
   375  }
   376  
   377  func (manifestTagIndexEntryLinkPathSpec) pathSpec() {}
   378  
   379  // blobLinkPathSpec specifies a path for a blob link, which is a file with a
   380  // blob id. The blob link will contain a content addressable blob id reference
   381  // into the blob store. The format of the contents is as follows:
   382  //
   383  // 	<algorithm>:<hex digest of layer data>
   384  //
   385  // The following example of the file contents is more illustrative:
   386  //
   387  // 	sha256:96443a84ce518ac22acb2e985eda402b58ac19ce6f91980bde63726a79d80b36
   388  //
   389  // This  indicates that there is a blob with the id/digest, calculated via
   390  // sha256 that can be fetched from the blob store.
   391  type layerLinkPathSpec struct {
   392  	name   string
   393  	digest digest.Digest
   394  }
   395  
   396  func (layerLinkPathSpec) pathSpec() {}
   397  
   398  // blobAlgorithmReplacer does some very simple path sanitization for user
   399  // input. Paths should be "safe" before getting this far due to strict digest
   400  // requirements but we can add further path conversion here, if needed.
   401  var blobAlgorithmReplacer = strings.NewReplacer(
   402  	"+", "/",
   403  	".", "/",
   404  	";", "/",
   405  )
   406  
   407  // // blobPathSpec contains the path for the registry global blob store.
   408  // type blobPathSpec struct {
   409  // 	digest digest.Digest
   410  // }
   411  
   412  // func (blobPathSpec) pathSpec() {}
   413  
   414  // blobDataPathSpec contains the path for the registry global blob store. For
   415  // now, this contains layer data, exclusively.
   416  type blobDataPathSpec struct {
   417  	digest digest.Digest
   418  }
   419  
   420  func (blobDataPathSpec) pathSpec() {}
   421  
   422  // uploadDataPathSpec defines the path parameters of the data file for
   423  // uploads.
   424  type uploadDataPathSpec struct {
   425  	name string
   426  	id   string
   427  }
   428  
   429  func (uploadDataPathSpec) pathSpec() {}
   430  
   431  // uploadDataPathSpec defines the path parameters for the file that stores the
   432  // start time of an uploads. If it is missing, the upload is considered
   433  // unknown. Admittedly, the presence of this file is an ugly hack to make sure
   434  // we have a way to cleanup old or stalled uploads that doesn't rely on driver
   435  // FileInfo behavior. If we come up with a more clever way to do this, we
   436  // should remove this file immediately and rely on the startetAt field from
   437  // the client to enforce time out policies.
   438  type uploadStartedAtPathSpec struct {
   439  	name string
   440  	id   string
   441  }
   442  
   443  func (uploadStartedAtPathSpec) pathSpec() {}
   444  
   445  // uploadHashStatePathSpec defines the path parameters for the file that stores
   446  // the hash function state of an upload at a specific byte offset. If `list` is
   447  // set, then the path mapper will generate a list prefix for all hash state
   448  // offsets for the upload identified by the name, id, and alg.
   449  type uploadHashStatePathSpec struct {
   450  	name   string
   451  	id     string
   452  	alg    digest.Algorithm
   453  	offset int64
   454  	list   bool
   455  }
   456  
   457  func (uploadHashStatePathSpec) pathSpec() {}
   458  
   459  // repositoriesRootPathSpec returns the root of repositories
   460  type repositoriesRootPathSpec struct {
   461  }
   462  
   463  func (repositoriesRootPathSpec) pathSpec() {}
   464  
   465  // digestPathComponents provides a consistent path breakdown for a given
   466  // digest. For a generic digest, it will be as follows:
   467  //
   468  // 	<algorithm>/<hex digest>
   469  //
   470  // If multilevel is true, the first two bytes of the digest will separate
   471  // groups of digest folder. It will be as follows:
   472  //
   473  // 	<algorithm>/<first two bytes of digest>/<full digest>
   474  //
   475  func digestPathComponents(dgst digest.Digest, multilevel bool) ([]string, error) {
   476  	if err := dgst.Validate(); err != nil {
   477  		return nil, err
   478  	}
   479  
   480  	algorithm := blobAlgorithmReplacer.Replace(string(dgst.Algorithm()))
   481  	hex := dgst.Hex()
   482  	prefix := []string{algorithm}
   483  
   484  	var suffix []string
   485  
   486  	if multilevel {
   487  		suffix = append(suffix, hex[:2])
   488  	}
   489  
   490  	suffix = append(suffix, hex)
   491  
   492  	return append(prefix, suffix...), nil
   493  }