Skip to content

Commit f4d697a

Browse files
committed
Move static check from common to isv
The checks are usually skipped by support to it doesn't make sense to have them in isv pipelines. Signed-off-by: Ales Raszka <[email protected]>
1 parent dfd0823 commit f4d697a

File tree

4 files changed

+270
-274
lines changed

4 files changed

+270
-274
lines changed
Lines changed: 1 addition & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
11
"""A common test suite for operator bundles"""
22

33
from collections.abc import Iterator
4-
from typing import Any, List
54

65
from operator_repo import Bundle
7-
from operator_repo.checks import CheckResult, Fail, Warn
8-
from operatorcert import utils
9-
10-
11-
class GraphLoopException(Exception):
12-
"""
13-
Exception raised when a loop is detected in the update graph
14-
"""
6+
from operator_repo.checks import CheckResult, Warn
157

168

179
def check_operator_name(bundle: Bundle) -> Iterator[CheckResult]:
@@ -31,111 +23,3 @@ def check_operator_name(bundle: Bundle) -> Iterator[CheckResult]:
3123
"To fix this issue define the annotation in "
3224
"'metadata/annotations.yaml' file that matches the CSV name."
3325
)
34-
35-
36-
def check_upgrade_graph_loop(bundle: Bundle) -> Iterator[CheckResult]:
37-
"""
38-
Detect loops in the upgrade graph
39-
40-
Example:
41-
42-
Channel beta: A -> B -> C -> B
43-
44-
Args:
45-
bundle (Bundle): Operator bundle
46-
47-
Yields:
48-
Iterator[CheckResult]: Failure if a loop is detected
49-
"""
50-
all_channels: set[str] = set(bundle.channels)
51-
if bundle.default_channel is not None:
52-
all_channels.add(bundle.default_channel)
53-
operator = bundle.operator
54-
for channel in sorted(all_channels):
55-
visited: List[Bundle] = []
56-
try:
57-
channel_bundles = operator.channel_bundles(channel)
58-
try:
59-
graph = operator.update_graph(channel)
60-
except (NotImplementedError, ValueError) as exc:
61-
yield Fail(str(exc))
62-
return
63-
follow_graph(graph, channel_bundles[0], visited)
64-
except GraphLoopException as exc:
65-
yield Fail(str(exc))
66-
67-
68-
def follow_graph(graph: Any, bundle: Bundle, visited: List[Bundle]) -> List[Bundle]:
69-
"""
70-
Follow operator upgrade graph and raise exception if loop is detected
71-
72-
Args:
73-
graph (Any): Operator update graph
74-
bundle (Bundle): Current bundle that started the graph traversal
75-
visited (List[Bundle]): List of bundles visited so far
76-
77-
Raises:
78-
GraphLoopException: Graph loop detected
79-
80-
Returns:
81-
List[Bundle]: List of bundles visited so far
82-
"""
83-
if bundle in visited:
84-
visited.append(bundle)
85-
raise GraphLoopException(f"Upgrade graph loop detected for bundle: {visited}")
86-
if bundle not in graph:
87-
return visited
88-
89-
visited.append(bundle)
90-
next_bundles = graph[bundle]
91-
for next_bundle in next_bundles:
92-
visited_copy = visited.copy()
93-
follow_graph(graph, next_bundle, visited_copy)
94-
return visited
95-
96-
97-
def check_replaces_availability(bundle: Bundle) -> Iterator[CheckResult]:
98-
"""
99-
Check if the current bundle and the replaced bundle support the same OCP versions
100-
101-
Args:
102-
bundle (Bundle): Operator bundle
103-
104-
Yields:
105-
Iterator[CheckResult]: Failure if the version of the replaced bundle
106-
does not match with the current bundle
107-
"""
108-
109-
replaces = bundle.csv.get("spec", {}).get("replaces")
110-
if not replaces:
111-
return
112-
delimiter = ".v" if ".v" in replaces else "."
113-
replaces_version = replaces.split(delimiter, 1)[1]
114-
replaces_bundle = bundle.operator.bundle(replaces_version)
115-
ocp_versions_str = bundle.annotations.get("com.redhat.openshift.versions")
116-
replaces_ocp_version_str = replaces_bundle.annotations.get(
117-
"com.redhat.openshift.versions"
118-
)
119-
if ocp_versions_str == replaces_ocp_version_str:
120-
# The annotations match, no need to check further
121-
return
122-
organization = bundle.operator.repo.config.get("organization")
123-
124-
indexes = set(utils.get_ocp_supported_versions(organization, ocp_versions_str))
125-
replaces_indexes = set(
126-
utils.get_ocp_supported_versions(organization, replaces_ocp_version_str)
127-
)
128-
129-
if indexes - replaces_indexes == set():
130-
# The replaces bundle supports all the same versions as the current bundle
131-
return
132-
yield Fail(
133-
f"Replaces bundle {replaces_bundle} {sorted(replaces_indexes)} does not support "
134-
f"the same OCP versions as bundle {bundle} {sorted(indexes)}. In order to fix this issue, "
135-
"align the OCP version range to match the range of the replaced bundle. "
136-
"This can be done by setting the `com.redhat.openshift.versions` annotation in the "
137-
"`metadata/annotations.yaml` file.\n"
138-
f"`{bundle}` - `{ocp_versions_str}`\n"
139-
f"`{replaces_bundle}` - `{replaces_ocp_version_str}`"
140-
)
141-
yield from []

