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