...

Source file src/github.com/docker/distribution/registry/api/v2/descriptors.go

Documentation: github.com/docker/distribution/registry/api/v2

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

View as plain text