| Index: go/src/infra/tools/cipd/remote.go
|
| diff --git a/go/src/infra/tools/cipd/remote.go b/go/src/infra/tools/cipd/remote.go
|
| index 1904342be2b146f372ae911bafbdb4ea1f114fe7..627fbba0def1f45319ce5fa4c6f640642e99378a 100644
|
| --- a/go/src/infra/tools/cipd/remote.go
|
| +++ b/go/src/infra/tools/cipd/remote.go
|
| @@ -67,6 +67,16 @@ type roleChangeMsg struct {
|
| Principal string `json:"principal"`
|
| }
|
|
|
| +// pendingProcessingError is returned by attachTags if package instance is not
|
| +// yet ready and the call should be retried later.
|
| +type pendingProcessingError struct {
|
| + message string
|
| +}
|
| +
|
| +func (e *pendingProcessingError) Error() string {
|
| + return e.message
|
| +}
|
| +
|
| // newRemoteService is mocked in tests.
|
| var newRemoteService = func(client *http.Client, url string, log logging.Logger) *remoteService {
|
| log.Infof("cipd: service URL is %s", url)
|
| @@ -339,6 +349,46 @@ func (r *remoteService) modifyACL(packagePath string, changes []PackageACLChange
|
| return fmt.Errorf("Unexpected reply status: %s", reply.Status)
|
| }
|
|
|
| +func (r *remoteService) attachTags(packageName, instanceID string, tags []string) error {
|
| + // Tags will be passed in the request body, not via URL.
|
| + endpoint, err := tagsEndpoint(packageName, instanceID, nil)
|
| + if err != nil {
|
| + return err
|
| + }
|
| +
|
| + if len(tags) == 0 {
|
| + return errors.New("At least one tag must be provided")
|
| + }
|
| + for _, tag := range tags {
|
| + err = ValidateInstanceTag(tag)
|
| + if err != nil {
|
| + return err
|
| + }
|
| + }
|
| + var request struct {
|
| + Tags []string `json:"tags"`
|
| + }
|
| + request.Tags = tags
|
| +
|
| + var reply struct {
|
| + Status string `json:"status"`
|
| + ErrorMessage string `json:"error_message"`
|
| + }
|
| + err = r.makeRequest(endpoint, "POST", &request, &reply)
|
| + if err != nil {
|
| + return err
|
| + }
|
| + switch reply.Status {
|
| + case "SUCCESS":
|
| + return nil
|
| + case "PROCESSING_NOT_FINISHED_YET":
|
| + return &pendingProcessingError{reply.ErrorMessage}
|
| + case "ERROR", "PROCESSING_FAILED":
|
| + return errors.New(reply.ErrorMessage)
|
| + }
|
| + return fmt.Errorf("Unexpected status when attaching tags: %s", reply.Status)
|
| +}
|
| +
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| func instanceEndpoint(packageName, instanceID string) (string, error) {
|
| @@ -366,6 +416,30 @@ func aclEndpoint(packagePath string) (string, error) {
|
| return "repo/v1/acl?" + params.Encode(), nil
|
| }
|
|
|
| +func tagsEndpoint(packageName, instanceID string, tags []string) (string, error) {
|
| + err := ValidatePackageName(packageName)
|
| + if err != nil {
|
| + return "", err
|
| + }
|
| + err = ValidateInstanceID(instanceID)
|
| + if err != nil {
|
| + return "", err
|
| + }
|
| + for _, tag := range tags {
|
| + err = ValidateInstanceTag(tag)
|
| + if err != nil {
|
| + return "", err
|
| + }
|
| + }
|
| + params := url.Values{}
|
| + params.Add("package_name", packageName)
|
| + params.Add("instance_id", instanceID)
|
| + for _, tag := range tags {
|
| + params.Add("tag", tag)
|
| + }
|
| + return "repo/v1/tags?" + params.Encode(), nil
|
| +}
|
| +
|
| // convertTimestamp coverts string with int64 timestamp in microseconds since
|
| // to time.Time
|
| func convertTimestamp(ts string) (time.Time, error) {
|
|
|