Skip to content

Commit 4ddd93a

Browse files
authored
[ISV-4497] validate catalog format (#639)
* [ISV-4497] validate catalog format Signed-off-by: Tomáš Man <[email protected]> --------- Signed-off-by: Tomáš Man <[email protected]>
1 parent 62c52da commit 4ddd93a

File tree

5 files changed

+202
-1
lines changed

5 files changed

+202
-1
lines changed

ansible/roles/operator-pipeline/templates/openshift/pipelines/operator-hosted-pipeline.yml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,14 +383,38 @@ spec:
383383
workspace: repository
384384
subPath: src
385385

386+
- name: validate-catalog-format
387+
taskRef:
388+
name: validate-catalog-format
389+
kind: Task
390+
runAfter:
391+
# This is just a temporary location until we have a proper
392+
# FCB branch based on config
393+
- read-config
394+
when:
395+
- input: "$(tasks.detect-changes.results.affected_catalogs)"
396+
operator: notin
397+
values: [""]
398+
params:
399+
- name: pipeline_image
400+
value: "$(params.pipeline_image)"
401+
- name: affected_catalogs
402+
value: "$(tasks.detect-changes.results.affected_catalogs)"
403+
- name: operator_path
404+
value: "$(tasks.detect-changes.results.operator_path)"
405+
workspaces:
406+
- name: source
407+
workspace: repository
408+
subPath: src
409+
386410
- name: build-fragment-images
387411
taskRef:
388412
name: build-fragment-images
389413
kind: Task
390414
runAfter:
391415
# This is just a temporary location until we have a proper
392416
# FCB branch based on config
393-
- read-config
417+
- validate-catalog-format
394418
when:
395419
- input: "$(tasks.detect-changes.results.affected_catalogs)"
396420
operator: notin
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
apiVersion: tekton.dev/v1
3+
kind: Task
4+
metadata:
5+
name: validate-catalog-format
6+
spec:
7+
params:
8+
- name: pipeline_image
9+
10+
- name: affected_catalogs
11+
description: Comma separated list of updated catalogs.
12+
13+
- name: operator_path
14+
description: Path to the operator.
15+
16+
workspaces:
17+
- name: source
18+
steps:
19+
- name: validate-catalog-format
20+
image: "$(params.pipeline_image)"
21+
workingDir: $(workspaces.source.path)
22+
script: |
23+
#! /usr/bin/env bash
24+
set -xe
25+
26+
validate-catalog-format \
27+
--repo-path "$(workspaces.source.path)" \
28+
--catalog-names "$(params.affected_catalogs)" \
29+
--verbose
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""Validate catalog format for all given catalogs."""
2+
3+
import argparse
4+
import logging
5+
from subprocess import CalledProcessError
6+
import sys
7+
8+
from operatorcert.logger import setup_logger
9+
from operatorcert.utils import SplitArgs, run_command
10+
11+
LOGGER = logging.getLogger("operator-cert")
12+
13+
14+
def setup_argparser() -> argparse.ArgumentParser:
15+
"""
16+
Setup argument parser
17+
18+
Returns:
19+
argparse.ArgumentParser: Argument parser
20+
"""
21+
parser = argparse.ArgumentParser(
22+
description="Validate catalog format for all given catalogs."
23+
)
24+
parser.add_argument(
25+
"--repo-path",
26+
default=".",
27+
help="Path to the root of the local clone of the repo",
28+
required=True,
29+
)
30+
parser.add_argument(
31+
"--catalog-names",
32+
default=[],
33+
action=SplitArgs,
34+
help="List of catalog names to validate",
35+
)
36+
parser.add_argument("--verbose", action="store_true", help="Verbose output")
37+
38+
return parser
39+
40+
41+
def validate_catalog_format(catalog_path: str, catalog_name: str) -> None:
42+
"""
43+
Run `opm validate` for a given catalog.
44+
"""
45+
cmd = [
46+
"opm",
47+
"validate",
48+
f"{catalog_path}/{catalog_name}",
49+
]
50+
LOGGER.info("Validating catalog %s", catalog_name)
51+
run_command(cmd)
52+
53+
54+
def main() -> None:
55+
"""
56+
Main function of the script
57+
"""
58+
# Args
59+
parser = setup_argparser()
60+
args = parser.parse_args()
61+
62+
# Logging
63+
log_level = "INFO"
64+
if args.verbose:
65+
log_level = "DEBUG"
66+
setup_logger(level=log_level)
67+
68+
invalid_catalogs = []
69+
70+
# For each catalog validate its format
71+
for catalog_name in args.catalog_names:
72+
catalog_path = f"{args.repo_path}/catalogs"
73+
try:
74+
validate_catalog_format(catalog_path, catalog_name)
75+
except CalledProcessError:
76+
LOGGER.error("Catalog %s format is invalid", catalog_name)
77+
invalid_catalogs.append(catalog_name)
78+
79+
if invalid_catalogs:
80+
sys.exit(1)
81+
82+
83+
if __name__ == "__main__": # pragma: no cover
84+
main()
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import pytest
2+
3+
from unittest.mock import MagicMock, patch, call
4+
from subprocess import CalledProcessError
5+
6+
import operatorcert.entrypoints.validate_catalog_format as validate_catalog_format
7+
8+
9+
@patch("operatorcert.entrypoints.validate_catalog_format.run_command")
10+
def test_validate_catalog_format(mock_run_command: MagicMock) -> None:
11+
12+
validate_catalog_format.validate_catalog_format("path/to/catalogs", "catalog1")
13+
14+
mock_run_command.assert_called_once_with(
15+
["opm", "validate", "path/to/catalogs/catalog1"]
16+
)
17+
18+
19+
@patch("operatorcert.entrypoints.validate_catalog_format.validate_catalog_format")
20+
@patch("operatorcert.entrypoints.validate_catalog_format.setup_logger")
21+
@patch("operatorcert.entrypoints.validate_catalog_format.setup_argparser")
22+
def test_main(
23+
mock_setup_argparser: MagicMock,
24+
mock_setup_logger: MagicMock,
25+
mock_validate_catalog_format: MagicMock,
26+
) -> None:
27+
args = MagicMock()
28+
args.catalog_names = ["catalog1", "catalog2"]
29+
args.repo_path = "repo"
30+
args.makefile_path = "path/to/repo/Makefile"
31+
32+
mock_setup_argparser.return_value.parse_args.return_value = args
33+
34+
validate_catalog_format.main()
35+
36+
catalogs_path = args.repo_path + "/catalogs"
37+
38+
mock_validate_catalog_format.assert_has_calls(
39+
[call(catalogs_path, "catalog1"), call(catalogs_path, "catalog2")]
40+
)
41+
42+
43+
@patch("operatorcert.entrypoints.validate_catalog_format.validate_catalog_format")
44+
@patch("operatorcert.entrypoints.validate_catalog_format.setup_logger")
45+
@patch("operatorcert.entrypoints.validate_catalog_format.setup_argparser")
46+
def test_main_invalid_catalog(
47+
mock_setup_argparser: MagicMock,
48+
mock_setup_logger: MagicMock,
49+
mock_validate_catalog_format: MagicMock,
50+
) -> None:
51+
args = MagicMock()
52+
args.catalog_names = ["catalog1"]
53+
mock_setup_argparser.return_value.parse_args.return_value = args
54+
55+
mock_validate_catalog_format.side_effect = CalledProcessError(1, "opm validate")
56+
57+
with pytest.raises(SystemExit) as invalid_catalog:
58+
validate_catalog_format.main()
59+
assert invalid_catalog.type == SystemExit
60+
61+
62+
def test_setup_argparser() -> None:
63+
assert validate_catalog_format.setup_argparser() is not None

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ static-tests = "operatorcert.entrypoints.static_tests:main"
8282
update-cert-project-status = "operatorcert.entrypoints.update_cert_project_status:main"
8383
upload-artifacts = "operatorcert.entrypoints.upload_artifacts:main"
8484
upload-signature = "operatorcert.entrypoints.upload_signature:main"
85+
validate-catalog-format = "operatorcert.entrypoints.validate_catalog_format:main"
8586
verify-changed-dirs = "operatorcert.entrypoints.verify_changed_dirs:main"
8687

8788

0 commit comments

Comments
 (0)