| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package swarming | 5 package swarming |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "encoding/json" | 9 "encoding/json" |
| 10 "fmt" | 10 "fmt" |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 *swarming.SwarmingRpcsTaskResult, string, error) { | 118 *swarming.SwarmingRpcsTaskResult, string, error) { |
| 119 | 119 |
| 120 var log string | 120 var log string |
| 121 var sr *swarming.SwarmingRpcsTaskResult | 121 var sr *swarming.SwarmingRpcsTaskResult |
| 122 var errLog, errRes error | 122 var errLog, errRes error |
| 123 | 123 |
| 124 // Detour: Return debugging results, useful for development. | 124 // Detour: Return debugging results, useful for development. |
| 125 if server == "debug" { | 125 if server == "debug" { |
| 126 sr, errRes = getDebugSwarmingResult(taskID) | 126 sr, errRes = getDebugSwarmingResult(taskID) |
| 127 log, errLog = getDebugTaskOutput(taskID) | 127 log, errLog = getDebugTaskOutput(taskID) |
| 128 » » if errLog != nil { | 128 » } else { |
| 129 » » » return sr, log, errLog | 129 » » sc, err := getSwarmingClient(c, server) |
| 130 » » if err != nil { |
| 131 » » » return nil, "", err |
| 130 } | 132 } |
| 131 » » return sr, log, errRes | 133 |
| 134 » » var wg sync.WaitGroup |
| 135 » » wg.Add(2) // Getting log and result can happen concurrently. Wa
it for both. |
| 136 |
| 137 » » go func() { |
| 138 » » » defer wg.Done() |
| 139 » » » log, errLog = getTaskOutput(sc, taskID) |
| 140 » » }() |
| 141 » » go func() { |
| 142 » » » defer wg.Done() |
| 143 » » » sr, errRes = getSwarmingResult(sc, taskID) |
| 144 » » }() |
| 145 » » wg.Wait() |
| 132 } | 146 } |
| 133 | 147 |
| 134 » sc, err := getSwarmingClient(c, server) | 148 » switch sr.State { |
| 135 » if err != nil { | 149 » case TaskCompleted, TaskRunning, TaskCanceled: |
| 136 » » return nil, "", err | 150 » default: |
| 151 » » // Ignore log errors if the task might be pending, timed out, e
xpired, etc. |
| 152 » » if errLog != nil { |
| 153 » » » errLog = nil |
| 154 » » » log = "" |
| 155 » » } |
| 137 } | 156 } |
| 138 | |
| 139 var wg sync.WaitGroup | |
| 140 wg.Add(2) // Getting log and result can happen concurrently. Wait for b
oth. | |
| 141 | |
| 142 go func() { | |
| 143 defer wg.Done() | |
| 144 log, errLog = getTaskOutput(sc, taskID) | |
| 145 }() | |
| 146 go func() { | |
| 147 defer wg.Done() | |
| 148 sr, errRes = getSwarmingResult(sc, taskID) | |
| 149 }() | |
| 150 wg.Wait() | |
| 151 if errRes != nil { | 157 if errRes != nil { |
| 158 // Swarming result errors take priority. |
| 152 return sr, log, errRes | 159 return sr, log, errRes |
| 153 } | 160 } |
| 154 return sr, log, errLog | 161 return sr, log, errLog |
| 155 } | 162 } |
| 156 | 163 |
| 157 func taskProperties(sr *swarming.SwarmingRpcsTaskResult) *resp.PropertyGroup { | 164 func taskProperties(sr *swarming.SwarmingRpcsTaskResult) *resp.PropertyGroup { |
| 158 props := &resp.PropertyGroup{GroupName: "Swarming"} | 165 props := &resp.PropertyGroup{GroupName: "Swarming"} |
| 159 if len(sr.CostsUsd) == 1 { | 166 if len(sr.CostsUsd) == 1 { |
| 160 props.Property = append(props.Property, &resp.Property{ | 167 props.Property = append(props.Property, &resp.Property{ |
| 161 Key: "Cost of job (USD)", | 168 Key: "Cost of job (USD)", |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 | 245 |
| 239 if sr.BotId != "" { | 246 if sr.BotId != "" { |
| 240 build.Summary.Bot = &resp.Link{ | 247 build.Summary.Bot = &resp.Link{ |
| 241 Label: sr.BotId, | 248 Label: sr.BotId, |
| 242 URL: botPageURL(server, sr.BotId), | 249 URL: botPageURL(server, sr.BotId), |
| 243 } | 250 } |
| 244 } | 251 } |
| 245 | 252 |
| 246 // Build times. Swarming timestamps are UTC RFC3339Nano, but without the | 253 // Build times. Swarming timestamps are UTC RFC3339Nano, but without the |
| 247 // timezone information. Make them valid RFC3339Nano. | 254 // timezone information. Make them valid RFC3339Nano. |
| 248 » build.Summary.Started = sr.StartedTs + "Z" | 255 » if sr.StartedTs != "" { |
| 256 » » build.Summary.Started = sr.StartedTs + "Z" |
| 257 » } |
| 249 if sr.CompletedTs != "" { | 258 if sr.CompletedTs != "" { |
| 250 build.Summary.Finished = sr.CompletedTs + "Z" | 259 build.Summary.Finished = sr.CompletedTs + "Z" |
| 251 } | 260 } |
| 252 if sr.Duration != 0 { | 261 if sr.Duration != 0 { |
| 253 build.Summary.Duration = uint64(sr.Duration) | 262 build.Summary.Duration = uint64(sr.Duration) |
| 254 } else if sr.State == TaskRunning { | 263 } else if sr.State == TaskRunning { |
| 255 started, err := time.Parse(time.RFC3339, build.Summary.Started) | 264 started, err := time.Parse(time.RFC3339, build.Summary.Started) |
| 256 if err != nil { | 265 if err != nil { |
| 257 return nil, fmt.Errorf("invalid task StartedTs: %s", err
) | 266 return nil, fmt.Errorf("invalid task StartedTs: %s", err
) |
| 258 } | 267 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 } | 318 } |
| 310 | 319 |
| 311 build, err := taskToBuild(c, server, sr) | 320 build, err := taskToBuild(c, server, sr) |
| 312 if err != nil { | 321 if err != nil { |
| 313 return nil, err | 322 return nil, err |
| 314 } | 323 } |
| 315 | 324 |
| 316 // Decode the data using annotee. The logdog stream returned here is ass
umed | 325 // Decode the data using annotee. The logdog stream returned here is ass
umed |
| 317 // to be consistent, which is why the following block of code are not | 326 // to be consistent, which is why the following block of code are not |
| 318 // expected to ever err out. | 327 // expected to ever err out. |
| 319 » lds, err := streamsFromAnnotatedLog(c, body) | 328 » if body != "" { |
| 320 » if err != nil { | 329 » » lds, err := streamsFromAnnotatedLog(c, body) |
| 321 » » build.Components = []*resp.BuildComponent{{ | 330 » » if err != nil { |
| 322 » » » Type: resp.Summary, | 331 » » » build.Components = []*resp.BuildComponent{{ |
| 323 » » » Label: "Milo annotation parser", | 332 » » » » Type: resp.Summary, |
| 324 » » » Text: []string{err.Error()}, | 333 » » » » Label: "Milo annotation parser", |
| 325 » » » Status: resp.InfraFailure, | 334 » » » » Text: []string{err.Error()}, |
| 326 » » » SubLink: []*resp.Link{{ | 335 » » » » Status: resp.InfraFailure, |
| 327 » » » » Label: "swarming task", | 336 » » » » SubLink: []*resp.Link{{ |
| 328 » » » » URL: taskPageURL(server, taskID), | 337 » » » » » Label: "swarming task", |
| 329 » » » }}, | 338 » » » » » URL: taskPageURL(server, taskID), |
| 330 » » }} | 339 » » » » }}, |
| 331 » } else { | 340 » » » }} |
| 332 » » logdog.AddLogDogToBuild(c, URL, lds, build) | 341 » » } else { |
| 342 » » » logdog.AddLogDogToBuild(c, URL, lds, build) |
| 343 » » } |
| 333 } | 344 } |
| 334 | 345 |
| 335 return build, nil | 346 return build, nil |
| 336 } | 347 } |
| 337 | 348 |
| 338 // taskPageURL returns a URL to a human-consumable page of a swarming task. | 349 // taskPageURL returns a URL to a human-consumable page of a swarming task. |
| 339 // Supports server aliases. | 350 // Supports server aliases. |
| 340 func taskPageURL(swarmingHostname, taskID string) string { | 351 func taskPageURL(swarmingHostname, taskID string) string { |
| 341 return fmt.Sprintf("https://%s/user/task/%s", resolveServer(swarmingHost
name), taskID) | 352 return fmt.Sprintf("https://%s/user/task/%s", resolveServer(swarmingHost
name), taskID) |
| 342 } | 353 } |
| 343 | 354 |
| 344 // botPageURL returns a URL to a human-consumable page of a swarming bot. | 355 // botPageURL returns a URL to a human-consumable page of a swarming bot. |
| 345 // Supports server aliases. | 356 // Supports server aliases. |
| 346 func botPageURL(swarmingHostname, botID string) string { | 357 func botPageURL(swarmingHostname, botID string) string { |
| 347 return fmt.Sprintf("https://%s/restricted/bot/%s", resolveServer(swarmin
gHostname), botID) | 358 return fmt.Sprintf("https://%s/restricted/bot/%s", resolveServer(swarmin
gHostname), botID) |
| 348 } | 359 } |
| OLD | NEW |