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