mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-03-27 05:43:02 +00:00
Add delete_emptydir_data to drain delete_options (#322)
Add delete_emptydir_data to drain delete_options
SUMMARY
Adds delete_emptydir_data option to k8s_drain.delete_options to evict pods with an emptyDir volume attached.
ISSUE TYPE
Feature Pull Request
COMPONENT NAME
k8s_drain
ADDITIONAL INFORMATION
Be gentle, this is my first pull request 😨
Basically adds the kubectl drain <node> --delete-emptydir-data feature, including tests.
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Jorn Eilander <None>
Reviewed-by: None <None>
Reviewed-by: None <None>
This commit is contained in:
@@ -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).
|
||||
@@ -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> ←</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>
|
||||
|
||||
@@ -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:
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -192,6 +250,14 @@
|
||||
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
|
||||
@@ -199,6 +265,7 @@
|
||||
delete_options:
|
||||
force: true
|
||||
ignore_daemonsets: true
|
||||
delete_emptydir_data: true
|
||||
register: drain_result
|
||||
|
||||
- name: Check idempotency
|
||||
|
||||
@@ -64,6 +64,12 @@ options:
|
||||
- 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
|
||||
version_added: 2.3.0
|
||||
disable_eviction:
|
||||
description:
|
||||
- Forces drain to use delete rather than evict.
|
||||
@@ -138,7 +144,7 @@ except ImportError:
|
||||
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:
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -349,8 +361,11 @@ class K8sDrainAnsible(object):
|
||||
# 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()
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user