...
1
15 package remote
16
17 import (
18 "context"
19 "encoding/json"
20 "fmt"
21 "net/http"
22 "strconv"
23
24 errdef "oras.land/oras-go/pkg/content"
25 "oras.land/oras-go/pkg/registry"
26 "oras.land/oras-go/pkg/registry/remote/auth"
27 "oras.land/oras-go/pkg/registry/remote/internal/errutil"
28 )
29
30
31 type Client interface {
32
33
34
35
36
37
38
39
40 Do(*http.Request) (*http.Response, error)
41 }
42
43
44 type Repository struct {
45
46
47 Client Client
48
49
50 Reference registry.Reference
51
52
53
54 PlainHTTP bool
55
56
57
58
59
60 ManifestMediaTypes []string
61
62
63
64
65 TagListPageSize int
66
67
68
69
70
71 ReferrerListPageSize int
72
73
74
75
76
77 MaxMetadataBytes int64
78 }
79
80
81
82
83 func NewRepository(reference string) (*Repository, error) {
84 ref, err := registry.ParseReference(reference)
85 if err != nil {
86 return nil, err
87 }
88 return &Repository{
89 Reference: ref,
90 }, nil
91 }
92
93
94
95 func (r *Repository) client() Client {
96 if r.Client == nil {
97 return auth.DefaultClient
98 }
99 return r.Client
100 }
101
102
103
104
105 func (r *Repository) parseReference(reference string) (registry.Reference, error) {
106 ref, err := registry.ParseReference(reference)
107 if err != nil {
108 ref = registry.Reference{
109 Registry: r.Reference.Registry,
110 Repository: r.Reference.Repository,
111 Reference: reference,
112 }
113 if err = ref.ValidateReference(); err != nil {
114 return registry.Reference{}, err
115 }
116 return ref, nil
117 }
118 if ref.Registry == r.Reference.Registry && ref.Repository == r.Reference.Repository {
119 return ref, nil
120 }
121 return registry.Reference{}, fmt.Errorf("%w %q: expect %q", errdef.ErrInvalidReference, ref, r.Reference)
122 }
123
124
125 func (r *Repository) Tags(ctx context.Context, fn func(tags []string) error) error {
126 ctx = withScopeHint(ctx, r.Reference, auth.ActionPull)
127 url := buildRepositoryTagListURL(r.PlainHTTP, r.Reference)
128 var err error
129 for err == nil {
130 url, err = r.tags(ctx, fn, url)
131 }
132 if err != errNoLink {
133 return err
134 }
135 return nil
136 }
137
138
139 func (r *Repository) tags(ctx context.Context, fn func(tags []string) error, url string) (string, error) {
140 req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
141 if err != nil {
142 return "", err
143 }
144 if r.TagListPageSize > 0 {
145 q := req.URL.Query()
146 q.Set("n", strconv.Itoa(r.TagListPageSize))
147 req.URL.RawQuery = q.Encode()
148 }
149
150 resp, err := r.client().Do(req)
151 if err != nil {
152 return "", err
153 }
154 defer resp.Body.Close()
155
156 if resp.StatusCode != http.StatusOK {
157 return "", errutil.ParseErrorResponse(resp)
158 }
159 var page struct {
160 Tags []string `json:"tags"`
161 }
162 lr := limitReader(resp.Body, r.MaxMetadataBytes)
163 if err := json.NewDecoder(lr).Decode(&page); err != nil {
164 return "", fmt.Errorf("%s %q: failed to decode response: %w", resp.Request.Method, resp.Request.URL, err)
165 }
166 if err := fn(page.Tags); err != nil {
167 return "", err
168 }
169
170 return parseLink(resp)
171 }
172
View as plain text