mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-06 13:02:37 +00:00
Add new waiter (#306)
Add new waiter SUMMARY This refactors the waiter logic from common.py into a separate module. ISSUE TYPE COMPONENT NAME ADDITIONAL INFORMATION Reviewed-by: None <None> Reviewed-by: Alina Buzachis <None> Reviewed-by: None <None>
This commit is contained in:
@@ -1,11 +1,34 @@
|
||||
---
|
||||
kind: Namespace
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: test-1
|
||||
---
|
||||
kind: Pod
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: foo
|
||||
namespace: bar
|
||||
name: pod-1
|
||||
namespace: test-1
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
---
|
||||
kind: ConfigMap
|
||||
kind: PodList
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: foo
|
||||
namespace: bar
|
||||
metadata: {}
|
||||
items:
|
||||
- kind: Pod
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: pod-1
|
||||
namespace: test-1
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
---
|
||||
kind: ConfigMapList
|
||||
apiVersion: v1
|
||||
metadata: {}
|
||||
items: []
|
||||
|
||||
48
tests/unit/module_utils/fixtures/deployments.yml
Normal file
48
tests/unit/module_utils/fixtures/deployments.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: deploy-1
|
||||
namespace: test-1
|
||||
generation: 1
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: foo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: foo
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
status:
|
||||
availableReplicas: 2
|
||||
replicas: 2
|
||||
observedGeneration: 1
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: deploy-2
|
||||
namespace: test-1
|
||||
generation: 1
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: foo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: foo
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
status:
|
||||
availableReplicas: 1
|
||||
replicas: 2
|
||||
observedGeneration: 1
|
||||
63
tests/unit/module_utils/fixtures/pods.yml
Normal file
63
tests/unit/module_utils/fixtures/pods.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
kind: Pod
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
namespace: test-1
|
||||
name: pod-1
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
status:
|
||||
containerStatuses:
|
||||
- name: busybox
|
||||
ready: true
|
||||
conditions:
|
||||
- type: "www.example.com/gate"
|
||||
status: "True"
|
||||
---
|
||||
kind: Pod
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
namespace: test-1
|
||||
name: pod-2
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
---
|
||||
kind: Pod
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
namespace: test-1
|
||||
name: pod-3
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
status:
|
||||
phase: Pending
|
||||
conditions:
|
||||
- type: "www.example.com/gate"
|
||||
status: "Unknown"
|
||||
containerStatuses:
|
||||
- name: busybox
|
||||
ready: true
|
||||
---
|
||||
kind: Pod
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
namespace: test-1
|
||||
name: pod-4
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: busybox
|
||||
status:
|
||||
phase: Pending
|
||||
conditions:
|
||||
- type: "www.example.com/other"
|
||||
status: "Unknown"
|
||||
containerStatuses:
|
||||
- name: busybox
|
||||
ready: true
|
||||
@@ -30,9 +30,9 @@ def test_create_definitions_loads_from_file():
|
||||
current = Path(os.path.dirname(os.path.abspath(__file__)))
|
||||
params = {"src": current / "fixtures/definitions.yml"}
|
||||
results = create_definitions(params)
|
||||
assert len(results) == 2
|
||||
assert results[0].kind == "Pod"
|
||||
assert results[1].kind == "ConfigMap"
|
||||
assert len(results) == 3
|
||||
assert results[0].kind == "Namespace"
|
||||
assert results[1].kind == "Pod"
|
||||
|
||||
|
||||
def test_create_definitions_loads_from_params():
|
||||
@@ -160,8 +160,8 @@ def test_from_yaml_loads_dictionary():
|
||||
def test_from_file_loads_definitions():
|
||||
current = Path(os.path.dirname(os.path.abspath(__file__)))
|
||||
result = list(from_file(current / "fixtures/definitions.yml"))
|
||||
assert result[0]["kind"] == "Pod"
|
||||
assert result[1]["kind"] == "ConfigMap"
|
||||
assert result[0]["kind"] == "Namespace"
|
||||
assert result[1]["kind"] == "Pod"
|
||||
|
||||
|
||||
def test_flatten_list_kind_flattens():
|
||||
|
||||
114
tests/unit/module_utils/test_waiter.py
Normal file
114
tests/unit/module_utils/test_waiter.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from kubernetes.dynamic.resource import ResourceInstance
|
||||
from kubernetes.dynamic.exceptions import NotFoundError
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
|
||||
clock,
|
||||
custom_condition,
|
||||
deployment_ready,
|
||||
DummyWaiter,
|
||||
exists,
|
||||
get_waiter,
|
||||
pod_ready,
|
||||
resource_absent,
|
||||
Waiter,
|
||||
)
|
||||
|
||||
|
||||
def resources(filepath):
|
||||
current = Path(os.path.dirname(os.path.abspath(__file__)))
|
||||
with open(current / filepath) as fp:
|
||||
return [ResourceInstance(None, d) for d in yaml.safe_load_all(fp)]
|
||||
|
||||
|
||||
RESOURCES = resources("fixtures/definitions.yml")
|
||||
PODS = resources("fixtures/pods.yml")
|
||||
DEPLOYMENTS = resources("fixtures/deployments.yml")
|
||||
|
||||
|
||||
def test_clock_times_out():
|
||||
start = time.monotonic()
|
||||
for x in clock(5, 1):
|
||||
pass
|
||||
elapsed = int(time.monotonic() - start)
|
||||
assert x == 5
|
||||
assert 5 <= elapsed <= 6
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"resource,expected",
|
||||
zip(RESOURCES + [None, {}], [True, True, True, False, False, False]),
|
||||
)
|
||||
def test_exists_and_absent_checks_for_existence(resource, expected):
|
||||
assert exists(resource) is expected
|
||||
assert resource_absent(resource) is not expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pod,expected", zip(PODS, [True, False, True, True]))
|
||||
def test_pod_ready_checks_readiness(pod, expected):
|
||||
assert pod_ready(pod) is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pod,expected", zip(PODS, [True, False, False, False]))
|
||||
def test_custom_condition_checks_readiness(pod, expected):
|
||||
condition = {"type": "www.example.com/gate", "status": "True"}
|
||||
assert custom_condition(condition, pod) is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("deployment,expected", zip(DEPLOYMENTS, [True, False]))
|
||||
def test_deployment_ready_checks_readiness(deployment, expected):
|
||||
assert deployment_ready(deployment) is expected
|
||||
|
||||
|
||||
def test_dummywaiter_returns_resource_immediately():
|
||||
resource = {
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {"name": "foopod", "namespace": "foobar"},
|
||||
}
|
||||
result, instance, elapsed = DummyWaiter().wait(resource, 10, 100)
|
||||
assert result is True
|
||||
assert instance == resource
|
||||
assert elapsed == 0
|
||||
|
||||
|
||||
def test_waiter_waits_for_missing_resource():
|
||||
spec = {"get.side_effect": NotFoundError(Mock())}
|
||||
client = Mock(**spec)
|
||||
resource = Mock()
|
||||
result, instance, elapsed = Waiter(client, resource, exists).wait(
|
||||
RESOURCES[0], 3, 1
|
||||
)
|
||||
assert result is False
|
||||
assert instance is None
|
||||
assert abs(elapsed - 3) <= 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("resource,expected", zip(RESOURCES, [True, True, True, False]))
|
||||
def test_waiter_waits_for_resource_to_exist(resource, expected):
|
||||
result = resource.to_dict()
|
||||
spec = {"get.side_effect": [NotFoundError(Mock()), resource, resource, resource]}
|
||||
client = Mock(**spec)
|
||||
success, instance, elapsed = Waiter(client, Mock(), exists).wait(result, 3, 1)
|
||||
assert success is expected
|
||||
assert instance == result
|
||||
assert abs(elapsed - 2) <= 1
|
||||
|
||||
|
||||
def test_get_waiter_returns_correct_waiter():
|
||||
assert get_waiter(Mock(), PODS[0]).predicate == pod_ready
|
||||
waiter = get_waiter(Mock(), PODS[0], check_mode=True)
|
||||
assert isinstance(waiter, DummyWaiter)
|
||||
assert get_waiter(Mock(), PODS[0], state="absent").predicate == resource_absent
|
||||
assert (
|
||||
get_waiter(
|
||||
Mock(), PODS[0], condition={"type": "Ready", "status": "True"}
|
||||
).predicate.func
|
||||
== custom_condition
|
||||
)
|
||||
Reference in New Issue
Block a user