blob: 04d6fa15179a6ced041ce5bebbdd849e1d5bf191 [file] [log] [blame]
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from functools import reduce
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 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 recipe_engine.post_process import DropExpectation, StatusFailure
from recipe_engine.post_process import StatusSuccess, StepCommandRE
from RECIPE_MODULES.infra.windows_scripts_executor import test_helper as t
DEPS = [
'recipe_engine/path',
'recipe_engine/properties',
'recipe_engine/platform',
'recipe_engine/json',
'recipe_engine/raw_io',
'windows_scripts_executor',
'depot_tools/bot_update',
'depot_tools/gclient',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/proto',
'recipe_engine/step',
'recipe_engine/buildbucket',
'recipe_engine/runtime',
'windows_adk',
]
PYTHON_VERSION_COMPATIBILITY = 'PY3'
PROPERTIES = input_pb.Inputs
################################ TEST DATA ####################################
###############################################################################
# The tests here ensure the correctness of the scheduler. The customizations #
# here are all designed to test one or the other features of the scheduler. #
# In a nutshell scheduler ensures that every customization is executed once #
# and only once and every customization can be executed when scheduled. The #
# scheduling logic groups together, the customizations that target the same #
# arch and can that can be executed on the same builder. There can be max of #
# MAX_CUST_BATCH_SIZE customizations in any job scheduled. #
# #
# The following are the images that are being used for the tests that follow #
# 1.ONLINE_BOOTSTRAP: This image contains seven customizations, 6 of them are #
# offline winpe customizations that can be run on Wim Customzation Builder #
# and the other is a windows iso customization. This can only be run on #
# Windows Customization Builder. The scheduler should ensure that wim #
# customizations are run first. Followed by windows customization. #
# 2.ONLINE_CUST: This image uses the bootstrapped ISO image generated by the #
# ONLINE_BOOTSTRAP. This can only be run after the ISO image is generated. #
# is also possible to package it with the customization that generates the #
# ISO image, but it has to come after the image is generated. #
###############################################################################
ONLINE_BOOTSTRAP = wib.Image(
name='bootstrap',
arch=wib.ARCH_AMD64,
customizations=[
wib.Customization(
offline_winpe_customization=owc.OfflineWinPECustomization(
name='wim-1',
offline_customization=[
actions.OfflineAction(name='wim-1-custs', actions=[])
])),
wib.Customization(
offline_winpe_customization=owc.OfflineWinPECustomization(
name='wim-2',
# Use the image from wim-1
image_src=sources.Src(
local_src='image(bootstrap)-cust(wim-1)-output'),
offline_customization=[
actions.OfflineAction(name='wim-2-custs', actions=[])
])),
wib.Customization(
offline_winpe_customization=owc.OfflineWinPECustomization(
name='wim-3',
# Use the image from wim-2
image_src=sources.Src(
local_src='image(bootstrap)-cust(wim-2)-output'),
offline_customization=[
actions.OfflineAction(name='wim-3-custs', actions=[])
])),
wib.Customization(
offline_winpe_customization=owc.OfflineWinPECustomization(
name='wim-4',
# Use the image from wim-3
image_src=sources.Src(
local_src='image(bootstrap)-cust(wim-3)-output'),
offline_customization=[
actions.OfflineAction(name='wim-4-custs', actions=[])
])),
wib.Customization(
offline_winpe_customization=owc.OfflineWinPECustomization(
name='wim-5',
# Use the image from wim-4
image_src=sources.Src(
local_src='image(bootstrap)-cust(wim-4)-output'),
offline_customization=[
actions.OfflineAction(name='wim-5-custs', actions=[])
])),
wib.Customization(
offline_winpe_customization=owc.OfflineWinPECustomization(
name='wim-6',
# Use the image from wim-5
image_src=sources.Src(
local_src='image(bootstrap)-cust(wim-5)-output'),
offline_customization=[
actions.OfflineAction(name='wim-6-custs', actions=[])
])),
wib.Customization(
windows_iso_customization=windows_iso.WinISOImage(
name='bootstrap_image',
base_image=sources.Src(
cipd_src=sources.CIPDSrc(
package='infra/labs/win10',
refs='latest',
platform='windows-amd64',
filename='Win10.iso')),
copy_files=[
windows_iso.CopyArtifact(
artifact=sources.Src(
local_src='image(bootstrap)-cust(wim-6)-output'),
mount=True,
source='sources/boot/boot.wim',
)
],
))
])
ONLINE_CUST = wib.Image(
name='online_cust_example',
arch=wib.ARCH_AMD64,
customizations=[
wib.Customization(
online_windows_customization=onwc.OnlineWinCustomization(
name='run_cust',
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='Win10.iso',
input_src=sources.Src(
# Use the image generated by
# ONLINE_BOOTSTRAP
local_src='image(bootstrap)'
'-cust(bootstrap_image)-output'),
interface='none',
media='cdrom',
readonly=True),
drive.Drive(
name='system.img',
interface='none',
media='drive',
size=1234546,
filesystem='fat')
])),
)
]))
])
TESTS = {'bootstrap.cfg': ONLINE_BOOTSTRAP, 'online.cfg': ONLINE_CUST}
DIR_DATA = {'tests/basic': ['bootstrap.cfg', 'online.cfg']}
def tests(config):
return TESTS[config]
def lsdir(path):
return DIR_DATA[path]
############################## TEST DATA END ##################################
def RunSteps(api, config):
api.windows_scripts_executor.init()
custs = []
images = []
cfg_path = api.path.cache_dir / config.config_path
# list the dir to get the corresponding inputs to tests. see DIR_DATA
cfgs = api.file.listdir(
"Read all the configs", cfg_path, test_data=lsdir(config.config_path))
for cfg in cfgs:
# read the configs using helper method tests, see TESTS
images.append(
api.file.read_proto(
name='Reading ' + str(cfg),
source=cfg,
msg_class=wib.Image,
codec='TEXTPB',
test_proto=tests(api.path.basename(cfg))))
# Initialize all the images
for image in images:
custs.extend(api.windows_scripts_executor.init_customizations(image))
# 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
custs = api.windows_scripts_executor.process_customizations(custs, {}, inputs)
configs = api.windows_scripts_executor.get_executable_configs(custs)
executed_custs = set()
while configs:
for builder, images in configs.items():
for image, keys in images:
if not keys.issubset(executed_custs):
executed_custs.update(keys)
api.file.write_proto('Write image', api.path.cache_dir / image.name,
image, 'TEXTPB')
else:
raise Exception('Failed to execute the last run') #pragma no cover
configs = api.windows_scripts_executor.get_executable_configs(custs)
def GenTests(api):
# keys corresponding to the test customizations
key_wim_1 = '861f1938f03821f71380f030cb4310d95e66f93ce299cc11e46b65a1405799ba'
key_wim_2 = '0268c9590f99bb16340b5850394995d32716d93439152d37bf47918872290e9c'
key_wim_3 = 'e1a4cfb3b0533403a04dcab86d20af0b8913bbc5fd9c26044aefb5ac9b45e551'
key_wim_4 = 'f8aae81ee074af51989d85af4820d3ec0f1e33af1862c8008954e80c2c7b6237'
key_wim_5 = 'faaaaea5ecd871b7885522d379843731efb09b43727bafc6e895fd2c0e592430'
key_wim_6 = 'd7ba1e059a49f980e58e704411b7134976336a16e03bf916697483b14af5d356'
key_boot = 'bbcff59ef2de5ccb05c8b9679ef78b371d35cc5d58dec639422e34b16bd25219'
key_img = 'e4eb670abbd2e02a9abe006c36aa439c7e931740b6ca1cd4ee48ec4ca5eab363'
system = 'boot(test_boot1)-drive(system.img)-output.zip'
def mock_gsutil(api, url, index):
""" mock_wim mocks the existence of output given by the url
mock the gsutil check. Mock file doesn't exist for index-1 times and
that the file exists for the index time it's queried.
Args:
url: The gsutil url representing the file
index: Index of the query for which we respond yes the file exists.
"""
return reduce(lambda a, b: a + b, [
t.MOCK_CUST_OUTPUT(
api, url +
(' ({})'.format(postfix) if postfix != 1 else ''), postfix == index)
for postfix in range(1, index + 1)
])
yield (
api.test('nothing scheduled', api.platform('linux', 64)) +
# Run the test with basic images
api.properties(input_pb.Inputs(config_path="tests/basic")) +
api.properties.environ(input_pb.EnvProperties(MAX_CUST_BATCH_SIZE="5")) +
# Scheduler gets exists response for all the queries. It doesn't have to
# execute anything
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_1), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_2), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_3), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_4), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_5), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_6), 1) +
mock_gsutil(api, 'gs://chrome-gce-images/WIB-ISO/{}.iso'.format(key_boot),
1) + api.post_process(StatusSuccess) +
api.post_process(DropExpectation))
yield (
api.test('schedule_online_cust', api.platform('linux', 64)) +
# Run the test with basic images
api.properties(input_pb.Inputs(config_path="tests/basic")) +
api.properties.environ(input_pb.EnvProperties(MAX_CUST_BATCH_SIZE="5")) +
# Scheduler only needs to schedule online builder
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_1), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_2), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_3), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_4), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_5), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_6), 1) +
mock_gsutil(api, 'gs://chrome-gce-images/WIB-ISO/{}.iso'.format(key_boot),
1) + api.post_process(StatusSuccess) +
api.post_process(DropExpectation))
yield (
api.test('schedule_iso_cust', api.platform('linux', 64)) +
# Run the test with basic images
api.properties(input_pb.Inputs(config_path="tests/basic")) + mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_1), 1) +
api.properties.environ(input_pb.EnvProperties(MAX_CUST_BATCH_SIZE="5")) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_2), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_3), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_4), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_5), 1) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_6), 1) +
mock_gsutil(api, 'gs://chrome-gce-images/WIB-ISO/{}.iso'.format(key_boot),
2) + api.post_process(StatusSuccess) +
api.post_process(DropExpectation))
yield (
api.test('schedule_wim_cust', api.platform('linux', 64)) +
api.properties.environ(input_pb.EnvProperties(MAX_CUST_BATCH_SIZE="5")) +
# Run the test with basic images
api.properties(input_pb.Inputs(config_path="tests/basic")) + mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_1), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_2), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_3), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_4), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_5), 3) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_6), 3) +
mock_gsutil(api, 'gs://chrome-gce-images/WIB-ISO/{}.iso'.format(key_boot),
1) + api.post_process(StatusSuccess) +
api.post_process(DropExpectation))
yield (
api.test('happy path', api.platform('linux', 64)) +
api.properties.environ(input_pb.EnvProperties(MAX_CUST_BATCH_SIZE="5")) +
# Run the test with basic images
api.properties(input_pb.Inputs(config_path="tests/basic")) + mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_1), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_2), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_3), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_4), 2) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_5), 3) +
mock_gsutil(
api, 'gs://chrome-gce-images/WIB-WIM/{}.zip'.format(key_wim_6), 5) +
mock_gsutil(api, 'gs://chrome-gce-images/WIB-ISO/{}.iso'.format(key_boot),
3) + api.post_process(StatusSuccess) +
api.post_process(DropExpectation))