OLD | NEW |
| (Empty) |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
2 // Use of this source code is governed under the Apache License, Version 2.0 | |
3 // that can be found in the LICENSE file. | |
4 | |
5 package buildbucket | |
6 | |
7 import ( | |
8 "fmt" | |
9 "net/http" | |
10 "strings" | |
11 "time" | |
12 | |
13 "golang.org/x/net/context" | |
14 | |
15 "github.com/luci/luci-go/common/api/buildbucket/buildbucket/v1" | |
16 "github.com/luci/luci-go/common/api/buildbucket/swarmbucket/v1" | |
17 "github.com/luci/luci-go/milo/api/resp" | |
18 "github.com/luci/luci-go/milo/appengine/common/model" | |
19 "github.com/luci/luci-go/server/auth" | |
20 ) | |
21 | |
22 const ( | |
23 // StatusScheduled means a build is pending. | |
24 StatusScheduled = "SCHEDULED" | |
25 // StatusStarted means a build is executing. | |
26 StatusStarted = "STARTED" | |
27 // StatusCompleted means a build is completed (successfully or not). | |
28 StatusCompleted = "COMPLETED" | |
29 ) | |
30 | |
31 // ParseTags parses buildbucket build tags to a map. | |
32 // Ignores tags that doesn't have colon (we don't have them in practice because | |
33 // buildbucket validates tags). | |
34 func ParseTags(tags []string) map[string]string { | |
35 result := make(map[string]string, len(tags)) | |
36 for _, t := range tags { | |
37 parts := strings.SplitN(t, ":", 2) | |
38 if len(parts) == 2 { | |
39 result[parts[0]] = parts[1] | |
40 } | |
41 } | |
42 return result | |
43 } | |
44 | |
45 func newSwarmbucketClient(c context.Context, server string) (*swarmbucket.Servic
e, error) { | |
46 c, _ = context.WithTimeout(c, time.Minute) | |
47 t, err := auth.GetRPCTransport(c, auth.AsUser) | |
48 if err != nil { | |
49 return nil, err | |
50 } | |
51 client, err := swarmbucket.New(&http.Client{Transport: t}) | |
52 if err != nil { | |
53 return nil, err | |
54 } | |
55 client.BasePath = fmt.Sprintf("https://%s/api/swarmbucket/v1/", server) | |
56 return client, nil | |
57 } | |
58 | |
59 func newBuildbucketClient(c context.Context, server string) (*buildbucket.Servic
e, error) { | |
60 c, _ = context.WithTimeout(c, time.Minute) | |
61 t, err := auth.GetRPCTransport(c, auth.AsUser) | |
62 if err != nil { | |
63 return nil, err | |
64 } | |
65 client, err := buildbucket.New(&http.Client{Transport: t}) | |
66 if err != nil { | |
67 return nil, err | |
68 } | |
69 client.BasePath = fmt.Sprintf("https://%s/api/buildbucket/v1/", server) | |
70 return client, nil | |
71 } | |
72 | |
73 // parseStatus converts a buildbucket build status to resp.Status. | |
74 func parseStatus(build *buildbucket.ApiCommonBuildMessage) (model.Status, error)
{ | |
75 switch build.Status { | |
76 case StatusScheduled: | |
77 return model.NotRun, nil | |
78 | |
79 case StatusStarted: | |
80 return model.Running, nil | |
81 | |
82 case StatusCompleted: | |
83 switch build.Result { | |
84 case "SUCCESS": | |
85 return model.Success, nil | |
86 | |
87 case "FAILURE": | |
88 switch build.FailureReason { | |
89 case "BUILD_FAILURE": | |
90 return model.Failure, nil | |
91 default: | |
92 return model.InfraFailure, nil | |
93 } | |
94 | |
95 case "CANCELED": | |
96 return model.InfraFailure, nil | |
97 | |
98 default: | |
99 return 0, fmt.Errorf("unexpected buildbucket build resul
t %q", build.Result) | |
100 } | |
101 | |
102 default: | |
103 return 0, fmt.Errorf("unexpected buildbucket build status %q", b
uild.Status) | |
104 } | |
105 } | |
106 | |
107 // getChangeList tries to extract CL information from a buildbucket build. | |
108 func getChangeList( | |
109 build *buildbucket.ApiCommonBuildMessage, params *buildParameters, | |
110 resultDetails *resultDetails) (result *resp.Commit) { | |
111 | |
112 prop := ¶ms.Properties | |
113 switch prop.PatchStorage { | |
114 case "rietveld": | |
115 if prop.RietveldURL != "" && prop.Issue != 0 { | |
116 result = &resp.Commit{ | |
117 RequestRevision: resp.NewLink(prop.Revision, "")
, | |
118 Changelist: resp.NewLink( | |
119 fmt.Sprintf("Rietveld CL %d", prop.Issue
), | |
120 fmt.Sprintf("%s/%d/#ps%d", prop.Rietveld
URL, prop.Issue, prop.PatchSet), | |
121 ), | |
122 } | |
123 if resultDetails.Properties.GotRevision != "" { | |
124 // TODO(hinoka): Figure out the full URL for the
se revisions, add it | |
125 // to the URL field. | |
126 result.Revision = resp.NewLink(resultDetails.Pro
perties.GotRevision, "") | |
127 } | |
128 } | |
129 | |
130 case "gerrit": | |
131 if prop.GerritPatchURL != "" && prop.GerritPatchIssue != 0 { | |
132 path := fmt.Sprintf("%d", prop.GerritPatchIssue) | |
133 if prop.GerritPatchSet != 0 { | |
134 path = fmt.Sprintf("%d/%d", prop.GerritPatchIssu
e, prop.GerritPatchSet) | |
135 } | |
136 result = &resp.Commit{ | |
137 Changelist: resp.NewLink( | |
138 fmt.Sprintf("Gerrit CL %d", prop.GerritP
atchIssue), | |
139 fmt.Sprintf("%s/c/%s", prop.GerritPatchU
RL, path), | |
140 ), | |
141 } | |
142 } | |
143 } | |
144 | |
145 if result != nil && len(params.Changes) != 0 { | |
146 // tryjobs have one change and it is the CL author | |
147 result.AuthorEmail = params.Changes[0].Author.Email | |
148 } | |
149 | |
150 return | |
151 } | |
OLD | NEW |