Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
b362382
Create size-labeler.yml
rnetser Dec 18, 2024
3c6a875
Delete .github/workflows/size-labeler.yml
rnetser Dec 18, 2024
ccb63af
Merge branch 'main' of github.com:rnetser/opendatahub-tests
rnetser Dec 24, 2024
da0c898
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 25, 2024
94a82ec
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 26, 2024
c0c82dd
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 27, 2024
5feb447
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 30, 2024
19b9c56
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 31, 2024
e22ac1a
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 31, 2024
56ab9c5
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 31, 2024
5a17f03
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 31, 2024
ef5fe65
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Dec 31, 2024
1875a44
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 1, 2025
840d442
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 2, 2025
c0d4436
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 2, 2025
ba7971a
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 3, 2025
fd73a94
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 7, 2025
bde0493
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 8, 2025
d3cd799
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 9, 2025
710befa
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 12, 2025
a662364
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 16, 2025
579c283
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 16, 2025
927cbb0
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 19, 2025
99e242e
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 22, 2025
5b83bab
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 24, 2025
4b5b007
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 27, 2025
b8e5dee
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 30, 2025
0039df0
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Jan 30, 2025
17938d6
model mesh - add auth tests
rnetser Feb 2, 2025
44a3120
xx
rnetser Feb 2, 2025
a418727
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 3, 2025
d291c32
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 4, 2025
b6650d8
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 5, 2025
8b9f838
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 7, 2025
d53a04c
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 10, 2025
db89111
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 10, 2025
773d81a
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 12, 2025
01be6e9
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 13, 2025
71df8d5
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 16, 2025
8896bd7
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 18, 2025
0eeb162
Merge branch 'main' of https://github.com/opendatahub-io/opendatahub-…
rnetser Feb 18, 2025
38e8a69
ci: on main upstream xxxxxxxxxxx
rnetser Feb 19, 2025
875f597
ci: reabsingxxxxxxxxxxxxxx
rnetser Feb 20, 2025
c90abf6
ci: reabsingxxxxxxxxxxxxxx
rnetser Feb 20, 2025
4a26346
ci: Merge branch 'main' of https://github.com/opendatahub-io/opendata…
rnetser Feb 24, 2025
8302ed1
ci: Merge branch 'main' of https://github.com/opendatahub-io/opendata…
rnetser Feb 25, 2025
ff1b155
ci: rebase on main
rnetser Mar 6, 2025
a04a3c4
ci: merge main branch
rnetser Mar 11, 2025
d1cb99b
ci: merge branch main
rnetser Mar 12, 2025
87faa7b
ci: merge branch main
rnetser Mar 17, 2025
dc8490d
ci: merge branch main
rnetser Mar 17, 2025
5856788
ci: merge main branch
rnetser Mar 18, 2025
a865aa8
ci: merge main branch
rnetser Mar 18, 2025
e8be67d
ci: merge main branch
rnetser Mar 18, 2025
e8c8b38
ci: merge main branch
rnetser Mar 20, 2025
667cb70
ci: merge main branch
rnetser Mar 20, 2025
a122d95
ci: merge main branch
rnetser Mar 20, 2025
defca5d
ci: merge main branch
rnetser Mar 21, 2025
d8879ae
ci: merge with main
rnetser Mar 24, 2025
a0477ca
ci: merge with main
rnetser Mar 24, 2025
0b37eb7
ci: merge with main
rnetser Mar 24, 2025
79b9288
ci: merge with main
rnetser Mar 24, 2025
3b9d6a1
ci: merge with main
rnetser Mar 25, 2025
a91c002
ci: merge with main
rnetser Mar 26, 2025
ba7a2a4
ci: merge main branch
rnetser Mar 27, 2025
7fd9c92
ci: merge main branch
rnetser Mar 27, 2025
96c2146
ci: merge main branch
rnetser Mar 31, 2025
dcdfc7a
ci: merge main branch
rnetser Mar 31, 2025
beaa1e5
ci: merge main branch
rnetser Apr 2, 2025
3c35249
ci: merge main branch
rnetser Apr 6, 2025
d40269a
ci: merge main branch
rnetser Apr 7, 2025
1abe771
ci: merge main branch
rnetser Apr 8, 2025
25786e7
ci: merge main branch
rnetser Apr 9, 2025
19b690a
feat: cluster sanity
rnetser Apr 10, 2025
f885c8c
ci: merge main branch
rnetser Apr 10, 2025
2bcbcf5
feat: cluster sanity
rnetser Apr 10, 2025
3e40963
feat: cluster sanity
rnetser Apr 10, 2025
46612fb
feat: cluster sanity add readme
rnetser Apr 10, 2025
4f386ee
fix: tix str typo
rnetser Apr 10, 2025
707d994
ci: merge main branch
rnetser Apr 10, 2025
e27e16f
fix: address comments
rnetser Apr 10, 2025
c8002d4
ci: resolve conflicts main
rnetser Apr 14, 2025
491e02b
fix: address review comments
rnetser Apr 14, 2025
f65b8d3
ci: merge main branch
rnetser Apr 14, 2025
e4dcdbc
ci: merge main branch
rnetser Apr 14, 2025
79d27a9
fix: address comment
rnetser Apr 14, 2025
4be92e2
fix: use dsci from global config
rnetser Apr 14, 2025
8e3fa78
ci: merge main branch
rnetser Apr 14, 2025
8afb108
ci: merge main branch
rnetser Apr 15, 2025
536fea6
ci: merge main branch
rnetser Apr 15, 2025
fb893a6
fix: remove duplicate fixture
rnetser Apr 15, 2025
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 conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def pytest_addoption(parser: Parser) -> None:
runtime_group = parser.getgroup(name="Runtime details")
upgrade_group = parser.getgroup(name="Upgrade options")
platform_group = parser.getgroup(name="Platform")
cluster_sanity_group = parser.getgroup(name="ClusterSanity")

