Skip to content
Open
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
13 changes: 13 additions & 0 deletions artcommon/artcommonlib/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,19 @@
"extras": "ocp-art-advisory",
"microshift-bootc": "ocp-art-advisory",
}
LP_RPA_KINDS = {
"oadp": "oadp-advisory",
"mta": "mta-advisory",
"rhmtc": "mtc-advisory",
"quay": "quay-advisory",
"multicluster-engine": "mce-advisory",
"rhacm2": "acm-advisory",
"cert-manager": "cm-advisory",
"external-secrets-operator": "eso-advisory",
"zero-trust-workload-identity-manager": "zt-advisory",
"openshift-logging": "logging-advisory",
"logging": "logging-advisory",
}
OCP_RPA_ENVS = ["stage", "prod"]

COREOS_RHEL10_STREAMS = [
Expand Down
44 changes: 32 additions & 12 deletions elliott/elliottlib/cli/konflux_release_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import aiohttp
import click
from artcommonlib import logutil
from artcommonlib.constants import OCP_RPA_BASE_URL, OCP_RPA_ENVS, OCP_RPA_KINDS
from artcommonlib.constants import LP_RPA_KINDS, OCP_RPA_BASE_URL, OCP_RPA_ENVS, OCP_RPA_KINDS
from artcommonlib.util import (
get_utc_now_formatted_str,
new_roundtrip_yaml_handler,
Expand Down Expand Up @@ -88,27 +88,47 @@ async def _validate_snapshot_against_single_rpa(kind: str, rpa_name: str, snapsh


async def validate_snapshot_against_rpa(group: str, env: str, kind: str, snapshot_components: List[str]) -> None:
match = re.fullmatch(r"openshift-(\d+)\.(\d+)", group)
if group.startswith("openshift-") and not match:
raise ValueError(f"Unrecognized openshift group format, refusing to skip RPA validation: {group!r}")
if not match:
return
major, minor = match.group(1), match.group(2)

# FBC allowedPackages use OLM package names which don't match Konflux component names
if kind == "fbc":
LOGGER.info("Skipping RPA validation for FBC releases (different naming scheme)")
return

if kind not in OCP_RPA_KINDS:
raise ValueError(f"Unsupported release kind for RPA validation: {kind!r}. Supported: {sorted(OCP_RPA_KINDS)}")

if env not in OCP_RPA_ENVS:
raise ValueError(f"Unsupported release env for RPA validation: {env!r}. Supported: {OCP_RPA_ENVS}")

# Detect OCP vs LP product: check OCP format first, then LP products from allow-list
ocp_match = re.fullmatch(r"openshift-(\d+)\.(\d+)", group)
if ocp_match:
# OCP path: openshift-X.Y
major, minor = ocp_match.group(1), ocp_match.group(2)
if kind not in OCP_RPA_KINDS:
raise ValueError(
f"Unsupported release kind for RPA validation: {kind!r}. Supported: {sorted(OCP_RPA_KINDS)}"
)
rpa_base = OCP_RPA_KINDS[kind]
elif group.startswith("openshift-"):
# Unrecognized openshift format
raise ValueError(f"Unrecognized openshift group format, refusing to skip RPA validation: {group!r}")
else:
# LP path: {product}-{major}.{minor}, check against allow-list
lp_match = re.match(r"^([a-z0-9-]+)-(\d+)\.(\d+)$", group)
if not lp_match:
LOGGER.info(f"Skipping RPA validation for unrecognized group format: {group!r}")
return

product_name = lp_match.group(1)
if product_name not in LP_RPA_KINDS:
LOGGER.info(f"Skipping RPA validation for unsupported LP product: {product_name!r}")
return

major, minor = lp_match.group(2), lp_match.group(3)
if kind != "image":
raise ValueError(f"Unsupported release kind for LP RPA validation: {kind!r}. LP supports 'image' only")
rpa_base = LP_RPA_KINDS[product_name]

envs_to_check = [env] + [e for e in OCP_RPA_ENVS if e != env]
for check_env in envs_to_check:
rpa_name = f"{OCP_RPA_KINDS[kind]}-{check_env}-{major}-{minor}"
rpa_name = f"{rpa_base}-{check_env}-{major}-{minor}"
await _validate_snapshot_against_single_rpa(kind, rpa_name, snapshot_components)


Expand Down
38 changes: 36 additions & 2 deletions elliott/tests/test_konflux_release_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -788,8 +788,42 @@ async def test_validate_rpa_checks_both_envs_stage_first(self, mock_fetch_rpa):
self.assertEqual(calls, ["ocp-art-advisory-stage-4-18", "ocp-art-advisory-prod-4-18"])

@patch("elliottlib.cli.konflux_release_cli.fetch_rpa", new_callable=AsyncMock)
async def test_validate_rpa_skipped_for_non_openshift(self, mock_fetch_rpa):
await validate_snapshot_against_rpa("oadp-1.5", "prod", "image", ["comp1"])
async def test_validate_rpa_lp_oadp_success(self, mock_fetch_rpa):
rpa_data = {"spec": {"data": {"mapping": {"components": [{"name": "oadp-1-5-oadp-operator"}]}}}}
mock_fetch_rpa.return_value = rpa_data

await validate_snapshot_against_rpa("oadp-1.5", "prod", "image", ["oadp-1-5-oadp-operator"])
self.assertEqual(mock_fetch_rpa.await_count, 2)
mock_fetch_rpa.assert_any_await("oadp-advisory-prod-1-5")
mock_fetch_rpa.assert_any_await("oadp-advisory-stage-1-5")

@patch("elliottlib.cli.konflux_release_cli.fetch_rpa", new_callable=AsyncMock)
async def test_validate_rpa_lp_mta_success(self, mock_fetch_rpa):
rpa_data = {"spec": {"data": {"mapping": {"components": [{"name": "mta-8-2-mta-operator"}]}}}}
mock_fetch_rpa.return_value = rpa_data

await validate_snapshot_against_rpa("mta-8.2", "stage", "image", ["mta-8-2-mta-operator"])
self.assertEqual(mock_fetch_rpa.await_count, 2)
calls = [c.args[0] for c in mock_fetch_rpa.await_args_list]
self.assertEqual(calls, ["mta-advisory-stage-8-2", "mta-advisory-prod-8-2"])

@patch("elliottlib.cli.konflux_release_cli.fetch_rpa", new_callable=AsyncMock)
async def test_validate_rpa_lp_logging_success(self, mock_fetch_rpa):
rpa_data = {"spec": {"data": {"mapping": {"components": [{"name": "logging-6-6-logging-operator"}]}}}}
mock_fetch_rpa.return_value = rpa_data

await validate_snapshot_against_rpa("logging-6.6", "prod", "image", ["logging-6-6-logging-operator"])
self.assertEqual(mock_fetch_rpa.await_count, 2)
mock_fetch_rpa.assert_any_await("logging-advisory-prod-6-6")

@patch("elliottlib.cli.konflux_release_cli.fetch_rpa", new_callable=AsyncMock)
async def test_validate_rpa_lp_unsupported_product_skipped(self, mock_fetch_rpa):
await validate_snapshot_against_rpa("unknown-product-1.0", "prod", "image", ["comp1"])
mock_fetch_rpa.assert_not_called()

@patch("elliottlib.cli.konflux_release_cli.fetch_rpa", new_callable=AsyncMock)
async def test_validate_rpa_lp_invalid_format_skipped(self, mock_fetch_rpa):
await validate_snapshot_against_rpa("invalid_group_format", "prod", "image", ["comp1"])
mock_fetch_rpa.assert_not_called()

@patch("elliottlib.cli.konflux_release_cli.fetch_rpa", new_callable=AsyncMock)
Expand Down
Loading