Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@
from tests.core.runner import env, run


@pytest.fixture(scope="session")
def default_storage_tier() -> str:
"""Reference installer-provided storage tier.

Defaults to "local" tier created by osac-installer when lvms.enabled=true.
Available to all suites that create ComputeInstances (vmaas, catalog, references).
"""
return env("OSAC_STORAGE_TIER", "local")


def pytest_configure(config: pytest.Config) -> None:
"""Give each xdist worker its own log_file so records stay chronologically ordered.

Expand Down
17 changes: 15 additions & 2 deletions tests/core/grpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,19 @@ def _build_args(self, *, service: str, data: dict[str, Any] | None = None) -> li
def call(self, *, service: str, data: dict[str, Any] | None = None) -> dict[str, Any]:
return json.loads(run(*self._build_args(service=service, data=data)))

def create_compute_instance(self, *, catalog_item: str, subnet_ids: list[str], name: str | None = None) -> str:
def create_compute_instance(
self,
*,
catalog_item: str,
subnet_ids: list[str],
name: str | None = None,
boot_disk_storage_tier: str | None = None,
) -> str:
attachments = [{"subnet": {"id": sid}} for sid in subnet_ids]
obj: dict[str, Any] = {"spec": {"catalog_item": {"id": catalog_item}, "network_attachments": attachments}}
spec: dict[str, Any] = {"catalog_item": {"id": catalog_item}, "network_attachments": attachments}
if boot_disk_storage_tier is not None:
spec["boot_disk"] = {"storage_tier": boot_disk_storage_tier}
obj: dict[str, Any] = {"spec": spec}
if name is not None:
obj["metadata"] = {"name": name}
response: dict[str, Any] = self.call(service=f"{PUBLIC_API}.ComputeInstances/Create", data={"object": obj})
Expand Down Expand Up @@ -598,6 +608,7 @@ def create_compute_instance_with_disk_image(
subnet_ids: list[str],
instance_type: str | None = None,
name: str | None = None,
boot_disk_storage_tier: str | None = None,
) -> dict[str, Any]:
attachments = [{"subnet": {"id": sid}} for sid in subnet_ids]
spec: dict[str, Any] = {
Expand All @@ -607,6 +618,8 @@ def create_compute_instance_with_disk_image(
}
if instance_type is not None:
spec["instance_type"] = {"name": instance_type}
if boot_disk_storage_tier is not None:
spec["boot_disk"] = {"storage_tier": boot_disk_storage_tier}
obj: dict[str, Any] = {"spec": spec}
if name is not None:
obj["metadata"] = {"name": name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


def test_catalog_item_disk_image_default_applied(
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str, default_storage_tier: str
) -> None:
"""AC-2 / TC-FR9-01: CatalogItem disk_image field_definition default is applied to the ComputeInstance."""
di_id: str | None = None
Expand All @@ -28,10 +28,12 @@ def test_catalog_item_disk_image_default_applied(
# network_attachments must also be declared: with field_definitions present, the server
# (applyFieldDefinitions) allowlists only catalog_item, template, and the declared fd
# paths, then rejects any other spec leaf. The CI-create below sends network_attachments
# (the subnet), so it must be a declared field or the create is rejected InvalidArgument.
# (the subnet) and boot_disk.storage_tier, so both must be declared or the create is
# rejected InvalidArgument.
field_defs = [
{"path": "disk_image", "display_name": "Disk Image", "editable": True, "default": di_name},
{"path": "network_attachments", "display_name": "Network", "editable": True},
{"path": "boot_disk.storage_tier", "display_name": "Boot Disk Storage Tier", "editable": True},
]
catalog_item_id = grpc.create_compute_instance_catalog_item(
name=unique_name("e2e-cidi-cat"),
Expand All @@ -42,7 +44,10 @@ def test_catalog_item_disk_image_default_applied(

# disk_image is deliberately omitted — it must be inherited from the catalog default.
ci_id = grpc.create_compute_instance(
name=unique_name("e2e-cidi-ci"), catalog_item=catalog_item_id, subnet_ids=[default_subnet_id]
name=unique_name("e2e-cidi-ci"),
catalog_item=catalog_item_id,
subnet_ids=[default_subnet_id],
boot_disk_storage_tier=default_storage_tier,
)

ci = grpc.get_compute_instance(ci_id=ci_id)
Expand Down
17 changes: 12 additions & 5 deletions tests/e2e/catalog/test_compute_instance_catalog_item_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def test_compute_instance_catalog_item_field_definitions(


def test_create_compute_instance_with_catalog_item(
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str, default_storage_tier: str
) -> None:
name = unique_name("e2e-ci-cat")
catalog_item_id = grpc.create_compute_instance_catalog_item(
Expand All @@ -152,7 +152,10 @@ def test_create_compute_instance_with_catalog_item(
try:
ci_name = unique_name("e2e-ci")
ci_id = grpc.create_compute_instance(
name=ci_name, catalog_item=catalog_item_id, subnet_ids=[default_subnet_id]
name=ci_name,
catalog_item=catalog_item_id,
subnet_ids=[default_subnet_id],
boot_disk_storage_tier=default_storage_tier,
)

assert ci_id in grpc.list_compute_instance_ids()
Expand All @@ -173,7 +176,7 @@ def test_create_compute_instance_with_catalog_item(


def test_create_compute_instance_with_unpublished_catalog_item_fails(
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str, default_storage_tier: str
) -> None:
name = unique_name("e2e-ci-unpub")
catalog_item_id = grpc.create_compute_instance_catalog_item(
Expand All @@ -186,6 +189,7 @@ def test_create_compute_instance_with_unpublished_catalog_item_fails(
"object": {
"spec": {
"catalog_item": {"id": catalog_item_id},
"boot_disk": {"storage_tier": default_storage_tier},
"network_attachments": [{"subnet": {"id": default_subnet_id}}],
}
}
Expand All @@ -198,7 +202,7 @@ def test_create_compute_instance_with_unpublished_catalog_item_fails(


def test_delete_compute_instance_catalog_item_blocked_when_referenced(
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str
grpc: GRPCClient, compute_instance_template: str, default_subnet_id: str, default_storage_tier: str
) -> None:
name = unique_name("e2e-ci-ref")
catalog_item_id = grpc.create_compute_instance_catalog_item(
Expand All @@ -208,7 +212,10 @@ def test_delete_compute_instance_catalog_item_blocked_when_referenced(
try:
ci_name = unique_name("e2e-ci")
ci_id = grpc.create_compute_instance(
name=ci_name, catalog_item=catalog_item_id, subnet_ids=[default_subnet_id]
name=ci_name,
catalog_item=catalog_item_id,
subnet_ids=[default_subnet_id],
boot_disk_storage_tier=default_storage_tier,
)

output, rc = grpc.call_unchecked(
Expand Down
17 changes: 16 additions & 1 deletion tests/e2e/references/test_compute_references.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@ def ref_disk_image(grpc: GRPCClient) -> Generator[str, None, None]:


def _ci_create_data(
name: str, cat_item_name: str, subnet_name: str, sg_name: str, instance_type: str, disk_image: str
name: str,
cat_item_name: str,
subnet_name: str,
sg_name: str,
instance_type: str,
disk_image: str,
storage_tier: str,
) -> dict[str, Any]:
return {
"object": {
Expand All @@ -75,6 +81,7 @@ def _ci_create_data(
"catalog_item": {"name": cat_item_name},
"instance_type": {"name": instance_type},
"disk_image": {"name": disk_image},
"boot_disk": {"storage_tier": storage_tier},
"network_attachments": [
{"subnet": {"name": subnet_name}, "security_groups": [{"name": sg_name}]}
],
Expand All @@ -95,6 +102,7 @@ def test_compute_instance_full_chain_by_name(
ref_ci_catalog_item: str,
ref_instance_type: str,
ref_disk_image: str,
default_storage_tier: str,
):
tag = uuid4().hex[:8]
ci_name = f"ref-ci-chain-{tag}"
Expand All @@ -110,6 +118,7 @@ def test_compute_instance_full_chain_by_name(
ref_security_group["name"],
ref_instance_type,
ref_disk_image,
default_storage_tier,
),
)
ci_id = response["object"]["id"]
Expand Down Expand Up @@ -144,6 +153,7 @@ def test_compute_instance_reaches_running_with_name_refs(
ref_ci_catalog_item: str,
ref_instance_type: str,
ref_disk_image: str,
default_storage_tier: str,
):
tag = uuid4().hex[:8]
ci_name = f"ref-ci-run-{tag}"
Expand All @@ -159,6 +169,7 @@ def test_compute_instance_reaches_running_with_name_refs(
ref_security_group["name"],
ref_instance_type,
ref_disk_image,
default_storage_tier,
),
)
ci_id = response["object"]["id"]
Expand All @@ -185,6 +196,7 @@ def test_invalid_subnet_name_returns_array_indexed_field_path(
ref_ci_catalog_item: str,
ref_instance_type: str,
ref_disk_image: str,
default_storage_tier: str,
):
tag = uuid4().hex[:8]
cat_item = grpc.get_compute_instance_catalog_item(catalog_item_id=ref_ci_catalog_item)
Expand All @@ -200,6 +212,7 @@ def test_invalid_subnet_name_returns_array_indexed_field_path(
"catalog_item": {"name": cat_item_name},
"instance_type": {"name": ref_instance_type},
"disk_image": {"name": ref_disk_image},
"boot_disk": {"storage_tier": default_storage_tier},
"network_attachments": [
{
"subnet": {"name": ref_subnet["name"]},
Expand All @@ -221,6 +234,7 @@ def test_cross_tenant_template_reference(
ref_security_group: dict[str, str],
ref_instance_type: str,
ref_disk_image: str,
default_storage_tier: str,
):
tag = uuid4().hex[:8]
cat_name = f"ref-xt-cat-{tag}"
Expand All @@ -236,6 +250,7 @@ def test_cross_tenant_template_reference(
ref_security_group["name"],
ref_instance_type,
ref_disk_image,
default_storage_tier,
),
)
ci_id = response["object"]["id"]
Expand Down
10 changes: 0 additions & 10 deletions tests/e2e/vmaas/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,16 +183,6 @@ def default_disk_image(grpc: GRPCClient, test_run_id: str) -> Iterator[str]:
raise


@pytest.fixture(scope="session")
def default_storage_tier() -> str:
"""
Reference installer-provided storage tier (matches network_class pattern).