# AWS config and credentials options
aws_group.addoption(
Expand Down Expand Up @@ -118,6 +119,18 @@ def pytest_addoption(parser: Parser) -> None:
help="RHOAI/ODH applications namespace",
)

# Cluster sanity options
cluster_sanity_group.addoption(
"--cluster-sanity-skip-check",
help="Skip cluster_sanity check",
action="store_true",
)
cluster_sanity_group.addoption(
"--cluster-sanity-skip-rhoai-check",
help="Skip RHOAI/ODH-related resources (DSCI and DSC) checks",
action="store_true",
)


def pytest_cmdline_main(config: Any) -> None:
config.option.basetemp = py_config["tmp_base_dir"] = f"{config.option.basetemp}-{shortuuid.uuid()}"
Expand Down
5 changes: 5 additions & 0 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ uv run pytest -k test_name
Bt default, RHOAI distribution is set.
To run on ODH, pass `--tc=distribution:upstream` to pytest.

### Skip cluster sanity checks
By default, cluster sanity checks are run to make cluster ready for tests.
To skip cluster sanity checks, pass `--cluster-sanity-skip-check` to skip all tests.
To skip RHOAI/ODH-related tests (for example when running in upstream), pass `--cluster-sanity-skip-rhoai-check`.

### jira integration
To skip running tests which have open bugs, [pytest_jira](https://github.com/rhevm-qe-automation/pytest_jira) plugin is used.
To run tests with jira integration, you need to set `PYTEST_JIRA_URL` and `PYTEST_JIRA_TOKEN` environment variables.
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ dependencies = [
"jira>=3.8.0",
"openshift-python-wrapper>=11.0.38",
"semver>=3.0.4",
"pytest-order>=1.3.0",
]

[project.urls]
Expand Down
46 changes: 40 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import base64
import os
import shutil
from typing import Any, Generator
from typing import Any, Callable, Generator

import pytest
import shortuuid
import yaml
from _pytest.tmpdir import TempPathFactory
from ocp_resources.config_map import ConfigMap
from ocp_resources.dsc_initialization import DSCInitialization
from ocp_resources.node import Node
from ocp_resources.pod import Pod
from ocp_resources.secret import Secret
from ocp_resources.service import Service
from pyhelper_utils.shell import run_command
from pytest import FixtureRequest, Config
from kubernetes.dynamic import DynamicClient
from kubernetes.dynamic.exceptions import ResourceNotFoundError
from ocp_resources.data_science_cluster import DataScienceCluster
from ocp_resources.namespace import Namespace
from ocp_resources.resource import get_client
Expand All @@ -25,6 +26,7 @@
from utilities.exceptions import ClusterLoginError
from utilities.general import get_s3_secret_dict
from utilities.infra import (
verify_cluster_sanity,
create_ns,
get_dsci_applications_namespace,
get_operator_distribution,
Expand Down Expand Up @@ -277,12 +279,14 @@ def unprivileged_client(
raise ClusterLoginError(user=non_admin_user_name)


@pytest.fixture(scope="session")
def dsci_resource(admin_client: DynamicClient) -> DSCInitialization:
return DSCInitialization(client=admin_client, name=py_config["dsci_name"], ensure_exists=True)


@pytest.fixture(scope="session")
def dsc_resource(admin_client: DynamicClient) -> DataScienceCluster:
name = py_config["dsc_name"]
for dsc in DataScienceCluster.get(dyn_client=admin_client, name=name):
return dsc
raise ResourceNotFoundError(f"DSC resource {name} not found")
return DataScienceCluster(client=admin_client, name=py_config["dsc_name"], ensure_exists=True)


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -444,3 +448,33 @@ def minio_data_connection(
},
) as minio_secret:
yield minio_secret


@pytest.fixture(scope="session")
def nodes(admin_client: DynamicClient) -> Generator[list[Node], Any, Any]:
yield list(Node.get(dyn_client=admin_client))


@pytest.fixture(scope="session")
def junitxml_plugin(
request: FixtureRequest, record_testsuite_property: Callable[[str, object], None]
) -> Callable[[str, object], None] | None:
return record_testsuite_property if request.config.pluginmanager.has_plugin("junitxml") else None


@pytest.fixture(scope="session", autouse=True)
@pytest.mark.early(order=0)
def cluster_sanity_scope_session(
request: FixtureRequest,
nodes: list[Node],
dsci_resource: DSCInitialization,
dsc_resource: DataScienceCluster,
junitxml_plugin: Callable[[str, object], None],
) -> None:
verify_cluster_sanity(
request=request,
nodes=nodes,
dsc_resource=dsc_resource,
dsci_resource=dsci_resource,
junitxml_property=junitxml_plugin,
)
1 change: 1 addition & 0 deletions tests/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
distribution: str = "downstream"
applications_namespace: str = "redhat-ods-applications" # overwritten in conftest.py if distribution is upstream
dsc_name: str = "default-dsc"
dsci_name: str = "default-dsci"

for _dir in dir():
val = locals()[_dir]
Expand Down
5 changes: 0 additions & 5 deletions tests/model_serving/model_server/multi_node/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@
from utilities.serving_runtime import ServingRuntimeFromTemplate


@pytest.fixture(scope="session")
def nodes(admin_client: DynamicClient) -> list[Node]:
return list(Node.get(dyn_client=admin_client))


@pytest.fixture(scope="session")
def nvidia_gpu_nodes(nodes: list[Node]) -> list[Node]:
return [node for node in nodes if "nvidia.com/gpu.present" in node.labels.keys()]
Expand Down
6 changes: 6 additions & 0 deletions utilities/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from typing import Optional

from ocp_resources.service import Service
Expand Down Expand Up @@ -92,3 +94,7 @@ def __init__(self, user: str):

def __str__(self) -> str:
return f"Failed to log in as user {self.user}."


class ResourceNotReadyError(Exception):
pass
89 changes: 84 additions & 5 deletions utilities/infra.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import shlex
from contextlib import contextmanager
from functools import cache
from typing import Any, Generator, Optional, Set
from typing import Any, Callable, Generator, Optional, Set

import kubernetes
import pytest
from _pytest.fixtures import FixtureRequest
from kubernetes.dynamic import DynamicClient
from kubernetes.dynamic.exceptions import ResourceNotFoundError, ResourceNotUniqueError
Expand All @@ -19,6 +20,7 @@
from ocp_resources.inference_service import InferenceService
from ocp_resources.infrastructure import Infrastructure
from ocp_resources.namespace import Namespace
from ocp_resources.node_config_openshift_io import Node
from ocp_resources.pod import Pod
from ocp_resources.project_project_openshift_io import Project
from ocp_resources.project_request import ProjectRequest
Expand All @@ -29,6 +31,11 @@
from ocp_resources.service import Service
from ocp_resources.service_account import ServiceAccount
from ocp_resources.serving_runtime import ServingRuntime
from ocp_utilities.exceptions import NodeNotReadyError, NodeUnschedulableError
from ocp_utilities.infra import (
assert_nodes_in_healthy_condition,
assert_nodes_schedulable,
)
from pyhelper_utils.shell import run_command
from pytest_testconfig import config as py_config
from semver import Version
Expand All @@ -37,7 +44,11 @@
from utilities.constants import ApiGroups, Labels, Timeout
from utilities.constants import KServeDeploymentType
from utilities.constants import Annotations
from utilities.exceptions import ClusterLoginError, FailedPodsError
from utilities.exceptions import (
ClusterLoginError,
FailedPodsError,
ResourceNotReadyError,
)
from timeout_sampler import TimeoutExpiredError, TimeoutSampler, retry
import utilities.general

Expand Down Expand Up @@ -719,13 +730,12 @@ def get_product_version(admin_client: DynamicClient) -> Version:
return Version.parse(operator_version)


def get_dsci_applications_namespace(client: DynamicClient, dsci_name: str = "default-dsci") -> str:
def get_dsci_applications_namespace(client: DynamicClient) -> str:
"""
Get the namespace where DSCI applications are deployed.

Args:
client (DynamicClient): DynamicClient object
dsci_name (str): DSCI name

Returns:
str: Namespace where DSCI applications are deployed.
Expand All @@ -735,6 +745,7 @@ def get_dsci_applications_namespace(client: DynamicClient, dsci_name: str = "def
MissingResourceError: If DSCI not found

"""
dsci_name = py_config["dsci_name"]
dsci = DSCInitialization(client=client, name=dsci_name)

if dsci.exists:
Expand Down Expand Up @@ -798,7 +809,11 @@ def wait_for_serverless_pods_deletion(resource: Project | Namespace, admin_clien
pod.wait_deleted(timeout=Timeout.TIMEOUT_1MIN)


@retry(wait_timeout=Timeout.TIMEOUT_30SEC, sleep=1, exceptions_dict={ResourceNotFoundError: []})
@retry(
wait_timeout=Timeout.TIMEOUT_30SEC,
sleep=1,
exceptions_dict={ResourceNotFoundError: []},
)
def wait_for_isvc_pods(client: DynamicClient, isvc: InferenceService, runtime_name: str | None = None) -> list[Pod]:
"""
Wait for ISVC pods.
Expand All @@ -816,3 +831,67 @@ def wait_for_isvc_pods(client: DynamicClient, isvc: InferenceService, runtime_na
"""
LOGGER.info("Waiting for pods to be created")
return get_pods_by_isvc_label(client=client, isvc=isvc, runtime_name=runtime_name)


def verify_dsci_status_ready(dsci_resource: DSCInitialization) -> None:
LOGGER.info(f"Verify DSCI {dsci_resource.name} are {dsci_resource.Status.READY}.")
if dsci_resource.status != dsci_resource.Status.READY:
raise ResourceNotReadyError(f"DSCI {dsci_resource.name} is not ready.\nStatus: {dsci_resource.instance.status}")


def verify_dsc_status_ready(dsc_resource: DataScienceCluster) -> None:
LOGGER.info(f"Verify DSC {dsc_resource.name} are {dsc_resource.Status.READY}.")
if dsc_resource.status != dsc_resource.Status.READY:
raise ResourceNotReadyError(f"DSC {dsc_resource.name} is not ready.\nStatus: {dsc_resource.instance.status}")


def verify_cluster_sanity(
request: FixtureRequest,
nodes: list[Node],
dsci_resource: DSCInitialization,
dsc_resource: DataScienceCluster,
junitxml_property: Callable[[str, object], None] | None = None,
) -> None:
"""
Check that cluster resources (Nodes, DSCI, DSC) are healthy and exists pytest execution on failure.

Args:
request (FixtureRequest): pytest request
nodes (list[Node]): list of nodes
dsci_resource (DSCInitialization): dsci resource
dsc_resource (DataScienceCluster): dsc resource
junitxml_property (property): Junitxml property

"""
skip_cluster_sanity_check = "--cluster-sanity-skip-check"
skip_rhoai_check = "--cluster-sanity-skip-rhoai-check"

if request.session.config.getoption(skip_cluster_sanity_check):
LOGGER.warning(f"Skipping cluster sanity check, got {skip_cluster_sanity_check}")
return

try:
LOGGER.info("Check cluster sanity.")

assert_nodes_in_healthy_condition(nodes=nodes, healthy_node_condition_type={"KubeletReady": "True"})
assert_nodes_schedulable(nodes=nodes)

if request.session.config.getoption(skip_rhoai_check):
LOGGER.warning(f"Skipping RHOAI resource checks, got {skip_rhoai_check}")

else:
verify_dsci_status_ready(dsci_resource=dsci_resource)
verify_dsc_status_ready(dsc_resource=dsc_resource)

except (ResourceNotReadyError, NodeUnschedulableError, NodeNotReadyError) as ex:
error_msg = f"Cluster sanity check failed: {str(ex)}"
# return_code set to 99 to not collide with https://docs.pytest.org/en/stable/reference/exit-codes.html
return_code = 99

LOGGER.error(error_msg)

if junitxml_property:
junitxml_property(name="exit_code", value=return_code) # type: ignore[call-arg]

# TODO: Write to file to easily report the failure in jenkins
pytest.exit(reason=error_msg, returncode=return_code)
17 changes: 15 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.