Compare commits

15 Commits
4.0.0 ... 3.1.0

Author SHA1 Message Date
Mike Graves
ef829b8a56 Release 3.1.0 (#719)
Release 3.1.0

SUMMARY

Release prep for 3.1.0

ISSUE TYPE


Bugfix Pull Request
Docs Pull Request
Feature Pull Request
New Module Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis
Reviewed-by: Helen Bailey <hebailey@redhat.com>
2024-05-16 14:15:47 +00:00
patchback[bot]
d86a652c36 Update deprecation version for merge_type=json (#700) (#718)
[PR #700/a4c1bd85 backport][stable-3] Update deprecation version for merge_type=json

This is a backport of PR #700 as merged into main (a4c1bd8).
SUMMARY
When looking at the parts of plugins/module_utils/common.py and plugins/module_utils/k8s/service.py during the post 3.0.0 release (see https://github.com/ansible-collections/kubernetes.core/pull/663/files#diff-9ee2d0860a5643da4e1f35136e9e7c3a41c5f2fd2952c197e7e32b941e5a301c) that affect merge_type when set to json, I don't believe merge_type=json was deprecated for (and removed from) the k8s module, and instead the deprecation version has moved to 4.0.0. Hence, the documentation update.
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
k8s module

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-15 18:57:45 +00:00
patchback[bot]
a4242f0131 minor: doc: use the same style of version_added across repo (#703) (#715)
[PR #703/63607630 backport][stable-3] minor(doc): use the same style of version_added across repo

This is a backport of PR #703 as merged into main (6360763).
SUMMARY
Currently is no single style of version_added, in some places it's unquoted, somewhere single quote is used, in another places it's double quoted. Moreover, some file had different styles in one single file.
The aim of this PR is to update whole repo to single style for version_added
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
kustomize
helm
helm_info
helm_plugin
helm_plugin_info
helm_pull
helm_repository
helm_template
k8s_cluster_info
k8s_cp
k8s_drain
k8s_exec
k8s_log
k8s_rollback
k8s_taint
ADDITIONAL INFORMATION
The same style is proposed as used in amazon.aws collections

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-15 15:10:34 +00:00
Mike Graves
d75e9a0fa3 Fix unsafe text assertion in tests (#716) (#717)
[Manual backport/stable-3] Fix unsafe text assertion in tests (#716)

Fix unsafe text assertion in tests
SUMMARY
This fixes a problem with unsafe text in an assertion.
ISSUE TYPE
Bugfix Pull Request
COMPONENT NAME
ADDITIONAL INFORMATION
Reviewed-by: GomathiselviS
Reviewed-by: Bikouo Aubin
(cherry picked from commit 8858b19)
2024-05-15 14:35:14 +00:00
patchback[bot]
5044cfc030 fixed typo in filename of 'k8s_json_patch'-action (#652) (#714)
[PR #652/ac943e98 backport][stable-3] fixed typo in filename of 'k8s_json_patch'-action 

This is a backport of PR #652 as merged into main (ac943e9).
SUMMARY

The filename/symlink of the action for the 'k8s_json_patch'-module was wrong. Renamed file from 'ks8_json_patch.py' to ' k8s_json_patch.py'

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

k8s_json_patch
ADDITIONAL INFORMATION


Because of the wrong filename things like unvaulting kubeconfig files did not worked.

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-14 17:07:48 +00:00
patchback[bot]
ddd7e79a12 Update kustomize.py add --enable-helm support (#592) (#712)
[PR #592/0408aa93 backport][stable-3] Update kustomize.py add --enable-helm support

This is a backport of PR #592 as merged into main (0408aa9).
Add --enable-helm support
SUMMARY
Fixes #568
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
Lookup plugin: kubernetes.core.kustomize
ADDITIONAL INFORMATION
Current and maintained arg:
lookup('kubernetes.core.kustomize', dir=item)

Additional feature args:
lookup('kubernetes.core.kustomize', dir=item, enable_helm=false)
lookup('kubernetes.core.kustomize', dir=item, enable_helm=true)

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-13 17:58:34 +00:00
patchback[bot]
edc979ff11 add support of kubectl_local_env_vars (#698) (#702) (#705)
[PR #702/fb25ff44 backport][stable-3] add support of kubectl_local_env_vars (#698)

This is a backport of PR #702 as merged into main (fb25ff4).
SUMMARY
Support of local environmental variable that may be required to be set on Ansible Controller before the connection is set and may be used for kubectl command. This PR addressed for #698
The main idea is to have the support of  additional/extra local environmental variable that may be required for kubectl itself, i.e. for authorization in case of public clouds
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
kubernetes.core.kubectl connection plugin
ADDITIONAL INFORMATION
This PR attempts to implement local env support for the kubectl connection plugin that may be useful in case of using kubectl against public cloud kubernetes environment that uses some authorization (i.e. aws cli) additionally to kubeconfig file. More detail in #698
The output that shows that the connection plugin can use local environment variable for kubectl command (with some debug that used during development but removed then):
root@ubuntu-shell:/# cat test.yaml
- hosts: localhost
  gather_facts: no
  any_errors_fatal: yes
  vars:
    ansible_connection: "kubectl"
    ansible_kubectl_namespace: "test"
    ansible_kubectl_config: "/.kube/config"
    ansible_kubectl_pod: "ubuntu"
    ansible_kubectl_container: "ubuntu"
    ansible_kubectl_local_env_vars:
      TESTVAR1: "test"
      TESTVAR2: "test"
      TESTVAR3: "test"
  environment:
    TEST_ENV1: value1
    TEST_ENV2: value2

  tasks:
  - name: test
    ansible.builtin.shell: env
    register: result
  - debug:
      var: result.stdout_lines
root@ubuntu-shell:/# ansible-playbook test.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] **************************************************************************************************************************************

TASK [test] *******************************************************************************************************************************************
changed: [localhost]

TASK [debug] ******************************************************************************************************************************************
ok: [localhost] => {
    "result.stdout_lines": [
        "KUBERNETES_PORT=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_PORT=443",
        "HOSTNAME=ubuntu",
        "HOME=/root",
        "LC_CTYPE=C.UTF-8",
        "TEST_ENV1=value1",
        "TEST_ENV2=value2",
        "TERM=xterm",
        "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "KUBERNETES_PORT_443_TCP_PORT=443",
        "KUBERNETES_PORT_443_TCP_PROTO=tcp",
        "KUBERNETES_SERVICE_PORT_HTTPS=443",
        "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_HOST=10.96.0.1",
        "PWD=/"
    ]
}

PLAY RECAP ********************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

root@ubuntu-shell:/# ansible-playbook test.yaml -vvv
ansible-playbook [core 2.14.5]
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.10/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible-playbook
  python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.1.3
  libyaml = True
No config file found; using defaults
host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
yaml declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
ini declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
toml declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.

PLAYBOOK: test.yaml ***********************************************************************************************************************************
1 plays in test.yaml

PLAY [localhost] **************************************************************************************************************************************

TASK [test] *******************************************************************************************************************************************
task path: /test.yaml:19
redirecting (type: connection) ansible.builtin.kubectl to kubernetes.core.kubectl
<127.0.0.1> ESTABLISH kubectl CONNECTION
<127.0.0.1> ENV: KUBERNETES_SERVICE_PORT_HTTPS=443
<127.0.0.1> ENV: KUBERNETES_SERVICE_PORT=443
<127.0.0.1> ENV: HOSTNAME=ubuntu-shell
<127.0.0.1> ENV: PWD=/
<127.0.0.1> ENV: HOME=/root
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
<127.0.0.1> ENV: LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
<127.0.0.1> ENV: TERM=xterm
<127.0.0.1> ENV: SHLVL=1
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP_PROTO=tcp
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
<127.0.0.1> ENV: KUBERNETES_SERVICE_HOST=10.96.0.1
<127.0.0.1> ENV: KUBERNETES_PORT=tcp://10.96.0.1:443
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP_PORT=443
<127.0.0.1> ENV: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
<127.0.0.1> ENV: _=/usr/local/bin/ansible-playbook
<127.0.0.1> ENV: LC_CTYPE=C.UTF-8
<127.0.0.1> ENV: TESTVAR1=test
<127.0.0.1> ENV: TESTVAR2=test
<127.0.0.1> ENV: TESTVAR3=test
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'echo ~ && sleep 0'"]
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', '/bin/sh -c \'( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133 `" && echo ansible-tmp-1713785852.548581-6866-69007595335133="` echo /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133 `" ) && sleep 0\'']
Using module file /usr/local/lib/python3.10/dist-packages/ansible/modules/command.py
<127.0.0.1> PUT /root/.ansible/tmp/ansible-local-6862s5_lr_wb/tmpxwmx0qeh TO /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/AnsiballZ_command.py
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/ /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/AnsiballZ_command.py && sleep 0'"]
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'TEST_ENV1=value1 TEST_ENV2=value2 /usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/AnsiballZ_command.py && sleep 0'"]
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/ > /dev/null 2>&1 && sleep 0'"]
changed: [localhost] => {
    "changed": true,
    "cmd": "env",
    "delta": "0:00:00.005088",
    "end": "2024-04-22 11:37:33.655340",
    "invocation": {
        "module_args": {
            "_raw_params": "env",
            "_uses_shell": true,
            "argv": null,
            "chdir": null,
            "creates": null,
            "executable": null,
            "removes": null,
            "stdin": null,
            "stdin_add_newline": true,
            "strip_empty_ends": true
        }
    },
    "msg": "",
    "rc": 0,
    "start": "2024-04-22 11:37:33.650252",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "KUBERNETES_PORT=tcp://10.96.0.1:443\nKUBERNETES_SERVICE_PORT=443\nHOSTNAME=ubuntu\nHOME=/root\nLC_CTYPE=C.UTF-8\nTEST_ENV1=value1\nTEST_ENV2=value2\nTERM=xterm\nKUBERNETES_PORT_443_TCP_ADDR=10.96.0.1\nPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\nKUBERNETES_PORT_443_TCP_PORT=443\nKUBERNETES_PORT_443_TCP_PROTO=tcp\nKUBERNETES_SERVICE_PORT_HTTPS=443\nKUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443\nKUBERNETES_SERVICE_HOST=10.96.0.1\nPWD=/",
    "stdout_lines": [
        "KUBERNETES_PORT=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_PORT=443",
        "HOSTNAME=ubuntu",
        "HOME=/root",
        "LC_CTYPE=C.UTF-8",
        "TEST_ENV1=value1",
        "TEST_ENV2=value2",
        "TERM=xterm",
        "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "KUBERNETES_PORT_443_TCP_PORT=443",
        "KUBERNETES_PORT_443_TCP_PROTO=tcp",
        "KUBERNETES_SERVICE_PORT_HTTPS=443",
        "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_HOST=10.96.0.1",
        "PWD=/"
    ]
}

TASK [debug] ******************************************************************************************************************************************
task path: /test.yaml:22
redirecting (type: connection) ansible.builtin.kubectl to kubernetes.core.kubectl
ok: [localhost] => {
    "result.stdout_lines": [
        "KUBERNETES_PORT=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_PORT=443",
        "HOSTNAME=ubuntu",
        "HOME=/root",
        "LC_CTYPE=C.UTF-8",
        "TEST_ENV1=value1",
        "TEST_ENV2=value2",
        "TERM=xterm",
        "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "KUBERNETES_PORT_443_TCP_PORT=443",
        "KUBERNETES_PORT_443_TCP_PROTO=tcp",
        "KUBERNETES_SERVICE_PORT_HTTPS=443",
        "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_HOST=10.96.0.1",
        "PWD=/"
    ]
}

PLAY RECAP ********************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

root@ubuntu-shell:/#

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-08 19:53:12 +00:00
patchback[bot]
37c1f911cb Add tests/sanity/ignore-2.18.txt (#704) (#706)
[PR #704/86d9a3f4 backport][stable-3] Add tests/sanity/ignore-2.18.txt

This is a backport of PR #704 as merged into main (86d9a3f).
SUMMARY

Add tests/sanity/ignore-2.18.txt

ISSUE TYPE


Bugfix Pull Request
Docs Pull Request
Feature Pull Request
New Module Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION
2024-05-08 19:00:55 +00:00
patchback[bot]
80a5bf623a Merge pull request #707 from gravesm/linting-fix (#708)
[PR #707/874fbfed backport][stable-3] Update ansible-lint GHA

This is a backport of PR #707 as merged into main (874fbfe).
SUMMARY

There seems to be a bug in older versions of ansible-lint where pinning to a version for the GHA still installs the main branch.
See ansible/ansible-lint#3762 for more info.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-08 15:32:59 +00:00
patchback[bot]
8f653558f6 helm - expand kubeconfig path with user's home dir (#654) (#695)
[PR #654/9f7c865c backport][stable-3] helm - expand kubeconfig path with user's home dir

This is a backport of PR #654 as merged into main (9f7c865).
SUMMARY

Currently the helm module fails when providing the default kubeconfig path explicitly, while the same path is fine for the k8s module.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

helm
ADDITIONAL INFORMATION



- name: Deploy kubelet-csr-approver
  delegate_to: client
  run_once: true
  kubernetes.core.helm:
    update_repo_cache: true
    kubeconfig: "~/.kube/config"
    state: present
    name: kubelet-csr-approver
    namespace: kubelet-csr-approver
    create_namespace: true
    chart_ref: kubelet-csr-approver/kubelet-csr-approver
    chart_version: 1.0.5
    values: "{{ lookup('template', 'values.yaml.j2') | from_yaml }}"
    atomic: true

Before change:
TASK [kubernetes/kubelet_csr_approver : Deploy kubelet-csr-approver] ***
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: FileNotFoundError: [Errno 2] No such file or directory: '~/.kube/config'
fatal: [node-1 -> client(192.168.121.56)]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n  File \"/home/vagrant/.ansible/tmp/ansible-tmp-1697293347.7135417-118207-9805169252135/AnsiballZ_helm.py\", line 107, in <module>\r\n    _ansiballz_main()\r\n  File \"/home/vagrant/.ansible/tmp/ansible-tmp-1697293347.7135417-118207-9805169252135/AnsiballZ_helm.py\", line 99, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/home/vagrant/.ansible/tmp/ansible-tmp-1697293347.7135417-118207-9805169252135/AnsiballZ_helm.py\", line 47, in invoke_module\r\n    runpy.run_module(mod_name='ansible_collections.kubernetes.core.plugins.modules.helm', init_globals=dict(_module_fqn='ansible_collections.kubernetes.core.plugins.modules.helm', _modlib_path=modlib_path),\r\n  File \"/usr/lib/python3.10/runpy.py\", line 224, in run_module\r\n    return _run_module_code(code, init_globals, run_name, mod_spec)\r\n  File \"/usr/lib/python3.10/runpy.py\", line 96, in _run_module_code\r\n    _run_code(code, mod_globals, init_globals,\r\n  File \"/usr/lib/python3.10/runpy.py\", line 86, in _run_code\r\n    exec(code, run_globals)\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/modules/helm.py\", line 924, in <module>\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/modules/helm.py\", line 737, in main\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/modules/helm.py\", line 435, in run_repo_update\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/module_utils/helm.py\", line 169, in run_helm_command\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/module_utils/helm.py\", line 162, in env_update\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/module_utils/helm.py\", line 120, in _prepare_helm_environment\r\nFileNotFoundError: [Errno 2] No such file or directory: '~/.kube/config'\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

After change:
TASK [kubernetes/kubelet_csr_approver : Deploy kubelet-csr-approver] ***
changed: [node-1 -> client(192.168.121.56)]

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-03-13 13:57:36 +00:00
Bikouo Aubin
1c0734dd4e Prepare release 3.0.1 (#693) 2024-03-04 11:02:29 +01:00
patchback[bot]
e6d138dbf0 helm - Add reuse-values when running helm diff (#683) (#692)
helm - Add reuse-values when running helm diff

SUMMARY

closes #680

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

helm

Reviewed-by: GomathiselviS
Reviewed-by: Alina Buzachis
(cherry picked from commit 23e94b60c1)

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2024-03-01 18:01:13 +01:00
patchback[bot]
eed4ebb0af Align helmdiff_check behavior with the deploy function (#670) (#690)
Align `helmdiff_check` behavior with the `deploy` function

SUMMARY
Align helmdiff_check behavior with the deploy function

Fixes #638
helmdiff_check respects set_values parameter
Fixes #669
helmdiff_check command line parameters sequence aligned to the deploy function

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
kubernetes.core.helm

Reviewed-by: Mike Graves <mgraves@redhat.com>
(cherry picked from commit 7c4ec3b982)

Co-authored-by: psmolkin <pavel.smolkin@gmail.com>
2024-03-01 17:50:35 +01:00
patchback[bot]
d3031fcbd3 [PR #676/19559892 backport][stable-3] fix(Collection's util resource discovery fails when complex subresources present #659) (#687)
* fix(Collection's util resource discovery fails when complex subresources present #659) (#676)

* fix(Collection's util resource discovery fails when complex subresources present #659)

* fix(add changelog fragment)

* update node image

* Create discovery.yml

* Update main.yml

---------

Co-authored-by: Bastien Bosser <bastien.bosser@eviden.com>
Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
(cherry picked from commit 1955989278)

* adding sanity ignore file

* Remove refresh_ignore_files

* Update ignore files content

---------

Co-authored-by: bastienbosser <55381632+bastienbosser@users.noreply.github.com>
Co-authored-by: abikouo <abikouo@redhat.com>
2024-03-01 16:44:19 +01:00
GomathiselviS
e756cedf33 Prepare for release 3.0.0 (#662)
Prepare for release 3.0.0

SUMMARY



Updated version to 3.0.0
Executed antsibull-changelog to generate changelog

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis
Reviewed-by: Helen Bailey <hebailey@redhat.com>
Reviewed-by: GomathiselviS
2023-11-17 21:52:11 +00:00
28 changed files with 1656 additions and 504 deletions

View File

@@ -4,39 +4,6 @@ Kubernetes Collection Release Notes
.. contents:: Topics .. contents:: Topics
v4.0.0
======
Release Summary
---------------
This major release brings several bug fixes. We have also removed support for ``ansible-core<2.15`` and deprecated functions and class from ``module_utils/common.py``.
Minor Changes
-------------
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
- k8s - The module and K8sService were changed so warnings returned by the K8S API are now displayed to the user.
Removed Features (previously deprecated)
----------------------------------------
- k8s - Support for ``merge_type=json`` has been removed in version 4.0.0. Please use ``kubernetes.core.k8s_json_patch`` instead (https://github.com/ansible-collections/kubernetes.core/pull/722).
- k8s_exec - the previously deprecated ``result.return_code`` return value has been removed, consider using ``result.rc`` instead (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``K8sAnsibleMixin`` class has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``configuration_digest()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``get_api_client()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``unique_string()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
Bugfixes
--------
- Resolve Collections util resource discovery fails when complex subresources present (https://github.com/ansible-collections/kubernetes.core/pull/676).
- align `helmdiff_check()` function commandline rendering with the `deploy()` function (https://github.com/ansible-collections/kubernetes.core/pull/670).
- avoid unsafe conditions in integration tests (https://github.com/ansible-collections/kubernetes.core/pull/665).
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
v3.1.0 v3.1.0
====== ======

View File

@@ -1,5 +1,5 @@
# Also needs to be updated in galaxy.yml # Also needs to be updated in galaxy.yml
VERSION = 4.0.0 VERSION = 3.0.0
TEST_ARGS ?= "" TEST_ARGS ?= ""
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'` PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`

View File

@@ -93,7 +93,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
--- ---
collections: collections:
- name: kubernetes.core - name: kubernetes.core
version: 4.0.0 version: 3.0.0
``` ```
### Installing the Kubernetes Python Library ### Installing the Kubernetes Python Library

View File

@@ -854,45 +854,3 @@ releases:
- 652-fix-json-patch-action.yml - 652-fix-json-patch-action.yml
- 654-helm-expand-user.yml - 654-helm-expand-user.yml
release_date: '2024-05-16' release_date: '2024-05-16'
4.0.0:
changes:
bugfixes:
- Resolve Collections util resource discovery fails when complex subresources
present (https://github.com/ansible-collections/kubernetes.core/pull/676).
- align `helmdiff_check()` function commandline rendering with the `deploy()`
function (https://github.com/ansible-collections/kubernetes.core/pull/670).
- avoid unsafe conditions in integration tests (https://github.com/ansible-collections/kubernetes.core/pull/665).
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability
with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
minor_changes:
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
- k8s - The module and K8sService were changed so warnings returned by the K8S
API are now displayed to the user.
release_summary: This major release brings several bug fixes. We have also removed
support for ``ansible-core<2.15`` and deprecated functions and class from
``module_utils/common.py``.
removed_features:
- k8s - Support for ``merge_type=json`` has been removed in version 4.0.0. Please
use ``kubernetes.core.k8s_json_patch`` instead (https://github.com/ansible-collections/kubernetes.core/pull/722).
- k8s_exec - the previously deprecated ``result.return_code`` return value has
been removed, consider using ``result.rc`` instead (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``K8sAnsibleMixin`` class
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``configuration_digest()``
function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``get_api_client()`` function
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
- module_utils/common.py - the previously deprecated ``unique_string()`` function
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
fragments:
- 20231206-fix-unsafe-condition-in-integration.yml
- 20240117-fix-helm-diff-cmd-line-rendering.yml
- 20240222-Collections-util-resource-discovery-fails-when-complex-subresources-present.yml
- 20240228-fix-helm-diff-with-reuse-values.yml
- 20240423-k8s-display-warnings-to-users.yml
- 4.0.0.yaml
- inventory-update_removal_date.yml
- k8s-merge_type-removed.yml
- module_utils-common-remove-deprecated-functions-and-class.yaml
release_date: '2024-05-24'

View File

@@ -17,7 +17,7 @@ DEPRECATED
---------- ----------
:Removed in collection release after :Removed in collection release after
:Why: As discussed in https://github.com/ansible-collections/kubernetes.core/issues/31, we decided to :Why: As discussed in https://github.com/ansible-collections/kubernetes.core/issues/31, we decided to
remove the k8s inventory plugin in release 5.0.0. remove the k8s inventory plugin in release 4.0.0.
:Alternative: Use :ref:`kubernetes.core.k8s_info <kubernetes.core.k8s_info_module>` and :ref:`ansible.builtin.add_host <ansible.builtin.add_host_module>` instead. :Alternative: Use :ref:`kubernetes.core.k8s_info <kubernetes.core.k8s_info_module>` and :ref:`ansible.builtin.add_host <ansible.builtin.add_host_module>` instead.
@@ -357,7 +357,7 @@ Status
------ ------
- This inventory will be removed in version 5.0.0. *[deprecated]* - This inventory will be removed in version 4.0.0. *[deprecated]*
- For more information see `DEPRECATED`_. - For more information see `DEPRECATED`_.

View File

@@ -121,7 +121,7 @@ Parameters
<td> <td>
<div><code>apply</code> compares the desired resource definition with the previously supplied resource definition, ignoring properties that are automatically generated</div> <div><code>apply</code> compares the desired resource definition with the previously supplied resource definition, ignoring properties that are automatically generated</div>
<div><code>apply</code> works better with Services than &#x27;force=yes&#x27;</div> <div><code>apply</code> works better with Services than &#x27;force=yes&#x27;</div>
<div>Mutually exclusive with <code>merge_type</code>.</div> <div>mutually exclusive with <code>merge_type</code></div>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -513,17 +513,18 @@ Parameters
</td> </td>
<td> <td>
<ul style="margin: 0; padding: 0"><b>Choices:</b> <ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>json</li>
<li>merge</li> <li>merge</li>
<li>strategic-merge</li> <li>strategic-merge</li>
</ul> </ul>
</td> </td>
<td> <td>
<div>Whether to override the default patch merge approach with a specific type. By default, the strategic merge will typically be used.</div> <div>Whether to override the default patch merge approach with a specific type. By default, the strategic merge will typically be used.</div>
<div>For example, Custom Resource Definitions typically aren&#x27;t updatable by the usual strategic merge. You may want to use <code>merge</code> if you see &quot;strategic merge patch format is not supported&quot;.</div> <div>For example, Custom Resource Definitions typically aren&#x27;t updatable by the usual strategic merge. You may want to use <code>merge</code> if you see &quot;strategic merge patch format is not supported&quot;</div>
<div>See <a href='https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment'>https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment</a></div> <div>See <a href='https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment'>https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment</a></div>
<div>If more than one <code>merge_type</code> is given, the merge_types will be tried in order. This defaults to <code>[&#x27;strategic-merge&#x27;, &#x27;merge&#x27;]</code>, which is ideal for using the same parameters on resource kinds that combine Custom Resources and built-in resources.</div> <div>If more than one <code>merge_type</code> is given, the merge_types will be tried in order. This defaults to <code>[&#x27;strategic-merge&#x27;, &#x27;merge&#x27;]</code>, which is ideal for using the same parameters on resource kinds that combine Custom Resources and built-in resources.</div>
<div>Mutually exclusive with <code>apply</code>.</div> <div>mutually exclusive with <code>apply</code></div>
<div><em>merge_type=json</em> has been removed in version 4.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div> <div><em>merge_type=json</em> is deprecated and will be removed in version 4.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div>
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -25,7 +25,7 @@ tags:
- openshift - openshift
- okd - okd
- cluster - cluster
version: 4.0.0 version: 3.1.0
build_ignore: build_ignore:
- .DS_Store - .DS_Store
- "*.tar.gz" - "*.tar.gz"

View File

@@ -22,10 +22,10 @@ plugin_routing:
redirect: community.okd.openshift redirect: community.okd.openshift
k8s: k8s:
deprecation: deprecation:
removal_version: 5.0.0 removal_version: 4.0.0
warning_text: >- warning_text: >-
The k8s inventory plugin has been deprecated and The k8s inventory plugin has been deprecated and
will be removed in release 5.0.0. will be removed in release 4.0.0.
modules: modules:
k8s_auth: k8s_auth:
redirect: community.okd.k8s_auth redirect: community.okd.k8s_auth

View File

@@ -20,10 +20,10 @@ DOCUMENTATION = """
- Uses k8s.(yml|yaml) YAML configuration file to set parameter values. - Uses k8s.(yml|yaml) YAML configuration file to set parameter values.
deprecated: deprecated:
removed_in: 5.0.0 removed_in: 4.0.0
why: | why: |
As discussed in U(https://github.com/ansible-collections/kubernetes.core/issues/31), we decided to As discussed in U(https://github.com/ansible-collections/kubernetes.core/issues/31), we decided to
remove the k8s inventory plugin in release 5.0.0. remove the k8s inventory plugin in release 4.0.0.
alternative: "Use M(kubernetes.core.k8s_info) and M(ansible.builtin.add_host) instead." alternative: "Use M(kubernetes.core.k8s_info) and M(ansible.builtin.add_host) instead."
options: options:
@@ -125,19 +125,19 @@ import json
from ansible.errors import AnsibleError from ansible.errors import AnsibleError
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
try: HAS_K8S_MODULE_HELPER,
from kubernetes.dynamic.exceptions import DynamicApiError k8s_import_exception,
)
HAS_K8S_MODULE_HELPER = True
k8s_import_exception = None
except ImportError as e:
HAS_K8S_MODULE_HELPER = False
k8s_import_exception = e
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client, get_api_client,
) )
try:
from kubernetes.dynamic.exceptions import DynamicApiError
except ImportError:
pass
def format_dynamic_api_exc(exc): def format_dynamic_api_exc(exc):
if exc.body: if exc.body:
@@ -164,8 +164,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
super(InventoryModule, self).parse(inventory, loader, path) super(InventoryModule, self).parse(inventory, loader, path)
self.display.deprecated( self.display.deprecated(
"The 'k8s' inventory plugin has been deprecated and will be removed in release 5.0.0", "The 'k8s' inventory plugin has been deprecated and will be removed in release 4.0.0",
version="5.0.0", version="4.0.0",
collection_name="kubernetes.core", collection_name="kubernetes.core",
) )
cache_key = self._get_cache_prefix(path) cache_key = self._get_cache_prefix(path)

View File

@@ -149,7 +149,6 @@ def k8s_apply(resource, definition, **kwargs):
force_conflicts=kwargs.get("force_conflicts"), force_conflicts=kwargs.get("force_conflicts"),
field_manager=kwargs.get("field_manager"), field_manager=kwargs.get("field_manager"),
dry_run=kwargs.get("dry_run"), dry_run=kwargs.get("dry_run"),
serialize=kwargs.get("serialize"),
) )
if not existing: if not existing:
return resource.create( return resource.create(
@@ -159,7 +158,6 @@ def k8s_apply(resource, definition, **kwargs):
return resource.get( return resource.get(
name=definition["metadata"]["name"], name=definition["metadata"]["name"],
namespace=definition["metadata"].get("namespace"), namespace=definition["metadata"].get("namespace"),
**kwargs
) )
return resource.patch( return resource.patch(
body=desired, body=desired,

File diff suppressed because it is too large Load Diff

View File

@@ -139,7 +139,6 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
result = {"changed": False, "result": {}} result = {"changed": False, "result": {}}
instance = {} instance = {}
warnings = []
resource = svc.find_resource(kind, api_version, fail=True) resource = svc.find_resource(kind, api_version, fail=True)
definition["kind"] = resource.kind definition["kind"] = resource.kind
@@ -173,7 +172,7 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
return result return result
if params.get("apply"): if params.get("apply"):
instance, warnings = svc.apply(resource, definition, existing) instance = svc.apply(resource, definition, existing)
result["method"] = "apply" result["method"] = "apply"
elif not existing: elif not existing:
if state == "patched": if state == "patched":
@@ -184,19 +183,16 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
) )
) )
return result return result
instance, warnings = svc.create(resource, definition) instance = svc.create(resource, definition)
result["method"] = "create" result["method"] = "create"
result["changed"] = True result["changed"] = True
elif params.get("force", False): elif params.get("force", False):
instance, warnings = svc.replace(resource, definition, existing) instance = svc.replace(resource, definition, existing)
result["method"] = "replace" result["method"] = "replace"
else: else:
instance, warnings = svc.update(resource, definition, existing) instance = svc.update(resource, definition, existing)
result["method"] = "update" result["method"] = "update"
if warnings:
result["warnings"] = warnings
# If needed, wait and/or create diff # If needed, wait and/or create diff
success = True success = True

View File

@@ -2,8 +2,6 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import copy import copy
from json import loads
from re import compile
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from ansible.module_utils.common.dict_transformations import dict_merge from ansible.module_utils.common.dict_transformations import dict_merge
@@ -144,12 +142,18 @@ class K8sService:
name: str, name: str,
namespace: str, namespace: str,
merge_type: str = None, merge_type: str = None,
) -> Tuple[Dict, List[str]]: ) -> Dict:
if merge_type == "json":
self.module.deprecate(
msg="json as a merge_type value is deprecated. Please use the k8s_json_patch module instead.",
version="4.0.0",
collection_name="kubernetes.core",
)
try: try:
params = dict(name=name, namespace=namespace, serialize=False) params = dict(name=name, namespace=namespace)
if merge_type: if merge_type:
params["content_type"] = "application/{0}-patch+json".format(merge_type) params["content_type"] = "application/{0}-patch+json".format(merge_type)
return decode_response(self.client.patch(resource, definition, **params)) return self.client.patch(resource, definition, **params).to_dict()
except Exception as e: except Exception as e:
reason = e.body if hasattr(e, "body") else e reason = e.body if hasattr(e, "body") else e
msg = "Failed to patch object: {0}".format(reason) msg = "Failed to patch object: {0}".format(reason)
@@ -326,124 +330,123 @@ class K8sService:
result["resources"].append(hide_fields(res, hidden_fields)) result["resources"].append(hide_fields(res, hidden_fields))
return result return result
def create(self, resource: Resource, definition: Dict) -> Tuple[Dict, List[str]]: def create(self, resource: Resource, definition: Dict) -> Dict:
namespace = definition["metadata"].get("namespace") namespace = definition["metadata"].get("namespace")
name = definition["metadata"].get("name") name = definition["metadata"].get("name")
if self._client_side_dry_run: if self._client_side_dry_run:
return _encode_stringdata(definition), [] k8s_obj = _encode_stringdata(definition)
else:
try: try:
return decode_response( k8s_obj = self.client.create(
self.client.create( resource, definition, namespace=namespace
resource, definition, namespace=namespace, serialize=False ).to_dict()
except ConflictError:
# Some resources, like ProjectRequests, can't be created multiple times,
# because the resources that they create don't match their kind
# In this case we'll mark it as unchanged and warn the user
self.module.warn(
"{0} was not found, but creating it returned a 409 Conflict error. This can happen \
if the resource you are creating does not directly create a resource of the same kind.".format(
name
)
) )
) return dict()
except ConflictError: except Exception as e:
# Some resources, like ProjectRequests, can't be created multiple times, reason = e.body if hasattr(e, "body") else e
# because the resources that they create don't match their kind msg = "Failed to create object: {0}".format(reason)
# In this case we'll mark it as unchanged and warn the user raise CoreException(msg) from e
self.module.warn( return k8s_obj
"{0} was not found, but creating it returned a 409 Conflict error. This can happen \
if the resource you are creating does not directly create a resource of the same kind.".format(
name
)
)
return dict(), []
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to create object: {0}".format(reason)
raise CoreException(msg) from e
def apply( def apply(
self, self,
resource: Resource, resource: Resource,
definition: Dict, definition: Dict,
existing: Optional[ResourceInstance] = None, existing: Optional[ResourceInstance] = None,
) -> Tuple[Dict, List[str]]: ) -> Dict:
namespace = definition["metadata"].get("namespace") namespace = definition["metadata"].get("namespace")
server_side_apply = self.module.params.get("server_side_apply") server_side_apply = self.module.params.get("server_side_apply")
if server_side_apply: if server_side_apply:
requires("kubernetes", "19.15.0", reason="to use server side apply") requires("kubernetes", "19.15.0", reason="to use server side apply")
if self._client_side_dry_run: if self._client_side_dry_run:
ignored, patch = apply_object(resource, _encode_stringdata(definition)) ignored, patch = apply_object(resource, _encode_stringdata(definition))
if existing: if existing:
return dict_merge(existing.to_dict(), patch), [] k8s_obj = dict_merge(existing.to_dict(), patch)
else: else:
return patch, [] k8s_obj = patch
else:
try: try:
params = {} params = {}
if server_side_apply: if server_side_apply:
params["server_side"] = True params["server_side"] = True
params.update(server_side_apply) params.update(server_side_apply)
return decode_response( k8s_obj = self.client.apply(
self.client.apply( resource, definition, namespace=namespace, **params
resource, definition, namespace=namespace, serialize=False, **params ).to_dict()
) except Exception as e:
) reason = e.body if hasattr(e, "body") else e
except Exception as e: msg = "Failed to apply object: {0}".format(reason)
reason = e.body if hasattr(e, "body") else e raise CoreException(msg) from e
msg = "Failed to apply object: {0}".format(reason) return k8s_obj
raise CoreException(msg) from e
def replace( def replace(
self, self,
resource: Resource, resource: Resource,
definition: Dict, definition: Dict,
existing: ResourceInstance, existing: ResourceInstance,
) -> Tuple[Dict, List[str]]: ) -> Dict:
append_hash = self.module.params.get("append_hash", False) append_hash = self.module.params.get("append_hash", False)
name = definition["metadata"].get("name") name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace") namespace = definition["metadata"].get("namespace")
if self._client_side_dry_run: if self._client_side_dry_run:
return _encode_stringdata(definition), [] k8s_obj = _encode_stringdata(definition)
else:
try: try:
return decode_response( k8s_obj = self.client.replace(
self.client.replace(
resource, resource,
definition, definition,
name=name, name=name,
namespace=namespace, namespace=namespace,
append_hash=append_hash, append_hash=append_hash,
serialize=False, ).to_dict()
) except Exception as e:
) reason = e.body if hasattr(e, "body") else e
except Exception as e: msg = "Failed to replace object: {0}".format(reason)
reason = e.body if hasattr(e, "body") else e raise CoreException(msg) from e
msg = "Failed to replace object: {0}".format(reason) return k8s_obj
raise CoreException(msg) from e
def update( def update(
self, resource: Resource, definition: Dict, existing: ResourceInstance self, resource: Resource, definition: Dict, existing: ResourceInstance
) -> Tuple[Dict, List[str]]: ) -> Dict:
name = definition["metadata"].get("name") name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace") namespace = definition["metadata"].get("namespace")
if self._client_side_dry_run: if self._client_side_dry_run:
return dict_merge(existing.to_dict(), _encode_stringdata(definition)), [] k8s_obj = dict_merge(existing.to_dict(), _encode_stringdata(definition))
else:
exception = None exception = None
for merge_type in self.module.params.get("merge_type") or [ for merge_type in self.module.params.get("merge_type") or [
"strategic-merge", "strategic-merge",
"merge", "merge",
]: ]:
try: try:
return self.patch_resource( k8s_obj = self.patch_resource(
resource, resource,
definition, definition,
name, name,
namespace, namespace,
merge_type=merge_type, merge_type=merge_type,
) )
except CoreException as e: exception = None
exception = e except CoreException as e:
continue exception = e
raise exception continue
break
if exception:
raise exception
return k8s_obj
def delete( def delete(
self, self,
@@ -540,83 +543,3 @@ def hide_field(definition: dict, hidden_field: str) -> dict:
else: else:
del definition[split[0]] del definition[split[0]]
return definition return definition
def decode_response(resp) -> Tuple[Dict, List[str]]:
"""
This function decodes unserialized responses from the Kubernetes python
client and decodes the RFC2616 14.46 warnings found in the response
headers.
"""
obj = ResourceInstance(None, loads(resp.data.decode("utf8"))).to_dict()
warnings = []
if (
resp.headers is not None
and "warning" in resp.headers
and resp.headers["warning"] is not None
):
warnings = resp.headers["warning"].split(", ")
return obj, decode_warnings(warnings)
def decode_warnings(warnings: str) -> List[str]:
"""
This function decodes RFC2616 14.46 warnings in a simplified way, where
only the warn-texts are returned in a list.
"""
p = compile('\\d{3} .+ (".+")')
decoded = []
for warning in warnings:
m = p.match(warning)
if m:
try:
parsed, unused = parse_quoted_string(m.group(1))
decoded.append(parsed)
except ValueError:
continue
return decoded
def parse_quoted_string(quoted_string: str) -> Tuple[str, str]:
"""
This function was adapted from:
https://github.com/kubernetes/apimachinery/blob/bb8822152cabfb4f34dbc26270f874ce53db50de/pkg/util/net/http.go#L609
"""
if len(quoted_string) == 0:
raise ValueError("invalid quoted string: 0-length")
if quoted_string[0] != '"':
raise ValueError("invalid quoted string: missing initial quote")
quoted_string = quoted_string[1:]
remainder = ""
escaping = False
closed_quote = False
result = []
for i, b in enumerate(quoted_string):
if b == '"':
if escaping:
result.append(b)
escaping = False
else:
closed_quote = True
remainder_start = i + 1
remainder = quoted_string[remainder_start:].strip()
break
elif b == "\\":
if escaping:
result.append(b)
escaping = False
else:
escaping = True
else:
result.append(b)
escaping = False
if not closed_quote:
raise ValueError("invalid quoted string: missing closing quote")
return "".join(result), remainder

View File

@@ -57,14 +57,15 @@ options:
- Whether to override the default patch merge approach with a specific type. By default, the strategic - Whether to override the default patch merge approach with a specific type. By default, the strategic
merge will typically be used. merge will typically be used.
- For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may - For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may
want to use C(merge) if you see "strategic merge patch format is not supported". want to use C(merge) if you see "strategic merge patch format is not supported"
- See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment) - See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)
- If more than one C(merge_type) is given, the merge_types will be tried in order. This defaults to - If more than one C(merge_type) is given, the merge_types will be tried in order. This defaults to
C(['strategic-merge', 'merge']), which is ideal for using the same parameters on resource kinds that C(['strategic-merge', 'merge']), which is ideal for using the same parameters on resource kinds that
combine Custom Resources and built-in resources. combine Custom Resources and built-in resources.
- Mutually exclusive with C(apply). - mutually exclusive with C(apply)
- I(merge_type=json) has been removed in version 4.0.0. Please use M(kubernetes.core.k8s_json_patch) instead. - I(merge_type=json) is deprecated and will be removed in version 4.0.0. Please use M(kubernetes.core.k8s_json_patch) instead.
choices: choices:
- json
- merge - merge
- strategic-merge - strategic-merge
type: list type: list
@@ -100,7 +101,7 @@ options:
- C(apply) compares the desired resource definition with the previously supplied resource definition, - C(apply) compares the desired resource definition with the previously supplied resource definition,
ignoring properties that are automatically generated ignoring properties that are automatically generated
- C(apply) works better with Services than 'force=yes' - C(apply) works better with Services than 'force=yes'
- Mutually exclusive with C(merge_type). - mutually exclusive with C(merge_type)
default: False default: False
type: bool type: bool
template: template:
@@ -459,7 +460,7 @@ def argspec():
argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC)) argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC))
argument_spec.update(copy.deepcopy(WAIT_ARG_SPEC)) argument_spec.update(copy.deepcopy(WAIT_ARG_SPEC))
argument_spec["merge_type"] = dict( argument_spec["merge_type"] = dict(
type="list", elements="str", choices=["merge", "strategic-merge"] type="list", elements="str", choices=["json", "merge", "strategic-merge"]
) )
argument_spec["validate"] = dict(type="dict", default=None, options=validate_spec()) argument_spec["validate"] = dict(type="dict", default=None, options=validate_spec())
argument_spec["append_hash"] = dict(type="bool", default=False) argument_spec["append_hash"] = dict(type="bool", default=False)

View File

@@ -135,7 +135,7 @@ from ansible.module_utils._text import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import ( from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule, AnsibleModule,
) )
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import ( from ansible_collections.kubernetes.core.plugins.module_utils.common import (
AUTH_ARG_SPEC, AUTH_ARG_SPEC,
) )
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
@@ -219,6 +219,12 @@ def execute_module(module, client):
else: else:
rc = int(err["details"]["causes"][0]["message"]) rc = int(err["details"]["causes"][0]["message"])
module.deprecate(
"The 'return_code' return key is being renamed to 'rc'. "
"Both keys are being returned for now to allow users to migrate their automation.",
version="4.0.0",
collection_name="kubernetes.core",
)
module.exit_json( module.exit_json(
# Some command might change environment, but ultimately failing at end # Some command might change environment, but ultimately failing at end
changed=True, changed=True,

View File

@@ -73,7 +73,7 @@
assert: assert:
that: that:
- install is changed - install is changed
- install.status.chart == chart_test+"-"+chart_test_version - install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- name: Check helm_info content - name: Check helm_info content
@@ -95,7 +95,7 @@
- name: "Assert that {{ chart_test }} is installed from {{ source }} with helm_info" - name: "Assert that {{ chart_test }} is installed from {{ source }} with helm_info"
assert: assert:
that: that:
- content_info.status.chart == chart_test+"-"+chart_test_version - content_info.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- content_info.status.status | lower == 'deployed' - content_info.status.status | lower == 'deployed'
- release_state_content_info.status.status | lower == 'deployed' - release_state_content_info.status.status | lower == 'deployed'
@@ -112,7 +112,7 @@
assert: assert:
that: that:
- install is not changed - install is not changed
- install.status.chart == chart_test+"-"+chart_test_version - install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- name: "Add vars to {{ chart_test }} from {{ source }}" - name: "Add vars to {{ chart_test }} from {{ source }}"
@@ -130,7 +130,7 @@
that: that:
- install is changed - install is changed
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- install.status.chart == chart_test+"-"+chart_test_version - install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- "install.status['values'].revisionHistoryLimit == 0" - "install.status['values'].revisionHistoryLimit == 0"
- name: Check idempotency after adding vars - name: Check idempotency after adding vars
@@ -148,7 +148,7 @@
that: that:
- install is not changed - install is not changed
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- install.status.chart == chart_test+"-"+chart_test_version - install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- "install.status['values'].revisionHistoryLimit == 0" - "install.status['values'].revisionHistoryLimit == 0"
- name: "Remove Vars to {{ chart_test }} from {{ source }}" - name: "Remove Vars to {{ chart_test }} from {{ source }}"
@@ -165,7 +165,7 @@
that: that:
- install is changed - install is changed
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- install.status.chart == chart_test+"-"+chart_test_version - install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- install.status['values'] == {} - install.status['values'] == {}
- name: Check idempotency after removing vars - name: Check idempotency after removing vars
@@ -182,7 +182,7 @@
that: that:
- install is not changed - install is not changed
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- install.status.chart == chart_test+"-"+chart_test_version - install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- install.status['values'] == {} - install.status['values'] == {}
- name: "Upgrade {{ chart_test }} from {{ source }}" - name: "Upgrade {{ chart_test }} from {{ source }}"
@@ -199,7 +199,7 @@
that: that:
- install is changed - install is changed
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- install.status.chart == chart_test+"-"+chart_test_version_upgrade - install.status.chart == "{{ chart_test }}-{{ chart_test_version_upgrade }}"
- name: Check idempotency after upgrade - name: Check idempotency after upgrade
helm: helm:
@@ -215,7 +215,7 @@
that: that:
- install is not changed - install is not changed
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- install.status.chart == chart_test+"-"+chart_test_version_upgrade - install.status.chart == "{{ chart_test }}-{{ chart_test_version_upgrade }}"
- name: "Remove {{ chart_test }} from {{ source }}" - name: "Remove {{ chart_test }} from {{ source }}"
helm: helm:
@@ -316,7 +316,7 @@
that: that:
- install is changed - install is changed
- install.status.status | lower == 'deployed' - install.status.status | lower == 'deployed'
- install.status.chart == chart_test+"-"+chart_test_version - install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
- "install.status['values'].revisionHistoryLimit == 0" - "install.status['values'].revisionHistoryLimit == 0"
- name: "Install {{ chart_test }} from {{ source }} with values_files (again)" - name: "Install {{ chart_test }} from {{ source }} with values_files (again)"
@@ -357,7 +357,7 @@
- result is changed - result is changed
- result is not failed - result is not failed
- result.rc == 0 - result.rc == 0
- result.command is match(helm_binary+" template "+chart_source) - result.command is match("{{ helm_binary }} template {{ chart_source }}")
- name: Check templates created - name: Check templates created
stat: stat:
@@ -388,7 +388,7 @@
- result is changed - result is changed
- result is not failed - result is not failed
- result.rc == 0 - result.rc == 0
- result.command is match(helm_binary+" template MyRelease "+chart_source) - result.command is match("{{ helm_binary }} template MyRelease {{ chart_source }}")
- result.stdout is search("ThisValue") - result.stdout is search("ThisValue")
when: chart_source is search("test-chart") when: chart_source is search("test-chart")
# limit assertion of test result to controlled (local) chart_source # limit assertion of test result to controlled (local) chart_source

View File

@@ -182,7 +182,7 @@
- name: assert that pods are running on cordoned node - name: assert that pods are running on cordoned node
assert: assert:
that: that:
- Pod.resources | selectattr('status.phase', 'equalto', 'Running') | selectattr('spec.nodeName', 'equalto', node_to_drain) | list | length > 0 - "{{ Pod.resources | selectattr('status.phase', 'equalto', 'Running') | selectattr('spec.nodeName', 'equalto', node_to_drain) | list | length > 0 }}"
- name: Uncordon node - name: Uncordon node
k8s_drain: k8s_drain:
@@ -236,7 +236,7 @@
assert: assert:
that: that:
- drain_result is changed - drain_result is changed
- '"node "+node_to_drain+" marked unschedulable." in drain_result.result' - '"node {{ node_to_drain }} marked unschedulable." in drain_result.result'
- name: assert that unmanaged pod were deleted - name: assert that unmanaged pod were deleted
k8s_info: k8s_info:
@@ -338,7 +338,7 @@
assert: assert:
that: that:
- disable_evict is changed - disable_evict is changed
- '"node "+node_to_drain+" marked unschedulable." in disable_evict.result' - '"node {{ node_to_drain }} marked unschedulable." in disable_evict.result'
- name: assert that unmanaged pod were deleted - name: assert that unmanaged pod were deleted
k8s_info: k8s_info:
@@ -401,7 +401,7 @@
assert: assert:
that: that:
- drain_pod_selector is changed - drain_pod_selector is changed
- '"node "+node_to_drain+" marked unschedulable." in drain_pod_selector.result' - '"node {{ node_to_drain }} marked unschedulable." in drain_pod_selector.result'
- name: assert that pod created before is still running - name: assert that pod created before is still running
k8s_info: k8s_info:
@@ -429,8 +429,8 @@
assert: assert:
that: that:
- drain_pod_selector_equal is changed - drain_pod_selector_equal is changed
- '"node "+node_to_drain+" already marked unschedulable." in drain_pod_selector_equal.result' - '"node {{ node_to_drain }} already marked unschedulable." in drain_pod_selector_equal.result'
- '"Deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: "+test_namespace+"/ansible-drain-pod." in drain_pod_selector_equal.warnings' - '"Deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: {{ test_namespace }}/ansible-drain-pod." in drain_pod_selector_equal.warnings'
- name: Uncordon node - name: Uncordon node
k8s_drain: k8s_drain:

View File

@@ -424,7 +424,7 @@
- assert: - assert:
that: that:
- result.resources[0].data.testkey == (cmap_data.stdout | b64encode) - result.resources[0].data.testkey == "{{ cmap_data.stdout | b64encode }}"
# test setting module defaults for kubernetes.core.k8s_info # test setting module defaults for kubernetes.core.k8s_info
- block: - block:

View File

@@ -69,7 +69,7 @@
- name: assert pod has been created - name: assert pod has been created
assert: assert:
that: that:
- pods.resources | length == 1 - "{{ pods.resources | length == 1 }}"
- name: create pod using generate_name parameter should succeed - name: create pod using generate_name parameter should succeed
k8s: k8s:
@@ -86,7 +86,7 @@
- name: assert pod has been created - name: assert pod has been created
assert: assert:
that: that:
- pods.resources | length == 2 - "{{ pods.resources | length == 2 }}"
- name: create pod using metadata.generateName parameter should succeed - name: create pod using metadata.generateName parameter should succeed
k8s: k8s:
@@ -102,7 +102,7 @@
- name: assert pod has been created - name: assert pod has been created
assert: assert:
that: that:
- pods.resources | length == 3 - "{{ pods.resources | length == 3 }}"
- name: create object using metadata.generateName should support wait option - name: create object using metadata.generateName should support wait option
k8s: k8s:

View File

@@ -11,7 +11,7 @@
assert: assert:
that: that:
- fake_pod is failed - fake_pod is failed
- fake_pod.msg == "Pod "+test_namespace+"/this_pod_does_exist not found." - 'fake_pod.msg == "Pod {{ test_namespace }}/this_pod_does_exist not found."'
- name: create hello-world deployment - name: create hello-world deployment
k8s: k8s:

View File

@@ -65,7 +65,7 @@
assert: assert:
that: that:
- _result.result.status.phase == 'Running' - _result.result.status.phase == 'Running'
- _result.result.spec.nodeName == node_to_taint - _result.result.spec.nodeName == "{{ node_to_taint }}"
- name: Taint node (check_mode) - name: Taint node (check_mode)
kubernetes.core.k8s_taint: kubernetes.core.k8s_taint:
@@ -89,8 +89,8 @@
assert: assert:
that: that:
- _result.changed - _result.changed
- item['effect'] == taint_patch_1[0]['effect'] - "{{ item['effect'] == taint_patch_1[0]['effect'] }}"
- item['key'] == taint_patch_1[0]['key'] - "{{ item['key'] == taint_patch_1[0]['key'] }}"
loop: "{{ _result.result.spec.taints }}" loop: "{{ _result.result.spec.taints }}"
- name: Taint node (idempotency) - (check_mode) - name: Taint node (idempotency) - (check_mode)

View File

@@ -213,8 +213,8 @@
- name: check that resources creation failed - name: check that resources creation failed
assert: assert:
that: that:
- resource.results.0.resources | length == 0 - '{{ resource.results[0].resources | length == 0 }}'
- resource.results.1.resources | length == 0 - '{{ resource.results[1].resources | length == 0 }}'
- name: create pod without namespace (continue_on_error = true) - name: create pod without namespace (continue_on_error = true)
kubernetes.core.k8s: kubernetes.core.k8s:

View File

@@ -130,9 +130,9 @@
assert: assert:
that: that:
- result_configmap.apiVersion == 'v1' - result_configmap.apiVersion == 'v1'
- result_configmap.metadata.name == configmap_name - result_configmap.metadata.name == "{{ configmap_name }}"
- result_configmap.metadata.namespace == test_namespace[2] - result_configmap.metadata.namespace == "{{ test_namespace[2] }}"
- result_configmap.data.value == configmap_data - result_configmap.data.value == "{{ configmap_data }}"
# test lookup plugin using src parameter # test lookup plugin using src parameter
- block: - block:
@@ -159,9 +159,9 @@
assert: assert:
that: that:
- src_configmap.apiVersion == 'v1' - src_configmap.apiVersion == 'v1'
- src_configmap.metadata.name == configmap_name - src_configmap.metadata.name == "{{ configmap_name }}"
- src_configmap.metadata.namespace == test_namespace[2] - src_configmap.metadata.namespace == "{{ test_namespace[2] }}"
- src_configmap.data.value == configmap_data - src_configmap.data.value == "{{ configmap_data }}"
always: always:
- name: Delete temporary file created - name: Delete temporary file created
@@ -198,9 +198,9 @@
assert: assert:
that: that:
- configmap_no_ssl.apiVersion == 'v1' - configmap_no_ssl.apiVersion == 'v1'
- configmap_no_ssl.metadata.name == configmap_name - configmap_no_ssl.metadata.name == "{{ configmap_name }}"
- configmap_no_ssl.metadata.namespace == test_namespace[2] - configmap_no_ssl.metadata.namespace == "{{ test_namespace[2] }}"
- configmap_no_ssl.data.value == configmap_data - configmap_no_ssl.data.value == "{{ configmap_data }}"
- name: Retrieve configmap using authentication aliases (validate_certs=true) - name: Retrieve configmap using authentication aliases (validate_certs=true)
set_fact: set_fact:
@@ -210,9 +210,9 @@
assert: assert:
that: that:
- configmap_with_ssl.apiVersion == 'v1' - configmap_with_ssl.apiVersion == 'v1'
- configmap_with_ssl.metadata.name == configmap_name - configmap_with_ssl.metadata.name == "{{ configmap_name }}"
- configmap_with_ssl.metadata.namespace == test_namespace[2] - configmap_with_ssl.metadata.namespace == "{{ test_namespace[2] }}"
- configmap_with_ssl.data.value == configmap_data - configmap_with_ssl.data.value == "{{ configmap_data }}"
always: always:
- name: Delete temporary directory - name: Delete temporary directory

View File

@@ -1,9 +1,18 @@
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py import-3.10!skip plugins/module_utils/client/discovery.py import-3.10!skip
plugins/module_utils/client/discovery.py import-3.11!skip plugins/module_utils/client/discovery.py import-3.11!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/client/resource.py import-3.10!skip plugins/module_utils/client/resource.py import-3.10!skip
plugins/module_utils/client/resource.py import-3.11!skip plugins/module_utils/client/resource.py import-3.11!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py import-3.10!skip plugins/module_utils/k8sdynamicclient.py import-3.10!skip
plugins/module_utils/k8sdynamicclient.py import-3.11!skip plugins/module_utils/k8sdynamicclient.py import-3.11!skip

View File

@@ -1,9 +1,18 @@
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py import-3.10!skip plugins/module_utils/client/discovery.py import-3.10!skip
plugins/module_utils/client/discovery.py import-3.11!skip plugins/module_utils/client/discovery.py import-3.11!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/client/resource.py import-3.10!skip plugins/module_utils/client/resource.py import-3.10!skip
plugins/module_utils/client/resource.py import-3.11!skip plugins/module_utils/client/resource.py import-3.11!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py import-3.10!skip plugins/module_utils/k8sdynamicclient.py import-3.10!skip
plugins/module_utils/k8sdynamicclient.py import-3.11!skip plugins/module_utils/k8sdynamicclient.py import-3.11!skip

View File

@@ -1,11 +1,20 @@
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py import-3.10!skip plugins/module_utils/client/discovery.py import-3.10!skip
plugins/module_utils/client/discovery.py import-3.11!skip plugins/module_utils/client/discovery.py import-3.11!skip
plugins/module_utils/client/discovery.py import-3.12!skip plugins/module_utils/client/discovery.py import-3.12!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/client/resource.py import-3.10!skip plugins/module_utils/client/resource.py import-3.10!skip
plugins/module_utils/client/resource.py import-3.11!skip plugins/module_utils/client/resource.py import-3.11!skip
plugins/module_utils/client/resource.py import-3.12!skip plugins/module_utils/client/resource.py import-3.12!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py import-3.10!skip plugins/module_utils/k8sdynamicclient.py import-3.10!skip
plugins/module_utils/k8sdynamicclient.py import-3.11!skip plugins/module_utils/k8sdynamicclient.py import-3.11!skip

View File

@@ -31,7 +31,7 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
@pytest.mark.parametrize( @pytest.mark.parametrize(
"action, params, existing, instance_warnings, expected", "action, params, existing, instance, expected",
[ [
( (
"delete", "delete",
@@ -51,26 +51,14 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
"apply", "apply",
{"apply": "yes"}, {"apply": "yes"},
{}, {},
(definition, []), definition,
{"changed": True, "method": "apply", "result": definition}, {"changed": True, "method": "apply", "result": definition},
), ),
(
"apply",
{"apply": "yes"},
{},
(definition, ["test warning"]),
{
"changed": True,
"method": "apply",
"result": definition,
"warnings": ["test warning"],
},
),
( (
"create", "create",
{"state": "patched"}, {"state": "patched"},
{}, {},
({}, []), {},
{ {
"changed": False, "changed": False,
"result": {}, "result": {},
@@ -83,78 +71,42 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
"create", "create",
{}, {},
{}, {},
(definition, []), definition,
{"changed": True, "method": "create", "result": definition}, {"changed": True, "method": "create", "result": definition},
), ),
(
"create",
{},
{},
(definition, ["test warning"]),
{
"changed": True,
"method": "create",
"result": definition,
"warnings": ["test warning"],
},
),
( (
"replace", "replace",
{"force": "yes"}, {"force": "yes"},
definition, definition,
(definition, []), definition,
{"changed": False, "method": "replace", "result": definition}, {"changed": False, "method": "replace", "result": definition},
), ),
( (
"replace", "replace",
{"force": "yes"}, {"force": "yes"},
definition, definition,
(modified_def, []), modified_def,
{"changed": True, "method": "replace", "result": modified_def}, {"changed": True, "method": "replace", "result": modified_def},
), ),
(
"replace",
{"force": "yes"},
definition,
(modified_def, ["test warning"]),
{
"changed": True,
"method": "replace",
"result": modified_def,
"warnings": ["test warning"],
},
),
( (
"update", "update",
{}, {},
definition, definition,
(definition, []), definition,
{"changed": False, "method": "update", "result": definition}, {"changed": False, "method": "update", "result": definition},
), ),
( (
"update", "update",
{}, {},
definition, definition,
(modified_def, []), modified_def,
{"changed": True, "method": "update", "result": modified_def}, {"changed": True, "method": "update", "result": modified_def},
), ),
(
"update",
{},
definition,
(modified_def, ["test warning"]),
{
"changed": True,
"method": "update",
"result": modified_def,
"warnings": ["test warning"],
},
),
( (
"create", "create",
{"label_selectors": ["app=foo"]}, {"label_selectors": ["app=foo"]},
{}, {},
(definition, []), definition,
{ {
"changed": False, "changed": False,
"msg": "resource 'kind=Pod,name=foo,namespace=foo' filtered by label_selectors.", "msg": "resource 'kind=Pod,name=foo,namespace=foo' filtered by label_selectors.",
@@ -164,18 +116,18 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
"create", "create",
{"label_selectors": ["app=nginx"]}, {"label_selectors": ["app=nginx"]},
{}, {},
(definition, []), definition,
{"changed": True, "method": "create", "result": definition}, {"changed": True, "method": "create", "result": definition},
), ),
], ],
) )
def test_perform_action(action, params, existing, instance_warnings, expected): def test_perform_action(action, params, existing, instance, expected):
svc = Mock() svc = Mock()
svc.find_resource.return_value = Mock( svc.find_resource.return_value = Mock(
kind=definition["kind"], group_version=definition["apiVersion"] kind=definition["kind"], group_version=definition["apiVersion"]
) )
svc.retrieve.return_value = ResourceInstance(None, existing) if existing else None svc.retrieve.return_value = ResourceInstance(None, existing) if existing else None
spec = {action + ".return_value": instance_warnings} spec = {action + ".return_value": instance}
svc.configure_mock(**spec) svc.configure_mock(**spec)
result = perform_action(svc, definition, params) result = perform_action(svc, definition, params)

View File

@@ -1,11 +1,9 @@
from json import dumps
from unittest.mock import Mock from unittest.mock import Mock
import pytest import pytest
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import ( from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService, K8sService,
diff_objects, diff_objects,
parse_quoted_string,
) )
from kubernetes.dynamic.exceptions import NotFoundError from kubernetes.dynamic.exceptions import NotFoundError
from kubernetes.dynamic.resource import Resource, ResourceInstance from kubernetes.dynamic.resource import Resource, ResourceInstance
@@ -59,22 +57,6 @@ def mock_pod_updated_resource_instance():
return ResourceInstance(None, pod_definition_updated) return ResourceInstance(None, pod_definition_updated)
@pytest.fixture(scope="module")
def mock_pod_response():
resp = Mock()
resp.data.decode.return_value = dumps(pod_definition)
resp.headers = {}
return resp
@pytest.fixture(scope="module")
def mock_pod_warnings_response():
resp = Mock()
resp.data.decode.return_value = dumps(pod_definition)
resp.headers = {"warning": '299 - "test warning 1", 299 - "test warning 2"'}
return resp
def test_diff_objects_no_diff(): def test_diff_objects_no_diff():
match, diff = diff_objects(pod_definition, pod_definition) match, diff = diff_objects(pod_definition, pod_definition)
@@ -177,33 +159,16 @@ def test_service_delete_existing_resource_check_mode(mock_pod_resource_instance)
client.delete.assert_not_called() client.delete.assert_not_called()
def test_service_create_resource(mock_pod_response, mock_pod_resource_instance): def test_service_create_resource(mock_pod_resource_instance):
spec = {"create.side_effect": [mock_pod_response]} spec = {"create.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec) client = Mock(**spec)
module = Mock() module = Mock()
module.params = {} module.params = {}
module.check_mode = False module.check_mode = False
svc = K8sService(client, module) svc = K8sService(client, module)
result, warnings = svc.create(Mock(), pod_definition) result = svc.create(Mock(), pod_definition)
assert result == mock_pod_resource_instance.to_dict() assert result == mock_pod_resource_instance.to_dict()
assert not warnings
def test_service_create_resource_warnings(
mock_pod_warnings_response, mock_pod_resource_instance
):
spec = {"create.side_effect": [mock_pod_warnings_response]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
result, warnings = svc.create(Mock(), pod_definition)
assert result == mock_pod_resource_instance.to_dict()
assert warnings[0] == "test warning 1"
assert warnings[1] == "test warning 2"
def test_service_create_resource_check_mode(): def test_service_create_resource_check_mode():
@@ -211,10 +176,9 @@ def test_service_create_resource_check_mode():
client.create.return_value = mock_pod_resource_instance client.create.return_value = mock_pod_resource_instance
module = Mock(params={}, check_mode=True) module = Mock(params={}, check_mode=True)
svc = K8sService(client, module) svc = K8sService(client, module)
result, warnings = svc.create(Mock(), pod_definition) result = svc.create(Mock(), pod_definition)
assert result == pod_definition assert result == pod_definition
assert not warnings
client.create.assert_not_called() client.create.assert_not_called()
@@ -260,99 +224,40 @@ def test_create_project_request():
assert results["result"] == project_definition assert results["result"] == project_definition
def test_service_apply_existing_resource(mock_pod_response, mock_pod_resource_instance): def test_service_apply_existing_resource(mock_pod_resource_instance):
spec = {"apply.side_effect": [mock_pod_response]} spec = {"apply.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec) client = Mock(**spec)
module = Mock() module = Mock()
module.params = {"apply": True} module.params = {"apply": True}
module.check_mode = False module.check_mode = False
svc = K8sService(client, module) svc = K8sService(client, module)
result, warnings = svc.apply( result = svc.apply(Mock(), pod_definition_updated, mock_pod_resource_instance)
Mock(), pod_definition_updated, mock_pod_resource_instance
)
assert result == mock_pod_resource_instance.to_dict() assert result == mock_pod_resource_instance.to_dict()
assert not warnings
def test_service_apply_existing_resource_warnings( def test_service_replace_existing_resource(mock_pod_resource_instance):
mock_pod_warnings_response, mock_pod_resource_instance spec = {"replace.side_effect": [mock_pod_resource_instance]}
):
spec = {"apply.side_effect": [mock_pod_warnings_response]}
client = Mock(**spec)
module = Mock()
module.params = {"apply": True}
module.check_mode = False
svc = K8sService(client, module)
result, warnings = svc.apply(
Mock(), pod_definition_updated, mock_pod_resource_instance
)
assert result == mock_pod_resource_instance.to_dict()
assert warnings[0] == "test warning 1"
assert warnings[1] == "test warning 2"
def test_service_replace_existing_resource(
mock_pod_response, mock_pod_resource_instance
):
spec = {"replace.side_effect": [mock_pod_response]}
client = Mock(**spec) client = Mock(**spec)
module = Mock() module = Mock()
module.params = {} module.params = {}
module.check_mode = False module.check_mode = False
svc = K8sService(client, module) svc = K8sService(client, module)
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance) result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
assert result == mock_pod_resource_instance.to_dict() assert result == mock_pod_resource_instance.to_dict()
assert not warnings
def test_service_replace_existing_resource_warnings( def test_service_update_existing_resource(mock_pod_resource_instance):
mock_pod_warnings_response, mock_pod_resource_instance spec = {"replace.side_effect": [mock_pod_resource_instance]}
):
spec = {"replace.side_effect": [mock_pod_warnings_response]}
client = Mock(**spec) client = Mock(**spec)
module = Mock() module = Mock()
module.params = {} module.params = {}
module.check_mode = False module.check_mode = False
svc = K8sService(client, module) svc = K8sService(client, module)
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance) result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
assert result == mock_pod_resource_instance.to_dict() assert result == mock_pod_resource_instance.to_dict()
assert warnings[0] == "test warning 1"
assert warnings[1] == "test warning 2"
def test_service_update_existing_resource(
mock_pod_response, mock_pod_resource_instance
):
spec = {"replace.side_effect": [mock_pod_response]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
assert result == mock_pod_resource_instance.to_dict()
assert not warnings
def test_service_update_existing_resource_warnings(
mock_pod_warnings_response, mock_pod_resource_instance
):
spec = {"replace.side_effect": [mock_pod_warnings_response]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
assert result == mock_pod_resource_instance.to_dict()
assert warnings[0] == "test warning 1"
assert warnings[1] == "test warning 2"
def test_service_find(mock_pod_resource_instance): def test_service_find(mock_pod_resource_instance):
@@ -383,24 +288,3 @@ def test_service_find_error():
assert isinstance(results, dict) assert isinstance(results, dict)
assert results["api_found"] is True assert results["api_found"] is True
assert results["resources"] == [] assert results["resources"] == []
@pytest.mark.parametrize(
"quoted_string,expected_val,expected_remainder",
[
(
'"Response is stale" Tue, 15 Nov 1994 12:45:26 GMT',
"Response is stale",
"Tue, 15 Nov 1994 12:45:26 GMT",
),
(
'"unknown field \\"spec.template.spec.disk\\""',
'unknown field "spec.template.spec.disk"',
"",
),
],
)
def test_parse_quoted_string(quoted_string, expected_val, expected_remainder):
val, remainder = parse_quoted_string(quoted_string)
assert val == expected_val
assert remainder == expected_remainder