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