Defaults to "local" tier created by osac-installer when lvms.enabled=true.
"""
return env("OSAC_STORAGE_TIER", "local")


@pytest.fixture(scope="session")
def additional_storage_tiers(private_grpc: GRPCClient, test_run_id: str) -> Iterator[dict[str, dict[str, str]]]:
"""
Expand Down
27 changes: 23 additions & 4 deletions tests/e2e/vmaas/test_compute_instance_disk_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ def _unique_name(prefix: str = "e2e-cidi") -> str:


def test_compute_instance_with_disk_image(
grpc: GRPCClient, vm_template: str, default_subnet: str, default_instance_type: str, k8s_hub_client: K8sClient
grpc: GRPCClient,
vm_template: str,
default_subnet: str,
default_instance_type: str,
k8s_hub_client: K8sClient,
default_storage_tier: str,
) -> None:
"""AC-1 / TC-FR7-01: Create CI with DiskImage reference, verify VM runs with correct image."""
di_name = _unique_name("e2e-di")
Expand All @@ -47,6 +52,7 @@ def test_compute_instance_with_disk_image(
subnet_ids=[default_subnet],
instance_type=default_instance_type,
name=_unique_name("e2e-ci"),
boot_disk_storage_tier=default_storage_tier,
)
ci_id = response["object"]["id"]
assert ci_id, "create_compute_instance should return a non-empty ID"
Expand Down Expand Up @@ -78,7 +84,7 @@ def test_compute_instance_with_disk_image(


def test_obsolete_disk_image_blocks_creation(
grpc: GRPCClient, vm_template: str, default_subnet: str, default_instance_type: str
grpc: GRPCClient, vm_template: str, default_subnet: str, default_instance_type: str, default_storage_tier: str
) -> None:
"""AC-2 / TC-FR7-04: OBSOLETE DiskImage blocks ComputeInstance creation."""
di_name = _unique_name("e2e-di")
Expand All @@ -96,6 +102,7 @@ def test_obsolete_disk_image_blocks_creation(
disk_image_name=di_name,
subnet_ids=[default_subnet],
instance_type=default_instance_type,
boot_disk_storage_tier=default_storage_tier,
)
assert_grpc_rejected(exc_info, "FailedPrecondition")
finally:
Expand All @@ -104,7 +111,12 @@ def test_obsolete_disk_image_blocks_creation(


def test_deprecated_disk_image_allows_creation_with_warning(
grpc: GRPCClient, vm_template: str, default_subnet: str, default_instance_type: str, k8s_hub_client: K8sClient
grpc: GRPCClient,
vm_template: str,
default_subnet: str,
default_instance_type: str,
k8s_hub_client: K8sClient,
default_storage_tier: str,
) -> None:
"""AC-3 / TC-FR7-05: DEPRECATED DiskImage allows creation with warning."""
di_name = _unique_name("e2e-di")
Expand All @@ -123,6 +135,7 @@ def test_deprecated_disk_image_allows_creation_with_warning(
subnet_ids=[default_subnet],
instance_type=default_instance_type,
name=_unique_name("e2e-ci"),
boot_disk_storage_tier=default_storage_tier,
)
ci_id = response["object"]["id"]
assert ci_id, "CI creation with DEPRECATED DiskImage should succeed"
Expand Down Expand Up @@ -187,7 +200,12 @@ def test_template_disk_image_default(grpc: GRPCClient, private_grpc: GRPCClient)


def test_disk_image_deletion_protection(
grpc: GRPCClient, vm_template: str, default_subnet: str, default_instance_type: str, k8s_hub_client: K8sClient
grpc: GRPCClient,
vm_template: str,
default_subnet: str,
default_instance_type: str,
k8s_hub_client: K8sClient,
default_storage_tier: str,
) -> None:
"""AC-5 / TC-FR12-01 + TC-FR12-04: Deletion protection lifecycle."""
di_name = _unique_name("e2e-di")
Expand All @@ -204,6 +222,7 @@ def test_disk_image_deletion_protection(
subnet_ids=[default_subnet],
instance_type=default_instance_type,
name=_unique_name("e2e-ci"),
boot_disk_storage_tier=default_storage_tier,
)
ci_id = response["object"]["id"]
ci_name = wait_for_cr(k8s=k8s_hub_client, uuid=ci_id)
Expand Down
Loading