Add snapshot selection to PR and non-PR builds.

PR builds will detect the destination branch and check if that branch
is one of the snapshot branches, otherwise it defaults to the 'development'
snapshot.

Non-PR builds use the user-selected snapshot from the list of available
snapshots.

Signed-off-by: John <jonawals@amazon.com>
This commit is contained in:
John
2021-06-24 18:39:03 +01:00
parent 41a5a5b4f4
commit 992e8500ea
2 changed files with 33 additions and 17 deletions
+21 -6
View File
@@ -18,6 +18,8 @@ EMPTY_JSON = readJSON text: '{}'
ENGINE_REPOSITORY_NAME = 'o3de'
def buildSnapshots = ['development', 'stabilization/2106']
def defaultBuildSnapshot = buildSnapshots.get(0)
def pipelineProperties = []
def pipelineParameters = [
@@ -26,7 +28,8 @@ def pipelineParameters = [
booleanParam(defaultValue: false, description: 'Deletes the contents of the output directory before building. This will cause a \"clean\" build. NOTE: does not imply CLEAN_ASSETS', name: 'CLEAN_OUTPUT_DIRECTORY'),
booleanParam(defaultValue: false, description: 'Deletes the contents of the output directories of the AssetProcessor before building.', name: 'CLEAN_ASSETS'),
booleanParam(defaultValue: false, description: 'Deletes the contents of the workspace and forces a complete pull.', name: 'CLEAN_WORKSPACE'),
booleanParam(defaultValue: false, description: 'Recreates the volume used for the workspace. The volume will be created out of a snapshot taken from main.', name: 'RECREATE_VOLUME')
booleanParam(defaultValue: false, description: 'Recreates the volume used for the workspace. The volume will be created out of a snapshot taken from main.', name: 'RECREATE_VOLUME'),
choice(defaultValue: defaultBuildSnapshot, name: 'SNAPSHOT', choices: buildSnapshots, description: 'Selects the build snapshot to use. A more diverted snapshot will cause longer build times, but will not cause build failures.')
]
def palSh(cmd, lbl = '', winSlashReplacement = true) {
@@ -258,7 +261,7 @@ def CheckoutRepo(boolean disableSubmodules = false) {
palRm('commitid')
}
def PreBuildCommonSteps(Map pipelineConfig, String repositoryName, String projectName, String pipeline, String branchName, String platform, String buildType, String workspace, boolean mount = true, boolean disableSubmodules = false) {
def PreBuildCommonSteps(Map pipelineConfig, String snapshot, String repositoryName, String projectName, String pipeline, String branchName, String platform, String buildType, String workspace, boolean mount = true, boolean disableSubmodules = false) {
echo 'Starting pre-build common steps...'
if (mount) {
@@ -272,7 +275,7 @@ def PreBuildCommonSteps(Map pipelineConfig, String repositoryName, String projec
palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action delete --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Deleting volume', winSlashReplacement=false)
}
timeout(5) {
palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action mount --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Mounting volume', winSlashReplacement=false)
palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action mount --snapshot ${snapshot} --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Mounting volume', winSlashReplacement=false)
}
if(env.IS_UNIX) {
@@ -391,10 +394,10 @@ def PostBuildCommonSteps(String workspace, boolean mount = true) {
}
}
def CreateSetupStage(Map pipelineConfig, String repositoryName, String projectName, String pipelineName, String branchName, String platformName, String jobName, Map environmentVars) {
def CreateSetupStage(Map pipelineConfig, String snapshot, String repositoryName, String projectName, String pipelineName, String branchName, String platformName, String jobName, Map environmentVars) {
return {
stage('Setup') {
PreBuildCommonSteps(pipelineConfig, repositoryName, projectName, pipelineName, branchName, platformName, jobName, environmentVars['WORKSPACE'], environmentVars['MOUNT_VOLUME'])
PreBuildCommonSteps(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platformName, jobName, environmentVars['WORKSPACE'], environmentVars['MOUNT_VOLUME'])
}
}
}
@@ -457,6 +460,18 @@ try {
branchName = scm.branches[0].name // for non-multibranch pipelines
env.BRANCH_NAME = branchName // so scripts that read this environment have it (e.g. incremental_build_util.py)
}
if(env.CHANGE_TARGET) {
if(buildSnapshots.contains(env.CHANGE_TARGET)) {
snapshot = defaultBuildSnapshot
echo "Snapshot for destination branch \"${env.CHANGE_TARGET}\" found."
} else {
snapshot = env.CHANGE_TARGET
echo "Snapshot for destination branch \"${env.CHANGE_TARGET}\" does not exist, defaulting to snapshot \"${snapshot}\""
}
} else {
snapshot = env.SNAPSHOT
echo "Snapshot \"${snapshot}\" selected."
}
pipelineProperties.add(disableConcurrentBuilds())
echo "Running \"${pipelineName}\" for \"${branchName}\"..."
@@ -514,7 +529,7 @@ try {
withEnv(GetEnvStringList(envVars)) {
def build_job_name = build_job.key
try {
CreateSetupStage(pipelineConfig, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars).call()
CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars).call()
if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages
build_job.value.steps.each { build_step ->
@@ -94,6 +94,7 @@ def error(message):
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--action', dest="action", help="Action (mount|unmount|delete)")
parser.add_argument('-snapshot', '--snapshot', dest="snapshot", help="Build snapshot")
parser.add_argument('-repository_name', '--repository_name', dest="repository_name", help="Repository name")
parser.add_argument('-project', '--project', dest="project", help="Project")
parser.add_argument('-pipe', '--pipeline', dest="pipeline", help="Pipeline")
@@ -177,8 +178,8 @@ def delete_volume(ec2_client, volume_id):
response = ec2_client.delete_volume(VolumeId=volume_id)
print 'Volume {} deleted'.format(volume_id)
def find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, build_type, disk_size):
mount_name = get_mount_name(repository_name, project, pipeline, 'main', platform, build_type) # we take snapshots out of main
def find_snapshot_id(ec2_client, snapshot, repository_name, project, pipeline, platform, build_type, disk_size):
mount_name = get_mount_name(repository_name, project, pipeline, snapshot, platform, build_type) # we take snapshots out of main
response = ec2_client.describe_snapshots(Filters= [{
'Name': 'tag:Name', 'Values': [mount_name]
}])
@@ -194,7 +195,7 @@ def find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, b
snapshot_id = snapshot['SnapshotId']
return snapshot_id
def create_volume(ec2_client, availability_zone, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
def create_volume(ec2_client, availability_zone, snapshot, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
# The actual EBS default calculation for IOps is a floating point number, the closest approxmiation is 4x of the disk size for simplicity
mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type)
pipeline_and_branch = get_pipeline_and_branch(pipeline, branch)
@@ -218,7 +219,7 @@ def create_volume(ec2_client, availability_zone, repository_name, project, pipel
if 'io1' in disk_type.lower():
parameters['Iops'] = (4 * disk_size)
snapshot_id = find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, build_type, disk_size)
snapshot_id = find_snapshot_id(ec2_client, snapshot, repository_name, project, pipeline, platform, build_type, disk_size)
if snapshot_id:
parameters['SnapshotId'] = snapshot_id
created = False
@@ -370,7 +371,7 @@ def attach_ebs_and_create_partition_with_retry(volume, volume_id, ec2_instance_i
mount_volume(created)
attempt += 1
def mount_ebs(repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
def mount_ebs(snapshot, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
session = boto3.session.Session()
region = session.region_name
if region is None:
@@ -399,7 +400,7 @@ def mount_ebs(repository_name, project, pipeline, branch, platform, build_type,
if 'Volumes' in response and not len(response['Volumes']):
print 'Volume for {} doesn\'t exist creating it...'.format(mount_name)
# volume doesn't exist, create it
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
volume_id, created = create_volume(ec2_client, ec2_availability_zone, snapshot, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
else:
volume = response['Volumes'][0]
volume_id = volume['VolumeId']
@@ -407,7 +408,7 @@ def mount_ebs(repository_name, project, pipeline, branch, platform, build_type,
if (volume['Size'] != disk_size or volume['VolumeType'] != disk_type):
print 'Override disk attributes does not match the existing volume, deleting {} and replacing the volume'.format(volume_id)
delete_volume(ec2_client, volume_id)
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
volume_id, created = create_volume(ec2_client, ec2_availability_zone, snapshot, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
if len(volume['Attachments']):
# this is bad we shouldn't be attached, we should have detached at the end of a build
attachment = volume['Attachments'][0]
@@ -437,7 +438,7 @@ def mount_ebs(repository_name, project, pipeline, branch, platform, build_type,
print 'Error: EBS disk size reached to the allowed maximum disk size {}MB, please contact ly-infra@ and ly-build@ to investigate.'.format(MAX_EBS_DISK_SIZE)
exit(1)
print 'Recreating the EBS with disk size {}'.format(new_disk_size)
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline, branch, platform, build_type, new_disk_size, disk_type)
volume_id, created = create_volume(ec2_client, ec2_availability_zone, snapshot, repository_name, project, pipeline, branch, platform, build_type, new_disk_size, disk_type)
volume = ec2_resource.Volume(volume_id)
attach_ebs_and_create_partition_with_retry(volume, volume_id, ec2_instance_id, created)
@@ -492,9 +493,9 @@ def delete_ebs(repository_name, project, pipeline, branch, platform, build_type)
delete_volume(ec2_client, volume_id)
def main(action, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
def main(action, snapshot, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
if action == 'mount':
mount_ebs(repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
mount_ebs(snapshot, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
elif action == 'unmount':
unmount_ebs()
elif action == 'delete':
@@ -502,5 +503,5 @@ def main(action, repository_name, project, pipeline, branch, platform, build_typ
if __name__ == "__main__":
args = parse_args()
ret = main(args.action, args.repository_name, args.project, args.pipeline, args.branch, args.platform, args.build_type, args.disk_size, args.disk_type)
ret = main(args.action, args.snapshot, args.repository_name, args.project, args.pipeline, args.branch, args.platform, args.build_type, args.disk_size, args.disk_type)
sys.exit(ret)