operator-pipeline-images/operatorcert/static_tests/community/bundle.py

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
import re
1212
import subprocess
1313
from collections.abc import Iterator
14+
from typing import Any, List
1415

1516
from operator_repo import Bundle
1617
from operator_repo.checks import CheckResult, Fail, Warn
1718
from operator_repo.utils import lookup_dict
18-
from semver import Version
1919
from operatorcert import utils
20+
from semver import Version
2021

2122
from .validations import (
2223
validate_capabilities,
@@ -54,6 +55,12 @@
5455
}
5556

5657

58+
class GraphLoopException(Exception):
59+
"""
60+
Exception raised when a loop is detected in the update graph
61+
"""
62+
63+
5764
def _parse_semver(version: str) -> Version:
5865
return Version.parse(version.strip(), optional_minor_and_patch=True).replace(
5966
prerelease=None, build=None
@@ -288,3 +295,111 @@ def check_api_version_constraints(bundle: Bundle) -> Iterator[CheckResult]:
288295
f"OCP version(s) {conflicting_str} conflict with "
289296
f"minKubeVersion={k8s_version_min}"
290297
)
298+
299+
300+
def check_upgrade_graph_loop(bundle: Bundle) -> Iterator[CheckResult]:
301+
"""
302+
Detect loops in the upgrade graph
303+
304+
Example:
305+
306+
Channel beta: A -> B -> C -> B
307+
308+
Args:
309+
bundle (Bundle): Operator bundle
310+
311+
Yields:
312+
Iterator[CheckResult]: Failure if a loop is detected
313+
"""
314+
all_channels: set[str] = set(bundle.channels)
315+
if bundle.default_channel is not None:
316+
all_channels.add(bundle.default_channel)
317+
operator = bundle.operator
318+
for channel in sorted(all_channels):
319+
visited: List[Bundle] = []
320+
try:
321+
channel_bundles = operator.channel_bundles(channel)
322+
try:
323+
graph = operator.update_graph(channel)
324+
except (NotImplementedError, ValueError) as exc:
325+
yield Fail(str(exc))
326+
return
327+
follow_graph(graph, channel_bundles[0], visited)
328+
except GraphLoopException as exc:
329+
yield Fail(str(exc))
330+
331+
332+
def follow_graph(graph: Any, bundle: Bundle, visited: List[Bundle]) -> List[Bundle]:
333+
"""
334+
Follow operator upgrade graph and raise exception if loop is detected
335+
336+
Args:
337+
graph (Any): Operator update graph
338+
bundle (Bundle): Current bundle that started the graph traversal
339+
visited (List[Bundle]): List of bundles visited so far
340+
341+
Raises:
342+
GraphLoopException: Graph loop detected
343+
344+
Returns:
345+
List[Bundle]: List of bundles visited so far
346+
"""
347+
if bundle in visited:
348+
visited.append(bundle)
349+
raise GraphLoopException(f"Upgrade graph loop detected for bundle: {visited}")
350+
if bundle not in graph:
351+
return visited
352+
353+
visited.append(bundle)
354+
next_bundles = graph[bundle]
355+
for next_bundle in next_bundles:
356+
visited_copy = visited.copy()
357+
follow_graph(graph, next_bundle, visited_copy)
358+
return visited
359+
360+
361+
def check_replaces_availability(bundle: Bundle) -> Iterator[CheckResult]:
362+
"""
363+
Check if the current bundle and the replaced bundle support the same OCP versions
364+
365+
Args:
366+
bundle (Bundle): Operator bundle
367+
368+
Yields:
369+
Iterator[CheckResult]: Failure if the version of the replaced bundle
370+
does not match with the current bundle
371+
"""
372+
373+
replaces = bundle.csv.get("spec", {}).get("replaces")
374+
if not replaces:
375+
return
376+
delimiter = ".v" if ".v" in replaces else "."
377+
replaces_version = replaces.split(delimiter, 1)[1]
378+
replaces_bundle = bundle.operator.bundle(replaces_version)
379+
ocp_versions_str = bundle.annotations.get("com.redhat.openshift.versions")
380+
replaces_ocp_version_str = replaces_bundle.annotations.get(
381+
"com.redhat.openshift.versions"
382+
)
383+
if ocp_versions_str == replaces_ocp_version_str:
384+
# The annotations match, no need to check further
385+
return
386+
organization = bundle.operator.repo.config.get("organization")
387+
388+
indexes = set(utils.get_ocp_supported_versions(organization, ocp_versions_str))
389+
replaces_indexes = set(
390+
utils.get_ocp_supported_versions(organization, replaces_ocp_version_str)
391+
)
392+
393+
if indexes - replaces_indexes == set():
394+
# The replaces bundle supports all the same versions as the current bundle
395+
return
396+
yield Fail(
397+
f"Replaces bundle {replaces_bundle} {sorted(replaces_indexes)} does not support "
398+
f"the same OCP versions as bundle {bundle} {sorted(indexes)}. In order to fix this issue, "
399+
"align the OCP version range to match the range of the replaced bundle. "
400+
"This can be done by setting the `com.redhat.openshift.versions` annotation in the "
401+
"`metadata/annotations.yaml` file.\n"
402+
f"`{bundle}` - `{ocp_versions_str}`\n"
403+
f"`{replaces_bundle}` - `{replaces_ocp_version_str}`"
404+
)
405+
yield from []

0 commit comments

Comments
 (0)