mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-03-26 21:33:02 +00:00
Move diff and wait to perform_action (#375)
This primarily moves the diff and wait logic from the various service methods to perform_action to eliminate code duplication. I also moved the diff_objects function out of the service object and moved most of the find_resource logic to a new resource client method. We ended up with several modules creating a service object just to use one of these methods, so it seemed to make sense to make these more accessible.
This commit is contained in:
@@ -43,7 +43,10 @@ _temp_files = []
|
||||
|
||||
def _remove_temp_file():
|
||||
for f in _temp_files:
|
||||
os.remove(f)
|
||||
try:
|
||||
os.remove(f)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def _create_temp_file(content=""):
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, call
|
||||
from copy import deepcopy
|
||||
from unittest.mock import Mock
|
||||
|
||||
from kubernetes.dynamic.resource import ResourceInstance
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import (
|
||||
perform_action,
|
||||
@@ -24,30 +27,109 @@ definition = {
|
||||
},
|
||||
}
|
||||
|
||||
modified_def = deepcopy(definition)
|
||||
modified_def["metadata"]["labels"]["environment"] = "testing"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params, expected",
|
||||
"action, params, existing, instance, expected",
|
||||
[
|
||||
({"state": "absent"}, call.__setitem__("method", "delete")),
|
||||
({"apply": True}, call.__setitem__("method", "apply")),
|
||||
({"force": True}, call.__setitem__("method", "replace")),
|
||||
({"apply": False}, call.__setitem__("method", "update")),
|
||||
({}, call.__setitem__("method", "update")),
|
||||
(
|
||||
"delete",
|
||||
{"state": "absent"},
|
||||
{},
|
||||
{},
|
||||
{"changed": False, "method": "delete", "result": {}},
|
||||
),
|
||||
(
|
||||
"delete",
|
||||
{"state": "absent"},
|
||||
definition,
|
||||
{"kind": "Status"},
|
||||
{"changed": True, "method": "delete", "result": {"kind": "Status"}},
|
||||
),
|
||||
(
|
||||
"apply",
|
||||
{"apply": "yes"},
|
||||
{},
|
||||
definition,
|
||||
{"changed": True, "method": "apply", "result": definition},
|
||||
),
|
||||
(
|
||||
"create",
|
||||
{"state": "patched"},
|
||||
{},
|
||||
{},
|
||||
{
|
||||
"changed": False,
|
||||
"result": {},
|
||||
"warnings": [
|
||||
"resource 'kind=Pod,name=foo' was not found but will not be created as 'state' parameter has been set to 'patched'"
|
||||
],
|
||||
},
|
||||
),
|
||||
(
|
||||
"create",
|
||||
{},
|
||||
{},
|
||||
definition,
|
||||
{"changed": True, "method": "create", "result": definition},
|
||||
),
|
||||
(
|
||||
"replace",
|
||||
{"force": "yes"},
|
||||
definition,
|
||||
definition,
|
||||
{"changed": False, "method": "replace", "result": definition},
|
||||
),
|
||||
(
|
||||
"replace",
|
||||
{"force": "yes"},
|
||||
definition,
|
||||
modified_def,
|
||||
{"changed": True, "method": "replace", "result": modified_def},
|
||||
),
|
||||
(
|
||||
"update",
|
||||
{},
|
||||
definition,
|
||||
definition,
|
||||
{"changed": False, "method": "update", "result": definition},
|
||||
),
|
||||
(
|
||||
"update",
|
||||
{},
|
||||
definition,
|
||||
modified_def,
|
||||
{"changed": True, "method": "update", "result": modified_def},
|
||||
),
|
||||
(
|
||||
"create",
|
||||
{"label_selectors": ["app=foo"]},
|
||||
{},
|
||||
definition,
|
||||
{
|
||||
"changed": False,
|
||||
"msg": "resource 'kind=Pod,name=foo,namespace=foo' filtered by label_selectors.",
|
||||
},
|
||||
),
|
||||
(
|
||||
"create",
|
||||
{"label_selectors": ["app=nginx"]},
|
||||
{},
|
||||
definition,
|
||||
{"changed": True, "method": "create", "result": definition},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_perform_action(params, expected):
|
||||
module = MagicMock()
|
||||
module.params = params
|
||||
def test_perform_action(action, params, existing, instance, expected):
|
||||
svc = Mock()
|
||||
svc.find_resource.return_value = Mock(
|
||||
kind=definition["kind"], group_version=definition["apiVersion"]
|
||||
)
|
||||
svc.retrieve.return_value = ResourceInstance(None, existing) if existing else None
|
||||
spec = {action + ".return_value": instance}
|
||||
svc.configure_mock(**spec)
|
||||
|
||||
result = perform_action(MagicMock(), definition, module.params)
|
||||
result.assert_has_calls([expected], any_order=True)
|
||||
|
||||
|
||||
def test_perform_action_create():
|
||||
spec = {"retrieve.side_effect": [{}]}
|
||||
svc = MagicMock(**spec)
|
||||
module = MagicMock()
|
||||
module.params = {}
|
||||
|
||||
result = perform_action(svc, definition, module.params)
|
||||
result.assert_has_calls([call.__setitem__("method", "create")], any_order=True)
|
||||
result = perform_action(svc, definition, params)
|
||||
assert expected.items() <= result.items()
|
||||
|
||||
@@ -5,6 +5,7 @@ from kubernetes.dynamic.resource import ResourceInstance, Resource
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
diff_objects,
|
||||
)
|
||||
|
||||
from kubernetes.dynamic.exceptions import NotFoundError
|
||||
@@ -59,16 +60,14 @@ def mock_pod_updated_resource_instance():
|
||||
|
||||
|
||||
def test_diff_objects_no_diff():
|
||||
svc = K8sService(Mock(), Mock())
|
||||
match, diff = svc.diff_objects(pod_definition, pod_definition)
|
||||
match, diff = diff_objects(pod_definition, pod_definition)
|
||||
|
||||
assert match is True
|
||||
assert diff == {}
|
||||
|
||||
|
||||
def test_diff_objects_meta_diff():
|
||||
svc = K8sService(Mock(), Mock())
|
||||
match, diff = svc.diff_objects(pod_definition, pod_definition_updated)
|
||||
match, diff = diff_objects(pod_definition, pod_definition_updated)
|
||||
|
||||
assert match is False
|
||||
assert diff["before"] == {
|
||||
@@ -98,8 +97,7 @@ def test_diff_objects_spec_diff():
|
||||
]
|
||||
},
|
||||
}
|
||||
svc = K8sService(Mock(), Mock())
|
||||
match, diff = svc.diff_objects(pod_definition, pod_definition_updated)
|
||||
match, diff = diff_objects(pod_definition, pod_definition_updated)
|
||||
|
||||
assert match is False
|
||||
assert diff["before"]["spec"] == pod_definition["spec"]
|
||||
@@ -110,7 +108,7 @@ def test_find_resource():
|
||||
mock_pod_resource = Resource(
|
||||
api_version="v1", kind="Pod", namespaced=False, preferred=True, prefix="api"
|
||||
)
|
||||
spec = {"resources.get.side_effect": [mock_pod_resource]}
|
||||
spec = {"resource.return_value": mock_pod_resource}
|
||||
client = Mock(**spec)
|
||||
svc = K8sService(client, Mock())
|
||||
resource = svc.find_resource("Pod", "v1")
|
||||
@@ -122,38 +120,45 @@ def test_find_resource():
|
||||
def test_service_delete_existing_resource(mock_pod_resource_instance):
|
||||
spec = {"delete.side_effect": [mock_pod_resource_instance]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
module = Mock(
|
||||
params={"delete_options": {"gracePeriodSeconds": 2}}, check_mode=False
|
||||
)
|
||||
resource = Mock()
|
||||
svc = K8sService(client, module)
|
||||
results = svc.delete(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
result = svc.delete(resource, pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is True
|
||||
assert results["result"] == pod_definition
|
||||
assert isinstance(result, dict)
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
client.delete.assert_called_with(
|
||||
resource,
|
||||
name=pod_definition["metadata"]["name"],
|
||||
namespace=pod_definition["metadata"]["namespace"],
|
||||
body={"apiVersion": "v1", "kind": "DeleteOptions", "gracePeriodSeconds": 2},
|
||||
)
|
||||
|
||||
|
||||
def test_service_delete_no_existing_resource():
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(Mock(), module)
|
||||
results = svc.delete(Mock(), pod_definition)
|
||||
client = Mock()
|
||||
client.delete.return_value = mock_pod_resource_instance
|
||||
svc = K8sService(client, module)
|
||||
result = svc.delete(Mock(), pod_definition)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is False
|
||||
assert results["result"] == {}
|
||||
assert result == {}
|
||||
client.delete.assert_not_called()
|
||||
|
||||
|
||||
def test_service_delete_existing_resource_check_mode(mock_pod_resource_instance):
|
||||
module = Mock()
|
||||
module.params = {"wait": False}
|
||||
module.check_mode = True
|
||||
svc = K8sService(Mock(), module)
|
||||
results = svc.delete(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
module = Mock(params={}, check_mode=True)
|
||||
client = Mock(dry_run=False)
|
||||
client.delete.return_value = mock_pod_resource_instance
|
||||
svc = K8sService(client, module)
|
||||
result = svc.delete(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is True
|
||||
assert result == {}
|
||||
client.delete.assert_not_called()
|
||||
|
||||
|
||||
def test_service_create_resource(mock_pod_resource_instance):
|
||||
@@ -163,25 +168,20 @@ def test_service_create_resource(mock_pod_resource_instance):
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.create(Mock(), pod_definition)
|
||||
result = svc.create(Mock(), pod_definition)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is True
|
||||
assert results["result"] == pod_definition
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
|
||||
|
||||
def test_service_create_resource_check_mode():
|
||||
client = Mock()
|
||||
client.dry_run = False
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = True
|
||||
client = Mock(dry_run=False)
|
||||
client.create.return_value = mock_pod_resource_instance
|
||||
module = Mock(params={}, check_mode=True)
|
||||
svc = K8sService(client, module)
|
||||
results = svc.create(Mock(), pod_definition)
|
||||
result = svc.create(Mock(), pod_definition)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is True
|
||||
assert results["result"] == pod_definition
|
||||
assert result == pod_definition
|
||||
client.create.assert_not_called()
|
||||
|
||||
|
||||
def test_service_retrieve_existing_resource(mock_pod_resource_instance):
|
||||
@@ -227,117 +227,39 @@ def test_create_project_request():
|
||||
|
||||
|
||||
def test_service_apply_existing_resource(mock_pod_resource_instance):
|
||||
spec = {"apply.side_effect": [ResourceInstance(None, pod_definition_updated)]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {"apply": True}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.apply(Mock(), pod_definition_updated, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is True
|
||||
assert results["diff"] is not {}
|
||||
assert results["result"] == pod_definition_updated
|
||||
|
||||
|
||||
def test_service_apply_existing_resource_no_diff(mock_pod_resource_instance):
|
||||
spec = {"apply.side_effect": [mock_pod_resource_instance]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {"apply": True}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.apply(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
result = svc.apply(Mock(), pod_definition_updated, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is False
|
||||
assert results["diff"] == {}
|
||||
assert results["result"] == pod_definition
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
|
||||
|
||||
def test_service_apply_existing_resource_no_apply(mock_pod_resource_instance):
|
||||
spec = {"apply.side_effect": [mock_pod_resource_instance]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {"apply": False}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.apply(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is False
|
||||
assert results["result"] == {}
|
||||
|
||||
|
||||
def test_service_replace_existing_resource_no_diff(mock_pod_resource_instance):
|
||||
def test_service_replace_existing_resource(mock_pod_resource_instance):
|
||||
spec = {"replace.side_effect": [mock_pod_resource_instance]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is False
|
||||
assert results["diff"] == {}
|
||||
assert results["result"] == pod_definition
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
|
||||
|
||||
def test_service_replace_existing_resource(
|
||||
mock_pod_resource_instance, mock_pod_updated_resource_instance
|
||||
):
|
||||
spec = {"replace.side_effect": [mock_pod_updated_resource_instance]}
|
||||
def test_service_update_existing_resource(mock_pod_resource_instance):
|
||||
spec = {"replace.side_effect": [mock_pod_resource_instance]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.replace(Mock(), pod_definition_updated, mock_pod_resource_instance)
|
||||
result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is True
|
||||
assert results["result"] == pod_definition_updated
|
||||
assert results["diff"] != {}
|
||||
assert results["diff"]["before"] is not {}
|
||||
assert results["diff"]["after"] is not {}
|
||||
|
||||
|
||||
def test_service_update_existing_resource(
|
||||
mock_pod_resource_instance, mock_pod_updated_resource_instance
|
||||
):
|
||||
spec = {"replace.side_effect": [mock_pod_updated_resource_instance]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.replace(Mock(), pod_definition_updated, mock_pod_resource_instance)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is True
|
||||
assert results["result"] == pod_definition_updated
|
||||
assert results["diff"] != {}
|
||||
assert results["diff"]["before"] is not {}
|
||||
assert results["diff"]["after"] is not {}
|
||||
|
||||
|
||||
def test_service_update_existing_resource_no_diff(mock_pod_updated_resource_instance):
|
||||
spec = {"replace.side_effect": [mock_pod_updated_resource_instance]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
results = svc.replace(
|
||||
Mock(), pod_definition_updated, mock_pod_updated_resource_instance
|
||||
)
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results["changed"] is False
|
||||
assert results["result"] == pod_definition_updated
|
||||
assert results["diff"] == {}
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
|
||||
|
||||
def test_service_find(mock_pod_resource_instance):
|
||||
|
||||
@@ -89,7 +89,7 @@ def test_waiter_waits_for_missing_resource():
|
||||
namespace=RESOURCES[0]["metadata"].get("namespace"),
|
||||
)
|
||||
assert result is False
|
||||
assert instance is None
|
||||
assert instance == {}
|
||||
assert abs(elapsed - 3) <= 1
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user