Allow user to specify a custom condition when waiting (#52185)
This commit is contained in:
parent
b979b26a74
commit
65424dd614
3 changed files with 88 additions and 7 deletions
|
@ -66,6 +66,14 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
strict=dict(type='bool', default=True)
|
||||
)
|
||||
|
||||
@property
|
||||
def condition_spec(self):
|
||||
return dict(
|
||||
type=dict(),
|
||||
status=dict(default=True, choices=[True, False, "Unknown"]),
|
||||
reason=dict()
|
||||
)
|
||||
|
||||
@property
|
||||
def argspec(self):
|
||||
argument_spec = copy.deepcopy(COMMON_ARG_SPEC)
|
||||
|
@ -73,6 +81,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
argument_spec['merge_type'] = dict(type='list', choices=['json', 'merge', 'strategic-merge'])
|
||||
argument_spec['wait'] = dict(type='bool', default=False)
|
||||
argument_spec['wait_timeout'] = dict(type='int', default=120)
|
||||
argument_spec['wait_condition'] = dict(type='dict', default=None, options=self.condition_spec)
|
||||
argument_spec['validate'] = dict(type='dict', default=None, options=self.validate_spec)
|
||||
argument_spec['append_hash'] = dict(type='bool', default=False)
|
||||
return argument_spec
|
||||
|
@ -211,6 +220,9 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
existing = None
|
||||
wait = self.params.get('wait')
|
||||
wait_timeout = self.params.get('wait_timeout')
|
||||
wait_condition = None
|
||||
if self.params.get('wait_condition') and self.params['wait_condition'].get('type'):
|
||||
wait_condition = self.params['wait_condition']
|
||||
|
||||
self.remove_aliases()
|
||||
|
||||
|
@ -280,7 +292,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
success = True
|
||||
result['result'] = k8s_obj
|
||||
if wait and not self.check_mode:
|
||||
success, result['result'], result['duration'] = self.wait(resource, definition, wait_timeout)
|
||||
success, result['result'], result['duration'] = self.wait(resource, definition, wait_timeout, condition=wait_condition)
|
||||
result['changed'] = True
|
||||
result['method'] = 'create'
|
||||
if not success:
|
||||
|
@ -305,7 +317,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
success = True
|
||||
result['result'] = k8s_obj
|
||||
if wait:
|
||||
success, result['result'], result['duration'] = self.wait(resource, definition, wait_timeout)
|
||||
success, result['result'], result['duration'] = self.wait(resource, definition, wait_timeout, condition=wait_condition)
|
||||
match, diffs = self.diff_objects(existing.to_dict(), result['result'].to_dict())
|
||||
result['changed'] = not match
|
||||
result['method'] = 'replace'
|
||||
|
@ -333,7 +345,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
success = True
|
||||
result['result'] = k8s_obj
|
||||
if wait:
|
||||
success, result['result'], result['duration'] = self.wait(resource, definition, wait_timeout)
|
||||
success, result['result'], result['duration'] = self.wait(resource, definition, wait_timeout, condition=wait_condition)
|
||||
match, diffs = self.diff_objects(existing.to_dict(), result['result'])
|
||||
result['result'] = k8s_obj
|
||||
result['changed'] = not match
|
||||
|
@ -398,7 +410,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
response = response.to_dict()
|
||||
return False, response, _wait_for_elapsed()
|
||||
|
||||
def wait(self, resource, definition, timeout, state='present'):
|
||||
def wait(self, resource, definition, timeout, state='present', condition=None):
|
||||
|
||||
def _deployment_ready(deployment):
|
||||
# FIXME: frustratingly bool(deployment.status) is True even if status is empty
|
||||
|
@ -416,6 +428,23 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
daemonset.status.numberReady == daemonset.status.desiredNumberScheduled and
|
||||
daemonset.status.observedGeneration == daemonset.metadata.generation)
|
||||
|
||||
def _custom_condition(resource):
|
||||
if not resource.status or not resource.status.conditions:
|
||||
return False
|
||||
match = [x for x in resource.status.conditions if x.type == condition['type']]
|
||||
if not match:
|
||||
return False
|
||||
# There should never be more than one condition of a specific type
|
||||
match = match[0]
|
||||
if match.status == 'Unknown':
|
||||
return False
|
||||
status = True if match.status == 'True' else False
|
||||
if status == condition['status']:
|
||||
if condition.get('reason'):
|
||||
return match.reason == condition['reason']
|
||||
return True
|
||||
return False
|
||||
|
||||
def _resource_absent(resource):
|
||||
return not resource
|
||||
|
||||
|
@ -425,8 +454,10 @@ class KubernetesRawModule(KubernetesAnsibleModule):
|
|||
Pod=_pod_ready
|
||||
)
|
||||
kind = definition['kind']
|
||||
if state == 'present':
|
||||
predicate = waiter.get(kind, lambda x: True)
|
||||
if state == 'present' and not condition:
|
||||
predicate = waiter.get(kind, lambda x: x)
|
||||
elif state == 'present' and condition:
|
||||
predicate = _custom_condition
|
||||
else:
|
||||
predicate = _resource_absent
|
||||
return self._wait_for(resource, definition['metadata']['name'], definition['metadata'].get('namespace'), predicate, timeout, state)
|
||||
|
|
|
@ -64,7 +64,7 @@ options:
|
|||
- Whether to wait for certain resource kinds to end up in the desired state. By default the module exits once Kubernetes has
|
||||
received the request
|
||||
- Implemented for C(state=present) for C(Deployment), C(DaemonSet) and C(Pod), and for C(state=absent) for all resource kinds.
|
||||
- For resource kinds without an implementation, C(wait) returns immediately.
|
||||
- For resource kinds without an implementation, C(wait) returns immediately unless C(wait_condition) is set.
|
||||
default: no
|
||||
type: bool
|
||||
version_added: "2.8"
|
||||
|
@ -73,6 +73,31 @@ options:
|
|||
- How long in seconds to wait for the resource to end up in the desired state. Ignored if C(wait) is not set.
|
||||
default: 120
|
||||
version_added: "2.8"
|
||||
wait_condition:
|
||||
description:
|
||||
- Specifies a custom condition on the status to wait for. Ignored if C(wait) is not set or is set to False.
|
||||
suboptions:
|
||||
type:
|
||||
description:
|
||||
- The type of condition to wait for. For example, the C(Pod) resource will set the C(Ready) condition (among others)
|
||||
- Required if you are specifying a C(wait_condition). If left empty, the C(wait_condition) field will be ignored.
|
||||
- The possible types for a condition are specific to each resource type in Kubernetes. See the API documentation of the status field
|
||||
for a given resource to see possible choices.
|
||||
status:
|
||||
description:
|
||||
- The value of the status field in your desired condition.
|
||||
- For example, if a C(Deployment) is paused, the C(Progressing) C(type) will have the C(Unknown) status.
|
||||
choices:
|
||||
- True
|
||||
- False
|
||||
- Unknown
|
||||
reason:
|
||||
description:
|
||||
- The value of the reason field in your desired condition
|
||||
- For example, if a C(Deployment) is paused, The C(Progressing) c(type) will have the C(DeploymentPaused) reason.
|
||||
- The possible reasons in a condition are specific to each resource type in Kubernetes. See the API documentation of the status field
|
||||
for a given resource to see possible choices.
|
||||
version_added: "2.8"
|
||||
validate:
|
||||
description:
|
||||
- how (if at all) to validate the resource definition against the kubernetes schema.
|
||||
|
|
|
@ -242,6 +242,31 @@
|
|||
- deploy.result.status.availableReplicas == deploy.result.status.replicas
|
||||
- updated_deploy_pods.resources[0].spec.containers[0].image.endswith(":2")
|
||||
|
||||
- name: pause a deployment
|
||||
k8s:
|
||||
definition:
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: wait-deploy
|
||||
namespace: "{{ wait_namespace }}"
|
||||
spec:
|
||||
paused: True
|
||||
wait: yes
|
||||
wait_condition:
|
||||
type: Progressing
|
||||
status: Unknown
|
||||
reason: DeploymentPaused
|
||||
register: pause_deploy
|
||||
|
||||
- name: check that paused deployment wait worked
|
||||
assert:
|
||||
that:
|
||||
- condition.reason == "DeploymentPaused"
|
||||
- condition.status == "Unknown"
|
||||
vars:
|
||||
condition: '{{ pause_deploy.result.status.conditions | selectattr("type", "Progressing")).0 }}'
|
||||
|
||||
- name: add a service based on the deployment
|
||||
k8s:
|
||||
definition:
|
||||
|
|
Loading…
Reference in a new issue