Skip to content

Commit a1d7baa

Browse files
committed
address review comments
updates! 50ec24b updates! f3a6c3e updates! 792156f updates! 399aa10 updates! 5080e3b updates! c34f4e7
1 parent 82ca808 commit a1d7baa

6 files changed

Lines changed: 106 additions & 49 deletions

File tree

conftest.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,11 @@ def _add_upgrade_test(_item: Item, _upgrade_deployment_modes: list[str]) -> bool
226226
def pytest_sessionstart(session: Session) -> None:
227227
log_file = session.config.getoption("log_file") or "pytest-tests.log"
228228
tests_log_file = os.path.join(get_base_dir(), log_file)
229+
LOGGER.info(f"Writing tests log to {tests_log_file}")
229230
if os.path.exists(tests_log_file):
230231
pathlib.Path(tests_log_file).unlink()
231-
232+
if session.config.getoption("--collect-must-gather"):
233+
session.config.option.must_gather_db = Database()
232234
session.config.option.log_listener = setup_logging(
233235
log_file=tests_log_file,
234236
log_level=session.config.getoption("log_cli_level") or logging.INFO,
@@ -261,10 +263,10 @@ def pytest_runtest_setup(item: Item) -> None:
261263
# start time
262264

263265
try:
264-
db = Database()
266+
db = item.config.option.must_gather_db
265267
db.insert_test_start_time(
266268
test_name=f"{item.fspath}::{item.name}",
267-
start_time=int(datetime.datetime.now().strftime("%s")),
269+
start_time=int(datetime.datetime.now().timestamp()),
268270
)
269271
except Exception as db_exception:
270272
LOGGER.error(f"Database error: {db_exception}. Must-gather collection may not be accurate")
@@ -320,10 +322,11 @@ def pytest_sessionfinish(session: Session, exitstatus: int) -> None:
320322
if session.config.option.setupplan or session.config.option.collectonly:
321323
return
322324
if session.config.getoption("--collect-must-gather"):
323-
db = Database()
325+
db = session.config.option.must_gather_db
324326
file_path = db.database_file_path
325327
LOGGER.info(f"Removing database file path {file_path}")
326-
os.remove(file_path)
328+
if os.path.exists(file_path):
329+
os.remove(file_path)
327330
# clean up the empty folders
328331
collector_directory = py_config["must_gather_collector"]["must_gather_base_directory"]
329332
if os.path.exists(collector_directory):
@@ -357,7 +360,7 @@ def pytest_exception_interact(node: Item | Collector, call: CallInfo[Any], repor
357360
LOGGER.info(f"Must-gather collection is enabled for {test_name}.")
358361

359362
try:
360-
db = Database()
363+
db = node.config.option.must_gather_db
361364
test_start_time = db.get_test_start_time(test_name=test_name)
362365
except Exception as db_exception:
363366
test_start_time = 0

utilities/constants.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,3 @@ class RunTimeConfig:
273273
}
274274

275275
RHOAI_OPERATOR_NAMESPACE = "redhat-ods-operator"
276-
RHOAI_SUBSCRIPTION_NAME = "rhoai-operator-dev"

utilities/database.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import os
23

34
from sqlalchemy import Integer, String, create_engine
45
from sqlalchemy.orm import Mapped, Session, mapped_column
@@ -24,7 +25,7 @@ class OpenDataHubTestTable(Base):
2425

2526
class Database:
2627
def __init__(self, database_file_name: str = TEST_DB, verbose: bool = True) -> None:
27-
self.database_file_path = f"{get_base_dir()}{database_file_name}"
28+
self.database_file_path = os.path.join(get_base_dir(), database_file_name)
2829
self.connection_string = f"sqlite:///{self.database_file_path}"
2930
self.verbose = verbose
3031
self.engine = create_engine(url=self.connection_string, echo=self.verbose)
@@ -38,9 +39,15 @@ def insert_test_start_time(self, test_name: str, start_time: int) -> None:
3839

3940
def get_test_start_time(self, test_name: str) -> int:
4041
with Session(bind=self.engine) as db_session:
41-
return (
42+
result_row = (
4243
db_session.query(OpenDataHubTestTable)
4344
.with_entities(OpenDataHubTestTable.start_time)
4445
.filter_by(test_name=test_name)
45-
.one()[0]
46+
.first()
4647
)
48+
if result_row:
49+
start_time_value = result_row[0]
50+
else:
51+
start_time_value = 0
52+
LOGGER.warning(f"No test found with name: {test_name}")
53+
return start_time_value

utilities/exceptions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ def __str__(self) -> str:
9696
return f"Failed to log in as user {self.user}."
9797

9898

99-
class InvalidArguments(Exception):
99+
class InvalidArgumentsError(Exception):
100+
"""Raised when mutually exclusive or invalid argument combinations are passed."""
101+
100102
pass
101103

102104

utilities/infra.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@
4646
)
4747
from pyhelper_utils.shell import run_command
4848
from pytest_testconfig import config as py_config
49-
from packaging.version import parse, Version
49+
from semver import Version
5050
from simple_logger.logger import get_logger
5151

5252
from ocp_resources.subscription import Subscription
53-
from utilities.constants import ApiGroups, Labels, Timeout, RHOAI_OPERATOR_NAMESPACE, RHOAI_SUBSCRIPTION_NAME
53+
from utilities.constants import ApiGroups, Labels, Timeout, RHOAI_OPERATOR_NAMESPACE
5454
from utilities.constants import KServeDeploymentType
5555
from utilities.constants import Annotations
5656
from utilities.exceptions import (
@@ -856,19 +856,34 @@ def wait_for_isvc_pods(client: DynamicClient, isvc: InferenceService, runtime_na
856856
return get_pods_by_isvc_label(client=client, isvc=isvc, runtime_name=runtime_name)
857857

858858

859-
def get_rhods_subscription() -> Subscription:
860-
return Subscription(name=RHOAI_SUBSCRIPTION_NAME, namespace=RHOAI_OPERATOR_NAMESPACE, ensure_exists=True)
859+
def get_rhods_subscription() -> Subscription | None:
860+
subscriptions = Subscription.get(dyn_client=get_client(), namespace=RHOAI_OPERATOR_NAMESPACE)
861+
if subscriptions:
862+
for subscription in subscriptions:
863+
LOGGER.info(f"Checking subscription {subscription.name}")
864+
if subscription.name.startswith(tuple(["rhods-operator", "rhoai-operator"])):
865+
return subscription
861866

867+
LOGGER.warning("No RHOAI subscription found. Potentially ODH cluster")
868+
return None
862869

863-
def get_rhods_operator_installed_csv() -> ClusterServiceVersion:
864-
subscription = get_rhods_subscription()
865-
return ClusterServiceVersion(
866-
name=subscription.instance.status.installedCSV, namespace=RHOAI_OPERATOR_NAMESPACE, ensure_exists=True
867-
)
868870

869-
870-
def get_rhods_csv_version() -> Version:
871-
return parse(version=get_rhods_operator_installed_csv().instance.spec.version)
871+
def get_rhods_operator_installed_csv() -> ClusterServiceVersion | None:
872+
subscription = get_rhods_subscription()
873+
if subscription:
874+
csv_name = subscription.instance.status.installedCSV
875+
LOGGER.info(f"Expected CSV: {csv_name}")
876+
return ClusterServiceVersion(name=csv_name, namespace=RHOAI_OPERATOR_NAMESPACE, ensure_exists=True)
877+
return None
878+
879+
880+
def get_rhods_csv_version() -> Version | None:
881+
rhoai_csv = get_rhods_operator_installed_csv()
882+
if rhoai_csv:
883+
LOGGER.info(f"RHOAI CSV version: {rhoai_csv.instance.spec.version}")
884+
return Version.parse(version=rhoai_csv.instance.spec.version)
885+
LOGGER.warning("No RHOAI CSV found. Potentially ODH cluster")
886+
return None
872887

873888

874889
@retry(

utilities/must_gather_collector.py

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,27 @@
44
from pytest_testconfig import config as py_config
55
from pytest import Item
66
from pyhelper_utils.shell import run_command
7-
8-
from utilities.exceptions import InvalidArguments
7+
from simple_logger.logger import get_logger
8+
from utilities.exceptions import InvalidArgumentsError
99
from utilities.infra import get_rhods_csv_version, get_oc_image_info, generate_openshift_pull_secret_file
1010

1111
BASE_DIRECTORY_NAME = "must-gather-collected"
12+
BASE_RESULTS_DIR = "/home/odh/opendatahub-tests/"
13+
LOGGER = get_logger(name=__name__)
1214

1315

1416
def get_base_dir() -> str:
15-
if os.path.exists("/home/odh/opendatahub-tests/"):
17+
if os.path.exists(BASE_RESULTS_DIR):
1618
# we are running from jenkins.
17-
return "/home/odh/opendatahub-tests/results"
19+
return os.path.join(BASE_RESULTS_DIR, "results")
1820
else:
1921
# this is local run
2022
return ""
2123

2224

2325
def set_must_gather_collector_values() -> dict[str, str]:
2426
py_config["must_gather_collector"] = {
25-
"must_gather_base_directory": f"{get_base_dir()}{BASE_DIRECTORY_NAME}",
27+
"must_gather_base_directory": os.path.join(get_base_dir(), BASE_DIRECTORY_NAME),
2628
}
2729
return py_config["must_gather_collector"]
2830

@@ -81,8 +83,22 @@ def run_must_gather(
8183
component_name: str = "",
8284
namespaces_dict: dict[str, str] | None = None,
8385
) -> str:
86+
"""
87+
Process the arguments to build must-gather command and run the same
88+
89+
Args:
90+
image_url (str): must-gather image url
91+
target_dir (str): must-gather target directory
92+
since (str): duration in seconds for must-gather log collection
93+
component_name (str): must-gather component name
94+
namespaces_dict (dict[str, str] | None): namespaces dict for extra data collection from different component
95+
namespaces
96+
97+
Returns:
98+
str: must-gather output
99+
"""
84100
if component_name and namespaces_dict:
85-
raise InvalidArguments("component name and namespaces can't be passed together")
101+
raise InvalidArgumentsError("component name and namespaces can't be passed together")
86102

87103
must_gather_command = "oc adm must-gather"
88104
if target_dir:
@@ -96,41 +112,56 @@ def run_must_gather(
96112
elif namespaces_dict:
97113
namespace_str = ""
98114
if namespaces_dict.get("operator"):
99-
namespace_str += f"export OPERATOR_NAMESPACE={namespaces_dict['operator']};"
115+
namespace_str += f"export OPERATOR_NAMESPACE={shlex.quote(namespaces_dict['operator'])};"
100116
if namespaces_dict.get("notebooks"):
101-
namespace_str += f"export NOTEBOOKS_NAMESPACE={namespaces_dict['notebooks']};"
117+
namespace_str += f"export NOTEBOOKS_NAMESPACE={shlex.quote(namespaces_dict['notebooks'])};"
102118
if namespaces_dict.get("monitoring"):
103-
namespace_str += f"export MONITORING_NAMESPACE={namespaces_dict['monitoring']};"
119+
namespace_str += f"export MONITORING_NAMESPACE={shlex.quote(namespaces_dict['monitoring'])};"
104120
if namespaces_dict.get("application"):
105-
namespace_str += f"export APPLICATIONS_NAMESPACE={namespaces_dict['application']};"
121+
namespace_str += f"export APPLICATIONS_NAMESPACE={shlex.quote(namespaces_dict['application'])};"
106122
if namespaces_dict.get("model_registries"):
107-
namespace_str += f"export MODEL_REGISTRIES_NAMESPACE={namespaces_dict['model_registries']};"
123+
namespace_str += f"export MODEL_REGISTRIES_NAMESPACE={shlex.quote(namespaces_dict['model_registries'])};"
108124
if namespaces_dict.get("ossm"):
109-
namespace_str += f"export OSSM_NS={namespaces_dict['ossm']};"
125+
namespace_str += f"export OSSM_NS={shlex.quote(namespaces_dict['ossm'])};"
110126
if namespaces_dict.get("knative"):
111-
namespace_str += f"export KNATIVE_NS={namespaces_dict['knative']};"
127+
namespace_str += f"export KNATIVE_NS={shlex.quote(namespaces_dict['knative'])};"
112128
if namespaces_dict.get("auth"):
113-
namespace_str += f"export AUTH_NS={namespaces_dict['auth']};"
114-
must_gather_command += " /usr/bin/gather"
129+
namespace_str += f"export AUTH_NS={shlex.quote(namespaces_dict['auth'])};"
130+
must_gather_command += f" -- '{namespace_str} /usr/bin/gather'"
115131

116132
return run_command(command=shlex.split(must_gather_command), check=False)[1]
117133

118134

119135
def get_must_gather_image_info(architecture: str = "linux/amd64") -> str:
120-
csv_version = get_rhods_csv_version()
121-
must_gather_image_manifest = f"quay.io/modh/must-gather:rhoai-{csv_version.major}.{csv_version.minor}"
122-
image_info = get_oc_image_info(
123-
image=must_gather_image_manifest, architecture=architecture, pull_secret=generate_openshift_pull_secret_file()
124-
)
125-
return f"quay.io/modh/must-gather@{image_info['digest']}"
136+
try:
137+
csv_version = get_rhods_csv_version()
138+
if csv_version:
139+
must_gather_image_manifest = f"quay.io/modh/must-gather:rhoai-{csv_version.major}.{csv_version.minor}"
140+
pull_secret = generate_openshift_pull_secret_file()
141+
image_info = get_oc_image_info(
142+
image=must_gather_image_manifest, architecture=architecture, pull_secret=pull_secret
143+
)
144+
return f"quay.io/modh/must-gather@{image_info['digest']}"
145+
else:
146+
LOGGER.warning(
147+
"No RHAOI CSV found. Potentially ODH cluster and must-gather collection is not "
148+
"relevant for this cluster"
149+
)
150+
return ""
151+
except Exception as exec:
152+
raise RuntimeError(f"Failed to retrieve must-gather image info: {str(exec)}") from exec
126153

127154

128155
def collect_rhoai_must_gather(
129156
target_dir: str, since: int, save_collection_output: bool = True, architecture: str = "linux/amd64"
130157
) -> str:
131158
must_gather_image = get_must_gather_image_info(architecture=architecture)
132-
output = run_must_gather(image_url=must_gather_image, target_dir=target_dir, since=f"{since}s")
133-
if save_collection_output:
134-
with open(os.path.join(target_dir, "output.log"), "w") as _file:
135-
_file.write(output)
136-
return get_must_gather_output_dir(must_gather_path=target_dir)
159+
if must_gather_image:
160+
output = run_must_gather(image_url=must_gather_image, target_dir=target_dir, since=f"{since}s")
161+
if save_collection_output:
162+
with open(os.path.join(target_dir, "output.log"), "w") as _file:
163+
_file.write(output)
164+
return get_must_gather_output_dir(must_gather_path=target_dir)
165+
else:
166+
LOGGER.warning("Must-gather collection would be skipped.")
167+
return ""

0 commit comments

Comments
 (0)