Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(710)

Side by Side Diff: go/src/infra/tools/cipd/remote.go

Issue 1129043003: cipd: Refactor client to make it more readable. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « go/src/infra/tools/cipd/reader_test.go ('k') | go/src/infra/tools/cipd/remote_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package cipd 5 package cipd
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "encoding/json" 9 "encoding/json"
10 "errors" 10 "errors"
11 "fmt" 11 "fmt"
12 "io" 12 "io"
13 "io/ioutil" 13 "io/ioutil"
14 "net/http" 14 "net/http"
15 "net/url" 15 "net/url"
16 "strconv" 16 "strconv"
17 "time" 17 "time"
18 18
19 » "infra/libs/logging" 19 » "infra/tools/cipd/common"
20 ) 20 )
21 21
22 // remoteService is a wrapper around Cloud Endpoints APIs exposed by backend. 22 // remoteMaxRetries is how many times to retry transient HTTP errors.
23 // See //appengine/chrome_infra_packages. 23 const remoteMaxRetries = 10
24 type remoteService struct {
25 » client *http.Client
26 » serviceURL string
27 » log logging.Logger
28 }
29 24
30 type uploadSession struct {
31 ID string
32 URL string
33 }
34
35 // packageInstanceMsg corresponds to PackageInstance message on the backend.
36 type packageInstanceMsg struct { 25 type packageInstanceMsg struct {
37 PackageName string `json:"package_name"` 26 PackageName string `json:"package_name"`
38 InstanceID string `json:"instance_id"` 27 InstanceID string `json:"instance_id"`
39 RegisteredBy string `json:"registered_by"` 28 RegisteredBy string `json:"registered_by"`
40 RegisteredTs string `json:"registered_ts"` 29 RegisteredTs string `json:"registered_ts"`
41 } 30 }
42 31
43 // packageInstance is almost like packageInstanceMsg, but with timestamp as
44 // time.Time. See also makePackageInstanceInfo.
45 type packageInstanceInfo struct {
46 PackageName string
47 InstanceID string
48 RegisteredBy string
49 RegisteredTs time.Time
50 }
51
52 type registerInstanceResponse struct {
53 UploadSession *uploadSession
54 AlreadyRegistered bool
55 Info packageInstanceInfo
56 }
57
58 type fetchInstanceResponse struct {
59 FetchURL string
60 Info packageInstanceInfo
61 }
62
63 // roleChangeMsg corresponds to RoleChange proto message on backend. 32 // roleChangeMsg corresponds to RoleChange proto message on backend.
64 type roleChangeMsg struct { 33 type roleChangeMsg struct {
65 Action string `json:"action"` 34 Action string `json:"action"`
66 Role string `json:"role"` 35 Role string `json:"role"`
67 Principal string `json:"principal"` 36 Principal string `json:"principal"`
68 } 37 }
69 38
70 // pendingProcessingError is returned by attachTags if package instance is not 39 // pendingProcessingError is returned by attachTags if package instance is not
71 // yet ready and the call should be retried later. 40 // yet ready and the call should be retried later.
72 type pendingProcessingError struct { 41 type pendingProcessingError struct {
73 message string 42 message string
74 } 43 }
75 44
76 func (e *pendingProcessingError) Error() string { 45 func (e *pendingProcessingError) Error() string {
77 return e.message 46 return e.message
78 } 47 }
79 48
80 // newRemoteService is mocked in tests. 49 // remoteImpl implements remote on top of real HTTP calls.
81 var newRemoteService = func(client *http.Client, url string, log logging.Logger) *remoteService { 50 type remoteImpl struct {
82 » log.Infof("cipd: service URL is %s", url) 51 » client *Client
83 » return &remoteService{
84 » » client: client,
85 » » serviceURL: url,
86 » » log: log,
87 » }
88 } 52 }
89 53
90 // makeRequest sends POST or GET request with retries. 54 func isTemporaryNetError(err error) bool {
91 func (r *remoteService) makeRequest(path, method string, request, response inter face{}) error { 55 » // TODO(vadimsh): Figure out how to recognize dial timeouts, read timeou ts,
56 » // etc. For now all errors that end up here are considered temporary.
57 » return true
58 }
59
60 // isTemporaryHTTPError returns true for HTTP status codes that indicate
61 // a temporary error that may go away if request is retried.
62 func isTemporaryHTTPError(statusCode int) bool {
63 » return statusCode >= 500 || statusCode == 408 || statusCode == 429
64 }
65
66 // makeRequest sends POST or GET REST JSON requests with retries.
67 func (r *remoteImpl) makeRequest(path, method string, request, response interfac e{}) error {
92 var body []byte 68 var body []byte
93 var err error
94 if request != nil { 69 if request != nil {
95 » » body, err = json.Marshal(request) 70 » » b, err := json.Marshal(request)
96 if err != nil { 71 if err != nil {
97 return err 72 return err
98 } 73 }
74 body = b
99 } 75 }
100 » url := fmt.Sprintf("%s/_ah/api/%s", r.serviceURL, path) 76
101 » for attempt := 0; attempt < 10; attempt++ { 77 » url := fmt.Sprintf("%s/_ah/api/%s", r.client.ServiceURL, path)
78 » for attempt := 0; attempt < remoteMaxRetries; attempt++ {
102 if attempt != 0 { 79 if attempt != 0 {
103 » » » r.log.Warningf("cipd: retrying request to %s", url) 80 » » » r.client.Log.Warningf("cipd: retrying request to %s", ur l)
104 » » » clock.Sleep(2 * time.Second) 81 » » » r.client.clock.sleep(2 * time.Second)
105 } 82 }
83
84 // Prepare request.
106 var bodyReader io.Reader 85 var bodyReader io.Reader
107 if body != nil { 86 if body != nil {
108 bodyReader = bytes.NewReader(body) 87 bodyReader = bytes.NewReader(body)
109 } 88 }
110 req, err := http.NewRequest(method, url, bodyReader) 89 req, err := http.NewRequest(method, url, bodyReader)
111 if err != nil { 90 if err != nil {
112 return err 91 return err
113 } 92 }
114 if body != nil { 93 if body != nil {
115 req.Header.Set("Content-Type", "application/json") 94 req.Header.Set("Content-Type", "application/json")
116 } 95 }
117 » » req.Header.Set("User-Agent", userAgent()) 96 » » req.Header.Set("User-Agent", r.client.UserAgent)
118 » » resp, err := r.client.Do(req) 97
98 » » // Connect, read response.
99 » » r.client.Log.Debugf("cipd: %s %s", method, url)
100 » » resp, err := r.client.doAuthenticatedHTTPRequest(req)
119 if err != nil { 101 if err != nil {
102 if isTemporaryNetError(err) {
103 r.client.Log.Warningf("cipd: connectivity error (%s)", err)
104 continue
105 }
120 return err 106 return err
121 } 107 }
108 responseBody, err := ioutil.ReadAll(resp.Body)
109 resp.Body.Close()
110 if err != nil {
111 if isTemporaryNetError(err) {
112 r.client.Log.Warningf("cipd: temporary error whe n reading response (%s)", err)
113 continue
114 }
115 return err
116 }
117 r.client.Log.Debugf("cipd: http %d: %s", resp.StatusCode, body)
118 if isTemporaryHTTPError(resp.StatusCode) {
119 continue
120 }
121
122 // Success? 122 // Success?
123 if resp.StatusCode < 300 { 123 if resp.StatusCode < 300 {
124 » » » defer resp.Body.Close() 124 » » » return json.Unmarshal(responseBody, response)
125 » » » return json.NewDecoder(resp.Body).Decode(response)
126 } 125 }
126
127 // Fatal error? 127 // Fatal error?
128 » » if resp.StatusCode >= 300 && resp.StatusCode < 500 { 128 » » if resp.StatusCode == 403 || resp.StatusCode == 401 {
129 » » » defer resp.Body.Close() 129 » » » return ErrAccessDenined
130 » » » body, _ := ioutil.ReadAll(resp.Body)
131 » » » return fmt.Errorf("Unexpected reply (HTTP %d):\n%s", res p.StatusCode, string(body))
132 } 130 }
133 » » // Retry. 131 » » return fmt.Errorf("Unexpected reply (HTTP %d):\n%s", resp.Status Code, string(body))
134 » » resp.Body.Close()
135 } 132 }
136 » return fmt.Errorf("Request to %s failed after 10 attempts", url) 133
134 » return ErrBackendInaccessible
137 } 135 }
138 136
139 func (r *remoteService) initiateUpload(sha1 string) (s *uploadSession, err error ) { 137 func (r *remoteImpl) initiateUpload(sha1 string) (s *UploadSession, err error) {
140 var reply struct { 138 var reply struct {
141 Status string `json:"status"` 139 Status string `json:"status"`
142 UploadSessionID string `json:"upload_session_id"` 140 UploadSessionID string `json:"upload_session_id"`
143 UploadURL string `json:"upload_url"` 141 UploadURL string `json:"upload_url"`
144 ErrorMessage string `json:"error_message"` 142 ErrorMessage string `json:"error_message"`
145 } 143 }
146 err = r.makeRequest("cas/v1/upload/SHA1/"+sha1, "POST", nil, &reply) 144 err = r.makeRequest("cas/v1/upload/SHA1/"+sha1, "POST", nil, &reply)
147 if err != nil { 145 if err != nil {
148 return 146 return
149 } 147 }
150 switch reply.Status { 148 switch reply.Status {
151 case "ALREADY_UPLOADED": 149 case "ALREADY_UPLOADED":
152 return 150 return
153 case "SUCCESS": 151 case "SUCCESS":
154 » » s = &uploadSession{ 152 » » s = &UploadSession{reply.UploadSessionID, reply.UploadURL}
155 » » » ID: reply.UploadSessionID,
156 » » » URL: reply.UploadURL,
157 » » }
158 case "ERROR": 153 case "ERROR":
159 err = fmt.Errorf("Server replied with error: %s", reply.ErrorMes sage) 154 err = fmt.Errorf("Server replied with error: %s", reply.ErrorMes sage)
160 default: 155 default:
161 err = fmt.Errorf("Unexpected status: %s", reply.Status) 156 err = fmt.Errorf("Unexpected status: %s", reply.Status)
162 } 157 }
163 return 158 return
164 } 159 }
165 160
166 func (r *remoteService) finalizeUpload(sessionID string) (finished bool, err err or) { 161 func (r *remoteImpl) finalizeUpload(sessionID string) (finished bool, err error) {
167 var reply struct { 162 var reply struct {
168 Status string `json:"status"` 163 Status string `json:"status"`
169 ErrorMessage string `json:"error_message"` 164 ErrorMessage string `json:"error_message"`
170 } 165 }
171 err = r.makeRequest("cas/v1/finalize/"+sessionID, "POST", nil, &reply) 166 err = r.makeRequest("cas/v1/finalize/"+sessionID, "POST", nil, &reply)
172 if err != nil { 167 if err != nil {
173 return 168 return
174 } 169 }
175 switch reply.Status { 170 switch reply.Status {
176 case "MISSING": 171 case "MISSING":
177 » » err = errors.New("Upload session is unexpectedly missing") 172 » » err = ErrUploadSessionDied
178 case "UPLOADING", "VERIFYING": 173 case "UPLOADING", "VERIFYING":
179 finished = false 174 finished = false
180 case "PUBLISHED": 175 case "PUBLISHED":
181 finished = true 176 finished = true
182 case "ERROR": 177 case "ERROR":
183 err = errors.New(reply.ErrorMessage) 178 err = errors.New(reply.ErrorMessage)
184 default: 179 default:
185 err = fmt.Errorf("Unexpected upload session status: %s", reply.S tatus) 180 err = fmt.Errorf("Unexpected upload session status: %s", reply.S tatus)
186 } 181 }
187 return 182 return
188 } 183 }
189 184
190 func (r *remoteService) registerInstance(packageName, instanceID string) (*regis terInstanceResponse, error) { 185 func (r *remoteImpl) registerInstance(pin common.Pin) (*registerInstanceResponse , error) {
191 » endpoint, err := instanceEndpoint(packageName, instanceID) 186 » endpoint, err := instanceEndpoint(pin)
192 if err != nil { 187 if err != nil {
193 return nil, err 188 return nil, err
194 } 189 }
195 var reply struct { 190 var reply struct {
196 Status string `json:"status"` 191 Status string `json:"status"`
197 Instance packageInstanceMsg `json:"instance"` 192 Instance packageInstanceMsg `json:"instance"`
198 UploadSessionID string `json:"upload_session_id"` 193 UploadSessionID string `json:"upload_session_id"`
199 UploadURL string `json:"upload_url"` 194 UploadURL string `json:"upload_url"`
200 ErrorMessage string `json:"error_message"` 195 ErrorMessage string `json:"error_message"`
201 } 196 }
202 err = r.makeRequest(endpoint, "POST", nil, &reply) 197 err = r.makeRequest(endpoint, "POST", nil, &reply)
203 if err != nil { 198 if err != nil {
204 return nil, err 199 return nil, err
205 } 200 }
206 switch reply.Status { 201 switch reply.Status {
207 case "REGISTERED", "ALREADY_REGISTERED": 202 case "REGISTERED", "ALREADY_REGISTERED":
208 » » info, err := makePackageInstanceInfo(reply.Instance) 203 » » ts, err := convertTimestamp(reply.Instance.RegisteredTs)
209 if err != nil { 204 if err != nil {
210 return nil, err 205 return nil, err
211 } 206 }
212 return &registerInstanceResponse{ 207 return &registerInstanceResponse{
213 » » » AlreadyRegistered: reply.Status == "ALREADY_REGISTERED", 208 » » » alreadyRegistered: reply.Status == "ALREADY_REGISTERED",
214 » » » Info: info, 209 » » » registeredBy: reply.Instance.RegisteredBy,
210 » » » registeredTs: ts,
215 }, nil 211 }, nil
216 case "UPLOAD_FIRST": 212 case "UPLOAD_FIRST":
217 if reply.UploadSessionID == "" { 213 if reply.UploadSessionID == "" {
218 » » » return nil, errors.New("Server didn't provide upload ses sion ID") 214 » » » return nil, ErrNoUploadSessionID
219 } 215 }
220 return &registerInstanceResponse{ 216 return &registerInstanceResponse{
221 » » » UploadSession: &uploadSession{ 217 » » » uploadSession: &UploadSession{reply.UploadSessionID, rep ly.UploadURL},
222 » » » » ID: reply.UploadSessionID,
223 » » » » URL: reply.UploadURL,
224 » » » },
225 }, nil 218 }, nil
226 case "ERROR": 219 case "ERROR":
227 return nil, errors.New(reply.ErrorMessage) 220 return nil, errors.New(reply.ErrorMessage)
228 } 221 }
229 return nil, fmt.Errorf("Unexpected register package status: %s", reply.S tatus) 222 return nil, fmt.Errorf("Unexpected register package status: %s", reply.S tatus)
230 } 223 }
231 224
232 func (r *remoteService) fetchInstance(packageName, instanceID string) (*fetchIns tanceResponse, error) { 225 func (r *remoteImpl) fetchInstance(pin common.Pin) (*fetchInstanceResponse, erro r) {
233 » endpoint, err := instanceEndpoint(packageName, instanceID) 226 » endpoint, err := instanceEndpoint(pin)
234 if err != nil { 227 if err != nil {
235 return nil, err 228 return nil, err
236 } 229 }
237 var reply struct { 230 var reply struct {
238 Status string `json:"status"` 231 Status string `json:"status"`
239 Instance packageInstanceMsg `json:"instance"` 232 Instance packageInstanceMsg `json:"instance"`
240 FetchURL string `json:"fetch_url"` 233 FetchURL string `json:"fetch_url"`
241 ErrorMessage string `json:"error_message"` 234 ErrorMessage string `json:"error_message"`
242 } 235 }
243 err = r.makeRequest(endpoint, "GET", nil, &reply) 236 err = r.makeRequest(endpoint, "GET", nil, &reply)
244 if err != nil { 237 if err != nil {
245 return nil, err 238 return nil, err
246 } 239 }
247 switch reply.Status { 240 switch reply.Status {
248 case "SUCCESS": 241 case "SUCCESS":
249 » » info, err := makePackageInstanceInfo(reply.Instance) 242 » » ts, err := convertTimestamp(reply.Instance.RegisteredTs)
250 if err != nil { 243 if err != nil {
251 return nil, err 244 return nil, err
252 } 245 }
253 return &fetchInstanceResponse{ 246 return &fetchInstanceResponse{
254 » » » FetchURL: reply.FetchURL, 247 » » » fetchURL: reply.FetchURL,
255 » » » Info: info, 248 » » » registeredBy: reply.Instance.RegisteredBy,
249 » » » registeredTs: ts,
256 }, nil 250 }, nil
257 case "PACKAGE_NOT_FOUND": 251 case "PACKAGE_NOT_FOUND":
258 » » return nil, fmt.Errorf("Package '%s' is not registered or you do not have permission to fetch it", packageName) 252 » » return nil, fmt.Errorf("Package '%s' is not registered or you do not have permission to fetch it", pin.PackageName)
259 case "INSTANCE_NOT_FOUND": 253 case "INSTANCE_NOT_FOUND":
260 » » return nil, fmt.Errorf("Package '%s' doesn't have instance '%s'" , packageName, instanceID) 254 » » return nil, fmt.Errorf("Package '%s' doesn't have instance '%s'" , pin.PackageName, pin.InstanceID)
261 case "ERROR": 255 case "ERROR":
262 return nil, errors.New(reply.ErrorMessage) 256 return nil, errors.New(reply.ErrorMessage)
263 } 257 }
264 return nil, fmt.Errorf("Unexpected reply status: %s", reply.Status) 258 return nil, fmt.Errorf("Unexpected reply status: %s", reply.Status)
265 } 259 }
266 260
267 func (r *remoteService) fetchACL(packagePath string) ([]PackageACL, error) { 261 func (r *remoteImpl) fetchACL(packagePath string) ([]PackageACL, error) {
268 endpoint, err := aclEndpoint(packagePath) 262 endpoint, err := aclEndpoint(packagePath)
269 if err != nil { 263 if err != nil {
270 return nil, err 264 return nil, err
271 } 265 }
272 var reply struct { 266 var reply struct {
273 Status string `json:"status"` 267 Status string `json:"status"`
274 ErrorMessage string `json:"error_message"` 268 ErrorMessage string `json:"error_message"`
275 Acls struct { 269 Acls struct {
276 Acls []struct { 270 Acls []struct {
277 PackagePath string `json:"package_path"` 271 PackagePath string `json:"package_path"`
(...skipping 24 matching lines...) Expand all
302 ModifiedTs: ts, 296 ModifiedTs: ts,
303 }) 297 })
304 } 298 }
305 return out, nil 299 return out, nil
306 case "ERROR": 300 case "ERROR":
307 return nil, errors.New(reply.ErrorMessage) 301 return nil, errors.New(reply.ErrorMessage)
308 } 302 }
309 return nil, fmt.Errorf("Unexpected reply status: %s", reply.Status) 303 return nil, fmt.Errorf("Unexpected reply status: %s", reply.Status)
310 } 304 }
311 305
312 func (r *remoteService) modifyACL(packagePath string, changes []PackageACLChange ) error { 306 func (r *remoteImpl) modifyACL(packagePath string, changes []PackageACLChange) e rror {
313 endpoint, err := aclEndpoint(packagePath) 307 endpoint, err := aclEndpoint(packagePath)
314 if err != nil { 308 if err != nil {
315 return err 309 return err
316 } 310 }
317 var request struct { 311 var request struct {
318 Changes []roleChangeMsg `json:"changes"` 312 Changes []roleChangeMsg `json:"changes"`
319 } 313 }
320 for _, c := range changes { 314 for _, c := range changes {
321 action := "" 315 action := ""
322 if c.Action == GrantRole { 316 if c.Action == GrantRole {
(...skipping 19 matching lines...) Expand all
342 } 336 }
343 switch reply.Status { 337 switch reply.Status {
344 case "SUCCESS": 338 case "SUCCESS":
345 return nil 339 return nil
346 case "ERROR": 340 case "ERROR":
347 return errors.New(reply.ErrorMessage) 341 return errors.New(reply.ErrorMessage)
348 } 342 }
349 return fmt.Errorf("Unexpected reply status: %s", reply.Status) 343 return fmt.Errorf("Unexpected reply status: %s", reply.Status)
350 } 344 }
351 345
352 func (r *remoteService) attachTags(packageName, instanceID string, tags []string ) error { 346 func (r *remoteImpl) attachTags(pin common.Pin, tags []string) error {
353 // Tags will be passed in the request body, not via URL. 347 // Tags will be passed in the request body, not via URL.
354 » endpoint, err := tagsEndpoint(packageName, instanceID, nil) 348 » endpoint, err := tagsEndpoint(pin, nil)
355 if err != nil { 349 if err != nil {
356 return err 350 return err
357 } 351 }
358
359 if len(tags) == 0 {
360 return errors.New("At least one tag must be provided")
361 }
362 for _, tag := range tags { 352 for _, tag := range tags {
363 » » err = ValidateInstanceTag(tag) 353 » » err = common.ValidateInstanceTag(tag)
364 if err != nil { 354 if err != nil {
365 return err 355 return err
366 } 356 }
367 } 357 }
358
368 var request struct { 359 var request struct {
369 Tags []string `json:"tags"` 360 Tags []string `json:"tags"`
370 } 361 }
371 request.Tags = tags 362 request.Tags = tags
372 363
373 var reply struct { 364 var reply struct {
374 Status string `json:"status"` 365 Status string `json:"status"`
375 ErrorMessage string `json:"error_message"` 366 ErrorMessage string `json:"error_message"`
376 } 367 }
377 err = r.makeRequest(endpoint, "POST", &request, &reply) 368 err = r.makeRequest(endpoint, "POST", &request, &reply)
378 if err != nil { 369 if err != nil {
379 return err 370 return err
380 } 371 }
381 switch reply.Status { 372 switch reply.Status {
382 case "SUCCESS": 373 case "SUCCESS":
383 return nil 374 return nil
384 case "PROCESSING_NOT_FINISHED_YET": 375 case "PROCESSING_NOT_FINISHED_YET":
385 return &pendingProcessingError{reply.ErrorMessage} 376 return &pendingProcessingError{reply.ErrorMessage}
386 case "ERROR", "PROCESSING_FAILED": 377 case "ERROR", "PROCESSING_FAILED":
387 return errors.New(reply.ErrorMessage) 378 return errors.New(reply.ErrorMessage)
388 } 379 }
389 return fmt.Errorf("Unexpected status when attaching tags: %s", reply.Sta tus) 380 return fmt.Errorf("Unexpected status when attaching tags: %s", reply.Sta tus)
390 } 381 }
391 382
392 //////////////////////////////////////////////////////////////////////////////// 383 ////////////////////////////////////////////////////////////////////////////////
393 384
394 func instanceEndpoint(packageName, instanceID string) (string, error) { 385 func instanceEndpoint(pin common.Pin) (string, error) {
395 » err := ValidatePackageName(packageName) 386 » err := common.ValidatePin(pin)
396 » if err != nil {
397 » » return "", err
398 » }
399 » err = ValidateInstanceID(instanceID)
400 if err != nil { 387 if err != nil {
401 return "", err 388 return "", err
402 } 389 }
403 params := url.Values{} 390 params := url.Values{}
404 » params.Add("package_name", packageName) 391 » params.Add("package_name", pin.PackageName)
405 » params.Add("instance_id", instanceID) 392 » params.Add("instance_id", pin.InstanceID)
406 return "repo/v1/instance?" + params.Encode(), nil 393 return "repo/v1/instance?" + params.Encode(), nil
407 } 394 }
408 395
409 func aclEndpoint(packagePath string) (string, error) { 396 func aclEndpoint(packagePath string) (string, error) {
410 » err := ValidatePackageName(packagePath) 397 » err := common.ValidatePackageName(packagePath)
411 if err != nil { 398 if err != nil {
412 return "", err 399 return "", err
413 } 400 }
414 params := url.Values{} 401 params := url.Values{}
415 params.Add("package_path", packagePath) 402 params.Add("package_path", packagePath)
416 return "repo/v1/acl?" + params.Encode(), nil 403 return "repo/v1/acl?" + params.Encode(), nil
417 } 404 }
418 405
419 func tagsEndpoint(packageName, instanceID string, tags []string) (string, error) { 406 func tagsEndpoint(pin common.Pin, tags []string) (string, error) {
420 » err := ValidatePackageName(packageName) 407 » err := common.ValidatePin(pin)
421 » if err != nil {
422 » » return "", err
423 » }
424 » err = ValidateInstanceID(instanceID)
425 if err != nil { 408 if err != nil {
426 return "", err 409 return "", err
427 } 410 }
428 for _, tag := range tags { 411 for _, tag := range tags {
429 » » err = ValidateInstanceTag(tag) 412 » » err = common.ValidateInstanceTag(tag)
430 if err != nil { 413 if err != nil {
431 return "", err 414 return "", err
432 } 415 }
433 } 416 }
434 params := url.Values{} 417 params := url.Values{}
435 » params.Add("package_name", packageName) 418 » params.Add("package_name", pin.PackageName)
436 » params.Add("instance_id", instanceID) 419 » params.Add("instance_id", pin.InstanceID)
437 for _, tag := range tags { 420 for _, tag := range tags {
438 params.Add("tag", tag) 421 params.Add("tag", tag)
439 } 422 }
440 return "repo/v1/tags?" + params.Encode(), nil 423 return "repo/v1/tags?" + params.Encode(), nil
441 } 424 }
442 425
443 // convertTimestamp coverts string with int64 timestamp in microseconds since 426 // convertTimestamp coverts string with int64 timestamp in microseconds since
444 // to time.Time 427 // to time.Time
445 func convertTimestamp(ts string) (time.Time, error) { 428 func convertTimestamp(ts string) (time.Time, error) {
446 i, err := strconv.ParseInt(ts, 10, 64) 429 i, err := strconv.ParseInt(ts, 10, 64)
447 if err != nil { 430 if err != nil {
448 return time.Time{}, fmt.Errorf("Unexpected timestamp value '%s' in the server response", ts) 431 return time.Time{}, fmt.Errorf("Unexpected timestamp value '%s' in the server response", ts)
449 } 432 }
450 return time.Unix(0, i*1000), nil 433 return time.Unix(0, i*1000), nil
451 } 434 }
452
453 func makePackageInstanceInfo(msg packageInstanceMsg) (pi packageInstanceInfo, er r error) {
454 ts, err := convertTimestamp(msg.RegisteredTs)
455 if err != nil {
456 return
457 }
458 pi = packageInstanceInfo{
459 PackageName: msg.PackageName,
460 InstanceID: msg.InstanceID,
461 RegisteredBy: msg.RegisteredBy,
462 RegisteredTs: ts,
463 }
464 return
465 }
OLDNEW
« no previous file with comments | « go/src/infra/tools/cipd/reader_test.go ('k') | go/src/infra/tools/cipd/remote_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698