Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ART-10563 Build bootc image as part of build-microshift-bootc job #1054

Merged
merged 7 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doozer/doozerlib/backend/konflux_image_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ async def build(self, metadata: ImageMetadata):

status = pipelinerun.status.conditions[0].status
outcome = KonfluxBuildOutcome.SUCCESS if status == "True" else KonfluxBuildOutcome.FAILURE
await self.update_konflux_db(metadata, build_repo, pipelinerun, outcome, building_arches)
if self._config.dry_run:
self._logger.info("[%s] Dry run: Would have inserted build record in Konflux DB",
metadata.distgit_key)
else:
await self.update_konflux_db(metadata, build_repo, pipelinerun, outcome, building_arches)

if status != "True":
error = KonfluxImageBuildError(f"Konflux image build for {metadata.distgit_key} failed",
Expand Down
13 changes: 12 additions & 1 deletion doozer/doozerlib/cli/config_plashet.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,10 @@ def from_tags(config: SimpleNamespace, brew_tag: Tuple[Tuple[str, str], ...], em
--brew-tag <tag> <product_version> example: --brew-tag rhaos-4.5-rhel-8-candidate OSE-4.5-RHEL-8 --brew-tag .. ..
"""
runtime: Runtime = config.runtime
runtime.initialize(mode="rpms", clone_source=False, clone_distgits=False, prevent_cloning=True)
runtime.initialize(mode="rpms", clone_source=False, clone_distgits=False, prevent_cloning=True,
# we load disabled to include microshift rpm which still could be pinned for assembly
# and eligible for plashet inclusion
disabled=True)
assembly = runtime.assembly
koji_proxy = runtime.build_retrying_koji_client()
koji_proxy.gssapi_login()
Expand Down Expand Up @@ -539,6 +542,14 @@ def from_tags(config: SimpleNamespace, brew_tag: Tuple[Tuple[str, str], ...], em
# If assemblies are disabled, the true latest rpm builds from the tag will be collected; Otherwise we will only collect the rpm builds specific to that assembly.
tagged_builds = builder.from_tag(tag, inherit, assembly, event)
component_builds.update(tagged_builds)

# microshift rpm is special as it builds in its own stream-type assembly named "microshift"
# And it builds for named assemblies after we promote
# We fetch the appropriate build from microshift assembly based on the given assembly brew event
# if it is pinned in the assembly, it will be overridden by the pinned build
microshift_build = builder.from_tag(tag, inherit, 'microshift', event, only=['microshift'])
component_builds.update(microshift_build)

signable_components |= tagged_builds.keys() # components from our tag are always signable

# rpms pinned by "is" and group dependencies should take precedence over every build from the tag
Expand Down
8 changes: 6 additions & 2 deletions doozer/doozerlib/plashet.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ def _cache_build(self, build: Dict):
if "epoch" in build:
self._build_cache[to_nvre(build)] = build

def from_tag(self, tag: str, inherit: bool, assembly: Optional[str], event: Optional[int] = None) -> Dict[str, Dict]:
def from_tag(self, tag: str, inherit: bool, assembly: Optional[str], event: Optional[int] = None,
only: Optional[List[str]] = None) -> Dict[str, Dict]:
""" Returns RPM builds from the specified brew tag
:param tag: Brew tag name
:param inherit: Descend into brew tag inheritance
:param assembly: Assembly name to query. If None, this method will return true latest builds.
:param event: Brew event ID
:param only: List of component names to include. If None, all components will be included.
:return: a dict; keys are component names, values are Brew build dicts
"""
if not assembly:
Expand All @@ -62,9 +64,11 @@ def from_tag(self, tag: str, inherit: bool, assembly: Optional[str], event: Opti
tagged_builds = self._koji_api.listTagged(tag, latest=False, inherit=inherit, event=event, type='rpm')
builds = find_latest_builds(tagged_builds, assembly)
component_builds = {build["name"]: build for build in builds}
self._logger.info("Found %s RPM builds.", len(component_builds))
for build in component_builds.values(): # Save to cache
self._cache_build(build)
if only:
component_builds = {name: build for name, build in component_builds.items() if name in only}
self._logger.info("Found %s qualified RPM builds.", len(component_builds))
return component_builds

def from_pinned_by_is(self, el_version: int, assembly: str, releases_config: Model, rpm_map: Dict[str, RPMMetadata]) -> Dict[str, Dict]:
Expand Down
6 changes: 1 addition & 5 deletions doozer/doozerlib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
import re
import urllib.parse
from collections import deque
from contextlib import contextmanager
from datetime import datetime
from inspect import getframeinfo, stack
from itertools import chain
from os.path import abspath
from pathlib import Path
from sys import getsizeof, stderr
from typing import Dict, List, Optional, Tuple, Union
from typing import Dict, List, Optional, Union

import semver
import yaml
Expand All @@ -31,8 +29,6 @@
except ImportError:
pass

from doozerlib import constants
from doozerlib.exceptions import DoozerFatalError
from functools import lru_cache

DICT_EMPTY = object()
Expand Down
2 changes: 1 addition & 1 deletion elliott/elliottlib/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def filter_disabled(n, d):
raise ElliottFatalError('The following images or rpms were either missing or filtered out: {}'.format(', '.join(missed_include)))

strict_mode = True
if not self.assembly or self.assembly in ['stream', 'test']:
if not self.assembly or self.assembly in ['stream', 'test', 'microshift']:
strict_mode = False
self.assembly_type = assembly_type(self.get_releases_config(), self.assembly)
self.assembly_basis_event = assembly_basis_event(self.get_releases_config(), self.assembly, strict=strict_mode)
Expand Down
2 changes: 1 addition & 1 deletion pyartcd/pyartcd/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from pyartcd.cli import cli
from pyartcd.pipelines import (
build_microshift, check_bugs, gen_assembly, ocp4_konflux, prepare_release, promote, rebuild,
build_microshift_bootc, build_microshift, check_bugs, gen_assembly, ocp4_konflux, prepare_release, promote, rebuild,
review_cvp, tarball_sources, build_sync, build_rhcos, ocp4_scan, ocp4_scan_konflux, images_health,
operator_sdk_sync, olm_bundle, ocp4, scan_for_kernel_bugs, tag_rpms, advisory_drop, cleanup_locks, brew_scan_osh,
sigstore_sign, update_golang, rebuild_golang_rpms, scan_fips, quay_doomsday_backup
Expand Down
18 changes: 16 additions & 2 deletions pyartcd/pyartcd/jenkins.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
class Jobs(Enum):
BUILD_SYNC = 'aos-cd-builds/build%2Fbuild-sync'
BUILD_MICROSHIFT = 'aos-cd-builds/build%2Fbuild-microshift'
BUILD_MICROSHIFT_BOOTC = 'aos-cd-builds/build%2Fbuild-microshift-bootc'
OCP4 = 'aos-cd-builds/build%2Focp4'
OCP4_KONFLUX = 'aos-cd-builds/build%2Focp4-konflux'
OCP4_SCAN = 'aos-cd-builds/build%2Focp4_scan'
Expand Down Expand Up @@ -386,12 +387,25 @@ def start_sync_for_ci(version: str, **kwargs):
)


def start_microshift_sync(version: str, assembly: str, **kwargs):
def start_microshift_sync(version: str, assembly: str, dry_run: bool, **kwargs):
return start_build(
job=Jobs.MICROSHIFT_SYNC,
params={
'BUILD_VERSION': version,
'ASSEMBLY': assembly
'ASSEMBLY': assembly,
'DRY_RUN': dry_run
},
**kwargs
)


def start_build_microshift_bootc(version: str, assembly: str, dry_run: bool, **kwargs):
return start_build(
job=Jobs.BUILD_MICROSHIFT_BOOTC,
params={
'BUILD_VERSION': version,
'ASSEMBLY': assembly,
'DRY_RUN': dry_run
},
**kwargs
)
Expand Down
119 changes: 55 additions & 64 deletions pyartcd/pyartcd/pipelines/build_microshift.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import asyncio
import json
import logging
import os
import re
import traceback
import click
from collections import namedtuple
from datetime import datetime, timezone
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Dict, Iterable, List, Optional, Tuple, cast

import click
from typing import Dict, Iterable, List, Optional, Tuple

from artcommonlib.arch_util import brew_arch_for_go_arch
from artcommonlib.assembly import AssemblyTypes
from artcommonlib.util import get_ocp_version_from_group, isolate_major_minor_in_group
from artcommonlib.release_util import isolate_assembly_in_release
from artcommonlib.util import get_ocp_version_from_group
from artcommonlib import exectools
from doozerlib.util import isolate_nightly_name_components
from ghapi.all import GhApi
Expand All @@ -28,7 +23,12 @@
from pyartcd.git import GitRepository
from pyartcd.record import parse_record_log
from pyartcd.runtime import Runtime
from pyartcd.util import (get_assembly_type, isolate_el_version_in_release, load_group_config, load_releases_config)
from pyartcd.util import (get_assembly_type,
isolate_el_version_in_release,
load_group_config,
load_releases_config,
default_release_suffix,
get_microshift_builds)

yaml = YAML(typ="rt")
yaml.default_flow_style = False
Expand Down Expand Up @@ -89,9 +89,12 @@ async def run(self):
await self._rebase_and_build_for_stream()
else:
await self._rebase_and_build_for_named_assembly()
await self._trigger_microshift_sync()
await self._trigger_build_microshift_bootc()

# Check if microshift advisory is defined in assembly
if 'microshift' not in advisories:
self._logger.warning(f"Skipping advisory prep since microshift advisory is not defined in assembly {self.assembly}")
self._logger.info(f"Skipping advisory prep since microshift advisory is not defined in assembly {self.assembly}")
else:
await self._prepare_advisory(advisories['microshift'])

Expand All @@ -100,7 +103,7 @@ async def _rebase_and_build_for_stream(self):
if self.assembly_type != AssemblyTypes.STREAM:
raise ValueError(f"Cannot process assembly type {self.assembly_type.value}")

major, minor = isolate_major_minor_in_group(self.group)
major, minor = self._ocp_version
# rebase against nightlies
# rpm version-release will be like `4.12.0~test-202201010000.p?`
if self.no_rebase:
Expand Down Expand Up @@ -139,8 +142,6 @@ async def _rebase_and_build_for_named_assembly(self):
if self.assembly_type == AssemblyTypes.STREAM:
raise ValueError(f"Cannot process assembly type {self.assembly_type.value}")

major, minor = isolate_major_minor_in_group(self.group)

# For named assemblies, check if builds are pinned or already exist
nvrs = []
pinned_nvrs = dict()
Expand All @@ -161,7 +162,7 @@ async def _rebase_and_build_for_named_assembly(self):
await self.slack_client.say_in_thread(message)
nvrs = list(pinned_nvrs.values())
else:
nvrs = await self._find_builds()
nvrs = await get_microshift_builds(self.group, self.assembly, env=self._elliott_env_vars)

if nvrs:
self._logger.info("Builds already exist: %s", nvrs)
Expand All @@ -180,23 +181,43 @@ async def _rebase_and_build_for_named_assembly(self):
message = f"PR to pin microshift build to the {self.assembly} assembly has been merged: {pr.html_url}"
await self.slack_client.say_in_thread(message)

if self.assembly_type in [AssemblyTypes.PREVIEW, AssemblyTypes.CANDIDATE]:
version = f'{major}.{minor}'
try:
if self.runtime.dry_run:
self._logger.info("[DRY RUN] Would have triggered microshift_sync job for version %s and assembly %s",
version, self.assembly)
else:
jenkins.start_microshift_sync(version=version, assembly=self.assembly)

message = f"microshift_sync for version {version} and assembly {self.assembly} has been triggered\n" \
f"This will publish the microshift build to mirror"
await self.slack_client.say_in_thread(message)
except Exception as err:
self._logger.warning("Failed to trigger microshift_sync job: %s", err)
message = f"@release-artists Please start <{constants.JENKINS_UI_URL}" \
"/job/aos-cd-builds/job/build%252Fmicroshift_sync|microshift sync> manually."
await self.slack_client.say_in_thread(message)
async def _trigger_microshift_sync(self):
if self.assembly_type not in [AssemblyTypes.PREVIEW, AssemblyTypes.CANDIDATE]:
return

major, minor = self._ocp_version
version = f'{major}.{minor}'
try:
jenkins.start_microshift_sync(version=version, assembly=self.assembly, dry_run=self.runtime.dry_run)
message = f"microshift_sync for version {version} and assembly {self.assembly} has been triggered\n" \
f"This will publish the microshift build to mirror"
await self.slack_client.say_in_thread(message)
except Exception as err:
self._logger.error("Failed to trigger microshift_sync job: %s", err)
message = f"@release-artists Please start <{constants.JENKINS_UI_URL}" \
"/job/aos-cd-builds/job/build%252Fmicroshift_sync|microshift sync> manually."
await self.slack_client.say_in_thread(message)

async def _trigger_build_microshift_bootc(self):
if self.assembly_type not in [AssemblyTypes.STANDARD, AssemblyTypes.PREVIEW, AssemblyTypes.CANDIDATE]:
return

if self._ocp_version < (4, 18):
self._logger.info("Skipping build-microshift-bootc job for OCP version < 4.18")
return

major, minor = self._ocp_version
version = f'{major}.{minor}'
try:
jenkins.start_build_microshift_bootc(version=version, assembly=self.assembly, dry_run=self.runtime.dry_run)
message = f"build_microshift_bootc for version {version} and assembly {self.assembly} has been triggered\n" \
f"This will build microshift-bootc and publish it's pullspec to mirror"
await self.slack_client.say_in_thread(message)
except Exception as err:
self._logger.error("Failed to trigger build-microshift-bootc job: %s", err)
message = f"@release-artists Please start <{constants.JENKINS_UI_URL}" \
"/job/aos-cd-builds/job/build%252Fbuild-microshift-bootc|build-microshift-bootc> manually."
await self.slack_client.say_in_thread(message)

async def _prepare_advisory(self, microshift_advisory_id):
await self.slack_client.say_in_thread(f"Start preparing microshift advisory for assembly {self.assembly}..")
Expand Down Expand Up @@ -311,49 +332,19 @@ async def parse_release_payloads(payloads: Iterable[str]):
return result

@staticmethod
def generate_microshift_version_release(ocp_version: str, timestamp: Optional[str] = None):
""" Generate version and release strings for microshift rpm.
def generate_microshift_version_release(ocp_version: str):
""" Generate version and release strings for microshift builds
Example version-releases:
- 4.12.42-202210011234
- 4.13.0~rc.4-202210011234
"""
if not timestamp:
timestamp = datetime.now(tz=timezone.utc).strftime("%Y%m%d%H%M")
release_version = VersionInfo.parse(ocp_version)
version = f"{release_version.major}.{release_version.minor}.{release_version.patch}"
if release_version.prerelease is not None:
version += f"~{release_version.prerelease.replace('-', '_')}"
release = f"{timestamp}.p?"
release = default_release_suffix()
return version, release

async def _find_builds(self) -> List[str]:
""" Find microshift builds in Brew
:param release: release field for rebase
:return: NVRs
"""
cmd = [
"elliott",
"--group", self.group,
"--assembly", self.assembly,
"-r", "microshift",
"find-builds",
"-k", "rpm",
"--member-only",
]
with TemporaryDirectory() as tmpdir:
path = f"{tmpdir}/out.json"
cmd.append(f"--json={path}")
await exectools.cmd_assert_async(cmd, env=self._elliott_env_vars)
with open(path) as f:
result = json.load(f)

nvrs = cast(List[str], result["builds"])

# microshift builds are special in that they build for each assembly after payload is promoted
# and they include the assembly name in its build name
# so make sure found nvrs are related to assembly
return [n for n in nvrs if isolate_assembly_in_release(n) == self.assembly]

async def _rebase_and_build_rpm(self, version, release: str, custom_payloads: Optional[Dict[str, str]]) -> List[str]:
""" Rebase and build RPM
:param release: release field for rebase
Expand Down
Loading