diff --git a/fulfillment-service/it/it_compute_instance_update_test.go b/fulfillment-service/it/it_compute_instance_update_test.go index 944cab3e40..a350febf7c 100644 --- a/fulfillment-service/it/it_compute_instance_update_test.go +++ b/fulfillment-service/it/it_compute_instance_update_test.go @@ -95,6 +95,80 @@ var _ = Describe("Compute instance updates", Label("compute-updates"), func() { Expect(err).NotTo(HaveOccurred()) }) + It("allows run_strategy updates through the public API", func(ctx context.Context) { + template := createCatalogItemComputeInstanceProvisioningTemplateFixture(ctx, nil) + network := createCatalogItemNetworkFixture(ctx, usersGroup, "") + conn := tool.ExternalView().UserConn() + instance, err := createComputeInstanceFixture(ctx, conn, publicv1.ComputeInstanceSpec_builder{ + Template: publicv1.ComputeInstanceTemplateReference_builder{Id: template}.Build(), + NetworkAttachments: []*publicv1.ComputeNetworkAttachment{network.computeInstanceAttachment()}, + }.Build()) + Expect(err).NotTo(HaveOccurred()) + Expect(instance.GetSpec().GetRunStrategy()).To(Equal(publicv1.ComputeInstanceRunStrategy_COMPUTE_INSTANCE_RUN_STRATEGY_ALWAYS)) + + client := publicv1.NewComputeInstancesClient(conn) + + By("updating run_strategy from ALWAYS to HALTED") + updateResponse, err := client.Update(ctx, publicv1.ComputeInstancesUpdateRequest_builder{ + Object: publicv1.ComputeInstance_builder{ + Id: instance.GetId(), + Spec: publicv1.ComputeInstanceSpec_builder{RunStrategy: new(publicv1.ComputeInstanceRunStrategy_COMPUTE_INSTANCE_RUN_STRATEGY_HALTED)}.Build(), + }.Build(), + UpdateMask: catalogItemUpdateMask("spec.run_strategy"), + }.Build()) + Expect(err).NotTo(HaveOccurred()) + Expect(updateResponse.GetObject().GetSpec().GetRunStrategy()).To(Equal(publicv1.ComputeInstanceRunStrategy_COMPUTE_INSTANCE_RUN_STRATEGY_HALTED)) + + persisted, err := client.Get(ctx, publicv1.ComputeInstancesGetRequest_builder{Id: instance.GetId()}.Build()) + Expect(err).NotTo(HaveOccurred()) + Expect(persisted.GetObject().GetSpec().GetRunStrategy()).To(Equal(publicv1.ComputeInstanceRunStrategy_COMPUTE_INSTANCE_RUN_STRATEGY_HALTED)) + + By("updating run_strategy back to ALWAYS") + updateResponse, err = client.Update(ctx, publicv1.ComputeInstancesUpdateRequest_builder{ + Object: publicv1.ComputeInstance_builder{ + Id: instance.GetId(), + Spec: publicv1.ComputeInstanceSpec_builder{RunStrategy: new(publicv1.ComputeInstanceRunStrategy_COMPUTE_INSTANCE_RUN_STRATEGY_ALWAYS)}.Build(), + }.Build(), + UpdateMask: catalogItemUpdateMask("spec.run_strategy"), + }.Build()) + Expect(err).NotTo(HaveOccurred()) + Expect(updateResponse.GetObject().GetSpec().GetRunStrategy()).To(Equal(publicv1.ComputeInstanceRunStrategy_COMPUTE_INSTANCE_RUN_STRATEGY_ALWAYS)) + + persisted, err = client.Get(ctx, publicv1.ComputeInstancesGetRequest_builder{Id: instance.GetId()}.Build()) + Expect(err).NotTo(HaveOccurred()) + Expect(persisted.GetObject().GetSpec().GetRunStrategy()).To(Equal(publicv1.ComputeInstanceRunStrategy_COMPUTE_INSTANCE_RUN_STRATEGY_ALWAYS)) + }) + + It("allows instance_type updates through the public API", func(ctx context.Context) { + firstInstanceType := createCatalogItemComputeInstanceTypeFixture(ctx) + secondInstanceType := createCatalogItemComputeInstanceTypeFixture(ctx) + template := createCatalogItemComputeInstanceProvisioningTemplateFixture(ctx, nil) + network := createCatalogItemNetworkFixture(ctx, usersGroup, "") + conn := tool.ExternalView().UserConn() + instance, err := createComputeInstanceFixture(ctx, conn, publicv1.ComputeInstanceSpec_builder{ + Template: publicv1.ComputeInstanceTemplateReference_builder{Id: template}.Build(), + InstanceType: publicv1.InstanceTypeReference_builder{Id: firstInstanceType}.Build(), + NetworkAttachments: []*publicv1.ComputeNetworkAttachment{network.computeInstanceAttachment()}, + }.Build()) + Expect(err).NotTo(HaveOccurred()) + + client := publicv1.NewComputeInstancesClient(conn) + + By("updating instance_type to the second type") + candidate := proto.Clone(instance).(*publicv1.ComputeInstance) + candidate.GetSpec().SetInstanceType(publicv1.InstanceTypeReference_builder{Id: secondInstanceType}.Build()) + updateResponse, err := client.Update(ctx, publicv1.ComputeInstancesUpdateRequest_builder{ + Object: candidate, + UpdateMask: catalogItemUpdateMask("spec.instance_type"), + }.Build()) + Expect(err).NotTo(HaveOccurred()) + Expect(updateResponse.GetObject().GetSpec().GetInstanceType().GetId()).To(Equal(secondInstanceType)) + + persisted, err := client.Get(ctx, publicv1.ComputeInstancesGetRequest_builder{Id: instance.GetId()}.Build()) + Expect(err).NotTo(HaveOccurred()) + Expect(persisted.GetObject().GetSpec().GetInstanceType().GetId()).To(Equal(secondInstanceType)) + }) + DescribeTable("validates networking against persisted deletion state", func(ctx context.Context, deleteFirst, maskDeletionTimestamp bool) { network := createCatalogItemNetworkFixture(ctx, usersGroup, "") template := createCatalogItemComputeInstanceProvisioningTemplateFixture(ctx, nil) diff --git a/tests/e2e/vmaas/regression/test_compute_instance_api_fields.py b/tests/e2e/vmaas/regression/test_compute_instance_api_fields.py deleted file mode 100644 index a6ac55cf3a..0000000000 --- a/tests/e2e/vmaas/regression/test_compute_instance_api_fields.py +++ /dev/null @@ -1,106 +0,0 @@ -from __future__ import annotations - -import pytest - -from tests.e2e.core.grpc_client import GRPCClient -from tests.e2e.core.helpers import unique_name, wait_for_cr, wait_for_deletion, wait_for_provision, wait_for_running -from tests.e2e.core.k8s_client import K8sClient -from tests.e2e.core.osac_cli import OsacCLI -from tests.e2e.core.runner import poll_until - -pytestmark = pytest.mark.regression - -TEST_BOOT_DISK_SIZE: int = 20 -TEST_RUN_STRATEGY: str = "Always" - - -def test_compute_instance_api_fields( - cli: OsacCLI, - grpc: GRPCClient, - k8s_hub_client: K8sClient, - k8s_virt_client: K8sClient, - default_network_attachment: dict[str, object], - vm_template: str, -) -> None: - ci_uuid: str = cli.create_compute_instance( - template=vm_template, - name=unique_name("e2e-api"), - network_attachments=[default_network_attachment], - boot_disk_size=TEST_BOOT_DISK_SIZE, - run_strategy=TEST_RUN_STRATEGY, - ) - instance_name: str | None = None - - try: - instance_name = wait_for_cr(k8s=k8s_hub_client, uuid=ci_uuid) - - wait_for_provision(k8s=k8s_hub_client, name=instance_name) - wait_for_running(k8s=k8s_hub_client, name=instance_name) - - vm_ns: str = k8s_hub_client.get_compute_instance_vm_namespace(name=instance_name) - - # runStrategy mutability: Always -> Halted - _, rc = k8s_hub_client.patch( - resource="computeinstance", name=instance_name, patch='{"spec":{"runStrategy":"Halted"}}' - ) - assert rc == 0, "runStrategy patch to Halted should succeed" - - poll_until( - fn=lambda: k8s_virt_client.get_vm_printable_status(name=instance_name, vm_namespace=vm_ns, checked=False), - until=lambda v: v == "Stopped", - retries=30, - delay=10, - description=f"{instance_name} VM stopped", - ) - - vm_strategy: str = k8s_virt_client.get_vm_run_strategy(name=instance_name, vm_namespace=vm_ns) - vm_status: str = k8s_virt_client.get_vm_printable_status(name=instance_name, vm_namespace=vm_ns, checked=False) - assert vm_strategy == "Halted", f"VM runStrategy should be Halted, got {vm_strategy}" - assert vm_status == "Stopped", f"VM should be Stopped, got {vm_status}" - - # runStrategy mutability: Halted -> Always - _, rc = k8s_hub_client.patch( - resource="computeinstance", name=instance_name, patch='{"spec":{"runStrategy":"Always"}}' - ) - assert rc == 0, "runStrategy patch to Always should succeed" - - poll_until( - fn=lambda: k8s_virt_client.get_vm_printable_status(name=instance_name, vm_namespace=vm_ns, checked=False), - until=lambda v: v == "Running", - retries=30, - delay=10, - description=f"{instance_name} VM running", - ) - - vm_strategy = k8s_virt_client.get_vm_run_strategy(name=instance_name, vm_namespace=vm_ns) - vm_status = k8s_virt_client.get_vm_printable_status(name=instance_name, vm_namespace=vm_ns, checked=False) - assert vm_strategy == "Always", f"VM runStrategy should be Always, got {vm_strategy}" - assert vm_status == "Running", f"VM should be Running, got {vm_status}" - - # Mutability: vCPUs - _, rc = k8s_hub_client.patch(resource="computeinstance", name=instance_name, patch='{"spec":{"vcpus":8}}') - assert rc == 0, "vCPUs update should succeed" - assert ( - k8s_hub_client.get_jsonpath(resource="computeinstance", name=instance_name, jsonpath="{.spec.vcpus}") == "8" - ) - - # Mutability: memoryGiB - _, rc = k8s_hub_client.patch(resource="computeinstance", name=instance_name, patch='{"spec":{"memoryGiB":16}}') - assert rc == 0, "memoryGiB update should succeed" - assert ( - k8s_hub_client.get_jsonpath(resource="computeinstance", name=instance_name, jsonpath="{.spec.memoryGiB}") - == "16" - ) - - # Immutability: image - output, rc = k8s_hub_client.patch( - resource="computeinstance", - name=instance_name, - patch='{"spec":{"image":{"sourceRef":"quay.io/fedora/fedora:latest"}}}', - ) - assert rc != 0, "image field should be immutable" - assert "image is immutable" in output, f"Expected immutability error, got: {output}" - finally: - cli.delete_compute_instance(uuid=ci_uuid) - if instance_name is not None: - wait_for_deletion(k8s=k8s_hub_client, name=instance_name)