mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-08 14:02:38 +00:00
updates
This commit is contained in:
3
changelogs/fragments/105-wait_property.yaml
Normal file
3
changelogs/fragments/105-wait_property.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- k8s - add new option ``wait_property`` to support ability to wait on arbitrary property (https://github.com/ansible-collections/kubernetes.core/pull/105).
|
||||||
@@ -364,6 +364,93 @@
|
|||||||
that:
|
that:
|
||||||
- short_wait_remove_pod is failed
|
- short_wait_remove_pod is failed
|
||||||
|
|
||||||
|
- name: add a simple crashing pod and wait until container is running
|
||||||
|
k8s:
|
||||||
|
definition:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: pod-crash-0
|
||||||
|
namespace: "{{ wait_namespace }}"
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: crashing-container
|
||||||
|
image: busybox
|
||||||
|
command: ['/dummy/dummy-shell', '-c', 'sleep 2000']
|
||||||
|
wait: yes
|
||||||
|
wait_timeout: 10
|
||||||
|
wait_property:
|
||||||
|
property: status.containerStatuses[*].state.running
|
||||||
|
ignore_errors: true
|
||||||
|
register: crash_pod
|
||||||
|
|
||||||
|
- name: assert that task failed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- crash_pod is failed
|
||||||
|
- crash_pod.changed
|
||||||
|
- '"Resource creation timed out" in crash_pod.msg'
|
||||||
|
|
||||||
|
- name: add a valid pod and wait until container is running
|
||||||
|
k8s:
|
||||||
|
definition:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: pod-valid-0
|
||||||
|
namespace: "{{ wait_namespace }}"
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: crashing-container
|
||||||
|
image: busybox
|
||||||
|
command: ['/bin/sh', '-c', 'sleep 10000']
|
||||||
|
wait: yes
|
||||||
|
wait_timeout: 10
|
||||||
|
wait_property:
|
||||||
|
property: status.containerStatuses[*].state.running
|
||||||
|
ignore_errors: true
|
||||||
|
register: valid_pod
|
||||||
|
|
||||||
|
- name: assert that task failed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- valid_pod is successful
|
||||||
|
- valid_pod.changed
|
||||||
|
- valid_pod.result.status.containerStatuses[0].state.running is defined
|
||||||
|
|
||||||
|
- name: create pod (waiting for container.ready set to false)
|
||||||
|
k8s:
|
||||||
|
definition:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: redis-pod
|
||||||
|
namespace: "{{ wait_namespace }}"
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: redis-container
|
||||||
|
image: redis
|
||||||
|
volumeMounts:
|
||||||
|
- name: test
|
||||||
|
mountPath: "/etc/test"
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: test
|
||||||
|
configMap:
|
||||||
|
name: redis-config
|
||||||
|
wait: yes
|
||||||
|
wait_timeout: 10
|
||||||
|
wait_property:
|
||||||
|
property: status.containerStatuses[0].ready
|
||||||
|
value: "false"
|
||||||
|
register: wait_boolean
|
||||||
|
|
||||||
|
- name: assert that pod was created but not running
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- wait_boolean.changed
|
||||||
|
- wait_boolean.result.status.phase == 'Pending'
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Remove namespace
|
- name: Remove namespace
|
||||||
k8s:
|
k8s:
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ options:
|
|||||||
- Specifies a property on the resource to wait for.
|
- Specifies a property on the resource to wait for.
|
||||||
- Ignored if C(wait) is not set or is set to I(False).
|
- Ignored if C(wait) is not set or is set to I(False).
|
||||||
type: dict
|
type: dict
|
||||||
|
version_added: '2.0.0'
|
||||||
suboptions:
|
suboptions:
|
||||||
property:
|
property:
|
||||||
type: str
|
type: str
|
||||||
@@ -80,5 +81,6 @@ options:
|
|||||||
type: str
|
type: str
|
||||||
description:
|
description:
|
||||||
- The expected value of the C(property).
|
- The expected value of the C(property).
|
||||||
|
- The value is not case-sensitive.
|
||||||
- If this is missing, we will check only that the attribute C(property) is present.
|
- If this is missing, we will check only that the attribute C(property) is present.
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -434,16 +434,8 @@ class K8sAnsibleMixin(object):
|
|||||||
def _resource_absent(resource):
|
def _resource_absent(resource):
|
||||||
return not resource
|
return not resource
|
||||||
|
|
||||||
with open("/tmp/resource.txt", "w+") as f:
|
|
||||||
import json
|
|
||||||
f.write("------- Property -------\n{}".format(json.dumps(property, indent=2)))
|
|
||||||
|
|
||||||
def _wait_for_property(resource):
|
def _wait_for_property(resource):
|
||||||
test = match_json_property(self, resource.to_dict(), property.get('property'), property.get('value', None))
|
return match_json_property(self, resource.to_dict(), property.get('property'), property.get('value', None))
|
||||||
with open("/tmp/resource.txt", "w+") as f:
|
|
||||||
import json
|
|
||||||
f.write("------- test = {}\n{}".format(test, json.dumps(resource.to_dict(), indent=2)))
|
|
||||||
return test
|
|
||||||
|
|
||||||
waiter = dict(
|
waiter = dict(
|
||||||
Deployment=_deployment_ready,
|
Deployment=_deployment_ready,
|
||||||
|
|||||||
@@ -23,11 +23,10 @@ from ansible.module_utils._text import to_native
|
|||||||
try:
|
try:
|
||||||
import jmespath
|
import jmespath
|
||||||
HAS_JMESPATH_LIB = True
|
HAS_JMESPATH_LIB = True
|
||||||
jmespath_import_exception = None
|
JMESPATH_IMP_ERR = None
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
HAS_JMESPATH_LIB = False
|
HAS_JMESPATH_LIB = False
|
||||||
jmespath_import_exception = e
|
JMESPATH_IMP_ERR = e
|
||||||
JMESPATH_IMP_ERR = traceback.format_exc()
|
|
||||||
|
|
||||||
|
|
||||||
def match_json_property(module, data, expr, value=None):
|
def match_json_property(module, data, expr, value=None):
|
||||||
@@ -44,18 +43,29 @@ def match_json_property(module, data, expr, value=None):
|
|||||||
raise err
|
raise err
|
||||||
|
|
||||||
def _match_value(buf, v):
|
def _match_value(buf, v):
|
||||||
# convert all values from bool to str and lowercase them
|
if isinstance(buf, list):
|
||||||
return v.lower() in [str(i).lower() for i in buf]
|
# convert all values from bool to str and lowercase them
|
||||||
|
return v.lower() in [str(i).lower() for i in buf]
|
||||||
|
elif isinstance(buf, str):
|
||||||
|
return v.lower() == content.lower()
|
||||||
|
elif isinstance(buf, bool):
|
||||||
|
return v.lower() == str(content).lower()
|
||||||
|
else:
|
||||||
|
# unable to test single value against dict
|
||||||
|
return False
|
||||||
|
|
||||||
if not HAS_JMESPATH_LIB:
|
if not HAS_JMESPATH_LIB:
|
||||||
_raise_or_fail(jmespath_import_exception, msg=missing_required_lib('jmespath'), exception=JMESPATH_IMP_ERR)
|
_raise_or_fail(JMESPATH_IMP_ERR, msg=missing_required_lib('jmespath'))
|
||||||
|
|
||||||
jmespath.functions.REVERSE_TYPES_MAP['string'] = jmespath.functions.REVERSE_TYPES_MAP['string'] + ('AnsibleUnicode', 'AnsibleUnsafeText', )
|
jmespath.functions.REVERSE_TYPES_MAP['string'] = jmespath.functions.REVERSE_TYPES_MAP['string'] + ('AnsibleUnicode', 'AnsibleUnsafeText', )
|
||||||
try:
|
try:
|
||||||
content = jmespath.search(expr, data)
|
content = jmespath.search(expr, data)
|
||||||
if not content:
|
with open("/tmp/play.cont", "w") as f:
|
||||||
|
f.write("{}".format(content))
|
||||||
|
if content is None or content == []:
|
||||||
return False
|
return False
|
||||||
if not value or _match_value(content, value):
|
if value is None or _match_value(content, value):
|
||||||
|
# looking for state present
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ jmespath = pytest.importorskip("jmespath")
|
|||||||
|
|
||||||
def test_property_present():
|
def test_property_present():
|
||||||
data = {
|
data = {
|
||||||
"Kind": "Pod",
|
|
||||||
"containers": [
|
"containers": [
|
||||||
{"name": "t0", "image": "nginx"},
|
{"name": "t0", "image": "nginx"},
|
||||||
{"name": "t1", "image": "python"},
|
{"name": "t1", "image": "python"},
|
||||||
@@ -37,7 +36,6 @@ def test_property_present():
|
|||||||
|
|
||||||
def test_property_value():
|
def test_property_value():
|
||||||
data = {
|
data = {
|
||||||
"Kind": "Pod",
|
|
||||||
"containers": [
|
"containers": [
|
||||||
{"name": "t0", "image": "nginx"},
|
{"name": "t0", "image": "nginx"},
|
||||||
{"name": "t1", "image": "python"},
|
{"name": "t1", "image": "python"},
|
||||||
@@ -52,7 +50,7 @@ def test_property_value():
|
|||||||
def test_boolean_value():
|
def test_boolean_value():
|
||||||
data = {
|
data = {
|
||||||
"containers": [
|
"containers": [
|
||||||
{"image": "nginx"},
|
{"image": "nginx", "poweron": False},
|
||||||
{"image": "python"},
|
{"image": "python"},
|
||||||
{"image": "mongo", "connected": True}
|
{"image": "mongo", "connected": True}
|
||||||
]
|
]
|
||||||
@@ -60,6 +58,7 @@ def test_boolean_value():
|
|||||||
assert match_json_property(None, data, "containers[*].connected", "true")
|
assert match_json_property(None, data, "containers[*].connected", "true")
|
||||||
assert match_json_property(None, data, "containers[*].connected", "True")
|
assert match_json_property(None, data, "containers[*].connected", "True")
|
||||||
assert match_json_property(None, data, "containers[*].connected", "TRUE")
|
assert match_json_property(None, data, "containers[*].connected", "TRUE")
|
||||||
|
assert match_json_property(None, data, "containers[0].poweron", "false")
|
||||||
|
|
||||||
|
|
||||||
def test_valid_expression():
|
def test_valid_expression():
|
||||||
|
|||||||
Reference in New Issue
Block a user