|
24 | 24 | from tests.model_serving.maas_billing.maas_subscription.utils import ( |
25 | 25 | MAAS_DB_NAMESPACE, |
26 | 26 | MAAS_SUBSCRIPTION_NAMESPACE, |
| 27 | + create_and_yield_api_key_id, |
27 | 28 | create_api_key, |
28 | 29 | create_maas_subscription, |
29 | 30 | get_maas_postgres_resources, |
30 | 31 | patch_llmisvc_with_maas_router_and_tiers, |
| 32 | + resolve_api_key_username, |
31 | 33 | revoke_api_key, |
| 34 | + wait_for_auth_ready, |
32 | 35 | wait_for_postgres_connection_log, |
33 | 36 | wait_for_postgres_deployment_ready, |
34 | 37 | ) |
35 | 38 | from tests.model_serving.maas_billing.utils import build_maas_headers |
36 | 39 | from utilities.constants import DscComponents |
37 | 40 | from utilities.general import generate_random_name |
38 | | -from utilities.infra import create_inference_token, create_ns, login_with_user_password |
| 41 | +from utilities.infra import create_inference_token, create_ns, get_openshift_token, login_with_user_password |
39 | 42 | from utilities.llmd_constants import ContainerImages, ModelStorage |
40 | 43 | from utilities.llmd_utils import create_llmisvc |
41 | 44 | from utilities.plugins.constant import OpenAIEnpoints |
| 45 | +from utilities.resources.auth import Auth |
42 | 46 |
|
43 | 47 | LOGGER = get_logger(name=__name__) |
44 | 48 |
|
@@ -662,22 +666,91 @@ def active_api_key_id( |
662 | 666 | """ |
663 | 667 | Create a single active API key and return its ID for revoke tests. |
664 | 668 | """ |
665 | | - key_name = f"e2e-fixture-key-{generate_random_name()}" |
666 | | - _, body = create_api_key( |
| 669 | + yield from create_and_yield_api_key_id( |
| 670 | + request_session_http=request_session_http, |
667 | 671 | base_url=base_url, |
668 | 672 | ocp_user_token=ocp_token_for_actor, |
669 | | - request_session_http=request_session_http, |
670 | | - api_key_name=key_name, |
| 673 | + key_name_prefix="e2e-fixture-key", |
671 | 674 | ) |
672 | | - LOGGER.info(f"active_api_key_id: created key id={body['id']}") |
673 | | - yield body["id"] |
674 | | - LOGGER.info(f"Fixture teardown: revoking key {body['id']}") |
675 | | - revoke_api_key( |
| 675 | + |
| 676 | + |
| 677 | +@pytest.fixture(scope="function") |
| 678 | +def free_user_username( |
| 679 | + request_session_http: requests.Session, |
| 680 | + base_url: str, |
| 681 | + ocp_token_for_actor: str, |
| 682 | + active_api_key_id: str, |
| 683 | +) -> str: |
| 684 | + """Resolve and return the free (non-admin) actor's username from their active API key.""" |
| 685 | + username = resolve_api_key_username( |
676 | 686 | request_session_http=request_session_http, |
677 | 687 | base_url=base_url, |
678 | | - key_id=body["id"], |
| 688 | + key_id=active_api_key_id, |
679 | 689 | ocp_user_token=ocp_token_for_actor, |
680 | 690 | ) |
| 691 | + LOGGER.info(f"free_user_username: resolved username from key id={active_api_key_id}") |
| 692 | + return username |
| 693 | + |
| 694 | + |
| 695 | +@pytest.fixture(scope="function") |
| 696 | +def admin_username( |
| 697 | + request_session_http: requests.Session, |
| 698 | + base_url: str, |
| 699 | + admin_ocp_token: str, |
| 700 | + admin_active_api_key_id: str, |
| 701 | +) -> str: |
| 702 | + """Resolve and return the admin actor's username from their active API key.""" |
| 703 | + username = resolve_api_key_username( |
| 704 | + request_session_http=request_session_http, |
| 705 | + base_url=base_url, |
| 706 | + key_id=admin_active_api_key_id, |
| 707 | + ocp_user_token=admin_ocp_token, |
| 708 | + ) |
| 709 | + LOGGER.info(f"admin_username: resolved username from key id={admin_active_api_key_id}") |
| 710 | + return username |
| 711 | + |
| 712 | + |
| 713 | +@pytest.fixture(scope="function") |
| 714 | +def admin_active_api_key_id( |
| 715 | + request_session_http: requests.Session, |
| 716 | + base_url: str, |
| 717 | + admin_ocp_token: str, |
| 718 | +) -> Generator[str, Any, Any]: |
| 719 | + """Create an active API key as the admin user, yield its ID, and revoke on teardown.""" |
| 720 | + yield from create_and_yield_api_key_id( |
| 721 | + request_session_http=request_session_http, |
| 722 | + base_url=base_url, |
| 723 | + ocp_user_token=admin_ocp_token, |
| 724 | + key_name_prefix="e2e-authz-admin", |
| 725 | + ) |
| 726 | + |
| 727 | + |
| 728 | +@pytest.fixture(scope="class") |
| 729 | +def admin_ocp_token(admin_client: DynamicClient) -> Generator[str, Any, Any]: |
| 730 | + """Temporarily adds dedicated-admins to Auth CR adminGroups so the admin token is recognised by MaaS.""" |
| 731 | + auth = Auth(client=admin_client, name="auth") |
| 732 | + current_groups: list[str] = list(auth.instance.spec.adminGroups or []) |
| 733 | + patched_groups = list(set(current_groups + ["dedicated-admins"])) |
| 734 | + |
| 735 | + auth_conditions = (auth.instance.status or {}).get("conditions") or [] |
| 736 | + ready_before = next( |
| 737 | + (condition for condition in auth_conditions if condition.get("type") == "Ready"), |
| 738 | + {}, |
| 739 | + ) |
| 740 | + baseline_time: str = ready_before.get("lastTransitionTime", "") |
| 741 | + |
| 742 | + LOGGER.info(f"admin_ocp_token: patching Auth CR adminGroups to {patched_groups}") |
| 743 | + with ResourceEditor(patches={auth: {"spec": {"adminGroups": patched_groups}}}): |
| 744 | + wait_for_auth_ready(auth=auth, baseline_time=baseline_time) |
| 745 | + auth_conditions_after = (auth.instance.status or {}).get("conditions") or [] |
| 746 | + ready_after = next( |
| 747 | + (condition for condition in auth_conditions_after if condition.get("type") == "Ready"), |
| 748 | + {}, |
| 749 | + ) |
| 750 | + cleanup_baseline_time: str = ready_after.get("lastTransitionTime", "") |
| 751 | + yield get_openshift_token(client=admin_client) |
| 752 | + |
| 753 | + wait_for_auth_ready(auth=auth, baseline_time=cleanup_baseline_time) |
681 | 754 |
|
682 | 755 |
|
683 | 756 | @pytest.fixture(scope="function") |
|
0 commit comments