mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-08 05:52:37 +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).
|
||||||
@@ -137,6 +137,26 @@ Parameters
|
|||||||
<div>Specify options to delete pods.</div>
|
<div>Specify options to delete pods.</div>
|
||||||
<div>This option has effect only when <code>state</code> is set to <em>drain</em>.</div>
|
<div>This option has effect only when <code>state</code> is set to <em>drain</em>.</div>
|
||||||
</td>
|
</td>
|
||||||
|
</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>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="elbow-placeholder"></td>
|
<td class="elbow-placeholder"></td>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
drain_namespace: "drain"
|
drain_namespace: "drain"
|
||||||
drain_daemonset_name: "promotheus-dset"
|
drain_daemonset_name: "promotheus-dset"
|
||||||
drain_pod_name: "pod-drain"
|
drain_pod_name: "pod-drain"
|
||||||
|
drain_deployment_emptydir_name: "deployment-emptydir-drain"
|
||||||
|
|
||||||
- name: Create {{ drain_namespace }} namespace
|
- name: Create {{ drain_namespace }} namespace
|
||||||
k8s:
|
k8s:
|
||||||
@@ -99,6 +100,61 @@
|
|||||||
- -c
|
- -c
|
||||||
- while true;do date;sleep 5; done
|
- 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
|
- name: Cordon node
|
||||||
k8s_drain:
|
k8s_drain:
|
||||||
state: cordon
|
state: cordon
|
||||||
@@ -167,14 +223,16 @@
|
|||||||
- drain_result is failed
|
- drain_result is failed
|
||||||
- '"cannot delete DaemonSet-managed Pods" in drain_result.msg'
|
- '"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 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:
|
k8s_drain:
|
||||||
state: drain
|
state: drain
|
||||||
name: '{{ node_to_drain }}'
|
name: '{{ node_to_drain }}'
|
||||||
delete_options:
|
delete_options:
|
||||||
force: true
|
force: true
|
||||||
ignore_daemonsets: true
|
ignore_daemonsets: true
|
||||||
|
delete_emptydir_data: true
|
||||||
wait_timeout: 0
|
wait_timeout: 0
|
||||||
register: drain_result
|
register: drain_result
|
||||||
|
|
||||||
@@ -192,6 +250,14 @@
|
|||||||
register: _result
|
register: _result
|
||||||
failed_when: _result.resources
|
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
|
- name: Test drain idempotency
|
||||||
k8s_drain:
|
k8s_drain:
|
||||||
state: drain
|
state: drain
|
||||||
@@ -199,6 +265,7 @@
|
|||||||
delete_options:
|
delete_options:
|
||||||
force: true
|
force: true
|
||||||
ignore_daemonsets: true
|
ignore_daemonsets: true
|
||||||
|
delete_emptydir_data: true
|
||||||
register: drain_result
|
register: drain_result
|
||||||
|
|
||||||
- name: Check idempotency
|
- name: Check idempotency
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ options:
|
|||||||
- Ignore DaemonSet-managed pods.
|
- Ignore DaemonSet-managed pods.
|
||||||
type: bool
|
type: bool
|
||||||
default: False
|
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:
|
disable_eviction:
|
||||||
description:
|
description:
|
||||||
- Forces drain to use delete rather than evict.
|
- Forces drain to use delete rather than evict.
|
||||||
@@ -138,7 +144,7 @@ except ImportError:
|
|||||||
pass
|
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"
|
k8s_kind_mirror = "kubernetes.io/config.mirror"
|
||||||
daemonSet, unmanaged, mirror, localStorage, to_delete = [], [], [], [], []
|
daemonSet, unmanaged, mirror, localStorage, to_delete = [], [], [], [], []
|
||||||
for pod in pods:
|
for pod in pods:
|
||||||
@@ -153,7 +159,6 @@ def filter_pods(pods, force, ignore_daemonset):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Pod with local storage cannot be deleted
|
# 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):
|
if pod.spec.volumes and any(vol.empty_dir for vol in pod.spec.volumes):
|
||||||
localStorage.append((pod.metadata.namespace, pod.metadata.name))
|
localStorage.append((pod.metadata.namespace, pod.metadata.name))
|
||||||
continue
|
continue
|
||||||
@@ -198,7 +203,14 @@ def filter_pods(pods, force, ignore_daemonset):
|
|||||||
# local storage
|
# local storage
|
||||||
if localStorage:
|
if localStorage:
|
||||||
pod_names = ",".join([pod[0] + "/" + pod[1] for pod in 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
|
# DaemonSet managed Pods
|
||||||
if daemonSet:
|
if daemonSet:
|
||||||
@@ -349,8 +361,11 @@ class K8sDrainAnsible(object):
|
|||||||
# Filter pods
|
# Filter pods
|
||||||
force = self._drain_options.get("force", False)
|
force = self._drain_options.get("force", False)
|
||||||
ignore_daemonset = self._drain_options.get("ignore_daemonsets", 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(
|
pods, warnings, errors = filter_pods(
|
||||||
pod_list.items, force, ignore_daemonset
|
pod_list.items, force, ignore_daemonset, delete_emptydir_data
|
||||||
)
|
)
|
||||||
if errors:
|
if errors:
|
||||||
_revert_node_patch()
|
_revert_node_patch()
|
||||||
@@ -467,6 +482,7 @@ def argspec():
|
|||||||
terminate_grace_period=dict(type="int"),
|
terminate_grace_period=dict(type="int"),
|
||||||
force=dict(type="bool", default=False),
|
force=dict(type="bool", default=False),
|
||||||
ignore_daemonsets=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),
|
disable_eviction=dict(type="bool", default=False),
|
||||||
wait_timeout=dict(type="int"),
|
wait_timeout=dict(type="int"),
|
||||||
wait_sleep=dict(type="int", default=5),
|
wait_sleep=dict(type="int", default=5),
|
||||||
|
|||||||
Reference in New Issue
Block a user