Skip to content

Commit bfc204f

Browse files
committed
fix: address review comments
Signed-off-by: lugi0 <lgiorgi@redhat.com>
1 parent 9b5871d commit bfc204f

File tree

4 files changed

+243
-268
lines changed

4 files changed

+243
-268
lines changed

tests/model_registry/conftest.py

Lines changed: 1 addition & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import pytest
22
import re
3-
import random
4-
import string
5-
import subprocess
63
import schemathesis
7-
import shlex
8-
from typing import Generator, Any, List, Dict
4+
from typing import Generator, Any
95
from kubernetes.dynamic.exceptions import ResourceNotFoundError
106
from ocp_resources.pod import Pod
117
from ocp_resources.secret import Secret
@@ -14,12 +10,8 @@
1410
from ocp_resources.persistent_volume_claim import PersistentVolumeClaim
1511
from ocp_resources.data_science_cluster import DataScienceCluster
1612
from ocp_resources.deployment import Deployment
17-
from ocp_resources.service_account import ServiceAccount
18-
from ocp_resources.role import Role
19-
from ocp_resources.role_binding import RoleBinding
2013

2114
from ocp_resources.model_registry import ModelRegistry
22-
import schemathesis.schemas
2315
from schemathesis.specs.openapi.schemas import BaseOpenAPISchema
2416
from schemathesis.generation.stateful.state_machine import APIStateMachine
2517
from schemathesis.core.transport import Response
@@ -30,7 +22,6 @@
3022
from simple_logger.logger import get_logger
3123
from kubernetes.dynamic import DynamicClient
3224
from pytest_testconfig import config as py_config
33-
from pyhelper_utils.shell import run_command
3425
from model_registry.types import RegisteredModel
3526
from tests.model_registry.constants import (
3627
MR_OPERATOR_NAME,
@@ -52,7 +43,6 @@
5243

5344

5445
LOGGER = get_logger(name=__name__)
55-
DEFAULT_TOKEN_DURATION = "10m"
5646

5747

5848
@pytest.fixture(scope="class")
@@ -302,194 +292,3 @@ def model_registry_operator_pod(admin_client: DynamicClient) -> Pod:
302292
if not model_registry_operator_pods:
303293
raise ResourceNotFoundError("Model registry operator pod not found")
304294
return model_registry_operator_pods[0]
305-
306-
307-
# --- Fixture Helper Function ---
308-
309-
310-
def generate_random_name(prefix: str = "test", length: int = 8) -> str:
311-
"""Generates a random string for resource names."""
312-
suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
313-
return f"{prefix}-{suffix}"
314-
315-
316-
# --- Service Account and Namespace Fixtures (Function Scoped for Isolation) ---
317-
318-
319-
@pytest.fixture(scope="function")
320-
def sa_namespace(admin_client: DynamicClient) -> Generator[Namespace, None, None]:
321-
"""
322-
Creates a temporary namespace using a context manager for automatic cleanup.
323-
Function scope ensures a fresh namespace for each test needing it.
324-
"""
325-
ns_name = generate_random_name(prefix="mr-rbac-test-ns")
326-
LOGGER.info(f"Creating temporary namespace: {ns_name}")
327-
# Use context manager for creation and deletion
328-
with Namespace(client=admin_client, name=ns_name) as ns:
329-
try:
330-
ns.wait_for_status(status=Namespace.Status.ACTIVE, timeout=120)
331-
LOGGER.info(f"Namespace {ns_name} is active.")
332-
yield ns
333-
# Cleanup happens automatically when exiting 'with' block
334-
LOGGER.info(f"Namespace {ns_name} deletion initiated by context manager.")
335-
# Add a final wait within the fixture if immediate confirmation is needed,
336-
# but context manager handles the delete call. Let's rely on the manager.
337-
# Consider adding ns.wait_deleted(timeout=180) here if needed AFTER yield returns.
338-
except Exception:
339-
LOGGER.error(f"Timeout waiting for namespace {ns_name} to become active.")
340-
pytest.fail(f"Namespace {ns_name} failed to become active.")
341-
342-
343-
@pytest.fixture(scope="function")
344-
def service_account(admin_client: DynamicClient, sa_namespace: Namespace) -> Generator[ServiceAccount, None, None]:
345-
"""
346-
Creates a ServiceAccount within the temporary namespace using a context manager.
347-
Function scope ensures it's tied to the lifetime of sa_namespace for that test.
348-
"""
349-
sa_name = generate_random_name(prefix="mr-test-user")
350-
LOGGER.info(f"Creating ServiceAccount: {sa_name} in namespace {sa_namespace.name}")
351-
# Use context manager for creation and deletion
352-
with ServiceAccount(client=admin_client, name=sa_name, namespace=sa_namespace.name) as sa:
353-
try:
354-
sa.wait(timeout=60) # Wait for SA object to exist
355-
LOGGER.info(f"ServiceAccount {sa_name} created.")
356-
yield sa
357-
# Cleanup happens automatically when exiting 'with' block
358-
LOGGER.info(f"ServiceAccount {sa_name} deletion initiated by context manager.")
359-
except Exception:
360-
LOGGER.error(f"Timeout waiting for ServiceAccount {sa_name} to be created.")
361-
pytest.fail(f"ServiceAccount {sa_name} failed to be created.")
362-
363-
364-
@pytest.fixture(scope="function")
365-
def sa_token(service_account: ServiceAccount) -> str: # type: ignore[return]
366-
"""
367-
Retrieves a short-lived token for the ServiceAccount using 'oc create token'.
368-
Function scope because token is temporary and tied to the SA for that test.
369-
"""
370-
sa_name = service_account.name
371-
namespace = service_account.namespace # Get namespace name from SA object
372-
LOGGER.info(f"Retrieving token for ServiceAccount: {sa_name} in namespace {namespace}")
373-
# (Keep the subprocess logic from previous version - it's appropriate here)
374-
try:
375-
cmd = f"oc create token {sa_name} -n {namespace} --duration={DEFAULT_TOKEN_DURATION}"
376-
LOGGER.debug(f"Executing command: {cmd}")
377-
res, out, err = run_command(command=shlex.split(cmd), verify_stderr=False, check=True)
378-
token = out.strip()
379-
if not token:
380-
pytest.fail(f"Retrieved token is empty for SA {sa_name} in {namespace}.")
381-
LOGGER.info(f"Successfully retrieved token for SA {sa_name}")
382-
return token
383-
except subprocess.CalledProcessError as e:
384-
LOGGER.error(f"Failed to create token for SA {sa_name} ns {namespace}: {e.stderr}")
385-
pytest.fail(f"Failed to create token for SA {sa_name}: {e.stderr}")
386-
except subprocess.TimeoutExpired:
387-
LOGGER.error(f"Timeout creating token for SA {sa_name} ns {namespace}")
388-
pytest.fail(f"Timeout creating token for SA {sa_name}")
389-
except Exception as e:
390-
LOGGER.error(
391-
f"An unexpected error occurred during token retrieval for SA {sa_name} ns {namespace}: {e}", exc_info=True
392-
)
393-
pytest.fail(f"Unexpected error getting token for SA {sa_name}: {e}")
394-
395-
396-
# --- RBAC Fixtures (Using Context Managers, Function Scoped) ---
397-
398-
399-
@pytest.fixture(scope="function")
400-
def mr_access_role(
401-
admin_client: DynamicClient,
402-
model_registry_namespace: str, # Existing fixture from main conftest
403-
sa_namespace: Namespace, # Used for unique naming
404-
) -> Generator[Role, None, None]:
405-
"""
406-
Creates the MR Access Role using direct constructor parameters and a context manager.
407-
"""
408-
role_name = f"registry-user-{MR_INSTANCE_NAME}-{sa_namespace.name[:8]}"
409-
LOGGER.info(f"Defining Role: {role_name} in namespace {model_registry_namespace}")
410-
411-
# Define rules directly as required by the Role constructor's 'rules' parameter
412-
role_rules: List[Dict[str, Any]] = [
413-
{
414-
"apiGroups": [""], # Core API group
415-
"resources": ["services"], # As per last refinement for REST access
416-
"resourceNames": [MR_INSTANCE_NAME], # Grant access only to the specific MR service object
417-
"verbs": ["get"],
418-
}
419-
]
420-
421-
# Define labels, to be passed via **kwargs
422-
role_labels = {
423-
"app.kubernetes.io/component": "model-registry-test-rbac",
424-
"test.opendatahub.io/namespace": sa_namespace.name,
425-
}
426-
427-
LOGGER.info(f"Attempting to create Role: {role_name} with rules and labels.")
428-
# Use context manager for creation and deletion
429-
# Pass rules and labels directly
430-
with Role(
431-
client=admin_client,
432-
name=role_name,
433-
namespace=model_registry_namespace,
434-
rules=role_rules,
435-
label=role_labels, # Pass labels via kwargs
436-
) as role:
437-
try:
438-
role.wait(timeout=60) # Wait for role object to exist
439-
LOGGER.info(f"Role {role.name} created successfully.")
440-
yield role
441-
LOGGER.info(f"Role {role.name} deletion initiated by context manager.")
442-
except Exception as e: # Catch other potential errors during Role instantiation or wait
443-
LOGGER.error(f"Error during Role {role_name} creation or wait: {e}", exc_info=True)
444-
pytest.fail(f"Failed during Role {role_name} creation: {e}")
445-
446-
447-
@pytest.fixture(scope="function")
448-
def mr_access_role_binding(
449-
admin_client: DynamicClient,
450-
model_registry_namespace: str, # Existing fixture from main conftest
451-
mr_access_role: Role, # Depend on the role fixture to get its name
452-
sa_namespace: Namespace, # The namespace containing the test SA
453-
) -> Generator[RoleBinding, None, None]:
454-
"""
455-
Creates the MR Access RoleBinding using direct constructor parameters and a context manager.
456-
"""
457-
binding_name = f"{mr_access_role.name}-binding" # Simplify name slightly, role name is already unique
458-
role_name_ref = mr_access_role.name # Get the actual name from the created Role object
459-
460-
LOGGER.info(
461-
f"Defining RoleBinding: {binding_name} linking Group 'system:serviceaccounts:{sa_namespace.name}' "
462-
f"to Role '{role_name_ref}' in namespace {model_registry_namespace}"
463-
)
464-
465-
# Define labels, to be passed via **kwargs
466-
binding_labels = {
467-
"app.kubernetes.io/component": "model-registry-test-rbac",
468-
"test.opendatahub.io/namespace": sa_namespace.name,
469-
}
470-
471-
LOGGER.info(f"Attempting to create RoleBinding: {binding_name} with labels.")
472-
# Use context manager for creation and deletion
473-
# Pass subject and role_ref details directly to constructor
474-
with RoleBinding(
475-
client=admin_client,
476-
name=binding_name,
477-
namespace=model_registry_namespace,
478-
# Subject parameters
479-
subjects_kind="Group",
480-
subjects_name=f"system:serviceaccounts:{sa_namespace.name}",
481-
subjects_api_group="rbac.authorization.k8s.io", # This is the default apiGroup for Group kind
482-
# Role reference parameters
483-
role_ref_kind="Role",
484-
role_ref_name=role_name_ref,
485-
# role_ref_api_group="rbac.authorization.k8s.io", # This is automatically set by the class
486-
label=binding_labels, # Pass labels via kwargs
487-
) as binding:
488-
try:
489-
binding.wait(timeout=60) # Wait for binding object to exist
490-
LOGGER.info(f"RoleBinding {binding.name} created successfully.")
491-
yield binding
492-
LOGGER.info(f"RoleBinding {binding.name} deletion initiated by context manager.")
493-
except Exception as e: # Catch other potential errors
494-
LOGGER.error(f"Error during RoleBinding {binding_name} creation or wait: {e}", exc_info=True)
495-
pytest.fail(f"Failed during RoleBinding {binding_name} creation: {e}")

0 commit comments

Comments
 (0)