---
- block:
  - set_fact:
      namespace: "{{ test_namespace }}"
      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 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 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
