Compare commits

28 Commits
5.1.0 ... 5.4.1

Author SHA1 Message Date
Mandar Kulkarni
b16bb15d34 prepare release 5.4.1 (#1009)
SUMMARY


ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
2025-10-07 13:07:26 +00:00
patchback[bot]
588f60adc8 update doc following #971 (#1006) (#1007)
This is a backport of PR #1006 as merged into main (200d64f).
SUMMARY
In the PR #971, support for copying files to initContainers, and this change includes a minor update for DOCUMENTATION for the k8s_cp module; however, docs/kubernetes.core.k8s_cp_module.rst wasn't updated, and it's a trivial change following the Updating documentation section of the CONTRIBUTING.md
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
docs/kubernetes.core.k8s_cp_module.rst
ADDITIONAL INFORMATION
As it is a trivial change and related to #971, I didn't created a chnagelog fragment and suggest adding skip-changelog label.
To be backported to stable-5 and stable-6

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-10-02 20:21:19 +00:00
patchback[bot]
deac7719c2 Add the PR head as reference to the checkout action of the splitter job. (#981) (#1004)
This is a backport of PR #981 as merged into main (ec35c74).
SUMMARY
During the workflow checks of one of my previous pull requests, I ran into an error as result of a new integration test that was added in the main branch but which was not present in my pull request (https://github.com/ansible-collections/kubernetes.core/actions/runs/16787189478/job/47540604109). I'd find out that the reference for the repository checkout for the splitter job was not set and therefor it referenced to the main branch.
With this change, the head of the pull request is used as reference for the repository checkout action, in the splitter job. This way it will not fail when someone creates a PR that doesn't have newly added integration tests that were added to the main branch in the meantime. It also adds that newly added integration tests will be tested during the PR workflow checks already.
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
bugfix in the workflow
ADDITIONAL INFORMATION
n/a

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-10-02 19:10:01 +00:00
patchback[bot]
cb4b792038 Added support for copying files to init Containers. (#971) (#1002)
This is a backport of PR #971 as merged into main (027700c).
SUMMARY
Was going trough the list with issues and found 958; which seemed a quick fix.
What I fixed with with this PR:

Added support for copying files to init containers.
Fixed the format message when an exec is failing for a pod (the order was wrong).
Added a check if the container that you try to run copy for is started.

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
copy.py module
ADDITIONAL INFORMATION
Some testing.
Verify that the pod does not exist:
kubectl -n default get pod/yorick
Output:
Error from server (NotFound): pods "yorick" not found

Run the playbook to create the file, create the deployment, wait for the init container to be ready, copy the created file to the init container, cat the copied file (using kubernetes.core.k8s_exec) that is now in the init container and try to copy the created file to the (not started) container (which fails - to see the new error message for it):
cat << EOF | ansible-playbook /dev/stdin
- hosts: localhost
  gather_facts: False
  tasks:

  - ansible.builtin.copy:
      content: |
        Hi there
      dest: /tmp/yorick.txt

  - name: Deploy pod with initContainer with an unlimited while loop
    kubernetes.core.k8s:
      kubeconfig: "~/.kube/config"
      definition:
        apiVersion: v1
        kind: Pod
        metadata:
          name: "yorick"
          namespace: "default"
        spec:
          initContainers:
            - name: "yorick-init"
              image: busybox:latest
              command: ["/bin/sh"]
              args:
                - "-c"
                - |
                  echo "Init container started, waiting for file..."
                  # Wait for the file to be copied
                  while :;do
                    echo "Waiting for file"
                    sleep 5
                  done
                  echo "File received! Init container completing..."
          containers:
            - name: "yorick-container"
              image: busybox:latest
              command: ["/bin/sh"]
              args:
                - "-c"
                - |
                  # Keep container running for testing
                  sleep 300

  - kubernetes.core.k8s_info:
      kubeconfig: "~/.kube/config"
      api_version: v1
      kind: Pod
      name: "yorick"
      namespace: "default"
    register: pod_status
    until: >-
      pod_status.resources|length > 0
      and 'initContainerStatuses' in pod_status.resources.0.status
      and pod_status.resources.0.status.initContainerStatuses|length > 0
      and pod_status.resources.0.status.initContainerStatuses.0.started|bool

  - name: Copy /tmp/yorick.txt to the yorick-init init container
    kubernetes.core.k8s_cp:
      kubeconfig: "~/.kube/config"
      namespace: default
      pod: yorick
      remote_path: /tmp/yorick.txt
      local_path: /tmp/yorick.txt
      container: yorick-init

  - name: Execute a command
    kubernetes.core.k8s_exec:
      kubeconfig: "~/.kube/config"
      namespace: default
      pod: yorick
      container: yorick-init
      command: cat /tmp/yorick.txt
    register: exec_out

  - ansible.builtin.debug:
      var: exec_out.stdout

  - name: Try to copy /tmp/yorick.txt to the yorick-container container
    kubernetes.core.k8s_cp:
      kubeconfig: "~/.kube/config"
      namespace: default
      pod: yorick
      remote_path: /tmp/yorick.txt
      local_path: /tmp/yorick.txt
      container: yorick-container
EOF
Output:
PLAY [localhost] ********************************************************************************************************************************************************************

TASK [ansible.builtin.copy] *********************************************************************************************************************************************************
Thursday 31 July 2025  02:01:21 +0200 (0:00:00.016)       0:00:00.016 *********
ok: [localhost]

TASK [Deploy pod with initContainer with an unlimited while loop] *******************************************************************************************************************
Thursday 31 July 2025  02:01:21 +0200 (0:00:00.788)       0:00:00.804 *********
changed: [localhost]

TASK [kubernetes.core.k8s_info] *****************************************************************************************************************************************************
Thursday 31 July 2025  02:01:25 +0200 (0:00:03.963)       0:00:04.768 *********
FAILED - RETRYING: [localhost]: kubernetes.core.k8s_info (3 retries left).
ok: [localhost]

TASK [Copy /tmp/yorick.txt to the yorick-init init container] ***********************************************************************************************************************
Thursday 31 July 2025  02:01:32 +0200 (0:00:06.598)       0:00:11.366 *********
changed: [localhost]

TASK [Execute a command] ************************************************************************************************************************************************************
Thursday 31 July 2025  02:01:39 +0200 (0:00:07.017)       0:00:18.383 *********
changed: [localhost]

TASK [ansible.builtin.debug] ********************************************************************************************************************************************************
Thursday 31 July 2025  02:01:40 +0200 (0:00:00.644)       0:00:19.028 *********
ok: [localhost] => {
    "exec_out.stdout": "Hi there\n"
}

TASK [Try to copy /tmp/yorick.txt to the yorick-container container] ****************************************************************************************************************
Thursday 31 July 2025  02:01:40 +0200 (0:00:00.021)       0:00:19.050 *********
fatal: [localhost]: FAILED! => {
    "changed": false
}

MSG:

Pod container yorick-container is not started

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

Playbook run took 0 days, 0 hours, 0 minutes, 21 seconds

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-09-25 15:10:52 +00:00
patchback[bot]
6215ac763b fix(k8s,service): Hide fields first before creating diffs (#915) (#962)
This is a backport of PR #915 as merged into main (6a0635a).
SUMMARY

By hiding fields first before creating a diff hidden fields will not be shown in the resulting diffs and therefore will also not trigger the changed condition.
The issue can only be reproduced when a mutating webhook changes the object while the kubernetes.core.k8s module is working with it.

kubevirt/kubevirt.core#145
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

kubernetes.core.module_utils.k8s.service
ADDITIONAL INFORMATION


Run kubernetes.core.k8s and create object with hidden fields. After run kubernetes.core.k8s again and let a webhook mutate the object that the module is working with. The module should return with changed: no.

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-09-24 20:36:59 +00:00
Bianca Henderson
d5599f1964 Minor updates to changelog to match stable-6 and main (#991)
This will make all stable-5 changelog content match main and stable-6 so there will no longer be formatting issues when doing release work in the future (basically a manual backport of #989).

Reviewed-by: Alina Buzachis
2025-09-22 19:58:15 +00:00
patchback[bot]
90134ec3e0 [CI Fix] Remove ansible.module_utils.six imports (#998) (#999)
This is a backport of PR #998 as merged into main (448d3fe).
SUMMARY
This PR is essentially attempting Option B from issue #996 (Option A is implemented here); this code update accounts for the recent merge of sanity: warn on ansible.module_utils.six imports #85651.

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Mandar Kulkarni <mandar242@gmail.com>
2025-09-22 16:54:06 +00:00
Bianca Henderson
dcbe52e722 Prep kubernetes.core 5.4.0 release (#970)
SUMMARY

Prep kubernetes.core 5.4.0 release

COMPONENT NAME
Multiple

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Alina Buzachis
Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
2025-08-12 16:56:58 +00:00
patchback[bot]
1e711d4da8 CI fix for 976 (#982) (#984)
This is a backport of PR #982 as merged into main (a861079).
SUMMARY
Exclude plugins/connection/kubectl.py from ansible-lint, as this file contains only examples that is simplificated and may not be accordingally to linter rules.
resolves #976
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
CI
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-08-12 15:41:03 +00:00
Bianca Henderson
4b2dc4f974 Reapply "Remove kubeconfig value from module invocation log (#826)" (#899) (#965) (#980)
This reverts commit eb0aeeb from stable-5 (i.e., reapplies the changes from #965); this is a temporary fix for #782 as it will re-introduce #870.

Reviewed-by: Alina Buzachis
Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
2025-08-11 16:48:07 +00:00
patchback[bot]
eb0aeeb318 Revert "Remove kubeconfig value from module invocation log (#826)" (#899) (#965)
This is a backport of PR #899 as merged into main (1705ced).
This reverts commit 6efabd3.
SUMMARY

Fixes #870
A better solution is necessary to address #782. The current code makes getting manifests practically unusable. We need to revert this commit until a better solution is found.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

kubeconfig

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-07-22 21:28:48 +00:00
patchback[bot]
f3d3696093 Fix the integration test for helm_registry_auth with helm >= 3.18.0 and clarify idempotency. (#946) (#961)
This is a backport of PR #946 as merged into main (642eb93).
SUMMARY
Fix the integration test for helm_registry_auth with helm >= 3.18.0 and clarify idempotency.
Fixes #944
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
helm_registry_auth
ADDITIONAL INFORMATION
Caused by the changes in helm starting from 3.18.0

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-07-15 16:01:23 +00:00
patchback[bot]
e3da2f28fd Fix integration test with ansibe-core 2.20 (#951) (#959)
This is a backport of PR #951 as merged into main (f568c9d).
SUMMARY
Now that ansible-core 2.19.0rc1 has been released, ansible-core’s devel branch has been bumped from 2.19.0.dev0 to 2.20.0.dev0. This potentially requires collection CIs to be updated which rely on devel using tests/sanity/ignore-2.19.txt, for example. Also it’s now time to add stable-2.19 to CI if you relied on devel to cover 2.19 so far. Note that milestone has also been updated to 2.20.0dev0.
During testing, I noticed that the failed test tasks/test_helm_not_installed.yml due to the new error message with ansible 2.20, please find here and following comments.
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
test/CI (tasks/test_helm_not_installed.yml)
ADDITIONAL INFORMATION
to be cherry-picked to the stable-6 and stable-5

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-07-15 15:34:36 +00:00
Yuriy Novostavskiy
bb599542e8 add sanity/ignore for 2.20 and remove 2.16 and 2.14 (#954)
SUMMARY
Now that ansible-core 2.19.0rc1 has been released, ansible-core’s devel branch has been bumped from 2.19.0.dev0 to 2.20.0.dev0. This  requires collection CI to be updated with using tests/sanity/ignore-2.20.txt. In the same time, ignore-2.14.txt ignore-2.15.txt is not required anymore as they are excluded from the matrix.
This PR is precific to stable-5 (and probably to be cherry-picked to stable-3) and is not required to main and stable-6 and such changes is already here.
ISSUE TYPE

CI Pull Request

COMPONENT NAME
CI
ADDITIONAL INFORMATION
It's required to have passed CI for #952

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Alina Buzachis
2025-07-15 13:53:17 +00:00
patchback[bot]
6560fb1c53 Fix unit tests (#939) (#940)
This is a backport of PR #939 as merged into main (34fd40d).
Some unit tests are broken with ansible-core 2.19, this PR aims to fix them.

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-07-11 02:23:06 +00:00
patchback[bot]
20084d119e Push changes from 3.3.1 into main branch (#893) (#894)
This is a backport of PR #893 as merged into main (0e7229c).
Release 3.3.1 is out; push changes to main branch

Reviewed-by: Bikouo Aubin
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-05-16 18:31:00 +00:00
Bikouo Aubin
c47343d7b6 Prepare release 5.3.0 (#929)
Release 5.3.0

Update galaxy.yml and README.md
Update release files using antsibull-changelog

Reviewed-by: Alina Buzachis
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Yuriy Novostavskiy
2025-05-16 08:16:23 +00:00
Bianca Henderson
c213b51741 Add info to the README about stable-4 branch no longer being maintained (#931)
SUMMARY

Resolves ACA-2383 and #918.
Adjusting the changes made in #926 per #930.

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

README.md
ADDITIONAL INFORMATION
Also made some minor capitalization edits.

Reviewed-by: Bikouo Aubin
2025-05-15 15:11:28 +00:00
patchback[bot]
60b53b9dc9 Add helm insecure skip tls verify (#901) (#925)
This is a backport of PR #901 as merged into main (914a16e).
SUMMARY
Added the option insecure_skip_tls_verify  to the following helm modules:

helm_repository
helm
Unified the option with alias in helm_pull

For helm, added the option to the helm diff call, as it got fixed upstream.
Upstream Issue: databus23/helm-diff#503
Fixed with: helm/helm#12856
Fixes #694
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

kubernetes.core.helm
kubernetes.core.helm_repository
kubernetes.core.helm_pull

ADDITIONAL INFORMATION
Basically the option was added in the parameters set in the ansible job, in the docs and then injected in the helm and helm diff binary calls if set. Defaults to False.
Example
---
- name: Test helm modules
  tasks:
    - name: Test helm repository insecure
      kubernetes.core.helm_repository:
        name: insecure
        repo_url: "<helm-repo-with-self-signed-tls>"
        state: present
        insecure_skip_tls_verify: true
    - name: Test helm pull insecure
      kubernetes.core.helm_pull:
        chart_ref: "oci://<helm-repo-with-self-signed-tls>/ptroject"
        destination: /tmp
        insecure_skip_tls_verify: true
    - name: Test helm insecure
      kubernetes.core.helm:
        name: insecure
        chart_ref: "oci://<helm-repo-with-self-signed-tls>/project"
        namespace: helm-insecure-test
        state: present
        insecure_skip_tls_verify: true
Note
Might need an alias for telm_template, as the option is called insecure_registry, in the manual and docs of helm it would be --insecure-skip-tls-verify as well though.
Not included, as it was recently merged with #805

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2025-05-06 23:32:56 +00:00
patchback[bot]
d531368d64 Update ansible-lint version to 25.1.2 (#919) (#921)
This is a backport of PR #919 as merged into main (b594d35).
Update ansible-lint version to 25.1.2

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-04-29 18:03:42 +00:00
patchback[bot]
c3bf5cc47f add reset_then_reuse_values support to helm module (#802) (#917)
This is a backport of PR #802 as merged into main (00699ac).
SUMMARY
Starting with version 3.14.0, Helm supports --reset-then-reuse-values. As discussed on the original PR. This greatly improves on --reuse-values as it allows to avoid templates errors when new features are added to an upgraded chart.
Closes #803
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
helm
ADDITIONAL INFORMATION
This PR is greatly 'inspired' by #575 and because I wasn't sure how I could provide additional tests for it, I actually copied those build previously for --reuse-values (as it is an improvement on this feature.

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-04-29 17:54:15 +00:00
patchback[bot]
349534b85b Rebase PR #898 (#905) (#913)
This is a backport of PR #905 as merged into main (d329e7e).
This PR is a rebase of #898 for CI to pass
Thanks @efussi for your collaboration.
Closes #892

Reviewed-by: Bikouo Aubin
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-04-25 15:59:21 +00:00
patchback[bot]
92e3f98a20 Bugfix: fix unit-source for pre-release of ansible-core 2.20 (devel and milestone branch) (#903) (#909)
SUMMARY
CI fix for #904
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
tests/unit
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
(cherry picked from commit d4fc22c74e)

Co-authored-by: Yuriy Novostavskiy <yuriy@novostavskiy.kiev.ua>
2025-04-25 11:01:13 -04:00
patchback[bot]
d2dcb9e55f Run integration tests using ansible-core 2.19 (#888) (#895)
* fix integration test ``k8s_full`` running with ansible-core 2.19

* Fix templating issues

* fix test on current ansible version

* fix tests cases

* Fix additional tests

* fix the templating mechanism

* consider using variable_[start/end]_string while parsing template

* Remove support for omit into template option

* Remove unnecessary unit tests

(cherry picked from commit 2cb5d6c316)

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2025-04-25 16:49:10 +02:00
Mike Graves
0eff03dd19 Prep 5.2.0 release (#891)
SUMMARY

Prep 5.2.0 release

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Yuriy Novostavskiy
Reviewed-by: Bikouo Aubin
Reviewed-by: Alina Buzachis
2025-03-31 17:20:25 +00:00
patchback[bot]
81fb8662da waiter.py Add ClusterOperator Test (#879) (#882)
This is a backport of PR #879 as merged into main (7cdf0d0).
SUMMARY
Fixes #869
During an OpenShift installation, one of the checks to see that the cluster is ready to proceed with configuration is to check to ensure that the Cluster Operators are in an Available: True Degraded: False Progressing: False state. While you can currently use the k8s_info module to get a json response, the resulting json needs to be iterated over several times to get the appropriate status.
This PR adds functionality into waiter.py which loops over all resource instances of the cluster operators. If any of them is not ready, waiter returns False and the task false. If the task returns, you can assume that all the cluster operators are healthy.


ISSUE TYPE


Feature Pull Request

COMPONENT NAME

waiter.py
ADDITIONAL INFORMATION



A simple playbook will trigger the waiter.py to watch the ClusterOperator object

---
- name: get operators
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Get cluster operators
      kubernetes.core.k8s_info:
        api_version: v1
        kind: ClusterOperator
        kubeconfig: "/home/ocp/one/auth/kubeconfig"
        wait: true
        wait_timeout: 30
      register: cluster_operators


This will produce the simple response if everything is functioning properly:
PLAY [get operators] *************************************************************************************************

TASK [Get cluster operators] *****************************************************************************************
ok: [localhost]

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

If the timeout is reached:
PLAY [get operators] *************************************************************************************************

TASK [Get cluster operators] *****************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions.CoreException: Failed to gather information about ClusterOperator(s) even after waiting for 30 seconds
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Failed to gather information about ClusterOperator(s) even after waiting for 30 seconds"}

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

UNSOLVED: How to know which Operators are failing

Reviewed-by: Bikouo Aubin
2025-03-26 15:50:36 +00:00
patchback[bot]
f74ee14d71 Extend hidden_fields to allow more complicated field definitions (#872) (#887)
This is a backport of PR #872 as merged into main (9ec6912).
SUMMARY
This allows us to ignore e.g. the last-applied-configuration annotation by specifying
metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
hidden_fields
This replaces #643 as I no longer have permissions to push to branches in this repo

Reviewed-by: Bikouo Aubin
2025-03-25 13:36:12 +00:00
patchback[bot]
6f75d86954 Fix linters in CI (#873) (#876)
This is a backport of PR #873 as merged into main (91df2f1).
SUMMARY
It seems that recent updates in linters break CI. Closes #874
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
CI
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2025-02-06 15:45:54 +00:00
82 changed files with 1697 additions and 497 deletions

View File

@@ -3,6 +3,7 @@ profile: production
exclude_paths:
- .ansible/
- .github/
- tests/integration
- tests/unit
- tests/sanity

6
.ansible-lint-ignore Normal file
View File

@@ -0,0 +1,6 @@
# https://docs.ansible.com/ansible-lint/docs/rules/
# no-changed-when is not requried for examples
plugins/connection/kubectl.py no-changed-when
# false positive result
plugins/connection/kubectl.py var-naming[no-reserved]
plugins/connection/kubectl.py jinja[invalid]

View File

@@ -1,2 +0,0 @@
# no-changed-when is not requried for examples
plugins/connection/kubectl.py no-changed-when

View File

@@ -26,6 +26,7 @@ jobs:
with:
path: ${{ env.source_dir }}
fetch-depth: "0"
ref: ${{ github.event.pull_request.head.sha }}
- name: list changes for pull request
id: splitter
@@ -50,6 +51,7 @@ jobs:
source: "./source"
cloud_common: "./cloudcommon"
ansible_posix: "./ansible_posix"
community_general: "./community_general"
strategy:
fail-fast: false
matrix:
@@ -61,7 +63,7 @@ jobs:
- true
- false
workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }}
name: "integration-py${{ matrix.python-version }}-${{ matrix.ansible-version }}-${{ matrix.workflow-id }}"
name: "integration-py${{ matrix.python-version }}-${{ matrix.ansible-version }}-${{ matrix.workflow-id }}-enable_turbo=${{ matrix.enable-turbo-mode }}"
steps:
- name: Read target
id: read-targets
@@ -118,6 +120,13 @@ jobs:
path: ${{ env.ansible_posix }}
ref: main
- name: checkout ansible-collections/community.general
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
with:
repository: ansible-collections/community.general
path: ${{ env.community_general }}
ref: main
- name: install cloud.common collection
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
with:
@@ -130,6 +139,12 @@ jobs:
install_python_dependencies: true
source_path: ${{ env.ansible_posix }}
- name: install community.general collection
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
with:
install_python_dependencies: false
source_path: ${{ env.community_general }}
- name: create kubernetes cluster
uses: helm/kind-action@v1.8.0
with:

View File

@@ -20,4 +20,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: run-ansible-lint
uses: ansible/ansible-lint@v24.12.2
uses: ansible/ansible-lint@main
with:
gh_action_ref: "v25.5.0"

4
.gitignore vendored
View File

@@ -13,6 +13,7 @@ changelogs/.plugin-cache.yaml
tests/output
tests/integration/cloud-config-*
.cache
.ansible
# Helm charts
tests/integration/*-chart-*.tgz
@@ -20,3 +21,6 @@ tests/integration/*-chart-*.tgz
# ansible-test generated file
tests/integration/inventory
tests/integration/*-*.yml
# VS Code settings
.vscode/

View File

@@ -5,16 +5,25 @@ rules:
braces:
max-spaces-inside: 1
level: error
brackets:
max-spaces-inside: 1
level: error
comments:
min-spaces-from-content: 1
comments-indentation: false
document-start: disable
line-length: disable
truthy: disable
indentation:
spaces: 2
indent-sequences: consistent
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true
ignore: |
.cache
.tox
.ansible
tests/output
plugins/connection/kubectl.py

View File

@@ -4,22 +4,90 @@ Kubernetes Collection Release Notes
.. contents:: Topics
v5.4.1
======
Release Summary
---------------
This release includes bugfixes for k8s service field handling, k8s_cp init containers support, and removes deprecated ansible.module_utils.six imports.
Bugfixes
--------
- Remove ``ansible.module_utils.six`` imports to avoid warnings (https://github.com/ansible-collections/kubernetes.core/pull/998).
- Update the `k8s_cp` module to also work for init containers (https://github.com/ansible-collections/kubernetes.core/pull/971).
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
v5.4.0
======
Release Summary
---------------
This release updates the ``helm_registry_auth`` module to match the behavior of ``helm >= 3.18.0`` which reports a successful logout regardless of the current state (i.e., no idempotency).
Minor Changes
-------------
- Module ``helm_registry_auth`` does not support idempotency with ``helm >= 3.18.0`` (https://github.com/ansible-collections/kubernetes.core/pull/946)
v5.3.0
======
Release Summary
---------------
This release includes minor changes, bug fixes and also bumps ``ansible-lint`` version to ``25.1.2``.
Minor Changes
-------------
- Bump version of ``ansible-lint`` to 25.1.2 (https://github.com/ansible-collections/kubernetes.core/pull/919).
- action/k8s_info - update templating mechanism with changes from ``ansible-core 2.19`` (https://github.com/ansible-collections/kubernetes.core/pull/888).
- helm - add ``reset_then_reuse_values`` support to helm module (https://github.com/ansible-collections/kubernetes.core/issues/803).
- helm - add support for ``insecure_skip_tls_verify`` option to helm and helm_repository(https://github.com/ansible-collections/kubernetes.core/issues/694).
Bugfixes
--------
- module_utils/k8s/service - fix issue when trying to delete resource using ``delete_options`` and ``check_mode=true`` (https://github.com/ansible-collections/kubernetes.core/issues/892).
v5.2.0
======
Release Summary
---------------
This release adds more functionality to the hidden_fields option and support for waiting on ClusterOperators to reach a ready state.
Minor Changes
-------------
- k8s - Extend hidden_fields to allow the expression of more complex field types to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
- k8s_info - Extend hidden_fields to allow the expression of more complex field types to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
- waiter.py - add ClusterOperator support. The module can now check OpenShift cluster health by verifying ClusterOperator status requiring 'Available: True', 'Degraded: False', and 'Progressing: False' for success. (https://github.com/ansible-collections/kubernetes.core/issues/869)
v5.1.0
======
Release Summary
---------------
This release came with new module ``helm_registry_auth``, improvements to the error messages in the k8s_drain module, new parameter ``insecure_registry`` for ``helm_template`` module and several bug fixes.
Minor Changes
-------------
- Bump version of ansible-lint to minimum 24.7.0 (https://github.com/ansible-collections/kubernetes.core/pull/765).
- Parameter insecure_registry added to helm_template as equivalent of insecure-skip-tls-verify (https://github.com/ansible-collections/kubernetes.core/pull/805).
- connection/kubectl.py - Added an example of using the kubectl connection plugin to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
- k8s_drain - Improve error message for pod disruption budget when draining a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
Bugfixes
--------
- helm - Helm version checks did not support RC versions. They now accept any version tags. (https://github.com/ansible-collections/kubernetes.core/pull/745).
- helm_pull - Apply no_log=True to pass_credentials to silence false positive warning.. (https://github.com/ansible-collections/kubernetes.core/pull/796).
- helm_pull - Apply no_log=True to pass_credentials to silence false positive warning. (https://github.com/ansible-collections/kubernetes.core/pull/796).
- k8s_drain - Fix k8s_drain does not wait for single pod (https://github.com/ansible-collections/kubernetes.core/issues/769).
- k8s_drain - Fix k8s_drain runs into a timeout when evicting a pod which is part of a stateful set (https://github.com/ansible-collections/kubernetes.core/issues/792).
- kubeconfig option should not appear in module invocation log (https://github.com/ansible-collections/kubernetes.core/issues/782).
@@ -42,6 +110,7 @@ This major release drops support for ``ansible-core<2.15``.
Minor Changes
-------------
- connection/kubectl.py - Added an example of using the kubectl connection plugin to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/734).
Breaking Changes / Porting Guide
@@ -82,17 +151,32 @@ Bugfixes
- 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.3.1
======
Release Summary
---------------
This release fixes the CI issues with the ``linters`` workflow.
v3.3.0
======
Release Summary
---------------
This release comes with improvements to the error messages in the k8s_drain module and several bug fixes.
Minor Changes
-------------
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/734).
- k8s_drain - Improve error message for pod disruption budget when draining a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
Bugfixes
--------
- helm - Helm version checks did not support RC versions. They now accept any version tags. (https://github.com/ansible-collections/kubernetes.core/pull/745).
- helm_pull - Apply no_log=True to pass_credentials to silence false positive warning.. (https://github.com/ansible-collections/kubernetes.core/pull/796).
- helm_pull - Apply no_log=True to pass_credentials to silence false positive warning. (https://github.com/ansible-collections/kubernetes.core/pull/796).
- k8s_drain - Fix k8s_drain does not wait for single pod (https://github.com/ansible-collections/kubernetes.core/issues/769).
- k8s_drain - Fix k8s_drain runs into a timeout when evicting a pod which is part of a stateful set (https://github.com/ansible-collections/kubernetes.core/issues/792).
- kubeconfig option should not appear in module invocation log (https://github.com/ansible-collections/kubernetes.core/issues/782).
@@ -104,13 +188,15 @@ v3.2.0
Release Summary
---------------
This release comes with documentation updates.
Minor Changes
-------------
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/734).
- connection/kubectl.py - Added an example of using the kubectl connection plugin to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/734).
v3.1.0
======

View File

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

View File

@@ -21,7 +21,7 @@ For more information about communication, see the [Ansible communication guide](
## Requirements
<!--start requires_ansible-->
## Ansible version compatibility
## Ansible Version Compatibility
This collection has been tested against following Ansible versions: **>=2.15.0**.
@@ -42,27 +42,27 @@ Note: Python2 is deprecated from [1st January 2020](https://www.python.org/doc/s
This collection supports Kubernetes versions >= 1.24.
### Included content
### Included Content
Click on the name of a plugin or module to view that content's documentation:
<!--start collection content-->
### Connection plugins
### Connection Plugins
Name | Description
--- | ---
[kubernetes.core.kubectl](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.kubectl_connection.rst)|Execute tasks in pods running on Kubernetes.
### K8s filter plugins
### K8s filter Plugins
Name | Description
--- | ---
kubernetes.core.k8s_config_resource_name|Generate resource name for the given resource of type ConfigMap, Secret
### Inventory plugins
### Inventory Plugins
Name | Description
--- | ---
[kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_inventory.rst)|Kubernetes (K8s) inventory source
### Lookup plugins
### Lookup Plugins
Name | Description
--- | ---
[kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_lookup.rst)|Query the K8s API
@@ -106,7 +106,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
---
collections:
- name: kubernetes.core
version: 5.1.0
version: 5.4.1
```
### Installing the Kubernetes Python Library
@@ -183,7 +183,7 @@ If upgrading older playbooks which were built prior to Ansible 2.10 and this col
For documentation on how to use individual modules and other content included in this collection, please see the links in the 'Included content' section earlier in this README.
## Ansible Turbo mode Tech Preview
## Ansible Turbo Mode Tech Preview
The ``kubernetes.core`` collection supports Ansible Turbo mode as a tech preview via the ``cloud.common`` collection. By default, this feature is disabled. To enable Turbo mode for modules, set the environment variable `ENABLE_TURBO_MODE=1` on the managed node. For example:
@@ -202,7 +202,7 @@ defined in the playbook using `environment` keyword as above, you must set it us
Please read more about Ansible Turbo mode - [here](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/ansible_turbo_mode.rst).
## Contributing to this collection
## Contributing to this Collection
If you want to develop new content for this collection or improve what's already here, the easiest way to work on the collection is to clone it into one of the configured [`COLLECTIONS_PATHS`](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#collections-paths), and work on it there.
@@ -252,6 +252,8 @@ The process for uploading a supported release to Automation Hub is documented se
<!--List available communication channels. In addition to channels specific to your collection, we also recommend to use the following ones.-->
> **Note:** The `stable-4` branch, which handles all `4.x.y` releases of this collection, is no longer supported. This means that no backports nor releases will be performed on the `stable-4` branch.
We announce releases and important changes through Ansible's [The Bullhorn newsletter](https://github.com/ansible/community/wiki/News#the-bullhorn). Be sure you are [subscribed](https://eepurl.com/gZmiEP).
We take part in the global quarterly [Ansible Contributor Summit](https://github.com/ansible/community/wiki/Contributor-Summit) virtually or in-person. Track [The Bullhorn newsletter](https://eepurl.com/gZmiEP) and join us.
@@ -263,7 +265,7 @@ For the latest supported versions, refer to the release notes below.
If you encounter issues or have questions, you can submit a support request through the following channels:
- GitHub Issues: Report bugs, request features, or ask questions by opening an issue in the [GitHub repository]((https://github.com/ansible-collections/kubernetes.core/).
## Release notes
## Release Notes
See the [raw generated changelog](https://github.com/ansible-collections/kubernetes.core/blob/main/CHANGELOG.rst).

View File

@@ -859,15 +859,15 @@ releases:
minor_changes:
- connection/kubectl.py - Added an example of using the kubectl connection plugin
to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0
(https://github.com/ansible-collections/kubernetes.core/pull/734).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
release_summary: This release comes with documentation updates.
fragments:
- 20240530-defer-removal-and-ansible-core-support-update.yaml
- 20240601-doc-example-of-using-kubectl.yaml
- inventory-update_removal_date.yml
- 3.2.0.yml
- inventory-update_removal_date.yml
release_date: '2024-06-14'
3.3.0:
changes:
@@ -885,7 +885,8 @@ releases:
minor_changes:
- k8s_drain - Improve error message for pod disruption budget when draining
a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
release_summary: This release comes with improvements to the error messages in the k8s_drain module and several bug fixes.
release_summary: This release comes with improvements to the error messages
in the k8s_drain module and several bug fixes.
fragments:
- 20240530-ansible-core-support-update.yaml
- 20240611-helm-rc-version.yaml
@@ -899,6 +900,12 @@ releases:
- 798-drain-pdb-error-message.yaml
- readme_template_update.yml
release_date: '2025-01-20'
3.3.1:
changes:
release_summary: This release fixes the CI issues with the ``linters`` workflow.
fragments:
- release_summary.yml
release_date: '2025-03-26'
4.0.0:
changes:
bugfixes:
@@ -946,10 +953,10 @@ releases:
breaking_changes:
- Remove support for ``ansible-core<2.15`` (https://github.com/ansible-collections/kubernetes.core/pull/737).
minor_changes:
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0
(https://github.com/ansible-collections/kubernetes.core/pull/734).
- connection/kubectl.py - Added an example of using the kubectl connection plugin
to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0
(https://github.com/ansible-collections/kubernetes.core/pull/734).
release_summary: This major release drops support for ``ansible-core<2.15``.
fragments:
- 20240530-ansible-core-support-update.yaml
@@ -970,14 +977,14 @@ releases:
- kustomize - kustomize plugin fails with deprecation warnings (https://github.com/ansible-collections/kubernetes.core/issues/639).
- waiter - Fix waiting for daemonset when desired number of pods is 0. (https://github.com/ansible-collections/kubernetes.core/pull/756).
minor_changes:
- Bump version of ansible-lint to minimum 24.7.0 (https://github.com/ansible-collections/kubernetes.core/pull/765).
- Bump version of ``ansible-lint`` to minimum 24.7.0 (https://github.com/ansible-collections/kubernetes.core/pull/765).
- Parameter insecure_registry added to helm_template as equivalent of insecure-skip-tls-verify
(https://github.com/ansible-collections/kubernetes.core/pull/805).
- k8s_drain - Improve error message for pod disruption budget when draining
a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
release_summary: This release came with new module ``helm_registry_auth``, improvements
to the error messages in the k8s_drain module, new parameter ``insecure_registry`` for
``helm_template`` module and several bug fixes.
to the error messages in the k8s_drain module, new parameter ``insecure_registry``
for ``helm_template`` module and several bug fixes.
fragments:
- 0-readme.yml
- 20240601-doc-example-of-using-kubectl.yaml
@@ -999,3 +1006,68 @@ releases:
name: helm_registry_auth
namespace: ''
release_date: '2025-01-20'
5.2.0:
changes:
minor_changes:
- k8s - Extend hidden_fields to allow the expression of more complex field types
to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
- k8s_info - Extend hidden_fields to allow the expression of more complex field
types to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
- 'waiter.py - add ClusterOperator support. The module can now check OpenShift
cluster health by verifying ClusterOperator status requiring ''Available:
True'', ''Degraded: False'', and ''Progressing: False'' for success. (https://github.com/ansible-collections/kubernetes.core/issues/869)'
release_summary: This release adds more functionality to the hidden_fields option
and support for waiting on ClusterOperators to reach a ready state.
fragments:
- 5.2.0.yml
- 643-extend-hidden-fields.yaml
- 879-clusteroperator-waiter.py.yaml
release_date: '2025-03-27'
5.3.0:
changes:
bugfixes:
- module_utils/k8s/service - fix issue when trying to delete resource using
`delete_options` and `check_mode=true` (https://github.com/ansible-collections/kubernetes.core/issues/892).
minor_changes:
- kubernetes.core - Bump version of ``ansible-lint`` to ``25.1.2`` (https://github.com/ansible-collections/kubernetes.core/pull/919).
- action/k8s_info - update templating mechanism with changes from ``ansible-core
2.19`` (https://github.com/ansible-collections/kubernetes.core/pull/888).
- helm - add ``reset_then_reuse_values`` support to helm module (https://github.com/ansible-collections/kubernetes.core/issues/803).
- helm - add support for ``insecure_skip_tls_verify`` option to helm and ``helm_repository`` (https://github.com/ansible-collections/kubernetes.core/issues/694).
release_summary: This release includes minor changes, bug fixes and also bumps
``ansible-lint`` version to ``25.1.2``.
fragments:
- 20250324-k8s_info-templating.yaml
- 5.3.0.yml
- 694-add-insecure-skip-tls-verify.yml
- 800-helm-add-reset_then_reuse_values-support.yml
- 898-k8s-dont-delete-in-check-mode.yaml
- 919-update-ansible-lint-version.yaml
release_date: '2025-05-16'
5.4.0:
changes:
minor_changes:
- Module ``helm_registry_auth`` does not support idempotency with ``helm >= 3.18.0``
(https://github.com/ansible-collections/kubernetes.core/pull/946).
release_summary: This release updates the ``helm_registry_auth`` module to match the behavior of ``helm >= 3.18.0`` which reports a successful logout regardless of the current state (i.e., no idempotency).
fragments:
- 20250411-kubeconfig-no_log-revert.yaml
- 20250503-fix-unit-tests.yml
- 20250605-fix-helm_registry_auth-integration_test.yaml
- 5.4.0.yml
release_date: '2025-08-12'
5.4.1:
changes:
bugfixes:
- Remove ``ansible.module_utils.six`` imports to avoid warnings (https://github.com/ansible-collections/kubernetes.core/pull/998).
- Update the `k8s_cp` module to also work for init containers (https://github.com/ansible-collections/kubernetes.core/pull/971).
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
release_summary: This release includes bugfixes for k8s service field handling,
k8s_cp init containers support, and removes deprecated ansible.module_utils.six
imports.
fragments:
- 20250428-k8s-service-hide-fields-first.yaml
- 20250731-fix-k8s_cp-initcontainers.yaml
- 20250922-remove-ansible-six-imports.yaml
- 5.4.1.yml
release_date: '2025-10-07'

View File

@@ -289,6 +289,29 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via <code>K8S_AUTH_HOST</code> environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>insecure_skip_tls_verify</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 5.3.0</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Skip tls certificate checks for the chart download.</div>
<div>Do not confuse with the <code>validate_certs</code> option.</div>
<div>This option is only available for helm &gt;= 3.16.0.</div>
<div style="font-size: small; color: darkgreen"><br/>aliases: skip_tls_certs_check</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -435,6 +458,28 @@ Parameters
<div>mutually exclusive with with <code>history_max</code>.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>reset_then_reuse_values</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.0.0</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>When upgrading package, reset the values to the ones built into the chart, apply the last release&#x27;s values and merge in any overrides from parameters O(release_values), O(values_files) or O(set_values).</div>
<div>If O(reset_values) or O(reuse_values) is set to V(True), this is ignored.</div>
<div>This feature requires helm diff &gt;= 3.9.12.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>

View File

@@ -279,7 +279,8 @@ Parameters
</td>
<td>
<div>Whether or not to check tls certificate for the chart download.</div>
<div>Requires helm &gt;= 3.3.0.</div>
<div>Requires helm &gt;= 3.3.0. Alias <code>insecure_skip_tls_verify</code> added in 5.3.0.</div>
<div style="font-size: small; color: darkgreen"><br/>aliases: insecure_skip_tls_verify</div>
</td>
</tr>
<tr>

View File

@@ -170,6 +170,7 @@ Parameters
<div>Desired state of the registry.</div>
<div>If set to V(present) attempt to log in to the remote registry server using the URL specified in O(host).</div>
<div>If set to V(absent) attempt to log out from the remote registry server using the URL specified in O(host).</div>
<div>As helm &gt;= 3.18.0 reports successful logout even if the user is not logged in, this module will report a change regardless of the current state.</div>
</td>
</tr>
<tr>

View File

@@ -143,6 +143,27 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via <code>K8S_AUTH_HOST</code> environment variable.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>insecure_skip_tls_verify</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 5.3.0</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Skip tls certificate checks for the repository url.</div>
<div style="font-size: small; color: darkgreen"><br/>aliases: skip_tls_certs_check</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>

View File

@@ -512,6 +512,7 @@ Notes
.. note::
- the tar binary is required on the container when copying from local filesystem to pod.
- the (init) container has to be started before you copy files or directories to it.
- To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.

View File

@@ -595,6 +595,7 @@ Examples
kubernetes.core.k8s_drain:
state: drain
name: foo
delete_options:
force: yes
- name: Drain node "foo", but abort if there are pods not managed by a ReplicationController, Job, or DaemonSet, and use a grace period of 15 minutes.

View File

@@ -174,8 +174,7 @@ Parameters
</td>
<td>
<div>Hide fields matching any of the field definitions in the result</div>
<div>An example might be <code>hidden_fields=[metadata.managedFields]</code></div>
<div>Only field definitions that don&#x27;t reference list items are supported (so V(spec.containers[0]) would not work)</div>
<div>An example might be <code>hidden_fields=[metadata.managedFields]</code> or V(hidden_fields=[spec.containers[0].env[3].value]) or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])</div>
</td>
</tr>
<tr>

View File

@@ -395,8 +395,7 @@ Parameters
</td>
<td>
<div>Hide fields matching this option in the result</div>
<div>An example might be <code>hidden_fields=[metadata.managedFields]</code></div>
<div>Only field definitions that don&#x27;t reference list items are supported (so V(spec.containers[0]) would not work)</div>
<div>An example might be <code>hidden_fields=[metadata.managedFields]</code> or V(hidden_fields=[spec.containers[0].env[3].value]) or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])</div>
</td>
</tr>
<tr>

View File

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

View File

@@ -22,33 +22,20 @@ from ansible.errors import (
)
from ansible.module_utils._text import to_bytes, to_native, to_text
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.module_utils.six import iteritems, string_types
from ansible.plugins.action import ActionBase
try:
from ansible.template import trust_as_template
except ImportError:
trust_as_template = None
class RemoveOmit(object):
def __init__(self, buffer, omit_value):
def _from_yaml_to_definition(buffer):
try:
import yaml
except ImportError:
raise AnsibleError("Failed to import the required Python library (PyYAML).")
self.data = yaml.safe_load_all(buffer)
self.omit = omit_value
def remove_omit(self, data):
if isinstance(data, dict):
result = dict()
for key, value in iteritems(data):
if value == self.omit:
continue
result[key] = self.remove_omit(value)
return result
if isinstance(data, list):
return [self.remove_omit(v) for v in data if v != self.omit]
return data
def output(self):
return [self.remove_omit(d) for d in self.data]
return list(yaml.safe_load_all(buffer))
ENV_KUBECONFIG_PATH_SEPARATOR = ";" if platform.system() == "Windows" else ":"
@@ -112,7 +99,7 @@ class ActionModule(ActionBase):
"trim_blocks": True,
"lstrip_blocks": False,
}
if isinstance(template, string_types):
if isinstance(template, str):
# treat this as raw_params
template_param["path"] = template
elif isinstance(template, dict):
@@ -132,7 +119,7 @@ class ActionModule(ActionBase):
):
if s_type in template_args:
value = ensure_type(template_args[s_type], "string")
if value is not None and not isinstance(value, string_types):
if value is not None and not isinstance(value, str):
raise AnsibleActionFail(
"%s is expected to be a string, but got %s instead"
% (s_type, type(value))
@@ -207,9 +194,8 @@ class ActionModule(ActionBase):
"'template' is only a supported parameter for the 'k8s' module."
)
omit_value = task_vars.get("omit")
template_params = []
if isinstance(template, string_types) or isinstance(template, dict):
if isinstance(template, str) or isinstance(template, dict):
template_params.append(self.get_template_args(template))
elif isinstance(template, list):
for element in template:
@@ -230,6 +216,7 @@ class ActionModule(ActionBase):
old_vars = self._templar.available_variables
default_environment = {}
if trust_as_template is None:
for key in (
"newline_sequence",
"variable_start_string",
@@ -257,26 +244,35 @@ class ActionModule(ActionBase):
with self.get_template_data(template_item["path"]) as template_data:
# add ansible 'template' vars
temp_vars = copy.deepcopy(task_vars)
for key, value in iteritems(template_item):
overrides = {}
for key, value in template_item.items():
if hasattr(self._templar.environment, key):
if value is not None:
overrides[key] = value
if trust_as_template is None:
setattr(self._templar.environment, key, value)
else:
elif trust_as_template is None:
setattr(
self._templar.environment,
key,
default_environment.get(key),
)
self._templar.available_variables = temp_vars
if trust_as_template:
template_data = trust_as_template(template_data)
result = self._templar.template(
template_data,
preserve_trailing_newlines=True,
escape_backslashes=False,
overrides=overrides,
)
else:
result = self._templar.do_template(
template_data,
preserve_trailing_newlines=True,
escape_backslashes=False,
)
if omit_value is not None:
result_template.extend(RemoveOmit(result, omit_value).output())
else:
result_template.append(result)
result_template.extend(_from_yaml_to_definition(result))
self._templar.available_variables = old_vars
resource_definition = self._task.args.get("definition", None)
if not resource_definition:
@@ -306,7 +302,7 @@ class ActionModule(ActionBase):
)
def get_kubeconfig(self, kubeconfig, remote_transport, new_module_args):
if isinstance(kubeconfig, string_types):
if isinstance(kubeconfig, str):
# find the kubeconfig in the expected search path
if not remote_transport:
# kubeconfig is local

View File

@@ -1,12 +1,10 @@
from __future__ import absolute_import, division, print_function
from ansible.module_utils.six import string_types
__metaclass__ = type
def list_dict_str(value):
if isinstance(value, (list, dict, string_types)):
if isinstance(value, (list, dict, str)):
return value
raise TypeError

View File

@@ -96,7 +96,7 @@ class K8SCopy(metaclass=ABCMeta):
return error, stdout, stderr
except Exception as e:
self.module.fail_json(
msg="Error while running/parsing from pod {1}/{2} command='{0}' : {3}".format(
msg="Error while running/parsing from pod {0}/{1} command='{2}' : {3}".format(
self.namespace, self.name, cmd, to_native(e)
)
)
@@ -435,11 +435,21 @@ def check_pod(svc):
try:
result = svc.client.get(resource, name=name, namespace=namespace)
containers = [
c["name"] for c in result.to_dict()["status"]["containerStatuses"]
]
if container and container not in containers:
containers = dict(
{
c["name"]: c
for cl in ["initContainerStatuses", "containerStatuses"]
for c in result.to_dict()["status"].get(cl, [])
}
)
if container and container not in containers.keys():
module.fail_json(msg="Pod has no container {0}".format(container))
return containers
if (
container
and container in containers
and not bool(containers[container].get("started", False))
):
module.fail_json(msg="Pod container {0} is not started".format(container))
return containers.keys()
except Exception as exc:
_fail(exc)

View File

@@ -15,7 +15,6 @@ import tempfile
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.six import string_types
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
LooseVersion,
)
@@ -113,7 +112,7 @@ class AnsibleHelmModule(object):
kubeconfig_content = None
kubeconfig = self.params.get("kubeconfig")
if kubeconfig:
if isinstance(kubeconfig, string_types):
if isinstance(kubeconfig, str):
with open(os.path.expanduser(kubeconfig)) as fd:
kubeconfig_content = yaml.safe_load(fd)
elif isinstance(kubeconfig, dict):

View File

@@ -5,7 +5,6 @@ import hashlib
import os
from typing import Any, Dict, List, Optional
from ansible.module_utils.six import iteritems, string_types
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_MAP,
AUTH_ARG_SPEC,
@@ -115,7 +114,7 @@ def _load_config(auth: Dict) -> None:
"persist_config": auth.get("persist_config"),
}
if kubeconfig:
if isinstance(kubeconfig, string_types):
if isinstance(kubeconfig, str):
kubernetes.config.load_kube_config(config_file=kubeconfig, **optional_arg)
elif isinstance(kubeconfig, dict):
kubernetes.config.load_kube_config_from_dict(
@@ -163,7 +162,7 @@ def _create_configuration(auth: Dict):
except AttributeError:
configuration = kubernetes.client.Configuration()
for key, value in iteritems(auth):
for key, value in auth.items():
if key in AUTH_ARG_MAP.keys() and value is not None:
if key == "api_key":
setattr(

View File

@@ -4,7 +4,6 @@
import os
from typing import Dict, Iterable, List, Optional, Union, cast
from ansible.module_utils.six import string_types
from ansible.module_utils.urls import Request
try:
@@ -78,11 +77,11 @@ def create_definitions(params: Dict) -> List[ResourceDefinition]:
def from_yaml(definition: Union[str, List, Dict]) -> Iterable[Dict]:
"""Load resource definitions from a yaml definition."""
definitions: List[Dict] = []
if isinstance(definition, string_types):
if isinstance(definition, str):
definitions += yaml.safe_load_all(definition)
elif isinstance(definition, list):
for item in definition:
if isinstance(item, string_types):
if isinstance(item, str):
definitions += yaml.safe_load_all(item)
else:
definitions.append(item)

View File

@@ -4,7 +4,7 @@
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, Union
from ansible.module_utils.common.dict_transformations import dict_merge
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
@@ -473,7 +473,7 @@ class K8sService:
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
if delete_options:
if delete_options and not self.module.check_mode:
body = {
"apiVersion": "v1",
"kind": "DeleteOptions",
@@ -498,50 +498,107 @@ def diff_objects(
if not diff:
return True, result
result["before"] = diff[0]
result["after"] = diff[1]
result["before"] = hide_fields(diff[0], hidden_fields)
result["after"] = hide_fields(diff[1], hidden_fields)
if list(result["after"].keys()) != ["metadata"] or list(
if list(result["after"].keys()) == ["metadata"] and list(
result["before"].keys()
) != ["metadata"]:
return False, result
) == ["metadata"]:
# If only metadata.generation and metadata.resourceVersion changed, ignore it
ignored_keys = set(["generation", "resourceVersion"])
if not set(result["after"]["metadata"].keys()).issubset(ignored_keys):
return False, result
if not set(result["before"]["metadata"].keys()).issubset(ignored_keys):
return False, result
result["before"] = hide_fields(result["before"], hidden_fields)
result["after"] = hide_fields(result["after"], hidden_fields)
if set(result["after"]["metadata"].keys()).issubset(ignored_keys) and set(
result["before"]["metadata"].keys()
).issubset(ignored_keys):
return True, result
return False, result
def hide_field_tree(hidden_field: str) -> List[str]:
result = []
key, rest = hide_field_split2(hidden_field)
result.append(key)
while rest:
key, rest = hide_field_split2(rest)
result.append(key)
def hide_fields(definition: dict, hidden_fields: Optional[list]) -> dict:
if not hidden_fields:
return definition
result = copy.deepcopy(definition)
for hidden_field in hidden_fields:
result = hide_field(result, hidden_field)
return result
# hide_field is not hugely sophisticated and designed to cope
# with e.g. status or metadata.managedFields rather than e.g.
# spec.template.spec.containers[0].env[3].value
def hide_field(definition: dict, hidden_field: str) -> dict:
split = hidden_field.split(".", 1)
if split[0] in definition:
if len(split) == 2:
definition[split[0]] = hide_field(definition[split[0]], split[1])
def build_hidden_field_tree(hidden_fields: List[str]) -> Dict[str, Any]:
"""Group hidden field targeting the same json key
Example:
Input: ['env[3]', 'env[0]']
Output: {'env': [0, 3]}
"""
output = {}
for hidden_field in hidden_fields:
current = output
tree = hide_field_tree(hidden_field)
for idx, key in enumerate(tree):
if current.get(key, "") is None:
break
if idx == (len(tree) - 1):
current[key] = None
elif key not in current:
current[key] = {}
current = current[key]
return output
# hide_field should be able to cope with simple or more complicated
# field definitions
# e.g. status or metadata.managedFields or
# spec.template.spec.containers[0].env[3].value or
# metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]
def hide_field(
definition: Union[Dict[str, Any], List[Any]], hidden_field: Dict[str, Any]
) -> Dict[str, Any]:
def dict_contains_key(obj: Dict[str, Any], key: str) -> bool:
return key in obj
def list_contains_key(obj: List[Any], key: str) -> bool:
return int(key) < len(obj)
hidden_keys = list(hidden_field.keys())
field_contains_key = dict_contains_key
field_get_key = str
if isinstance(definition, list):
# Sort with reverse=true so that when we delete an item from the list, the order is not changed
hidden_keys = sorted(
[k for k in hidden_field.keys() if k.isdecimal()], reverse=True
)
field_contains_key = list_contains_key
field_get_key = int
for key in hidden_keys:
if field_contains_key(definition, key):
value = hidden_field.get(key)
convert_key = field_get_key(key)
if value is None:
del definition[convert_key]
else:
del definition[split[0]]
definition[convert_key] = hide_field(definition[convert_key], value)
if (
definition[convert_key] == dict()
or definition[convert_key] == list()
):
del definition[convert_key]
return definition
def hide_fields(
definition: Dict[str, Any], hidden_fields: Optional[List[str]]
) -> Dict[str, Any]:
if not hidden_fields:
return definition
result = copy.deepcopy(definition)
hidden_field_tree = build_hidden_field_tree(hidden_fields)
return hide_field(result, hidden_field_tree)
def decode_response(resp) -> Tuple[Dict, List[str]]:
"""
This function decodes unserialized responses from the Kubernetes python
@@ -620,3 +677,35 @@ def parse_quoted_string(quoted_string: str) -> Tuple[str, str]:
raise ValueError("invalid quoted string: missing closing quote")
return "".join(result), remainder
# hide_field_split2 returns the first key in hidden_field and the rest of the hidden_field
# We expect the first key to either be in brackets, to be terminated by the start of a left
# bracket, or to be terminated by a dot.
# examples would be:
# field.another.next -> (field, another.next)
# field[key].value -> (field, [key].value)
# [key].value -> (key, value)
# [one][two] -> (one, [two])
def hide_field_split2(hidden_field: str) -> Tuple[str, str]:
lbracket = hidden_field.find("[")
rbracket = hidden_field.find("]")
dot = hidden_field.find(".")
if lbracket == 0:
# skip past right bracket and any following dot
rest = hidden_field[rbracket + 1 :] # noqa: E203
if rest and rest[0] == ".":
rest = rest[1:]
return (hidden_field[lbracket + 1 : rbracket], rest) # noqa: E203
if lbracket != -1 and (dot == -1 or lbracket < dot):
return (hidden_field[:lbracket], hidden_field[lbracket:])
split = hidden_field.split(".", 1)
if len(split) == 1:
return split[0], ""
return split

View File

@@ -117,11 +117,34 @@ def exists(resource: Optional[ResourceInstance]) -> bool:
return bool(resource) and not empty_list(resource)
def cluster_operator_ready(resource: ResourceInstance) -> bool:
"""
Predicate to check if a single ClusterOperator is healthy.
Returns True if:
- "Available" is True
- "Degraded" is False
- "Progressing" is False
"""
if not resource:
return False
# Extract conditions from the resource's status
conditions = resource.get("status", {}).get("conditions", [])
status = {x.get("type", ""): x.get("status") for x in conditions}
return (
(status.get("Degraded") == "False")
and (status.get("Progressing") == "False")
and (status.get("Available") == "True")
)
RESOURCE_PREDICATES = {
"DaemonSet": daemonset_ready,
"Deployment": deployment_ready,
"Pod": pod_ready,
"StatefulSet": statefulset_ready,
"ClusterOperator": cluster_operator_ready,
}

View File

@@ -145,6 +145,16 @@ options:
required: false
default: True
version_added: 3.0.0
reset_then_reuse_values:
description:
- When upgrading package, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from
parameters O(release_values), O(values_files) or O(set_values).
- If O(reset_values) or O(reuse_values) is set to V(True), this is ignored.
- This feature requires helm diff >= 3.9.12.
type: bool
required: false
default: False
version_added: 6.0.0
#Helm options
disable_hook:
@@ -218,6 +228,15 @@ options:
- mutually exclusive with with C(replace).
type: int
version_added: 2.2.0
insecure_skip_tls_verify:
description:
- Skip tls certificate checks for the chart download.
- Do not confuse with the C(validate_certs) option.
- This option is only available for helm >= 3.16.0.
type: bool
default: False
aliases: [ skip_tls_certs_check ]
version_added: 5.3.0
extends_documentation_fragment:
- kubernetes.core.helm_common_options
"""
@@ -476,12 +495,15 @@ def run_dep_update(module, chart_ref):
rc, out, err = module.run_helm_command(dep_update)
def fetch_chart_info(module, command, chart_ref):
def fetch_chart_info(module, command, chart_ref, insecure_skip_tls_verify=False):
"""
Get chart info
"""
inspect_command = command + f" show chart '{chart_ref}'"
if insecure_skip_tls_verify:
inspect_command += " --insecure-skip-tls-verify"
rc, out, err = module.run_helm_command(inspect_command)
return yaml.safe_load(out)
@@ -509,6 +531,8 @@ def deploy(
set_value_args=None,
reuse_values=None,
reset_values=True,
reset_then_reuse_values=False,
insecure_skip_tls_verify=False,
):
"""
Install/upgrade/rollback release chart
@@ -526,6 +550,17 @@ def deploy(
if reuse_values is not None:
deploy_command += " --reuse-values=" + str(reuse_values)
if reset_then_reuse_values:
helm_version = module.get_helm_version()
if LooseVersion(helm_version) < LooseVersion("3.14.0"):
module.fail_json(
msg="reset_then_reuse_values requires helm >= 3.14.0, current version is {0}".format(
helm_version
)
)
else:
deploy_command += " --reset-then-reuse-values"
if wait:
deploy_command += " --wait"
if wait_timeout is not None:
@@ -549,6 +584,17 @@ def deploy(
if create_namespace:
deploy_command += " --create-namespace"
if insecure_skip_tls_verify:
helm_version = module.get_helm_version()
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
module.fail_json(
msg="insecure_skip_tls_verify requires helm >= 3.16.0, current version is {0}".format(
helm_version
)
)
else:
deploy_command += " --insecure-skip-tls-verify"
if values_files:
for value_file in values_files:
deploy_command += " --values=" + value_file
@@ -642,6 +688,8 @@ def helmdiff_check(
set_value_args=None,
reuse_values=None,
reset_values=True,
reset_then_reuse_values=False,
insecure_skip_tls_verify=False,
):
"""
Use helm diff to determine if a release would change by upgrading a chart.
@@ -676,6 +724,27 @@ def helmdiff_check(
if reuse_values:
cmd += " --reuse-values"
if reset_then_reuse_values:
helm_diff_version = get_plugin_version("diff")
helm_version = module.get_helm_version()
fail_msg = ""
if LooseVersion(helm_diff_version) < LooseVersion("3.9.12"):
fail_msg = "reset_then_reuse_values requires helm diff >= 3.9.12, current version is {0}\n".format(
helm_diff_version
)
if LooseVersion(helm_version) < LooseVersion("3.14.0"):
fail_msg += "reset_then_reuse_values requires helm >= 3.14.0, current version is {0}\n".format(
helm_version
)
if fail_msg:
module.fail_json(msg=fail_msg)
else:
cmd += " --reset-then-reuse-values"
if insecure_skip_tls_verify:
cmd += " --insecure-skip-tls-verify"
rc, out, err = module.run_helm_command(cmd)
return (len(out.strip()) > 0, out.strip())
@@ -735,6 +804,10 @@ def argument_spec():
set_values=dict(type="list", elements="dict"),
reuse_values=dict(type="bool"),
reset_values=dict(type="bool", default=True),
reset_then_reuse_values=dict(type="bool", default=False),
insecure_skip_tls_verify=dict(
type="bool", default=False, aliases=["skip_tls_certs_check"]
),
)
)
return arg_spec
@@ -787,6 +860,8 @@ def main():
set_values = module.params.get("set_values")
reuse_values = module.params.get("reuse_values")
reset_values = module.params.get("reset_values")
reset_then_reuse_values = module.params.get("reset_then_reuse_values")
insecure_skip_tls_verify = module.params.get("insecure_skip_tls_verify")
if update_repo_cache:
run_repo_update(module)
@@ -824,7 +899,9 @@ def main():
helm_cmd += " --repo=" + chart_repo_url
# Fetch chart info to have real version and real name for chart_ref from archive, folder or url
chart_info = fetch_chart_info(module, helm_cmd, chart_ref)
chart_info = fetch_chart_info(
module, helm_cmd, chart_ref, insecure_skip_tls_verify
)
if dependency_update:
if chart_info.get("dependencies"):
@@ -883,6 +960,8 @@ def main():
set_value_args=set_value_args,
reuse_values=reuse_values,
reset_values=reset_values,
reset_then_reuse_values=reset_then_reuse_values,
insecure_skip_tls_verify=insecure_skip_tls_verify,
)
changed = True
@@ -908,6 +987,8 @@ def main():
set_value_args,
reuse_values=reuse_values,
reset_values=reset_values,
reset_then_reuse_values=reset_then_reuse_values,
insecure_skip_tls_verify=insecure_skip_tls_verify,
)
if would_change and module._diff:
opt_result["diff"] = {"prepared": prepared}
@@ -943,6 +1024,8 @@ def main():
set_value_args=set_value_args,
reuse_values=reuse_values,
reset_values=reset_values,
reset_then_reuse_values=reset_then_reuse_values,
insecure_skip_tls_verify=insecure_skip_tls_verify,
)
changed = True

View File

@@ -75,9 +75,10 @@ options:
skip_tls_certs_check:
description:
- Whether or not to check tls certificate for the chart download.
- Requires helm >= 3.3.0.
- Requires helm >= 3.3.0. Alias C(insecure_skip_tls_verify) added in 5.3.0.
type: bool
default: False
aliases: [ insecure_skip_tls_verify ]
chart_devel:
description:
- Use development versions, too. Equivalent to version '>0.0.0-0'.
@@ -190,7 +191,9 @@ def main():
type="str", no_log=True, aliases=["password", "chart_repo_password"]
),
pass_credentials=dict(type="bool", default=False, no_log=False),
skip_tls_certs_check=dict(type="bool", default=False),
skip_tls_certs_check=dict(
type="bool", default=False, aliases=["insecure_skip_tls_verify"]
),
chart_devel=dict(type="bool"),
untar_chart=dict(type="bool", default=False),
destination=dict(type="path", required=True),

View File

@@ -31,6 +31,7 @@ options:
- Desired state of the registry.
- If set to V(present) attempt to log in to the remote registry server using the URL specified in O(host).
- If set to V(absent) attempt to log out from the remote registry server using the URL specified in O(host).
- As helm >= 3.18.0 reports successful logout even if the user is not logged in, this module will report a change regardless of the current state.
required: false
default: present
choices: ['present', 'absent']
@@ -129,6 +130,9 @@ failed:
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
AnsibleHelmModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
LooseVersion,
)
def arg_spec():
@@ -231,6 +235,13 @@ def main():
command=helm_cmd,
)
helm_version = module.get_helm_version()
if LooseVersion(helm_version) >= LooseVersion("3.18.0") and state == "absent":
# https://github.com/ansible-collections/kubernetes.core/issues/944
module.warn(
"The helm_registry_auth is not idempotent with helm >= 3.18.0, always report a change."
)
module.exit_json(changed=changed, stdout=out, stderr=err, command=helm_cmd)

View File

@@ -119,6 +119,13 @@ options:
aliases: [ force ]
default: False
version_added: 2.4.0
insecure_skip_tls_verify:
description:
- Skip tls certificate checks for the repository url.
type: bool
default: False
aliases: [ skip_tls_certs_check ]
version_added: "5.3.0"
"""
EXAMPLES = r"""
@@ -226,6 +233,7 @@ def install_repository(
repository_password,
pass_credentials,
force_update,
insecure_skip_tls_verify,
):
install_command = command + " repo add " + repository_name + " " + repository_url
@@ -239,6 +247,9 @@ def install_repository(
if force_update:
install_command += " --force-update"
if insecure_skip_tls_verify:
install_command += " --insecure-skip-tls-verify"
return install_command
@@ -262,6 +273,9 @@ def argument_spec():
),
pass_credentials=dict(type="bool", default=False, no_log=True),
force_update=dict(type="bool", default=False, aliases=["force"]),
insecure_skip_tls_verify=dict(
type="bool", default=False, aliases=["skip_tls_certs_check"]
),
)
)
return arg_spec
@@ -290,6 +304,7 @@ def main():
repo_state = module.params.get("repo_state")
pass_credentials = module.params.get("pass_credentials")
force_update = module.params.get("force_update")
insecure_skip_tls_verify = module.params.get("insecure_skip_tls_verify")
helm_cmd = module.get_helm_binary()
@@ -308,6 +323,7 @@ def main():
repo_password,
pass_credentials,
force_update,
insecure_skip_tls_verify,
)
changed = True
elif repository_status["url"] != repo_url:

View File

@@ -188,7 +188,8 @@ options:
description:
- Hide fields matching this option in the result
- An example might be C(hidden_fields=[metadata.managedFields])
- Only field definitions that don't reference list items are supported (so V(spec.containers[0]) would not work)
or V(hidden_fields=[spec.containers[0].env[3].value])
or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])
type: list
elements: str
version_added: 3.0.0

View File

@@ -79,6 +79,7 @@ options:
notes:
- the tar binary is required on the container when copying from local filesystem to pod.
- the (init) container has to be started before you copy files or directories to it.
"""
EXAMPLES = r"""

View File

@@ -48,7 +48,8 @@ options:
description:
- Hide fields matching any of the field definitions in the result
- An example might be C(hidden_fields=[metadata.managedFields])
- Only field definitions that don't reference list items are supported (so V(spec.containers[0]) would not work)
or V(hidden_fields=[spec.containers[0].env[3].value])
or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])
type: list
elements: str
version_added: 3.0.0

View File

@@ -3,6 +3,7 @@ helm_default_archive_name: "helm-{{ helm_version }}-{{ ansible_system | lower }}
helm_binary: "/tmp/helm/{{ ansible_system | lower }}-amd64/helm"
chart_test: "ingress-nginx"
chart_test_oci: "oci://registry-1.docker.io/bitnamicharts/redis"
chart_test_local_path: "nginx-ingress"
chart_test_version: 4.2.4
chart_test_version_local_path: 1.32.0
@@ -26,3 +27,5 @@ test_namespace:
- "helm-from-url"
- "helm-reuse-values"
- "helm-chart-with-space-into-name"
- "helm-reset-then-reuse-values"
- "helm-insecure"

View File

@@ -4,4 +4,5 @@
loop_control:
loop_var: helm_version
with_items:
- "v3.8.0"
- "v3.15.4"
- "v3.16.0"

View File

@@ -28,6 +28,9 @@
- name: test helm upgrade with reuse_values
include_tasks: test_helm_reuse_values.yml
- name: test helm upgrade with reset_then_reuse_values
include_tasks: test_helm_reset_then_reuse_values.yml
- name: test helm dependency update
include_tasks: test_up_dep.yml
@@ -41,6 +44,9 @@
- name: Test Skip CRDS feature in helm chart install
include_tasks: test_crds.yml
- name: Test insecure registry flag feature
include_tasks: test_helm_insecure.yml
- name: Clean helm install
file:
path: "{{ item }}"

View File

@@ -3,12 +3,23 @@
vars:
test_chart: "test-crds"
helm_namespace: "{{ test_namespace[0] }}"
helm_binary: helm
block:
- name: Create namespace
k8s:
kind: Namespace
name: "{{ helm_namespace }}"
- name: Check if CRD resource is already present
k8s_info:
namespace: default
kind: Foo
api_version: ansible.com/v1
ignore_errors: true
register: crd_check
- when: crd_check is failed
block:
- name: Copy test chart
copy:
src: "{{ test_chart }}"

View File

@@ -0,0 +1,52 @@
---
- name: Test helm insecure
vars:
helm_namespace: "{{ test_namespace[12] }}"
block:
- name: Initial chart installation (no flag set)
helm:
binary_path: "{{ helm_binary }}"
chart_ref: "{{ chart_test_oci }}"
release_name: test-secure
release_namespace: "{{ helm_namespace }}"
create_namespace: true
register: install
- name: Validate that insecure flag is not set
assert:
that:
- install is changed
- '"--insecure-skip-tls-verify" not in install.command'
- name: Initial chart installation (insecure flag set)
helm:
binary_path: "{{ helm_binary }}"
chart_ref: "{{ chart_test_oci }}"
release_name: test-insecure
release_namespace: "{{ helm_namespace }}"
insecure_skip_tls_verify: true
register: install
ignore_errors: true
- name: Validate that insecure flag IS set if helm version is >= 3.16.0
assert:
that:
- install is changed
- '"--insecure-skip-tls-verify" in install.command'
when: '"v3.16.0" <= helm_version'
- name: Validate that feature fails for helm < 3.16.0
assert:
that:
- install is failed
- '"insecure_skip_tls_verify requires helm >= 3.16.0" in install.msg'
when: 'helm_version < "v3.16.0"'
always:
- name: Remove helm namespace
k8s:
api_version: v1
kind: Namespace
name: "{{ helm_namespace }}"
state: absent

View File

@@ -13,3 +13,11 @@
that:
- helm_missing_binary is failed
- "'No such file or directory' in helm_missing_binary.msg"
when: ansible_version.full is version('2.20', '<')
- name: Assert that helm is not installed (ansible 2.20+)
assert:
that:
- helm_missing_binary is failed
- "'Error executing command' in helm_missing_binary.msg"
when: ansible_version.full is version('2.20', '>=')

View File

@@ -0,0 +1,75 @@
---
- name: Test helm reset_then_reuse_values
vars:
helm_namespace: "{{ test_namespace[11] }}"
chart_release_values:
replica:
replicaCount: 3
master:
count: 1
kind: Deployment
chart_reset_then_reuse_values:
replica:
replicaCount: 1
master:
count: 3
block:
- name: Initial chart installation
helm:
binary_path: "{{ helm_binary }}"
chart_ref: oci://registry-1.docker.io/bitnamicharts/redis
release_name: test-redis
release_namespace: "{{ helm_namespace }}"
create_namespace: true
release_values: "{{ chart_release_values }}"
register: install
- name: Get value set as string
helm_info:
binary_path: "{{ helm_binary }}"
release_name: test-redis
release_namespace: "{{ helm_namespace }}"
register: release_value
- name: Validate that chart values are as expected
assert:
that:
- install is changed
- '"--reset-then-reuse-values" not in install.command'
- release_value["status"]["values"] == chart_release_values
- name: Upgrade chart using reset_then_reuse_values=true
helm:
binary_path: "{{ helm_binary }}"
chart_ref: oci://registry-1.docker.io/bitnamicharts/redis
release_name: test-redis
release_namespace: "{{ helm_namespace }}"
reuse_values: false
reset_values: false
reset_then_reuse_values: true
release_values: "{{ chart_reset_then_reuse_values }}"
register: upgrade
- name: Get value set as string
helm_info:
binary_path: "{{ helm_binary }}"
release_name: test-redis
release_namespace: "{{ helm_namespace }}"
register: release_value
- name: Validate that chart values are as expected
assert:
that:
- upgrade is changed
- '"--reset-then-reuse-values" in upgrade.command'
- '"--reuse-values " not in upgrade.command'
- '"--reset-values" not in upgrade.command'
- release_value["status"]["values"] == chart_release_values | combine(chart_reset_then_reuse_values, recursive=true)
always:
- name: Remove helm namespace
k8s:
api_version: v1
kind: Namespace
name: "{{ helm_namespace }}"
state: absent

View File

@@ -10,7 +10,7 @@
binary_path: "{{ helm_binary }}"
state: present
plugin_path: https://github.com/databus23/helm-diff
plugin_version: 3.4.0
plugin_version: 3.9.13
- name: Copy test chart
copy:
@@ -324,3 +324,5 @@
ignore_errors: true
- include_tasks: reuse_values.yml
- include_tasks: reset_then_reuse_values.yml

View File

@@ -0,0 +1,189 @@
---
- name: Create temporary directory for helm chart
tempfile:
suffix: .helm
state: directory
register: helm_dir
- name: Test helm diff functionality
vars:
test_chart_path: "{{ helm_dir.path }}/test-chart-reuse-values"
test_release_name: "myrelease"
block:
- name: Install helm diff
kubernetes.core.helm_plugin:
binary_path: "{{ helm_binary }}"
state: present
plugin_path: https://github.com/databus23/helm-diff
plugin_version: 3.9.14
- name: Copy test chart
ansible.builtin.copy:
src: "test-chart-reuse-values"
dest: "{{ helm_dir.path }}"
- name: Delete existing namespace
kubernetes.core.k8s:
state: absent
wait: true
kind: Namespace
name: "{{ helm_namespace }}"
ignore_errors: true
- name: Create helm release
kubernetes.core.helm:
state: present
binary_path: "{{ helm_binary }}"
chart_ref: "{{ test_chart_path }}"
release_name: "{{ test_release_name }}"
release_namespace: "{{ helm_namespace }}"
create_namespace: true
release_values:
ansible_version: devel
phase: ci
wait: true
- name: Upgrade helm release (reset_values=false and reuse_values=false and reset_then_reuse_values=true)
kubernetes.core.helm:
binary_path: "{{ helm_binary }}"
chart_ref: "{{ test_chart_path }}"
reset_values: false
reuse_values: false
reset_then_reuse_values: true
release_name: "{{ test_release_name }}"
release_namespace: "{{ helm_namespace }}"
values:
ansible_version: devel
register: helm_upgrade
- name: Ensure task did not reported change
assert:
that:
- helm_upgrade is not changed
- name: Upgrade helm release (reset_then_reuse_values=true with default value for reset_values and reuse_values=false)
kubernetes.core.helm:
binary_path: "{{ helm_binary }}"
chart_ref: "{{ test_chart_path }}"
reuse_values: false
reset_then_reuse_values: true
release_name: "{{ test_release_name }}"
release_namespace: "{{ helm_namespace }}"
values:
ansible_version: devel
register: helm_upgrade
- name: Ensure task reported change
assert:
that:
- helm_upgrade is changed
# Delete helm and helm diff to install older version
- name: Uninstall helm diff
helm_plugin:
binary_path: "{{ helm_binary }}"
state: absent
plugin_name: diff
ignore_errors: true
- name: Delete Helm folders
file:
path: /tmp/helm/
state: absent
- name: Init Helm folders
file:
path: /tmp/helm
state: directory
- name: Set Helm old version
set_fact:
helm_archive_name: "helm-v3.8.0-linux-amd64.tar.gz"
helm_diff_old_version: "3.8.0"
- name: Unarchive Helm binary
unarchive:
src: "https://get.helm.sh/{{ helm_archive_name | default(helm_default_archive_name) }}"
dest: /tmp/helm/
remote_src: yes
retries: 10
delay: 5
register: result
until: result is not failed
- name: Upgrade helm release (with reset_then_reuse_values=true)
kubernetes.core.helm:
binary_path: "{{ helm_binary }}"
chart_ref: "{{ test_chart_path }}"
reuse_values: false
reset_then_reuse_values: true
release_name: "{{ test_release_name }}"
release_namespace: "{{ helm_namespace }}"
values:
ansible_version: test
register: helm_upgrade
ignore_errors: true
- name: Debug
debug:
var: helm_upgrade
- name: Ensure warning for Helm version
assert:
that:
- helm_upgrade is failed
- '"reset_then_reuse_values requires helm >= 3.14.0, current version is" in helm_upgrade.msg'
- name: Install helm diff
helm_plugin:
binary_path: "{{ helm_binary }}"
state: present
plugin_path: https://github.com/databus23/helm-diff
plugin_version: "{{ helm_diff_old_version }}"
- name: Upgrade helm release (with reset_then_reuse_values=true)
kubernetes.core.helm:
binary_path: "{{ helm_binary }}"
chart_ref: "{{ test_chart_path }}"
reuse_values: false
reset_then_reuse_values: true
release_name: "{{ test_release_name }}"
release_namespace: "{{ helm_namespace }}"
values:
ansible_version: devel
register: helm_upgrade
ignore_errors: true
- name: Debug
debug:
var: helm_upgrade
- name: Ensure warning for Helm Diff version
assert:
that:
- helm_upgrade is failed
- '"reset_then_reuse_values requires helm diff >= 3.9.12, current version is" in helm_upgrade.msg'
always:
- name: Remove temporary directory
file:
path: "{{ helm_dir.path }}"
state: absent
ignore_errors: true
- name: Uninstall helm diff
kubernetes.core.helm_plugin:
binary_path: "{{ helm_binary }}"
state: absent
plugin_name: diff
ignore_errors: true
- name: Remove helm namespace
kubernetes.core.k8s:
api_version: v1
kind: Namespace
name: "{{ helm_namespace }}"
state: absent
wait: true
ignore_errors: true

View File

@@ -90,4 +90,5 @@
kind: Namespace
name: "{{ helm_namespace }}"
state: absent
wait: true
ignore_errors: true

View File

@@ -108,6 +108,10 @@
assert:
that: "'Removing login credentials' in _helm_registry_auth_logout.stderr"
- name: Test idempotency of logout with helm < 3.18.0
when: _helm_version.stdout is ansible.builtin.version('v3.18.0', '<')
block:
- name: Test logout idempotency
helm_registry_auth:
host: localhost:{{ registry_port }}
@@ -133,8 +137,7 @@
# Helm binary prints the message to stderr
ansible.builtin.assert:
that:
- "'push access denied' in _save_chart.stderr"
- "'authorization failed' in _save_chart.stderr"
- "'push access denied' in _save_chart.stderr or 'basic credential not found' in _save_chart.stderr"
- "_save_chart.rc != 0"
- "'localhost:{{ registry_port }}' not in _config_json.content | b64decode"
@@ -155,7 +158,8 @@
- name: Assert that the registry is not logged in and auth data is not saved
ansible.builtin.assert:
that:
- "'401 Unauthorized' in _helm_registry_auth_wrong.stderr"
- "'401' in _helm_registry_auth_wrong.stderr"
- "'unauthorized' in _helm_registry_auth_wrong.stderr | lower"
- "'{{ wrong_password }}' not in _helm_registry_auth_correct.command"
- "'{{ wrong_password }}' not in _helm_registry_auth_correct.stdout"
- "'{{ wrong_password }}' not in _helm_registry_auth_correct.stderr"

View File

@@ -1,3 +1,5 @@
---
collections:
- kubernetes.core
dependencies:
- install_helm

View File

@@ -16,6 +16,7 @@
assert:
that:
- repository is changed
- '"--insecure-skip-tls-verify" not in repository.command'
- name: Check idempotency
helm_repository:
@@ -78,3 +79,23 @@
assert:
that:
- repository is not changed
- name: Add test_helm_repo chart repository as insecure
helm_repository:
binary_path: "{{ helm_binary }}"
name: test_helm_repo
repo_url: "{{ chart_test_repo }}"
insecure_skip_tls_verify: true
register: repository
- name: Assert that repository added and flag set
assert:
that:
- repository is changed
- '"--insecure-skip-tls-verify" in repository.command'
- name: Clean test_helm_repo chart repository
helm_repository:
binary_path: "{{ helm_binary }}"
name: test_helm_repo
state: absent

View File

@@ -1,4 +1,4 @@
---
helm_version: v3.8.0
helm_version: v3.16.4
helm_install_path: /tmp/helm
helm_default_archive_name: "helm-{{ helm_version }}-{{ ansible_system | lower }}-amd64.tar.gz"

View File

@@ -26,7 +26,8 @@
assert:
that:
- k8s_configmap is changed
- k8s_configmap.result.metadata.annotations|default(False)
- '"annotations" in k8s_configmap.result.metadata'
- k8s_configmap.result.metadata.annotations != {}
- name: Add same configmap again
k8s:
@@ -467,7 +468,7 @@
assert:
that:
- k8s_secret is changed
- k8s_secret.result.data.foo
- k8s_secret.result.data.foo != ""
- name: Add same secret
k8s:
@@ -748,7 +749,7 @@
assert:
that:
- _create is changed
- not _info.resources
- _info.resources | length == 0
# server side apply over kubernetes client releases
- name: Create temporary directory

View File

@@ -14,3 +14,9 @@ pod_with_two_container:
pod_without_executable_find:
name: openjdk-pod
pod_with_initcontainer_and_container:
name: pod-copy-2
container:
- container-20
- container-21

View File

@@ -18,6 +18,23 @@
wait: yes
template: pods_definition.j2
- name: Create Init Pod
k8s:
namespace: '{{ copy_namespace }}'
template: pods_definition_init.j2
- kubernetes.core.k8s_info:
api_version: v1
kind: Pod
name: '{{ pod_with_initcontainer_and_container.name }}'
namespace: '{{ copy_namespace }}'
register: init_pod_status
until: >-
init_pod_status.resources|length > 0
and 'initContainerStatuses' in init_pod_status.resources.0.status
and init_pod_status.resources.0.status.initContainerStatuses|length > 0
and init_pod_status.resources.0.status.initContainerStatuses.0.started|bool
- include_tasks: test_copy_errors.yml
- include_tasks: test_check_mode.yml
- include_tasks: test_copy_file.yml
@@ -25,6 +42,7 @@
- include_tasks: test_copy_directory.yml
- include_tasks: test_copy_large_file.yml
- include_tasks: test_copy_item_with_space_in_its_name.yml
- include_tasks: test_init_container_pod.yml
always:

View File

@@ -67,3 +67,21 @@
that:
- copy_fake_container is failed
- copy_fake_container.msg == "Pod has no container this_is_a_fake_container"
# copy file to not started container in pod should fail
- name: copy file to not started container in pod should fail
k8s_cp:
namespace: '{{ copy_namespace }}'
pod: '{{ pod_with_initcontainer_and_container.name }}'
remote_path: /tmp
local_path: files/simple_file.txt
state: to_pod
container: '{{ pod_with_initcontainer_and_container.container[1] }}'
ignore_errors: true
register: copy_not_started_container
- name: check that error message is as expected
assert:
that:
- copy_not_started_container is failed
- copy_not_started_container.msg == "Pod container {{ pod_with_initcontainer_and_container.container[1] }} is not started"

View File

@@ -0,0 +1,25 @@
---
- set_fact:
random_content: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits,punctuation length=128') }}"
- name: Copy content into init container
k8s_cp:
namespace: '{{ copy_namespace }}'
pod: '{{ pod_with_initcontainer_and_container.name }}'
remote_path: /file_from_localhost.txt
content: '{{ random_content }}'
container: '{{ pod_with_initcontainer_and_container.container[0] }}'
state: to_pod
- name: Get the content from copied file
kubernetes.core.k8s_exec:
namespace: '{{ copy_namespace }}'
pod: '{{ pod_with_initcontainer_and_container.name }}'
container: '{{ pod_with_initcontainer_and_container.container[0] }}'
command: cat /file_from_localhost.txt
register: exec_out
- name: check that content is found and the same as generated earlier
assert:
that:
- exec_out.stdout == random_content

View File

@@ -0,0 +1,20 @@
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ pod_with_initcontainer_and_container.name }}'
spec:
initContainers:
- name: '{{ pod_with_initcontainer_and_container.container[0] }}'
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
containers:
- name: '{{ pod_with_initcontainer_and_container.container[1] }}'
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done

View File

@@ -38,7 +38,7 @@
- name: Assert that there are pods
assert:
that:
- pods_create.resources
- pods_create.resources | length > 0
- name: Remove the daemonset
k8s:
@@ -74,7 +74,7 @@
- name: Assert that deleting the daemonset deleted the pods
assert:
that:
- not pods_delete.resources
- pods_delete.resources | length == 0
# test deletion using label selector
- name: Deploy load balancer
@@ -152,6 +152,27 @@
register: _deployment
failed_when: _deployment.resources | length == 0
- name: Trying to delete nginx deployment in check mode and with deleteOptions set
k8s:
kind: Deployment
api_version: apps/v1
namespace: "{{ test_namespace }}"
name: nginx-d
state: absent
delete_options:
propagationPolicy: Foreground
check_mode: true
register: _delete
- name: Validate that Deployment nginx-d still exists
k8s_info:
kind: Deployment
api_version: apps/v1
namespace: "{{ test_namespace }}"
name: nginx-d
register: _deployment
failed_when: _deployment.resources | length != 1
- name: Trying to delete using delete_all=true but missing kind option
k8s:
api_version: apps/v1

View File

@@ -244,7 +244,7 @@
kind: Pod
name: '{{ drain_pod_name }}'
register: _result
failed_when: _result.resources
failed_when: _result.resources | length > 0
- name: assert that emptyDir pod was deleted
k8s_info:
@@ -346,7 +346,7 @@
kind: Pod
name: '{{ drain_pod_name }}-01'
register: _result
failed_when: _result.resources
failed_when: _result.resources | length > 0
# test: drain using pod_selectors
- name: Uncordon node

View File

@@ -394,9 +394,11 @@
register: k8s_info_testing6
failed_when: not k8s_info_testing6.resources or k8s_info_testing6.resources[0].status.phase != "Active"
- name: Create large configmap data
command: dd if=/dev/urandom bs=500K count=1
register: cmap_data
- name: Create a file with specific size and attributes, to be used as swap space
community.general.filesize:
path: /tmp/configmap.bin
size: 500K
source: /dev/urandom
- name: Create configmap with large value
k8s:
@@ -407,7 +409,7 @@
name: testmap
namespace: testing
data:
testkey: "{{ cmap_data.stdout | b64encode }}"
testkey: "{{ lookup('file', '/tmp/configmap.bin') | b64encode }}"
wait: true
register: result
@@ -424,7 +426,7 @@
- assert:
that:
- result.resources[0].data.testkey == (cmap_data.stdout | b64encode)
- result.resources[0].data.testkey == (lookup('file', '/tmp/configmap.bin') | b64encode)
# test setting module defaults for kubernetes.core.k8s_info
- block:

View File

@@ -36,7 +36,7 @@
label_selectors:
- "job=gc"
register: wait_job
until: wait_job.resources
until: wait_job.resources | length > 0
retries: 5
delay: 10
@@ -87,7 +87,7 @@
label_selectors:
- "job=gc"
register: wait_job
until: wait_job.resources
until: wait_job.resources | length > 0
retries: 5
delay: 10
@@ -139,7 +139,7 @@
label_selectors:
- "job=gc"
register: wait_job
until: wait_job.resources
until: wait_job.resources | length > 0
retries: 5
delay: 10
@@ -225,7 +225,7 @@
- name: Assert job is deleted
assert:
that: not job.resources
that: job.resources | length == 0
always:
- name: Delete namespace

View File

@@ -58,7 +58,7 @@
- "'managedFields' not in hf4.resources[0]['metadata']"
- name: Hiding a changed field should still result in a change
- name: Hiding a changed field should not result in a change
k8s:
definition: "{{ hide_fields_base_configmap | combine({'data':{'hello':'different'}}) }}"
hidden_fields:
@@ -67,16 +67,17 @@
register: hf5
diff: true
- name: Ensure that hidden changed field changed
- name: Ensure that hidden changed field not changed
assert:
that:
- hf5.changed
- not hf5.changed
- name: Apply works with hidden fields
k8s:
definition: "{{ hide_fields_base_configmap | combine({'data':{'anew':'value'}}) }}"
hidden_fields:
- data
- metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]
apply: true
register: hf6
diff: true
@@ -86,6 +87,22 @@
that:
- hf6.changed
- name: Ensure hidden fields are not present
assert:
that:
- >-
'annotations' not in hf6.result.metadata or
'kubectl.kubernetes.io/last-applied-configuration'
not in hf6.result.metadata.annotations
- >-
'annotations' not in hf6.diff.before.metadata or
'kubectl.kubernetes.io/last-applied-configuration'
not in hf6.diff.before.metadata.annotations
- >-
'annotations' not in hf6.diff.after.metadata or
'kubectl.kubernetes.io/last-applied-configuration'
not in hf6.diff.after.metadata.annotations
- name: Hidden field should not show up in deletion
k8s:
definition: "{{ hide_fields_base_configmap}}"

View File

@@ -47,7 +47,7 @@
- result.changed
- result.result.metadata.labels.label2 == "bar"
- result.result.spec.containers[0].image == "busybox:glibc"
- result.diff
- result.diff != {}
- name: Describe pod
kubernetes.core.k8s_info:

View File

@@ -23,7 +23,7 @@
- name: Update directory permissions
file:
path: "{{ manifests_dir.path }}"
mode: 0755
mode: '0755'
- name: Create manifests files
copy:

View File

@@ -129,7 +129,7 @@
that:
- scale_down is changed
- '"duration" in scale_down'
- scale_down.diff
- scale_down.diff != {}
- name: Scale the deployment once again (idempotency)
k8s_scale:
@@ -274,7 +274,7 @@
assert:
that:
- scale_down_no_wait is changed
- scale_down_no_wait.diff
- scale_down_no_wait.diff != {}
- scale_down_no_wait_pods.resources | length == 1
# scale multiple resource using label selectors

View File

@@ -421,7 +421,7 @@
- name: Assert that taints have been removed
assert:
that:
- _result.resources | selectattr('spec.taints', 'undefined')
- _result.resources | selectattr('spec.taints', 'undefined') | list | length > 0
always:

View File

@@ -7,7 +7,7 @@
kubernetes.core.k8s_service:
template: "pod_one.j2"
state: present
ignore_errors: yes
ignore_errors: true
register: r
- name: Check for expected failures in last tasks
@@ -35,7 +35,7 @@
k8s_pod_name_one: pod
k8s_pod_namespace: "{{ template_namespace }}"
register: r
ignore_errors: yes
ignore_errors: true
- name: Check if definition and template are mutually exclusive
assert:
@@ -52,7 +52,7 @@
k8s_pod_name_one: pod
k8s_pod_namespace: "{{ template_namespace }}"
register: r
ignore_errors: yes
ignore_errors: true
- name: Check if src and template are mutually exclusive
assert:
@@ -63,7 +63,7 @@
- name: Create pod using template (direct specification)
kubernetes.core.k8s:
template: "pod_one.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-1
@@ -79,7 +79,7 @@
kubernetes.core.k8s:
template:
- default
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-2
@@ -96,7 +96,7 @@
kubernetes.core.k8s:
template:
path: "pod_one.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-3
@@ -114,12 +114,11 @@
path: "pod_two.j2"
variable_start_string: '[['
variable_end_string: ']]'
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_two: pod-4
k8s_pod_namespace: "[[ template_namespace ]]"
ansible_python_interpreter: "[[ ansible_playbook_python ]]"
k8s_pod_namespace: "template-test"
register: r
- name: Assert that pod creation succeeded using template
@@ -131,7 +130,7 @@
kubernetes.core.k8s:
template:
path: "pod_three.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_three_one: pod-5
@@ -152,7 +151,7 @@
variable_start_string: '[['
variable_end_string: ']]'
- path: "pod_three.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-7
@@ -239,63 +238,6 @@
- resource.result.results | selectattr('changed') | list | length == 1
- resource.result.results | selectattr('error', 'defined') | list | length == 1
# Test resource definition using template with 'omit'
- name: Deploy configmap using template
k8s:
namespace: "{{ template_namespace }}"
name: test-data
template: configmap.yml.j2
- name: Read configmap created
k8s_info:
kind: configmap
namespace: "{{ template_namespace }}"
name: test-data
register: _configmap
- name: Validate that the configmap does not contains annotations
assert:
that:
- '"annotations" not in _configmap.resources.0.metadata'
- name: Create resource once again
k8s:
namespace: "{{ template_namespace }}"
name: test-data
template: configmap.yml.j2
register: _configmap
- name: assert that nothing changed
assert:
that:
- _configmap is not changed
- name: Create resource once again (using description)
k8s:
namespace: "{{ template_namespace }}"
name: test-data
template: configmap.yml.j2
register: _configmap
vars:
k8s_configmap_desc: "This is a simple configmap used to test ansible k8s collection"
- name: assert that configmap was changed
assert:
that:
- _configmap is changed
- name: Read configmap created
k8s_info:
kind: configmap
namespace: "{{ template_namespace }}"
name: test-data
register: _configmap
- name: Validate that the configmap does not contains annotations
assert:
that:
- _configmap.resources.0.metadata.annotations.description == "This is a simple configmap used to test ansible k8s collection"
always:
- name: Remove namespace (Cleanup)
kubernetes.core.k8s:

View File

@@ -1,7 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
description: "{{ k8s_configmap_desc | default(omit) }}"
data:
key: "testing-template"

View File

@@ -56,8 +56,8 @@
# Without wantlist=True lookup should return mapping
- test5 is mapping
- test6 is mapping
# errors='ignore'
- test7 is string
# errors='ignore' (return null with ansible-core 2.19)
- test7 is string or not test7
- test8 is not defined
- name: Create another namespace with label

View File

@@ -45,7 +45,7 @@
- name: make script as executable
file:
path: "{{ tmp_dir_path }}/install_kustomize.sh"
mode: 0755
mode: '0755'
- name: Install kustomize
command: "{{ tmp_dir_path }}/install_kustomize.sh"

View File

@@ -4,10 +4,17 @@
namespace_to_create: "{{ item.name | default(item) }}"
namespace_labels: "{{ item.labels | default(omit) }}"
with_items: "{{ test_namespace }}"
when: test_namespace | type_debug == "list"
when:
- test_namespace is not string
- test_namespace is not mapping
- test_namespace is iterable
- include_tasks: tasks/create.yml
vars:
namespace_to_create: "{{ test_namespace }}"
namespace_labels: "{{ test_namespace_labels | default(omit) }}"
when: test_namespace | type_debug == "AnsibleUnicode"
when:
- test_namespace is string
- test_namespace is iterable
- test_namespace is sequence
- test_namespace is not mapping

View File

@@ -1,30 +0,0 @@
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.11!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.11!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.11!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/unit/module_utils/fixtures/pods.yml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm_diff/files/test-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py validate-modules:return-syntax-error
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
tests/integration/targets/k8s_delete/files/deployments.yaml yamllint!skip
tests/integration/targets/helm_diff/files/test-chart-reuse-values/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm_registry_auth/tasks/main.yaml yamllint!skip
tests/integration/targets/helm_diff/files/test-chart-deployment-time/templates/configmap.yaml yamllint!skip

View File

@@ -14,6 +14,7 @@ plugins/module_utils/version.py pylint!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/unit/module_utils/fixtures/clusteroperator.yml yamllint!skip
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/integration/targets/k8s_delete/files/deployments.yaml yamllint!skip

View File

@@ -14,6 +14,7 @@ plugins/module_utils/version.py pylint!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/unit/module_utils/fixtures/clusteroperator.yml yamllint!skip
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/integration/targets/k8s_delete/files/deployments.yaml yamllint!skip

View File

@@ -11,6 +11,7 @@ plugins/module_utils/version.py pylint!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/unit/module_utils/fixtures/clusteroperator.yml yamllint!skip
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/integration/targets/k8s_delete/files/deployments.yaml yamllint!skip

View File

@@ -11,6 +11,7 @@ plugins/module_utils/version.py pylint!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/unit/module_utils/fixtures/clusteroperator.yml yamllint!skip
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/integration/targets/k8s_delete/files/deployments.yaml yamllint!skip

View File

@@ -1,16 +1,17 @@
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.11!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/discovery.py import-3.12!skip
plugins/module_utils/client/discovery.py import-3.13!skip
plugins/module_utils/client/resource.py import-3.11!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py import-3.10!skip
plugins/module_utils/client/resource.py import-3.12!skip
plugins/module_utils/client/resource.py import-3.13!skip
plugins/module_utils/k8sdynamicclient.py import-3.11!skip
plugins/module_utils/k8sdynamicclient.py import-3.12!skip
plugins/module_utils/k8sdynamicclient.py import-3.13!skip
plugins/module_utils/version.py pylint!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/unit/module_utils/fixtures/clusteroperator.yml yamllint!skip
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/integration/targets/k8s_delete/files/deployments.yaml yamllint!skip

View File

@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2022, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from datetime import datetime
from ansible_collections.kubernetes.core.plugins.action.k8s_info import RemoveOmit
def get_omit_token():
return "__omit_place_holder__%s" % datetime.now().strftime("%Y%m%d%H%M%S")
def test_remove_omit_from_str():
omit_token = get_omit_token()
src = """
project: ansible
collection: {omit}
""".format(
omit=omit_token
)
result = RemoveOmit(src, omit_value=omit_token).output()
assert len(result) == 1
assert result[0] == dict(project="ansible")
def test_remove_omit_from_list():
omit_token = get_omit_token()
src = """
items:
- {omit}
""".format(
omit=omit_token
)
result = RemoveOmit(src, omit_value=omit_token).output()
assert len(result) == 1
assert result[0] == dict(items=[])
def test_remove_omit_from_list_of_dict():
omit_token = get_omit_token()
src = """
items:
- owner: ansible
team: {omit}
- simple_list_item
""".format(
omit=omit_token
)
result = RemoveOmit(src, omit_value=omit_token).output()
assert len(result) == 1
assert result[0] == dict(items=[dict(owner="ansible"), "simple_list_item"])
def test_remove_omit_combined():
omit_token = get_omit_token()
src = """
items:
- {omit}
- list_item_a
- list_item_b
parent:
child:
subchilda: {omit}
subchildb:
name: {omit}
age: 3
""".format(
omit=omit_token
)
result = RemoveOmit(src, omit_value=omit_token).output()
assert len(result) == 1
assert result[0] == dict(
items=["list_item_a", "list_item_b"],
parent=dict(child=dict(subchildb=dict(age=3))),
)
def test_remove_omit_mutiple_documents():
omit_token = get_omit_token()
src = [
"""
project: ansible
collection: {omit}
""".format(
omit=omit_token
),
"---",
"""
project: kubernetes
environment: production
collection: {omit}""".format(
omit=omit_token
),
]
src = "\n".join(src)
print(src)
result = RemoveOmit(src, omit_value=omit_token).output()
assert len(result) == 2
assert result[0] == dict(project="ansible")
assert result[1] == dict(project="kubernetes", environment="production")

View File

@@ -10,7 +10,6 @@ import ansible.module_utils.basic
import pytest
from ansible.module_utils._text import to_bytes
from ansible.module_utils.common._collections_compat import MutableMapping
from ansible.module_utils.six import string_types
@pytest.fixture
@@ -20,7 +19,7 @@ def stdin(mocker, request):
old_argv = sys.argv
sys.argv = ["ansible_unittest"]
if isinstance(request.param, string_types):
if isinstance(request.param, str):
args = request.param
elif isinstance(request.param, MutableMapping):
if "ANSIBLE_MODULE_ARGS" not in request.param:

View File

@@ -0,0 +1,99 @@
---
apiVersion: config.openshift.io/v1
kind: ClusterOperator
metadata:
name: authentication
spec: {}
status:
conditions:
- message: All is well
reason: AsExpected
status: 'False'
type: Degraded
- message: 'AuthenticatorCertKeyProgressing: All is well'
reason: AsExpected
status: 'False'
type: Progressing
- message: All is well
reason: AsExpected
status: 'True'
type: Available
- message: All is well
reason: AsExpected
status: 'True'
type: Upgradeable
- reason: NoData
status: Unknown
type: EvaluationConditionsDetected
---
apiVersion: config.openshift.io/v1
kind: ClusterOperator
metadata:
name: dns
spec: {}
status:
conditions:
- message: DNS "default" is available.
reason: AsExpected
status: 'True'
type: Available
- message: 'DNS "default" reports Progressing=True: "Have 2 available node-resolver
pods, want 3."'
reason: DNSReportsProgressingIsTrue
status: 'True'
type: Progressing
- reason: DNSNotDegraded
status: 'False'
type: Degraded
- message: 'DNS default is upgradeable: DNS Operator can be upgraded'
reason: DNSUpgradeable
status: 'True'
type: Upgradeable
---
apiVersion: config.openshift.io/v1
kind: ClusterOperator
metadata:
name: dns
spec: {}
status:
conditions:
- message: DNS "default" is available.
reason: AsExpected
status: 'True'
type: Available
- message: 'DNS "default" reports Progressing=True: "Have 2 available node-resolver
pods, want 3."'
reason: DNSReportsProgressingIsTrue
status: 'False'
type: Progressing
- reason: DNSNotDegraded
status: 'True'
type: Degraded
- message: 'DNS default is upgradeable: DNS Operator can be upgraded'
reason: DNSUpgradeable
status: 'False'
type: Upgradeable
---
apiVersion: config.openshift.io/v1
kind: ClusterOperator
metadata:
name: dns
spec: {}
status:
conditions:
- message: DNS "default" is available.
reason: AsExpected
status: 'False'
type: Available
- message: 'DNS "default" reports Progressing=True: "Have 2 available node-resolver
pods, want 3."'
reason: DNSReportsProgressingIsTrue
status: 'True'
type: Progressing
- reason: DNSNotDegraded
status: 'True'
type: Degraded
- message: 'DNS default is upgradeable: DNS Operator can be upgraded'
reason: DNSUpgradeable
status: 'False'
type: Upgradeable

View File

@@ -2,50 +2,57 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import re
import kubernetes
import pytest
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from mock import MagicMock, patch
MINIMAL_K8S_VERSION = "24.2.0"
UNSUPPORTED_K8S_VERSION = "11.0.0"
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_no_warn(monkeypatch, stdin, capfd):
class FakeAnsibleModule:
def __init__(self, **kwargs):
pass
def exit_json(self):
raise SystemExit(0)
@patch.object(AnsibleK8SModule, "warn")
def test_no_warn(m_ansible_k8s_module_warn, monkeypatch, capfd):
monkeypatch.setattr(kubernetes, "__version__", MINIMAL_K8S_VERSION)
module = AnsibleK8SModule(argument_spec={})
m_ansible_k8s_module_warn.side_effect = print
module = AnsibleK8SModule(argument_spec={}, module_class=FakeAnsibleModule)
with pytest.raises(SystemExit):
module.exit_json()
out, err = capfd.readouterr()
return_value = json.loads(out)
assert return_value.get("exception") is None
assert return_value.get("warnings") is None
assert return_value.get("failed") is None
m_ansible_k8s_module_warn.assert_not_called()
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_warn_on_k8s_version(monkeypatch, stdin, capfd):
@patch.object(AnsibleK8SModule, "warn")
def test_warn_on_k8s_version(m_ansible_k8s_module_warn, monkeypatch, capfd):
monkeypatch.setattr(kubernetes, "__version__", UNSUPPORTED_K8S_VERSION)
module = AnsibleK8SModule(argument_spec={})
m_ansible_k8s_module_warn.side_effect = print
module = AnsibleK8SModule(argument_spec={}, module_class=FakeAnsibleModule)
with pytest.raises(SystemExit):
module.exit_json()
m_ansible_k8s_module_warn.assert_called_once()
out, err = capfd.readouterr()
return_value = json.loads(out)
assert return_value.get("warnings") is not None
warnings = return_value["warnings"]
assert len(warnings) == 1
assert "kubernetes" in warnings[0]
assert MINIMAL_K8S_VERSION in warnings[0]
assert (
re.search(
r"kubernetes<([0-9]+\.[0-9]+\.[0-9]+) is not supported or tested. Some features may not work.",
out,
)
is not None
)
dependencies = [
@@ -58,9 +65,17 @@ dependencies = [
@pytest.mark.parametrize(
"stdin,desired,actual,result", [({}, *d) for d in dependencies], indirect=["stdin"]
)
def test_has_at_least(monkeypatch, stdin, desired, actual, result, capfd):
@patch.object(AnsibleK8SModule, "warn")
def test_has_at_least(
m_ansible_k8s_module_warn, monkeypatch, stdin, desired, actual, result, capfd
):
monkeypatch.setattr(kubernetes, "__version__", actual)
def fake_warn(x):
print(x)
raise SystemExit(1)
m_ansible_k8s_module_warn.side_effect = fake_warn
module = AnsibleK8SModule(argument_spec={})
assert module.has_at_least("kubernetes", desired) is result
@@ -80,11 +95,18 @@ def test_requires_fails_with_message(
monkeypatch, stdin, dependency, version, msg, capfd
):
monkeypatch.setattr(kubernetes, "__version__", "24.2.0")
module = AnsibleK8SModule(argument_spec={})
module = AnsibleK8SModule(argument_spec={}, module_class=FakeAnsibleModule)
def fake_fail_json(**kwargs):
print(f"Printing message => {kwargs}")
print(kwargs.get("msg"))
raise SystemExit(1)
module.fail_json = MagicMock()
module.fail_json.side_effect = fake_fail_json
with pytest.raises(SystemExit):
module.requires(dependency, version)
module.fail_json.assert_called_once()
out, err = capfd.readouterr()
return_value = json.loads(out)
assert return_value.get("failed")
assert msg in return_value.get("msg")
assert msg in out

View File

@@ -0,0 +1,264 @@
# Copyright [2025] [Red Hat, Inc.]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
build_hidden_field_tree,
hide_fields,
)
def test_hiding_missing_field_does_nothing():
output = dict(
kind="ConfigMap", metadata=dict(name="foo"), data=dict(one="1", two="2")
)
hidden_fields = ["doesnotexist"]
assert hide_fields(output, hidden_fields) == output
def test_hiding_simple_field():
output = dict(
kind="ConfigMap", metadata=dict(name="foo"), data=dict(one="1", two="2")
)
hidden_fields = ["metadata"]
expected = dict(kind="ConfigMap", data=dict(one="1", two="2"))
assert hide_fields(output, hidden_fields) == expected
def test_hiding_only_key_in_dict_removes_dict():
output = dict(kind="ConfigMap", metadata=dict(name="foo"), data=dict(one="1"))
hidden_fields = ["data.one"]
expected = dict(kind="ConfigMap", metadata=dict(name="foo"))
assert hide_fields(output, hidden_fields) == expected
def test_hiding_all_keys_in_dict_removes_dict():
output = dict(
kind="ConfigMap", metadata=dict(name="foo"), data=dict(one="1", two="2")
)
hidden_fields = ["data.one", "data.two"]
expected = dict(kind="ConfigMap", metadata=dict(name="foo"))
assert hide_fields(output, hidden_fields) == expected
def test_hiding_multiple_fields():
output = dict(
kind="ConfigMap", metadata=dict(name="foo"), data=dict(one="1", two="2")
)
hidden_fields = ["metadata", "data.one"]
expected = dict(kind="ConfigMap", data=dict(two="2"))
assert hide_fields(output, hidden_fields) == expected
def test_hiding_dict_key():
output = dict(
kind="ConfigMap",
metadata=dict(
name="foo",
annotations={
"kubectl.kubernetes.io/last-applied-configuration": '{"testvalue"}'
},
),
data=dict(one="1", two="2"),
)
hidden_fields = [
"metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]",
]
expected = dict(
kind="ConfigMap", metadata=dict(name="foo"), data=dict(one="1", two="2")
)
assert hide_fields(output, hidden_fields) == expected
def test_hiding_list_value_key():
output = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(
containers=[
dict(
name="containers",
image="busybox",
env=[
dict(name="ENV1", value="env1"),
dict(name="ENV2", value="env2"),
dict(name="ENV3", value="env3"),
],
)
]
),
)
hidden_fields = ["spec.containers[0].env[1].value"]
expected = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(
containers=[
dict(
name="containers",
image="busybox",
env=[
dict(name="ENV1", value="env1"),
dict(name="ENV2"),
dict(name="ENV3", value="env3"),
],
)
]
),
)
assert hide_fields(output, hidden_fields) == expected
def test_hiding_last_list_item():
output = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(
containers=[
dict(
name="containers",
image="busybox",
env=[
dict(name="ENV1", value="env1"),
],
)
]
),
)
hidden_fields = ["spec.containers[0].env[0]"]
expected = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(
containers=[
dict(
name="containers",
image="busybox",
)
]
),
)
assert hide_fields(output, hidden_fields) == expected
def test_hiding_nested_dicts_using_brackets():
output = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(
containers=[
dict(
name="containers",
image="busybox",
securityContext=dict(runAsUser=101),
)
]
),
)
hidden_fields = ["spec.containers[0][securityContext][runAsUser]"]
expected = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(
containers=[
dict(
name="containers",
image="busybox",
)
]
),
)
assert hide_fields(output, hidden_fields) == expected
def test_using_jinja_syntax():
output = dict(
kind="ConfigMap", metadata=dict(name="foo"), data=["0", "1", "2", "3"]
)
hidden_fields = ["data.2"]
expected = dict(kind="ConfigMap", metadata=dict(name="foo"), data=["0", "1", "3"])
assert hide_fields(output, hidden_fields) == expected
def test_remove_multiple_items_from_list():
output = dict(
kind="ConfigMap", metadata=dict(name="foo"), data=["0", "1", "2", "3"]
)
hidden_fields = ["data[0]", "data[2]"]
expected = dict(kind="ConfigMap", metadata=dict(name="foo"), data=["1", "3"])
assert hide_fields(output, hidden_fields) == expected
def test_hide_dict_and_nested_dict():
output = {
"kind": "Pod",
"metadata": {
"labels": {
"control-plane": "controller-manager",
"pod-template-hash": "687b856498",
},
"annotations": {
"kubectl.kubernetes.io/default-container": "awx-manager",
"creationTimestamp": "2025-01-16T12:40:43Z",
},
},
}
hidden_fields = ["metadata.labels.pod-template-hash", "metadata.labels"]
expected = {
"kind": "Pod",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/default-container": "awx-manager",
"creationTimestamp": "2025-01-16T12:40:43Z",
}
},
}
assert hide_fields(output, hidden_fields) == expected
@pytest.mark.parametrize(
"hidden_fields,expected",
[
(
[
"data[0]",
"data[1]",
"metadata.annotation",
"metadata.annotation[0].name",
],
{"data": {"0": None, "1": None}, "metadata": {"annotation": None}},
),
(
[
"data[0]",
"data[1]",
"metadata.annotation[0].name",
"metadata.annotation",
],
{"data": {"0": None, "1": None}, "metadata": {"annotation": None}},
),
(
[
"data[0]",
"data[1]",
"data",
"metadata.annotation[0].name",
"metadata.annotation",
],
{"data": None, "metadata": {"annotation": None}},
),
],
)
def test_build_hidden_field_tree(hidden_fields, expected):
assert build_hidden_field_tree(hidden_fields) == expected

View File

@@ -202,8 +202,8 @@ def test_service_create_resource_warnings(
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"
assert str(warnings[0]) == "test warning 1"
assert str(warnings[1]) == "test warning 2"
def test_service_create_resource_check_mode():
@@ -289,8 +289,8 @@ def test_service_apply_existing_resource_warnings(
)
assert result == mock_pod_resource_instance.to_dict()
assert warnings[0] == "test warning 1"
assert warnings[1] == "test warning 2"
assert str(warnings[0]) == "test warning 1"
assert str(warnings[1]) == "test warning 2"
def test_service_replace_existing_resource(

View File

@@ -9,6 +9,7 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import
DummyWaiter,
Waiter,
clock,
cluster_operator_ready,
custom_condition,
deployment_ready,
exists,
@@ -29,6 +30,7 @@ def resources(filepath):
RESOURCES = resources("fixtures/definitions.yml")
PODS = resources("fixtures/pods.yml")
DEPLOYMENTS = resources("fixtures/deployments.yml")
CLUSTER_OPERATOR = resources("fixtures/clusteroperator.yml")
def test_clock_times_out():
@@ -119,3 +121,10 @@ def test_get_waiter_returns_correct_waiter():
).predicate.func
== custom_condition
)
@pytest.mark.parametrize(
"clusteroperator,expected", zip(CLUSTER_OPERATOR, [True, False, False, False])
)
def test_cluster_operator(clusteroperator, expected):
assert cluster_operator_ready(clusteroperator) is expected