Migrate Canary-G3 to task driver
Migrates https://skia.googlesource.com/skia/+/master/infra/bots/g3_canary/trigger_wait_g3_task.py to task driver with a similar flow. Bug: skia:10545 Change-Id: I1ccea76faa67fa1179cfd6ce9e6a9519fb38dd07 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/305566 Reviewed-by: Eric Boren <borenet@google.com> Commit-Queue: Ravi Mistry <rmistry@google.com>
This commit is contained in:
parent
1e67f7fced
commit
b394db45e6
@ -1,11 +0,0 @@
|
||||
{
|
||||
'includes': [
|
||||
'infrabots.isolate',
|
||||
'run_recipe.isolate',
|
||||
],
|
||||
'variables': {
|
||||
'files': [
|
||||
'../../../.gclient',
|
||||
],
|
||||
},
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2019 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Script that triggers and waits for g3 canary tasks."""
|
||||
|
||||
import json
|
||||
import math
|
||||
import optparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
INFRA_BOTS_DIR = os.path.abspath(os.path.realpath(os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), os.pardir)))
|
||||
sys.path.insert(0, INFRA_BOTS_DIR)
|
||||
import utils
|
||||
|
||||
|
||||
G3_COMPILE_BUCKET = 'g3-compile-tasks'
|
||||
|
||||
GS_RETRIES = 5
|
||||
GS_RETRY_WAIT_BASE = 15
|
||||
|
||||
POLLING_FREQUENCY_SECS = 10
|
||||
DEADLINE_SECS = 60 * 60 # 60 minutes.
|
||||
|
||||
INFRA_FAILURE_ERROR_MSG = (
|
||||
'\n\n'
|
||||
'Your run failed due to unknown infrastructure failures.\n'
|
||||
'Please contact rmistry@ or the trooper from '
|
||||
'http://tree-status.skia.org/trooper\n'
|
||||
'Sorry for the inconvenience!\n'
|
||||
)
|
||||
MISSING_APPROVAL_ERROR_MSG = (
|
||||
'\n\n'
|
||||
'To run the G3 tryjob, changes must be either owned and authored by \n'
|
||||
'Googlers or approved (Code-Review+1) by Googlers.\n'
|
||||
)
|
||||
MERGE_CONFLICT_ERROR_MSG = (
|
||||
'\n\n'
|
||||
'G3 tryjob failed because the change is causing a merge conflict when \n'
|
||||
'applying it to the Skia hash in G3.\n'
|
||||
)
|
||||
PATCHING_INFORMATION = (
|
||||
'\n\n'
|
||||
'Tip: If needed, could try patching in the CL into a local G3 client \n'
|
||||
'with "g4 patch" and then hacking on it.'
|
||||
)
|
||||
|
||||
|
||||
class G3CompileException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _create_task_dict(options):
|
||||
"""Creates a dict representation of the requested task."""
|
||||
params = {}
|
||||
params['issue'] = options.issue
|
||||
params['patchset'] = options.patchset
|
||||
return params
|
||||
|
||||
|
||||
def _get_gs_bucket():
|
||||
"""Returns the Google storage bucket with the gs:// prefix."""
|
||||
return 'gs://%s' % G3_COMPILE_BUCKET
|
||||
|
||||
|
||||
def _write_to_storage(task):
|
||||
"""Writes the specified compile task to Google storage."""
|
||||
with utils.tmp_dir():
|
||||
json_file = os.path.join(os.getcwd(), _get_task_file_name(task))
|
||||
with open(json_file, 'w') as f:
|
||||
json.dump(task, f)
|
||||
subprocess.check_call(['gsutil', 'cp', json_file, '%s/' % _get_gs_bucket()])
|
||||
print 'Created %s/%s' % (_get_gs_bucket(), os.path.basename(json_file))
|
||||
|
||||
|
||||
def _get_task_file_name(task):
|
||||
"""Returns the file name of the compile task. Eg: ${issue}-${patchset}.json"""
|
||||
return '%s-%s.json' % (task['issue'], task['patchset'])
|
||||
|
||||
|
||||
def _does_running_task_exist_in_storage(task):
|
||||
"""Checks to see if the task file exists in storage and is running."""
|
||||
gs_file = '%s/%s' % (_get_gs_bucket(), _get_task_file_name(task))
|
||||
try:
|
||||
# Read without exponential backoff because it is unlikely that the file
|
||||
# already exists and we do not want to waste minutes every time.
|
||||
taskJSON = _read_from_storage(gs_file, use_expo_retries=False)
|
||||
except (subprocess.CalledProcessError, ValueError):
|
||||
return False
|
||||
if taskJSON.get('status'):
|
||||
print 'Task exists in Google storage and has completed.'
|
||||
return False
|
||||
print 'Task exists in Google storage and is still running.'
|
||||
return True
|
||||
|
||||
|
||||
def _trigger_task(options):
|
||||
"""Triggers a g3 compile task by creating a file in storage."""
|
||||
task = _create_task_dict(options)
|
||||
# Check to see if the task is already running in Google Storage.
|
||||
if not _does_running_task_exist_in_storage(task):
|
||||
_write_to_storage(task)
|
||||
return task
|
||||
|
||||
|
||||
def _read_from_storage(gs_file, use_expo_retries=True):
|
||||
"""Returns the contents of the specified file from storage."""
|
||||
num_retries = GS_RETRIES if use_expo_retries else 1
|
||||
for retry in range(num_retries):
|
||||
try:
|
||||
output = subprocess.check_output(['gsutil', 'cat', gs_file])
|
||||
ret = json.loads(output)
|
||||
return ret
|
||||
except (subprocess.CalledProcessError, ValueError), e:
|
||||
print 'Error when reading and loading %s: %s' % (gs_file, e)
|
||||
if retry == (num_retries-1):
|
||||
print '%d retries did not help' % num_retries
|
||||
raise
|
||||
waittime = GS_RETRY_WAIT_BASE * math.pow(2, retry)
|
||||
print 'Retry in %d seconds.' % waittime
|
||||
time.sleep(waittime)
|
||||
continue
|
||||
|
||||
|
||||
def trigger_and_wait(options):
|
||||
"""Triggers a g3 compile task and waits for it to complete."""
|
||||
task = _trigger_task(options)
|
||||
print 'G3 Compile Task for %d/%d has been successfully added to %s.' % (
|
||||
options.issue, options.patchset, G3_COMPILE_BUCKET)
|
||||
print '%s will be polled every %d seconds.' % (G3_COMPILE_BUCKET,
|
||||
POLLING_FREQUENCY_SECS)
|
||||
|
||||
# Now poll the Google storage file till the task completes or till deadline
|
||||
# is hit.
|
||||
time_started_polling = time.time()
|
||||
while True:
|
||||
if (time.time() - time_started_polling) > DEADLINE_SECS:
|
||||
raise G3CompileException(
|
||||
'Task did not complete in the deadline of %s seconds.' % (
|
||||
DEADLINE_SECS))
|
||||
|
||||
# Get the status of the task.
|
||||
gs_file = '%s/%s' % (_get_gs_bucket(), _get_task_file_name(task))
|
||||
ret = _read_from_storage(gs_file)
|
||||
|
||||
if ret.get('status'):
|
||||
# The task is done, delete the file.
|
||||
subprocess.check_call(['gsutil', 'rm', gs_file])
|
||||
if options.output_file:
|
||||
# Write the task to the output_file.
|
||||
with open(options.output_file, 'w') as output_file:
|
||||
json.dump(ret, output_file)
|
||||
|
||||
# Now either raise an Exception or return success based on the status.
|
||||
if ret['status'] == 'exception':
|
||||
if ret.get('error'):
|
||||
raise G3CompileException('Run failed with:\n\n%s\n' % ret['error'])
|
||||
else:
|
||||
# Use a general purpose error message.
|
||||
raise G3CompileException(INFRA_FAILURE_ERROR_MSG)
|
||||
elif ret['status'] == 'missing_approval':
|
||||
raise G3CompileException(MISSING_APPROVAL_ERROR_MSG)
|
||||
elif ret['status'] == 'merge_conflict':
|
||||
raise G3CompileException(MERGE_CONFLICT_ERROR_MSG)
|
||||
elif ret['status'] == 'failure':
|
||||
raise G3CompileException(
|
||||
'\n\nRun failed G3 TAP: cl/%s' % ret['cl'] + PATCHING_INFORMATION)
|
||||
elif ret['status'] == 'success':
|
||||
print '\n\nRun passed G3 TAP: cl/%s' % ret['cl']
|
||||
return 0
|
||||
else:
|
||||
# Not sure what happened here. Use a general purpose error message.
|
||||
raise G3CompileException(INFRA_FAILURE_ERROR_MSG)
|
||||
|
||||
# Print status of the task.
|
||||
print 'Task: %s\n' % pretty_task_str(ret)
|
||||
time.sleep(POLLING_FREQUENCY_SECS)
|
||||
|
||||
|
||||
def pretty_task_str(task):
|
||||
if task.get('result'):
|
||||
status = task['result']
|
||||
else:
|
||||
status = 'Task not started yet'
|
||||
return '[status: %s, cl: %s]' % (status, task.get('cl'))
|
||||
|
||||
|
||||
def main():
|
||||
option_parser = optparse.OptionParser()
|
||||
option_parser.add_option(
|
||||
'', '--issue', type=int, default=0,
|
||||
help='The Gerrit change number to get the patch from.')
|
||||
option_parser.add_option(
|
||||
'', '--patchset', type=int, default=0,
|
||||
help='The Gerrit change patchset to use.')
|
||||
option_parser.add_option(
|
||||
'', '--output_file', type=str,
|
||||
help='The file to write the task to.')
|
||||
option_parser.add_option(
|
||||
'', '--builder_name', type=str, default='',
|
||||
help='The builder that triggered this run.')
|
||||
options, _ = option_parser.parse_args()
|
||||
sys.exit(trigger_and_wait(options))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1167,13 +1167,23 @@ func (b *jobBuilder) androidFrameworkCompile() {
|
||||
// should add as a dependency.
|
||||
func (b *jobBuilder) g3FrameworkCanary() {
|
||||
b.addTask(b.Name, func(b *taskBuilder) {
|
||||
b.recipeProps(EXTRA_PROPS)
|
||||
b.kitchenTask("g3_canary", OUTPUT_NONE)
|
||||
b.isolate("canary.isolate")
|
||||
b.serviceAccount("skia-g3-framework-compile@skia-swarming-bots.iam.gserviceaccount.com")
|
||||
b.isolate("empty.isolate")
|
||||
b.dep(b.buildTaskDrivers())
|
||||
b.cmd("./g3_canary",
|
||||
"--local=false",
|
||||
"--project_id", "skia-swarming-bots",
|
||||
"--task_id", specs.PLACEHOLDER_TASK_ID,
|
||||
"--task_name", b.Name,
|
||||
"--repo", specs.PLACEHOLDER_REPO,
|
||||
"--revision", specs.PLACEHOLDER_REVISION,
|
||||
"--patch_issue", specs.PLACEHOLDER_ISSUE,
|
||||
"--patch_set", specs.PLACEHOLDER_PATCHSET,
|
||||
"--patch_server", specs.PLACEHOLDER_CODEREVIEW_SERVER,
|
||||
"--alsologtostderr")
|
||||
b.linuxGceDimensions(MACHINE_TYPE_SMALL)
|
||||
b.cipd(CIPD_PKG_LUCI_AUTH)
|
||||
b.serviceAccount("skia-g3-framework-compile@skia-swarming-bots.iam.gserviceaccount.com")
|
||||
b.timeout(3 * time.Hour)
|
||||
b.usesGit()
|
||||
b.attempts(1)
|
||||
})
|
||||
}
|
||||
@ -1457,7 +1467,7 @@ func (b *jobBuilder) puppeteer() {
|
||||
b.serviceAccount(b.cfg.ServiceAccountCompile)
|
||||
|
||||
webglversion := "2"
|
||||
if (b.extraConfig("WebGL1")) {
|
||||
if b.extraConfig("WebGL1") {
|
||||
webglversion = "1"
|
||||
}
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
[
|
||||
{
|
||||
"cmd": [],
|
||||
"name": "RECIPE CRASH (Uncaught exception)",
|
||||
"~followup_annotations": [
|
||||
"@@@STEP_EXCEPTION@@@",
|
||||
"The recipe has crashed at point 'Uncaught exception'!",
|
||||
"",
|
||||
"Traceback (most recent call last):",
|
||||
" File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/engine.py\", in run_steps",
|
||||
" raw_result = recipe_obj.run_steps(api, engine)",
|
||||
" File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/recipe_deps.py\", in run_steps",
|
||||
" properties_def, api=api)",
|
||||
" File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in invoke_with_properties",
|
||||
" arg_names, **additional_args)",
|
||||
" File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties",
|
||||
" return callable_obj(*props, **additional_args)",
|
||||
" File \"RECIPE_REPO[skia]/infra/bots/recipes/g3_canary.py\", line 22, in RunSteps",
|
||||
" raise Exception('%s can only be run as a trybot.' % api.vars.builder_name)",
|
||||
"Exception: Build-Debian10-Clang-TAP-Presubmit-G3_Framework can only be run as a trybot."
|
||||
]
|
||||
},
|
||||
{
|
||||
"failure": {
|
||||
"humanReason": "Uncaught Exception: Exception('Build-Debian10-Clang-TAP-Presubmit-G3_Framework can only be run as a trybot.',)"
|
||||
},
|
||||
"name": "$result"
|
||||
}
|
||||
]
|
@ -1,25 +0,0 @@
|
||||
[
|
||||
{
|
||||
"cmd": [
|
||||
"python",
|
||||
"[START_DIR]/skia/infra/bots/g3_canary/trigger_wait_g3_task.py",
|
||||
"--issue",
|
||||
"456789",
|
||||
"--patchset",
|
||||
"12",
|
||||
"--output_file",
|
||||
"[CLEANUP]/g3_try_tmp_1/output_file",
|
||||
"--builder_name",
|
||||
"Build-Debian10-Clang-TAP-Presubmit-G3_Framework"
|
||||
],
|
||||
"cwd": "[START_DIR]/skia",
|
||||
"env": {
|
||||
"CHROME_HEADLESS": "1",
|
||||
"PATH": "<PATH>:RECIPE_REPO[depot_tools]"
|
||||
},
|
||||
"name": "Trigger and wait for g3 canary task"
|
||||
},
|
||||
{
|
||||
"name": "$result"
|
||||
}
|
||||
]
|
@ -1,51 +0,0 @@
|
||||
[
|
||||
{
|
||||
"cmd": [
|
||||
"python",
|
||||
"[START_DIR]/skia/infra/bots/g3_canary/trigger_wait_g3_task.py",
|
||||
"--issue",
|
||||
"456789",
|
||||
"--patchset",
|
||||
"12",
|
||||
"--output_file",
|
||||
"[CLEANUP]/g3_try_tmp_1/output_file",
|
||||
"--builder_name",
|
||||
"Build-Debian10-Clang-TAP-Presubmit-G3_Framework"
|
||||
],
|
||||
"cwd": "[START_DIR]/skia",
|
||||
"env": {
|
||||
"CHROME_HEADLESS": "1",
|
||||
"PATH": "<PATH>:RECIPE_REPO[depot_tools]"
|
||||
},
|
||||
"name": "Trigger and wait for g3 canary task",
|
||||
"~followup_annotations": [
|
||||
"@@@STEP_FAILURE@@@"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cmd": [
|
||||
"vpython",
|
||||
"-u",
|
||||
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
|
||||
"--json-output",
|
||||
"/path/to/tmp/json",
|
||||
"copy",
|
||||
"[CLEANUP]/g3_try_tmp_1/output_file",
|
||||
"/path/to/tmp/"
|
||||
],
|
||||
"infra_step": true,
|
||||
"name": "Read task json",
|
||||
"~followup_annotations": [
|
||||
"@@@STEP_LOG_LINE@output_file@{\"cl\": 12345}@@@",
|
||||
"@@@STEP_LOG_END@output_file@@@",
|
||||
"@@@STEP_LINK@CL link@http://cl/12345@@@"
|
||||
]
|
||||
},
|
||||
{
|
||||
"failure": {
|
||||
"failure": {},
|
||||
"humanReason": "Step('Trigger and wait for g3 canary task') (retcode: 1)"
|
||||
},
|
||||
"name": "$result"
|
||||
}
|
||||
]
|
@ -1,92 +0,0 @@
|
||||
# Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
|
||||
DEPS = [
|
||||
'recipe_engine/context',
|
||||
'recipe_engine/file',
|
||||
'recipe_engine/json',
|
||||
'recipe_engine/path',
|
||||
'recipe_engine/properties',
|
||||
'recipe_engine/raw_io',
|
||||
'recipe_engine/step',
|
||||
'run',
|
||||
'vars',
|
||||
]
|
||||
|
||||
|
||||
def RunSteps(api):
|
||||
api.vars.setup()
|
||||
if not api.vars.is_trybot:
|
||||
raise Exception('%s can only be run as a trybot.' % api.vars.builder_name)
|
||||
|
||||
infrabots_dir = api.path['start_dir'].join('skia', 'infra', 'bots')
|
||||
trigger_wait_g3_script = infrabots_dir.join('g3_canary',
|
||||
'trigger_wait_g3_task.py')
|
||||
|
||||
output_dir = api.path.mkdtemp('g3_try')
|
||||
output_file = output_dir.join('output_file')
|
||||
# Trigger a canary task and wait for it to complete.
|
||||
cmd = ['python', trigger_wait_g3_script,
|
||||
'--issue', api.vars.issue,
|
||||
'--patchset', api.vars.patchset,
|
||||
'--output_file', output_file,
|
||||
'--builder_name', api.vars.builder_name,
|
||||
]
|
||||
try:
|
||||
with api.context(cwd=api.path['start_dir'].join('skia')):
|
||||
api.run(api.step, 'Trigger and wait for g3 canary task', cmd=cmd)
|
||||
except api.step.StepFailure as e:
|
||||
# Add CL link if it exists in the output_file.
|
||||
task_json = api.file.read_json(
|
||||
'Read task json', output_file, test_data={'cl': 12345})
|
||||
if task_json.get('cl'):
|
||||
api.step.active_result.presentation.links['CL link'] = (
|
||||
'http://cl/%d' % task_json['cl'])
|
||||
raise e
|
||||
|
||||
|
||||
def GenTests(api):
|
||||
yield(
|
||||
api.test('g3_canary_trybot') +
|
||||
api.properties.tryserver(
|
||||
gerrit_project='skia',
|
||||
gerrit_url='https://skia-review.googlesource.com/',
|
||||
) +
|
||||
api.properties(
|
||||
buildername='Build-Debian10-Clang-TAP-Presubmit-G3_Framework',
|
||||
path_config='kitchen',
|
||||
swarm_out_dir='[SWARM_OUT_DIR]',
|
||||
repository='https://skia.googlesource.com/skia.git',
|
||||
revision='abc123',
|
||||
)
|
||||
)
|
||||
|
||||
yield(
|
||||
api.test('g3_canary_trybot_failure') +
|
||||
api.properties.tryserver(
|
||||
gerrit_project='skia',
|
||||
gerrit_url='https://skia-review.googlesource.com/',
|
||||
) +
|
||||
api.properties(
|
||||
buildername='Build-Debian10-Clang-TAP-Presubmit-G3_Framework',
|
||||
path_config='kitchen',
|
||||
swarm_out_dir='[SWARM_OUT_DIR]',
|
||||
repository='https://skia.googlesource.com/skia.git',
|
||||
revision='abc123',
|
||||
) +
|
||||
api.step_data('Trigger and wait for g3 canary task', retcode=1)
|
||||
)
|
||||
|
||||
yield(
|
||||
api.test('g3_canary_nontrybot') +
|
||||
api.properties(
|
||||
buildername='Build-Debian10-Clang-TAP-Presubmit-G3_Framework',
|
||||
path_config='kitchen',
|
||||
swarm_out_dir='[SWARM_OUT_DIR]',
|
||||
repository='https://skia.googlesource.com/skia.git',
|
||||
revision='abc123',
|
||||
) +
|
||||
api.expect_exception('Exception')
|
||||
)
|
235
infra/bots/task_drivers/g3_canary/g3_canary.go
Normal file
235
infra/bots/task_drivers/g3_canary/g3_canary.go
Normal file
@ -0,0 +1,235 @@
|
||||
// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/option"
|
||||
|
||||
"go.skia.org/infra/go/auth"
|
||||
"go.skia.org/infra/go/gcs"
|
||||
"go.skia.org/infra/go/gcs/gcsclient"
|
||||
"go.skia.org/infra/go/httputils"
|
||||
"go.skia.org/infra/go/skerr"
|
||||
"go.skia.org/infra/go/sklog"
|
||||
"go.skia.org/infra/task_driver/go/lib/auth_steps"
|
||||
"go.skia.org/infra/task_driver/go/lib/checkout"
|
||||
"go.skia.org/infra/task_driver/go/td"
|
||||
)
|
||||
|
||||
const (
|
||||
g3CanaryBucketName = "g3-compile-tasks"
|
||||
|
||||
InfraFailureErrorMsg = "Your run failed due to unknown infrastructure failures. Ask the trooper to investigate (or directly ping rmistry@)."
|
||||
MissingApprovalErrorMsg = "To run the G3 tryjob, changes must be either owned and authored by Googlers or approved (Code-Review+1) by Googlers."
|
||||
MergeConflictErrorMsg = "G3 tryjob failed because the change is causing a merge conflict when applying it to the Skia hash in G3."
|
||||
|
||||
PatchingInformation = "Tip: If needed, could try patching in the CL into a local G3 client with \"g4 patch\" and then hacking on it."
|
||||
)
|
||||
|
||||
type CanaryStatusType string
|
||||
|
||||
const (
|
||||
ExceptionStatus CanaryStatusType = "exception"
|
||||
MissingApprovalStatus CanaryStatusType = "missing_approval"
|
||||
MergeConflictStatus CanaryStatusType = "merge_conflict"
|
||||
FailureStatus CanaryStatusType = "failure"
|
||||
SuccessStatus CanaryStatusType = "success"
|
||||
)
|
||||
|
||||
type G3CanaryTask struct {
|
||||
Issue int `json:"issue"`
|
||||
Patchset int `json:"patchset"`
|
||||
Status CanaryStatusType `json:"status"`
|
||||
Result string `json:"result"`
|
||||
Error string `json:"error"`
|
||||
CL int `json:"cl"`
|
||||
}
|
||||
|
||||
func isNotFoundError(err error) bool {
|
||||
if err == storage.ErrObjectNotExist {
|
||||
return true
|
||||
} else if strings.Contains(err.Error(), "No such object") {
|
||||
// https://github.com/googleapis/google-cloud-go/issues/2635
|
||||
return true
|
||||
} else if t, ok := err.(*googleapi.Error); ok {
|
||||
// The storage library doesn't return gs.ErrObjectNotExist when Delete
|
||||
// returns a 404. Catch that explicitly.
|
||||
if t.Code == http.StatusNotFound {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
projectId = flag.String("project_id", "", "ID of the Google Cloud project.")
|
||||
taskId = flag.String("task_id", "", "ID of this task.")
|
||||
taskName = flag.String("task_name", "", "Name of the task.")
|
||||
output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.")
|
||||
local = flag.Bool("local", true, "True if running locally (as opposed to on the bots)")
|
||||
|
||||
checkoutFlags = checkout.SetupFlags(nil)
|
||||
)
|
||||
ctx := td.StartRun(projectId, taskId, taskName, output, local)
|
||||
defer td.EndRun(ctx)
|
||||
|
||||
rs, err := checkout.GetRepoState(checkoutFlags)
|
||||
if err != nil {
|
||||
td.Fatal(ctx, skerr.Wrap(err))
|
||||
}
|
||||
if rs.Issue == "" || rs.Patchset == "" {
|
||||
td.Fatalf(ctx, "This task driver should be run only as a try bot")
|
||||
}
|
||||
|
||||
// Create token source with scope for GCS access.
|
||||
ts, err := auth_steps.Init(ctx, *local, auth.SCOPE_USERINFO_EMAIL, auth.SCOPE_FULL_CONTROL)
|
||||
if err != nil {
|
||||
td.Fatal(ctx, skerr.Wrap(err))
|
||||
}
|
||||
client := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()
|
||||
store, err := storage.NewClient(ctx, option.WithHTTPClient(client))
|
||||
if err != nil {
|
||||
td.Fatalf(ctx, "Failed to create storage service client: %s", err)
|
||||
}
|
||||
gcsClient := gcsclient.New(store, g3CanaryBucketName)
|
||||
|
||||
taskFileName := fmt.Sprintf("%s-%s.json", rs.Issue, rs.Patchset)
|
||||
taskStoragePath := fmt.Sprintf("gs://%s/%s", g3CanaryBucketName, taskFileName)
|
||||
|
||||
err = td.Do(ctx, td.Props("Trigger new task if not already running"), func(ctx context.Context) error {
|
||||
if _, err := gcsClient.GetFileContents(ctx, taskFileName); err != nil {
|
||||
if isNotFoundError(err) {
|
||||
// The task is not already running. Create a new file to trigger a new run.
|
||||
if err := triggerCanaryRoll(ctx, rs.Issue, rs.Patchset, taskFileName, taskStoragePath, gcsClient); err != nil {
|
||||
td.Fatal(ctx, fmt.Errorf("Could not trigger canary roll for %s/%s: %s", rs.Issue, rs.Patchset, err))
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Could not read %s: %s", taskStoragePath, err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("G3 canary task for %s/%s already exists\n", rs.Issue, rs.Patchset)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
td.Fatal(ctx, skerr.Wrap(err))
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Cleanup the storage file after the task finishes.
|
||||
if err := gcsClient.DeleteFile(ctx, taskFileName); err != nil {
|
||||
sklog.Errorf("Could not delete %s: %s", taskStoragePath, err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Add documentation link for canary rolls.
|
||||
td.StepText(ctx, "Canary roll doc", "https://goto.google.com/autoroller-canary-bots")
|
||||
|
||||
// Wait for the canary roll to finish.
|
||||
if err := waitForCanaryRoll(ctx, taskFileName, taskStoragePath, gcsClient); err != nil {
|
||||
td.Fatal(ctx, skerr.Wrap(err))
|
||||
}
|
||||
}
|
||||
|
||||
func triggerCanaryRoll(ctx context.Context, issue, patchset, taskFileName, taskStoragePath string, gcsClient gcs.GCSClient) error {
|
||||
ctx = td.StartStep(ctx, td.Props("Trigger canary roll"))
|
||||
defer td.EndStep(ctx)
|
||||
|
||||
i, err := strconv.Atoi(issue)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not convert %s to int: %s", issue, err)
|
||||
}
|
||||
p, err := strconv.Atoi(patchset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not convert %s to int: %s", patchset, err)
|
||||
}
|
||||
newTask := G3CanaryTask{
|
||||
Issue: i,
|
||||
Patchset: p,
|
||||
}
|
||||
taskJson, err := json.Marshal(newTask)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not encode task to JSON: %s", err)
|
||||
}
|
||||
if err := gcsClient.SetFileContents(ctx, taskFileName, gcs.FILE_WRITE_OPTS_TEXT, taskJson); err != nil {
|
||||
return fmt.Errorf("Could not write task to %s: %s", taskStoragePath, err)
|
||||
}
|
||||
fmt.Printf("G3 canary task for %s/%s has been successfully added to %s\n", issue, patchset, taskStoragePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForCanaryRoll(parentCtx context.Context, taskFileName, taskStoragePath string, gcsClient gcs.GCSClient) error {
|
||||
ctx := td.StartStep(parentCtx, td.Props("Wait for canary roll"))
|
||||
defer td.EndStep(ctx)
|
||||
|
||||
// For writing to the step's log stream.
|
||||
stdout := td.NewLogStream(ctx, "stdout", td.Info)
|
||||
// Lets add the roll link only once to step data.
|
||||
addedRollLinkStepData := false
|
||||
for {
|
||||
// Read task status from storage.
|
||||
contents, err := gcsClient.GetFileContents(ctx, taskFileName)
|
||||
if err != nil {
|
||||
return td.FailStep(ctx, fmt.Errorf("Could not read contents of %s: %s", taskStoragePath, err))
|
||||
}
|
||||
var task G3CanaryTask
|
||||
if err := json.Unmarshal(contents, &task); err != nil {
|
||||
return td.FailStep(ctx, fmt.Errorf("Could not unmarshal %s: %s", taskStoragePath, err))
|
||||
}
|
||||
|
||||
var rollStatus string
|
||||
if task.CL == 0 {
|
||||
rollStatus = "Waiting for Canary roll to start"
|
||||
} else {
|
||||
clLink := fmt.Sprintf("http://cl/%d", task.CL)
|
||||
if !addedRollLinkStepData {
|
||||
// Add the roll link to both the current step and it's parent.
|
||||
td.StepText(ctx, "Canary roll CL", clLink)
|
||||
td.StepText(parentCtx, "Canary roll CL", clLink)
|
||||
addedRollLinkStepData = true
|
||||
}
|
||||
rollStatus = fmt.Sprintf("Canary roll [ %s ] has status %s", clLink, task.Result)
|
||||
}
|
||||
if _, err := stdout.Write([]byte(rollStatus)); err != nil {
|
||||
return td.FailStep(ctx, fmt.Errorf("Could not write to stdout: %s", err))
|
||||
}
|
||||
|
||||
switch task.Status {
|
||||
case "":
|
||||
// Still waiting for the task.
|
||||
time.Sleep(30 * time.Second)
|
||||
continue
|
||||
case ExceptionStatus:
|
||||
if task.Error == "" {
|
||||
return td.FailStep(ctx, fmt.Errorf("Run failed with: %s", task.Error))
|
||||
} else {
|
||||
// Use a general purpose error message.
|
||||
return td.FailStep(ctx, errors.New(InfraFailureErrorMsg))
|
||||
}
|
||||
case MissingApprovalStatus:
|
||||
return td.FailStep(ctx, errors.New(MissingApprovalErrorMsg))
|
||||
case MergeConflictStatus:
|
||||
return td.FailStep(ctx, errors.New(MergeConflictErrorMsg))
|
||||
case FailureStatus:
|
||||
return td.FailStep(ctx, fmt.Errorf("Run failed G3 TAP.\n%s", PatchingInformation))
|
||||
case SuccessStatus:
|
||||
// Run passed G3 TAP.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
@ -14972,63 +14972,36 @@
|
||||
"service_account": "skia-canary@skia-swarming-bots.iam.gserviceaccount.com"
|
||||
},
|
||||
"Canary-G3": {
|
||||
"caches": [
|
||||
{
|
||||
"name": "git",
|
||||
"path": "cache/git"
|
||||
},
|
||||
{
|
||||
"name": "git_cache",
|
||||
"path": "cache/git_cache"
|
||||
},
|
||||
{
|
||||
"name": "vpython",
|
||||
"path": "cache/vpython"
|
||||
}
|
||||
],
|
||||
"cipd_packages": [
|
||||
{
|
||||
"name": "infra/3pp/tools/git/linux-amd64",
|
||||
"path": "cipd_bin_packages",
|
||||
"version": "version:2.28.0-rc1.chromium.6"
|
||||
},
|
||||
{
|
||||
"name": "infra/tools/git/${platform}",
|
||||
"path": "cipd_bin_packages",
|
||||
"version": "git_revision:08768c6d238082f3c552dcabef6aaf4c6792d91a"
|
||||
},
|
||||
{
|
||||
"name": "infra/tools/luci-auth/${platform}",
|
||||
"path": "cipd_bin_packages",
|
||||
"version": "git_revision:08768c6d238082f3c552dcabef6aaf4c6792d91a"
|
||||
},
|
||||
{
|
||||
"name": "infra/tools/luci/git-credential-luci/${platform}",
|
||||
"path": "cipd_bin_packages",
|
||||
"version": "git_revision:08768c6d238082f3c552dcabef6aaf4c6792d91a"
|
||||
},
|
||||
{
|
||||
"name": "infra/tools/luci/kitchen/${platform}",
|
||||
"path": ".",
|
||||
"version": "git_revision:08768c6d238082f3c552dcabef6aaf4c6792d91a"
|
||||
},
|
||||
{
|
||||
"name": "infra/tools/luci/vpython/${platform}",
|
||||
"path": "cipd_bin_packages",
|
||||
"version": "git_revision:08768c6d238082f3c552dcabef6aaf4c6792d91a"
|
||||
}
|
||||
],
|
||||
"command": [
|
||||
"cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
|
||||
"-u",
|
||||
"skia/infra/bots/run_recipe.py",
|
||||
"${ISOLATED_OUTDIR}",
|
||||
"g3_canary",
|
||||
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Canary-G3\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
|
||||
"skia"
|
||||
"./g3_canary",
|
||||
"--local=false",
|
||||
"--project_id",
|
||||
"skia-swarming-bots",
|
||||
"--task_id",
|
||||
"<(TASK_ID)",
|
||||
"--task_name",
|
||||
"Canary-G3",
|
||||
"--repo",
|
||||
"<(REPO)",
|
||||
"--revision",
|
||||
"<(REVISION)",
|
||||
"--patch_issue",
|
||||
"<(ISSUE)",
|
||||
"--patch_set",
|
||||
"<(PATCHSET)",
|
||||
"--patch_server",
|
||||
"<(CODEREVIEW_SERVER)",
|
||||
"--alsologtostderr"
|
||||
],
|
||||
"dependencies": [
|
||||
"Housekeeper-PerCommit-BundleRecipes"
|
||||
"Housekeeper-PerCommit-BuildTaskDrivers"
|
||||
],
|
||||
"dimensions": [
|
||||
"cpu:x86-64-Haswell_GCE",
|
||||
@ -15037,21 +15010,9 @@
|
||||
"os:Debian-10.3",
|
||||
"pool:Skia"
|
||||
],
|
||||
"env_prefixes": {
|
||||
"PATH": [
|
||||
"cipd_bin_packages",
|
||||
"cipd_bin_packages/bin"
|
||||
],
|
||||
"VPYTHON_VIRTUALENV_ROOT": [
|
||||
"cache/vpython"
|
||||
]
|
||||
},
|
||||
"execution_timeout_ns": 10800000000000,
|
||||
"extra_tags": {
|
||||
"log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
|
||||
},
|
||||
"io_timeout_ns": 10800000000000,
|
||||
"isolate": "canary.isolate",
|
||||
"isolate": "empty.isolate",
|
||||
"max_attempts": 1,
|
||||
"service_account": "skia-g3-framework-compile@skia-swarming-bots.iam.gserviceaccount.com"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user