blob: 7bed784be3269e7b6371d55c5ebb34e96120f9de [file] [log] [blame]
# Copyright 2022 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.
from collections import OrderedDict
from recipe_engine import post_process
from PB.recipe_engine.result import RawResult
from google.protobuf.struct_pb2 import Struct
from PB.recipes.infra.windows_image_builder import input as input_pb
from PB.recipes.infra.windows_image_builder import actions
from PB.recipes.infra.windows_image_builder import vm
from PB.recipes.infra.windows_image_builder import drive
from PB.recipes.infra.windows_image_builder import dest
from PB.recipes.infra.windows_image_builder import sources
from PB.recipes.infra.windows_image_builder import windows_vm
from PB.recipes.infra.windows_image_builder import windows_iso
from PB.recipes.infra.windows_image_builder \
import windows_image_builder as wib
from PB.recipes.infra.windows_image_builder \
import offline_winpe_customization as owc
from PB.recipes.infra.windows_image_builder \
import online_windows_customization as onwc
from PB.go.chromium.org.luci.buildbucket.proto \
import builds_service as bs_pb2
from PB.go.chromium.org.luci.buildbucket.proto \
import build as b_pb2
from PB.go.chromium.org.luci.buildbucket.proto \
import common as common_pb2
from RECIPE_MODULES.infra.windows_scripts_executor \
import test_helper as t
DEPS = [
'depot_tools/bot_update',
'depot_tools/gclient',
'depot_tools/gitiles',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/proto',
'recipe_engine/step',
'recipe_engine/buildbucket',
'recipe_engine/raw_io',
'recipe_engine/runtime',
'windows_adk',
'windows_scripts_executor',
]
PYTHON_VERSION_COMPATIBILITY = 'PY3'
PROPERTIES = input_pb.Inputs
################################ TEST DATA ####################################
TEST_IMAGE = wib.Image(
name='test',
arch=wib.ARCH_X86,
customizations=[
wib.Customization(
offline_winpe_customization=owc.OfflineWinPECustomization(
name='test_cust',
offline_customization=[
actions.OfflineAction(
name='tests',
actions=[
actions.Action(
add_file=actions.AddFile(
name='add_psovercom',
src=sources.Src(
git_src=sources.GITSrc(
repo='https://winimage.gsrc.com/r',
src='images/PSOverCom.ps1',
ref='HEAD')))),
actions.Action(
add_file=actions.AddFile(
name='add_startnet',
src=sources.Src(
git_src=sources.GITSrc(
repo='https://winimage.gsrc.com/r',
src='images/startnet.cmd',
ref='HEAD')))),
])
])),
wib.Customization(
windows_iso_customization=windows_iso.WinISOImage(
name='bimage',
base_image=sources.Src(
cipd_src=sources.CIPDSrc(
package='infra_internal/labs/images/windows/10/22h2',
refs='latest',
platform='windows-amd64',
filename='Win10.iso')),
copy_files=[
windows_iso.CopyArtifact(
artifact=sources.Src(
local_src='image(test)-cust(test_cust)-output'),
mount=True,
source='sources/boot/boot.wim',
)
],
)),
wib.Customization(
online_windows_customization=onwc.OnlineWinCustomization(
name='test_win',
online_customizations=[
onwc.OnlineCustomization(
name='test_boot1',
vm_config=vm.VM(
qemu_vm=vm.QEMU_VM(
name='squidward',
version='latest',
smp='cores=8',
memory=8192,
device=['ide-cd,drive=newWin.iso'],
drives=[
drive.Drive(
name='WinXP.iso',
input_src=sources.Src(
local_src='image(test)-'
'cust(bimage)-output'),
interface='none',
media='cdrom',
readonly=True),
drive.Drive(
name='system.img',
output_dests=[
dest.Dest(
gcs_src=sources.GCSSrc(
bucket='chrome-gce-images',
source='tests/sys.img',
))
],
interface='none',
media='drive',
size=1234546,
filesystem='fat')
])),
win_vm_config=windows_vm.WindowsVMConfig(
boot_time=300, shutdown_time=300),
)
]))
])
TEST_ISO_IMAGE = wib.Image(
name='test',
arch=wib.ARCH_X86,
customizations=[
wib.Customization(
windows_iso_customization=windows_iso.WinISOImage(
name='bimage',
base_image=sources.Src(
cipd_src=sources.CIPDSrc(
package='infra_internal/labs/images/windows/10/22h2',
refs='latest',
platform='windows-amd64',
filename='Win10.iso')),
copy_files=[
windows_iso.CopyArtifact(
artifact=sources.Src(
local_src='image(test)-cust(test_cust)-output'),
mount=True,
source='sources/boot/boot.wim',
)
],
)),
])
TESTS = {'test1.cfg': TEST_IMAGE, 'test2.cfg': TEST_ISO_IMAGE}
DIR_DATA = {
'tests/basic': ['test1.cfg'],
'tests/collision': ['test1.cfg', 'test2.cfg']
}
def mock_tests(config):
if config in TESTS.keys():
return TESTS[config]
return None #pragma: no cover
def mock_lsdir(path):
if path in DIR_DATA.keys():
return DIR_DATA[path]
return None #pragma: no cover
############################## TEST DATA END ##################################
def url_title(build):
""" url_title is a helper function to display the customization
name over the build link in schedule process.
Returns string formatted with builder name and customization
"""
props = build.input.properties
return props['name']
def RunSteps(api, inputs):
"""This recipe runs image builder for a given user config."""
configs = []
if not inputs.config_path:
raise api.step.StepFailure("`config_path` is a required property")
builder_named_cache = api.path['cache'].join('builder')
with api.step.nest('read user config') as c:
# download the configs repo
api.gclient.set_config('infradata_config')
api.gclient.c.solutions[0].revision = 'origin/main'
with api.context(cwd=builder_named_cache):
api.bot_update.ensure_checkout()
api.gclient.runhooks()
# split the string on '/' as luci scheduler passes a unix path and this
# recipe is expected to run on windows ('\')
cfg_path = builder_named_cache.join('infra-data-config',
*inputs.config_path.split('/'))
# Recursively call the offline.py recipe with all configs
cfgs = api.file.listdir(
"Read all the configs",
cfg_path,
test_data=mock_lsdir(inputs.config_path))
reqs = []
for cfg in cfgs:
if str(cfg).endswith('.cfg'):
try:
configs.append(
api.file.read_proto(
name='Reading ' + inputs.config_path,
source=cfg,
msg_class=wib.Image,
codec='TEXTPB',
test_proto=mock_tests(api.path.basename(cfg))))
except ValueError as e: #pragma: no cover
_, name = api.path.split(cfg)
summary = c.step_summary_text
summary += 'Failed to read {}: {} <br>'.format(name, e)
c.step_summary_text = summary
if not configs:
# If there are no config files, exit
return #pragma: no cover
# initialize the recipe_module
api.windows_scripts_executor.init()
# collect all the customizations from all the configs
custs = []
for config in configs:
custs.extend(api.windows_scripts_executor.init_customizations(config))
# Get all the inputs required. This will be used to determine if we have
# to cache any images in online customization
inputs = []
for cust in custs:
for ip in cust.inputs:
if ip.WhichOneof('src') == 'local_src':
inputs.append(ip.local_src)
# process all the customizations (pin artifacts, generate hash)
api.windows_scripts_executor.process_customizations(custs, {}, inputs)
# Dict mapping the customization object key to list of customizations
# corresponding to a customization. This ensures that we don't miss
# executing a customization if its an exact copy of another. We only
# execute one. But show links to both
key_cust_map = OrderedDict()
for cust in custs:
if cust.get_key() in key_cust_map:
raise Exception('{} and {} are duplicate customizations'.format(
cust.id, key_cust_map[cust.get_key()].id))
# Update the key map
key_cust_map[cust.get_key()] = cust
# triggered_custs contains the list of cust keys that have been triggered
triggered_custs = set()
# list of cust keys that failed to build
failed_custs = set()
# list of cust keys that had infra failure
infra_failed_custs = set()
# list of cust keys that were cancelled
cancelled_custs = set()
# list of cust keys that were built
built_custs = set()
# mapping from build_id to keys
build_id_keys = {}
with api.step.nest('Execute customizations') as e:
# Get all the images that can be executed at this time
executions = api.windows_scripts_executor.get_executable_configs(custs)
while executions:
# list of builds to wait for
blds = []
# execute the customizations that need to be executed
for builder, images in executions.items():
for img, key_list in images:
# collect tags to add to the build request
tags = {}
for key in key_list:
cust = key_cust_map[key]
tags[cust.id] = key
# convert image to json config
props = api.json.loads(api.proto.encode(img, 'JSONPB'))
req = api.buildbucket.schedule_request(
builder=builder,
properties=props,
tags=api.buildbucket.tags(**tags),
)
triggered_custs = triggered_custs.union(key_list)
# schedule all the builds
builds = api.buildbucket.schedule([req], url_title_fn=url_title)
blds.append(builds[0].id)
# Record all the keys associated with the build id
build_id_keys[builds[0].id] = key_list
for key in key_list:
cust = key_cust_map[key]
# Add a link to the cust build
e.links['{}/{}'.format(
cust.id,
key)] = api.buildbucket.build_url(build_id=builds[0].id)
# wait for all the triggered builds to complete
build_map = api.buildbucket.collect_builds(
[i for i in blds],
step_name='waiting for builds to complete',
timeout=7200)
for build_id, build in build_map.items():
# Collect all 4 terminal build status
if build.status == common_pb2.Status.FAILURE:
failed_custs = failed_custs.union(build_id_keys[build_id])
if build.status == common_pb2.Status.CANCELED:
cancelled_custs = cancelled_custs.union(build_id_keys[build_id])
if build.status == common_pb2.Status.INFRA_FAILURE:
infra_failed_custs = infra_failed_custs.union(build_id_keys[build_id])
if build.status == common_pb2.Status.SUCCESS:
built_custs = built_custs.union(build_id_keys[build_id])
# Avoid triggering the builds again. (In case they failed)
rcusts = [cust for cust in custs if cust.get_key() not in triggered_custs]
# generate the new set of images that can be built
executions = api.windows_scripts_executor.get_executable_configs(rcusts)
summary = 'Summary:<br>'
if failed_custs:
summary += 'Failed:<br>'
for cust in custs:
if cust.get_key() in failed_custs:
summary += '{}/{}<br>'.format(cust.id, cust.get_key())
if infra_failed_custs:
summary += 'InfraFailure:<br>'
for cust in custs:
if cust.get_key() in infra_failed_custs:
summary += '{}/{}<br>'.format(cust.id, cust.get_key())
if cancelled_custs:
summary += 'Canceled:<br>'
for cust in custs:
if cust.get_key() in cancelled_custs:
summary += '{}/{}<br>'.format(cust.id, cust.get_key())
if built_custs:
summary += 'Built:<br>'
for cust in custs:
if cust.get_key() in built_custs:
summary += '{}/{}<br>'.format(cust.id, cust.get_key())
not_built = set()
for cust in custs:
if cust.get_key() not in triggered_custs:
not_built.add(cust.get_key())
if not_built:
summary += 'Did not build:<br>'
for cust in custs:
if cust.get_key() in not_built:
summary += '{}/{}<br>'.format(cust.id, cust.get_key())
if failed_custs or infra_failed_custs or cancelled_custs:
# looks like we failed a few builds
return RawResult(status=common_pb2.FAILURE, summary_markdown=summary)
else:
# looks like everything executed properly, return result
return RawResult(status=common_pb2.SUCCESS, summary_markdown=summary)
def GenTests(api):
key_wim = '0ba325f4cf5356b9864719365a807f2c9d48bf882d333149cebd9d1ec0b64e7b'
key_win = '0f796362b84871b7a0d65e9c3f3d00685614441a3490f64fb4b2a391b4fb9fc4'
key_iso = '2cb3344a7ae9c8e2772563ad8244a1bd99062f629d7c50ecc48e3d0e32974d7d'
system = 'boot(test_boot1)-drive(system.img)-output.zip'
image = 'test'
cust = 'test_cust'
# Mock schedule requests batch response for Wim builder
prop_wim = b_pb2.Build.Input()
prop_wim.properties['name'] = image
prop_wim.properties['customizations'] = [{
'offline_winpe_customization': {
'name': 'test_cust'
},
}]
# Mock schedule requests batch response for windows builder
prop_win = b_pb2.Build.Input()
prop_win.properties['name'] = image
prop_win.properties['customizations'] = [{
'online_windows_customization': {
'name': 'test_win',
},
'windows_iso_customization': {
'name': 'bimage',
}
}]
BATCH_RESPONSE_WIM = bs_pb2.BatchResponse(responses=[
dict(
schedule_build=dict(
builder=dict(builder='Wim Customization Builder'),
input=prop_wim,
id=1234567890123456789)),
])
BATCH_RESPONSE_WIN = bs_pb2.BatchResponse(responses=[
dict(
schedule_build=dict(
builder=dict(builder='Windows Customization Builder'),
input=prop_win,
id=9016911228971028736,
)),
])
def MOCK_CUST_OUTPUT(api, file, success=True):
retcode = 1
if success:
retcode = 0
url = 'gs://chrome-gce-images/{}'.format(file)
return api.step_data(
'Execute customizations.gsutil stat {}'.format(url),
api.raw_io.stream_output(t._gcs_stat.format(url, url)),
retcode=retcode,
)
# Test the happy path for the scheduler. We give scheduler the TEST_IMAGE
# as input. As there are 3 customizations in that image with one dependent on
# the other. It is expected that the scheduler will schedule the WinPE builder
# first (Wim Customization Builder) followed by the Windows customization
# builder (for the remaining 2 customizations).
yield (
api.test('basic_scheduled', api.platform('win', 64)) +
api.properties(input_pb.Inputs(config_path="tests/basic")) +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/PSOverCom.ps1', 'HEAD') +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/startnet.cmd', 'HEAD') +
# Mock the check for output existence. Twice for wim (as output of
# test_cust and input for bimage), twice for iso and once for system.img
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{}'.format(key_win, system),
False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (2)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (2)'.format(key_wim), False) +
# mock schedule output to test builds scheduled
api.buildbucket.simulated_schedule_output(
BATCH_RESPONSE_WIM,
step_name='Execute customizations.buildbucket.schedule') +
# mock collecting the build status
api.buildbucket.simulated_collect_output(
[
api.buildbucket.ci_build_message(
build_id=1234567890123456789, status='SUCCESS'),
],
step_name='Execute customizations.waiting for builds to complete') +
# mock wim output check to show it exists. (wim build was successful)
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (3)'.format(key_wim), True) +
# mock check for iso and img. Show it doesn't exist.
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{} (2)'.format(
key_win, system), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (3)'.format(key_iso), False) +
# mock the windows customization schedule
api.buildbucket.simulated_schedule_output(
BATCH_RESPONSE_WIN,
step_name='Execute customizations.buildbucket.schedule (2)') +
api.buildbucket.simulated_collect_output(
[
api.buildbucket.ci_build_message(
build_id=9016911228971028736, status='SUCCESS'),
],
step_name='Execute customizations.waiting for builds to complete (2)')
+ api.post_process(post_process.StatusSuccess) +
api.post_process(post_process.DropExpectation))
# Test failure on one of the cust. This should fail the recipe
yield (
api.test('basic_partial_failure', api.platform('win', 64)) +
api.properties(input_pb.Inputs(config_path="tests/basic")) +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/PSOverCom.ps1', 'HEAD') +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/startnet.cmd', 'HEAD') +
# Mock the check for output existence. Twice for wim (as output of
# test_cust and input for bimage), twice for iso and once for system.img
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{}'.format(key_win, system),
False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (2)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (2)'.format(key_wim), False) +
# mock schedule output to test builds scheduled
api.buildbucket.simulated_schedule_output(
BATCH_RESPONSE_WIM,
step_name='Execute customizations.buildbucket.schedule') +
# mock collecting the build status
api.buildbucket.simulated_collect_output(
[
api.buildbucket.ci_build_message(
build_id=1234567890123456789, status='SUCCESS'),
],
step_name='Execute customizations.waiting for builds to complete') +
# mock wim output check to show it exists. (wim build was successful)
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (3)'.format(key_wim), True) +
# mock check for iso and img. Show it doesn't exist.
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{} (2)'.format(
key_win, system), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (3)'.format(key_iso), False) +
# mock the windows customization schedule
api.buildbucket.simulated_schedule_output(
BATCH_RESPONSE_WIN,
step_name='Execute customizations.buildbucket.schedule (2)') +
api.buildbucket.simulated_collect_output(
[
api.buildbucket.ci_build_message(
build_id=9016911228971028736, status='FAILURE'),
],
step_name='Execute customizations.waiting for builds to complete (2)')
# img file doesn't exist as it failed to build
+ api.post_process(post_process.StatusFailure) +
api.post_process(post_process.DropExpectation))
# Test builds not scheduled. If all the outputs exist, we don't need to
# schedule a build.
yield (
api.test('basic_no_scheduled', api.platform('win', 64)) +
api.properties(input_pb.Inputs(config_path="tests/basic")) +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/PSOverCom.ps1', 'HEAD') +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/startnet.cmd', 'HEAD') +
# mock all three outputs as exists
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip'.format(key_wim), True) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{}'.format(key_win, system),
True) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso'.format(key_iso), True) +
api.post_process(post_process.StatusSuccess) +
api.post_process(post_process.DropExpectation))
# Test failure due to duplicate customizations. TEST_ISO_IMAGE with TEST_IMAGE
# repeats a customization.
yield (
api.test('basic_failure', api.platform('linux', 64)) +
api.properties(input_pb.Inputs(config_path="tests/collision")) +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/PSOverCom.ps1', 'HEAD') +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/startnet.cmd', 'HEAD') +
# expect exception as a customization was redefined
api.expect_exception('Exception') +
api.post_process(post_process.StatusException) +
api.post_process(post_process.DropExpectation))
# Test failure of a build that was scheduled.
yield (
api.test('basic_scheduled_failure', api.platform('win', 64)) +
api.properties(input_pb.Inputs(config_path="tests/basic")) +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/PSOverCom.ps1', 'HEAD') +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/startnet.cmd', 'HEAD') +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{}'.format(key_win, system),
False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (2)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (3)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (2)'.format(key_wim), False) +
# mock schedule output to test builds scheduled state
api.buildbucket.simulated_schedule_output(
BATCH_RESPONSE_WIM,
step_name='Execute customizations.buildbucket.schedule') +
api.buildbucket.simulated_collect_output(
[
api.buildbucket.ci_build_message(
build_id=1234567890123456789, status='FAILURE'),
],
step_name='Execute customizations.waiting for builds to complete') +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (3)'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{} (2)'.format(
key_win, system), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (4)'.format(key_iso), False) +
api.post_process(post_process.StatusFailure) +
api.post_process(post_process.DropExpectation))
# Test cancellation of a build that was scheduled.
yield (
api.test('basic_scheduled_cancellation', api.platform('win', 64)) +
api.properties(input_pb.Inputs(config_path="tests/basic")) +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/PSOverCom.ps1', 'HEAD') +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/startnet.cmd', 'HEAD') +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{}'.format(key_win, system),
False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (2)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (3)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (2)'.format(key_wim), False) +
# mock schedule output to test builds scheduled state
api.buildbucket.simulated_schedule_output(
BATCH_RESPONSE_WIM,
step_name='Execute customizations.buildbucket.schedule') +
api.buildbucket.simulated_collect_output(
[
api.buildbucket.ci_build_message(
build_id=1234567890123456789, status='CANCELED'),
],
step_name='Execute customizations.waiting for builds to complete') +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (3)'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{} (2)'.format(
key_win, system), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (4)'.format(key_iso), False) +
api.post_process(post_process.StatusFailure) +
api.post_process(post_process.DropExpectation))
# Test failure of a build that was scheduled.
yield (
api.test('basic_scheduled_infra_failure', api.platform('win', 64)) +
api.properties(input_pb.Inputs(config_path="tests/basic")) +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/PSOverCom.ps1', 'HEAD') +
t.GIT_PIN_FILE(api, 'test_cust', 'HEAD', 'images/startnet.cmd', 'HEAD') +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{}'.format(key_win, system),
False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (2)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (2)'.format(key_wim), False) +
# mock schedule output to test builds scheduled state
api.buildbucket.simulated_schedule_output(
BATCH_RESPONSE_WIM,
step_name='Execute customizations.buildbucket.schedule') +
api.buildbucket.simulated_collect_output(
[
api.buildbucket.ci_build_message(
build_id=1234567890123456789, status='INFRA_FAILURE'),
],
step_name='Execute customizations.waiting for builds to complete') +
MOCK_CUST_OUTPUT(api, 'WIB-WIM/{}.zip (3)'.format(key_wim), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ONLINE-CACHE/{}-{} (2)'.format(
key_win, system), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (3)'.format(key_iso), False) +
MOCK_CUST_OUTPUT(api, 'WIB-ISO/{}.iso (4)'.format(key_iso), False) +
api.post_process(post_process.StatusFailure) +
api.post_process(post_process.DropExpectation))
# test failure when run without a config file path.
yield (api.test('run_without_config_path', api.platform('win', 64)) +
api.properties(input_pb.Inputs(config_path="",),) +
api.post_process(post_process.StatusFailure) +
api.post_process(post_process.DropExpectation))