mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-06 04:52:37 +00:00
Add check_mode support for k8s_drain module (#1086)
SUMMARY Closes #1037 added support for check_mode Converted warnings into informational display when user has explicitly requested to delete daemontset-managed pods, unmanaged pods or pods with local storage ISSUE TYPE Feature Pull Request COMPONENT NAME k8s_drain Reviewed-by: Bianca Henderson <beeankha@gmail.com>
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
---
|
||||
test_namespace: "drain"
|
||||
k8s_wait_timeout: 400
|
||||
daemonset_name: promotheus
|
||||
deployment_name: busybox-emptydir
|
||||
pod1_name: "busybox-1"
|
||||
pod2_name: "busybox-2"
|
||||
|
||||
65
tests/integration/targets/k8s_drain/tasks/cordon.yml
Normal file
65
tests/integration/targets/k8s_drain/tasks/cordon.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
- name: Cordon node (check mode)
|
||||
k8s_drain:
|
||||
state: cordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: cordon_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: assert that module reported change while running in check_mode
|
||||
assert:
|
||||
that:
|
||||
- cordon_check_mode is changed
|
||||
|
||||
- name: Ensure the node remain schedulable (cordon run on check mode)
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
- name: Cordon node
|
||||
k8s_drain:
|
||||
state: cordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: cordon
|
||||
|
||||
- name: assert that cordon is changed
|
||||
assert:
|
||||
that:
|
||||
- cordon is changed
|
||||
|
||||
- name: Ensure the node is unschedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
|
||||
- name: Test cordon idempotency (check_mode=true)
|
||||
k8s_drain:
|
||||
state: cordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: cordon_checkmode_idempotency
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that module is idempotent while running in check mode
|
||||
assert:
|
||||
that:
|
||||
- cordon_checkmode_idempotency is not changed
|
||||
|
||||
- name: Test cordon idempotency
|
||||
k8s_drain:
|
||||
state: cordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: cordon
|
||||
|
||||
- name: assert that cordon is not changed
|
||||
assert:
|
||||
that:
|
||||
- cordon is not changed
|
||||
|
||||
- name: Get pods
|
||||
k8s_info:
|
||||
kind: Pod
|
||||
namespace: '{{ test_namespace }}'
|
||||
register: Pod
|
||||
|
||||
- name: assert that pods are running on cordoned node
|
||||
assert:
|
||||
that:
|
||||
- Pod.resources | selectattr('status.phase', 'equalto', 'Running') | selectattr('spec.nodeName', 'equalto', node_to_drain) | list | length > 0
|
||||
389
tests/integration/targets/k8s_drain/tasks/drain.yml
Normal file
389
tests/integration/targets/k8s_drain/tasks/drain.yml
Normal file
@@ -0,0 +1,389 @@
|
||||
---
|
||||
# Drain the node (Should failed)
|
||||
- name: Drain node with expected failure (check_mode=true)
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
ignore_errors: true
|
||||
register: drain_failed_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that drain failed due to DaemonSet managed Pods
|
||||
assert:
|
||||
that:
|
||||
- drain_failed_check_mode is failed
|
||||
- '"cannot delete DaemonSet-managed Pods" in drain_failed_check_mode.msg'
|
||||
- '"cannot delete Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet" in drain_failed_check_mode.msg'
|
||||
- '"cannot delete Pods with local storage" in drain_failed_check_mode.msg'
|
||||
|
||||
- name: Ensure that the node remains schedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
- name: Drain node with expected failure
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
ignore_errors: true
|
||||
register: drain_failed
|
||||
|
||||
- name: Assert that drain failed due to DaemonSet managed Pods
|
||||
assert:
|
||||
that:
|
||||
- drain_failed is failed
|
||||
- '"cannot delete DaemonSet-managed Pods" in drain_failed.msg'
|
||||
- '"cannot delete Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet" in drain_failed.msg'
|
||||
- '"cannot delete Pods with local storage" in drain_failed.msg'
|
||||
|
||||
- name: Ensure that the node remains schedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
# Drain the node ignoring non-candidate Pods
|
||||
# check_mode
|
||||
- name: Drain node using ignore_daemonsets, force, and delete_emptydir_data options (check_mode=true)
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
delete_emptydir_data: true
|
||||
wait_timeout: 0
|
||||
register: drain_force_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that module reported changed while node was not drained
|
||||
assert:
|
||||
that:
|
||||
- drain_force_check_mode is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_force_check_mode.result'
|
||||
|
||||
- name: Ensure node remains schedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
- name: Assert that running with check_mode did not delete any Pod
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- "{{ item }}"
|
||||
register: pods
|
||||
failed_when: pods.resources | length == 0
|
||||
loop:
|
||||
- drain=unmanaged-pod
|
||||
- drain=daemonset-pod
|
||||
- drain=emptyDir
|
||||
|
||||
# Apply
|
||||
- name: Drain node using ignore_daemonsets, force, and delete_emptydir_data options
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
delete_emptydir_data: true
|
||||
wait_timeout: 0
|
||||
register: drain_force
|
||||
|
||||
- name: Assert that module reported changed
|
||||
assert:
|
||||
that:
|
||||
- drain_force is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_force.result'
|
||||
|
||||
- name: Ensure node is now unschedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
|
||||
- name: Assert that unmanaged Pod were deleted
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=unmanaged-pod
|
||||
register: pods
|
||||
failed_when: pods.resources | length > 0
|
||||
|
||||
- name: Assert that Pod with local storage are not Pending
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=emptyDir
|
||||
register: pods
|
||||
failed_when: pods.resources | map(attribute='status.phase') | unique != ['Pending']
|
||||
|
||||
- name: Assert that DaemonSet-managed pod were not deleted
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=daemonset-pod
|
||||
register: pods
|
||||
failed_when: pods.resources | length == 0
|
||||
|
||||
# Idempotency
|
||||
- name: Test drain idempotency (check_mode=true)
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
delete_emptydir_data: true
|
||||
register: drain_force_idempotency_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: Validate idempotency with check_mode
|
||||
assert:
|
||||
that:
|
||||
- drain_force_idempotency_check_mode is not changed
|
||||
|
||||
- name: Ensure node remains unschedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
|
||||
- name: Assert that DaemonSet-managed pod were not deleted
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=daemonset-pod
|
||||
register: pods
|
||||
failed_when: pods.resources | length == 0
|
||||
|
||||
# Drain with disable_eviction = true
|
||||
# check_mode
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
|
||||
- name: Create once again the Pod deleted before
|
||||
k8s:
|
||||
namespace: '{{ test_namespace }}'
|
||||
wait: true
|
||||
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
|
||||
template: pod1.yml.j2
|
||||
|
||||
- name: Drain node using disable_eviction (check_mode)
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
disable_eviction: true
|
||||
terminate_grace_period: 0
|
||||
ignore_daemonsets: true
|
||||
wait_timeout: 0
|
||||
delete_emptydir_data: true
|
||||
register: disable_evict_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that node has been drained
|
||||
assert:
|
||||
that:
|
||||
- disable_evict_check_mode is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in disable_evict_check_mode.result'
|
||||
|
||||
- name: Ensure node remains schedulable (check_mode)
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
- name: Assert that unmanaged Pod were not deleted
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=unmanaged-pod
|
||||
register: pods
|
||||
failed_when: pods.resources | length == 0
|
||||
|
||||
# apply
|
||||
- name: Drain node using disable_eviction
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
disable_eviction: true
|
||||
terminate_grace_period: 0
|
||||
ignore_daemonsets: true
|
||||
wait_timeout: 0
|
||||
delete_emptydir_data: true
|
||||
register: disable_evict
|
||||
|
||||
- name: Assert that node has been drained
|
||||
assert:
|
||||
that:
|
||||
- disable_evict is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in disable_evict_check_mode.result'
|
||||
|
||||
- name: Ensure the node is unschedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
|
||||
# Drain using pod_selectors
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
|
||||
- name: Create Pod with label selector
|
||||
k8s:
|
||||
namespace: "{{ test_namespace }}"
|
||||
wait: true
|
||||
template: pod1.yml.j2
|
||||
|
||||
# check_mode
|
||||
- name: Drain the node using pod_selectors matching no Pod
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
pod_selectors:
|
||||
- drain=no_match_selector
|
||||
delete_options:
|
||||
terminate_grace_period: 0
|
||||
delete_emptydir_data: true
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
register: drain_pod_selector_no_match_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that module reported change while running in check_mode
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector_no_match_check_mode is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_pod_selector_no_match_check_mode.result'
|
||||
|
||||
- name: Ensure that the node remains schedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
- name: Validate that Pod are still running
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=unmanaged-pod
|
||||
field_selectors:
|
||||
- status.phase=Running
|
||||
register: pods
|
||||
failed_when: pods.resources | length == 0
|
||||
|
||||
# apply
|
||||
- name: Drain the node using pod_selectors matching no Pod
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
pod_selectors:
|
||||
- drain=no_match_selector
|
||||
delete_options:
|
||||
terminate_grace_period: 0
|
||||
delete_emptydir_data: true
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
register: drain_pod_selector_no_match
|
||||
|
||||
- name: Assert that node has been drained
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector_no_match is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_pod_selector_no_match.result'
|
||||
|
||||
- name: Ensure the node is unschedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
|
||||
- name: Validate that Pod are still running
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=unmanaged-pod
|
||||
field_selectors:
|
||||
- status.phase=Running
|
||||
register: pods
|
||||
failed_when: pods.resources | length == 0
|
||||
|
||||
# Drain the node using matching pod_selector
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
|
||||
# check_mode
|
||||
- name: Drain the node using matching pod_selectors
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
pod_selectors:
|
||||
- drain=unmanaged-pod
|
||||
delete_options:
|
||||
terminate_grace_period: 0
|
||||
delete_emptydir_data: true
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
register: drain_pod_selector_match_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that module reported change while running in check_mode
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector_match_check_mode is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_pod_selector_match_check_mode.result'
|
||||
|
||||
- name: Ensure that the node remains schedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
- name: Validate that Pod are still running
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=unmanaged-pod
|
||||
field_selectors:
|
||||
- status.phase=Running
|
||||
register: pods
|
||||
failed_when: pods.resources | length == 0
|
||||
|
||||
# apply
|
||||
- name: Drain the node using matching pod_selectors
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
pod_selectors:
|
||||
- drain=unmanaged-pod
|
||||
delete_options:
|
||||
terminate_grace_period: 0
|
||||
delete_emptydir_data: true
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
wait_timeout: 0
|
||||
register: drain_pod_selector_match
|
||||
|
||||
- name: Assert that node has been drained
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector_match is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_pod_selector_match.result'
|
||||
|
||||
- name: Ensure the node is unschedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
|
||||
- name: Validate that Pod are not running
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- drain=unmanaged-pod
|
||||
field_selectors:
|
||||
- status.phase=Running
|
||||
register: pods
|
||||
failed_when: pods.resources | length > 0
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
- block:
|
||||
- name: Set common facts
|
||||
set_fact:
|
||||
drain_daemonset_name: "promotheus-dset"
|
||||
drain_pod_name: "pod-drain"
|
||||
drain_deployment_emptydir_name: "deployment-emptydir-drain"
|
||||
|
||||
# It seems that the default ServiceAccount can take a bit to be created
|
||||
# right after a cluster is brought up. This can lead to the ServiceAccount
|
||||
# admission controller rejecting a Pod creation request because the
|
||||
@@ -35,407 +29,23 @@
|
||||
set_fact:
|
||||
node_to_drain: '{{ uncordoned_nodes[0] }}'
|
||||
|
||||
- name: Deploy daemonset on cluster
|
||||
- name: Create resources
|
||||
k8s:
|
||||
namespace: '{{ test_namespace }}'
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: '{{ drain_daemonset_name }}'
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
selector:
|
||||
matchLabels:
|
||||
name: prometheus-exporter
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: prometheus-exporter
|
||||
spec:
|
||||
containers:
|
||||
- name: prometheus
|
||||
image: prom/node-exporter
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
- name: Create Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet.
|
||||
k8s:
|
||||
namespace: '{{ test_namespace }}'
|
||||
wait: yes
|
||||
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: '{{ drain_pod_name }}'
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
containers:
|
||||
- name: c0
|
||||
image: busybox
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- while true;do date;sleep 5; done
|
||||
|
||||
- name: Create Deployment with an emptyDir volume.
|
||||
k8s:
|
||||
namespace: '{{ test_namespace }}'
|
||||
wait: yes
|
||||
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: '{{ drain_deployment_emptydir_name }}'
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
drain: emptyDir
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
drain: emptyDir
|
||||
spec:
|
||||
metadata:
|
||||
labels:
|
||||
drain: emptyDir
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
containers:
|
||||
- name: c0
|
||||
image: busybox
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- while true;do date;sleep 5; done
|
||||
volumeMounts:
|
||||
- mountPath: /emptydir
|
||||
name: emptydir
|
||||
volumes:
|
||||
- name: emptydir
|
||||
emptyDir: {}
|
||||
|
||||
- name: Register emptyDir Pod name
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- "drain = emptyDir"
|
||||
register: emptydir_pod_result
|
||||
failed_when:
|
||||
- emptydir_pod_result.resources | length != 1
|
||||
|
||||
- name: Cordon node
|
||||
k8s_drain:
|
||||
state: cordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: cordon
|
||||
|
||||
- name: assert that cordon is changed
|
||||
assert:
|
||||
that:
|
||||
- cordon is changed
|
||||
|
||||
- name: Test cordon idempotency
|
||||
k8s_drain:
|
||||
state: cordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: cordon
|
||||
|
||||
- name: assert that cordon is not changed
|
||||
assert:
|
||||
that:
|
||||
- cordon is not changed
|
||||
|
||||
- name: Get pods
|
||||
k8s_info:
|
||||
kind: Pod
|
||||
namespace: '{{ test_namespace }}'
|
||||
register: Pod
|
||||
|
||||
- name: assert that pods are running on cordoned node
|
||||
assert:
|
||||
that:
|
||||
- Pod.resources | selectattr('status.phase', 'equalto', 'Running') | selectattr('spec.nodeName', 'equalto', node_to_drain) | list | length > 0
|
||||
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: uncordon
|
||||
|
||||
- name: assert that uncordon is changed
|
||||
assert:
|
||||
that:
|
||||
- uncordon is changed
|
||||
|
||||
- name: Test uncordon idempotency
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: uncordon
|
||||
|
||||
- name: assert that uncordon is not changed
|
||||
assert:
|
||||
that:
|
||||
- uncordon is not changed
|
||||
|
||||
- name: Drain node
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
ignore_errors: true
|
||||
register: drain_result
|
||||
|
||||
- name: assert that drain failed due to DaemonSet managed Pods
|
||||
assert:
|
||||
that:
|
||||
- drain_result is failed
|
||||
- '"cannot delete DaemonSet-managed Pods" in drain_result.msg'
|
||||
- '"cannot delete Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet" in drain_result.msg'
|
||||
- '"cannot delete Pods with local storage" in drain_result.msg'
|
||||
|
||||
- name: Drain node using ignore_daemonsets, force, and delete_emptydir_data options
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
delete_emptydir_data: true
|
||||
wait_timeout: 0
|
||||
register: drain_result
|
||||
|
||||
- name: assert that node has been drained
|
||||
assert:
|
||||
that:
|
||||
- drain_result is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_result.result'
|
||||
|
||||
- name: assert that unmanaged pod were deleted
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
name: '{{ drain_pod_name }}'
|
||||
register: _result
|
||||
failed_when: _result.resources | length > 0
|
||||
|
||||
- name: assert that emptyDir pod was deleted
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
name: "{{ emptydir_pod_result.resources[0].metadata.name }}"
|
||||
register: _result
|
||||
failed_when: _result.resources | length != 0
|
||||
|
||||
- name: Test drain idempotency
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
delete_emptydir_data: true
|
||||
register: drain_result
|
||||
|
||||
- name: Check idempotency
|
||||
assert:
|
||||
that:
|
||||
- drain_result is not changed
|
||||
|
||||
- name: Get DaemonSet
|
||||
k8s_info:
|
||||
kind: DaemonSet
|
||||
namespace: '{{ test_namespace }}'
|
||||
name: '{{ drain_daemonset_name }}'
|
||||
register: dset_result
|
||||
|
||||
- name: assert that daemonset managed pods were not removed
|
||||
assert:
|
||||
that:
|
||||
- dset_result.resources | list | length > 0
|
||||
|
||||
# test: drain using disable_eviction=true
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
|
||||
- name: Create another Pod
|
||||
k8s:
|
||||
namespace: '{{ test_namespace }}'
|
||||
wait: yes
|
||||
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: '{{ drain_pod_name }}-01'
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
containers:
|
||||
- name: c0
|
||||
image: busybox
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- while true;do date;sleep 5; done
|
||||
volumeMounts:
|
||||
- mountPath: /emptydir
|
||||
name: emptydir
|
||||
volumes:
|
||||
- name: emptydir
|
||||
emptyDir: {}
|
||||
|
||||
- name: Drain node using disable_eviction set to yes
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
delete_options:
|
||||
force: true
|
||||
disable_eviction: yes
|
||||
terminate_grace_period: 0
|
||||
ignore_daemonsets: yes
|
||||
wait_timeout: 0
|
||||
delete_emptydir_data: true
|
||||
register: disable_evict
|
||||
|
||||
- name: assert that node has been drained
|
||||
assert:
|
||||
that:
|
||||
- disable_evict is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in disable_evict.result'
|
||||
|
||||
- name: assert that unmanaged pod were deleted
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
name: '{{ drain_pod_name }}-01'
|
||||
register: _result
|
||||
failed_when: _result.resources | length > 0
|
||||
|
||||
# test: drain using pod_selectors
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
|
||||
- name: create a Pod for test
|
||||
k8s:
|
||||
namespace: '{{ test_namespace }}'
|
||||
wait: true
|
||||
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: 'ansible-drain-pod'
|
||||
labels:
|
||||
app: ansible-drain
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
containers:
|
||||
- name: ansible-container
|
||||
image: busybox
|
||||
command:
|
||||
- '/bin/sh'
|
||||
- '-c'
|
||||
- 'while true; do echo $(date); sleep 10; done'
|
||||
namespace: "{{ test_namespace }}"
|
||||
template:
|
||||
- daemonset.yml.j2
|
||||
- deployment.yml.j2
|
||||
- pod1.yml.j2
|
||||
|
||||
- name: Drain node using pod_selectors 'app!=ansible-drain'
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
pod_selectors:
|
||||
- app!=ansible-drain
|
||||
delete_options:
|
||||
terminate_grace_period: 0
|
||||
delete_emptydir_data: true
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
register: drain_pod_selector
|
||||
- name: Test Cordon node
|
||||
ansible.builtin.include_tasks: tasks/cordon.yml
|
||||
|
||||
- name: assert that node has been drained
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector is changed
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_pod_selector.result'
|
||||
- name: Test Uncordon node
|
||||
ansible.builtin.include_tasks: tasks/uncordon.yml
|
||||
|
||||
- name: assert that pod created before is still running
|
||||
k8s_info:
|
||||
namespace: '{{ test_namespace }}'
|
||||
kind: Pod
|
||||
label_selectors:
|
||||
- app=ansible-drain
|
||||
field_selectors:
|
||||
- status.phase=Running
|
||||
register: pods
|
||||
failed_when: pods.resources == []
|
||||
|
||||
- name: Drain node using pod_selectors 'app=ansible-drain'
|
||||
k8s_drain:
|
||||
state: drain
|
||||
name: '{{ node_to_drain }}'
|
||||
pod_selectors:
|
||||
- app=ansible-drain
|
||||
delete_options:
|
||||
terminate_grace_period: 0
|
||||
force: true
|
||||
register: drain_pod_selector_equal
|
||||
|
||||
- name: assert that node was not drained
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector_equal is changed
|
||||
- '"node "+node_to_drain+" already marked unschedulable." in drain_pod_selector_equal.result'
|
||||
- '"Deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: "+test_namespace+"/ansible-drain-pod." in drain_pod_selector_equal.warnings'
|
||||
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
- name: Test drain node
|
||||
ansible.builtin.include_tasks: tasks/drain.yml
|
||||
|
||||
always:
|
||||
- name: Uncordon node
|
||||
|
||||
54
tests/integration/targets/k8s_drain/tasks/uncordon.yml
Normal file
54
tests/integration/targets/k8s_drain/tasks/uncordon.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
- name: Uncordon node (check_mode=true)
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: uncordon_check_mode
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that module reported change while running in check_mode
|
||||
assert:
|
||||
that:
|
||||
- uncordon_check_mode is changed
|
||||
|
||||
- name: Ensure the node is still unschedulable (uncordon run in check_mode)
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: uncordon
|
||||
|
||||
- name: Assert that module reported change
|
||||
assert:
|
||||
that:
|
||||
- uncordon is changed
|
||||
|
||||
- name: Ensure the node is now schedulable
|
||||
ansible.builtin.include_tasks: tasks/validate_node_status.yml
|
||||
vars:
|
||||
schedulable: true
|
||||
|
||||
- name: Test uncordon idempotency (check_mode=true)
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: uncordon_checkmode_idempotency
|
||||
check_mode: true
|
||||
|
||||
- name: assert that uncordon is not changed (idempotency with check mode)
|
||||
assert:
|
||||
that:
|
||||
- uncordon_checkmode_idempotency is not changed
|
||||
|
||||
- name: Test uncordon idempotency
|
||||
k8s_drain:
|
||||
state: uncordon
|
||||
name: '{{ node_to_drain }}'
|
||||
register: uncordon
|
||||
|
||||
- name: assert that uncordon is not changed
|
||||
assert:
|
||||
that:
|
||||
- uncordon is not changed
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Retrieve node information
|
||||
k8s_info:
|
||||
kind: Node
|
||||
name: "{{ node_to_drain }}"
|
||||
register: node_info
|
||||
|
||||
- name: Validate that node is schedulable
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- node_info.resources.0.spec.unschedulable is undefined
|
||||
when: schedulable | default('false') | bool
|
||||
|
||||
- name: Validate that node is unschedulable
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- node_info.resources.0.spec.unschedulable | bool
|
||||
when: not (schedulable | default('false') | bool)
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: '{{ daemonset_name }}'
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
selector:
|
||||
matchLabels:
|
||||
drain: daemonset-pod
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
drain: daemonset-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: prometheus
|
||||
image: prom/node-exporter
|
||||
ports:
|
||||
- containerPort: 80
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: '{{ deployment_name }}'
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
drain: emptyDir
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
drain: emptyDir
|
||||
spec:
|
||||
metadata:
|
||||
labels:
|
||||
drain: emptyDir
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
containers:
|
||||
- name: busybox-container
|
||||
image: busybox
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- while true;do date;sleep 5; done
|
||||
volumeMounts:
|
||||
- mountPath: /emptydir
|
||||
name: emptydir
|
||||
volumes:
|
||||
- name: emptydir
|
||||
emptyDir: {}
|
||||
24
tests/integration/targets/k8s_drain/templates/pod1.yml.j2
Normal file
24
tests/integration/targets/k8s_drain/templates/pod1.yml.j2
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: '{{ pod1_name }}'
|
||||
labels:
|
||||
drain: unmanaged-pod
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchFields:
|
||||
- key: metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ node_to_drain }}'
|
||||
containers:
|
||||
- name: busybox-container
|
||||
image: busybox
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- while true;do date;sleep 5; done
|
||||
Reference in New Issue
Block a user