Skip to content
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
14 changes: 13 additions & 1 deletion solution/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,18 @@ function validate_registry()
{
echo "Validating ISO registry completeness against zenkoversion.yaml..."
local missing=0
local image_refs

# `..` walks sequences too (capabilities.lifecycleRules, ...), and indexing one with
# a string key is a hard error in yq: only mappings can hold an image reference.
if ! image_refs=$(yq eval '.. | select(tag == "!!map") | select(has("image") and has("tag")) | .image + ":" + .tag' ${ISO_ROOT}/zenkoversion.yaml) ; then
echo "::error::Failed to list the images referenced in zenkoversion.yaml. Aborting."
exit 1
fi
if [ -z "${image_refs}" ] ; then
echo "::error::No image referenced in zenkoversion.yaml, it is most likely malformed. Aborting."
exit 1
fi

while IFS= read -r image_ref ; do
local image="${image_ref%:*}"
Expand All @@ -283,7 +295,7 @@ function validate_registry()
echo "::error::Missing image in ISO registry: ${image}:${tag}"
missing=$((missing + 1))
fi
done < <(yq eval '.. | select(.image and .tag) | .image + ":" + .tag' ${ISO_ROOT}/zenkoversion.yaml | sort -u)
done < <(sort -u <<< "${image_refs}")

if [ ${missing} -gt 0 ] ; then
echo "::error::${missing} image(s) referenced in zenkoversion.yaml are missing from the ISO registry. Aborting."
Expand Down
143 changes: 143 additions & 0 deletions tests/scripts/test_validate_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import re
import shlex
import shutil
import subprocess
import textwrap
from pathlib import Path

import pytest

BUILD_SH = Path(__file__).resolve().parents[2] / "solution" / "build.sh"

# Mirrors the shape of solution/zenkoversion.yaml: images live in mappings at
# several depths, next to sequences that must not be mistaken for image entries.
ZENKOVERSION = """
---
apiVersion: zenko.io/v1alpha1
kind: ZenkoVersion
metadata:
name: 2.14.0
spec:
dashboards:
backbeat:
image: backbeat-dashboard
tag: 8.6.55
policies:
vault:
image: vault-policy
tag: 8.11.9
versions:
cloudserver:
image: cloudserver
tag: 8.8.44
kafka:
cluster:
image: kafka
tag: 3.9.0-8f2a1c3
featureFlags:
lifecycleOptimization: true
capabilities:
lifecycleRules:
- Expiration
- Transition
locationTypes:
- location-azure-v1
- location-aws-s3-v1
"""

ALL_IMAGES = [
"backbeat-dashboard:8.6.55",
"vault-policy:8.11.9",
"cloudserver:8.8.44",
"kafka:3.9.0-8f2a1c3",
]


@pytest.fixture(scope="module", autouse=True)
def require_yq():
assert shutil.which("yq"), "yq is required to run the build.sh registry tests"


@pytest.fixture(scope="module")
def validate_registry_source():
script = BUILD_SH.read_text(encoding="utf-8")
match = re.search(
r"^function validate_registry\(\)\n\{\n.*?^\}$",
script,
re.MULTILINE | re.DOTALL,
)
assert match, f"validate_registry() not found in {BUILD_SH}"
return match.group(0)


def run_validate_registry(tmp_path, source, zenkoversion=ZENKOVERSION, images=ALL_IMAGES):
"""Run build.sh's validate_registry() against a throwaway ISO root."""
iso_root = tmp_path / "root"
images_root = iso_root / "images"
images_root.mkdir(parents=True)
(iso_root / "zenkoversion.yaml").write_text(
textwrap.dedent(zenkoversion).lstrip(), encoding="utf-8"
)

for image_ref in images:
image, _, tag = image_ref.rpartition(":")
manifest_dir = images_root / image / tag
manifest_dir.mkdir(parents=True, exist_ok=True)
(manifest_dir / "manifest.json").write_text("{}", encoding="utf-8")

harness = f"""
set -e
set -u
ISO_ROOT={shlex.quote(str(iso_root))}
IMAGES_ROOT={shlex.quote(str(images_root))}

{source}

validate_registry
"""
return subprocess.run(
["bash", "-c", textwrap.dedent(harness)],
capture_output=True,
text=True,
)


def test_passes_when_every_image_is_in_the_registry(tmp_path, validate_registry_source):
result = run_validate_registry(tmp_path, validate_registry_source)

assert result.returncode == 0, result.stderr
assert "Registry validation passed" in result.stdout


@pytest.mark.parametrize("missing", ALL_IMAGES)
def test_aborts_when_an_image_is_missing(tmp_path, validate_registry_source, missing):
result = run_validate_registry(
tmp_path,
validate_registry_source,
images=[image for image in ALL_IMAGES if image != missing],
)

assert result.returncode != 0
assert f"Missing image in ISO registry: {missing}" in result.stdout
assert "Registry validation passed" not in result.stdout


def test_aborts_when_the_image_list_cannot_be_read(tmp_path, validate_registry_source):
result = run_validate_registry(
tmp_path, validate_registry_source, zenkoversion="spec: [unterminated\n"
)

assert result.returncode != 0
assert "Registry validation passed" not in result.stdout


def test_aborts_when_no_image_is_referenced(tmp_path, validate_registry_source):
result = run_validate_registry(
tmp_path,
validate_registry_source,
zenkoversion="spec:\n capabilities:\n locationTypes:\n - location-azure-v1\n",
images=[],
)

assert result.returncode != 0
assert "Registry validation passed" not in result.stdout
Loading