mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-08 22:12:44 +00:00
K8s_taint new module (#264)
K8s_taint new module SUMMARY k8s_taint - new module to apply/remove taints to/from nodes. ISSUE TYPE New Module Pull Request COMPONENT NAME k8s_taint Reviewed-by: Mike Graves <mgraves@redhat.com> Reviewed-by: Alina Buzachis <None> Reviewed-by: None <None> Reviewed-by: None <None>
This commit is contained in:
@@ -10,3 +10,7 @@
|
|||||||
- name: Include drain.yml
|
- name: Include drain.yml
|
||||||
include_tasks:
|
include_tasks:
|
||||||
file: tasks/drain.yml
|
file: tasks/drain.yml
|
||||||
|
|
||||||
|
- name: Include taint.yml
|
||||||
|
include_tasks:
|
||||||
|
file: tasks/taint.yml
|
||||||
|
|||||||
454
molecule/default/tasks/taint.yml
Normal file
454
molecule/default/tasks/taint.yml
Normal file
@@ -0,0 +1,454 @@
|
|||||||
|
---
|
||||||
|
- block:
|
||||||
|
- set_fact:
|
||||||
|
namespace: "namespace-taint"
|
||||||
|
pod_name_1: "pod-1-taint"
|
||||||
|
taint_patch_1:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: "key1"
|
||||||
|
value: "value1"
|
||||||
|
taint_patch_1_update:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: "key1"
|
||||||
|
value: "value_updated"
|
||||||
|
taint_patch_2:
|
||||||
|
- effect: NoSchedule
|
||||||
|
key: "key2"
|
||||||
|
value: "value2"
|
||||||
|
- effect: NoExecute
|
||||||
|
key: "key2"
|
||||||
|
taint_patch_3:
|
||||||
|
- effect: NoSchedule
|
||||||
|
key: "key3"
|
||||||
|
- effect: NoExecute
|
||||||
|
key: "key1"
|
||||||
|
|
||||||
|
- name: List cluster nodes
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: node
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Select a node to taint
|
||||||
|
set_fact:
|
||||||
|
node_to_taint: "{{ _result.resources[0].metadata.name }}"
|
||||||
|
|
||||||
|
- name: Create namespace
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
kind: Namespace
|
||||||
|
name: "{{ namespace }}"
|
||||||
|
|
||||||
|
- name: Create Pod
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
namespace: '{{ namespace }}'
|
||||||
|
wait: yes
|
||||||
|
definition:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: "{{ pod_name_1 }}"
|
||||||
|
spec:
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchFields:
|
||||||
|
- key: metadata.name
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- '{{ node_to_taint }}'
|
||||||
|
containers:
|
||||||
|
- name: c0
|
||||||
|
image: busybox
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- while true; do date;sleep 5; done
|
||||||
|
terminationGracePeriodSeconds: 10
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that pod is running on the node
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.result.status.phase == 'Running'
|
||||||
|
- _result.result.spec.nodeName == "{{ node_to_taint }}"
|
||||||
|
|
||||||
|
- name: Taint node (check_mode)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
check_mode: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted (check_mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
|
||||||
|
- name: Taint node
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
- "{{ item['effect'] == taint_patch_1[0]['effect'] }}"
|
||||||
|
- "{{ item['key'] == taint_patch_1[0]['key'] }}"
|
||||||
|
loop: "{{ _result.result.spec.taints }}"
|
||||||
|
|
||||||
|
- name: Taint node (idempotency) - (check_mode)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
check_mode: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted (idempotency - no change) - (check_mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Taint node (idempotency)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted (idempotency - no change)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Pause for 30 seconds
|
||||||
|
pause:
|
||||||
|
seconds: 30
|
||||||
|
|
||||||
|
- name: Get Pods
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: Pod
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that Pod has been evicted
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.resources | list | length == 0
|
||||||
|
|
||||||
|
- name: Taint node with replace=true (check_mode)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1}}"
|
||||||
|
replace: true
|
||||||
|
check_mode: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted (replace=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Taint node with replace=true
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1}}"
|
||||||
|
replace: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted (replace=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Taint again node with replace=true (check_mode)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
replace: true
|
||||||
|
check_mode: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted (replace=true) - (check_mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Taint again node with replace=true
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
replace: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that node has been tainted (replace=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Update node taints
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1_update }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Update node taints
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
- all_taints | selectattr('key', 'equalto', search_key) | selectattr('effect', 'equalto', search_effect) | selectattr('value', 'equalto', search_value) | list | count > 0
|
||||||
|
vars:
|
||||||
|
search_key: "{{ item.key}}"
|
||||||
|
search_effect: "{{ item.effect }}"
|
||||||
|
search_value: "{{ item.value }}"
|
||||||
|
all_taints: "{{ taint_patch_1_update }}"
|
||||||
|
with_items: "{{ _result.result.spec.taints }}"
|
||||||
|
|
||||||
|
- name: Update node taints (idempotence)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1_update }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Update node taints (idempotence)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Add other taints to node (check_mode)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_2 }}"
|
||||||
|
check_mode: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that other taints has been added (check_mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
|
||||||
|
- name: Add other taints to node
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_2 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that other taints has been added
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
- all_taints | selectattr('key', 'equalto', search_key) | selectattr('effect', 'equalto', search_effect) | list | count > 0
|
||||||
|
vars:
|
||||||
|
search_key: "{{ item.key}}"
|
||||||
|
search_effect: "{{ item.effect }}"
|
||||||
|
all_taints: "{{ taint_patch_1 }} + {{ taint_patch_2 }}"
|
||||||
|
with_items: "{{ _result.result.spec.taints }}"
|
||||||
|
|
||||||
|
- name: Remove taints from node (check_mode)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
check_mode: true
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint has been removed (check_mode)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
|
||||||
|
- name: Remove taint from node
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint has been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
|
||||||
|
- name: Get node taints
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: node
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint has been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- all_taints | selectattr('key', 'equalto', search_key) | selectattr('effect', 'equalto', search_effect) | list | count > 0
|
||||||
|
vars:
|
||||||
|
search_key: "{{ item.key}}"
|
||||||
|
search_effect: "{{ item.effect }}"
|
||||||
|
all_taints: "{{ taint_patch_2 }}"
|
||||||
|
with_items: "{{ _result.resources[0].spec.taints }}"
|
||||||
|
|
||||||
|
- name: Remove taint from node (idempotency)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint has been removed (idempotency)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Remove nonexistent taint from node
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_3 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert taint has been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not _result.changed
|
||||||
|
|
||||||
|
- name: Re-add taint to node
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_1 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint has been added
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
- all_taints | selectattr('key', 'equalto', search_key) | selectattr('effect', 'equalto', search_effect) | list | count > 0
|
||||||
|
vars:
|
||||||
|
search_key: "{{ item.key}}"
|
||||||
|
search_effect: "{{ item.effect }}"
|
||||||
|
all_taints: "{{ taint_patch_1 }} + {{ taint_patch_2 }}"
|
||||||
|
with_items: "{{ _result.result.spec.taints }}"
|
||||||
|
|
||||||
|
- name: Add other taints and update
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints: "{{ taint_patch_3 }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taints have been added and updated
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
- all_taints | selectattr('key', 'equalto', search_key) | selectattr('effect', 'equalto', search_effect) | list | count > 0
|
||||||
|
vars:
|
||||||
|
search_key: "{{ item.key}}"
|
||||||
|
search_effect: "{{ item.effect }}"
|
||||||
|
all_taints: "{{ taint_patch_3 }} + {{ taint_patch_2 }}"
|
||||||
|
with_items: "{{ _result.result.spec.taints }}"
|
||||||
|
|
||||||
|
- name: Remove taint using key:effect
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints:
|
||||||
|
- key: "key2"
|
||||||
|
effect: "NoSchedule"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint using key:effect has been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
|
||||||
|
- name: Get node taints
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: node
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
left_taint_patch_2: [ "{{ taint_patch_2[1] }}" ]
|
||||||
|
|
||||||
|
- name: Assert that taint using key:effect has been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- all_taints | selectattr('key', 'equalto', search_key) | selectattr('effect', 'equalto', search_effect) | list | count > 0
|
||||||
|
vars:
|
||||||
|
search_key: "{{ item.key}}"
|
||||||
|
search_effect: "{{ item.effect }}"
|
||||||
|
all_taints: "{{ taint_patch_3 }} + {{ left_taint_patch_2 }}"
|
||||||
|
with_items: "{{ _result.resources[0].spec.taints }}"
|
||||||
|
|
||||||
|
- name: Remove taint using key
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints:
|
||||||
|
- key: "key3"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint using key has been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.changed
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
left_taint_patch_3: [ "{{ taint_patch_3[1] }}" ]
|
||||||
|
|
||||||
|
- name: Get node taints
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: node
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taint using key has been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- all_taints | selectattr('key', 'equalto', search_key) | selectattr('effect', 'equalto', search_effect) | list | count > 0
|
||||||
|
vars:
|
||||||
|
search_key: "{{ item.key}}"
|
||||||
|
search_effect: "{{ item.effect }}"
|
||||||
|
all_taints: "{{ left_taint_patch_2 }} + {{ left_taint_patch_3 }}"
|
||||||
|
with_items: "{{ _result.resources[0].spec.taints }}"
|
||||||
|
|
||||||
|
- name: Remove taints (including non existing ones)
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints:
|
||||||
|
- key: "key1"
|
||||||
|
- key: "key2"
|
||||||
|
- key: "key3"
|
||||||
|
|
||||||
|
- name: Get node taints
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: node
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Assert that taints have been removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.resources | selectattr('spec.taints', 'undefined')
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Delete Pods
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
state: absent
|
||||||
|
kind: Pod
|
||||||
|
name: "{{ pod_name_1 }}"
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Delete namespace
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
state: absent
|
||||||
|
kind: Namespace
|
||||||
|
name: "{{ namespace }}"
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Remove taints
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: "{{ node_to_taint }}"
|
||||||
|
taints:
|
||||||
|
- key: "key1"
|
||||||
|
- key: "key2"
|
||||||
|
- key: "key3"
|
||||||
|
ignore_errors: true
|
||||||
310
plugins/modules/k8s_taint.py
Normal file
310
plugins/modules/k8s_taint.py
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2021, Alina Buzachis <@alinabuzachis>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION = r"""
|
||||||
|
module: k8s_taint
|
||||||
|
short_description: Taint a node in a Kubernetes/OpenShift cluster
|
||||||
|
version_added: "2.3.0"
|
||||||
|
author: Alina Buzachis (@alinabuzachis)
|
||||||
|
description:
|
||||||
|
- Taint allows a node to refuse Pod to be scheduled unless that Pod has a matching toleration.
|
||||||
|
- Untaint will remove taints from nodes as needed.
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- kubernetes.core.k8s_auth_options
|
||||||
|
options:
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- Determines whether to add or remove taints.
|
||||||
|
type: str
|
||||||
|
default: present
|
||||||
|
choices: [ present, absent ]
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The name of the node.
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
taints:
|
||||||
|
description:
|
||||||
|
- List containing the taints.
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
|
elements: dict
|
||||||
|
suboptions:
|
||||||
|
key:
|
||||||
|
description:
|
||||||
|
- The taint key to be applied to a node.
|
||||||
|
type: str
|
||||||
|
value:
|
||||||
|
description:
|
||||||
|
- The taint value corresponding to the taint key.
|
||||||
|
type: str
|
||||||
|
effect:
|
||||||
|
description:
|
||||||
|
- The effect of the taint on Pods that do not tolerate the taint.
|
||||||
|
- Required when I(state=present).
|
||||||
|
type: str
|
||||||
|
choices: [ NoSchedule, NoExecute, PreferNoSchedule ]
|
||||||
|
replace:
|
||||||
|
description:
|
||||||
|
- If C(true), allow taints to be replaced.
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: bool
|
||||||
|
requirements:
|
||||||
|
- python >= 3.6
|
||||||
|
- kubernetes >= 12.0.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
- name: Taint node "foo"
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: present
|
||||||
|
name: foo
|
||||||
|
taints:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: "key1"
|
||||||
|
|
||||||
|
- name: Taint node "foo"
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: present
|
||||||
|
name: foo
|
||||||
|
taints:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: "key1"
|
||||||
|
value: "value1"
|
||||||
|
- effect: NoSchedule
|
||||||
|
key: "key1"
|
||||||
|
value: "value1"
|
||||||
|
|
||||||
|
- name: Remove taint from "foo".
|
||||||
|
kubernetes.core.k8s_taint:
|
||||||
|
state: absent
|
||||||
|
name: foo
|
||||||
|
taints:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: "key1"
|
||||||
|
value: "value1"
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = r"""
|
||||||
|
result:
|
||||||
|
description:
|
||||||
|
- The tainted Node object. Will be empty in the case of a deletion.
|
||||||
|
returned: success
|
||||||
|
type: complex
|
||||||
|
contains:
|
||||||
|
api_version:
|
||||||
|
description: The versioned schema of this representation of an object.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
kind:
|
||||||
|
description: Represents the REST resource this object represents.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
metadata:
|
||||||
|
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
|
||||||
|
returned: success
|
||||||
|
type: complex
|
||||||
|
spec:
|
||||||
|
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
|
||||||
|
returned: success
|
||||||
|
type: complex
|
||||||
|
status:
|
||||||
|
description: Current status details for the object.
|
||||||
|
returned: success
|
||||||
|
type: complex
|
||||||
|
"""
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils._text import to_native
|
||||||
|
|
||||||
|
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||||
|
K8sAnsibleMixin,
|
||||||
|
get_api_client,
|
||||||
|
)
|
||||||
|
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||||
|
AUTH_ARG_SPEC,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from kubernetes.client.api import core_v1_api
|
||||||
|
from kubernetes.dynamic.exceptions import ApiException
|
||||||
|
except ImportError:
|
||||||
|
# ImportError are managed by the common module already.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _equal_dicts(a, b):
|
||||||
|
keys = ["key", "effect"]
|
||||||
|
if "effect" not in set(a).intersection(b):
|
||||||
|
keys.remove("effect")
|
||||||
|
|
||||||
|
return all((a[x] == b[x] for x in keys))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_difference(a, b):
|
||||||
|
return [
|
||||||
|
a_item for a_item in a if not any(_equal_dicts(a_item, b_item) for b_item in b)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_intersection(a, b):
|
||||||
|
return [a_item for a_item in a if any(_equal_dicts(a_item, b_item) for b_item in b)]
|
||||||
|
|
||||||
|
|
||||||
|
def _update_exists(a, b):
|
||||||
|
return any(
|
||||||
|
(
|
||||||
|
any(
|
||||||
|
_equal_dicts(a_item, b_item)
|
||||||
|
and a_item.get("value") != b_item.get("value")
|
||||||
|
for b_item in b
|
||||||
|
)
|
||||||
|
for a_item in a
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def argspec():
|
||||||
|
argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
|
||||||
|
argument_spec.update(
|
||||||
|
dict(
|
||||||
|
state=dict(type="str", choices=["present", "absent"], default="present"),
|
||||||
|
name=dict(type="str", required=True),
|
||||||
|
taints=dict(type="list", required=True, elements="dict"),
|
||||||
|
replace=dict(type="bool", default=False),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return argument_spec
|
||||||
|
|
||||||
|
|
||||||
|
class K8sTaintAnsible:
|
||||||
|
def __init__(self, module):
|
||||||
|
self.module = module
|
||||||
|
self.k8s_ansible_mixin = K8sAnsibleMixin(module=self.module)
|
||||||
|
self.k8s_ansible_mixin.client = get_api_client(module=self.module)
|
||||||
|
self.k8s_ansible_mixin.module = self.module
|
||||||
|
self.k8s_ansible_mixin.argspec = self.module.argument_spec
|
||||||
|
self.k8s_ansible_mixin.check_mode = self.module.check_mode
|
||||||
|
self.k8s_ansible_mixin.params = self.module.params
|
||||||
|
self.k8s_ansible_mixin.fail_json = self.module.fail_json
|
||||||
|
self.k8s_ansible_mixin.fail = self.module.fail_json
|
||||||
|
self.k8s_ansible_mixin.exit_json = self.module.exit_json
|
||||||
|
self.k8s_ansible_mixin.warn = self.module.warn
|
||||||
|
self.k8s_ansible_mixin.warnings = []
|
||||||
|
self.api_instance = core_v1_api.CoreV1Api(self.k8s_ansible_mixin.client.client)
|
||||||
|
self.k8s_ansible_mixin.check_library_version()
|
||||||
|
self.changed = False
|
||||||
|
|
||||||
|
def get_node(self, name):
|
||||||
|
try:
|
||||||
|
node = self.api_instance.read_node(name=name)
|
||||||
|
except ApiException as exc:
|
||||||
|
if exc.reason == "Not Found":
|
||||||
|
self.module.fail_json(msg="Node '{0}' has not been found.".format(name))
|
||||||
|
self.module.fail_json(
|
||||||
|
msg="Failed to retrieve node '{0}' due to: {1}".format(
|
||||||
|
name, exc.reason
|
||||||
|
),
|
||||||
|
status=exc.status,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
self.module.fail_json(
|
||||||
|
msg="Failed to retrieve node '{0}' due to: {1}".format(
|
||||||
|
name, to_native(exc)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return node
|
||||||
|
|
||||||
|
def patch_node(self, taints):
|
||||||
|
body = {"spec": {"taints": taints}}
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self.api_instance.patch_node(
|
||||||
|
name=self.module.params.get("name"), body=body
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
self.module.fail_json(
|
||||||
|
msg="Failed to patch node due to: {0}".format(to_native(exc))
|
||||||
|
)
|
||||||
|
|
||||||
|
return result.to_dict()
|
||||||
|
|
||||||
|
def execute_module(self):
|
||||||
|
result = {"result": {}}
|
||||||
|
state = self.module.params.get("state")
|
||||||
|
taints = self.module.params.get("taints")
|
||||||
|
name = self.module.params.get("name")
|
||||||
|
|
||||||
|
node = self.get_node(name)
|
||||||
|
existing_taints = node.spec.to_dict().get("taints") or []
|
||||||
|
diff = _get_difference(taints, existing_taints)
|
||||||
|
|
||||||
|
if state == "present":
|
||||||
|
if diff:
|
||||||
|
# There are new taints to be added
|
||||||
|
self.changed = True
|
||||||
|
if self.module.check_mode:
|
||||||
|
self.module.exit_json(changed=self.changed, **result)
|
||||||
|
|
||||||
|
if self.module.params.get("replace"):
|
||||||
|
# Patch with the new taints
|
||||||
|
result["result"] = self.patch_node(taints=taints)
|
||||||
|
self.module.exit_json(changed=self.changed, **result)
|
||||||
|
|
||||||
|
result["result"] = self.patch_node(
|
||||||
|
taints=[*_get_difference(existing_taints, taints), *taints]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# No new taints to be added, but maybe there is something to be updated
|
||||||
|
if _update_exists(existing_taints, taints):
|
||||||
|
self.changed = True
|
||||||
|
if self.module.check_mode:
|
||||||
|
self.module.exit_json(changed=self.changed, **result)
|
||||||
|
result["result"] = self.patch_node(
|
||||||
|
taints=[*_get_difference(existing_taints, taints), *taints]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result["result"] = node.to_dict()
|
||||||
|
elif state == "absent":
|
||||||
|
# Nothing to be removed
|
||||||
|
if not existing_taints:
|
||||||
|
result["result"] = node.to_dict()
|
||||||
|
if not diff:
|
||||||
|
self.changed = True
|
||||||
|
if self.module.check_mode:
|
||||||
|
self.module.exit_json(changed=self.changed, **result)
|
||||||
|
self.patch_node(taints=_get_difference(existing_taints, taints))
|
||||||
|
else:
|
||||||
|
if _get_intersection(existing_taints, taints):
|
||||||
|
self.changed = True
|
||||||
|
if self.module.check_mode:
|
||||||
|
self.module.exit_json(changed=self.changed, **result)
|
||||||
|
self.patch_node(taints=_get_difference(existing_taints, taints))
|
||||||
|
else:
|
||||||
|
self.module.exit_json(changed=self.changed, **result)
|
||||||
|
|
||||||
|
self.module.exit_json(changed=self.changed, **result)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True,)
|
||||||
|
k8s_taint = K8sTaintAnsible(module)
|
||||||
|
k8s_taint.execute_module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -249,4 +249,11 @@ plugins/lookup/kustomize.py future-import-boilerplate!skip
|
|||||||
plugins/lookup/kustomize.py metaclass-boilerplate!skip
|
plugins/lookup/kustomize.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py metaclass-boilerplate!skip
|
molecule/default/roles/helm/library/helm_test_version.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py future-import-boilerplate!skip
|
molecule/default/roles/helm/library/helm_test_version.py future-import-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py shebang
|
molecule/default/roles/helm/library/helm_test_version.py shebang
|
||||||
|
plugins/modules/k8s_taint.py compile-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py compile-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py future-import-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py import-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py import-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
|
||||||
@@ -249,4 +249,11 @@ plugins/lookup/kustomize.py future-import-boilerplate!skip
|
|||||||
plugins/lookup/kustomize.py metaclass-boilerplate!skip
|
plugins/lookup/kustomize.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py metaclass-boilerplate!skip
|
molecule/default/roles/helm/library/helm_test_version.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py future-import-boilerplate!skip
|
molecule/default/roles/helm/library/helm_test_version.py future-import-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py shebang
|
molecule/default/roles/helm/library/helm_test_version.py shebang
|
||||||
|
plugins/modules/k8s_taint.py compile-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py compile-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py future-import-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py import-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py import-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
|
||||||
@@ -241,4 +241,11 @@ plugins/modules/k8s_cp.py import-2.6!skip
|
|||||||
plugins/modules/k8s_cp.py import-2.7!skip
|
plugins/modules/k8s_cp.py import-2.7!skip
|
||||||
plugins/module_utils/selector.py future-import-boilerplate!skip
|
plugins/module_utils/selector.py future-import-boilerplate!skip
|
||||||
plugins/module_utils/selector.py metaclass-boilerplate!skip
|
plugins/module_utils/selector.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py shebang
|
molecule/default/roles/helm/library/helm_test_version.py shebang
|
||||||
|
plugins/modules/k8s_taint.py compile-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py compile-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py future-import-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py import-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py import-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
|
||||||
@@ -187,4 +187,11 @@ plugins/modules/k8s_cp.py compile-2.7!skip
|
|||||||
plugins/modules/k8s_cp.py import-2.7!skip
|
plugins/modules/k8s_cp.py import-2.7!skip
|
||||||
plugins/module_utils/selector.py future-import-boilerplate!skip
|
plugins/module_utils/selector.py future-import-boilerplate!skip
|
||||||
plugins/module_utils/selector.py metaclass-boilerplate!skip
|
plugins/module_utils/selector.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py shebang
|
molecule/default/roles/helm/library/helm_test_version.py shebang
|
||||||
|
plugins/modules/k8s_taint.py compile-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py compile-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py future-import-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py import-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py import-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
|
||||||
@@ -243,4 +243,10 @@ plugins/lookup/kustomize.py future-import-boilerplate!skip
|
|||||||
plugins/lookup/kustomize.py metaclass-boilerplate!skip
|
plugins/lookup/kustomize.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py metaclass-boilerplate!skip
|
molecule/default/roles/helm/library/helm_test_version.py metaclass-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py future-import-boilerplate!skip
|
molecule/default/roles/helm/library/helm_test_version.py future-import-boilerplate!skip
|
||||||
molecule/default/roles/helm/library/helm_test_version.py shebang
|
molecule/default/roles/helm/library/helm_test_version.py shebang
|
||||||
|
plugins/modules/k8s_taint.py compile-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py compile-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py future-import-boilerplate!skip
|
||||||
|
plugins/modules/k8s_taint.py import-2.7!skip
|
||||||
|
plugins/modules/k8s_taint.py import-3.5!skip
|
||||||
|
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
|
||||||
Reference in New Issue
Block a user