diff --git a/tests/conftest.py b/tests/conftest.py index 6ef06175d..1cddecc2a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -161,6 +161,19 @@ def cli(namespace: str, fulfillment_address: str, service_account: str) -> Itera instance.close() +@pytest.fixture(scope="session") +def private_cli(namespace: str, fulfillment_private_address: str, service_account: str) -> Iterator[OsacCLI]: + instance = OsacCLI( + binary=env("OSAC_CLI_PATH", "osac"), + address=f"https://{fulfillment_private_address.rsplit(':', 1)[0]}", + token_script=f"oc create token -n {namespace} {service_account} --as system:admin", + namespace=namespace, + private=True, + ) + yield instance + instance.close() + + @pytest.fixture(scope="session") def keycloak_url(cluster_domain: str) -> str: return env("OSAC_KEYCLOAK_URL", f"https://keycloak-keycloak.{cluster_domain}") diff --git a/tests/core/osac_cli.py b/tests/core/osac_cli.py index 00eaa5f6b..b08f576e8 100644 --- a/tests/core/osac_cli.py +++ b/tests/core/osac_cli.py @@ -17,17 +17,22 @@ def __init__( token_script: str, namespace: str, default_instance_type: str | None = None, + private: bool = False, ) -> None: self.binary: str = binary self.namespace: str = namespace self._address: str = address self._token_script: str = token_script + self._private: bool = private self.default_instance_type: str | None = default_instance_type # Each OsacCLI instance gets its own config directory so that parallel # xdist workers (or multiple CLI fixtures) don't overwrite each other's # login credentials via the shared ~/.config/osac/config.json. self._config_dir: str = tempfile.mkdtemp(prefix="osac-config-") - self._run("login", "--address", address, "--insecure", "--token-script", token_script) + login_args = ["login", "--address", address, "--insecure", "--token-script", token_script] + if self._private: + login_args.append("--private") + self._run(*login_args) def close(self) -> None: shutil.rmtree(self._config_dir, ignore_errors=True) @@ -43,7 +48,10 @@ def _run_unchecked(self, *args: str, timeout: int = 300) -> tuple[str, int]: return run_unchecked(self.binary, "--config", self._config_dir, *args, timeout=timeout) def relogin(self) -> None: - self._run("login", "--address", self._address, "--insecure", "--token-script", self._token_script) + login_args = ["login", "--address", self._address, "--insecure", "--token-script", self._token_script] + if self._private: + login_args.append("--private") + self._run(*login_args) @staticmethod def _parse_uuid(stdout: str) -> str: @@ -124,10 +132,28 @@ def create_compute_instance( def delete_compute_instance(self, *, uuid: str) -> None: self._run("delete", "computeinstance", uuid) - def create_instance_type(self, *, name: str, cores: int, memory_gib: int, description: str = "") -> str: - args: list[str] = ["create", "instancetype", "--name", name, "--cores", str(cores), "--memory-gib", str(memory_gib)] + def create_instance_type( + self, + *, + name: str, + cores: int, + memory_gib: int, + description: str = "", + gpu_pci_device_selector: str = "", + gpu_resource_name: str = "", + gpu_count: int = 0, + ) -> str: + args: list[str] = [ + "create", "instancetype", "--name", name, "--cores", str(cores), "--memory-gib", str(memory_gib), + ] if description: args.extend(["--description", description]) + if gpu_pci_device_selector: + args.extend(["--gpu-pci-device-selector", gpu_pci_device_selector]) + if gpu_resource_name: + args.extend(["--gpu-resource-name", gpu_resource_name]) + if gpu_count: + args.extend(["--gpu-count", str(gpu_count)]) return self._parse_uuid(self._run(*args)) def describe_instance_type(self, *, name: str) -> str: diff --git a/tests/vmaas/test_instance_type_lifecycle.py b/tests/vmaas/test_instance_type_lifecycle.py index 2a2353064..0d7c0fa4b 100644 --- a/tests/vmaas/test_instance_type_lifecycle.py +++ b/tests/vmaas/test_instance_type_lifecycle.py @@ -92,6 +92,45 @@ def test_instance_type_lifecycle(cli: OsacCLI, private_grpc: GRPCClient) -> None raise +def test_create_instance_type_via_cli(private_cli: OsacCLI, private_grpc: GRPCClient) -> None: + it_name: str = f"e2e-cli-it-{uuid4().hex[:8]}" + + try: + private_cli.create_instance_type( + name=it_name, + cores=TEST_CORES, + memory_gib=TEST_MEMORY_GIB, + description="CLI create test type", + gpu_pci_device_selector=TEST_GPU["pci_device_selector"], + gpu_resource_name=TEST_GPU["resource_name"], + gpu_count=TEST_GPU["count"], + ) + + response: dict = private_grpc.get_instance_type(name=it_name) + spec: dict = response["object"]["spec"] + assert spec["cores"] == TEST_CORES, f"spec.cores mismatch: {spec['cores']} != {TEST_CORES}" + assert spec["memoryGib"] == TEST_MEMORY_GIB, ( + f"spec.memoryGib mismatch: {spec['memoryGib']} != {TEST_MEMORY_GIB}" + ) + assert "gpu" in spec, f"spec.gpu missing from response: {spec}" + gpu: dict = spec["gpu"] + assert gpu["pciDeviceSelector"] == TEST_GPU["pci_device_selector"], ( + f"gpu.pciDeviceSelector mismatch: {gpu['pciDeviceSelector']} != {TEST_GPU['pci_device_selector']}" + ) + assert gpu["resourceName"] == TEST_GPU["resource_name"], ( + f"gpu.resourceName mismatch: {gpu['resourceName']} != {TEST_GPU['resource_name']}" + ) + assert gpu["count"] == TEST_GPU["count"], f"gpu.count mismatch: {gpu['count']} != {TEST_GPU['count']}" + + finally: + try: + private_grpc.delete_instance_type(name=it_name) + except subprocess.CalledProcessError as e: + output = ((e.stdout or "") + (e.stderr or "")).lower() + if "not found" not in output: + raise + + def test_gpu_instance_type(private_grpc: GRPCClient) -> None: gpu_name: str = f"e2e-gpu-lifecycle-{uuid4().hex[:8]}" nogpu_name: str = f"e2e-nogpu-lifecycle-{uuid4().hex[:8]}"