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