1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package remote
16
17 import (
18 "context"
19 "encoding/json"
20 "fmt"
21 "net/http"
22 "net/url"
23
24 "github.com/google/go-containerregistry/pkg/name"
25 "github.com/google/go-containerregistry/pkg/v1/remote/transport"
26 )
27
28 type Catalogs struct {
29 Repos []string `json:"repositories"`
30 Next string `json:"next,omitempty"`
31 }
32
33
34 func CatalogPage(target name.Registry, last string, n int, options ...Option) ([]string, error) {
35 o, err := makeOptions(options...)
36 if err != nil {
37 return nil, err
38 }
39
40 f, err := newPuller(o).fetcher(o.context, target)
41 if err != nil {
42 return nil, err
43 }
44
45 uri := url.URL{
46 Scheme: target.Scheme(),
47 Host: target.RegistryStr(),
48 Path: "/v2/_catalog",
49 RawQuery: fmt.Sprintf("last=%s&n=%d", url.QueryEscape(last), n),
50 }
51
52 req, err := http.NewRequest(http.MethodGet, uri.String(), nil)
53 if err != nil {
54 return nil, err
55 }
56 resp, err := f.client.Do(req.WithContext(o.context))
57 if err != nil {
58 return nil, err
59 }
60 defer resp.Body.Close()
61
62 if err := transport.CheckError(resp, http.StatusOK); err != nil {
63 return nil, err
64 }
65
66 var parsed Catalogs
67 if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil {
68 return nil, err
69 }
70
71 return parsed.Repos, nil
72 }
73
74
75 func Catalog(ctx context.Context, target name.Registry, options ...Option) ([]string, error) {
76 o, err := makeOptions(options...)
77 if err != nil {
78 return nil, err
79 }
80
81
82 if o.context != context.Background() {
83 ctx = o.context
84 }
85
86 return newPuller(o).catalog(ctx, target, o.pageSize)
87 }
88
89 func (f *fetcher) catalogPage(ctx context.Context, reg name.Registry, next string, pageSize int) (*Catalogs, error) {
90 if next == "" {
91 uri := &url.URL{
92 Scheme: reg.Scheme(),
93 Host: reg.RegistryStr(),
94 Path: "/v2/_catalog",
95 }
96 if pageSize > 0 {
97 uri.RawQuery = fmt.Sprintf("n=%d", pageSize)
98 }
99 next = uri.String()
100 }
101
102 req, err := http.NewRequestWithContext(ctx, "GET", next, nil)
103 if err != nil {
104 return nil, err
105 }
106
107 resp, err := f.client.Do(req)
108 if err != nil {
109 return nil, err
110 }
111
112 if err := transport.CheckError(resp, http.StatusOK); err != nil {
113 return nil, err
114 }
115
116 parsed := Catalogs{}
117 if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil {
118 return nil, err
119 }
120
121 if err := resp.Body.Close(); err != nil {
122 return nil, err
123 }
124
125 uri, err := getNextPageURL(resp)
126 if err != nil {
127 return nil, err
128 }
129
130 if uri != nil {
131 parsed.Next = uri.String()
132 }
133
134 return &parsed, nil
135 }
136
137 type Catalogger struct {
138 f *fetcher
139 reg name.Registry
140 pageSize int
141
142 page *Catalogs
143 err error
144
145 needMore bool
146 }
147
148 func (l *Catalogger) Next(ctx context.Context) (*Catalogs, error) {
149 if l.needMore {
150 l.page, l.err = l.f.catalogPage(ctx, l.reg, l.page.Next, l.pageSize)
151 } else {
152 l.needMore = true
153 }
154 return l.page, l.err
155 }
156
157 func (l *Catalogger) HasNext() bool {
158 return l.page != nil && (!l.needMore || l.page.Next != "")
159 }
160
View as plain text