Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add delete_emptydir_data to drain delete_options #322

Merged
merged 1 commit into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- k8s_drain - Adds ``delete_emptydir_data`` option to ``k8s_drain.delete_options`` to evict pods with an ``emptyDir`` volume attached (https://github.com/ansible-collections/kubernetes.core/pull/322).
20 changes: 20 additions & 0 deletions docs/kubernetes.core.k8s_drain_module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,26 @@ Parameters
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>delete_emptydir_data</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Continue even if there are pods using emptyDir (local data that will be deleted when the node is drained)</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>disable_eviction</b>
Expand Down
69 changes: 68 additions & 1 deletion molecule/default/tasks/drain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
drain_namespace: "drain"
drain_daemonset_name: "promotheus-dset"
drain_pod_name: "pod-drain"
drain_deployment_emptydir_name: "deployment-emptydir-drain"

- name: Create {{ drain_namespace }} namespace
k8s:
Expand Down Expand Up @@ -99,6 +100,61 @@
- -c
- while true;do date;sleep 5; done

- name: Create Deployment with an emptyDir volume.
k8s:
namespace: '{{ drain_namespace }}'
wait: yes
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: '{{ drain_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
Expand Down Expand Up @@ -167,14 +223,16 @@
- 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 and force options
- 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

Expand All @@ -192,13 +250,22 @@
register: _result
failed_when: _result.resources

- name: assert that emptyDir pod was deleted
k8s_info:
namespace: '{{ drain_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
Expand Down
24 changes: 20 additions & 4 deletions plugins/modules/k8s_drain.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
- Ignore DaemonSet-managed pods.
type: bool
default: False
delete_emptydir_data:
description:
- Continue even if there are pods using emptyDir (local data that will be deleted when the node is drained).
type: bool
default: False
Akasurde marked this conversation as resolved.
Show resolved Hide resolved
version_added: 2.3.0
disable_eviction:
description:
- Forces drain to use delete rather than evict.
Expand Down Expand Up @@ -138,7 +144,7 @@
pass


def filter_pods(pods, force, ignore_daemonset):
def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
k8s_kind_mirror = "kubernetes.io/config.mirror"
daemonSet, unmanaged, mirror, localStorage, to_delete = [], [], [], [], []
for pod in pods:
Expand All @@ -153,7 +159,6 @@ def filter_pods(pods, force, ignore_daemonset):
continue

# Pod with local storage cannot be deleted
# TODO: support new option delete-emptydatadir in order to allow deletion of such pod
if pod.spec.volumes and any(vol.empty_dir for vol in pod.spec.volumes):
localStorage.append((pod.metadata.namespace, pod.metadata.name))
continue
Expand Down Expand Up @@ -198,7 +203,14 @@ def filter_pods(pods, force, ignore_daemonset):
# local storage
if localStorage:
pod_names = ",".join([pod[0] + "/" + pod[1] for pod in localStorage])
errors.append("cannot delete Pods with local storage: {0}.".format(pod_names))
if not delete_emptydir_data:
errors.append(
"cannot delete Pods with local storage: {0}.".format(pod_names)
)
else:
warnings.append("Deleting Pods with local storage: {0}.".format(pod_names))
for pod in localStorage:
to_delete.append((pod[0], pod[1]))

# DaemonSet managed Pods
if daemonSet:
Expand Down Expand Up @@ -349,8 +361,11 @@ def _revert_node_patch():
# Filter pods
force = self._drain_options.get("force", False)
ignore_daemonset = self._drain_options.get("ignore_daemonsets", False)
delete_emptydir_data = self._drain_options.get(
"delete_emptydir_data", False
)
pods, warnings, errors = filter_pods(
pod_list.items, force, ignore_daemonset
pod_list.items, force, ignore_daemonset, delete_emptydir_data
)
if errors:
_revert_node_patch()
Expand Down Expand Up @@ -467,6 +482,7 @@ def argspec():
terminate_grace_period=dict(type="int"),
force=dict(type="bool", default=False),
ignore_daemonsets=dict(type="bool", default=False),
delete_emptydir_data=dict(type="bool", default=False),
disable_eviction=dict(type="bool", default=False),
wait_timeout=dict(type="int"),
wait_sleep=dict(type="int", default=5),
Expand Down