concourse/pipelines/linux-image-build.jsonnet (554 lines of code) (raw):
// Imports.
local arle = import '../templates/arle.libsonnet';
local common = import '../templates/common.libsonnet';
local daisy = import '../templates/daisy.libsonnet';
local gcp_secret_manager = import '../templates/gcp-secret-manager.libsonnet';
local lego = import '../templates/lego.libsonnet';
// Common
local envs = ['testing', 'prod'];
local underscore(input) = std.strReplace(input, '-', '_');
local trim_strings(s, trim) =
if std.length(trim) == 0 then
s
else
trim_strings(std.strReplace(s, trim[0], ''), trim[1:]);
local imgbuildtask = daisy.daisyimagetask {
gcs_url: '((.:gcs-url))',
sbom_destination: '((.:sbom-destination))',
shasum_destination: '((.:shasum-destination))',
};
local prepublishtesttask = common.imagetesttask {
filter: '(shapevalidation)', // TODO enable oslogin
extra_args: [ '-shapevalidation_test_filter=^(([A-Z][0-3])|(N4))' ],
};
local imgbuildjob = {
local tl = self,
image:: error 'must set image in imgbuildjob',
image_prefix:: self.image,
workflow_dir:: error 'must set workflow_dir in imgbuildjob',
workflow:: '%s/%s.wf.json' % [tl.workflow_dir, underscore(tl.image)],
build_task:: imgbuildtask {
workflow: tl.workflow,
vars+: ['google_cloud_repo=stable'],
},
extra_tasks:: [],
daily:: true,
daily_task:: if self.daily then [
{
get: 'daily-time',
trigger: true,
},
] else [],
// Start of job
name: 'build-' + self.image,
plan: tl.daily_task + [
{ get: 'compute-image-tools' },
{ get: 'guest-test-infra' },
{
task: 'generate-timestamp',
file: 'guest-test-infra/concourse/tasks/generate-timestamp.yaml',
},
{
load_var: 'start-timestamp-ms',
file: 'timestamp/timestamp-ms',
},
{
// generate the build id so the tarball and sbom have the same name
task: 'generate-id',
file: 'guest-test-infra/concourse/tasks/generate-id.yaml',
},
{
load_var: 'id',
file: 'generate-id/id',
},
{
task: 'generate-build-id',
file: 'guest-test-infra/concourse/tasks/generate-build-id.yaml',
vars: { prefix: tl.image_prefix, id: '((.:id))' },
},
// This is the 'put trick'. We don't have the real image tarball to write to GCS here, but we want
// Concourse to treat this job as producing it. So we write an empty file now, and overwrite it later in
// the daisy workflow. This also generates the final URL for use in the daisy workflow.
// This is also done for the sbom file.
{
put: tl.image + '-gcs',
params: {
// empty file written to GCS e.g. 'build-id-dir/centos-7-v20210107.tar.gz'
file: 'build-id-dir/%s*' % tl.image_prefix,
},
get_params: {
skip_download: 'true',
},
},
{
load_var: 'gcs-url',
file: '%s-gcs/url' % tl.image,
},
{
task: 'generate-build-id-sbom',
file: 'guest-test-infra/concourse/tasks/generate-build-id-sbom.yaml',
vars: { prefix: tl.image_prefix, id: '((.:id))' },
},
{
put: tl.image + '-sbom',
params: {
// empty file written to GCS e.g. 'build-id-dir/centos-7-v20210107-1681318938.sbom.json'
file: 'build-id-dir-sbom/%s*' % tl.image_prefix,
},
get_params: {
skip_download: 'true',
},
},
{
load_var: 'sbom-destination',
file: '%s-sbom/url' % tl.image,
},
{
task: 'generate-build-id-shasum',
file: 'guest-test-infra/concourse/tasks/generate-build-id-shasum.yaml',
vars: { prefix: tl.image_prefix, id: '((.:id))' },
},
{
put: tl.image + '-shasum',
params: {
// empty file written to GCS e.g. 'build-id-dir/centos-7-v20210107-1681318938.txt'
file: 'build-id-dir-shasum/%s*' % tl.image_prefix,
},
get_params: {
skip_download: 'true',
},
},
{
load_var: 'shasum-destination',
file: '%s-shasum/url' % tl.image,
},
{
task: 'generate-build-date',
file: 'guest-test-infra/concourse/tasks/generate-version.yaml',
},
{
load_var: 'build-date',
file: 'publish-version/version',
},
] + tl.extra_tasks + [
{
task: 'daisy-build-' + tl.image,
config: tl.build_task,
},
],
on_success: {
task: 'success',
config: common.publishresulttask {
pipeline: 'linux-image-build',
job: tl.name,
result_state: 'success',
start_timestamp: '((.:start-timestamp-ms))',
},
},
on_failure: {
task: 'failure',
config: common.publishresulttask {
pipeline: 'linux-image-build',
job: tl.name,
result_state: 'failure',
start_timestamp: '((.:start-timestamp-ms))',
},
},
};
local elimgbuildjob = imgbuildjob {
local tl = self,
workflow_dir: 'enterprise_linux',
sbom_util_secret_name:: 'sbom-util-secret',
isopath:: trim_strings(tl.image, ['-byos', '-sap', '-nvidia-latest', '-nvidia-550']),
// Add tasks to obtain ISO location and sbom util source
// Store those in .:iso-secret and .:sbom-util-secret
extra_tasks: [
{
task: 'get-secret-iso',
config: gcp_secret_manager.getsecrettask { secret_name: tl.isopath },
},
{
load_var: 'iso-secret',
file: 'gcp-secret-manager/' + tl.isopath,
},
{
task: 'get-secret-sbom-util',
config: gcp_secret_manager.getsecrettask { secret_name: tl.sbom_util_secret_name },
},
{
load_var: 'sbom-util-secret',
file: 'gcp-secret-manager/' + tl.sbom_util_secret_name,
},
],
// Add EL and sbom util args to build task.
build_task+: { vars+: ['installer_iso=((.:iso-secret))', 'sbom_util_gcs_root=((.:sbom-util-secret))'] },
};
local debianimgbuildjob = imgbuildjob {
local tl = self,
workflow_dir: 'debian',
sbom_util_secret_name:: 'sbom-util-secret',
// Add tasks to obtain sbom util source
// Store in .:sbom-util-secret
extra_tasks: [
{
task: 'get-secret-sbom-util',
config: gcp_secret_manager.getsecrettask { secret_name: tl.sbom_util_secret_name },
},
{
load_var: 'sbom-util-secret',
file: 'gcp-secret-manager/' + tl.sbom_util_secret_name,
},
],
// Add sbom util args to build task.
build_task+: { vars+: ['sbom_util_gcs_root=((.:sbom-util-secret))'] },
};
local imgpublishjob = {
local tl = self,
env:: error 'must set publish env in imgpublishjob',
workflow:: '%s/%s.publish.json' % [self.workflow_dir, underscore(self.image)],
workflow_dir:: error 'must set workflow_dir in imgpublishjob',
image:: error 'must set image in imgpublishjob',
image_prefix:: self.image,
gcs:: 'gs://%s/%s' % [self.gcs_bucket, self.gcs_dir],
gcs_dir:: error 'must set gcs directory in imgpublishjob',
gcs_bucket:: common.prod_bucket,
// Publish to testing after build
passed:: if tl.env == 'testing' then
'build-' + tl.image
else if tl.env == 'prod' then
'publish-to-testing-' + tl.image,
trigger:: if tl.env == 'testing' then true
else false,
citfilter:: common.default_linux_image_build_cit_filter,
cit_extra_args:: [],
cit_project:: common.default_cit_project,
cit_test_projects:: common.default_cit_test_projects,
// Rather than modifying the default CIT invocation above, it's also possible to specify a extra CIT invocations.
// The images field will be overriden with the image under test.
extra_test_tasks:: [],
runtests:: if tl.env == 'testing' then true
else false,
// Start of job.
name: 'publish-to-%s-%s' % [tl.env, tl.image],
plan: [
{ get: 'guest-test-infra' },
{ get: 'compute-image-tools' },
{
task: 'generate-timestamp',
file: 'guest-test-infra/concourse/tasks/generate-timestamp.yaml',
},
{
load_var: 'start-timestamp-ms',
file: 'timestamp/timestamp-ms',
},
// all the "get" steps must happen before loading them, otherwise concourse seems to
// erase all the other "get" steps
{
get: tl.image + '-gcs',
passed: [tl.passed],
trigger: tl.trigger,
params: { skip_download: 'true' },
},
{
get: tl.image + '-sbom',
passed: [tl.passed],
params: { skip_download: 'true' },
},
{
get: tl.image + '-shasum',
passed: [tl.passed],
params: { skip_download: 'true' },
},
{
load_var: 'sbom-destination',
file: '%s-sbom/url' % tl.image,
},
{
load_var: 'shasum-destination',
file: '%s-shasum/url' % tl.image,
},
{
load_var: 'source-version',
file: tl.image + '-gcs/version',
},
{
task: 'generate-version',
file: 'guest-test-infra/concourse/tasks/generate-version.yaml',
},
{
load_var: 'publish-version',
file: 'publish-version/version',
},
] +
// Run prepublish tests and invoke ARLE in prod
if tl.env == 'prod' then
[
{
task: 'prepublish-test-' + tl.image,
config: prepublishtesttask {
images: 'projects/bct-prod-images/global/images/%s-((.:publish-version))' % tl.image_prefix,
},
attempts: 3,
},
{
task: 'publish-' + tl.image,
config: arle.arlepublishtask {
gcs_image_path: tl.gcs,
gcs_sbom_path: '((.:sbom-destination))',
image_sha256_hash_txt: '((.:shasum-destination))',
source_version: 'v((.:source-version))',
publish_version: '((.:publish-version))',
wf: tl.workflow,
image_name: underscore(tl.image),
},
},
]
// Other releases use gce_image_publish directly.
else
[
{
task: if tl.env == 'testing' then
'publish-' + tl.image
else
'publish-%s-%s' % [tl.env, tl.image],
config: arle.gcepublishtask {
source_gcs_path: tl.gcs,
source_version: 'v((.:source-version))',
publish_version: '((.:publish-version))',
wf: tl.workflow,
environment: if tl.env == 'testing' then 'test' else tl.env,
},
},
]
+
// Run post-publish tests in 'publish-to-testing-' jobs.
if tl.runtests then
[
{
task: 'image-test-' + tl.image,
config: common.imagetesttask {
filter: tl.citfilter,
project: tl.cit_project,
test_projects: tl.cit_test_projects,
images: 'projects/bct-prod-images/global/images/%s-((.:publish-version))' % tl.image_prefix,
extra_args:: tl.cit_extra_args,
},
attempts: 3,
},
] + [
{
task: 'extra-image-test-' + tl.image + '-' + testtask.task,
config: testtask {
images: 'projects/bct-prod-images/global/images/%s-((.:publish-version))' % tl.image_prefix,
},
attempts: 3,
}
for testtask in tl.extra_test_tasks
]
else
[],
on_success: {
task: 'success',
config: common.publishresulttask {
pipeline: 'linux-image-build',
job: tl.name,
result_state: 'success',
start_timestamp: '((.:start-timestamp-ms))',
},
},
on_failure: {
task: 'failure',
config: common.publishresulttask {
pipeline: 'linux-image-build',
job: 'publish-to-%s-%s' % [tl.env, tl.image],
result_state: 'failure',
start_timestamp: '((.:start-timestamp-ms))',
},
},
};
local imggroup = {
local tl = self,
images:: error 'must set images in imggroup',
envs:: envs,
// Start of group.
name: error 'must set name in imggroup',
jobs: [
'build-' + image
for image in tl.images
] + [
'publish-to-%s-%s' % [env, image]
for env in tl.envs
for image in tl.images
],
};
{
local almalinux_images = ['almalinux-8', 'almalinux-9'],
local debian_images = ['debian-11', 'debian-12', 'debian-12-arm64'],
local centos_images = ['centos-stream-9', 'centos-stream-9-arm64'],
local rhel_sap_images = [
'rhel-8-4-sap',
'rhel-8-4-sap-byos',
'rhel-8-6-sap',
'rhel-8-6-sap-byos',
'rhel-8-8-sap',
'rhel-8-8-sap-byos',
'rhel-8-10-sap',
'rhel-8-10-sap-byos',
'rhel-9-0-sap',
'rhel-9-0-sap-byos',
'rhel-9-2-sap',
'rhel-9-2-sap-byos',
'rhel-9-4-sap',
'rhel-9-4-sap-byos',
],
local rhel_images = rhel_sap_images + [
'rhel-8',
'rhel-8-arm64',
'rhel-8-byos',
'rhel-8-byos-arm64',
'rhel-9',
'rhel-9-arm64',
'rhel-9-byos',
'rhel-9-byos-arm64',
],
local rocky_linux_images = [
'rocky-linux-8',
'rocky-linux-8-optimized-gcp',
'rocky-linux-8-optimized-gcp-arm64',
'rocky-linux-9',
'rocky-linux-9-arm64',
'rocky-linux-9-optimized-gcp',
'rocky-linux-9-optimized-gcp-arm64',
],
// Start of output.
resource_types: [
{
name: 'gcs',
type: 'registry-image',
source: { repository: 'frodenas/gcs-resource' },
},
{
name: 'registry-image-forked',
type: 'registry-image',
source: { repository: 'gcr.io/compute-image-tools/registry-image-forked' },
},
],
resources: [
{
name: 'daily-time',
type: 'time',
source: { interval: '24h', start: '8:00 AM', stop: '8:30 AM', location: 'America/Los_Angeles', days: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], initial_version: true },
},
common.gitresource { name: 'compute-image-tools' },
common.gitresource { name: 'guest-test-infra' },
] +
[common.gcsimgresource { image: image, gcs_dir: 'almalinux' } for image in almalinux_images] +
[common.gcssbomresource { image: image, sbom_destination: 'almalinux' } for image in almalinux_images] +
[common.gcsshasumresource { image: image, shasum_destination: 'almalinux' } for image in almalinux_images] +
[common.gcsimgresource { image: image, gcs_dir: 'rocky-linux' } for image in rocky_linux_images] +
[common.gcssbomresource { image: image, sbom_destination: 'rocky-linux' } for image in rocky_linux_images] +
[common.gcsshasumresource { image: image, shasum_destination: 'rocky-linux' } for image in rocky_linux_images] +
[
common.gcsimgresource {
image: image,
regexp: 'debian/%s-v([0-9]+).tar.gz' % common.debian_image_prefixes[self.image],
}
for image in debian_images
] +
[common.gcssbomresource {
image: image,
image_prefix: common.debian_image_prefixes[image],
sbom_destination: 'debian',
} for image in debian_images] +
[common.gcsshasumresource {
image: image,
image_prefix: common.debian_image_prefixes[image],
shasum_destination: 'debian',
} for image in debian_images] +
[common.gcsimgresource { image: image, gcs_dir: 'centos' } for image in centos_images] +
[common.gcssbomresource { image: image, sbom_destination: 'centos' } for image in centos_images] +
[common.gcsshasumresource { image: image, shasum_destination: 'centos' } for image in centos_images] +
[common.gcsimgresource { image: image, gcs_dir: 'rhel' } for image in rhel_images] +
[common.gcssbomresource { image: image, sbom_destination: 'rhel' } for image in rhel_images] +
[common.gcsshasumresource { image: image, shasum_destination: 'rhel' } for image in rhel_images],
jobs: [
// Debian build jobs
debianimgbuildjob {
image: image,
image_prefix: common.debian_image_prefixes[image],
}
for image in debian_images
] +
[
// EL build jobs
elimgbuildjob { image: image }
for image in rhel_images + centos_images + almalinux_images + rocky_linux_images
] +
[
// Debian publish jobs
imgpublishjob {
image: image,
env: env,
gcs_dir: 'debian',
workflow_dir: 'debian',
// Debian tarballs and images use a longer name, but jobs use the shorter name.
image_prefix: common.debian_image_prefixes[image],
}
for env in envs
for image in debian_images
] +
[
// RHEL publish jobs
imgpublishjob {
image: image,
env: env,
gcs_dir: 'rhel',
workflow_dir: 'enterprise_linux',
}
for env in envs
for image in rhel_images
] +
[
// CentOS publish jobs
imgpublishjob {
image: image,
env: env,
gcs_dir: 'centos',
workflow_dir: 'enterprise_linux',
}
for env in envs
for image in centos_images
] +
[
// AlmaLinux publish jobs
imgpublishjob {
image: image,
env: env,
gcs_dir: 'almalinux',
workflow_dir: 'enterprise_linux',
}
for env in envs
for image in almalinux_images
] +
[
// Rocky Linux publish jobs
imgpublishjob {
image: image,
env: env,
gcs_dir: 'rocky-linux',
workflow_dir: 'enterprise_linux',
}
for env in envs
for image in rocky_linux_images
],
groups: [
imggroup { name: 'debian', images: debian_images },
imggroup {
name: 'rhel',
images: rhel_images,
},
imggroup { name: 'centos', images: centos_images },
imggroup { name: 'almalinux', images: almalinux_images },
imggroup { name: 'rocky-linux', images: rocky_linux_images},
],
}