mirror of
https://github.com/kubevirt/kubevirt.core.git
synced 2026-07-25 08:54:43 +00:00
feat(kubevirt_vm): add state=start/stop/restart to manage VM lifecycle
It adds support for starting, stopping, and restarting VirtualMachines. The operations are performed through the KubeVirt restart subresource API (PUT to `subresources.kubevirt.io/v1`). These calls are extracted into a single shared helper function `_call_subresource()` to handle the common flow, i.e., check mode, API call and wait conditions. Moreover, it adds the optional parameter `grace_period_seconds` for stop and start operations. Additionally, it adds unit tests and integration tests for all operations. Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Javier Cano Cano <jcanocan@redhat.com>
This commit is contained in:
@@ -21,6 +21,341 @@
|
||||
recreate.diff.before.metadata.annotations.get('kubemacpool.io/transaction-timestamp') and
|
||||
not recreate.diff.after.metadata.annotations
|
||||
|
||||
- name: Stop VM
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Stop the VM
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: stopped
|
||||
wait: true
|
||||
wait_timeout: 300
|
||||
register: stop_result
|
||||
- name: Assert VM was stopped
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- stop_result.changed
|
||||
|
||||
- name: Verify VM is stopped
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Get VM info
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
register: vm_info
|
||||
- name: Assert VM is not ready
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not vm_info.resources[0].status.get('ready', False)
|
||||
|
||||
- name: Stop already-stopped VM is idempotent
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Stop the VM again
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: stopped
|
||||
register: stop_again
|
||||
- name: Assert module reported no changes
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not stop_again.changed
|
||||
|
||||
- name: Start VM
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Start the VM
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: started
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: start_result
|
||||
- name: Assert VM was started
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- start_result.changed
|
||||
|
||||
- name: Verify VM is running after start
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Get VM info
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: vm_info
|
||||
- name: Assert VM is ready
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- vm_info.resources[0].status.ready
|
||||
|
||||
- name: Start already-running VM is idempotent
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Start the VM again
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: started
|
||||
register: start_again
|
||||
- name: Assert module reported no changes
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not start_again.changed
|
||||
|
||||
- name: Verify VM definition is untouched when only name and state started are provided
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Snapshot VM definition before
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
register: before
|
||||
- name: Run module with only name and state
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: started
|
||||
register: noop_result
|
||||
- name: Snapshot VM definition after
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
register: after
|
||||
- name: Assert definition was not changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not noop_result.changed
|
||||
- before.resources[0].spec.template.spec == after.resources[0].spec.template.spec
|
||||
- before.resources[0].metadata.labels == after.resources[0].metadata.labels
|
||||
- before.resources[0].metadata.annotations == after.resources[0].metadata.annotations
|
||||
|
||||
- name: Verify VM definition is untouched when only name and state restarted are provided
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Snapshot VM definition before
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
register: before
|
||||
- name: Run module with only name and state restarted
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: restarted
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: restart_noop_result
|
||||
- name: Snapshot VM definition after
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: after
|
||||
- name: Assert definition was not changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- restart_noop_result.changed
|
||||
- before.resources[0].spec.template.spec == after.resources[0].spec.template.spec
|
||||
- before.resources[0].metadata.labels == after.resources[0].metadata.labels
|
||||
- before.resources[0].metadata.annotations == after.resources[0].metadata.annotations
|
||||
|
||||
- name: Verify VM definition is untouched when only name and state stopped are provided
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Snapshot VM definition before
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
register: before
|
||||
- name: Run module with only name and state stopped
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: stopped
|
||||
wait: true
|
||||
wait_timeout: 300
|
||||
register: stop_noop_result
|
||||
- name: Snapshot VM definition after
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
register: after
|
||||
- name: Assert definition was not changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- stop_noop_result.changed
|
||||
- before.resources[0].spec.template.spec == after.resources[0].spec.template.spec
|
||||
- before.resources[0].metadata.labels == after.resources[0].metadata.labels
|
||||
- before.resources[0].metadata.annotations == after.resources[0].metadata.annotations
|
||||
|
||||
- name: Re-start VM for remaining tests
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Start the VM again
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: started
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
|
||||
- name: Restart VM
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Restart the VM
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: restarted
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: restart_result
|
||||
- name: Assert VM was restarted
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- restart_result.changed
|
||||
|
||||
- name: Verify VM is running after restart
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Get VM info
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: vm_info
|
||||
- name: Assert VM is ready
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- vm_info.resources[0].status.ready
|
||||
|
||||
- name: Update labels and stop VM at the same time
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Add labels and stop the VM
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: stopped
|
||||
labels:
|
||||
environment: stopped
|
||||
wait: true
|
||||
wait_timeout: 300
|
||||
register: combined_stop
|
||||
- name: Assert VM was changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- combined_stop.changed
|
||||
- name: Get VM info
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
register: vm_info
|
||||
- name: Assert labels were applied and VM is stopped
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not vm_info.resources[0].status.get('ready', False)
|
||||
- vm_info.resources[0].metadata.labels.environment == 'stopped'
|
||||
|
||||
- name: Update labels and start VM at the same time
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Update labels and start the VM
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: started
|
||||
labels:
|
||||
environment: started
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: combined_start
|
||||
- name: Assert VM was changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- combined_start.changed
|
||||
- name: Get VM info
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: vm_info
|
||||
- name: Assert labels were applied and VM is running
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- vm_info.resources[0].status.ready
|
||||
- vm_info.resources[0].metadata.labels.environment == 'started'
|
||||
|
||||
- name: Update labels and restart VM at the same time
|
||||
connection: local
|
||||
gather_facts: false
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: Update labels and restart the VM
|
||||
kubevirt.core.kubevirt_vm:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
state: restarted
|
||||
labels:
|
||||
environment: restarted
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: combined_restart
|
||||
- name: Assert VM was changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- combined_restart.changed
|
||||
- name: Get VM info
|
||||
kubevirt.core.kubevirt_vm_info:
|
||||
name: testvm
|
||||
namespace: {{ NAMESPACE }}
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
register: vm_info
|
||||
- name: Assert labels were applied and VM is running
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- vm_info.resources[0].status.ready
|
||||
- vm_info.resources[0].metadata.labels.environment == 'restarted'
|
||||
|
||||
- name: Delete VM
|
||||
connection: local
|
||||
gather_facts: false
|
||||
|
||||
@@ -11,6 +11,7 @@ import pytest
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s import runner
|
||||
from ansible_collections.kubevirt.core.plugins.modules import kubevirt_vm
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import (
|
||||
AnsibleFailJson,
|
||||
AnsibleExitJson,
|
||||
@@ -159,6 +160,7 @@ MODULE_PARAMS_DEFAULT = {
|
||||
"state": "present",
|
||||
"force": False,
|
||||
"delete_options": None,
|
||||
"grace_period_seconds": None,
|
||||
}
|
||||
|
||||
MODULE_PARAMS_CREATE = MODULE_PARAMS_DEFAULT | {
|
||||
@@ -334,7 +336,7 @@ def test_module(mocker, module_params, k8s_module_params, vm_definition, method)
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
mocker.patch.object(runner, "get_api_client")
|
||||
|
||||
perform_action = mocker.patch.object(
|
||||
mock_perform_action = mocker.patch.object(
|
||||
runner,
|
||||
"perform_action",
|
||||
return_value={
|
||||
@@ -347,7 +349,7 @@ def test_module(mocker, module_params, k8s_module_params, vm_definition, method)
|
||||
with pytest.raises(AnsibleExitJson), patch_module_args(module_params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
perform_action.assert_called_once_with(
|
||||
mock_perform_action.assert_called_once_with(
|
||||
mocker.ANY,
|
||||
vm_definition,
|
||||
k8s_module_params,
|
||||
@@ -711,3 +713,264 @@ def test_set_wait_condition(mocker, params, expected):
|
||||
kubevirt_vm.set_wait_condition(module)
|
||||
|
||||
assert module.params == params | expected
|
||||
|
||||
|
||||
MODULE_PARAMS_START = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": "started",
|
||||
}
|
||||
|
||||
MODULE_PARAMS_STOP = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": "stopped",
|
||||
}
|
||||
|
||||
MODULE_PARAMS_STOP_FORCE = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": "stopped",
|
||||
"grace_period_seconds": 0,
|
||||
}
|
||||
|
||||
MODULE_PARAMS_RESTART = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": "restarted",
|
||||
}
|
||||
|
||||
MODULE_PARAMS_RESTART_FORCE = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": "restarted",
|
||||
"grace_period_seconds": 0,
|
||||
}
|
||||
|
||||
SUBRESOURCE_BASE_PATH = (
|
||||
f"/apis/{kubevirt_vm.SUBRESOURCE_API}/namespaces/default/virtualmachines/testvm"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"module_params,action,expected_body",
|
||||
[
|
||||
(MODULE_PARAMS_START, "start", None),
|
||||
(MODULE_PARAMS_STOP, "stop", None),
|
||||
(MODULE_PARAMS_STOP_FORCE, "stop", {"gracePeriod": 0}),
|
||||
(MODULE_PARAMS_RESTART, "restart", None),
|
||||
(MODULE_PARAMS_RESTART_FORCE, "restart", {"gracePeriodSeconds": 0}),
|
||||
],
|
||||
)
|
||||
def test_subresource_action(mocker, module_params, action, expected_body):
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
mocker.patch.object(
|
||||
kubevirt_vm, "_apply_definition", return_value={"changed": False}
|
||||
)
|
||||
mock_client = mocker.Mock()
|
||||
mocker.patch.object(kubevirt_vm, "get_api_client", return_value=mock_client)
|
||||
|
||||
with pytest.raises(AnsibleExitJson) as exc, patch_module_args(module_params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
mock_client.client.request.assert_called_once_with(
|
||||
"put",
|
||||
f"{SUBRESOURCE_BASE_PATH}/{action}",
|
||||
body=expected_body,
|
||||
header_params={"Accept": "*/*"},
|
||||
)
|
||||
assert exc.value.args[0]["changed"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("state", ["started", "stopped", "restarted"])
|
||||
def test_subresource_action_check_mode(mocker, state):
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
mocker.patch.object(
|
||||
kubevirt_vm, "_apply_definition", return_value={"changed": False}
|
||||
)
|
||||
mock_client = mocker.Mock()
|
||||
mocker.patch.object(kubevirt_vm, "get_api_client", return_value=mock_client)
|
||||
|
||||
params = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": state,
|
||||
"_ansible_check_mode": True,
|
||||
}
|
||||
with pytest.raises(AnsibleExitJson) as exc, patch_module_args(params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
mock_client.client.request.assert_not_called()
|
||||
assert exc.value.args[0]["changed"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("state", ["started", "stopped", "restarted"])
|
||||
def test_subresource_action_fails_without_name(mocker, state):
|
||||
mocker.patch.object(AnsibleModule, "fail_json", fail_json)
|
||||
|
||||
params = MODULE_PARAMS_DEFAULT | {
|
||||
"namespace": "default",
|
||||
"state": state,
|
||||
"generate_name": "testvm-",
|
||||
}
|
||||
with pytest.raises(AnsibleFailJson), patch_module_args(params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"state,action",
|
||||
[
|
||||
("started", "start"),
|
||||
("stopped", "stop"),
|
||||
("restarted", "restart"),
|
||||
],
|
||||
)
|
||||
def test_subresource_action_api_error(mocker, state, action):
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
mocker.patch.object(AnsibleModule, "fail_json", fail_json)
|
||||
mocker.patch.object(
|
||||
kubevirt_vm, "_apply_definition", return_value={"changed": False}
|
||||
)
|
||||
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.client.request.side_effect = ApiException(
|
||||
status=422, reason="API error"
|
||||
)
|
||||
mocker.patch.object(kubevirt_vm, "get_api_client", return_value=mock_client)
|
||||
|
||||
params = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": state,
|
||||
}
|
||||
with pytest.raises(AnsibleFailJson) as exc, patch_module_args(params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
assert f"Failed to {action} VirtualMachine" in exc.value.args[0]["msg"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"state,expected_condition",
|
||||
[
|
||||
("started", kubevirt_vm.WAIT_CONDITION_READY),
|
||||
("stopped", kubevirt_vm.WAIT_CONDITION_VMI_NOT_EXISTS),
|
||||
("restarted", kubevirt_vm.WAIT_CONDITION_READY),
|
||||
],
|
||||
)
|
||||
def test_subresource_action_wait(mocker, state, expected_condition):
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
mocker.patch.object(
|
||||
kubevirt_vm, "_apply_definition", return_value={"changed": False}
|
||||
)
|
||||
mock_client = mocker.Mock()
|
||||
mocker.patch.object(kubevirt_vm, "get_api_client", return_value=mock_client)
|
||||
|
||||
mock_svc = mocker.Mock()
|
||||
mock_svc.find.return_value = {"resources": [], "api_found": True}
|
||||
mocker.patch.object(kubevirt_vm, "K8sService", return_value=mock_svc)
|
||||
|
||||
params = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": state,
|
||||
"wait": True,
|
||||
}
|
||||
with pytest.raises(AnsibleExitJson) as exc, patch_module_args(params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
mock_svc.find.assert_called_once_with(
|
||||
kind="VirtualMachine",
|
||||
api_version="kubevirt.io/v1",
|
||||
name="testvm",
|
||||
namespace="default",
|
||||
wait=True,
|
||||
wait_sleep=5,
|
||||
wait_timeout=5,
|
||||
condition=expected_condition,
|
||||
)
|
||||
assert exc.value.args[0]["changed"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("state", ["started", "stopped"])
|
||||
def test_subresource_action_idempotent(mocker, state):
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
mocker.patch.object(
|
||||
kubevirt_vm, "_apply_definition", return_value={"changed": False}
|
||||
)
|
||||
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.client.request.side_effect = ApiException(status=409, reason="Conflict")
|
||||
mocker.patch.object(kubevirt_vm, "get_api_client", return_value=mock_client)
|
||||
|
||||
params = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": state,
|
||||
}
|
||||
with pytest.raises(AnsibleExitJson) as exc, patch_module_args(params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
assert exc.value.args[0]["changed"] is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"state,action,definition_changed",
|
||||
[
|
||||
("started", "start", True),
|
||||
("stopped", "stop", True),
|
||||
("restarted", "restart", True),
|
||||
("started", "start", False),
|
||||
("stopped", "stop", False),
|
||||
("restarted", "restart", False),
|
||||
],
|
||||
)
|
||||
def test_subresource_action_with_definition(mocker, state, action, definition_changed):
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
|
||||
mock_client = mocker.Mock()
|
||||
mocker.patch.object(kubevirt_vm, "get_api_client", return_value=mock_client)
|
||||
|
||||
def fake_run_module(module):
|
||||
module.exit_json(changed=definition_changed, method="update", result={})
|
||||
|
||||
mocker.patch.object(runner, "run_module", side_effect=fake_run_module)
|
||||
|
||||
params = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": state,
|
||||
"labels": {"app": "test"},
|
||||
}
|
||||
with pytest.raises(AnsibleExitJson) as exc, patch_module_args(params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
runner.run_module.assert_called_once()
|
||||
mock_client.client.request.assert_called_once_with(
|
||||
"put",
|
||||
f"{SUBRESOURCE_BASE_PATH}/{action}",
|
||||
body=None,
|
||||
header_params={"Accept": "*/*"},
|
||||
)
|
||||
assert exc.value.args[0]["changed"] is (definition_changed or True)
|
||||
|
||||
|
||||
def test_apply_definition_propagates_runner_error(mocker):
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
|
||||
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
|
||||
mocker.patch.object(AnsibleModule, "fail_json", fail_json)
|
||||
mocker.patch.object(
|
||||
runner, "run_module", side_effect=CoreException("cluster unreachable")
|
||||
)
|
||||
|
||||
params = MODULE_PARAMS_DEFAULT | {
|
||||
"name": "testvm",
|
||||
"namespace": "default",
|
||||
"state": "started",
|
||||
}
|
||||
with pytest.raises(AnsibleFailJson) as exc, patch_module_args(params):
|
||||
kubevirt_vm.main()
|
||||
|
||||
assert "cluster unreachable" in exc.value.args[0]["msg"]
|
||||
|
||||
Reference in New Issue
Block a user