diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index 5853460e..fafc6fd2 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -10,3 +10,7 @@ - name: Include drain.yml include_tasks: file: tasks/drain.yml + + - name: Include taint.yml + include_tasks: + file: tasks/taint.yml diff --git a/molecule/default/tasks/taint.yml b/molecule/default/tasks/taint.yml new file mode 100644 index 00000000..d190f4fc --- /dev/null +++ b/molecule/default/tasks/taint.yml @@ -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 diff --git a/plugins/modules/k8s_taint.py b/plugins/modules/k8s_taint.py new file mode 100644 index 00000000..4234fd6a --- /dev/null +++ b/plugins/modules/k8s_taint.py @@ -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() diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index 45a04643..ed35367d 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -249,4 +249,11 @@ plugins/lookup/kustomize.py future-import-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 future-import-boilerplate!skip -molecule/default/roles/helm/library/helm_test_version.py shebang \ No newline at end of file +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 \ No newline at end of file diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 45a04643..ed35367d 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -249,4 +249,11 @@ plugins/lookup/kustomize.py future-import-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 future-import-boilerplate!skip -molecule/default/roles/helm/library/helm_test_version.py shebang \ No newline at end of file +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 \ No newline at end of file diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt index 64253c03..2606f230 100644 --- a/tests/sanity/ignore-2.12.txt +++ b/tests/sanity/ignore-2.12.txt @@ -241,4 +241,11 @@ plugins/modules/k8s_cp.py import-2.6!skip plugins/modules/k8s_cp.py import-2.7!skip plugins/module_utils/selector.py future-import-boilerplate!skip plugins/module_utils/selector.py metaclass-boilerplate!skip -molecule/default/roles/helm/library/helm_test_version.py shebang \ No newline at end of file +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 \ No newline at end of file diff --git a/tests/sanity/ignore-2.13.txt b/tests/sanity/ignore-2.13.txt index 68da734f..ade37359 100644 --- a/tests/sanity/ignore-2.13.txt +++ b/tests/sanity/ignore-2.13.txt @@ -187,4 +187,11 @@ plugins/modules/k8s_cp.py compile-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 metaclass-boilerplate!skip -molecule/default/roles/helm/library/helm_test_version.py shebang \ No newline at end of file +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 \ No newline at end of file diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index ba5ac517..c2e8063e 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -243,4 +243,10 @@ plugins/lookup/kustomize.py future-import-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 future-import-boilerplate!skip -molecule/default/roles/helm/library/helm_test_version.py shebang \ No newline at end of file +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 \ No newline at end of file