Compare commits

53 Commits
6.4.0 ... main

Author SHA1 Message Date
Youssef Ali
e79ed52a4d Add kubeconfig module for managing Kubernetes config files (#1104)
* Add kubeconfig module for managing Kubernetes config files

* Remove unnecessary requirement & Change version

* Move functions to module_utils

* Add unit tests

* Add kubeconfig module for managing Kubernetes config files

* Remove unnecessary requirement & Change version

* Move functions to module_utils

* Add unit tests

* Avoid linter errors

* Improve documentation clarity

* Redact sensitive kubeconfig information

* Imprvoe verbosity

* Move import statement for to_native to avoid linters check failure

* Fix linting error

---------

Co-authored-by: Bianca Henderson <bianca@redhat.com>
2026-05-06 07:56:22 -04:00
Birger Johan Nordølum
4d7dc2a7d1 docs: add until example to k8s_info (#885)
I would liked to have an example like this when I was using the
documentation.
2026-05-05 15:47:19 -04:00
Bianca Henderson
c2cfa51655 [ACA-5027] Configure SonarQube Cloud (#1116)
* Configure SonarQube Cloud

* Update workflow file and add info to README

* Resolve sanity errors

* Add pinned version details to sonarcloud.yml
2026-05-05 13:09:24 -04:00
Bianca Henderson
fb10b41918 Release prep for 6.4.0 (#1101) (#1115)
Merging changes from stable-6 back into main.

Reviewed-by: Matthew Johnson
Reviewed-by: Hannah DeFazio <h2defazio@gmail.com>
2026-04-24 18:46:37 +00:00
Bianca Henderson
210467b26d Update URL reference in integration-test CI file (#1112) 2026-04-22 11:59:04 -04:00
Yuriy Novostavskiy
11f619b69e ci: conditionally test turbo mode and cloud.common (#1109)
The cloud.common collection is incompatible with ansible-core >= 2.19.0.
With the current testing matrix using Python 3.12 and the ansible
milestone (currently 2.22), this incompatibility causes integration
tests to fail.

Instead of completely removing turbo mode from the testing matrix, this
commit adds ansible-core 2.18 to the matrix and excludes the combination
of the ansible milestone and turbo mode. The checkout and installation
of the cloud.common collection are now conditionally executed only when
turbo mode is enabled.
2026-04-21 14:20:06 -04:00
Yuriy Novostavskiy
16e92a20e8 trivial(doc): post #1090 cosmetic update (#1097)
SUMMARY
Name of the Helm plays in the integration test framework test updated to reflect the actual version of Helm (addressed comments #1090 (review))
Updated documentation for the modules updated in the PR with the https://github.com/ansible-network/collection_prep, as per CONTRIBUTING.md
ISSUE TYPE

Docs Pull Request

COMPONENT NAME

tests/integration/targets/helm_v3_*/play.yaml
docs/kubernetes.core.helm*.rst

ADDITIONAL INFORMATION
Only cosmetic changes in this PR, so the label skip-changelog is suggested

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2026-04-20 21:33:42 +00:00
Yuriy Novostavskiy
58f8f2e6e9 Add sanity test ignores for ansible-core 2.22 (#1102)
The `devel` and `milestone` branches for ansible-core have been bumped to
`2.22.0.dev0` as the `stable-2.21` branch was created. Testing against `devel`
and `milestone` now uses 2.22, which requires creation of the
`tests/sanity/ignore-2.22.txt` file in all maintained collection branches.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch ignore-2.22
# Changes to be committed:
#	new file:   tests/sanity/ignore-2.22.txt
#
2026-04-20 17:00:09 -04:00
Matthew Johnson
52f9a5b54b ACA-2437: added a CI.md file (#1094)
SUMMARY
Added a CI file to outline the testing strategy in more detail
ISSUE TYPE

Docs Pull Request

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Matthew Johnson
2026-03-18 15:42:30 +00:00
Bikouo Aubin
e6076e5568 Ensure compatibility with Helm v4 for the collection (#1090)
SUMMARY

Ensure compatibility with Helm v4 for modules helm_plugin and helm_plugin_info
Partially addresses #1038

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm_plugin
helm_plugin_info
helm_info
helm_pull
helm_registry_auth
helm
helm_template

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Alina Buzachis
2026-03-06 14:50:14 +00:00
Thisora
42acb4f52b Support take_ownership parameter in helm installation (#1034)
* Support take_ownership parameter in helm installation

SUMMARY
Adds support for the take_ownership for initial release installation operations.

ISSUE TYPE
Feature Pull Request

COMPONENT NAME
plugins/modules/helm.py

* Update changelogs/fragments/20251224-take-ownership-helm-initialization.yaml

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>

---------

Co-authored-by: Mathis Raemy <mathis.raemy@swissdotnet.ch>
Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2026-02-19 14:59:23 +01:00
Bikouo Aubin
d239adbbbc Add check_mode support for k8s_drain module (#1086)
SUMMARY

Closes #1037

added support for check_mode
Converted warnings into informational display when user has explicitly requested to delete daemontset-managed pods, unmanaged pods or pods with local storage


ISSUE TYPE


Feature Pull Request

COMPONENT NAME

k8s_drain

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2026-02-16 13:10:38 +00:00
Bianca Henderson
6d942f5e45 Prep 6.3.0 release (#1084) (#1088)
Merging changes from stable-6 back into main.

Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
Reviewed-by: Matthew Johnson
2026-02-04 20:39:45 +00:00
Bianca Henderson
bc0406cf02 Prep 5.4.2 release (#1083) (#1087)
Merging changes from stable-5 back into main.

Reviewed-by: Hannah DeFazio <h2defazio@gmail.com>
Reviewed-by: Bikouo Aubin
2026-02-04 15:23:34 +00:00
Bianca Henderson
23b6cec173 Fix incorrect assertion in helm_pull integration test (#1077)
SUMMARY

The error message emitted for incorrect helm version has changed since the merge of #1039. This PR updates the related assertion in the helm_pull integration test

Reviewed-by: Chyna Sanders
Reviewed-by: Mike Graves <mgraves@redhat.com>
2026-01-29 16:49:49 +00:00
Yuriy Novostavskiy
34beacf32b Add idempotency to helm_pull module (#1055)
SUMMARY
This PR implements idempotency for the helm_pull module, addressing issue #889.

New force parameter with defaults to False.
implemented chart_exists() function
checks chart existence before downloading, returns changed=False when chart exists

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
helm_pull
ADDITIONAL INFORMATION
Force parameter added for backward compatibility and edge cases.
Implemented with the partial support of GitHub Copilot with Claude Sonnet 4.5 model

Reviewed-by: Bikouo Aubin
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Alina Buzachis
2026-01-29 14:03:56 +00:00
Yuriy Novostavskiy
3e32c12c40 Replace passing `warnings to exit_json with AnsibleModule.warn` for the few modules (#1033)
SUMMARY
Using exit_json or fail_json for warnings is deprecated in ansible-core>=2.19.0 and will be removed in ansible-core>=2.23.0
Tested with ansible-core 2.19.3 as the latest released version at the time of the start of this PR and with 2.16.0 as the lowest version supported by kubernetes.core 6.x
Resolves: #1031
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
k8s_drain
k8s_rollback
k8s_scale
ADDITIONAL INFORMATION
The initial version of this PR covers only the module k8s_drain, with the following commits extended to k8s_rollback
k8s_scale

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis
2026-01-26 19:52:15 +00:00
Yuriy Novostavskiy
13791ec7bf Limit compatibility to Helm =>v3.0.0,<4.0.0 (#1039)
SUMMARY
Helm v4 is a major version with backward-incompatible changes, including to the flags and output of the Helm CLI and to the SDK. This version is currently not supported in the kubernetes.core. This PR is related to #1038 and is a short-term solution to mark compatibility explicitly
ISSUE TYPE

Bugfix Pull Request
Docs Pull Request

COMPONENT NAME

helm
helm_template
helm_info
helm_repository
helm_pull
helm_registry_auth
helm_plugin
helm_plugin_info

ADDITIONAL INFORMATION
Added `validate_helm_version()`` method to AnsibleHelmModule that enforces version constraint >=3.0.0,<4.0.0.
Fails fast with clear error message: "Helm version must be >=3.0.0,<4.0.0, current version is {version}"
Some modules (i.e. helm_registry_auth) technically is compatible with Helm v4, but validation was added to all helm modules.
Partially coauthored by GitHub Copilot with Claude Sonnet 4 model.
Addresses issue #1038

Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2026-01-26 18:39:07 +00:00
Yuriy Novostavskiy
452fb3d7cb Replace deprecated ansible.module_utils._text imports (#1053)
SUMMARY
Importing from ansible.module_utils._text is deprecated in ansible-core 2.20 and removed in 2.24. All imports of to_bytes, to_native, and to_text now use ansible.module_utils.common.text.converters.
Before:
from ansible.module_utils._text import to_bytes, to_native, to_text

After:
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
plugins/module_utils/common.py
plugins/action/k8s_info.py
plugins/connection/kubectl.py
plugins/module_utils/{copy.py, k8s/runner.py}
plugins/modules/{k8s_cp.py, k8s_drain.py, k8s_exec.py, k8s_json_patch.py, k8s_scale.py, k8s_taint.py}
ADDITIONAL INFORMATION
It's not an actual Bugfix, more a lifecycle management to ensure compatibility with future Ansible versions.
Tested with ansible-core 2.20 to ensure no deprecation warnings are raised and with ansible-core 2.16 to ensure backward compatibility.
Patrially coauthored-by: GitHub Copilot with Claude Code 4.5 model.
Addresses issue #1052.

Reviewed-by: Bikouo Aubin
Reviewed-by: Alina Buzachis
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2026-01-24 01:28:15 +00:00
Yuriy Novostavskiy
12abc9bda9 Fix K8S_AUTH_VERIFY_SSL environment value handling in kubectl connection plugin (#1049)
SUMMARY
Fixed a bug where setting K8S_AUTH_VERIFY_SSL=true (or any string value) caused the value to be treated as a separate kubectl command argument instead of being properly converted to a boolean.
The option key name is validate_certs, which does NOT end with "verify_ssl", so the original condition key.endswith("verify_ssl") at line 327 failed. This caused the code to fall through to the else block which added the value as separate
arguments: ["--insecure-skip-tls-verify", "true"], making "true" appear as a kubectl command.
Fixes #1021
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
kubernetes.core.kubectl
ADDITIONAL INFORMATION
Changes Made

Changed condition from key.endswith("verify_ssl") to key == "validate_certs"
Added import of boolean function from ansible.module_utils.parsing.convert_bool
Added proper boolean conversion using boolean(self.get_option(key), strict=False)

Partially used LLM (GitHub Copilot with Claude Sonnet 4).
Before Fix
K8S_AUTH_VERIFY_SSL=true
Command: ['/usr/bin/kubectl', '--insecure-skip-tls-verify', 'true', 'exec', ...]

                                                            ^^^^^ treated as kubectl command (BUG!)

After Fix
K8S_AUTH_VERIFY_SSL=true
Command: ['/usr/bin/kubectl', '--insecure-skip-tls-verify=false', 'exec', ...]
                                                           ^^^^^ properly converted (FIXED!)

Reviewed-by: Bikouo Aubin
Reviewed-by: Alina Buzachis
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2026-01-23 15:30:28 +00:00
Yuriy Novostavskiy
987c029c87 chore(CI): prevent patchback bot from labeling new PRs (#1062)
currently, the patchback bot creates PRs that get labeled
as "needs-triage". This change prevents labeling PR created by
the patchback bot.
2026-01-20 17:22:52 -06:00
Yuriy Novostavskiy
6c00f7c7de chore(doc): update typos (#1059)
SUMMARY
This trivial documentation-only pull request correcting a few errors in README.md.

Documentation corrections:

Fixed a broken Markdown link for the GitHub repository in the support request section.
Corrected the reference from LICENCE to LICENSE to match the actual file name.



ISSUE TYPE

Docs Pull Request

COMPONENT NAME
README.md
ADDITIONAL INFORMATION
Trivial documentation-only change, no changelog is required.

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Alina Buzachis
2026-01-15 19:07:26 +00:00
Bikouo Aubin
bd1cacc0cf address sanity issues (#1056)
SUMMARY


helm/helm_info - Deprecate some parameters and add new ones to resolve sanity issues.
k8s - the return block doc is not aligned with what the module returns


ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

helm, helm_info, k8s
Fixes: #1046

Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2026-01-14 08:22:31 +00:00
Hannah DeFazio
9cfa9038fc Add new workflow to label prs with needs_triage (#1045) 2025-12-18 10:21:05 +01:00
Bianca Henderson
1c16a2d2b5 Add 2.21 ignore file for sanity tests (#1032)
SUMMARY

Resolves #1027

Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
2025-10-23 19:18:13 +00:00
Jan-Philipp Litza
798f5493f9 Extend k8s action group (#992)
SUMMARY


Add all k8s_* modules to the action group in order to esaily set kubeconfig parameter
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

meta
ADDITIONAL INFORMATION

Reviewed-by: Bikouo Aubin
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-10-13 17:57:04 +00:00
Bianca Henderson
4fa36487ab Selectively redact sensitive kubeconfig data from logs (#1014)
SUMMARY

Resolves #782

ISSUE TYPE


Bugfix Pull Request

ADDITIONAL INFORMATION


The proper redaction of kubeconfig data can be seen by running this example playbook with verbosity of -vvv against the code in this PR.
Prior to these changes, all info was redacted (as shown in the example below):
ok: [local] => {
    "changed": false,
    "invocation": {
        "module_args": {
            "api_key": null,
            "binary_path": null,
            "ca_cert": null,
            "context": null,
            "get_all_values": false,
            "host": null,
            "kubeconfig": {
                "apiVersion": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                "clusters": [
                    {
                        "cluster": {
                            "insecure-skip-tls-verify": true,
                            "server": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                        },
                        "name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                    },
                    {
                        "cluster": {
                            "certificate-authority-data": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                            "server": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                        },
                        "name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                    },
                    {
                        "cluster": {
                            "certificate-authority": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                            "extensions": [
                                {
                                    "extension": {
                                        "last-update": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                                        "provider": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                                        "version": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                                    },
                                    "name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                                }
                            ],
                            "server": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                        },
                        "name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                    }
                ],
                "contexts": [
                    {
                        "context": {
                            "cluster": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                            "user": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                        },
                        "name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                    },
                    {
                        "context": {
                            "cluster": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                            "user": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                        },
                        "name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
                    },
[output shortened]

With the changes in this PR, only sensitive data is redacted:
ok: [local] => {
    "changed": false,
    "invocation": {
        "module_args": {
            "api_key": null,
            "binary_path": null,
            "ca_cert": null,
            "context": null,
            "get_all_values": false,
            "host": null,
            "kubeconfig": {
                "apiVersion": "v1",
                "clusters": [
                    {
                        "cluster": {
                            "insecure-skip-tls-verify": true,
                            "server": "<server address>"
                        },
                        "name": "exercise"
                    },
                    {
                        "cluster": {
                            "certificate-authority-data": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                            "server": "<server address>"
                        },
                        "name": "kind-drain-test"
                    },
                    {
                        "cluster": {
                            "certificate-authority": "<path to .crt>",
                            "extensions": [
                                {
                                    "extension": {
                                        "last-update": "Tue, 07 Oct 2025 11:25:54 EDT",
                                        "provider": "minikube.sigs.k8s.io",
                                        "version": "v1.35.0"
                                    },
                                    "name": "cluster_info"
                                }
                            ],
                            "server": "<server address>"
                        },
                        "name": "minikube"
                    }
                ],
                "contexts": [
                    {
                        "context": {
                            "cluster": "exercise-pod",
                            "user": "bianca"
                        },
                        "name": "exercise"
                    },
                    {
                        "context": {
                            "cluster": "kind-drain-test",
                            "user": "kind-drain-test"
                        },
                        "name": "kind-drain-test"
                    },
[output shortened]

Reviewed-by: Bikouo Aubin
Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Alina Buzachis
2025-10-13 15:01:22 +00:00
Bianca Henderson
34467d42b3 Update main after 6.2.0 release (#1016)
Reviewed-by: Bikouo Aubin
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
Reviewed-by: Alina Buzachis
2025-10-10 15:05:46 +00:00
Pablo Fontanilla
ae624cfa44 775 document proxy configurations (#1018)
SUMMARY
Added documentation for no_proxy, proxy, and proxy_headers parameters that were missing from the k8s lookup plugin. These parameters are already implemented in the codebase but were not documented.
no_proxy: Comma separated list of hosts that shouldn't use proxy
proxy: HTTP proxy URL for connections
proxy_headers: Dictionary of proxy headers with suboptions for proxy_basic_auth, basic_auth, and user_agent
Fixes #775
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
k8s lookup
Additional comment
This is a reissue of #993, which was lost during the latest release due to me PR incorrectly from my fork's main branch. Linter errors on the previous PR should already be resolved.
This had the backport-5, backport-6 and skip-changelog labels.

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-10-10 14:30:12 +00:00
Bianca Henderson
05e5e0a5ac prepare release 5.4.1 (#1009) (#1013)
SUMMARY
Update main branch after kubernetes.core 5.4.0 release

Reviewed-by: Mandar Kulkarni <mandar242@gmail.com>
2025-10-07 17:32:21 +00:00
Yuriy Novostavskiy
87344b93fc Add support of local environment variables in kustomize lookup plugin (#786)
SUMMARY
kustomize doesn't support an environment that makes it impossible to use HTTP_PROXY or provide some templatized parameters.
This PR is the result of the issue #783
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
kubernetes.core.kustomize lookup plugin

Reviewed-by: Bikouo Aubin
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-10-03 13:44:40 +00:00
Yuriy Novostavskiy
200d64f5ea update doc following #971 (#1006)
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 19:14:18 +00:00
Yorick Gruijthuijzen
ec35c74e2e Add the PR head as reference to the checkout action of the splitter job. (#981)
* Add the pull request its head as reference to the repo-checkout of the splitter job.

* Delete changelogs/fragments/20250808-bugfix-workflow-splitter-ref.yaml

Remove changelog file as this is a CI-only change

---------

Co-authored-by: Bianca Henderson <bianca@redhat.com>
2025-10-01 15:41:34 -04:00
Yorick Gruijthuijzen
027700c3f4 Added support for copying files to init Containers. (#971)
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: spatterlight
Reviewed-by: Yorick Gruijthuijzen <yorick-1989@hotmail.com>
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Bikouo Aubin
2025-09-24 20:36:56 +00:00
Yuriy Novostavskiy
da93cce1fa Add support for skip-schema-validation in helm module (#995)
SUMMARY
This pull request adds support for a new skip_schema_validation option to the helm module, allowing users to disable JSON schema validation for Helm charts and values (requires helm >= 3.16.0).
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
helm
ADDITIONAL INFORMATION
Added the skip_schema_validation boolean parameter to the helm module, allowing users to disable JSON schema validation for charts and values. This option is only available with Helm versions >= 3.16.0, and an appropriate error is raised for older versions.
Added integration tests to verify the behavior of the skip_schema_validation option, including cases for both supported and unsupported Helm versions.
Closes #994

Reviewed-by: Bikouo Aubin
2025-09-24 15:47:46 +00:00
Bianca Henderson
448d3fe156 [CI Fix] Remove ansible.module_utils.six imports (#998)
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: Alina Buzachis
Reviewed-by: Yuriy Novostavskiy <yuriy@novostavskiy.kyiv.ua>
2025-09-22 16:08:18 +00:00
Alina Buzachis
6158300062 Changelog fixes (#989)
SUMMARY

Changelog fixes

ISSUE TYPE


Docs Pull Request

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-08-14 13:50:55 +00:00
Alina Buzachis
3160e4aad6 Update main branch docs after release 6.1.0 (#988)
SUMMARY

Update main branch docs after release 6.1.0

ISSUE TYPE


Docs Pull Request

Reviewed-by: Bikouo Aubin
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Alina Buzachis
2025-08-13 15:47:37 +00:00
Bianca Henderson
93734fcefc Prep kubernetes.core 5.4.0 release (#970) (#986)
SUMMARY
Update main branch after kubernetes.core 5.4.0 release

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Yuriy Novostavskiy
Reviewed-by: Bikouo Aubin
Reviewed-by: Alina Buzachis
2025-08-13 08:36:33 +00:00
Yuriy Novostavskiy
a861079dc1 CI fix for 976 (#982)
* exclude plugins/connection/kubectl.py from ansible-lint

documentation inside a python file have simplification

resolves #976

* Update to use .ansible-lint and ansible-lint@v25.5.0

Signed-off-by: Alina Buzachis <abuzachis@redhat.com>

* Update .github/workflows/linters.yaml

Signed-off-by: Alina Buzachis <abuzachis@redhat.com>

---------

Signed-off-by: Alina Buzachis <abuzachis@redhat.com>
Co-authored-by: Alina Buzachis <abuzachis@redhat.com>
2025-08-12 17:30:11 +02:00
Bianca Henderson
5148ee5f74 Reapply "Remove kubeconfig value from module invocation log (#826)" (#899) (#978)
This reverts commit 1705ced (i.e., reapplies the changes from #826); this is a temporary fix for #782 as it will re-introduce #870, which will need to be re-opened.

Reviewed-by: Alina Buzachis
Reviewed-by: GomathiselviS <gomathiselvi@gmail.com>
2025-08-11 16:46:40 +00:00
Frank Villaro-Dixon
c48778d709 k8s_json_patch: support the hidden_fields param (#964)
SUMMARY
Add support for hidden_fields on k8s_json_patch

ISSUE TYPE

Feature Pull Request

COMPONENT NAME
k8s_json_patch
ADDITIONAL INFORMATION
Works exactly the same as k8s
Haven't pushed the doc yet, because of many changes. Will do it on a separate commit if the tests pass.
1st commit here, sorry if I forget some things.
Thanks!

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Alina Buzachis
Reviewed-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
2025-07-31 14:21:40 +00:00
Rémy Jacquin
cf3c3a9dcc Add support for take-ownership Helm flag (#957)
SUMMARY
Add support for take-ownership Helm flag added in Helm 3.17.0
ISSUE TYPE

Feature Pull Request

COMPONENT NAME

kubernetes.core.helm

Reviewed-by: Yuriy Novostavskiy
Reviewed-by: Rémy Jacquin
Reviewed-by: Bikouo Aubin
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-07-28 15:18:46 +00:00
James Mighion
1705ced1b5 Revert "Remove kubeconfig value from module invocation log (#826)" (#899)
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 16:49:34 +00:00
Felix Matouschek
6a0635a2bb fix(k8s,service): Hide fields first before creating diffs (#915)
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: Bikouo Aubin
Reviewed-by: Mike Graves <mgraves@redhat.com>
2025-07-15 16:10:26 +00:00
Yuriy Novostavskiy
f568c9da62 Fix integration test with ansibe-core 2.20 (#951)
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>
Reviewed-by: Alina Buzachis
2025-07-15 13:53:51 +00:00
Bianca Henderson
94e42354cd Add more functionality coverage to k8s_rollback integration test (#950)
SUMMARY

Resolves #344

This revision adds the following test coverage:

Label Selectors: Tests rollback using label selectors to target specific deployments.
No Rollout History: Tests the warning scenario when attempting to rollback a deployment with only one revision.
Unsupported Resource Types: Tests error handling when trying to rollback unsupported resources like Services.
Non-existent Resources: Tests behavior when attempting to rollback resources that don't exist.
Multiple Resource Rollback: Tests bulk rollback operations using label selectors on multiple deployments.
Return Value Validation: Comprehensive validation of the rollback_info structure and content.
Field Selectors: Tests rollback using field selectors to target specific resources.
Check Mode Validation: Additional validation of check mode behavior and return values.

COMPONENT NAME

tests/integration/targets/k8s_rollback/tasks/main.yml

Reviewed-by: Alina Buzachis
Reviewed-by: Bikouo Aubin
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-07-08 15:28:41 +00:00
Yuriy Novostavskiy
642eb936c0 Fix the integration test for helm_registry_auth with helm >= 3.18.0 and clarify idempotency. (#946)
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>
Reviewed-by: Alina Buzachis
Reviewed-by: Yuriy Novostavskiy
Reviewed-by: Bikouo Aubin
2025-07-08 09:50:08 +00:00
Matteo Danelon
775959c3f9 Add plain_http parameter to helm, helm_pull and helm_template (#934)
SUMMARY

This change introduces the plain_http parameter to modules that can interact with OCI registries. This in needed in cases where the OCI registry does not use SSL encryption, forcing Helm to send HTTP requests instead of HTTPS

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm, helm_pull and helm_template
ADDITIONAL INFORMATION


This is the output when trying to use an OCI registry that is not configured to use SSL certs.

fatal: [localhost]: FAILED! => {"changed": false, "command": "/usr/local/bin/helm show chart 'oci://<http-registry>/charts/foo'", "msg": "Failure when executing Helm command. Exited 1.\nstdout: \nstderr: Error: Get \"https://<http-registry>/v2/charts/foo/tags/list\": http: server gave HTTP response to HTTPS client\n", "stderr": "Error: Get \"https://<http-registry>/v2/charts/foo/tags/list\": http: server gave HTTP response to HTTPS client\n", "stderr_lines": ["Error: Get \"https://<http-registry>/v2/charts/foo/tags/list\": http: server gave HTTP response to HTTPS client"], "stdout": "", "stdout_lines": []}

Reviewed-by: Bikouo Aubin
Reviewed-by: Matteo Danelon
2025-06-12 10:39:40 +00:00
Chyna Sanders
77627bb8d9 Copied automation script for tagging new issues from the main AWS collection (#936) 2025-06-10 13:33:52 -04:00
Bianca Henderson
b1fbd38352 Prep release 6.0.0 (#933) (#945)
Updating main branch after 6.0.0 release.

Reviewed-by: Bikouo Aubin
Reviewed-by: Yuriy Novostavskiy
2025-06-05 14:44:46 +00:00
Bianca Henderson
a06b2c3969 Update dev version after 6.0.0 major release (#941)
SUMMARY
Updating the dev version listed in Makefile and galaxy.yml since 6.0.0 has been released.

Reviewed-by: Mandar Kulkarni <mandar242@gmail.com>
2025-06-04 20:01:59 +00:00
Bikouo Aubin
34fd40d46a Fix unit tests (#939)
Some unit tests are broken with ansible-core 2.19, this PR aims to fix them.

Reviewed-by: Bianca Henderson <beeankha@gmail.com>
2025-06-03 14:26:26 +00:00
17 changed files with 1156 additions and 2 deletions

18
.github/workflows/label-new-issues.yaml vendored Normal file
View File

@@ -0,0 +1,18 @@
---
name: label new issues
on:
issues:
types:
- opened
- reopened
jobs:
add_label:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
steps:
- uses: actions-ecosystem/action-add-labels@v1
with:
labels: needs_triage

70
.github/workflows/sonarcloud.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
---
# SonarCloud analysis for kubernetes.core
#
# Uses the same-repo + default-branch push model: GitHub does not expose org secrets to workflows
# from fork PRs (see https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions).
# This job is gated so the Sonar token is never available in untrusted fork contexts. A follow-up
# workflow triggered by workflow_run + artifacts is an alternative if the org later requires Sonar
# with coverage on fork PRs (see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run).
name: SonarCloud
on:
push:
branches:
- main
- stable-*
pull_request:
branches:
- main
- stable-*
workflow_dispatch:
permissions:
contents: read
pull-requests: read
jobs:
sonarqube:
name: SonarCloud Scan
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
env:
# Pin ansible-test behavior; bump when raising supported ansible-core (see meta/runtime.yml).
ANSIBLE_CORE_VERSION: "2.19.5"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Ansible (ansible-test)
run: |
pip install --upgrade pip
pip install "ansible-core==${ANSIBLE_CORE_VERSION}"
- name: Unit tests with coverage
run: ansible-test units --venv --coverage --python 3.12 --requirements
- name: Coverage combine and XML for Sonar
run: |
ansible-test coverage combine --venv --python 3.12 --requirements
ansible-test coverage xml --venv --python 3.12 --requirements
- name: Copy coverage report to repo root
run: |
set -euo pipefail
ls -la tests/output/reports/
xml=$(find tests/output/reports -maxdepth 1 -name '*.xml' ! -name '*powershell*' | head -1)
test -n "$xml"
cp "$xml" coverage.xml
- name: SonarCloud Scan
# Same pinned version as ansible-collections/amazon.aws sonarcloud.yml
uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9
env:
SONAR_TOKEN: ${{ secrets.ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT }}

3
.gitignore vendored
View File

@@ -25,3 +25,6 @@ tests/integration/*-*.yml
# VS Code settings
.vscode/
# Root coverage report for SonarCloud (generated locally or in CI)
/coverage.xml

View File

@@ -112,12 +112,52 @@ Bugfixes
v6.0.0
======
Release Summary
---------------
This major release removes the deprecated ``k8s`` inventory plugin and also removes ``ansible-core<2.16`` support.
Breaking Changes / Porting Guide
--------------------------------
- Remove deprecated ``k8s`` invetory plugin (https://github.com/ansible-collections/kubernetes.core/pull/867).
- Remove support for ``ansible-core<2.16`` (https://github.com/ansible-collections/kubernetes.core/pull/867).
v5.4.2
======
Release Summary
---------------
This release includes bugfixes such as replacing the passing of ``warnings`` to ``exit_json`` with ``AnsibleModule.warn`` as well as a security update for selectively redacting sensitive information from kubeconfig.
Minor Changes
-------------
- helm - add ``release_values`` key to ``status`` return value that can be accessed using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
- helm_info - add ``release_values`` key to ``status`` return value that can be accessed using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
Deprecated Features
-------------------
- helm - the ``status.values`` return value has been deprecated and will be removed in a release after 2027-01-08. Use ``status.release_values`` instead (https://github.com/ansible-collections/kubernetes.core/pull/1056).
- helm_info - the ``status.values`` return value has been deprecated and will be removed in a release after 2027-01-08. Use ``status.release_values`` instead (https://github.com/ansible-collections/kubernetes.core/pull/1056).
Security Fixes
--------------
- Selectively redact sensitive info from kubeconfig instead of applying blanket ``no_log=True`` (https://github.com/ansible-collections/kubernetes.core/pull/1014).
Bugfixes
--------
- Add idempotency for ``helm_pull`` module (https://github.com/ansible-collections/kubernetes.core/pull/1055).
- Fixed a bug where setting ``K8S_AUTH_VERIFY_SSL=true`` (or any string value) caused the value to be treated as a separate ``kubectl`` command argument (https://github.com/ansible-collections/kubernetes.core/pull/1049).
- Limit supported versions of Helm to <4.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/1039).
- Replace passing ``warnings`` to ``exit_json`` with ``AnsibleModule.warn`` in the ``k8s_drain``, ``k8s_rollback.py`` and ``k8s_scale.py`` modules as it deprecated in ``ansible-core>=2.19.0`` and will be removed in ``ansible-core>=2.23.0`` (https://github.com/ansible-collections/kubernetes.core/pull/1033).
- k8s - Fix return block from the module documentation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
- meta - Add ``k8s_cluster_info``, ``k8s_json_patch`` and ``k8s_rollback`` to k8s action group (https://github.com/ansible-collections/kubernetes.core/pull/992).
v5.4.1
======

32
CI.md Normal file
View File

@@ -0,0 +1,32 @@
# Continuous Integration (CI)
## Kubernetes Upstream Testing
GitHub Actions are used to run the CI for the kubernetes.core collection. The workflows used for the CI can be found in the [.github/workflows](.github/workflows) directory.
### PR Testing Workflows
The following tests run on every pull request:
| Job | Description | Python Versions | ansible-core Versions |
| --- | ----------- | --------------- | --------------------- |
| [Changelog](.github/workflows/changelog.yaml) | Checks for the presence of changelog fragments | 3.12 | devel |
| [Linters](.github/workflows/linters.yaml) | Runs `black`, `flake8`, `isort`, `yamllint`, and `ansible-lint` on plugins and tests | 3.10 | devel |
| [Sanity](.github/workflows/sanity-tests.yaml) | Runs ansible sanity checks | See compatibility table below | devel, stable-2.18, stable-2.19, stable-2.20 |
| [Unit tests](.github/workflows/unit-tests.yaml) | Executes unit test cases | See compatibility table below | devel, stable-2.16, stable-2.17, stable-2.18, stable-2.19, stable-2.20 |
| [Integration](.github/workflows/integration-tests.yaml) | Executes integration test suites using KinD cluster (split across 8 jobs, tests with Turbo mode enabled/disabled) | 3.12 | milestone |
**Note:** Integration tests require a KinD (Kubernetes in Docker) cluster and test both with Turbo mode enabled and disabled.
### Python Version Compatibility by ansible-core Version
These are outlined in the collection's [tox.ini](tox.ini) file (`envlist`) and GitHub Actions workflow exclusions.
| ansible-core Version | Sanity Tests | Unit Tests |
| -------------------- | ------------ | ---------- |
| devel | 3.12, 3.13, 3.14 | 3.12, 3.13 |
| stable-2.20 | 3.12, 3.13, 3.14 | 3.12, 3.13, 3.14 |
| stable-2.19 | 3.11, 3.12, 3.13 | 3.11, 3.12, 3.13 |
| stable-2.18 | 3.11, 3.12, 3.13 | 3.11, 3.12, 3.13 |
| stable-2.17 | 3.10, 3.11, 3.12 | 3.10, 3.11, 3.12 |
| stable-2.16 | 3.10, 3.11 | 3.10, 3.11 |

View File

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

View File

@@ -6,6 +6,24 @@ This repository hosts the `kubernetes.core` (formerly known as `community.kubern
The collection includes a variety of Ansible content to help automate the management of applications in Kubernetes and OpenShift clusters, as well as the provisioning and maintenance of clusters themselves.
## SonarCloud (code quality)
Static analysis runs on [SonarCloud](https://sonarcloud.io) using `sonar-project.properties` and
`.github/workflows/sonarcloud.yml`. Coverage shown in Sonar comes from unit-test coverage exported as
`coverage.xml` at the repository root during CI.
The SonarCloud project key must match `sonar.projectKey` (`ansible-collections_kubernetes.core`). Adding
or renaming the project is coordinated via Ansible Collections maintainers.
GitHub does not expose organization secrets to workflows for pull requests opened from forks. The
Sonar job therefore only runs on pushes to this repository's branches and on pull requests where the
head branch is on `ansible-collections/kubernetes.core` (not from forks). That matches GitHub's
documented behavior for [secrets in Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions).
If the project later needs Sonar with coverage on **fork** PRs, maintainers typically add a separate
trusted job after a workflow that uploads coverage artifacts, using GitHub's `workflow_run` event.
See [workflow_run (GitHub Docs)](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run).
## Communication
* Join the Ansible forum:

View File

@@ -1073,6 +1073,49 @@ releases:
- 20250922-remove-ansible-six-imports.yaml
- 5.4.1.yml
release_date: '2025-10-07'
5.4.2:
changes:
bugfixes:
- Add idempotency for ``helm_pull`` module (https://github.com/ansible-collections/kubernetes.core/pull/1055).
- Fixed a bug where setting ``K8S_AUTH_VERIFY_SSL=true`` (or any string value)
caused the value to be treated as a separate ``kubectl`` command argument
(https://github.com/ansible-collections/kubernetes.core/pull/1049).
- Limit supported versions of Helm to <4.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/1039).
- Replace passing ``warnings`` to ``exit_json`` with ``AnsibleModule.warn``
in the ``k8s_drain``, ``k8s_rollback.py`` and ``k8s_scale.py`` modules as
it deprecated in ``ansible-core>=2.19.0`` and will be removed in ``ansible-core>=2.23.0``
(https://github.com/ansible-collections/kubernetes.core/pull/1033).
- k8s - Fix return block from the module documentation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
- meta - Add ``k8s_cluster_info``, ``k8s_json_patch`` and ``k8s_rollback`` to
k8s action group (https://github.com/ansible-collections/kubernetes.core/pull/992).
deprecated_features:
- helm - the ``status.values`` return value has been deprecated and will be
removed in a release after 2027-01-08. Use ``status.release_values`` instead
(https://github.com/ansible-collections/kubernetes.core/pull/1056).
- helm_info - the ``status.values`` return value has been deprecated and will
be removed in a release after 2027-01-08. Use ``status.release_values`` instead
(https://github.com/ansible-collections/kubernetes.core/pull/1056).
minor_changes:
- helm - added ``release_values`` key to ``status`` return value that can be
accessed using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
- helm_info - added ``release_values`` key to ``status`` return value that can
be accessed using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
release_summary: This release includes various bugfixes such as replacing the
passing of ``warnings`` to ``exit_json`` with ``AnsibleModule.warn`` as well
as security updates for selectively redacting sensitive information from kubeconfig.
security_fixes:
- Selectively redact sensitive info from kubeconfig instead of applying blanket
``no_log=True`` (https://github.com/ansible-collections/kubernetes.core/pull/1014).
fragments:
- 1033-warnings-deprecations.yaml
- 20251002-fix-k8s-actiongroup.yaml
- 20251007-selective-kubeconfig-redaction.yaml
- 20251115-limit-versions-of-helm.yaml
- 20251220-fix-K8S_AUTH_VERIFY_SSL-in-kubectl-connecton-plugion.yaml
- 20260107-add-idempodency-for-helm-pull.yaml
- 20260108-fix-sanity-failures.yml
- 5-4-2.yaml
release_date: '2026-02-03'
6.0.0:
changes:
breaking_changes:

View File

@@ -701,6 +701,21 @@ Examples
wait_sleep: 10
wait_timeout: 360
- name: Wait for OpenShift bootstrap to complete
kubernetes.core.k8s_info:
api_version: v1
kind: ConfigMap
name: bootstrap
namespace: kube-system
register: ocp_bootstrap_status
until: >
ocp_bootstrap_status.resources is defined and
(ocp_bootstrap_status.resources | length > 0) and
(ocp_bootstrap_status.resources[0].data.status is defined) and
(ocp_bootstrap_status.resources[0].data.status == 'complete')
retries: 60
delay: 15
Return Values

View File

@@ -25,7 +25,7 @@ tags:
- openshift
- okd
- cluster
version: 6.4.0
version: 7.0.0-dev0
build_ignore:
- .DS_Store
- "*.tar.gz"

View File

@@ -0,0 +1,91 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import hashlib
import os
import traceback
try:
import yaml
IMP_YAML = True
IMP_YAML_ERR = None
except ImportError:
IMP_YAML = False
IMP_YAML_ERR = traceback.format_exc()
def load_yaml_file(path):
if not path or not os.path.exists(path):
return {}
with open(path, "r") as f:
return yaml.safe_load(f) or {}
def deep_merge(base, updates):
result = base.copy()
for key, value in updates.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge(result[key], value)
else:
result[key] = value
return result
def merge_by_name(existing, new):
merged = {}
for item in existing:
if isinstance(item, dict) and "name" in item:
merged[item["name"]] = item
for item in new:
if not isinstance(item, dict) or "name" not in item:
continue
name = item["name"]
behavior = item.get("behavior", "merge")
item_copy = {k: v for k, v in item.items() if k != "behavior"}
if name in merged:
if behavior == "keep":
continue
elif behavior == "replace":
merged[name] = item_copy
else:
result = {"name": name}
for key in ["cluster", "user", "context"]:
if key in merged[name] or key in item_copy:
existing_config = merged[name].get(key, {})
new_config = item_copy.get(key, {})
result[key] = deep_merge(existing_config, new_config)
for key in merged[name]:
if key not in ["name", "cluster", "user", "context"]:
result[key] = merged[name][key]
for key in item_copy:
if (
key not in ["name", "cluster", "user", "context"]
and key not in result
):
result[key] = item_copy[key]
merged[name] = result
else:
merged[name] = item_copy
return list(merged.values())
def hash_data(data):
"""Generate SHA-256 hash for idempotency checking."""
return hashlib.sha256(yaml.safe_dump(data, sort_keys=True).encode()).hexdigest()
def write_file(dest, data):
if not dest:
return False
with open(dest, "w") as f:
yaml.safe_dump(data, f, sort_keys=False)
return True

View File

@@ -120,6 +120,21 @@ EXAMPLES = r"""
namespace: default
wait_sleep: 10
wait_timeout: 360
- name: Wait for OpenShift bootstrap to complete
kubernetes.core.k8s_info:
api_version: v1
kind: ConfigMap
name: bootstrap
namespace: kube-system
register: ocp_bootstrap_status
until: >
ocp_bootstrap_status.resources is defined and
(ocp_bootstrap_status.resources | length > 0) and
(ocp_bootstrap_status.resources[0].data.status is defined) and
(ocp_bootstrap_status.resources[0].data.status == 'complete')
retries: 60
delay: 15
"""
RETURN = r"""

View File

@@ -0,0 +1,441 @@
#!/usr/bin/python
#
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = r"""
---
module: kubeconfig
short_description: Generate, update, and optionally write Kubernetes kubeconfig files
version_added: "6.5.0"
author: "Youssef Khalid Ali (@YoussefKhalidAli)"
description:
- Build, update, and manage Kubernetes kubeconfig files using structured input.
- Supports loading an existing kubeconfig file and merging clusters, users, and contexts.
- Can optionally write the resulting kubeconfig to a destination path.
- Ensures idempotent behavior by only updating files when changes occur.
requirements:
- "PyYAML >= 5.1"
notes:
- Input data is merged by resource name (cluster, user, context).
- Updates under O(clusters), O(users), and O(contexts) are matched by C(name) against the kubeconfig loaded from O(path).
- For an existing C(name), each entry's C(behavior) suboption controls the update.
- The default is V(merge), which merges nested C(cluster), C(user), and C(context) data so unspecified keys are preserved.
- With V(replace), the previous entry for that name is dropped and only the new definition is used.
- With V(keep), the existing entry is left unchanged.
- This can be used to move kubeconfig files to a different location with different content.
- This module does not validate cluster connectivity or authentication.
- The module supports C(check_mode) and will not write files when enabled.
- The structure follows standard Kubernetes kubeconfig format as defined in the Kubernetes documentation.
- Tokens and sensitive data should be protected using ansible-vault or environment variables.
options:
path:
description:
- Path to an existing kubeconfig file to load and merge from.
- If the file does not exist, a new kubeconfig will be created.
- This becomes the default destination if O(dest) is not specified.
type: str
required: true
dest:
description:
- Destination path where the final kubeconfig should be written.
- If not specified, the kubeconfig will be saved to O(path).
- Allows copying and modifying a kubeconfig to a new location.
type: str
required: false
clusters:
description:
- List of cluster definitions to merge into the kubeconfig.
- Each cluster is identified by its C(name).
- When C(name) matches an existing cluster, the default C(behavior) is V(merge).
- See the C(behavior) suboption for V(replace) and V(keep).
type: list
elements: dict
required: false
default: []
suboptions:
name:
description:
- Unique name identifier for the cluster.
type: str
required: true
behavior:
description:
- How to handle merging if a cluster with this name already exists.
- C(merge) - Update only the specified fields, preserve others (default).
- C(replace) - Replace the entire cluster definition.
- C(keep) - Keep existing cluster, skip this entry.
type: str
choices: ['merge', 'replace', 'keep']
default: merge
cluster:
description:
- Cluster configuration details.
type: dict
required: true
suboptions:
server:
description:
- Kubernetes API server URL (e.g., C(https://k8s.example.com:6443)).
type: str
required: true
certificate-authority:
description:
- Path to a CA certificate file for validating the API server certificate.
type: str
certificate-authority-data:
description:
- Base64 encoded CA certificate data.
- Use this instead of C(certificate-authority) for embedded certificates.
type: str
insecure-skip-tls-verify:
description:
- If true, the server's certificate will not be validated.
type: bool
proxy-url:
description:
- Optional proxy URL for cluster connections.
type: str
tls-server-name:
description:
- Server name to use for server certificate validation.
type: str
users:
description:
- List of user authentication configurations.
- Each user is identified by its C(name).
- When C(name) matches an existing user, the default C(behavior) is V(merge).
- See the C(behavior) suboption for V(replace) and V(keep).
type: list
elements: dict
required: false
default: []
suboptions:
name:
description:
- Unique name identifier for the user.
type: str
required: true
behavior:
description:
- How to handle merging if a user with this name already exists.
- C(merge) - Update only the specified fields, preserve others (default).
- C(replace) - Replace the entire user definition.
- C(keep) - Keep existing user, skip this entry.
type: str
choices: ['merge', 'replace', 'keep']
default: merge
user:
description:
- User authentication configuration.
type: dict
required: true
suboptions:
token:
description:
- Bearer token for authentication.
type: str
username:
description:
- Username for basic authentication.
type: str
password:
description:
- Password for basic authentication.
type: str
client-certificate:
description:
- Path to client certificate file.
- Used for certificate-based authentication.
type: str
client-key:
description:
- Path to client private key file.
- Must be provided with C(client-certificate).
type: str
client-certificate-data:
description:
- Base64 encoded client certificate.
- Use instead of C(client-certificate) for embedded certificates.
type: str
client-key-data:
description:
- Base64 encoded client private key.
- Use instead of C(client-key) for embedded keys.
type: str
auth-provider:
description:
- Authentication provider configuration (e.g., for GCP, Azure).
type: dict
exec:
description:
- Exec-based credential plugin configuration.
- Used for external authentication providers.
type: dict
contexts:
description:
- List of context definitions linking users and clusters.
- Each context is identified by its C(name).
- When C(name) matches an existing context, the default C(behavior) is V(merge).
- See the C(behavior) suboption for V(replace) and V(keep).
type: list
elements: dict
required: false
default: []
suboptions:
name:
description:
- Unique name identifier for the context.
type: str
required: true
behavior:
description:
- How to handle merging if a context with this name already exists.
- C(merge) - Update only the specified fields, preserve others (default).
- C(replace) - Replace the entire context definition.
- C(keep) - Keep existing context, skip this entry.
type: str
choices: ['merge', 'replace', 'keep']
default: merge
context:
description:
- Context configuration linking cluster and user.
type: dict
required: true
suboptions:
cluster:
description:
- Name of the cluster to use (must match a cluster name in O(clusters)).
type: str
required: true
user:
description:
- Name of the user to authenticate as (must match a user name in O(users)).
type: str
required: true
namespace:
description:
- Default namespace to use for this context.
- If not specified, defaults to C(default).
type: str
preferences:
description:
- Kubeconfig preferences.
- Used for client-side settings like color output, default editor, etc.
type: dict
required: false
default: {}
current_context:
description:
- Name of the context to set as current/active.
- This context will be used by default when using kubectl.
- Must match one of the context names defined in O(contexts).
type: str
required: false
seealso:
- name: Kubernetes kubeconfig documentation
description: Official Kubernetes documentation for kubeconfig files
link: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/
- name: kubectl config documentation
description: kubectl commands for working with kubeconfig files
link: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_config/
"""
EXAMPLES = r"""
# Create a new kubeconfig file with a single cluster
- name: Create basic kubeconfig
kubernetes.core.kubeconfig:
path: /home/user/.kube/config
clusters:
- name: production-cluster
cluster:
server: https://prod.k8s.example.com:6443
certificate-authority-data: LS0tLS1CRUdJTi...
users:
- name: admin-user
user:
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9...
contexts:
- name: prod-admin
context:
cluster: production-cluster
user: admin-user
namespace: production
current_context: prod-admin
- name: Copy and modify kubeconfig
kubernetes.core.kubeconfig:
path: /home/user/.kube/config
dest: /home/user/.kube/config-backup
clusters:
- name: new-cluster
cluster:
server: https://new.example.com:6443
- name: Switch current context
kubernetes.core.kubeconfig:
path: ~/.kube/config
current_context: prod-context
- name: Update user credentials
kubernetes.core.kubeconfig:
path: ~/.kube/config
users:
- name: admin-user
user:
token: "{{ new_admin_token }}"
"""
RETURN = r"""
kubeconfig:
description: The complete kubeconfig data structure.
type: dict
returned: always
dest:
description: The path where the kubeconfig was written.
type: str
returned: always
sample: /home/user/.kube/config
"""
import os
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
extract_sensitive_values_from_kubeconfig,
)
from ansible_collections.kubernetes.core.plugins.module_utils.kubeconfig import (
hash_data,
load_yaml_file,
merge_by_name,
write_file,
)
try:
import yaml
IMP_YAML = True
IMP_YAML_ERR = None
except ImportError:
IMP_YAML = False
IMP_YAML_ERR = traceback.format_exc()
def run_module():
module_args = dict(
path=dict(type="str", required=True),
dest=dict(type="str", required=False),
clusters=dict(type="list", elements="dict", required=False, default=[]),
users=dict(type="list", elements="dict", required=False, default=[]),
contexts=dict(type="list", elements="dict", required=False, default=[]),
preferences=dict(type="dict", required=False, default={}),
current_context=dict(type="str", required=False),
)
module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
path = module.params["path"]
dest = module.params["dest"] or path
clusters_input = module.params["clusters"]
users_input = module.params["users"]
contexts_input = module.params["contexts"]
preferences = module.params["preferences"]
current_context = module.params["current_context"]
# Load existing kubeconfig
try:
if not IMP_YAML:
module.fail_json(
msg=missing_required_lib("pyyaml"),
exception=IMP_YAML_ERR,
)
existing = load_yaml_file(path) if path else {}
except Exception as e:
module.fail_json(
msg="Failed to load existing kubeconfig: %s" % to_native(e),
exception=traceback.format_exc(),
)
clusters = merge_by_name(existing.get("clusters", []), clusters_input)
users = merge_by_name(existing.get("users", []), users_input)
contexts = merge_by_name(existing.get("contexts", []), contexts_input)
# Build final kubeconfig
kubeconfig = {
"apiVersion": "v1",
"kind": "Config",
"preferences": preferences or existing.get("preferences", {}),
"clusters": clusters,
"users": users,
"contexts": contexts,
"current-context": current_context or existing.get("current-context") or "",
}
changed = False
old_data = {}
if os.path.exists(dest):
try:
with open(dest, "r") as f:
old_data = yaml.safe_load(f) or {}
except Exception as e:
module.fail_json(
msg="Failed to read destination file: %s" % to_native(e),
exception=traceback.format_exc(),
)
old_hash = hash_data(old_data)
new_hash = hash_data(kubeconfig)
if old_hash != new_hash:
if not module.check_mode:
try:
write_file(dest, kubeconfig)
except Exception as e:
module.fail_json(
msg="Failed to write kubeconfig: %s" % to_native(e),
exception=traceback.format_exc(),
)
changed = True
if isinstance(kubeconfig, dict):
module.no_log_values.update(
extract_sensitive_values_from_kubeconfig(kubeconfig)
)
module.exit_json(
changed=changed,
kubeconfig=kubeconfig,
dest=dest,
msg=(
"Kubeconfig file has been updated."
if changed
else "Kubeconfig file is already up to date."
),
)
def main():
run_module()
if __name__ == "__main__":
main()

14
sonar-project.properties Normal file
View File

@@ -0,0 +1,14 @@
# SonarCloud project configuration for kubernetes.core
# Parameters: https://docs.sonarqube.org/latest/analysis/analysis-parameters/
sonar.projectKey=ansible-collections_kubernetes.core
sonar.organization=ansible-collections
sonar.sources=.
sonar.projectName=kubernetes.core
sonar.python.coverage.reportPaths=coverage.xml
sonar.tests=tests/unit,tests/integration
sonar.python.version=3.12
sonar.newCode.referenceBranch=main
sonar.exclusions=tests/**,.tox/**

View File

@@ -0,0 +1 @@
test_directory: /tmp

View File

@@ -0,0 +1,122 @@
---
- name: Set test variables
set_fact:
test_config_path: /tmp/test-kubeconfig
test_cluster_name: test-cluster
test_user_name: test-user
test_context_name: test-context
# Test 1: Create new kubeconfig
- name: Create new kubeconfig file
kubernetes.core.kubeconfig:
path: "{{ test_config_path }}"
clusters:
- name: "{{ test_cluster_name }}"
cluster:
server: https://test.example.com:6443
insecure-skip-tls-verify: true
users:
- name: "{{ test_user_name }}"
user:
token: test-token-123
contexts:
- name: "{{ test_context_name }}"
context:
cluster: "{{ test_cluster_name }}"
user: "{{ test_user_name }}"
namespace: default
current_context: "{{ test_context_name }}"
register: create_result
- name: Verify file was created
assert:
that:
- create_result is changed
- create_result.kubeconfig.clusters | length == 1
- create_result.kubeconfig['current-context'] == test_context_name
# Test 2: Idempotency check
- name: Run same configuration again
kubernetes.core.kubeconfig:
path: "{{ test_config_path }}"
clusters:
- name: "{{ test_cluster_name }}"
cluster:
server: https://test.example.com:6443
insecure-skip-tls-verify: true
users:
- name: "{{ test_user_name }}"
user:
token: test-token-123
contexts:
- name: "{{ test_context_name }}"
context:
cluster: "{{ test_cluster_name }}"
user: "{{ test_user_name }}"
namespace: default
current_context: "{{ test_context_name }}"
register: idempotent_result
- name: Verify idempotency
assert:
that:
- idempotent_result is not changed
# Test 3: Merge new cluster
- name: Add second cluster
kubernetes.core.kubeconfig:
path: "{{ test_config_path }}"
clusters:
- name: cluster-2
cluster:
server: https://cluster2.example.com:6443
users:
- name: user-2
user:
token: token-2
contexts:
- name: context-2
context:
cluster: cluster-2
user: user-2
register: merge_result
- name: Verify merge
assert:
that:
- merge_result is changed
- merge_result.kubeconfig.clusters | length == 2
# Test 4: Update existing entry
- name: Update cluster server
kubernetes.core.kubeconfig:
path: "{{ test_config_path }}"
clusters:
- name: "{{ test_cluster_name }}"
cluster:
server: https://updated.example.com:6443
insecure-skip-tls-verify: true
register: update_result
- name: Verify update
assert:
that:
- update_result is changed
- update_result.kubeconfig.clusters[0].cluster.server == "https://updated.example.com:6443"
# Test 5: Check mode
- name: Test check mode
kubernetes.core.kubeconfig:
path: "{{ test_config_path }}"
clusters:
- name: check-mode-cluster
cluster:
server: https://check.example.com:6443
check_mode: true
register: check_mode_result
- name: Verify check mode didn't write
assert:
that:
- check_mode_result is changed
- check_mode_result.kubeconfig.clusters | length == 3 # Includes new cluster in output

View File

@@ -0,0 +1,231 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import yaml
from ansible_collections.kubernetes.core.plugins.module_utils.kubeconfig import (
deep_merge,
hash_data,
load_yaml_file,
merge_by_name,
write_file,
)
# load_yaml_file
def test_load_yaml_file_returns_empty_dict_for_missing_file():
assert load_yaml_file("/nonexistent/path/config") == {}
def test_load_yaml_file_returns_empty_dict_for_none():
assert load_yaml_file(None) == {}
def test_load_yaml_file_returns_empty_dict_for_empty_string():
assert load_yaml_file("") == {}
def test_load_yaml_file_loads_valid_yaml(tmp_path):
config = {"apiVersion": "v1", "kind": "Config", "clusters": []}
f = tmp_path / "config"
f.write_text(yaml.safe_dump(config))
assert load_yaml_file(str(f)) == config
def test_load_yaml_file_returns_empty_dict_for_empty_file(tmp_path):
f = tmp_path / "config"
f.write_text("")
assert load_yaml_file(str(f)) == {}
# deep_merge
def test_deep_merge_adds_new_keys():
base = {"a": 1}
updates = {"b": 2}
assert deep_merge(base, updates) == {"a": 1, "b": 2}
def test_deep_merge_overwrites_scalar():
base = {"a": 1}
updates = {"a": 99}
assert deep_merge(base, updates) == {"a": 99}
def test_deep_merge_recursively_merges_dicts():
base = {
"cluster": {
"server": "https://old.example.com",
"insecure-skip-tls-verify": True,
}
}
updates = {"cluster": {"server": "https://new.example.com"}}
result = deep_merge(base, updates)
assert result["cluster"]["server"] == "https://new.example.com"
assert result["cluster"]["insecure-skip-tls-verify"] is True
def test_deep_merge_does_not_mutate_base():
base = {"a": {"b": 1}}
updates = {"a": {"c": 2}}
deep_merge(base, updates)
assert base == {"a": {"b": 1}}
def test_deep_merge_overwrites_dict_with_scalar():
base = {"a": {"nested": 1}}
updates = {"a": "flat"}
assert deep_merge(base, updates) == {"a": "flat"}
# merge_by_name
def test_merge_by_name_adds_new_entry():
existing = []
new = [{"name": "cluster-a", "cluster": {"server": "https://a.example.com"}}]
result = merge_by_name(existing, new)
assert len(result) == 1
assert result[0]["name"] == "cluster-a"
def test_merge_by_name_preserves_existing_when_no_new():
existing = [{"name": "cluster-a", "cluster": {"server": "https://a.example.com"}}]
result = merge_by_name(existing, [])
assert len(result) == 1
assert result[0]["name"] == "cluster-a"
def test_merge_by_name_default_behavior_merges_fields():
existing = [
{
"name": "cluster-a",
"cluster": {"server": "https://old.com", "insecure-skip-tls-verify": True},
}
]
new = [{"name": "cluster-a", "cluster": {"server": "https://new.com"}}]
result = merge_by_name(existing, new)
assert len(result) == 1
assert result[0]["cluster"]["server"] == "https://new.com"
assert result[0]["cluster"]["insecure-skip-tls-verify"] is True
def test_merge_by_name_replace_behavior_replaces_entire_entry():
existing = [
{
"name": "cluster-a",
"cluster": {"server": "https://old.com", "insecure-skip-tls-verify": True},
}
]
new = [
{
"name": "cluster-a",
"behavior": "replace",
"cluster": {"server": "https://new.com"},
}
]
result = merge_by_name(existing, new)
assert result[0]["cluster"] == {"server": "https://new.com"}
assert "insecure-skip-tls-verify" not in result[0]["cluster"]
def test_merge_by_name_keep_behavior_preserves_existing():
existing = [{"name": "cluster-a", "cluster": {"server": "https://old.com"}}]
new = [
{
"name": "cluster-a",
"behavior": "keep",
"cluster": {"server": "https://new.com"},
}
]
result = merge_by_name(existing, new)
assert result[0]["cluster"]["server"] == "https://old.com"
def test_merge_by_name_behavior_key_not_in_output():
existing = []
new = [
{
"name": "cluster-a",
"behavior": "replace",
"cluster": {"server": "https://a.com"},
}
]
result = merge_by_name(existing, new)
assert "behavior" not in result[0]
def test_merge_by_name_skips_items_without_name():
existing = []
new = [{"cluster": {"server": "https://a.com"}}]
result = merge_by_name(existing, new)
assert result == []
def test_merge_by_name_skips_non_dict_items():
existing = []
new = ["not-a-dict", 42]
result = merge_by_name(existing, new)
assert result == []
def test_merge_by_name_adds_multiple_new_entries():
existing = []
new = [
{"name": "cluster-a", "cluster": {"server": "https://a.com"}},
{"name": "cluster-b", "cluster": {"server": "https://b.com"}},
]
result = merge_by_name(existing, new)
names = [r["name"] for r in result]
assert "cluster-a" in names
assert "cluster-b" in names
def test_merge_by_name_existing_non_dict_items_are_skipped():
existing = ["not-a-dict", {"cluster": {"server": "https://a.com"}}]
new = [{"name": "cluster-b", "cluster": {"server": "https://b.com"}}]
result = merge_by_name(existing, new)
assert len(result) == 1
assert result[0]["name"] == "cluster-b"
# hash_data
def test_hash_data_returns_string():
assert isinstance(hash_data({}), str)
def test_hash_data_different_input_different_hash():
assert hash_data({"a": 1}) != hash_data({"a": 2})
def test_hash_data_order_independent():
a = {"x": 1, "y": 2}
b = {"y": 2, "x": 1}
assert hash_data(a) == hash_data(b)
# write_file
def test_write_file_returns_false_for_empty_dest():
assert write_file("", {"apiVersion": "v1"}) is False
def test_write_file_returns_false_for_none_dest():
assert write_file(None, {"apiVersion": "v1"}) is False
def test_write_file_writes_valid_yaml(tmp_path):
dest = str(tmp_path / "config")
data = {"apiVersion": "v1", "kind": "Config"}
result = write_file(dest, data)
assert result is True
with open(dest, "r") as f:
written = yaml.safe_load(f)
assert written == data
def test_write_file_overwrites_existing_file(tmp_path):
dest = str(tmp_path / "config")
write_file(dest, {"apiVersion": "v1"})
write_file(dest, {"apiVersion": "v2"})
with open(dest, "r") as f:
written = yaml.safe_load(f)
assert written["apiVersion"] == "v2"