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

     1  package v2
     2  
     3  import (
     4  	"net/http"
     5  	"regexp"
     6  
     7  	"github.com/docker/distribution/digest"
     8  	"github.com/docker/distribution/reference"
     9  	"github.com/docker/distribution/registry/api/errcode"
    10  )
    11  
    12  var (
    13  	nameParameterDescriptor = ParameterDescriptor{
    14  		Name:        "name",
    15  		Type:        "string",
    16  		Format:      reference.NameRegexp.String(),
    17  		Required:    true,
    18  		Description: `Name of the target repository.`,
    19  	}
    20  
    21  	referenceParameterDescriptor = ParameterDescriptor{
    22  		Name:        "reference",
    23  		Type:        "string",
    24  		Format:      reference.TagRegexp.String(),
    25  		Required:    true,
    26  		Description: `Tag or digest of the target manifest.`,
    27  	}
    28  
    29  	uuidParameterDescriptor = ParameterDescriptor{
    30  		Name:        "uuid",
    31  		Type:        "opaque",
    32  		Required:    true,
    33  		Description: "A uuid identifying the upload. This field can accept characters that match `[a-zA-Z0-9-_.=]+`.",
    34  	}
    35  
    36  	digestPathParameter = ParameterDescriptor{
    37  		Name:        "digest",
    38  		Type:        "path",
    39  		Required:    true,
    40  		Format:      digest.DigestRegexp.String(),
    41  		Description: `Digest of desired blob.`,
    42  	}
    43  
    44  	hostHeader = ParameterDescriptor{
    45  		Name:        "Host",
    46  		Type:        "string",
    47  		Description: "Standard HTTP Host Header. Should be set to the registry host.",
    48  		Format:      "<registry host>",
    49  		Examples:    []string{"registry-1.docker.io"},
    50  	}
    51  
    52  	authHeader = ParameterDescriptor{
    53  		Name:        "Authorization",
    54  		Type:        "string",
    55  		Description: "An RFC7235 compliant authorization header.",
    56  		Format:      "<scheme> <token>",
    57  		Examples:    []string{"Bearer dGhpcyBpcyBhIGZha2UgYmVhcmVyIHRva2VuIQ=="},
    58  	}
    59  
    60  	authChallengeHeader = ParameterDescriptor{
    61  		Name:        "WWW-Authenticate",
    62  		Type:        "string",
    63  		Description: "An RFC7235 compliant authentication challenge header.",
    64  		Format:      `<scheme> realm="<realm>", ..."`,
    65  		Examples: []string{
    66  			`Bearer realm="https://auth.docker.com/", service="registry.docker.com", scopes="repository:library/ubuntu:pull"`,
    67  		},
    68  	}
    69  
    70  	contentLengthZeroHeader = ParameterDescriptor{
    71  		Name:        "Content-Length",
    72  		Description: "The `Content-Length` header must be zero and the body must be empty.",
    73  		Type:        "integer",
    74  		Format:      "0",
    75  	}
    76  
    77  	dockerUploadUUIDHeader = ParameterDescriptor{
    78  		Name:        "Docker-Upload-UUID",
    79  		Description: "Identifies the docker upload uuid for the current request.",
    80  		Type:        "uuid",
    81  		Format:      "<uuid>",
    82  	}
    83  
    84  	digestHeader = ParameterDescriptor{
    85  		Name:        "Docker-Content-Digest",
    86  		Description: "Digest of the targeted content for the request.",
    87  		Type:        "digest",
    88  		Format:      "<digest>",
    89  	}
    90  
    91  	linkHeader = ParameterDescriptor{
    92  		Name:        "Link",
    93  		Type:        "link",
    94  		Description: "RFC5988 compliant rel='next' with URL to next result set, if available",
    95  		Format:      `<<url>?n=<last n value>&last=<last entry from response>>; rel="next"`,
    96  	}
    97  
    98  	paginationParameters = []ParameterDescriptor{
    99  		{
   100  			Name:        "n",
   101  			Type:        "integer",
   102  			Description: "Limit the number of entries in each response. It not present, all entries will be returned.",
   103  			Format:      "<integer>",
   104  			Required:    false,
   105  		},
   106  		{
   107  			Name:        "last",
   108  			Type:        "string",
   109  			Description: "Result set will include values lexically after last.",
   110  			Format:      "<integer>",
   111  			Required:    false,
   112  		},
   113  	}
   114  
   115  	unauthorizedResponseDescriptor = ResponseDescriptor{
   116  		Name:        "Authentication Required",
   117  		StatusCode:  http.StatusUnauthorized,
   118  		Description: "The client is not authenticated.",
   119  		Headers: []ParameterDescriptor{
   120  			authChallengeHeader,
   121  			{
   122  				Name:        "Content-Length",
   123  				Type:        "integer",
   124  				Description: "Length of the JSON response body.",
   125  				Format:      "<length>",
   126  			},
   127  		},
   128  		Body: BodyDescriptor{
   129  			ContentType: "application/json; charset=utf-8",
   130  			Format:      errorsBody,
   131  		},
   132  		ErrorCodes: []errcode.ErrorCode{
   133  			errcode.ErrorCodeUnauthorized,
   134  		},
   135  	}
   136  
   137  	repositoryNotFoundResponseDescriptor = ResponseDescriptor{
   138  		Name:        "No Such Repository Error",
   139  		StatusCode:  http.StatusNotFound,
   140  		Description: "The repository is not known to the registry.",
   141  		Headers: []ParameterDescriptor{
   142  			{
   143  				Name:        "Content-Length",
   144  				Type:        "integer",
   145  				Description: "Length of the JSON response body.",
   146  				Format:      "<length>",
   147  			},
   148  		},
   149  		Body: BodyDescriptor{
   150  			ContentType: "application/json; charset=utf-8",
   151  			Format:      errorsBody,
   152  		},
   153  		ErrorCodes: []errcode.ErrorCode{
   154  			ErrorCodeNameUnknown,
   155  		},
   156  	}
   157  
   158  	deniedResponseDescriptor = ResponseDescriptor{
   159  		Name:        "Access Denied",
   160  		StatusCode:  http.StatusForbidden,
   161  		Description: "The client does not have required access to the repository.",
   162  		Headers: []ParameterDescriptor{
   163  			{
   164  				Name:        "Content-Length",
   165  				Type:        "integer",
   166  				Description: "Length of the JSON response body.",
   167  				Format:      "<length>",
   168  			},
   169  		},
   170  		Body: BodyDescriptor{
   171  			ContentType: "application/json; charset=utf-8",
   172  			Format:      errorsBody,
   173  		},
   174  		ErrorCodes: []errcode.ErrorCode{
   175  			errcode.ErrorCodeDenied,
   176  		},
   177  	}
   178  )
   179  
   180  const (
   181  	manifestBody = `{
   182     "name": <name>,
   183     "tag": <tag>,
   184     "fsLayers": [
   185        {
   186           "blobSum": "<digest>"
   187        },
   188        ...
   189      ]
   190     ],
   191     "history": <v1 images>,
   192     "signature": <JWS>
   193  }`
   194  
   195  	errorsBody = `{
   196  	"errors:" [
   197  	    {
   198              "code": <error code>,
   199              "message": "<error message>",
   200              "detail": ...
   201          },
   202          ...
   203      ]
   204  }`
   205  
   206  	unauthorizedErrorsBody = `{
   207  	"errors:" [
   208  	    {
   209              "code": "UNAUTHORIZED",
   210              "message": "access to the requested resource is not authorized",
   211              "detail": ...
   212          },
   213          ...
   214      ]
   215  }`
   216  )
   217  
   218  // APIDescriptor exports descriptions of the layout of the v2 registry API.
   219  var APIDescriptor = struct {
   220  	// RouteDescriptors provides a list of the routes available in the API.
   221  	RouteDescriptors []RouteDescriptor
   222  }{
   223  	RouteDescriptors: routeDescriptors,
   224  }
   225  
   226  // RouteDescriptor describes a route specified by name.
   227  type RouteDescriptor struct {
   228  	// Name is the name of the route, as specified in RouteNameXXX exports.
   229  	// These names a should be considered a unique reference for a route. If
   230  	// the route is registered with gorilla, this is the name that will be
   231  	// used.
   232  	Name string
   233  
   234  	// Path is a gorilla/mux-compatible regexp that can be used to match the
   235  	// route. For any incoming method and path, only one route descriptor
   236  	// should match.
   237  	Path string
   238  
   239  	// Entity should be a short, human-readalbe description of the object
   240  	// targeted by the endpoint.
   241  	Entity string
   242  
   243  	// Description should provide an accurate overview of the functionality
   244  	// provided by the route.
   245  	Description string
   246  
   247  	// Methods should describe the various HTTP methods that may be used on
   248  	// this route, including request and response formats.
   249  	Methods []MethodDescriptor
   250  }
   251  
   252  // MethodDescriptor provides a description of the requests that may be
   253  // conducted with the target method.
   254  type MethodDescriptor struct {
   255  
   256  	// Method is an HTTP method, such as GET, PUT or POST.
   257  	Method string
   258  
   259  	// Description should provide an overview of the functionality provided by
   260  	// the covered method, suitable for use in documentation. Use of markdown
   261  	// here is encouraged.
   262  	Description string
   263  
   264  	// Requests is a slice of request descriptors enumerating how this
   265  	// endpoint may be used.
   266  	Requests []RequestDescriptor
   267  }
   268  
   269  // RequestDescriptor covers a particular set of headers and parameters that
   270  // can be carried out with the parent method. Its most helpful to have one
   271  // RequestDescriptor per API use case.
   272  type RequestDescriptor struct {
   273  	// Name provides a short identifier for the request, usable as a title or
   274  	// to provide quick context for the particalar request.
   275  	Name string
   276  
   277  	// Description should cover the requests purpose, covering any details for
   278  	// this particular use case.
   279  	Description string
   280  
   281  	// Headers describes headers that must be used with the HTTP request.
   282  	Headers []ParameterDescriptor
   283  
   284  	// PathParameters enumerate the parameterized path components for the
   285  	// given request, as defined in the route's regular expression.
   286  	PathParameters []ParameterDescriptor
   287  
   288  	// QueryParameters provides a list of query parameters for the given
   289  	// request.
   290  	QueryParameters []ParameterDescriptor
   291  
   292  	// Body describes the format of the request body.
   293  	Body BodyDescriptor
   294  
   295  	// Successes enumerates the possible responses that are considered to be
   296  	// the result of a successful request.
   297  	Successes []ResponseDescriptor
   298  
   299  	// Failures covers the possible failures from this particular request.
   300  	Failures []ResponseDescriptor
   301  }
   302  
   303  // ResponseDescriptor describes the components of an API response.
   304  type ResponseDescriptor struct {
   305  	// Name provides a short identifier for the response, usable as a title or
   306  	// to provide quick context for the particalar response.
   307  	Name string
   308  
   309  	// Description should provide a brief overview of the role of the
   310  	// response.
   311  	Description string
   312  
   313  	// StatusCode specifies the status recieved by this particular response.
   314  	StatusCode int
   315  
   316  	// Headers covers any headers that may be returned from the response.
   317  	Headers []ParameterDescriptor
   318  
   319  	// Fields describes any fields that may be present in the response.
   320  	Fields []ParameterDescriptor
   321  
   322  	// ErrorCodes enumerates the error codes that may be returned along with
   323  	// the response.
   324  	ErrorCodes []errcode.ErrorCode
   325  
   326  	// Body describes the body of the response, if any.
   327  	Body BodyDescriptor
   328  }
   329  
   330  // BodyDescriptor describes a request body and its expected content type. For
   331  // the most  part, it should be example json or some placeholder for body
   332  // data in documentation.
   333  type BodyDescriptor struct {
   334  	ContentType string
   335  	Format      string
   336  }
   337  
   338  // ParameterDescriptor describes the format of a request parameter, which may
   339  // be a header, path parameter or query parameter.
   340  type ParameterDescriptor struct {
   341  	// Name is the name of the parameter, either of the path component or
   342  	// query parameter.
   343  	Name string
   344  
   345  	// Type specifies the type of the parameter, such as string, integer, etc.
   346  	Type string
   347  
   348  	// Description provides a human-readable description of the parameter.
   349  	Description string
   350  
   351  	// Required means the field is required when set.
   352  	Required bool
   353  
   354  	// Format is a specifying the string format accepted by this parameter.
   355  	Format string
   356  
   357  	// Regexp is a compiled regular expression that can be used to validate
   358  	// the contents of the parameter.
   359  	Regexp *regexp.Regexp
   360  
   361  	// Examples provides multiple examples for the values that might be valid
   362  	// for this parameter.
   363  	Examples []string
   364  }
   365  
   366  var routeDescriptors = []RouteDescriptor{
   367  	{
   368  		Name:        RouteNameBase,
   369  		Path:        "/v2/",
   370  		Entity:      "Base",
   371  		Description: `Base V2 API route. Typically, this can be used for lightweight version checks and to validate registry authentication.`,
   372  		Methods: []MethodDescriptor{
   373  			{
   374  				Method:      "GET",
   375  				Description: "Check that the endpoint implements Docker Registry API V2.",
   376  				Requests: []RequestDescriptor{
   377  					{
   378  						Headers: []ParameterDescriptor{
   379  							hostHeader,
   380  							authHeader,
   381  						},
   382  						Successes: []ResponseDescriptor{
   383  							{
   384  								Description: "The API implements V2 protocol and is accessible.",
   385  								StatusCode:  http.StatusOK,
   386  							},
   387  						},
   388  						Failures: []ResponseDescriptor{
   389  							{
   390  								Description: "The registry does not implement the V2 API.",
   391  								StatusCode:  http.StatusNotFound,
   392  							},
   393  							unauthorizedResponseDescriptor,
   394  						},
   395  					},
   396  				},
   397  			},
   398  		},
   399  	},
   400  	{
   401  		Name:        RouteNameTags,
   402  		Path:        "/v2/{name:" + reference.NameRegexp.String() + "}/tags/list",
   403  		Entity:      "Tags",
   404  		Description: "Retrieve information about tags.",
   405  		Methods: []MethodDescriptor{
   406  			{
   407  				Method:      "GET",
   408  				Description: "Fetch the tags under the repository identified by `name`.",
   409  				Requests: []RequestDescriptor{
   410  					{
   411  						Name:        "Tags",
   412  						Description: "Return all tags for the repository",
   413  						Headers: []ParameterDescriptor{
   414  							hostHeader,
   415  							authHeader,
   416  						},
   417  						PathParameters: []ParameterDescriptor{
   418  							nameParameterDescriptor,
   419  						},
   420  						Successes: []ResponseDescriptor{
   421  							{
   422  								StatusCode:  http.StatusOK,
   423  								Description: "A list of tags for the named repository.",
   424  								Headers: []ParameterDescriptor{
   425  									{
   426  										Name:        "Content-Length",
   427  										Type:        "integer",
   428  										Description: "Length of the JSON response body.",
   429  										Format:      "<length>",
   430  									},
   431  								},
   432  								Body: BodyDescriptor{
   433  									ContentType: "application/json; charset=utf-8",
   434  									Format: `{
   435      "name": <name>,
   436      "tags": [
   437          <tag>,
   438          ...
   439      ]
   440  }`,
   441  								},
   442  							},
   443  						},
   444  						Failures: []ResponseDescriptor{
   445  							unauthorizedResponseDescriptor,
   446  							repositoryNotFoundResponseDescriptor,
   447  							deniedResponseDescriptor,
   448  						},
   449  					},
   450  					{
   451  						Name:            "Tags Paginated",
   452  						Description:     "Return a portion of the tags for the specified repository.",
   453  						PathParameters:  []ParameterDescriptor{nameParameterDescriptor},
   454  						QueryParameters: paginationParameters,
   455  						Successes: []ResponseDescriptor{
   456  							{
   457  								StatusCode:  http.StatusOK,
   458  								Description: "A list of tags for the named repository.",
   459  								Headers: []ParameterDescriptor{
   460  									{
   461  										Name:        "Content-Length",
   462  										Type:        "integer",
   463  										Description: "Length of the JSON response body.",
   464  										Format:      "<length>",
   465  									},
   466  									linkHeader,
   467  								},
   468  								Body: BodyDescriptor{
   469  									ContentType: "application/json; charset=utf-8",
   470  									Format: `{
   471      "name": <name>,
   472      "tags": [
   473          <tag>,
   474          ...
   475      ],
   476  }`,
   477  								},
   478  							},
   479  						},
   480  						Failures: []ResponseDescriptor{
   481  							unauthorizedResponseDescriptor,
   482  							repositoryNotFoundResponseDescriptor,
   483  							deniedResponseDescriptor,
   484  						},
   485  					},
   486  				},
   487  			},
   488  		},
   489  	},
   490  	{
   491  		Name:        RouteNameManifest,
   492  		Path:        "/v2/{name:" + reference.NameRegexp.String() + "}/manifests/{reference:" + reference.TagRegexp.String() + "|" + digest.DigestRegexp.String() + "}",
   493  		Entity:      "Manifest",
   494  		Description: "Create, update, delete and retrieve manifests.",
   495  		Methods: []MethodDescriptor{
   496  			{
   497  				Method:      "GET",
   498  				Description: "Fetch the manifest identified by `name` and `reference` where `reference` can be a tag or digest. A `HEAD` request can also be issued to this endpoint to obtain resource information without receiving all data.",
   499  				Requests: []RequestDescriptor{
   500  					{
   501  						Headers: []ParameterDescriptor{
   502  							hostHeader,
   503  							authHeader,
   504  						},
   505  						PathParameters: []ParameterDescriptor{
   506  							nameParameterDescriptor,
   507  							referenceParameterDescriptor,
   508  						},
   509  						Successes: []ResponseDescriptor{
   510  							{
   511  								Description: "The manifest identified by `name` and `reference`. The contents can be used to identify and resolve resources required to run the specified image.",
   512  								StatusCode:  http.StatusOK,
   513  								Headers: []ParameterDescriptor{
   514  									digestHeader,
   515  								},
   516  								Body: BodyDescriptor{
   517  									ContentType: "application/json; charset=utf-8",
   518  									Format:      manifestBody,
   519  								},
   520  							},
   521  						},
   522  						Failures: []ResponseDescriptor{
   523  							{
   524  								Description: "The name or reference was invalid.",
   525  								StatusCode:  http.StatusBadRequest,
   526  								ErrorCodes: []errcode.ErrorCode{
   527  									ErrorCodeNameInvalid,
   528  									ErrorCodeTagInvalid,
   529  								},
   530  								Body: BodyDescriptor{
   531  									ContentType: "application/json; charset=utf-8",
   532  									Format:      errorsBody,
   533  								},
   534  							},
   535  							unauthorizedResponseDescriptor,
   536  							repositoryNotFoundResponseDescriptor,
   537  							deniedResponseDescriptor,
   538  						},
   539  					},
   540  				},
   541  			},
   542  			{
   543  				Method:      "PUT",
   544  				Description: "Put the manifest identified by `name` and `reference` where `reference` can be a tag or digest.",
   545  				Requests: []RequestDescriptor{
   546  					{
   547  						Headers: []ParameterDescriptor{
   548  							hostHeader,
   549  							authHeader,
   550  						},
   551  						PathParameters: []ParameterDescriptor{
   552  							nameParameterDescriptor,
   553  							referenceParameterDescriptor,
   554  						},
   555  						Body: BodyDescriptor{
   556  							ContentType: "application/json; charset=utf-8",
   557  							Format:      manifestBody,
   558  						},
   559  						Successes: []ResponseDescriptor{
   560  							{
   561  								Description: "The manifest has been accepted by the registry and is stored under the specified `name` and `tag`.",
   562  								StatusCode:  http.StatusCreated,
   563  								Headers: []ParameterDescriptor{
   564  									{
   565  										Name:        "Location",
   566  										Type:        "url",
   567  										Description: "The canonical location url of the uploaded manifest.",
   568  										Format:      "<url>",
   569  									},
   570  									contentLengthZeroHeader,
   571  									digestHeader,
   572  								},
   573  							},
   574  						},
   575  						Failures: []ResponseDescriptor{
   576  							{
   577  								Name:        "Invalid Manifest",
   578  								Description: "The received manifest was invalid in some way, as described by the error codes. The client should resolve the issue and retry the request.",
   579  								StatusCode:  http.StatusBadRequest,
   580  								Body: BodyDescriptor{
   581  									ContentType: "application/json; charset=utf-8",
   582  									Format:      errorsBody,
   583  								},
   584  								ErrorCodes: []errcode.ErrorCode{
   585  									ErrorCodeNameInvalid,
   586  									ErrorCodeTagInvalid,
   587  									ErrorCodeManifestInvalid,
   588  									ErrorCodeManifestUnverified,
   589  									ErrorCodeBlobUnknown,
   590  								},
   591  							},
   592  							unauthorizedResponseDescriptor,
   593  							repositoryNotFoundResponseDescriptor,
   594  							deniedResponseDescriptor,
   595  							{
   596  								Name:        "Missing Layer(s)",
   597  								Description: "One or more layers may be missing during a manifest upload. If so, the missing layers will be enumerated in the error response.",
   598  								StatusCode:  http.StatusBadRequest,
   599  								ErrorCodes: []errcode.ErrorCode{
   600  									ErrorCodeBlobUnknown,
   601  								},
   602  								Body: BodyDescriptor{
   603  									ContentType: "application/json; charset=utf-8",
   604  									Format: `{
   605      "errors:" [{
   606              "code": "BLOB_UNKNOWN",
   607              "message": "blob unknown to registry",
   608              "detail": {
   609                  "digest": "<digest>"
   610              }
   611          },
   612          ...
   613      ]
   614  }`,
   615  								},
   616  							},
   617  							{
   618  								Name:        "Not allowed",
   619  								Description: "Manifest put is not allowed because the registry is configured as a pull-through cache or for some other reason",
   620  								StatusCode:  http.StatusMethodNotAllowed,
   621  								ErrorCodes: []errcode.ErrorCode{
   622  									errcode.ErrorCodeUnsupported,
   623  								},
   624  							},
   625  						},
   626  					},
   627  				},
   628  			},
   629  			{
   630  				Method:      "DELETE",
   631  				Description: "Delete the manifest identified by `name` and `reference`. Note that a manifest can _only_ be deleted by `digest`.",
   632  				Requests: []RequestDescriptor{
   633  					{
   634  						Headers: []ParameterDescriptor{
   635  							hostHeader,
   636  							authHeader,
   637  						},
   638  						PathParameters: []ParameterDescriptor{
   639  							nameParameterDescriptor,
   640  							referenceParameterDescriptor,
   641  						},
   642  						Successes: []ResponseDescriptor{
   643  							{
   644  								StatusCode: http.StatusAccepted,
   645  							},
   646  						},
   647  						Failures: []ResponseDescriptor{
   648  							{
   649  								Name:        "Invalid Name or Reference",
   650  								Description: "The specified `name` or `reference` were invalid and the delete was unable to proceed.",
   651  								StatusCode:  http.StatusBadRequest,
   652  								ErrorCodes: []errcode.ErrorCode{
   653  									ErrorCodeNameInvalid,
   654  									ErrorCodeTagInvalid,
   655  								},
   656  								Body: BodyDescriptor{
   657  									ContentType: "application/json; charset=utf-8",
   658  									Format:      errorsBody,
   659  								},
   660  							},
   661  							unauthorizedResponseDescriptor,
   662  							repositoryNotFoundResponseDescriptor,
   663  							deniedResponseDescriptor,
   664  							{
   665  								Name:        "Unknown Manifest",
   666  								Description: "The specified `name` or `reference` are unknown to the registry and the delete was unable to proceed. Clients can assume the manifest was already deleted if this response is returned.",
   667  								StatusCode:  http.StatusNotFound,
   668  								ErrorCodes: []errcode.ErrorCode{
   669  									ErrorCodeNameUnknown,
   670  									ErrorCodeManifestUnknown,
   671  								},
   672  								Body: BodyDescriptor{
   673  									ContentType: "application/json; charset=utf-8",
   674  									Format:      errorsBody,
   675  								},
   676  							},
   677  							{
   678  								Name:        "Not allowed",
   679  								Description: "Manifest delete is not allowed because the registry is configured as a pull-through cache or `delete` has been disabled.",
   680  								StatusCode:  http.StatusMethodNotAllowed,
   681  								ErrorCodes: []errcode.ErrorCode{
   682  									errcode.ErrorCodeUnsupported,
   683  								},
   684  							},
   685  						},
   686  					},
   687  				},
   688  			},
   689  		},
   690  	},
   691  
   692  	{
   693  		Name:        RouteNameBlob,
   694  		Path:        "/v2/{name:" + reference.NameRegexp.String() + "}/blobs/{digest:" + digest.DigestRegexp.String() + "}",
   695  		Entity:      "Blob",
   696  		Description: "Operations on blobs identified by `name` and `digest`. Used to fetch or delete layers by digest.",
   697  		Methods: []MethodDescriptor{
   698  			{
   699  				Method:      "GET",
   700  				Description: "Retrieve the blob from the registry identified by `digest`. A `HEAD` request can also be issued to this endpoint to obtain resource information without receiving all data.",
   701  				Requests: []RequestDescriptor{
   702  					{
   703  						Name: "Fetch Blob",
   704  						Headers: []ParameterDescriptor{
   705  							hostHeader,
   706  							authHeader,
   707  						},
   708  						PathParameters: []ParameterDescriptor{
   709  							nameParameterDescriptor,
   710  							digestPathParameter,
   711  						},
   712  						Successes: []ResponseDescriptor{
   713  							{
   714  								Description: "The blob identified by `digest` is available. The blob content will be present in the body of the request.",
   715  								StatusCode:  http.StatusOK,
   716  								Headers: []ParameterDescriptor{
   717  									{
   718  										Name:        "Content-Length",
   719  										Type:        "integer",
   720  										Description: "The length of the requested blob content.",
   721  										Format:      "<length>",
   722  									},
   723  									digestHeader,
   724  								},
   725  								Body: BodyDescriptor{
   726  									ContentType: "application/octet-stream",
   727  									Format:      "<blob binary data>",
   728  								},
   729  							},
   730  							{
   731  								Description: "The blob identified by `digest` is available at the provided location.",
   732  								StatusCode:  http.StatusTemporaryRedirect,
   733  								Headers: []ParameterDescriptor{
   734  									{
   735  										Name:        "Location",
   736  										Type:        "url",
   737  										Description: "The location where the layer should be accessible.",
   738  										Format:      "<blob location>",
   739  									},
   740  									digestHeader,
   741  								},
   742  							},
   743  						},
   744  						Failures: []ResponseDescriptor{
   745  							{
   746  								Description: "There was a problem with the request that needs to be addressed by the client, such as an invalid `name` or `tag`.",
   747  								StatusCode:  http.StatusBadRequest,
   748  								ErrorCodes: []errcode.ErrorCode{
   749  									ErrorCodeNameInvalid,
   750  									ErrorCodeDigestInvalid,
   751  								},
   752  								Body: BodyDescriptor{
   753  									ContentType: "application/json; charset=utf-8",
   754  									Format:      errorsBody,
   755  								},
   756  							},
   757  							{
   758  								Description: "The blob, identified by `name` and `digest`, is unknown to the registry.",
   759  								StatusCode:  http.StatusNotFound,
   760  								Body: BodyDescriptor{
   761  									ContentType: "application/json; charset=utf-8",
   762  									Format:      errorsBody,
   763  								},
   764  								ErrorCodes: []errcode.ErrorCode{
   765  									ErrorCodeNameUnknown,
   766  									ErrorCodeBlobUnknown,
   767  								},
   768  							},
   769  							unauthorizedResponseDescriptor,
   770  							repositoryNotFoundResponseDescriptor,
   771  							deniedResponseDescriptor,
   772  						},
   773  					},
   774  					{
   775  						Name:        "Fetch Blob Part",
   776  						Description: "This endpoint may also support RFC7233 compliant range requests. Support can be detected by issuing a HEAD request. If the header `Accept-Range: bytes` is returned, range requests can be used to fetch partial content.",
   777  						Headers: []ParameterDescriptor{
   778  							hostHeader,
   779  							authHeader,
   780  							{
   781  								Name:        "Range",
   782  								Type:        "string",
   783  								Description: "HTTP Range header specifying blob chunk.",
   784  								Format:      "bytes=<start>-<end>",
   785  							},
   786  						},
   787  						PathParameters: []ParameterDescriptor{
   788  							nameParameterDescriptor,
   789  							digestPathParameter,
   790  						},
   791  						Successes: []ResponseDescriptor{
   792  							{
   793  								Description: "The blob identified by `digest` is available. The specified chunk of blob content will be present in the body of the request.",
   794  								StatusCode:  http.StatusPartialContent,
   795  								Headers: []ParameterDescriptor{
   796  									{
   797  										Name:        "Content-Length",
   798  										Type:        "integer",
   799  										Description: "The length of the requested blob chunk.",
   800  										Format:      "<length>",
   801  									},
   802  									{
   803  										Name:        "Content-Range",
   804  										Type:        "byte range",
   805  										Description: "Content range of blob chunk.",
   806  										Format:      "bytes <start>-<end>/<size>",
   807  									},
   808  								},
   809  								Body: BodyDescriptor{
   810  									ContentType: "application/octet-stream",
   811  									Format:      "<blob binary data>",
   812  								},
   813  							},
   814  						},
   815  						Failures: []ResponseDescriptor{
   816  							{
   817  								Description: "There was a problem with the request that needs to be addressed by the client, such as an invalid `name` or `tag`.",
   818  								StatusCode:  http.StatusBadRequest,
   819  								ErrorCodes: []errcode.ErrorCode{
   820  									ErrorCodeNameInvalid,
   821  									ErrorCodeDigestInvalid,
   822  								},
   823  								Body: BodyDescriptor{
   824  									ContentType: "application/json; charset=utf-8",
   825  									Format:      errorsBody,
   826  								},
   827  							},
   828  							{
   829  								StatusCode: http.StatusNotFound,
   830  								ErrorCodes: []errcode.ErrorCode{
   831  									ErrorCodeNameUnknown,
   832  									ErrorCodeBlobUnknown,
   833  								},
   834  								Body: BodyDescriptor{
   835  									ContentType: "application/json; charset=utf-8",
   836  									Format:      errorsBody,
   837  								},
   838  							},
   839  							{
   840  								Description: "The range specification cannot be satisfied for the requested content. This can happen when the range is not formatted correctly or if the range is outside of the valid size of the content.",
   841  								StatusCode:  http.StatusRequestedRangeNotSatisfiable,
   842  							},
   843  							unauthorizedResponseDescriptor,
   844  							repositoryNotFoundResponseDescriptor,
   845  							deniedResponseDescriptor,
   846  						},
   847  					},
   848  				},
   849  			},
   850  			{
   851  				Method:      "DELETE",
   852  				Description: "Delete the blob identified by `name` and `digest`",
   853  				Requests: []RequestDescriptor{
   854  					{
   855  						Headers: []ParameterDescriptor{
   856  							hostHeader,
   857  							authHeader,
   858  						},
   859  						PathParameters: []ParameterDescriptor{
   860  							nameParameterDescriptor,
   861  							digestPathParameter,
   862  						},
   863  						Successes: []ResponseDescriptor{
   864  							{
   865  								StatusCode: http.StatusAccepted,
   866  								Headers: []ParameterDescriptor{
   867  									{
   868  										Name:        "Content-Length",
   869  										Type:        "integer",
   870  										Description: "0",
   871  										Format:      "0",
   872  									},
   873  									digestHeader,
   874  								},
   875  							},
   876  						},
   877  						Failures: []ResponseDescriptor{
   878  							{
   879  								Name:       "Invalid Name or Digest",
   880  								StatusCode: http.StatusBadRequest,
   881  								ErrorCodes: []errcode.ErrorCode{
   882  									ErrorCodeDigestInvalid,
   883  									ErrorCodeNameInvalid,
   884  								},
   885  							},
   886  							{
   887  								Description: "The blob, identified by `name` and `digest`, is unknown to the registry.",
   888  								StatusCode:  http.StatusNotFound,
   889  								Body: BodyDescriptor{
   890  									ContentType: "application/json; charset=utf-8",
   891  									Format:      errorsBody,
   892  								},
   893  								ErrorCodes: []errcode.ErrorCode{
   894  									ErrorCodeNameUnknown,
   895  									ErrorCodeBlobUnknown,
   896  								},
   897  							},
   898  							{
   899  								Description: "Blob delete is not allowed because the registry is configured as a pull-through cache or `delete` has been disabled",
   900  								StatusCode:  http.StatusMethodNotAllowed,
   901  								Body: BodyDescriptor{
   902  									ContentType: "application/json; charset=utf-8",
   903  									Format:      errorsBody,
   904  								},
   905  								ErrorCodes: []errcode.ErrorCode{
   906  									errcode.ErrorCodeUnsupported,
   907  								},
   908  							},
   909  							unauthorizedResponseDescriptor,
   910  							repositoryNotFoundResponseDescriptor,
   911  							deniedResponseDescriptor,
   912  						},
   913  					},
   914  				},
   915  			},
   916  
   917  			// TODO(stevvooe): We may want to add a PUT request here to
   918  			// kickoff an upload of a blob, integrated with the blob upload
   919  			// API.
   920  		},
   921  	},
   922  
   923  	{
   924  		Name:        RouteNameBlobUpload,
   925  		Path:        "/v2/{name:" + reference.NameRegexp.String() + "}/blobs/uploads/",
   926  		Entity:      "Initiate Blob Upload",
   927  		Description: "Initiate a blob upload. This endpoint can be used to create resumable uploads or monolithic uploads.",
   928  		Methods: []MethodDescriptor{
   929  			{
   930  				Method:      "POST",
   931  				Description: "Initiate a resumable blob upload. If successful, an upload location will be provided to complete the upload. Optionally, if the `digest` parameter is present, the request body will be used to complete the upload in a single request.",
   932  				Requests: []RequestDescriptor{
   933  					{
   934  						Name:        "Initiate Monolithic Blob Upload",
   935  						Description: "Upload a blob identified by the `digest` parameter in single request. This upload will not be resumable unless a recoverable error is returned.",
   936  						Headers: []ParameterDescriptor{
   937  							hostHeader,
   938  							authHeader,
   939  							{
   940  								Name:   "Content-Length",
   941  								Type:   "integer",
   942  								Format: "<length of blob>",
   943  							},
   944  						},
   945  						PathParameters: []ParameterDescriptor{
   946  							nameParameterDescriptor,
   947  						},
   948  						QueryParameters: []ParameterDescriptor{
   949  							{
   950  								Name:        "digest",
   951  								Type:        "query",
   952  								Format:      "<digest>",
   953  								Regexp:      digest.DigestRegexp,
   954  								Description: `Digest of uploaded blob. If present, the upload will be completed, in a single request, with contents of the request body as the resulting blob.`,
   955  							},
   956  						},
   957  						Body: BodyDescriptor{
   958  							ContentType: "application/octect-stream",
   959  							Format:      "<binary data>",
   960  						},
   961  						Successes: []ResponseDescriptor{
   962  							{
   963  								Description: "The blob has been created in the registry and is available at the provided location.",
   964  								StatusCode:  http.StatusCreated,
   965  								Headers: []ParameterDescriptor{
   966  									{
   967  										Name:   "Location",
   968  										Type:   "url",
   969  										Format: "<blob location>",
   970  									},
   971  									contentLengthZeroHeader,
   972  									dockerUploadUUIDHeader,
   973  								},
   974  							},
   975  						},
   976  						Failures: []ResponseDescriptor{
   977  							{
   978  								Name:       "Invalid Name or Digest",
   979  								StatusCode: http.StatusBadRequest,
   980  								ErrorCodes: []errcode.ErrorCode{
   981  									ErrorCodeDigestInvalid,
   982  									ErrorCodeNameInvalid,
   983  								},
   984  							},
   985  							{
   986  								Name:        "Not allowed",
   987  								Description: "Blob upload is not allowed because the registry is configured as a pull-through cache or for some other reason",
   988  								StatusCode:  http.StatusMethodNotAllowed,
   989  								ErrorCodes: []errcode.ErrorCode{
   990  									errcode.ErrorCodeUnsupported,
   991  								},
   992  							},
   993  							unauthorizedResponseDescriptor,
   994  							repositoryNotFoundResponseDescriptor,
   995  							deniedResponseDescriptor,
   996  						},
   997  					},
   998  					{
   999  						Name:        "Initiate Resumable Blob Upload",
  1000  						Description: "Initiate a resumable blob upload with an empty request body.",
  1001  						Headers: []ParameterDescriptor{
  1002  							hostHeader,
  1003  							authHeader,
  1004  							contentLengthZeroHeader,
  1005  						},
  1006  						PathParameters: []ParameterDescriptor{
  1007  							nameParameterDescriptor,
  1008  						},
  1009  						Successes: []ResponseDescriptor{
  1010  							{
  1011  								Description: "The upload has been created. The `Location` header must be used to complete the upload. The response should be identical to a `GET` request on the contents of the returned `Location` header.",
  1012  								StatusCode:  http.StatusAccepted,
  1013  								Headers: []ParameterDescriptor{
  1014  									contentLengthZeroHeader,
  1015  									{
  1016  										Name:        "Location",
  1017  										Type:        "url",
  1018  										Format:      "/v2/<name>/blobs/uploads/<uuid>",
  1019  										Description: "The location of the created upload. Clients should use the contents verbatim to complete the upload, adding parameters where required.",
  1020  									},
  1021  									{
  1022  										Name:        "Range",
  1023  										Format:      "0-0",
  1024  										Description: "Range header indicating the progress of the upload. When starting an upload, it will return an empty range, since no content has been received.",
  1025  									},
  1026  									dockerUploadUUIDHeader,
  1027  								},
  1028  							},
  1029  						},
  1030  						Failures: []ResponseDescriptor{
  1031  							{
  1032  								Name:       "Invalid Name or Digest",
  1033  								StatusCode: http.StatusBadRequest,
  1034  								ErrorCodes: []errcode.ErrorCode{
  1035  									ErrorCodeDigestInvalid,
  1036  									ErrorCodeNameInvalid,
  1037  								},
  1038  							},
  1039  							unauthorizedResponseDescriptor,
  1040  							repositoryNotFoundResponseDescriptor,
  1041  							deniedResponseDescriptor,
  1042  						},
  1043  					},
  1044  				},
  1045  			},
  1046  		},
  1047  	},
  1048  
  1049  	{
  1050  		Name:        RouteNameBlobUploadChunk,
  1051  		Path:        "/v2/{name:" + reference.NameRegexp.String() + "}/blobs/uploads/{uuid:[a-zA-Z0-9-_.=]+}",
  1052  		Entity:      "Blob Upload",
  1053  		Description: "Interact with blob uploads. Clients should never assemble URLs for this endpoint and should only take it through the `Location` header on related API requests. The `Location` header and its parameters should be preserved by clients, using the latest value returned via upload related API calls.",
  1054  		Methods: []MethodDescriptor{
  1055  			{
  1056  				Method:      "GET",
  1057  				Description: "Retrieve status of upload identified by `uuid`. The primary purpose of this endpoint is to resolve the current status of a resumable upload.",
  1058  				Requests: []RequestDescriptor{
  1059  					{
  1060  						Description: "Retrieve the progress of the current upload, as reported by the `Range` header.",
  1061  						Headers: []ParameterDescriptor{
  1062  							hostHeader,
  1063  							authHeader,
  1064  						},
  1065  						PathParameters: []ParameterDescriptor{
  1066  							nameParameterDescriptor,
  1067  							uuidParameterDescriptor,
  1068  						},
  1069  						Successes: []ResponseDescriptor{
  1070  							{
  1071  								Name:        "Upload Progress",
  1072  								Description: "The upload is known and in progress. The last received offset is available in the `Range` header.",
  1073  								StatusCode:  http.StatusNoContent,
  1074  								Headers: []ParameterDescriptor{
  1075  									{
  1076  										Name:        "Range",
  1077  										Type:        "header",
  1078  										Format:      "0-<offset>",
  1079  										Description: "Range indicating the current progress of the upload.",
  1080  									},
  1081  									contentLengthZeroHeader,
  1082  									dockerUploadUUIDHeader,
  1083  								},
  1084  							},
  1085  						},
  1086  						Failures: []ResponseDescriptor{
  1087  							{
  1088  								Description: "There was an error processing the upload and it must be restarted.",
  1089  								StatusCode:  http.StatusBadRequest,
  1090  								ErrorCodes: []errcode.ErrorCode{
  1091  									ErrorCodeDigestInvalid,
  1092  									ErrorCodeNameInvalid,
  1093  									ErrorCodeBlobUploadInvalid,
  1094  								},
  1095  								Body: BodyDescriptor{
  1096  									ContentType: "application/json; charset=utf-8",
  1097  									Format:      errorsBody,
  1098  								},
  1099  							},
  1100  							{
  1101  								Description: "The upload is unknown to the registry. The upload must be restarted.",
  1102  								StatusCode:  http.StatusNotFound,
  1103  								ErrorCodes: []errcode.ErrorCode{
  1104  									ErrorCodeBlobUploadUnknown,
  1105  								},
  1106  								Body: BodyDescriptor{
  1107  									ContentType: "application/json; charset=utf-8",
  1108  									Format:      errorsBody,
  1109  								},
  1110  							},
  1111  							unauthorizedResponseDescriptor,
  1112  							repositoryNotFoundResponseDescriptor,
  1113  							deniedResponseDescriptor,
  1114  						},
  1115  					},
  1116  				},
  1117  			},
  1118  			{
  1119  				Method:      "PATCH",
  1120  				Description: "Upload a chunk of data for the specified upload.",
  1121  				Requests: []RequestDescriptor{
  1122  					{
  1123  						Name:        "Stream upload",
  1124  						Description: "Upload a stream of data to upload without completing the upload.",
  1125  						PathParameters: []ParameterDescriptor{
  1126  							nameParameterDescriptor,
  1127  							uuidParameterDescriptor,
  1128  						},
  1129  						Headers: []ParameterDescriptor{
  1130  							hostHeader,
  1131  							authHeader,
  1132  						},
  1133  						Body: BodyDescriptor{
  1134  							ContentType: "application/octet-stream",
  1135  							Format:      "<binary data>",
  1136  						},
  1137  						Successes: []ResponseDescriptor{
  1138  							{
  1139  								Name:        "Data Accepted",
  1140  								Description: "The stream of data has been accepted and the current progress is available in the range header. The updated upload location is available in the `Location` header.",
  1141  								StatusCode:  http.StatusNoContent,
  1142  								Headers: []ParameterDescriptor{
  1143  									{
  1144  										Name:        "Location",
  1145  										Type:        "url",
  1146  										Format:      "/v2/<name>/blobs/uploads/<uuid>",
  1147  										Description: "The location of the upload. Clients should assume this changes after each request. Clients should use the contents verbatim to complete the upload, adding parameters where required.",
  1148  									},
  1149  									{
  1150  										Name:        "Range",
  1151  										Type:        "header",
  1152  										Format:      "0-<offset>",
  1153  										Description: "Range indicating the current progress of the upload.",
  1154  									},
  1155  									contentLengthZeroHeader,
  1156  									dockerUploadUUIDHeader,
  1157  								},
  1158  							},
  1159  						},
  1160  						Failures: []ResponseDescriptor{
  1161  							{
  1162  								Description: "There was an error processing the upload and it must be restarted.",
  1163  								StatusCode:  http.StatusBadRequest,
  1164  								ErrorCodes: []errcode.ErrorCode{
  1165  									ErrorCodeDigestInvalid,
  1166  									ErrorCodeNameInvalid,
  1167  									ErrorCodeBlobUploadInvalid,
  1168  								},
  1169  								Body: BodyDescriptor{
  1170  									ContentType: "application/json; charset=utf-8",
  1171  									Format:      errorsBody,
  1172  								},
  1173  							},
  1174  							{
  1175  								Description: "The upload is unknown to the registry. The upload must be restarted.",
  1176  								StatusCode:  http.StatusNotFound,
  1177  								ErrorCodes: []errcode.ErrorCode{
  1178  									ErrorCodeBlobUploadUnknown,
  1179  								},
  1180  								Body: BodyDescriptor{
  1181  									ContentType: "application/json; charset=utf-8",
  1182  									Format:      errorsBody,
  1183  								},
  1184  							},
  1185  							unauthorizedResponseDescriptor,
  1186  							repositoryNotFoundResponseDescriptor,
  1187  							deniedResponseDescriptor,
  1188  						},
  1189  					},
  1190  					{
  1191  						Name:        "Chunked upload",
  1192  						Description: "Upload a chunk of data to specified upload without completing the upload. The data will be uploaded to the specified Content Range.",
  1193  						PathParameters: []ParameterDescriptor{
  1194  							nameParameterDescriptor,
  1195  							uuidParameterDescriptor,
  1196  						},
  1197  						Headers: []ParameterDescriptor{
  1198  							hostHeader,
  1199  							authHeader,
  1200  							{
  1201  								Name:        "Content-Range",
  1202  								Type:        "header",
  1203  								Format:      "<start of range>-<end of range, inclusive>",
  1204  								Required:    true,
  1205  								Description: "Range of bytes identifying the desired block of content represented by the body. Start must the end offset retrieved via status check plus one. Note that this is a non-standard use of the `Content-Range` header.",
  1206  							},
  1207  							{
  1208  								Name:        "Content-Length",
  1209  								Type:        "integer",
  1210  								Format:      "<length of chunk>",
  1211  								Description: "Length of the chunk being uploaded, corresponding the length of the request body.",
  1212  							},
  1213  						},
  1214  						Body: BodyDescriptor{
  1215  							ContentType: "application/octet-stream",
  1216  							Format:      "<binary chunk>",
  1217  						},
  1218  						Successes: []ResponseDescriptor{
  1219  							{
  1220  								Name:        "Chunk Accepted",
  1221  								Description: "The chunk of data has been accepted and the current progress is available in the range header. The updated upload location is available in the `Location` header.",
  1222  								StatusCode:  http.StatusNoContent,
  1223  								Headers: []ParameterDescriptor{
  1224  									{
  1225  										Name:        "Location",
  1226  										Type:        "url",
  1227  										Format:      "/v2/<name>/blobs/uploads/<uuid>",
  1228  										Description: "The location of the upload. Clients should assume this changes after each request. Clients should use the contents verbatim to complete the upload, adding parameters where required.",
  1229  									},
  1230  									{
  1231  										Name:        "Range",
  1232  										Type:        "header",
  1233  										Format:      "0-<offset>",
  1234  										Description: "Range indicating the current progress of the upload.",
  1235  									},
  1236  									contentLengthZeroHeader,
  1237  									dockerUploadUUIDHeader,
  1238  								},
  1239  							},
  1240  						},
  1241  						Failures: []ResponseDescriptor{
  1242  							{
  1243  								Description: "There was an error processing the upload and it must be restarted.",
  1244  								StatusCode:  http.StatusBadRequest,
  1245  								ErrorCodes: []errcode.ErrorCode{
  1246  									ErrorCodeDigestInvalid,
  1247  									ErrorCodeNameInvalid,
  1248  									ErrorCodeBlobUploadInvalid,
  1249  								},
  1250  								Body: BodyDescriptor{
  1251  									ContentType: "application/json; charset=utf-8",
  1252  									Format:      errorsBody,
  1253  								},
  1254  							},
  1255  							{
  1256  								Description: "The upload is unknown to the registry. The upload must be restarted.",
  1257  								StatusCode:  http.StatusNotFound,
  1258  								ErrorCodes: []errcode.ErrorCode{
  1259  									ErrorCodeBlobUploadUnknown,
  1260  								},
  1261  								Body: BodyDescriptor{
  1262  									ContentType: "application/json; charset=utf-8",
  1263  									Format:      errorsBody,
  1264  								},
  1265  							},
  1266  							{
  1267  								Description: "The `Content-Range` specification cannot be accepted, either because it does not overlap with the current progress or it is invalid.",
  1268  								StatusCode:  http.StatusRequestedRangeNotSatisfiable,
  1269  							},
  1270  							unauthorizedResponseDescriptor,
  1271  							repositoryNotFoundResponseDescriptor,
  1272  							deniedResponseDescriptor,
  1273  						},
  1274  					},
  1275  				},
  1276  			},
  1277  			{
  1278  				Method:      "PUT",
  1279  				Description: "Complete the upload specified by `uuid`, optionally appending the body as the final chunk.",
  1280  				Requests: []RequestDescriptor{
  1281  					{
  1282  						Description: "Complete the upload, providing all the data in the body, if necessary. A request without a body will just complete the upload with previously uploaded content.",
  1283  						Headers: []ParameterDescriptor{
  1284  							hostHeader,
  1285  							authHeader,
  1286  							{
  1287  								Name:        "Content-Length",
  1288  								Type:        "integer",
  1289  								Format:      "<length of data>",
  1290  								Description: "Length of the data being uploaded, corresponding to the length of the request body. May be zero if no data is provided.",
  1291  							},
  1292  						},
  1293  						PathParameters: []ParameterDescriptor{
  1294  							nameParameterDescriptor,
  1295  							uuidParameterDescriptor,
  1296  						},
  1297  						QueryParameters: []ParameterDescriptor{
  1298  							{
  1299  								Name:        "digest",
  1300  								Type:        "string",
  1301  								Format:      "<digest>",
  1302  								Regexp:      digest.DigestRegexp,
  1303  								Required:    true,
  1304  								Description: `Digest of uploaded blob.`,
  1305  							},
  1306  						},
  1307  						Body: BodyDescriptor{
  1308  							ContentType: "application/octet-stream",
  1309  							Format:      "<binary data>",
  1310  						},
  1311  						Successes: []ResponseDescriptor{
  1312  							{
  1313  								Name:        "Upload Complete",
  1314  								Description: "The upload has been completed and accepted by the registry. The canonical location will be available in the `Location` header.",
  1315  								StatusCode:  http.StatusNoContent,
  1316  								Headers: []ParameterDescriptor{
  1317  									{
  1318  										Name:        "Location",
  1319  										Type:        "url",
  1320  										Format:      "<blob location>",
  1321  										Description: "The canonical location of the blob for retrieval",
  1322  									},
  1323  									{
  1324  										Name:        "Content-Range",
  1325  										Type:        "header",
  1326  										Format:      "<start of range>-<end of range, inclusive>",
  1327  										Description: "Range of bytes identifying the desired block of content represented by the body. Start must match the end of offset retrieved via status check. Note that this is a non-standard use of the `Content-Range` header.",
  1328  									},
  1329  									contentLengthZeroHeader,
  1330  									digestHeader,
  1331  								},
  1332  							},
  1333  						},
  1334  						Failures: []ResponseDescriptor{
  1335  							{
  1336  								Description: "There was an error processing the upload and it must be restarted.",
  1337  								StatusCode:  http.StatusBadRequest,
  1338  								ErrorCodes: []errcode.ErrorCode{
  1339  									ErrorCodeDigestInvalid,
  1340  									ErrorCodeNameInvalid,
  1341  									ErrorCodeBlobUploadInvalid,
  1342  									errcode.ErrorCodeUnsupported,
  1343  								},
  1344  								Body: BodyDescriptor{
  1345  									ContentType: "application/json; charset=utf-8",
  1346  									Format:      errorsBody,
  1347  								},
  1348  							},
  1349  							{
  1350  								Description: "The upload is unknown to the registry. The upload must be restarted.",
  1351  								StatusCode:  http.StatusNotFound,
  1352  								ErrorCodes: []errcode.ErrorCode{
  1353  									ErrorCodeBlobUploadUnknown,
  1354  								},
  1355  								Body: BodyDescriptor{
  1356  									ContentType: "application/json; charset=utf-8",
  1357  									Format:      errorsBody,
  1358  								},
  1359  							},
  1360  							unauthorizedResponseDescriptor,
  1361  							repositoryNotFoundResponseDescriptor,
  1362  							deniedResponseDescriptor,
  1363  						},
  1364  					},
  1365  				},
  1366  			},
  1367  			{
  1368  				Method:      "DELETE",
  1369  				Description: "Cancel outstanding upload processes, releasing associated resources. If this is not called, the unfinished uploads will eventually timeout.",
  1370  				Requests: []RequestDescriptor{
  1371  					{
  1372  						Description: "Cancel the upload specified by `uuid`.",
  1373  						PathParameters: []ParameterDescriptor{
  1374  							nameParameterDescriptor,
  1375  							uuidParameterDescriptor,
  1376  						},
  1377  						Headers: []ParameterDescriptor{
  1378  							hostHeader,
  1379  							authHeader,
  1380  							contentLengthZeroHeader,
  1381  						},
  1382  						Successes: []ResponseDescriptor{
  1383  							{
  1384  								Name:        "Upload Deleted",
  1385  								Description: "The upload has been successfully deleted.",
  1386  								StatusCode:  http.StatusNoContent,
  1387  								Headers: []ParameterDescriptor{
  1388  									contentLengthZeroHeader,
  1389  								},
  1390  							},
  1391  						},
  1392  						Failures: []ResponseDescriptor{
  1393  							{
  1394  								Description: "An error was encountered processing the delete. The client may ignore this error.",
  1395  								StatusCode:  http.StatusBadRequest,
  1396  								ErrorCodes: []errcode.ErrorCode{
  1397  									ErrorCodeNameInvalid,
  1398  									ErrorCodeBlobUploadInvalid,
  1399  								},
  1400  								Body: BodyDescriptor{
  1401  									ContentType: "application/json; charset=utf-8",
  1402  									Format:      errorsBody,
  1403  								},
  1404  							},
  1405  							{
  1406  								Description: "The upload is unknown to the registry. The client may ignore this error and assume the upload has been deleted.",
  1407  								StatusCode:  http.StatusNotFound,
  1408  								ErrorCodes: []errcode.ErrorCode{
  1409  									ErrorCodeBlobUploadUnknown,
  1410  								},
  1411  								Body: BodyDescriptor{
  1412  									ContentType: "application/json; charset=utf-8",
  1413  									Format:      errorsBody,
  1414  								},
  1415  							},
  1416  							unauthorizedResponseDescriptor,
  1417  							repositoryNotFoundResponseDescriptor,
  1418  							deniedResponseDescriptor,
  1419  						},
  1420  					},
  1421  				},
  1422  			},
  1423  		},
  1424  	},
  1425  	{
  1426  		Name:        RouteNameCatalog,
  1427  		Path:        "/v2/_catalog",
  1428  		Entity:      "Catalog",
  1429  		Description: "List a set of available repositories in the local registry cluster. Does not provide any indication of what may be available upstream. Applications can only determine if a repository is available but not if it is not available.",
  1430  		Methods: []MethodDescriptor{
  1431  			{
  1432  				Method:      "GET",
  1433  				Description: "Retrieve a sorted, json list of repositories available in the registry.",
  1434  				Requests: []RequestDescriptor{
  1435  					{
  1436  						Name:        "Catalog Fetch Complete",
  1437  						Description: "Request an unabridged list of repositories available.",
  1438  						Successes: []ResponseDescriptor{
  1439  							{
  1440  								Description: "Returns the unabridged list of repositories as a json response.",
  1441  								StatusCode:  http.StatusOK,
  1442  								Headers: []ParameterDescriptor{
  1443  									{
  1444  										Name:        "Content-Length",
  1445  										Type:        "integer",
  1446  										Description: "Length of the JSON response body.",
  1447  										Format:      "<length>",
  1448  									},
  1449  								},
  1450  								Body: BodyDescriptor{
  1451  									ContentType: "application/json; charset=utf-8",
  1452  									Format: `{
  1453  	"repositories": [
  1454  		<name>,
  1455  		...
  1456  	]
  1457  }`,
  1458  								},
  1459  							},
  1460  						},
  1461  					},
  1462  					{
  1463  						Name:            "Catalog Fetch Paginated",
  1464  						Description:     "Return the specified portion of repositories.",
  1465  						QueryParameters: paginationParameters,
  1466  						Successes: []ResponseDescriptor{
  1467  							{
  1468  								StatusCode: http.StatusOK,
  1469  								Body: BodyDescriptor{
  1470  									ContentType: "application/json; charset=utf-8",
  1471  									Format: `{
  1472  	"repositories": [
  1473  		<name>,
  1474  		...
  1475  	]
  1476  	"next": "<url>?last=<name>&n=<last value of n>"
  1477  }`,
  1478  								},
  1479  								Headers: []ParameterDescriptor{
  1480  									{
  1481  										Name:        "Content-Length",
  1482  										Type:        "integer",
  1483  										Description: "Length of the JSON response body.",
  1484  										Format:      "<length>",
  1485  									},
  1486  									linkHeader,
  1487  								},
  1488  							},
  1489  						},
  1490  					},
  1491  				},
  1492  			},
  1493  		},
  1494  	},
  1495  }
  1496  
  1497  var routeDescriptorsMap map[string]RouteDescriptor
  1498  
  1499  func init() {
  1500  	routeDescriptorsMap = make(map[string]RouteDescriptor, len(routeDescriptors))
  1501  
  1502  	for _, descriptor := range routeDescriptors {
  1503  		routeDescriptorsMap[descriptor.Name] = descriptor
  1504  	}
  1505  }