ansible-collections / community.kubernetes

Kubernetes Collection for Ansible
https://galaxy.ansible.com/community/kubernetes
GNU General Public License v3.0
265 stars 104 forks source link

k8s Secret check_mode issue #282

Closed eMCeee89 closed 3 years ago

eMCeee89 commented 4 years ago
SUMMARY

When k8s module is used for Kubernetes Secret (state=present) with a check_mode enabled, then it always returns changed: True. Without check_mode, the very same manifest returns changed: False. Please note this issue only applies to Secret resource type, ConfigMap resource returns expected results.

ISSUE TYPE
COMPONENT NAME

community.kubernetes.k8s

ANSIBLE VERSION
ansible 2.9.14
  config file = /kubernetes/ansible.cfg
  configured module search path = ['/home/bambus/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.6.8 (default, Apr 16 2020, 01:36:27) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
CONFIGURATION
ANSIBLE_SSH_ARGS(/kubernetes/ansible.cfg) = -C -o ControlMaster=auto -o ControlPersist=60s -o PubkeyAuthentication=no
COLOR_CHANGED(/kubernetes/ansible.cfg) = yellow
COLOR_DEBUG(/kubernetes/ansible.cfg) = dark gray
COLOR_DEPRECATE(/kubernetes/ansible.cfg) = purple
COLOR_DIFF_ADD(/kubernetes/ansible.cfg) = green
COLOR_DIFF_LINES(/kubernetes/ansible.cfg) = cyan
COLOR_DIFF_REMOVE(/kubernetes/ansible.cfg) = red
COLOR_ERROR(/kubernetes/ansible.cfg) = red
COLOR_HIGHLIGHT(/kubernetes/ansible.cfg) = white
COLOR_OK(/kubernetes/ansible.cfg) = green
COLOR_SKIP(/kubernetes/ansible.cfg) = cyan
COLOR_UNREACHABLE(/kubernetes/ansible.cfg) = red
COLOR_VERBOSE(/kubernetes/ansible.cfg) = blue
COLOR_WARN(/kubernetes/ansible.cfg) = bright purple
DEFAULT_LOAD_CALLBACK_PLUGINS(/kubernetes/ansible.cfg) = True
DEFAULT_STDOUT_CALLBACK(/kubernetes/ansible.cfg) = yaml
HOST_KEY_CHECKING(/kubernetes/ansible.cfg) = False
OS / ENVIRONMENT

Centos 8.2.2004

STEPS TO REPRODUCE
---
- hosts: "127.0.0.1"
  connection: local
  gather_facts: False
  vars:
    _definition:
      apiVersion: v1
      kind: Secret
      metadata:
        name: test
        namespace: default
      type: Opaque
      stringData:
        foo: "bar"
  tasks:
    - name: dry run
      k8s:
        state: present
        definition: "{{ _definition }}"
      check_mode: yes

    - name: real run
      k8s:
        state: present
        definition: "{{ _definition }}"
EXPECTED RESULTS

Having check_mode enabled, it should return empty diff when there is no change to be applied and change result set to False.

ACTUAL RESULTS

When check_mode is enabled, it always returns changed: True result even though there is no change to be applied.

$ ansible-playbook -i inventory/localhost.yml playbooks/playbook.yml -vvvv
ansible-playbook 2.9.14
  config file = /kubernetes/ansible.cfg
  configured module search path = ['/home/bambus/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 3.6.8 (default, Apr 16 2020, 01:36:27) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
Using /kubernetes/ansible.cfg as config file
setting up inventory plugins
host_list declined parsing /kubernetes/inventory/localhost.yml as it did not pass its verify_file() method
script declined parsing /kubernetes/inventory/localhost.yml as it did not pass its verify_file() method
Parsed /kubernetes/inventory/localhost.yml inventory source with yaml plugin
Loading callback plugin yaml of type stdout, v2.0 from /usr/lib/python3.6/site-packages/ansible/plugins/callback/yaml.py

PLAYBOOK: playbook.yml ******************************************************************************************************************************************************
Positional arguments: playbooks/playbook.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('/kubernetes/inventory/localhost.yml',)
forks: 5
1 plays in playbooks/playbook.yml

PLAY [127.0.0.1] ************************************************************************************************************************************************************
META: ran handlers

TASK [dry run] **************************************************************************************************************************************************************
task path: /kubernetes/playbooks/playbook.yml:16
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: bambus
<127.0.0.1> EXEC /bin/sh -c 'echo ~bambus && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/bambus/.ansible/tmp `"&& mkdir "` echo /home/bambus/.ansible/tmp/ansible-tmp-1603373084.7558267-273572-266149592071148 `" && echo ansible-tmp-1603373084.7558267-273572-266149592071148="` echo /home/bambus/.ansible/tmp/ansible-tmp-1603373084.7558267-273572-266149592071148 `" ) && sleep 0'
Using module file /usr/lib/python3.6/site-packages/ansible/modules/clustering/k8s/k8s.py
<127.0.0.1> PUT /home/bambus/.ansible/tmp/ansible-local-273555e76twcw7/tmpuulrd53s TO /home/bambus/.ansible/tmp/ansible-tmp-1603373084.7558267-273572-266149592071148/AnsiballZ_k8s.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/bambus/.ansible/tmp/ansible-tmp-1603373084.7558267-273572-266149592071148/ /home/bambus/.ansible/tmp/ansible-tmp-1603373084.7558267-273572-266149592071148/AnsiballZ_k8s.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3.6 /home/bambus/.ansible/tmp/ansible-tmp-1603373084.7558267-273572-266149592071148/AnsiballZ_k8s.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /home/bambus/.ansible/tmp/ansible-tmp-1603373084.7558267-273572-266149592071148/ > /dev/null 2>&1 && sleep 0'
changed: [127.0.0.1] => changed=true 
  diff:
    after:
      stringData:
        foo: bar
    before: {}
  invocation:
    module_args:
      api_key: null
      api_version: v1
      append_hash: false
      apply: false
      ca_cert: null
      client_cert: null
      client_key: null
      context: null
      force: false
      host: null
      kind: null
      kubeconfig: null
      merge_type: null
      name: null
      namespace: null
      password: null
      proxy: null
      resource_definition:
        apiVersion: v1
        kind: Secret
        metadata:
          name: test
          namespace: default
        stringData:
          foo: bar
        type: Opaque
      src: null
      state: present
      username: null
      validate: null
      validate_certs: null
      wait: false
      wait_condition: null
      wait_sleep: 5
      wait_timeout: 120
  method: patch
  result:
    apiVersion: v1
    data:
      foo: YmFy
    kind: Secret
    metadata:
      creationTimestamp: '2020-10-22T13:22:26Z'
      name: test
      namespace: default
      resourceVersion: '491215'
      selfLink: /api/v1/namespaces/default/secrets/test
      uid: b8cdc934-521e-469d-a06a-4cffe10ef05e
    stringData:
      foo: bar
    type: Opaque

TASK [real run] *************************************************************************************************************************************************************
task path: /kubernetes/playbooks/playbook.yml:22
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: bambus
<127.0.0.1> EXEC /bin/sh -c 'echo ~bambus && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/bambus/.ansible/tmp `"&& mkdir "` echo /home/bambus/.ansible/tmp/ansible-tmp-1603373087.9914718-273624-167314503481206 `" && echo ansible-tmp-1603373087.9914718-273624-167314503481206="` echo /home/bambus/.ansible/tmp/ansible-tmp-1603373087.9914718-273624-167314503481206 `" ) && sleep 0'
Using module file /usr/lib/python3.6/site-packages/ansible/modules/clustering/k8s/k8s.py
<127.0.0.1> PUT /home/bambus/.ansible/tmp/ansible-local-273555e76twcw7/tmp9we0qr90 TO /home/bambus/.ansible/tmp/ansible-tmp-1603373087.9914718-273624-167314503481206/AnsiballZ_k8s.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/bambus/.ansible/tmp/ansible-tmp-1603373087.9914718-273624-167314503481206/ /home/bambus/.ansible/tmp/ansible-tmp-1603373087.9914718-273624-167314503481206/AnsiballZ_k8s.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3.6 /home/bambus/.ansible/tmp/ansible-tmp-1603373087.9914718-273624-167314503481206/AnsiballZ_k8s.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /home/bambus/.ansible/tmp/ansible-tmp-1603373087.9914718-273624-167314503481206/ > /dev/null 2>&1 && sleep 0'
ok: [127.0.0.1] => changed=false 
  diff: {}
  invocation:
    module_args:
      api_key: null
      api_version: v1
      append_hash: false
      apply: false
      ca_cert: null
      client_cert: null
      client_key: null
      context: null
      force: false
      host: null
      kind: null
      kubeconfig: null
      merge_type: null
      name: null
      namespace: null
      password: null
      proxy: null
      resource_definition:
        apiVersion: v1
        kind: Secret
        metadata:
          name: test
          namespace: default
        stringData:
          foo: bar
        type: Opaque
      src: null
      state: present
      username: null
      validate: null
      validate_certs: null
      wait: false
      wait_condition: null
      wait_sleep: 5
      wait_timeout: 120
  method: patch
  result:
    apiVersion: v1
    data:
      foo: YmFy
    kind: Secret
    metadata:
      creationTimestamp: '2020-10-22T13:22:26Z'
      name: test
      namespace: default
      resourceVersion: '491215'
      selfLink: /api/v1/namespaces/default/secrets/test
      uid: b8cdc934-521e-469d-a06a-4cffe10ef05e
    type: Opaque
META: ran handlers
META: ran handlers

PLAY RECAP ******************************************************************************************************************************************************************
127.0.0.1                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
eMCeee89 commented 4 years ago

One finding regarding the issue:

fabianvf commented 4 years ago

Since the k8s module is generic, in check mode it sees that what the user wants is stringData with content, and what is there is data with different content, and so it assumes that this is a real change and reports that the resource will change (which should be correct for pretty much any other resource).

We can just special case secrets in check mode here to make it work correctly, we just need to change stringData to data and base64 encode the values before running the diff

Akasurde commented 3 years ago

@eMCeee89 Could you please check if #343 works for you and let us know? Thanks.

needs_info

Akasurde commented 3 years ago

resolved_by_pr #343

eMCeee89 commented 3 years ago

@Akasurde I can confirm it works well using a version from #343, thank you.