mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-11 20:12:18 +00:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e79ed52a4d | ||
|
|
4d7dc2a7d1 | ||
|
|
c2cfa51655 | ||
|
|
fb10b41918 | ||
|
|
210467b26d | ||
|
|
11f619b69e | ||
|
|
16e92a20e8 | ||
|
|
58f8f2e6e9 | ||
|
|
52f9a5b54b | ||
|
|
e6076e5568 | ||
|
|
42acb4f52b | ||
|
|
d239adbbbc | ||
|
|
6d942f5e45 | ||
|
|
bc0406cf02 | ||
|
|
23b6cec173 | ||
|
|
34beacf32b | ||
|
|
3e32c12c40 | ||
|
|
13791ec7bf | ||
|
|
452fb3d7cb | ||
|
|
12abc9bda9 | ||
|
|
987c029c87 | ||
|
|
6c00f7c7de | ||
|
|
bd1cacc0cf | ||
|
|
9cfa9038fc | ||
|
|
1c16a2d2b5 | ||
|
|
798f5493f9 | ||
|
|
4fa36487ab | ||
|
|
34467d42b3 | ||
|
|
ae624cfa44 | ||
|
|
05e5e0a5ac | ||
|
|
87344b93fc | ||
|
|
200d64f5ea | ||
|
|
ec35c74e2e | ||
|
|
027700c3f4 | ||
|
|
da93cce1fa | ||
|
|
448d3fe156 | ||
|
|
6158300062 | ||
|
|
3160e4aad6 | ||
|
|
93734fcefc | ||
|
|
a861079dc1 | ||
|
|
5148ee5f74 | ||
|
|
c48778d709 | ||
|
|
cf3c3a9dcc | ||
|
|
1705ced1b5 | ||
|
|
6a0635a2bb | ||
|
|
f568c9da62 | ||
|
|
94e42354cd | ||
|
|
642eb936c0 | ||
|
|
775959c3f9 | ||
|
|
77627bb8d9 | ||
|
|
b1fbd38352 | ||
|
|
a06b2c3969 | ||
|
|
34fd40d46a |
@@ -3,6 +3,7 @@ profile: production
|
||||
|
||||
exclude_paths:
|
||||
- .ansible/
|
||||
- .github/
|
||||
- tests/integration
|
||||
- tests/unit
|
||||
- tests/sanity
|
||||
@@ -3,3 +3,4 @@
|
||||
plugins/connection/kubectl.py no-changed-when
|
||||
# false positive result
|
||||
plugins/connection/kubectl.py var-naming[no-reserved]
|
||||
plugins/connection/kubectl.py jinja[invalid]
|
||||
|
||||
14
.github/workflows/integration-tests.yaml
vendored
14
.github/workflows/integration-tests.yaml
vendored
@@ -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
|
||||
@@ -54,13 +55,14 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ansible-version:
|
||||
- milestone
|
||||
# Ref must match a branch/tag on github.com/ansible/ansible (e.g. stable-2.18, not 2.18).
|
||||
ansible-version: ["stable-2.18", "milestone"]
|
||||
enable-turbo-mode: [true, false]
|
||||
exclude:
|
||||
- ansible-version: "milestone"
|
||||
enable-turbo-mode: true
|
||||
python-version:
|
||||
- "3.12"
|
||||
enable-turbo-mode:
|
||||
- true
|
||||
- false
|
||||
workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }}
|
||||
name: "integration-py${{ matrix.python-version }}-${{ matrix.ansible-version }}-${{ matrix.workflow-id }}-enable_turbo=${{ matrix.enable-turbo-mode }}"
|
||||
steps:
|
||||
@@ -106,6 +108,7 @@ jobs:
|
||||
source_path: ${{ env.source }}
|
||||
|
||||
- name: checkout ansible-collections/cloud.common
|
||||
if: ${{ matrix.enable-turbo-mode == true }}
|
||||
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
|
||||
with:
|
||||
repository: ansible-collections/cloud.common
|
||||
@@ -127,6 +130,7 @@ jobs:
|
||||
ref: main
|
||||
|
||||
- name: install cloud.common collection
|
||||
if: ${{ matrix.enable-turbo-mode == true }}
|
||||
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
|
||||
with:
|
||||
install_python_dependencies: true
|
||||
|
||||
18
.github/workflows/label-new-issues.yaml
vendored
Normal file
18
.github/workflows/label-new-issues.yaml
vendored
Normal 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
|
||||
28
.github/workflows/label-new-prs.yaml
vendored
Normal file
28
.github/workflows/label-new-prs.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: label new prs
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- converted_to_draft
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
if: github.actor != 'patchback[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Add 'needs_triage' label if the pr is not a draft
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
if: github.event.pull_request.draft == false
|
||||
with:
|
||||
labels: needs_triage
|
||||
|
||||
- name: Remove 'needs_triage' label if the pr is a draft
|
||||
uses: actions-ecosystem/action-remove-labels@v1
|
||||
if: github.event.pull_request.draft == true
|
||||
with:
|
||||
labels: needs_triage
|
||||
4
.github/workflows/linters.yaml
vendored
4
.github/workflows/linters.yaml
vendored
@@ -20,4 +20,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: run-ansible-lint
|
||||
uses: ansible/ansible-lint@v25.1.2
|
||||
uses: ansible/ansible-lint@main
|
||||
with:
|
||||
gh_action_ref: "v25.5.0"
|
||||
|
||||
70
.github/workflows/sonarcloud.yml
vendored
Normal file
70
.github/workflows/sonarcloud.yml
vendored
Normal 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 }}
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -17,7 +17,14 @@ tests/integration/cloud-config-*
|
||||
|
||||
# Helm charts
|
||||
tests/integration/*-chart-*.tgz
|
||||
tests/integration/targets/*/*.tgz
|
||||
|
||||
# ansible-test generated file
|
||||
tests/integration/inventory
|
||||
tests/integration/*-*.yml
|
||||
|
||||
# VS Code settings
|
||||
.vscode/
|
||||
|
||||
# Root coverage report for SonarCloud (generated locally or in CI)
|
||||
/coverage.xml
|
||||
|
||||
@@ -26,3 +26,4 @@ ignore: |
|
||||
.tox
|
||||
.ansible
|
||||
tests/output
|
||||
plugins/connection/kubectl.py
|
||||
|
||||
192
CHANGELOG.rst
192
CHANGELOG.rst
@@ -4,6 +4,188 @@ Kubernetes Collection Release Notes
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
v6.4.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release adds Helm v4 compatibility across the Helm modules and improves ``k8s_drain`` with check mode. When you explicitly allow evicting unmanaged pods, pods with local storage, or pods managed by a ``DaemonSet``, those cases are reported as informational output instead of module warnings.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- helm_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_pull - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - add new option plain_http to allow insecure http connection when running ``helm registry login`` (https://github.com/ansible-collections/kubernetes.core/pull/1090).
|
||||
- helm_repository - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- k8s_drain - Add support for ``check_mode`` (https://github.com/ansible-collections/kubernetes.core/pull/1086).
|
||||
- k8s_drain - Convert module warnings into informational displays when users explicitly request the deletion of unmanaged pods, pods with local storage, or those managed by a `DaemonSet` (https://github.com/ansible-collections/kubernetes.core/issues/1037).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Helm - Allow taking ownership of existing Kubernetes resources on the first installation of a Helm release. Previously, the ``take_ownership`` parameter was always disabled during the initial install, preventing resource adoption (https://github.com/ansible-collections/kubernetes.core/pull/1034).
|
||||
|
||||
v6.3.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release includes bugfixes such as implementing idempotency for the ``helm_pull`` module as well as a security update for selectively redacting sensitive information from kubeconfig.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Remove deprecated import from ``ansible.module_utils._text`` (https://github.com/ansible-collections/kubernetes.core/pull/1053).
|
||||
- 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).
|
||||
|
||||
v6.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release adds minor changes and bugfixes, including support of skip-schema-validation in ``helm`` module and removing deprecated ``ansible.module_utils.six`` imports.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Add support of skip-schema-validation in ``helm`` module (https://github.com/ansible-collections/kubernetes.core/pull/995)
|
||||
- kustomize - Add support of local environ (https://github.com/ansible-collections/kubernetes.core/pull/786).
|
||||
|
||||
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).
|
||||
|
||||
v6.1.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release adds ``plain_http`` and ``take_ownership`` parameters for helm modules, support for ``hidden_fields`` in ``k8s_json_patch``, documented lack of idempotency support in ``helm_registry_auth`` with ``helm ≥ 3.18.0``, and improved ``k8s_rollback`` test coverage.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Module helm_registry_auth do not support idempotency with `helm >= 3.18.0` (https://github.com/ansible-collections/kubernetes.core/pull/946)
|
||||
- Module k8s_json_patch - Add support for `hidden_fields` (https://github.com/ansible-collections/kubernetes.core/pull/964).
|
||||
- helm - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm - Parameter take_ownership added (https://github.com/ansible-collections/kubernetes.core/pull/957).
|
||||
- helm_pull - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm_template - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
|
||||
|
||||
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
|
||||
======
|
||||
|
||||
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
|
||||
======
|
||||
|
||||
@@ -15,15 +197,15 @@ This release includes minor changes, bug fixes and also bumps ``ansible-lint`` v
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- kubernetes.core - Bump version of ``ansible-lint`` to ``25.1.2`` (https://github.com/ansible-collections/kubernetes.core/pull/919).
|
||||
- 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).
|
||||
- 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).
|
||||
- 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
|
||||
======
|
||||
@@ -51,7 +233,7 @@ This release came with new module ``helm_registry_auth``, improvements to the er
|
||||
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).
|
||||
|
||||
@@ -124,7 +306,7 @@ Bugfixes
|
||||
- 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
|
||||
---------------
|
||||
|
||||
32
CI.md
Normal file
32
CI.md
Normal 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 |
|
||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
# Also needs to be updated in galaxy.yml
|
||||
VERSION = 6.0.0-dev0
|
||||
VERSION = 7.0.0-dev0
|
||||
|
||||
TEST_ARGS ?= ""
|
||||
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`
|
||||
|
||||
44
README.md
44
README.md
@@ -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:
|
||||
@@ -21,17 +39,19 @@ 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.16.0**.
|
||||
This collection has been tested against the following Ansible versions: **>=2.16.0**.
|
||||
|
||||
For collections that support Ansible 2.9, please ensure you update your `network_os` to use the
|
||||
fully qualified collection name (for example, `cisco.ios.ios`).
|
||||
Plugins and modules within a collection may be tested with only specific Ansible versions.
|
||||
A collection may contain metadata that identifies these versions.
|
||||
PEP440 is the schema used to describe the versions of Ansible.
|
||||
<!--end requires_ansible-->
|
||||
|
||||
### Helm Version Compatibility
|
||||
|
||||
This collection supports Helm v3.x and newer. Please note that specific modules or certain parameters may have additional version requirements.
|
||||
|
||||
### Python Support
|
||||
|
||||
* Collection supports 3.9+
|
||||
@@ -47,17 +67,17 @@ This collection supports Kubernetes versions >= 1.24.
|
||||
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
|
||||
|
||||
### 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
|
||||
@@ -101,7 +121,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
|
||||
---
|
||||
collections:
|
||||
- name: kubernetes.core
|
||||
version: 5.2.0
|
||||
version: 6.4.0
|
||||
```
|
||||
|
||||
### Installing the Kubernetes Python Library
|
||||
@@ -180,7 +200,6 @@ For documentation on how to use individual modules and other content included in
|
||||
|
||||
## 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:
|
||||
|
||||
```yaml
|
||||
@@ -219,7 +238,7 @@ You can run the collection's test suites with the commands:
|
||||
|
||||
### Testing with `molecule`
|
||||
|
||||
There are also integration tests in the `molecule` directory which are meant to be run against a local Kubernetes cluster, e.g. using [KinD](https://kind.sigs.k8s.io) or [Minikube](https://minikube.sigs.k8s.io). To setup a local cluster using KinD and run Molecule:
|
||||
There are also integration tests in the `molecule` directory which are meant to be run against a local Kubernetes cluster, e.g. using [KinD](https://kind.sigs.k8s.io) or [Minikube](https://minikube.sigs.k8s.io). To set up a local cluster using KinD and run Molecule:
|
||||
|
||||
kind create cluster
|
||||
make test-molecule
|
||||
@@ -258,7 +277,7 @@ For more information about communication, refer to the [Ansible Communication gu
|
||||
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/).
|
||||
- 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
|
||||
|
||||
@@ -270,9 +289,8 @@ We follow the [Ansible Code of Conduct](https://docs.ansible.com/ansible/devel/c
|
||||
|
||||
If you encounter abusive behavior, please refer to the [policy violations](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html#policy-violations) section of the Code for information on how to raise a complaint.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0 or later
|
||||
|
||||
See LICENCE to see the full text.
|
||||
See LICENSE to see the full text.
|
||||
|
||||
@@ -977,7 +977,7 @@ 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
|
||||
@@ -1027,15 +1027,15 @@ releases:
|
||||
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).
|
||||
``delete_options`` and ``check_mode=true`` (https://github.com/ansible-collections/kubernetes.core/issues/892).
|
||||
minor_changes:
|
||||
- Bump version of ansible-lint to 25.1.2 (https://github.com/ansible-collections/kubernetes.core/pull/919).
|
||||
- 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 ``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``.
|
||||
``ansible-lint`` version to ``25.1.2``.
|
||||
fragments:
|
||||
- 20250324-k8s_info-templating.yaml
|
||||
- 5.3.0.yml
|
||||
@@ -1044,3 +1044,205 @@ releases:
|
||||
- 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'
|
||||
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:
|
||||
- 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).
|
||||
fragments:
|
||||
- 20250121-breaking-changes-6.0.0.yml
|
||||
release_date: '2025-05-19'
|
||||
6.1.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
|
||||
minor_changes:
|
||||
- Module ``helm_registry_auth`` does not support idempotency with `helm >= 3.18.0`
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/946).
|
||||
- Module k8s_json_patch - Add support for ``hidden_fields`` (https://github.com/ansible-collections/kubernetes.core/pull/964).
|
||||
- helm - Parameter plain_http added for working with insecure OCI registries
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm - Parameter take_ownership added (https://github.com/ansible-collections/kubernetes.core/pull/957).
|
||||
- helm_pull - Parameter plain_http added for working with insecure OCI registries
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm_template - Parameter plain_http added for working with insecure OCI registries
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
release_summary: "This release adds ``plain_http`` and ``take_ownership`` parameters
|
||||
for helm modules, support for ``hidden_fields`` in ``k8s_json_patch``, documented
|
||||
lack of idempotency support in ``helm_registry_auth`` with ``helm \u2265 3.18.0``,
|
||||
and improved ``k8s_rollback`` test coverage."
|
||||
fragments:
|
||||
- 20250411-kubeconfig-no_log-revert.yaml
|
||||
- 20250428-k8s-service-hide-fields-first.yaml
|
||||
- 20250522-add-plain-http-for-oci-registries.yaml
|
||||
- 20250605-fix-helm_registry_auth-integration_test.yaml
|
||||
- 20250704-k8s-rollback-integration-test-coverage.yaml
|
||||
- 20250720-k8s-patch-add-hidden-fields.yaml
|
||||
- 20250911-add-support-helm-take-ownership.yaml
|
||||
- release_summary.yml
|
||||
release_date: '2025-08-12'
|
||||
6.2.0:
|
||||
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).
|
||||
minor_changes:
|
||||
- Add support of skip-schema-validation in ``helm`` module (https://github.com/ansible-collections/kubernetes.core/pull/995)
|
||||
- kustomize - Add support of local environ (https://github.com/ansible-collections/kubernetes.core/pull/786).
|
||||
release_summary: This release adds minor changes and bugfixes, including support
|
||||
of skip-schema-validation in ``helm`` module and removing deprecated ``ansible.module_utils.six``
|
||||
imports.
|
||||
fragments:
|
||||
- 20241030-support-of-evrion-for-kustomize-lookup-plugin.yaml
|
||||
- 20250731-fix-k8s_cp-initcontainers.yaml
|
||||
- 20250916-skip-schema-validation.yaml
|
||||
- 20250922-remove-ansible-six-imports.yaml
|
||||
- 6_2_0.yml
|
||||
release_date: '2025-10-07'
|
||||
6.3.0:
|
||||
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:
|
||||
- Remove deprecated import from ``ansible.module_utils._text`` (https://github.com/ansible-collections/kubernetes.core/pull/1053).
|
||||
- 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).
|
||||
release_summary: This release includes bugfixes such as implementing idempotency
|
||||
for the ``helm_pull`` module as well as a security update 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
|
||||
- 20251228-1053-remove-deprecated-import.yaml
|
||||
- 20260107-add-idempodency-for-helm-pull.yaml
|
||||
- 20260108-fix-sanity-failures.yml
|
||||
- 6-3-0.yaml
|
||||
release_date: '2026-02-03'
|
||||
6.4.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Helm - Allow taking ownership of existing Kubernetes resources on the first
|
||||
installation of a Helm release. Previously, the ``take_ownership`` parameter
|
||||
was always disabled during the initial install, preventing resource adoption
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/1034).
|
||||
minor_changes:
|
||||
- helm_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_pull - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - add new option plain_http to allow insecure http connection
|
||||
when running ``helm registry login`` (https://github.com/ansible-collections/kubernetes.core/pull/1090).
|
||||
- helm_repository - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- k8s_drain - Add support for ``check_mode`` (https://github.com/ansible-collections/kubernetes.core/pull/1086).
|
||||
- k8s_drain - Convert module warnings into informational displays when users
|
||||
explicitly request the deletion of unmanaged pods, pods with local storage,
|
||||
or those managed by a ``DaemonSet`` (https://github.com/ansible-collections/kubernetes.core/issues/1037).
|
||||
release_summary: This release adds Helm v4 compatibility across the Helm modules
|
||||
and improves ``k8s_drain`` with check mode. When you explicitly allow evicting
|
||||
unmanaged pods, pods with local storage, or pods managed by a ``DaemonSet``,
|
||||
those cases are reported as informational output instead of module warnings.
|
||||
fragments:
|
||||
- 20251224-take-ownership-helm-initialization.yaml
|
||||
- 20260203-k8s_drain-warning-fixes.yaml
|
||||
- 20260213-support-helm-v4-for-helm-plugin-modules.yaml
|
||||
- release-6-4-0.yml
|
||||
release_date: '2026-04-22'
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
breaking_changes:
|
||||
- Remove support for ``ansible-core<2.16`` (https://github.com/ansible-collections/kubernetes.core/pull/867).
|
||||
- Remove deprecated ``k8s`` invetory plugin (https://github.com/ansible-collections/kubernetes.core/pull/867).
|
||||
@@ -1,147 +0,0 @@
|
||||
.. _ansible_turbo_mode:
|
||||
|
||||
|
||||
******************
|
||||
Ansible Turbo mode
|
||||
******************
|
||||
|
||||
Following document provides overview of Ansible Turbo mode in ``kubernetes.core`` collection.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
- A brief introduction about Ansible Turbo mode in ``kuberentes.core`` collection.
|
||||
- Ansible Turbo mode is an optional performance optimization. It can be enabled by installing the cloud.common collection and setting the ``ENABLE_TURBO_MODE`` environment variable.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The following requirement is needed on the host that executes this module.
|
||||
|
||||
- The ``cloud.common`` collection (https://github.com/ansible-collections/cloud.common)
|
||||
|
||||
You will also need to set the environment variable ``ENABLE_TURBO_MODE=1`` on the managed host. This can be done in the same ways you would usually do so, for example::
|
||||
|
||||
---
|
||||
- hosts: remote
|
||||
environment:
|
||||
ENABLE_TURBO_MODE: 1
|
||||
tasks:
|
||||
...
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install ``cloud.common`` collection using following command::
|
||||
|
||||
# ansible-galaxy collection install cloud.common
|
||||
|
||||
|
||||
Current situation without Ansible Turbo mode
|
||||
============================================
|
||||
|
||||
The traditional execution flow of an Ansible module includes the following steps:
|
||||
|
||||
- Upload of a ZIP archive with the module and its dependencies
|
||||
- Execution of the module
|
||||
- Ansible collects the results once the script is finished
|
||||
|
||||
These steps happen for each task of a playbook, and on every host.
|
||||
|
||||
Most of the time, the execution of a module is fast enough for
|
||||
the user. However, sometime the module requires significant amount of time,
|
||||
just to initialize itself. This is a common situation with the API based modules.
|
||||
|
||||
A classic initialization involves the following steps:
|
||||
|
||||
- Load a Python library to access the remote resource (via SDK)
|
||||
- Open a client
|
||||
- Load a bunch of Python modules.
|
||||
- Request a new TCP connection.
|
||||
- Create a session.
|
||||
- Authenticate the client.
|
||||
|
||||
All these steps are time consuming and the same operations will be running again and again.
|
||||
|
||||
For instance, here:
|
||||
|
||||
- ``import openstack``: takes 0.569s
|
||||
- ``client = openstack.connect()``: takes 0.065s
|
||||
- ``client.authorize()``: takes 1.360s,
|
||||
|
||||
These numbers are from test running against VexxHost public cloud.
|
||||
|
||||
In this case, it's a 2s-ish overhead per task. If the playbook
|
||||
comes with 10 tasks, the execution time cannot go below 20s.
|
||||
|
||||
How Ansible Turbo Module improve the situation
|
||||
==============================================
|
||||
|
||||
``AnsibleTurboModule`` is actually a class that inherites from
|
||||
the standard ``AnsibleModule`` class that your modules probably
|
||||
already use.
|
||||
The big difference is that when a module starts, it also spawns
|
||||
a little Python daemon. If a daemon already exists, it will just
|
||||
reuse it.
|
||||
All the module logic is run inside this Python daemon. This means:
|
||||
|
||||
- Python modules are actually loaded one time
|
||||
- Ansible module can reuse an existing authenticated session.
|
||||
|
||||
The background service
|
||||
======================
|
||||
|
||||
The daemon kills itself after 15s, and communication are done
|
||||
through an Unix socket.
|
||||
It runs in one single process and uses ``asyncio`` internally.
|
||||
Consequently you can use the ``async`` keyword in your Ansible module.
|
||||
This will be handy if you interact with a lot of remote systems
|
||||
at the same time.
|
||||
|
||||
Security impact
|
||||
===============
|
||||
|
||||
``ansible_module.turbo`` open an Unix socket to interact with the background service.
|
||||
We use this service to open the connection toward the different target systems.
|
||||
|
||||
This is similar to what SSH does with the sockets.
|
||||
|
||||
Keep in mind that:
|
||||
|
||||
- All the modules can access the same cache. Soon an isolation will be done at the collection level (https://github.com/ansible-collections/cloud.common/pull/17)
|
||||
- A task can load a different version of a library and impact the next tasks.
|
||||
- If the same user runs two ``ansible-playbook`` at the same time, they will have access to the same cache.
|
||||
|
||||
When a module stores a session in a cache, it's a good idea to use a hash of the authentication information to identify the session.
|
||||
|
||||
Error management
|
||||
================
|
||||
|
||||
``ansible_module.turbo`` uses exceptions to communicate a result back to the module.
|
||||
|
||||
- ``EmbeddedModuleFailure`` is raised when ``json_fail()`` is called.
|
||||
- ``EmbeddedModuleSuccess`` is raised in case of success and returns the result to the origin module process.
|
||||
|
||||
These exceptions are defined in ``ansible_collections.cloud.common.plugins.module_utils.turbo.exceptions``.
|
||||
You can raise ``EmbeddedModuleFailure`` exception yourself, for instance from a module in ``module_utils``.
|
||||
|
||||
.. note:: Be careful with the ``except Exception:`` blocks.
|
||||
Not only they are bad practice, but also may interface with this
|
||||
mechanism.
|
||||
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
You may want to manually start the server. This can be done with the following command:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
PYTHONPATH=$HOME/.ansible/collections python -m ansible_collections.cloud.common.plugins.module_utils.turbo.server --socket-path $HOME/.ansible/tmp/turbo_mode.kubernetes.core.socket
|
||||
|
||||
You can use the ``--help`` argument to get a list of the optional parameters.
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -410,6 +410,23 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.3.0</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
@@ -465,12 +482,13 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<b>values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy</div>
|
||||
<div>This return value has been deprecated and will be removed in a release after 2027-01-08. Use RV(status.release_values) instead.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
@@ -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> ←</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 >= 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>
|
||||
@@ -307,6 +330,27 @@ Parameters
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: kubeconfig_path</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plain_http</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.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use HTTP instead of HTTPS when working with OCI registries</div>
|
||||
<div>Requires Helm >= 3.13.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -578,6 +622,48 @@ Parameters
|
||||
<div>Skip custom resource definitions when installing or upgrading.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>skip_schema_validation</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.2.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Disables JSON schema validation for Chart and values.</div>
|
||||
<div>This feature requires helm >= 3.16.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>take_ownership</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.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Helm will ignore the check for helm annotations and take ownership of the existing resources</div>
|
||||
<div>This feature requires helm >= 3.17.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -787,6 +873,12 @@ Examples
|
||||
chart_ref: "https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz"
|
||||
release_namespace: monitoring
|
||||
|
||||
- name: Deploy Bitnami's MongoDB latest chart from OCI registry
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: "oci://registry-1.docker.io/bitnamicharts/mongodb"
|
||||
release_namespace: database
|
||||
|
||||
# Using complex Values
|
||||
- name: Deploy new-relic client chart
|
||||
kubernetes.core.helm:
|
||||
@@ -828,7 +920,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -934,6 +1026,23 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.3.0</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
@@ -989,12 +1098,13 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<b>values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy</div>
|
||||
<div>Dict of Values used to deploy.</div>
|
||||
<div>This return value has been deprecated and will be removed in a release after 2027-01-08. Use RV(status.release_values) instead.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -196,7 +196,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -231,6 +231,28 @@ Parameters
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>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 6.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>no</li>
|
||||
<li><div style="color: blue"><b>yes</b> ←</div></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Verify the plugin signature before installing.</div>
|
||||
<div>This option requires helm version >= 4.0.0</div>
|
||||
<div>Used with <em>state=present</em>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
@@ -272,7 +294,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm >= 3.0 (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -174,6 +174,28 @@ Parameters
|
||||
<div>location to write the chart.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>force</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.3.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Force download of the chart even if it already exists in the destination directory.</div>
|
||||
<div>By default, the module will skip downloading if the chart with the same version already exists for idempotency.</div>
|
||||
<div>When used with O(untar_chart=true), will remove any existing chart directory before extracting.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -193,6 +215,27 @@ Parameters
|
||||
<div>Pass credentials to all domains.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plain_http</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.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use HTTP instead of HTTPS when working with OCI registries</div>
|
||||
<div>Requires Helm >= 3.13.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -279,7 +322,8 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Whether or not to check tls certificate for the chart download.</div>
|
||||
<div>Requires helm >= 3.3.0.</div>
|
||||
<div>Requires helm >= 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>
|
||||
@@ -375,11 +419,28 @@ Examples
|
||||
username: myuser
|
||||
password: mypassword123
|
||||
|
||||
- name: Download Chart (force re-download even if exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
force: yes
|
||||
|
||||
- name: Download and untar chart (force re-extraction even if directory exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
untar_chart: yes
|
||||
force: yes
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -406,6 +467,23 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">helm pull --repo test ...</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>msg</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>when chart already exists</td>
|
||||
<td>
|
||||
<div>A message indicating the result of the operation.</div>
|
||||
<br/>
|
||||
<div style="font-size: smaller"><b>Sample:</b></div>
|
||||
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">Chart redis version 17.0.0 already exists in destination directory</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases) => 3.8.0
|
||||
- helm (https://github.com/helm/helm/releases) >= 3.8.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -151,6 +151,27 @@ Parameters
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: repo_password</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plain_http</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.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use insecure HTTP connections for <code>helm registry login</code>.</div>
|
||||
<div>Requires Helm >= 3.18.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -170,6 +191,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 >= 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>
|
||||
@@ -214,7 +236,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
@@ -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> ←</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>
|
||||
@@ -315,7 +336,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -20,6 +20,13 @@ Synopsis
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -194,6 +201,27 @@ Parameters
|
||||
<div>If the directory already exists, it will be overwritten.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plain_http</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.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use HTTP instead of HTTPS when working with OCI registries</div>
|
||||
<div>Requires Helm >= 3.13.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -409,7 +437,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
@@ -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'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>
|
||||
@@ -702,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
|
||||
|
||||
@@ -140,6 +140,25 @@ Parameters
|
||||
<div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>hidden_fields</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
/ <span style="color: purple">elements=string</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of fields to hide from the diff output.</div>
|
||||
<div>This is useful for fields that are not relevant to the patch operation, such as `metadata.managedFields`.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
|
||||
@@ -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'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>
|
||||
|
||||
@@ -95,6 +95,26 @@ Parameters
|
||||
<div>Enable the helm chart inflation generator</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>environment</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">raw</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.2.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">{}</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The environment variables to pass to the kustomize or kubectl command.</div>
|
||||
<div>This can be a dictionary or a string in the format key=value, multiple pairs separated by space.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -145,6 +165,14 @@ Examples
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', dir='/path/to/kustomization', enable_helm=True) }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in string format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment='HTTP_PROXY=http://proxy.example.com:3128') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in dict format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment={'HTTP_PROXY': 'http://proxy.example.com:3128'}) }}"
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
|
||||
@@ -25,7 +25,7 @@ tags:
|
||||
- openshift
|
||||
- okd
|
||||
- cluster
|
||||
version: 6.0.0-dev0
|
||||
version: 7.0.0-dev0
|
||||
build_ignore:
|
||||
- .DS_Store
|
||||
- "*.tar.gz"
|
||||
|
||||
@@ -8,13 +8,16 @@ action_groups:
|
||||
- helm_repository
|
||||
k8s:
|
||||
- k8s
|
||||
- k8s_exec
|
||||
- k8s_info
|
||||
- k8s_log
|
||||
- k8s_scale
|
||||
- k8s_service
|
||||
- k8s_cluster_info
|
||||
- k8s_cp
|
||||
- k8s_drain
|
||||
- k8s_exec
|
||||
- k8s_info
|
||||
- k8s_json_patch
|
||||
- k8s_log
|
||||
- k8s_rollback
|
||||
- k8s_scale
|
||||
- k8s_service
|
||||
|
||||
plugin_routing:
|
||||
inventory:
|
||||
@@ -22,8 +25,7 @@ plugin_routing:
|
||||
redirect: community.okd.openshift
|
||||
k8s:
|
||||
tombstone:
|
||||
# To be replaced with 6.0.0 in release 6.0.0 commit
|
||||
removal_version: 6.0.0-dev0
|
||||
removal_version: 6.0.0
|
||||
warning_text: >-
|
||||
The k8s inventory plugin was slated for deprecation in 3.3.0 and has been removed in release 6.0.0. Use kubernetes.core.k8s_info and ansible.builtin.add_host instead.
|
||||
modules:
|
||||
|
||||
@@ -20,9 +20,8 @@ from ansible.errors import (
|
||||
AnsibleError,
|
||||
AnsibleFileNotFound,
|
||||
)
|
||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.common.text.converters 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:
|
||||
@@ -100,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):
|
||||
@@ -120,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))
|
||||
@@ -196,7 +195,7 @@ class ActionModule(ActionBase):
|
||||
)
|
||||
|
||||
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:
|
||||
@@ -246,7 +245,7 @@ class ActionModule(ActionBase):
|
||||
# add ansible 'template' vars
|
||||
temp_vars = copy.deepcopy(task_vars)
|
||||
overrides = {}
|
||||
for key, value in iteritems(template_item):
|
||||
for key, value in template_item.items():
|
||||
if hasattr(self._templar.environment, key):
|
||||
if value is not None:
|
||||
overrides[key] = value
|
||||
@@ -303,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
|
||||
|
||||
@@ -264,7 +264,8 @@ import subprocess
|
||||
import tempfile
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleFileNotFound
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.plugins.connection import BUFSIZE, ConnectionBase
|
||||
@@ -324,9 +325,12 @@ class Connection(ConnectionBase):
|
||||
# Build command options based on doc string
|
||||
doc_yaml = AnsibleLoader(self.documentation).get_single_data()
|
||||
for key in doc_yaml.get("options"):
|
||||
if key.endswith("verify_ssl") and self.get_option(key) != "":
|
||||
# Translate verify_ssl to skip_verify_ssl, and output as string
|
||||
skip_verify_ssl = not self.get_option(key)
|
||||
if key == "validate_certs" and self.get_option(key) != "":
|
||||
# Translate validate_certs to --insecure-skip-tls-verify flag
|
||||
# validate_certs=True means verify certs (don't skip verification)
|
||||
# validate_certs=False means don't verify certs (skip verification)
|
||||
validate_certs_value = boolean(self.get_option(key), strict=False)
|
||||
skip_verify_ssl = not validate_certs_value
|
||||
local_cmd.append(
|
||||
"{0}={1}".format(
|
||||
self.connection_options[key], str(skip_verify_ssl).lower()
|
||||
|
||||
@@ -86,14 +86,48 @@ DOCUMENTATION = """
|
||||
description:
|
||||
- Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment
|
||||
variable.
|
||||
no_proxy:
|
||||
description:
|
||||
- The comma separated list of hosts/domains/IP/CIDR that shouldn't go through proxy.
|
||||
Can also be specified via K8S_AUTH_NO_PROXY environment variable.
|
||||
- Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).
|
||||
- This feature requires kubernetes>=19.15.0.
|
||||
When kubernetes library is less than 19.15.0, it fails even if no_proxy is set correctly.
|
||||
type: str
|
||||
password:
|
||||
description:
|
||||
- Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment
|
||||
variable.
|
||||
proxy:
|
||||
description:
|
||||
- The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.
|
||||
- Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).
|
||||
type: str
|
||||
proxy_headers:
|
||||
description:
|
||||
- The Header used for the HTTP proxy.
|
||||
- Documentation can be found here
|
||||
U(https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers).
|
||||
type: dict
|
||||
suboptions:
|
||||
proxy_basic_auth:
|
||||
type: str
|
||||
description:
|
||||
- Colon-separated username:password for proxy basic authentication header.
|
||||
- Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.
|
||||
basic_auth:
|
||||
type: str
|
||||
description:
|
||||
- Colon-separated username:password for basic authentication header.
|
||||
- Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.
|
||||
user_agent:
|
||||
type: str
|
||||
description:
|
||||
- String representing the user-agent you want, such as foo/1.0.
|
||||
- Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.
|
||||
client_cert:
|
||||
description:
|
||||
- Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
|
||||
environment
|
||||
- Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment
|
||||
variable.
|
||||
aliases: [ cert_file ]
|
||||
client_key:
|
||||
|
||||
@@ -34,6 +34,13 @@ DOCUMENTATION = """
|
||||
description:
|
||||
- Enable the helm chart inflation generator
|
||||
default: "False"
|
||||
environment:
|
||||
description:
|
||||
- The environment variables to pass to the kustomize or kubectl command.
|
||||
- This can be a dictionary or a string in the format key=value, multiple pairs separated by space.
|
||||
type: raw
|
||||
default: {}
|
||||
version_added: 6.2.0
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
@@ -55,6 +62,14 @@ EXAMPLES = """
|
||||
- name: Create kubernetes resources for lookup output with `--enable-helm` set
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', dir='/path/to/kustomization', enable_helm=True) }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in string format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment='HTTP_PROXY=http://proxy.example.com:3128') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in dict format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment={'HTTP_PROXY': 'http://proxy.example.com:3128'}) }}"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
@@ -72,6 +87,7 @@ RETURN = """
|
||||
key1: val1
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from ansible.errors import AnsibleLookupError
|
||||
@@ -92,8 +108,10 @@ def get_binary_from_path(name, opt_dirs=None):
|
||||
return None
|
||||
|
||||
|
||||
def run_command(command):
|
||||
cmd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
def run_command(command, environ=None):
|
||||
cmd = subprocess.Popen(
|
||||
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=environ
|
||||
)
|
||||
stdout, stderr = cmd.communicate()
|
||||
return cmd.returncode, stdout, stderr
|
||||
|
||||
@@ -107,6 +125,7 @@ class LookupModule(LookupBase):
|
||||
binary_path=None,
|
||||
opt_dirs=None,
|
||||
enable_helm=False,
|
||||
environment=None,
|
||||
**kwargs
|
||||
):
|
||||
executable_path = binary_path
|
||||
@@ -141,7 +160,21 @@ class LookupModule(LookupBase):
|
||||
if enable_helm:
|
||||
command += ["--enable-helm"]
|
||||
|
||||
(ret, out, err) = run_command(command)
|
||||
environ = None
|
||||
if environment:
|
||||
environ = os.environ.copy()
|
||||
if isinstance(environment, str):
|
||||
if not all(env.count("=") == 1 for env in environment.split(" ")):
|
||||
raise AnsibleLookupError(
|
||||
"environment should be dict or string in the format key=value, multiple pairs separated by space"
|
||||
)
|
||||
for env in environment.split(" "):
|
||||
key, value = env.split("=")
|
||||
environ[key] = value
|
||||
if isinstance(environment, dict):
|
||||
environ.update(environment)
|
||||
|
||||
(ret, out, err) = run_command(command, environ=environ)
|
||||
if ret != 0:
|
||||
if err:
|
||||
raise AnsibleLookupError(
|
||||
|
||||
@@ -1,16 +1,64 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ansible.module_utils.six import string_types
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import warnings
|
||||
|
||||
|
||||
def list_dict_str(value):
|
||||
if isinstance(value, (list, dict, string_types)):
|
||||
if isinstance(value, (list, dict, str)):
|
||||
return value
|
||||
raise TypeError
|
||||
|
||||
|
||||
def extract_sensitive_values_from_kubeconfig(kubeconfig_data):
|
||||
"""
|
||||
Extract only sensitive string values from kubeconfig data for no_log_values.
|
||||
|
||||
:arg kubeconfig_data: Dictionary containing kubeconfig data
|
||||
:returns: Set of sensitive string values to be added to no_log_values
|
||||
"""
|
||||
values = set()
|
||||
sensitive_fields = {
|
||||
"token",
|
||||
"password",
|
||||
"secret",
|
||||
"client-key-data",
|
||||
"client-certificate-data",
|
||||
"certificate-authority-data",
|
||||
"api_key",
|
||||
"access-token",
|
||||
"refresh-token",
|
||||
}
|
||||
|
||||
# Check API version and warn if not v1
|
||||
if isinstance(kubeconfig_data, dict):
|
||||
api_version = kubeconfig_data.get("apiVersion", "v1")
|
||||
if api_version != "v1":
|
||||
warnings.warn(
|
||||
f"Kubeconfig API version '{api_version}' is not 'v1'. "
|
||||
f"Sensitive field redaction is only guaranteed for API version 'v1'. "
|
||||
f"Some sensitive data may not be properly redacted from the logs.",
|
||||
UserWarning,
|
||||
)
|
||||
|
||||
def _extract_recursive(data, current_path=""):
|
||||
if isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
path = f"{current_path}.{key}" if current_path else key
|
||||
if key in sensitive_fields:
|
||||
if isinstance(value, str):
|
||||
values.add(value)
|
||||
else:
|
||||
_extract_recursive(value, path)
|
||||
elif isinstance(data, list):
|
||||
for i, item in enumerate(data):
|
||||
_extract_recursive(item, f"{current_path}[{i}]")
|
||||
|
||||
_extract_recursive(kubeconfig_data)
|
||||
return values
|
||||
|
||||
|
||||
AUTH_PROXY_HEADERS_SPEC = dict(
|
||||
proxy_basic_auth=dict(type="str", no_log=True),
|
||||
basic_auth=dict(type="str", no_log=True),
|
||||
@@ -18,7 +66,7 @@ AUTH_PROXY_HEADERS_SPEC = dict(
|
||||
)
|
||||
|
||||
AUTH_ARG_SPEC = {
|
||||
"kubeconfig": {"type": "raw", "no_log": True},
|
||||
"kubeconfig": {"type": "raw"},
|
||||
"context": {},
|
||||
"host": {},
|
||||
"api_key": {"no_log": True},
|
||||
|
||||
@@ -23,7 +23,7 @@ import base64
|
||||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.urls import Request
|
||||
|
||||
try:
|
||||
|
||||
@@ -23,7 +23,7 @@ from abc import ABCMeta, abstractmethod
|
||||
from select import select
|
||||
from tempfile import NamedTemporaryFile, TemporaryFile
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
# from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import AnsibleModule
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
@@ -278,11 +278,15 @@ class K8SCopyFromPod(K8SCopy):
|
||||
def run(self):
|
||||
self.files_to_copy = self.list_remote_files()
|
||||
if self.files_to_copy == []:
|
||||
# Using warn method instead of passing warnings to exit_json as it is
|
||||
# deprecated in ansible-core>=2.19.0
|
||||
self._module.warn(
|
||||
"No file found from directory '{0}' into remote Pod.".format(
|
||||
self.remote_path
|
||||
)
|
||||
)
|
||||
self.module.exit_json(
|
||||
changed=False,
|
||||
warning="No file found from directory '{0}' into remote Pod.".format(
|
||||
self.remote_path
|
||||
),
|
||||
)
|
||||
self.copy()
|
||||
|
||||
@@ -435,11 +439,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)
|
||||
|
||||
@@ -15,7 +15,9 @@ 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.args_common import (
|
||||
extract_sensitive_values_from_kubeconfig,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
@@ -38,16 +40,20 @@ def parse_helm_plugin_list(output=None):
|
||||
if not output:
|
||||
return ret
|
||||
|
||||
parsing_grammar = None
|
||||
for line in output:
|
||||
if line.startswith("NAME"):
|
||||
parsing_grammar = [s.strip().lower() for s in line.split("\t")]
|
||||
continue
|
||||
name, version, description = line.split("\t", 3)
|
||||
name = name.strip()
|
||||
version = version.strip()
|
||||
description = description.strip()
|
||||
if name == "":
|
||||
if parsing_grammar is None:
|
||||
continue
|
||||
ret.append((name, version, description))
|
||||
plugin = {
|
||||
parsing_grammar[i]: v.strip()
|
||||
for i, v in enumerate(line.split("\t", len(parsing_grammar)))
|
||||
}
|
||||
if plugin["name"] == "":
|
||||
continue
|
||||
ret.append(plugin)
|
||||
|
||||
return ret
|
||||
|
||||
@@ -113,12 +119,19 @@ 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):
|
||||
kubeconfig_content = kubeconfig
|
||||
|
||||
# Redact sensitive fields from kubeconfig for logging purposes
|
||||
if kubeconfig_content:
|
||||
# Add original sensitive values to no_log_values to prevent them from appearing in logs
|
||||
self._module.no_log_values.update(
|
||||
extract_sensitive_values_from_kubeconfig(kubeconfig_content)
|
||||
)
|
||||
|
||||
if self.params.get("ca_cert"):
|
||||
ca_cert = self.params.get("ca_cert")
|
||||
if LooseVersion(self.get_helm_version()) < LooseVersion("3.5.0"):
|
||||
@@ -193,6 +206,38 @@ class AnsibleHelmModule(object):
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
def is_helm_v4(self):
|
||||
helm_version = self.get_helm_version()
|
||||
if helm_version is None:
|
||||
return False
|
||||
return LooseVersion(helm_version) >= LooseVersion("4.0.0")
|
||||
|
||||
def is_helm_version_compatible_with_helm_diff(self, helm_diff_version):
|
||||
"""
|
||||
Return true if the helm version is compatible with the helm diff version
|
||||
Helm v4 requires helm diff v3.14.0
|
||||
"""
|
||||
if not helm_diff_version:
|
||||
return False
|
||||
if self.is_helm_v4():
|
||||
return LooseVersion(helm_diff_version) >= LooseVersion("3.14.0")
|
||||
return True
|
||||
|
||||
def validate_helm_version(self, version="3.0.0"):
|
||||
"""
|
||||
Validate that Helm version is >= version (default version=3.0.0).
|
||||
"""
|
||||
helm_version = self.get_helm_version()
|
||||
if helm_version is None:
|
||||
self.fail_json(msg="Unable to determine Helm version")
|
||||
|
||||
if LooseVersion(helm_version) < LooseVersion(version):
|
||||
self.fail_json(
|
||||
msg="Helm version must be >= {0}, current version is {1}".format(
|
||||
version, helm_version
|
||||
)
|
||||
)
|
||||
|
||||
def get_values(self, release_name, get_all=False):
|
||||
"""
|
||||
Get Values from deployed release
|
||||
|
||||
@@ -16,7 +16,6 @@ HELM_AUTH_ARG_SPEC = dict(
|
||||
type="raw",
|
||||
aliases=["kubeconfig_path"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_KUBECONFIG"]),
|
||||
no_log=True,
|
||||
),
|
||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||
ca_cert=dict(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -3,6 +3,9 @@ from typing import Optional
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
extract_sensitive_values_from_kubeconfig,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
@@ -33,6 +36,15 @@ class AnsibleK8SModule:
|
||||
|
||||
self._module = self.settings["module_class"](**kwargs)
|
||||
|
||||
# Apply kubeconfig redaction for logging purposes
|
||||
if hasattr(self._module, "params") and hasattr(self._module, "no_log_values"):
|
||||
kubeconfig = self._module.params.get("kubeconfig")
|
||||
if kubeconfig and isinstance(kubeconfig, dict):
|
||||
# Add sensitive values to no_log_values to prevent them from appearing in logs
|
||||
self._module.no_log_values.update(
|
||||
extract_sensitive_values_from_kubeconfig(kubeconfig)
|
||||
)
|
||||
|
||||
if self.settings["check_k8s"]:
|
||||
self.requires("kubernetes")
|
||||
self.has_at_least("kubernetes", "24.2.0", warn=True)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
@@ -498,8 +498,8 @@ 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"] and list(
|
||||
result["before"].keys()
|
||||
@@ -512,9 +512,6 @@ def diff_objects(
|
||||
).issubset(ignored_keys):
|
||||
return True, result
|
||||
|
||||
result["before"] = hide_fields(result["before"], hidden_fields)
|
||||
result["after"] = hide_fields(result["after"], hidden_fields)
|
||||
|
||||
return False, result
|
||||
|
||||
|
||||
|
||||
91
plugins/module_utils/kubeconfig.py
Normal file
91
plugins/module_utils/kubeconfig.py
Normal 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
|
||||
@@ -21,7 +21,7 @@ author:
|
||||
- Matthieu Diehr (@d-matt)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
description:
|
||||
@@ -237,6 +237,27 @@ options:
|
||||
default: False
|
||||
aliases: [ skip_tls_certs_check ]
|
||||
version_added: 5.3.0
|
||||
plain_http:
|
||||
description:
|
||||
- Use HTTP instead of HTTPS when working with OCI registries
|
||||
- Requires Helm >= 3.13.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
take_ownership:
|
||||
description:
|
||||
- Helm will ignore the check for helm annotations and take ownership of the existing resources
|
||||
- This feature requires helm >= 3.17.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
skip_schema_validation:
|
||||
description:
|
||||
- Disables JSON schema validation for Chart and values.
|
||||
- This feature requires helm >= 3.16.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.2.0
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.helm_common_options
|
||||
"""
|
||||
@@ -319,6 +340,12 @@ EXAMPLES = r"""
|
||||
chart_ref: "https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz"
|
||||
release_namespace: monitoring
|
||||
|
||||
- name: Deploy Bitnami's MongoDB latest chart from OCI registry
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: "oci://registry-1.docker.io/bitnamicharts/mongodb"
|
||||
release_namespace: database
|
||||
|
||||
# Using complex Values
|
||||
- name: Deploy new-relic client chart
|
||||
kubernetes.core.helm:
|
||||
@@ -392,9 +419,18 @@ status:
|
||||
returned: always
|
||||
description: The Date of last update
|
||||
values:
|
||||
type: str
|
||||
type: dict
|
||||
returned: always
|
||||
description: Dict of Values used to deploy
|
||||
description:
|
||||
- Dict of Values used to deploy.
|
||||
- This return value has been deprecated and will be removed in a release after
|
||||
2027-01-08. Use RV(status.release_values) instead.
|
||||
release_values:
|
||||
type: dict
|
||||
returned: always
|
||||
description:
|
||||
- Dict of Values used to deploy.
|
||||
version_added: 6.3.0
|
||||
stdout:
|
||||
type: str
|
||||
description: Full `helm` command stdout, in case you want to display it or examine the event log
|
||||
@@ -464,9 +500,13 @@ def get_release_status(module, release_name, all_status=False):
|
||||
"--filter",
|
||||
release_name,
|
||||
]
|
||||
if all_status:
|
||||
if all_status and not module.is_helm_v4():
|
||||
# --all has been removed from `helm list` command on helm v4
|
||||
list_command.append("--all")
|
||||
|
||||
elif not all_status:
|
||||
# The default behavior to display only deployed releases has been removed from
|
||||
# Helm v4
|
||||
list_command.append("--deployed")
|
||||
rc, out, err = module.run_helm_command(list_command)
|
||||
|
||||
release = get_release(yaml.safe_load(out), release_name)
|
||||
@@ -474,7 +514,8 @@ def get_release_status(module, release_name, all_status=False):
|
||||
if release is None: # not install
|
||||
return None
|
||||
|
||||
release["values"] = module.get_values(release_name)
|
||||
release["release_values"] = module.get_values(release_name)
|
||||
release["values"] = release["release_values"]
|
||||
|
||||
return release
|
||||
|
||||
@@ -495,7 +536,9 @@ def run_dep_update(module, chart_ref):
|
||||
rc, out, err = module.run_helm_command(dep_update)
|
||||
|
||||
|
||||
def fetch_chart_info(module, command, chart_ref, insecure_skip_tls_verify=False):
|
||||
def fetch_chart_info(
|
||||
module, command, chart_ref, insecure_skip_tls_verify=False, plain_http=False
|
||||
):
|
||||
"""
|
||||
Get chart info
|
||||
"""
|
||||
@@ -504,6 +547,17 @@ def fetch_chart_info(module, command, chart_ref, insecure_skip_tls_verify=False)
|
||||
if insecure_skip_tls_verify:
|
||||
inspect_command += " --insecure-skip-tls-verify"
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
inspect_command += " --plain-http"
|
||||
|
||||
rc, out, err = module.run_helm_command(inspect_command)
|
||||
|
||||
return yaml.safe_load(out)
|
||||
@@ -533,6 +587,9 @@ def deploy(
|
||||
reset_values=True,
|
||||
reset_then_reuse_values=False,
|
||||
insecure_skip_tls_verify=False,
|
||||
plain_http=False,
|
||||
take_ownership=False,
|
||||
skip_schema_validation=False,
|
||||
):
|
||||
"""
|
||||
Install/upgrade/rollback release chart
|
||||
@@ -546,6 +603,8 @@ def deploy(
|
||||
deploy_command = command + " upgrade -i" # install/upgrade
|
||||
if reset_values:
|
||||
deploy_command += " --reset-values"
|
||||
if take_ownership:
|
||||
deploy_command += " --take-ownership"
|
||||
|
||||
if reuse_values is not None:
|
||||
deploy_command += " --reuse-values=" + str(reuse_values)
|
||||
@@ -595,6 +654,9 @@ def deploy(
|
||||
else:
|
||||
deploy_command += " --insecure-skip-tls-verify"
|
||||
|
||||
if plain_http:
|
||||
deploy_command += " --plain-http"
|
||||
|
||||
if values_files:
|
||||
for value_file in values_files:
|
||||
deploy_command += " --values=" + value_file
|
||||
@@ -618,6 +680,17 @@ def deploy(
|
||||
if set_value_args:
|
||||
deploy_command += " " + set_value_args
|
||||
|
||||
if skip_schema_validation:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
|
||||
module.fail_json(
|
||||
msg="skip_schema_validation requires helm >= 3.16.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
deploy_command += " --skip-schema-validation"
|
||||
|
||||
deploy_command += " " + release_name + f" '{chart_name}'"
|
||||
return deploy_command
|
||||
|
||||
@@ -670,8 +743,8 @@ def get_plugin_version(plugin):
|
||||
return None
|
||||
|
||||
for line in out:
|
||||
if line[0] == plugin:
|
||||
return line[1]
|
||||
if line["name"] == plugin:
|
||||
return line["version"]
|
||||
return None
|
||||
|
||||
|
||||
@@ -690,6 +763,8 @@ def helmdiff_check(
|
||||
reset_values=True,
|
||||
reset_then_reuse_values=False,
|
||||
insecure_skip_tls_verify=False,
|
||||
plain_http=False,
|
||||
skip_schema_validation=False,
|
||||
):
|
||||
"""
|
||||
Use helm diff to determine if a release would change by upgrading a chart.
|
||||
@@ -745,6 +820,28 @@ def helmdiff_check(
|
||||
if insecure_skip_tls_verify:
|
||||
cmd += " --insecure-skip-tls-verify"
|
||||
|
||||
if skip_schema_validation:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
|
||||
module.fail_json(
|
||||
msg="skip_schema_validation requires helm >= 3.16.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
cmd += " --skip-schema-validation"
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
cmd += " --plain-http"
|
||||
|
||||
rc, out, err = module.run_helm_command(cmd)
|
||||
return (len(out.strip()) > 0, out.strip())
|
||||
|
||||
@@ -808,6 +905,9 @@ def argument_spec():
|
||||
insecure_skip_tls_verify=dict(
|
||||
type="bool", default=False, aliases=["skip_tls_certs_check"]
|
||||
),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
take_ownership=dict(type="bool", default=False),
|
||||
skip_schema_validation=dict(type="bool", default=False),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
@@ -832,6 +932,9 @@ def main():
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
changed = False
|
||||
|
||||
chart_ref = module.params.get("chart_ref")
|
||||
@@ -862,6 +965,9 @@ def main():
|
||||
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")
|
||||
plain_http = module.params.get("plain_http")
|
||||
take_ownership = module.params.get("take_ownership")
|
||||
skip_schema_validation = module.params.get("skip_schema_validation")
|
||||
|
||||
if update_repo_cache:
|
||||
run_repo_update(module)
|
||||
@@ -871,6 +977,33 @@ def main():
|
||||
release_status = get_release_status(module, release_name, all_status=all_status)
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
if take_ownership:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.17.0"):
|
||||
module.fail_json(
|
||||
msg="take_ownership requires helm >= 3.17.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
|
||||
if skip_schema_validation:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
|
||||
module.fail_json(
|
||||
msg="skip_schema_validation requires helm >= 3.16.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
|
||||
opt_result = {}
|
||||
if release_state == "absent" and release_status is not None:
|
||||
# skip release statuses 'uninstalled' and 'uninstalling'
|
||||
@@ -881,8 +1014,7 @@ def main():
|
||||
if wait:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.7.0"):
|
||||
opt_result["warnings"] = []
|
||||
opt_result["warnings"].append(
|
||||
module.warn(
|
||||
"helm uninstall support option --wait for helm release >= 3.7.0"
|
||||
)
|
||||
wait = False
|
||||
@@ -900,7 +1032,7 @@ def main():
|
||||
|
||||
# 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, insecure_skip_tls_verify
|
||||
module, helm_cmd, chart_ref, insecure_skip_tls_verify, plain_http
|
||||
)
|
||||
|
||||
if dependency_update:
|
||||
@@ -962,19 +1094,29 @@ def main():
|
||||
reset_values=reset_values,
|
||||
reset_then_reuse_values=reset_then_reuse_values,
|
||||
insecure_skip_tls_verify=insecure_skip_tls_verify,
|
||||
plain_http=plain_http,
|
||||
take_ownership=take_ownership,
|
||||
skip_schema_validation=skip_schema_validation,
|
||||
)
|
||||
changed = True
|
||||
|
||||
else:
|
||||
helm_diff_version = get_plugin_version("diff")
|
||||
if helm_diff_version and (
|
||||
helm_version_compatible = module.is_helm_version_compatible_with_helm_diff(
|
||||
helm_diff_version
|
||||
)
|
||||
if (
|
||||
helm_diff_version
|
||||
and helm_version_compatible
|
||||
and (
|
||||
not chart_repo_url
|
||||
or (
|
||||
chart_repo_url
|
||||
and LooseVersion(helm_diff_version) >= LooseVersion("3.4.1")
|
||||
)
|
||||
)
|
||||
):
|
||||
(would_change, prepared) = helmdiff_check(
|
||||
would_change, prepared = helmdiff_check(
|
||||
module,
|
||||
release_name,
|
||||
chart_ref,
|
||||
@@ -989,9 +1131,19 @@ def main():
|
||||
reset_values=reset_values,
|
||||
reset_then_reuse_values=reset_then_reuse_values,
|
||||
insecure_skip_tls_verify=insecure_skip_tls_verify,
|
||||
plain_http=plain_http,
|
||||
skip_schema_validation=skip_schema_validation,
|
||||
)
|
||||
if would_change and module._diff:
|
||||
opt_result["diff"] = {"prepared": prepared}
|
||||
else:
|
||||
if helm_diff_version and not helm_version_compatible:
|
||||
module.warn(
|
||||
"Idempotency checks are currently disabled due to a version mismatch."
|
||||
f" Helm version {module.get_helm_version()} requires helm-diff >= 3.14.0,"
|
||||
f" but the environment is currently running {helm_diff_version}."
|
||||
" Please align the plugin versions to restore standard behavior."
|
||||
)
|
||||
else:
|
||||
module.warn(
|
||||
"The default idempotency check can fail to report changes in certain cases. "
|
||||
@@ -1026,14 +1178,22 @@ def main():
|
||||
reset_values=reset_values,
|
||||
reset_then_reuse_values=reset_then_reuse_values,
|
||||
insecure_skip_tls_verify=insecure_skip_tls_verify,
|
||||
plain_http=plain_http,
|
||||
take_ownership=take_ownership,
|
||||
skip_schema_validation=skip_schema_validation,
|
||||
)
|
||||
changed = True
|
||||
|
||||
if module.check_mode:
|
||||
check_status = {"values": {"current": {}, "declared": {}}}
|
||||
check_status = {
|
||||
"values": {"current": {}, "declared": {}},
|
||||
"release_values": {"current": {}, "declared": {}},
|
||||
}
|
||||
if release_status:
|
||||
check_status["values"]["current"] = release_status["values"]
|
||||
check_status["values"]["current"] = release_status["release_values"]
|
||||
check_status["values"]["declared"] = release_status
|
||||
check_status["release_values"]["current"] = release_status["release_values"]
|
||||
check_status["release_values"]["declared"] = release_status
|
||||
|
||||
module.exit_json(
|
||||
changed=changed,
|
||||
|
||||
@@ -20,7 +20,7 @@ author:
|
||||
- Lucas Boisserie (@LucasBoisserie)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
description:
|
||||
@@ -115,9 +115,18 @@ status:
|
||||
returned: always
|
||||
description: The Date of last update
|
||||
values:
|
||||
type: str
|
||||
type: dict
|
||||
returned: always
|
||||
description: Dict of Values used to deploy
|
||||
description:
|
||||
- Dict of Values used to deploy
|
||||
- This return value has been deprecated and will be removed in a release after
|
||||
2027-01-08. Use RV(status.release_values) instead.
|
||||
release_values:
|
||||
type: dict
|
||||
returned: always
|
||||
description:
|
||||
- Dict of Values used to deploy.
|
||||
version_added: 6.3.0
|
||||
hooks:
|
||||
type: list
|
||||
elements: dict
|
||||
@@ -202,7 +211,8 @@ def get_release_status(module, release_name, release_state, get_all_values=False
|
||||
if release is None: # not install
|
||||
return None
|
||||
|
||||
release["values"] = module.get_values(release_name, get_all_values)
|
||||
release["release_values"] = module.get_values(release_name, get_all_values)
|
||||
release["values"] = release["release_values"]
|
||||
release["manifest"] = module.get_manifest(release_name)
|
||||
release["notes"] = module.get_notes(release_name)
|
||||
release["hooks"] = module.get_hooks(release_name)
|
||||
@@ -235,6 +245,9 @@ def main():
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
release_name = module.params.get("release_name")
|
||||
release_state = module.params.get("release_state")
|
||||
get_all_values = module.params.get("get_all_values")
|
||||
|
||||
@@ -16,7 +16,7 @@ version_added: 1.0.0
|
||||
author:
|
||||
- Abhijeet Kasurde (@Akasurde)
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
description:
|
||||
- Manages Helm plugins.
|
||||
options:
|
||||
@@ -48,6 +48,14 @@ options:
|
||||
required: false
|
||||
type: str
|
||||
version_added: 2.3.0
|
||||
verify:
|
||||
description:
|
||||
- Verify the plugin signature before installing.
|
||||
- This option requires helm version >= 4.0.0
|
||||
- Used with I(state=present).
|
||||
type: bool
|
||||
default: true
|
||||
version_added: 6.4.0
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.helm_common_options
|
||||
"""
|
||||
@@ -118,6 +126,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common i
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
def argument_spec():
|
||||
@@ -138,6 +149,10 @@ def argument_spec():
|
||||
default="present",
|
||||
choices=["present", "absent", "latest"],
|
||||
),
|
||||
verify=dict(
|
||||
type="bool",
|
||||
default=True,
|
||||
),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
@@ -161,6 +176,9 @@ def main():
|
||||
mutually_exclusive=mutually_exclusive(),
|
||||
)
|
||||
|
||||
# Validate helm version >= 3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
state = module.params.get("state")
|
||||
|
||||
helm_cmd_common = module.get_helm_binary() + " plugin"
|
||||
@@ -168,8 +186,19 @@ def main():
|
||||
if state == "present":
|
||||
helm_cmd_common += " install %s" % module.params.get("plugin_path")
|
||||
plugin_version = module.params.get("plugin_version")
|
||||
verify = module.params.get("verify")
|
||||
if plugin_version is not None:
|
||||
helm_cmd_common += " --version=%s" % plugin_version
|
||||
if not verify:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("4.0.0"):
|
||||
module.warn(
|
||||
"verify parameter requires helm >= 4.0.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
helm_cmd_common += " --verify=false"
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_cmd_common, fails_on_error=False
|
||||
@@ -208,9 +237,9 @@ def main():
|
||||
elif state == "absent":
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
out = parse_helm_plugin_list(output=output.splitlines())
|
||||
plugins = parse_helm_plugin_list(output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
if not plugins:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
@@ -221,12 +250,7 @@ def main():
|
||||
rc=rc,
|
||||
)
|
||||
|
||||
found = False
|
||||
for line in out:
|
||||
if line[0] == plugin_name:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
if all(plugin["name"] != plugin_name for plugin in plugins):
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
@@ -264,9 +288,9 @@ def main():
|
||||
elif state == "latest":
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
out = parse_helm_plugin_list(output=output.splitlines())
|
||||
plugins = parse_helm_plugin_list(output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
if not plugins:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
@@ -277,12 +301,7 @@ def main():
|
||||
rc=rc,
|
||||
)
|
||||
|
||||
found = False
|
||||
for line in out:
|
||||
if line[0] == plugin_name:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
if all(plugin["name"] != plugin_name for plugin in plugins):
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
|
||||
@@ -16,7 +16,7 @@ version_added: 1.0.0
|
||||
author:
|
||||
- Abhijeet Kasurde (@Akasurde)
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
description:
|
||||
- Gather information about Helm plugins installed in namespace.
|
||||
options:
|
||||
@@ -98,26 +98,16 @@ def main():
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
# Validate helm version >= 3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
plugin_list = []
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
|
||||
out = parse_helm_plugin_list(output=output.splitlines())
|
||||
|
||||
for line in out:
|
||||
if plugin_name is None:
|
||||
plugin_list.append(
|
||||
{"name": line[0], "version": line[1], "description": line[2]}
|
||||
)
|
||||
continue
|
||||
|
||||
if plugin_name == line[0]:
|
||||
plugin_list.append(
|
||||
{"name": line[0], "version": line[1], "description": line[2]}
|
||||
)
|
||||
break
|
||||
plugins = parse_helm_plugin_list(output=output.splitlines())
|
||||
if plugin_name is not None:
|
||||
plugins = [plugin for plugin in plugins if plugin.get("name") == plugin_name]
|
||||
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
@@ -125,7 +115,7 @@ def main():
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
plugin_list=plugin_list,
|
||||
plugin_list=plugins,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ description:
|
||||
- There are options for unpacking the chart after download.
|
||||
|
||||
requirements:
|
||||
- "helm >= 3.0 (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
|
||||
options:
|
||||
chart_ref:
|
||||
@@ -89,6 +89,14 @@ options:
|
||||
- if set to true, will untar the chart after downloading it.
|
||||
type: bool
|
||||
default: False
|
||||
force:
|
||||
description:
|
||||
- Force download of the chart even if it already exists in the destination directory.
|
||||
- By default, the module will skip downloading if the chart with the same version already exists for idempotency.
|
||||
- When used with O(untar_chart=true), will remove any existing chart directory before extracting.
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.3.0
|
||||
destination:
|
||||
description:
|
||||
- location to write the chart.
|
||||
@@ -114,6 +122,13 @@ options:
|
||||
- The path of a helm binary to use.
|
||||
required: false
|
||||
type: path
|
||||
plain_http:
|
||||
description:
|
||||
- Use HTTP instead of HTTPS when working with OCI registries
|
||||
- Requires Helm >= 3.13.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -145,6 +160,23 @@ EXAMPLES = r"""
|
||||
destination: /path/to/chart
|
||||
username: myuser
|
||||
password: mypassword123
|
||||
|
||||
- name: Download Chart (force re-download even if exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
force: yes
|
||||
|
||||
- name: Download and untar chart (force re-extraction even if directory exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
untar_chart: yes
|
||||
force: yes
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
@@ -163,6 +195,11 @@ command:
|
||||
description: Full `helm pull` command built by this module, in case you want to re-run the command outside the module or debug a problem.
|
||||
returned: always
|
||||
sample: helm pull --repo test ...
|
||||
msg:
|
||||
type: str
|
||||
description: A message indicating the result of the operation.
|
||||
returned: when chart already exists
|
||||
sample: Chart redis version 17.0.0 already exists in destination directory
|
||||
rc:
|
||||
type: int
|
||||
description: Helm pull command return code
|
||||
@@ -170,6 +207,18 @@ rc:
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tarfile
|
||||
import uuid
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
HAS_YAML = True
|
||||
except ImportError:
|
||||
HAS_YAML = False
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
@@ -178,6 +227,115 @@ from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
)
|
||||
|
||||
|
||||
def extract_chart_name(chart_ref):
|
||||
"""
|
||||
Extract chart name from chart reference.
|
||||
|
||||
Args:
|
||||
chart_ref (str): Chart reference (name, URL, or OCI reference)
|
||||
|
||||
Returns:
|
||||
str: Extracted chart name
|
||||
"""
|
||||
chart_name = chart_ref.split("/")[-1]
|
||||
# Remove any query parameters or fragments from URL-based refs
|
||||
if "?" in chart_name:
|
||||
chart_name = chart_name.split("?")[0]
|
||||
if "#" in chart_name:
|
||||
chart_name = chart_name.split("#")[0]
|
||||
# Remove .tgz extension if present
|
||||
if chart_name.endswith(".tgz"):
|
||||
chart_name = chart_name[:-4]
|
||||
return chart_name
|
||||
|
||||
|
||||
def chart_exists(destination, chart_ref, chart_version, untar_chart):
|
||||
"""
|
||||
Check if the chart already exists in the destination directory.
|
||||
|
||||
For untarred charts: check if directory exists with Chart.yaml matching version
|
||||
For tarred charts: check if .tgz file exists and contains matching version
|
||||
|
||||
Args:
|
||||
destination (str): Destination directory path
|
||||
chart_ref (str): Chart reference (name or URL)
|
||||
chart_version (str): Chart version to check for
|
||||
untar_chart (bool): Whether to check for untarred or tarred chart
|
||||
|
||||
Returns:
|
||||
bool: True if chart with matching version exists, False otherwise
|
||||
"""
|
||||
# YAML is required for version checking
|
||||
if not HAS_YAML:
|
||||
return False
|
||||
|
||||
# Without version, we can't reliably check
|
||||
if not chart_version:
|
||||
return False
|
||||
|
||||
# Extract chart name from chart_ref using shared helper
|
||||
chart_name = extract_chart_name(chart_ref)
|
||||
|
||||
if untar_chart:
|
||||
# Check for extracted directory
|
||||
chart_dir = os.path.join(destination, chart_name)
|
||||
chart_yaml_path = os.path.join(chart_dir, "Chart.yaml")
|
||||
|
||||
if os.path.isdir(chart_dir) and os.path.isfile(chart_yaml_path):
|
||||
try:
|
||||
with open(chart_yaml_path, "r", encoding="utf-8") as chart_file:
|
||||
chart_metadata = yaml.safe_load(chart_file)
|
||||
# Ensure chart_metadata is a dict and has a version that matches
|
||||
if (
|
||||
chart_metadata
|
||||
and isinstance(chart_metadata, dict)
|
||||
and chart_metadata.get("version") == chart_version
|
||||
and chart_metadata.get("name") == chart_name
|
||||
):
|
||||
return True
|
||||
except (yaml.YAMLError, IOError, OSError, TypeError):
|
||||
# If we can't read or parse the file, treat as non-existent
|
||||
pass
|
||||
else:
|
||||
# Check for .tgz file
|
||||
chart_file = os.path.join(destination, f"{chart_name}-{chart_version}.tgz")
|
||||
|
||||
if os.path.isfile(chart_file):
|
||||
try:
|
||||
# Verify it's a valid tarball with matching version
|
||||
with tarfile.open(chart_file, "r:gz") as tar:
|
||||
# Try to extract Chart.yaml to verify version
|
||||
# Look for Chart.yaml at the expected path: <chart-name>/Chart.yaml
|
||||
expected_chart_yaml = f"{chart_name}/Chart.yaml"
|
||||
try:
|
||||
member = tar.getmember(expected_chart_yaml)
|
||||
chart_yaml_file = tar.extractfile(member)
|
||||
if chart_yaml_file:
|
||||
try:
|
||||
chart_metadata = yaml.safe_load(chart_yaml_file)
|
||||
# Ensure chart_metadata is a dict and has a version that matches
|
||||
if (
|
||||
chart_metadata
|
||||
and isinstance(chart_metadata, dict)
|
||||
and chart_metadata.get("version") == chart_version
|
||||
and chart_metadata.get("name") == chart_name
|
||||
):
|
||||
return True
|
||||
except (yaml.YAMLError, TypeError):
|
||||
# If we can't parse the YAML, treat as non-existent
|
||||
pass
|
||||
finally:
|
||||
chart_yaml_file.close()
|
||||
except KeyError:
|
||||
# Chart.yaml not found at expected path
|
||||
pass
|
||||
except (tarfile.TarError, yaml.YAMLError, IOError, OSError, TypeError):
|
||||
# If we can't read or parse the tarball, treat as non-existent
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
argspec = dict(
|
||||
chart_ref=dict(type="str", required=True),
|
||||
@@ -196,11 +354,13 @@ def main():
|
||||
),
|
||||
chart_devel=dict(type="bool"),
|
||||
untar_chart=dict(type="bool", default=False),
|
||||
force=dict(type="bool", default=False),
|
||||
destination=dict(type="path", required=True),
|
||||
chart_ca_cert=dict(type="path"),
|
||||
chart_ssl_cert_file=dict(type="path"),
|
||||
chart_ssl_key_file=dict(type="path"),
|
||||
binary_path=dict(type="path"),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
)
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argspec,
|
||||
@@ -212,19 +372,17 @@ def main():
|
||||
mutually_exclusive=[("chart_version", "chart_devel")],
|
||||
)
|
||||
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.0.0"):
|
||||
module.fail_json(
|
||||
msg="This module requires helm >= 3.0.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
|
||||
helm_pull_opt_versionning = dict(
|
||||
skip_tls_certs_check="3.3.0",
|
||||
chart_ca_cert="3.1.0",
|
||||
chart_ssl_cert_file="3.1.0",
|
||||
chart_ssl_key_file="3.1.0",
|
||||
plain_http="3.13.0",
|
||||
)
|
||||
|
||||
def test_version_requirement(opt):
|
||||
@@ -264,6 +422,7 @@ def main():
|
||||
skip_tls_certs_check=dict(key="insecure-skip-tls-verify"),
|
||||
chart_devel=dict(key="devel"),
|
||||
untar_chart=dict(key="untar"),
|
||||
plain_http=dict(key="plain-http"),
|
||||
)
|
||||
|
||||
for k, v in helm_flag_args.items():
|
||||
@@ -276,8 +435,72 @@ def main():
|
||||
module.params.get("chart_ref"),
|
||||
" ".join(helm_pull_opts),
|
||||
)
|
||||
|
||||
# Check if chart already exists (idempotency)
|
||||
if module.params.get("chart_version") and not module.params.get("force"):
|
||||
chart_exists_locally = chart_exists(
|
||||
module.params.get("destination"),
|
||||
module.params.get("chart_ref"),
|
||||
module.params.get("chart_version"),
|
||||
module.params.get("untar_chart"),
|
||||
)
|
||||
|
||||
if chart_exists_locally:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Chart {0} version {1} already exists in destination directory".format(
|
||||
module.params.get("chart_ref"), module.params.get("chart_version")
|
||||
),
|
||||
command="",
|
||||
stdout="",
|
||||
stderr="",
|
||||
rc=0,
|
||||
)
|
||||
|
||||
# When both untar_chart and force are enabled, we need to remove the existing chart directory
|
||||
# BEFORE running helm pull to prevent helm's "directory already exists" error.
|
||||
# We do this by:
|
||||
# 1. Renaming the existing directory to a temporary name (if it exists)
|
||||
# 2. Running helm pull
|
||||
# 3. On success: remove the temporary directory
|
||||
# 4. On failure: restore the temporary directory and report the error
|
||||
chart_dir_renamed = False
|
||||
chart_dir = None
|
||||
chart_dir_backup = None
|
||||
|
||||
if module.params.get("untar_chart") and module.params.get("force"):
|
||||
chart_name = extract_chart_name(module.params.get("chart_ref"))
|
||||
chart_dir = os.path.join(module.params.get("destination"), chart_name)
|
||||
|
||||
# Check if directory exists and contains a Chart.yaml (to be safe)
|
||||
if os.path.isdir(chart_dir):
|
||||
chart_yaml_path = os.path.join(chart_dir, "Chart.yaml")
|
||||
# Only rename if it looks like a Helm chart directory (have Chart.yaml)
|
||||
if os.path.isfile(chart_yaml_path):
|
||||
if not module.check_mode:
|
||||
# Rename to temporary backup name using uuid for uniqueness
|
||||
backup_suffix = uuid.uuid4().hex[:8]
|
||||
chart_dir_backup = os.path.join(
|
||||
module.params.get("destination"),
|
||||
f".{chart_name}_backup_{backup_suffix}",
|
||||
)
|
||||
os.rename(chart_dir, chart_dir_backup)
|
||||
chart_dir_renamed = True
|
||||
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_helm_command(helm_cmd_common, fails_on_error=False)
|
||||
|
||||
# Handle cleanup/restore based on helm command result
|
||||
if chart_dir_renamed:
|
||||
if rc == 0:
|
||||
# Success: remove the backup directory
|
||||
if os.path.isdir(chart_dir_backup):
|
||||
shutil.rmtree(chart_dir_backup)
|
||||
else:
|
||||
# Failure: restore the backup directory
|
||||
if os.path.isdir(chart_dir_backup) and not os.path.exists(chart_dir):
|
||||
os.rename(chart_dir_backup, chart_dir)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ author:
|
||||
- Yuriy Novostavskiy (@yurnov)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases) => 3.8.0"
|
||||
- "helm (https://github.com/helm/helm/releases) >= 3.8.0"
|
||||
|
||||
description:
|
||||
- Helm registry authentication module allows you to login C(helm registry login) and logout C(helm registry logout) from a Helm registry.
|
||||
@@ -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']
|
||||
@@ -74,6 +75,14 @@ options:
|
||||
- Path to the CA certificate SSL file for verify registry server certificate.
|
||||
required: false
|
||||
type: path
|
||||
plain_http:
|
||||
description:
|
||||
- Use insecure HTTP connections for C(helm registry login).
|
||||
- Requires Helm >= 3.18.0
|
||||
required: false
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.4.0
|
||||
binary_path:
|
||||
description:
|
||||
- The path of a helm binary to use.
|
||||
@@ -129,6 +138,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():
|
||||
@@ -144,6 +156,7 @@ def arg_spec():
|
||||
key_file=dict(type="path", required=False),
|
||||
cert_file=dict(type="path", required=False),
|
||||
ca_file=dict(type="path", required=False),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
)
|
||||
|
||||
|
||||
@@ -156,6 +169,7 @@ def login(
|
||||
key_file,
|
||||
cert_file,
|
||||
ca_file,
|
||||
plain_http,
|
||||
):
|
||||
login_command = command + " registry login " + host
|
||||
|
||||
@@ -173,6 +187,8 @@ def login(
|
||||
|
||||
if ca_file is not None:
|
||||
login_command += " --ca-file=" + ca_file
|
||||
if plain_http:
|
||||
login_command += " --plain-http"
|
||||
|
||||
return login_command
|
||||
|
||||
@@ -190,6 +206,9 @@ def main():
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
# Validate Helm version >=3.8.0
|
||||
module.validate_helm_version(version="3.8.0")
|
||||
|
||||
changed = False
|
||||
|
||||
host = module.params.get("host")
|
||||
@@ -200,6 +219,19 @@ def main():
|
||||
key_file = module.params.get("key_file")
|
||||
cert_file = module.params.get("cert_file")
|
||||
ca_file = module.params.get("ca_file")
|
||||
plain_http = module.params.get("plain_http")
|
||||
|
||||
helm_version = module.get_helm_version()
|
||||
|
||||
if plain_http:
|
||||
if LooseVersion(helm_version) < LooseVersion("3.18.0"):
|
||||
module.warn(
|
||||
"plain_http option requires helm >= 3.18.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
# reset option
|
||||
plain_http = False
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
|
||||
@@ -208,7 +240,15 @@ def main():
|
||||
changed = True
|
||||
elif state == "present":
|
||||
helm_cmd = login(
|
||||
helm_cmd, host, insecure, username, password, key_file, cert_file, ca_file
|
||||
helm_cmd,
|
||||
host,
|
||||
insecure,
|
||||
username,
|
||||
password,
|
||||
key_file,
|
||||
cert_file,
|
||||
ca_file,
|
||||
plain_http,
|
||||
)
|
||||
changed = True
|
||||
|
||||
@@ -231,6 +271,12 @@ def main():
|
||||
command=helm_cmd,
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ author:
|
||||
- Lucas Boisserie (@LucasBoisserie)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
description:
|
||||
@@ -295,6 +295,9 @@ def main():
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
# Validate Helm version >= 3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
changed = False
|
||||
|
||||
repo_name = module.params.get("repo_name")
|
||||
|
||||
@@ -21,6 +21,10 @@ author:
|
||||
description:
|
||||
- Render chart templates to an output directory or as text of concatenated yaml documents.
|
||||
|
||||
requirements:
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
options:
|
||||
binary_path:
|
||||
description:
|
||||
@@ -147,6 +151,13 @@ options:
|
||||
- json
|
||||
- file
|
||||
version_added: 2.4.0
|
||||
plain_http:
|
||||
description:
|
||||
- Use HTTP instead of HTTPS when working with OCI registries
|
||||
- Requires Helm >= 3.13.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -218,6 +229,9 @@ from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
def template(
|
||||
@@ -236,6 +250,7 @@ def template(
|
||||
values_files=None,
|
||||
include_crds=False,
|
||||
set_values=None,
|
||||
plain_http=False,
|
||||
):
|
||||
cmd += " template "
|
||||
|
||||
@@ -262,6 +277,9 @@ def template(
|
||||
if insecure_registry:
|
||||
cmd += " --insecure-skip-tls-verify"
|
||||
|
||||
if plain_http:
|
||||
cmd += " --plain-http"
|
||||
|
||||
if show_only:
|
||||
for template in show_only:
|
||||
cmd += " -s " + template
|
||||
@@ -307,6 +325,7 @@ def main():
|
||||
values_files=dict(type="list", default=[], elements="str"),
|
||||
update_repo_cache=dict(type="bool", default=False),
|
||||
set_values=dict(type="list", elements="dict"),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
@@ -327,12 +346,25 @@ def main():
|
||||
values_files = module.params.get("values_files")
|
||||
update_repo_cache = module.params.get("update_repo_cache")
|
||||
set_values = module.params.get("set_values")
|
||||
plain_http = module.params.get("plain_http")
|
||||
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
|
||||
if update_repo_cache:
|
||||
update_cmd = helm_cmd + " repo update"
|
||||
module.run_helm_command(update_cmd)
|
||||
@@ -357,6 +389,7 @@ def main():
|
||||
values_files=values_files,
|
||||
include_crds=include_crds,
|
||||
set_values=set_values_args,
|
||||
plain_http=plain_http,
|
||||
)
|
||||
|
||||
if not check_mode:
|
||||
|
||||
@@ -383,28 +383,24 @@ result:
|
||||
contains:
|
||||
api_version:
|
||||
description: The versioned schema of this representation of an object.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
kind:
|
||||
description: Represents the REST resource this object represents.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
metadata:
|
||||
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
spec:
|
||||
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
status:
|
||||
description: Current status details for the object.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
items:
|
||||
description: Returned only when multiple yaml documents are passed to src or resource_definition
|
||||
returned: when resource_definition or src contains list of objects
|
||||
type: list
|
||||
duration:
|
||||
description: elapsed time of task in seconds
|
||||
returned: when C(wait) is true
|
||||
@@ -414,6 +410,46 @@ result:
|
||||
description: error while trying to create/delete the object.
|
||||
returned: error
|
||||
type: complex
|
||||
results:
|
||||
description: An array of created, patched, or otherwise present objects.
|
||||
returned: when O(resource_definition) or O(src) contains a list of objects.
|
||||
type: complex
|
||||
contains:
|
||||
api_version:
|
||||
description: The versioned schema of this representation of an object.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
kind:
|
||||
description: Represents the REST resource this object represents.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
metadata:
|
||||
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
spec:
|
||||
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
status:
|
||||
description: Current status details for the object.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
duration:
|
||||
description: elapsed time of task in seconds
|
||||
returned: when C(wait) is true
|
||||
type: int
|
||||
sample: 48
|
||||
error:
|
||||
description: error while trying to create/delete the object.
|
||||
returned: error
|
||||
type: complex
|
||||
method:
|
||||
description:
|
||||
- The method used to deploy the resource.
|
||||
returned: success
|
||||
type: str
|
||||
sample: create
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
@@ -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"""
|
||||
@@ -139,7 +140,7 @@ result:
|
||||
|
||||
import copy
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
|
||||
@@ -149,7 +149,7 @@ import time
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
@@ -230,7 +230,7 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
else:
|
||||
to_delete.append((pod.metadata.namespace, pod.metadata.name))
|
||||
|
||||
warnings, errors = [], []
|
||||
warnings, errors, info = [], [], []
|
||||
if unmanaged:
|
||||
pod_names = ",".join([pod[0] + "/" + pod[1] for pod in unmanaged])
|
||||
if not force:
|
||||
@@ -242,7 +242,7 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
)
|
||||
else:
|
||||
# Pod not managed will be deleted as 'force' is true
|
||||
warnings.append(
|
||||
info.append(
|
||||
"Deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: {0}.".format(
|
||||
pod_names
|
||||
)
|
||||
@@ -264,7 +264,7 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
"cannot delete Pods with local storage: {0}.".format(pod_names)
|
||||
)
|
||||
else:
|
||||
warnings.append("Deleting Pods with local storage: {0}.".format(pod_names))
|
||||
info.append("Deleting Pods with local storage: {0}.".format(pod_names))
|
||||
for pod in localStorage:
|
||||
to_delete.append((pod[0], pod[1]))
|
||||
|
||||
@@ -278,8 +278,8 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
)
|
||||
)
|
||||
else:
|
||||
warnings.append("Ignoring DaemonSet-managed Pods: {0}.".format(pod_names))
|
||||
return to_delete, warnings, errors
|
||||
info.append("Ignoring DaemonSet-managed Pods: {0}.".format(pod_names))
|
||||
return to_delete, warnings, errors, info
|
||||
|
||||
|
||||
class K8sDrainAnsible(object):
|
||||
@@ -334,6 +334,7 @@ class K8sDrainAnsible(object):
|
||||
def evict_pods(self, pods):
|
||||
for namespace, name in pods:
|
||||
try:
|
||||
if not self._module.check_mode:
|
||||
if self._drain_options.get("disable_eviction"):
|
||||
self._api_instance.delete_namespaced_pod(
|
||||
name=name, namespace=namespace, body=self._delete_options
|
||||
@@ -362,11 +363,7 @@ class K8sDrainAnsible(object):
|
||||
)
|
||||
|
||||
def list_pods(self):
|
||||
params = {
|
||||
"field_selector": "spec.nodeName={name}".format(
|
||||
name=self._module.params.get("name")
|
||||
)
|
||||
}
|
||||
params = {"field_selector": "spec.nodeName=" + self._module.params.get("name")}
|
||||
pod_selectors = self._module.params.get("pod_selectors")
|
||||
if pod_selectors:
|
||||
params["label_selector"] = ",".join(pod_selectors)
|
||||
@@ -376,6 +373,7 @@ class K8sDrainAnsible(object):
|
||||
# Mark node as unschedulable
|
||||
result = []
|
||||
if not node_unschedulable:
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=True)
|
||||
result.append(
|
||||
"node {0} marked unschedulable.".format(self._module.params.get("name"))
|
||||
@@ -391,6 +389,7 @@ class K8sDrainAnsible(object):
|
||||
def _revert_node_patch():
|
||||
if self._changed:
|
||||
self._changed = False
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=False)
|
||||
|
||||
try:
|
||||
@@ -401,7 +400,7 @@ class K8sDrainAnsible(object):
|
||||
delete_emptydir_data = self._drain_options.get(
|
||||
"delete_emptydir_data", False
|
||||
)
|
||||
pods, warnings, errors = filter_pods(
|
||||
pods, warnings, errors, info = filter_pods(
|
||||
pod_list.items, force, ignore_daemonset, delete_emptydir_data
|
||||
)
|
||||
if errors:
|
||||
@@ -431,17 +430,25 @@ class K8sDrainAnsible(object):
|
||||
if pods:
|
||||
self.evict_pods(pods)
|
||||
number_pod = len(pods)
|
||||
if self._drain_options.get("wait_timeout") is not None:
|
||||
warn = self.wait_for_pod_deletion(
|
||||
pods,
|
||||
self._drain_options.get("wait_timeout"),
|
||||
self._drain_options.get("wait_sleep"),
|
||||
if self._module.check_mode:
|
||||
result.append(
|
||||
"Would have deleted {0} Pod(s) from node if not in check mode.".format(
|
||||
number_pod
|
||||
)
|
||||
)
|
||||
else:
|
||||
wait_timeout = self._drain_options.get("wait_timeout")
|
||||
wait_sleep = self._drain_options.get("wait_sleep")
|
||||
if wait_timeout is not None:
|
||||
warn = self.wait_for_pod_deletion(pods, wait_timeout, wait_sleep)
|
||||
if warn:
|
||||
warnings.append(warn)
|
||||
result.append("{0} Pod(s) deleted from node.".format(number_pod))
|
||||
if warnings:
|
||||
return dict(result=" ".join(result), warnings=warnings)
|
||||
for warning in warnings:
|
||||
self._module.warn(warning)
|
||||
for line in info:
|
||||
self._module.debug(line)
|
||||
return dict(result=" ".join(result))
|
||||
|
||||
def patch_node(self, unschedulable):
|
||||
@@ -482,6 +489,7 @@ class K8sDrainAnsible(object):
|
||||
self._module.exit_json(
|
||||
result="node {0} already marked unschedulable.".format(name)
|
||||
)
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=True)
|
||||
result["result"] = "node {0} marked unschedulable.".format(name)
|
||||
self._changed = True
|
||||
@@ -491,6 +499,7 @@ class K8sDrainAnsible(object):
|
||||
self._module.exit_json(
|
||||
result="node {0} already marked schedulable.".format(name)
|
||||
)
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=False)
|
||||
result["result"] = "node {0} marked schedulable.".format(name)
|
||||
self._changed = True
|
||||
@@ -534,7 +543,9 @@ def argspec():
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(module_class=AnsibleModule, argument_spec=argspec())
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
)
|
||||
|
||||
if not HAS_EVICTION_API:
|
||||
module.fail_json(
|
||||
|
||||
@@ -131,7 +131,7 @@ except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -33,6 +33,14 @@ options:
|
||||
aliases:
|
||||
- api
|
||||
- version
|
||||
hidden_fields:
|
||||
description:
|
||||
- List of fields to hide from the diff output.
|
||||
- This is useful for fields that are not relevant to the patch operation, such as `metadata.managedFields`.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
version_added: 6.1.0
|
||||
kind:
|
||||
description:
|
||||
- Use to specify an object model.
|
||||
@@ -127,8 +135,8 @@ error:
|
||||
import copy
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
@@ -147,6 +155,7 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions imp
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
diff_objects,
|
||||
hide_fields,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
|
||||
get_waiter,
|
||||
@@ -174,6 +183,7 @@ JSON_PATCH_ARGS = {
|
||||
"namespace": {"type": "str"},
|
||||
"name": {"type": "str", "required": True},
|
||||
"patch": {"type": "list", "required": True, "elements": "dict"},
|
||||
"hidden_fields": {"type": "list", "elements": "str", "default": []},
|
||||
}
|
||||
|
||||
|
||||
@@ -203,6 +213,7 @@ def execute_module(module, client):
|
||||
namespace = module.params.get("namespace")
|
||||
patch = module.params.get("patch")
|
||||
|
||||
hidden_fields = module.params.get("hidden_fields")
|
||||
wait = module.params.get("wait")
|
||||
wait_sleep = module.params.get("wait_sleep")
|
||||
wait_timeout = module.params.get("wait_timeout")
|
||||
@@ -260,13 +271,13 @@ def execute_module(module, client):
|
||||
module.fail_json(msg=msg, error=to_native(exc), status="", reason="")
|
||||
|
||||
success = True
|
||||
result = {"result": obj}
|
||||
result = {"result": hide_fields(obj, hidden_fields)}
|
||||
if wait and not module.check_mode:
|
||||
waiter = get_waiter(client, resource, condition=wait_condition)
|
||||
success, result["result"], result["duration"] = waiter.wait(
|
||||
wait_timeout, wait_sleep, name, namespace
|
||||
)
|
||||
match, diffs = diff_objects(existing.to_dict(), obj)
|
||||
match, diffs = diff_objects(existing.to_dict(), obj, hidden_fields)
|
||||
result["changed"] = not match
|
||||
if module._diff:
|
||||
result["diff"] = diffs
|
||||
|
||||
@@ -168,7 +168,9 @@ def perform_action(svc, resource):
|
||||
module.params["kind"],
|
||||
resource["metadata"]["name"],
|
||||
)
|
||||
result = {"changed": False, "warnings": [warn]}
|
||||
if warn:
|
||||
module.warn(warn)
|
||||
result = {"changed": False}
|
||||
return result
|
||||
|
||||
if module.params["kind"] == "Deployment":
|
||||
|
||||
@@ -149,7 +149,7 @@ except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
@@ -243,10 +243,12 @@ def execute_module(client, module):
|
||||
module.fail_json(msg=error, **return_attributes)
|
||||
|
||||
def _continue_or_exit(warn):
|
||||
if warn:
|
||||
module.warn(warn)
|
||||
if multiple_scale:
|
||||
return_attributes["results"].append({"warning": warn, "changed": False})
|
||||
return_attributes["results"].append({"changed": False})
|
||||
else:
|
||||
module.exit_json(warning=warn, **return_attributes)
|
||||
module.exit_json(**return_attributes)
|
||||
|
||||
for existing in existing_items:
|
||||
if kind.lower() == "job":
|
||||
|
||||
@@ -126,7 +126,7 @@ result:
|
||||
|
||||
import copy
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
|
||||
441
plugins/modules/kubeconfig.py
Normal file
441
plugins/modules/kubeconfig.py
Normal 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
14
sonar-project.properties
Normal 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/**
|
||||
@@ -1,4 +1 @@
|
||||
time=100
|
||||
helm_info
|
||||
helm_repository
|
||||
helm_template
|
||||
disabled # used by test targets helm_vX_XX_XX
|
||||
|
||||
@@ -9,23 +9,25 @@ chart_test_version: 4.2.4
|
||||
chart_test_version_local_path: 1.32.0
|
||||
chart_test_version_upgrade: 4.2.5
|
||||
chart_test_version_upgrade_local_path: 1.33.0
|
||||
chart_test_repo: "https://kubernetes.github.io/ingress-nginx"
|
||||
chart_test_repo: "https://stenic.github.io/k8status/"
|
||||
chart_test_git_repo: "http://github.com/helm/charts.git"
|
||||
chart_test_values:
|
||||
revisionHistoryLimit: 0
|
||||
myValue: "changed"
|
||||
|
||||
test_namespace:
|
||||
- "helm-test-crds"
|
||||
- "helm-uninstall"
|
||||
- "helm-read-envvars"
|
||||
- "helm-dep-update"
|
||||
- "helm-local-path-001"
|
||||
- "helm-local-path-002"
|
||||
- "helm-local-path-003"
|
||||
- "helm-from-repository"
|
||||
- "helm-from-url"
|
||||
- "helm-reuse-values"
|
||||
- "helm-chart-with-space-into-name"
|
||||
- "helm-reset-then-reuse-values"
|
||||
- "helm-insecure"
|
||||
- "helm-test-crds-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-uninstall-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-read-envvars-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-dep-update-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-local-path-001-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-local-path-002-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-local-path-003-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-from-repository-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-from-url-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-reuse-values-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-chart-with-space-into-name-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-reset-then-reuse-values-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-insecure-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-test-take-ownership-{{ helm_version | replace('.', '-') }}"
|
||||
- "helm-skip-schema-validation-{{ helm_version | replace('.', '-') }}"
|
||||
|
||||
@@ -52,7 +52,9 @@ import json
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
|
||||
|
||||
class HelmReleaseNotFoundError(Exception):
|
||||
@@ -60,7 +62,9 @@ class HelmReleaseNotFoundError(Exception):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
def create_pending_install_release(helm_binary, chart_ref, chart_release, namespace):
|
||||
def create_pending_install_release(
|
||||
module, helm_binary, chart_ref, chart_release, namespace
|
||||
):
|
||||
# create pending-install release
|
||||
command = [
|
||||
helm_binary,
|
||||
@@ -78,13 +82,14 @@ def create_pending_install_release(helm_binary, chart_ref, chart_release, namesp
|
||||
command = [
|
||||
helm_binary,
|
||||
"list",
|
||||
"--all",
|
||||
"--output=json",
|
||||
"--namespace",
|
||||
namespace,
|
||||
"--filter",
|
||||
chart_release,
|
||||
]
|
||||
if not module.is_helm_v4():
|
||||
command.append("--all")
|
||||
cmd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = cmd.communicate()
|
||||
|
||||
@@ -92,11 +97,11 @@ def create_pending_install_release(helm_binary, chart_ref, chart_release, namesp
|
||||
if not data:
|
||||
error = "Release %s not found." % chart_release
|
||||
raise HelmReleaseNotFoundError(message=error)
|
||||
return data[0]["status"] == "pending-install", data[0]["status"]
|
||||
return data[0]["status"] in ("pending-install", "failed"), data[0]["status"]
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path", required=True),
|
||||
chart_ref=dict(type="str", required=True),
|
||||
@@ -106,6 +111,7 @@ def main():
|
||||
)
|
||||
|
||||
params = dict(
|
||||
module=module,
|
||||
helm_binary=module.params.get("binary_path"),
|
||||
chart_release=module.params.get("chart_release"),
|
||||
chart_ref=module.params.get("chart_ref"),
|
||||
@@ -116,7 +122,7 @@ def main():
|
||||
result, status = create_pending_install_release(**params)
|
||||
if not result:
|
||||
module.fail_json(
|
||||
msg="unable to create pending-install release, current status is %s"
|
||||
msg="unable to create pending-install/failed release, current status is %s"
|
||||
% status
|
||||
)
|
||||
module.exit_json(changed=True, msg="Release created with status '%s'" % status)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
---
|
||||
collections:
|
||||
- kubernetes.core
|
||||
dependencies:
|
||||
- remove_namespace
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
- connection: local
|
||||
gather_facts: true
|
||||
hosts: localhost
|
||||
|
||||
roles:
|
||||
- helm
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
- name: Init Helm folders
|
||||
file:
|
||||
path: /tmp/helm/
|
||||
state: directory
|
||||
|
||||
- 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
|
||||
@@ -1,8 +1,22 @@
|
||||
---
|
||||
- name: Ensure helm is not installed
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "/tmp/helm"
|
||||
|
||||
- name: Check failed if helm is not installed
|
||||
include_tasks: test_helm_not_installed.yml
|
||||
|
||||
- name: Install Helm v4
|
||||
ansible.builtin.include_role:
|
||||
name: install_helm
|
||||
vars:
|
||||
helm_version: v3.6.0
|
||||
|
||||
- name: Test helm uninstall
|
||||
ansible.builtin.include_tasks: test_helm_uninstall.yml
|
||||
|
||||
- name: Run tests
|
||||
include_tasks: run_test.yml
|
||||
loop_control:
|
||||
loop_var: helm_version
|
||||
with_items:
|
||||
- "v3.15.4"
|
||||
- "v3.16.0"
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
---
|
||||
- name: Ensure helm is not installed
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "/tmp/helm"
|
||||
|
||||
- name: Check failed if helm is not installed
|
||||
include_tasks: test_helm_not_installed.yml
|
||||
|
||||
- name: "Install {{ helm_version }}"
|
||||
include_role:
|
||||
name: install_helm
|
||||
|
||||
- name: "Ensure we honor the environment variables"
|
||||
- name: Main helm tests
|
||||
block:
|
||||
- name: Install helm-diff plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_path: https://github.com/databus23/helm-diff
|
||||
plugin_version: "{{ helm_version is version('v4.0.0', '>=') | ternary('v3.14.0', 'v3.10.0') }}"
|
||||
verify: false
|
||||
|
||||
- name: "Ensure we honor the environment variables"
|
||||
include_tasks: test_read_envvars.yml
|
||||
|
||||
- name: Deploy charts
|
||||
- name: Deploy charts
|
||||
include_tasks: "tests_chart/{{ test_chart_type }}.yml"
|
||||
loop_control:
|
||||
loop_var: test_chart_type
|
||||
@@ -25,31 +24,40 @@
|
||||
- from_repository
|
||||
- from_url
|
||||
|
||||
- name: test helm upgrade with reuse_values
|
||||
- name: test helm upgrade with reuse_values
|
||||
include_tasks: test_helm_reuse_values.yml
|
||||
|
||||
- name: test helm upgrade with reset_then_reuse_values
|
||||
- name: test helm upgrade with reset_then_reuse_values
|
||||
include_tasks: test_helm_reset_then_reuse_values.yml
|
||||
|
||||
- name: test helm dependency update
|
||||
- name: test helm dependency update
|
||||
include_tasks: test_up_dep.yml
|
||||
|
||||
- name: Test helm uninstall
|
||||
include_tasks: test_helm_uninstall.yml
|
||||
|
||||
- name: Test helm install with chart name containing space
|
||||
- name: Test helm install with chart name containing space
|
||||
include_tasks: test_helm_with_space_into_chart_name.yml
|
||||
|
||||
# https://github.com/ansible-collections/community.kubernetes/issues/296
|
||||
- name: Test Skip CRDS feature in helm chart install
|
||||
# https://github.com/ansible-collections/community.kubernetes/issues/296
|
||||
- name: Test Skip CRDS feature in helm chart install
|
||||
include_tasks: test_crds.yml
|
||||
|
||||
- name: Test insecure registry flag feature
|
||||
- name: Test insecure registry flag feature
|
||||
include_tasks: test_helm_insecure.yml
|
||||
|
||||
- name: Clean helm install
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
- name: Test take ownership flag feature
|
||||
include_tasks: test_helm_take_ownership.yml
|
||||
|
||||
- name: Test helm skip_schema_validation
|
||||
include_tasks: test_skip_schema_validation.yml
|
||||
|
||||
always:
|
||||
- name: Remove helm-diff plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_name: diff
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
|
||||
- name: Clean helm install
|
||||
ansible.builtin.file:
|
||||
path: "/tmp/helm/"
|
||||
state: absent
|
||||
with_items:
|
||||
- "/tmp/helm/"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
name: test
|
||||
chart_ref: "{{ chart_test }}"
|
||||
namespace: "helm-test"
|
||||
ignore_errors: yes
|
||||
ignore_errors: true
|
||||
register: helm_missing_binary
|
||||
|
||||
- name: Assert that helm is not installed
|
||||
@@ -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', '>=')
|
||||
|
||||
@@ -36,7 +36,29 @@
|
||||
that:
|
||||
- install is changed
|
||||
- '"--reset-then-reuse-values" not in install.command'
|
||||
- release_value["status"]["values"] == chart_release_values
|
||||
- release_value["status"]["release_values"] == chart_release_values
|
||||
|
||||
# We need to provide the actual redis password otherwise the update command
|
||||
# will fail with the following:
|
||||
# Error: execution error at (redis/templates/replicas/application.yaml:55:35):
|
||||
# PASSWORDS ERROR: You must provide your current passwords when upgrading the release.
|
||||
# Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims.
|
||||
# Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases
|
||||
# 'global.redis.password' must not be empty, please add '--set global.redis.password=$REDIS_PASSWORD' to the command. To get the current value:
|
||||
- name: Retrieve release password
|
||||
kubernetes.core.k8s_info:
|
||||
namespace: "{{ helm_namespace }}"
|
||||
kind: Secret
|
||||
name: test-redis
|
||||
register: redis_secret
|
||||
|
||||
- ansible.builtin.set_fact:
|
||||
chart_reset_then_reuse_values: "{{ chart_reset_then_reuse_values | combine(redis_global_password) }}"
|
||||
vars:
|
||||
redis_global_password:
|
||||
global:
|
||||
redis:
|
||||
password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}"
|
||||
|
||||
- name: Upgrade chart using reset_then_reuse_values=true
|
||||
helm:
|
||||
@@ -64,7 +86,7 @@
|
||||
- '"--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)
|
||||
- release_value["status"]["release_values"] == chart_release_values | combine(chart_reset_then_reuse_values, recursive=true)
|
||||
|
||||
always:
|
||||
- name: Remove helm namespace
|
||||
@@ -73,3 +95,4 @@
|
||||
kind: Namespace
|
||||
name: "{{ helm_namespace }}"
|
||||
state: absent
|
||||
wait: false
|
||||
|
||||
@@ -36,7 +36,22 @@
|
||||
that:
|
||||
- install is changed
|
||||
- '"--reuse-values=True" not in install.command'
|
||||
- release_value["status"]["values"] == chart_release_values
|
||||
- release_value["status"]["release_values"] == chart_release_values
|
||||
|
||||
- name: Retrieve release password
|
||||
kubernetes.core.k8s_info:
|
||||
namespace: "{{ helm_namespace }}"
|
||||
kind: Secret
|
||||
name: test-redis
|
||||
register: redis_secret
|
||||
|
||||
- ansible.builtin.set_fact:
|
||||
chart_reuse_values: "{{ chart_reuse_values | combine(redis_global_password) }}"
|
||||
vars:
|
||||
redis_global_password:
|
||||
global:
|
||||
redis:
|
||||
password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}"
|
||||
|
||||
- name: Upgrade chart using reuse_values=true
|
||||
helm:
|
||||
@@ -62,7 +77,7 @@
|
||||
- upgrade is changed
|
||||
- '"--reuse-values=True" in upgrade.command'
|
||||
- '"--reset-values" not in upgrade.command'
|
||||
- release_value["status"]["values"] == chart_release_values | combine(chart_reuse_values, recursive=true)
|
||||
- release_value["status"]["release_values"] == chart_release_values | combine(chart_reuse_values, recursive=true)
|
||||
|
||||
always:
|
||||
- name: Remove helm namespace
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
- name: Test helm take ownership
|
||||
vars:
|
||||
helm_namespace: "{{ test_namespace[13] }}"
|
||||
block:
|
||||
|
||||
- name: Initial chart installation (no flag set)
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
chart_ref: "{{ chart_test_oci }}"
|
||||
release_name: test-take-ownership
|
||||
release_namespace: "{{ helm_namespace }}"
|
||||
create_namespace: true
|
||||
register: install
|
||||
|
||||
- name: Validate that take-ownership flag is not set
|
||||
assert:
|
||||
that:
|
||||
- install is changed
|
||||
- '"--take-ownership" not in install.command'
|
||||
|
||||
# We need to provide the actual redis password otherwise the update command
|
||||
# will fail with the following:
|
||||
# Error: execution error at (redis/templates/replicas/application.yaml:55:35):
|
||||
# PASSWORDS ERROR: You must provide your current passwords when upgrading the release.
|
||||
# Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims.
|
||||
# Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases
|
||||
# 'global.redis.password' must not be empty, please add '--set global.redis.password=$REDIS_PASSWORD' to the command. To get the current value:
|
||||
- name: Retrieve release password
|
||||
kubernetes.core.k8s_info:
|
||||
namespace: "{{ helm_namespace }}"
|
||||
kind: Secret
|
||||
name: test-take-ownership-redis
|
||||
register: redis_secret
|
||||
|
||||
- name: Upgrade chart (take-onwership flag set)
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
chart_ref: "{{ chart_test_oci }}"
|
||||
release_name: test-take-ownership
|
||||
release_namespace: "{{ helm_namespace }}"
|
||||
take_ownership: true
|
||||
values:
|
||||
commonLabels:
|
||||
take-onwership: "set"
|
||||
global:
|
||||
redis:
|
||||
password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}"
|
||||
register: upgrade
|
||||
ignore_errors: true
|
||||
|
||||
- name: Validate that take-ownership flag IS set if helm version is >= 3.17.0
|
||||
assert:
|
||||
that:
|
||||
- upgrade is changed
|
||||
- '"--take-ownership" in upgrade.command'
|
||||
when: '"v3.17.0" <= helm_version'
|
||||
|
||||
- name: Validate that feature fails for helm < 3.17.0
|
||||
assert:
|
||||
that:
|
||||
- upgrade is failed
|
||||
- '"take_ownership requires helm >= 3.17.0" in upgrade.msg'
|
||||
when: 'helm_version < "v3.17.0"'
|
||||
|
||||
- name: Upgrade chart (take-onwership flag not set)
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
chart_ref: "{{ chart_test_oci }}"
|
||||
release_name: test-take-ownership
|
||||
release_namespace: "{{ helm_namespace }}"
|
||||
values:
|
||||
commonLabels:
|
||||
take-onwership: "not-set"
|
||||
global:
|
||||
redis:
|
||||
password: "{{ redis_secret.resources.0.data['redis-password'] | b64decode }}"
|
||||
register: upgrade
|
||||
ignore_errors: true
|
||||
|
||||
- name: Validate that take-ownership flag IS set if helm version is >= 3.17.0
|
||||
assert:
|
||||
that:
|
||||
- upgrade is changed
|
||||
- '"--take-ownership" not in upgrade.command'
|
||||
when: '"v3.17.0" <= helm_version'
|
||||
|
||||
- name: Validate that feature fails for helm < 3.17.0
|
||||
assert:
|
||||
that:
|
||||
- upgrade is changed
|
||||
- upgrade.msg is not defined
|
||||
when: 'helm_version < "v3.17.0"'
|
||||
|
||||
always:
|
||||
- name: Remove helm namespace
|
||||
k8s:
|
||||
api_version: v1
|
||||
kind: Namespace
|
||||
name: "{{ helm_namespace }}"
|
||||
state: absent
|
||||
@@ -31,26 +31,18 @@
|
||||
- name: assert warning has been raised
|
||||
assert:
|
||||
that:
|
||||
- uninstall.warnings
|
||||
- uninstall.warnings is defined
|
||||
- '"helm uninstall support option --wait for helm release >= 3.7.0" in uninstall.warnings'
|
||||
|
||||
- name: Create temp directory
|
||||
tempfile:
|
||||
state: directory
|
||||
suffix: .test
|
||||
register: _result
|
||||
|
||||
- set_fact:
|
||||
helm_tmp_dir: "{{ _result.path }}"
|
||||
|
||||
- name: Unarchive Helm binary
|
||||
unarchive:
|
||||
src: 'https://get.helm.sh/helm-v3.7.0-linux-amd64.tar.gz'
|
||||
dest: "{{ helm_tmp_dir }}"
|
||||
remote_src: yes
|
||||
- name: Install Helm v4
|
||||
ansible.builtin.include_role:
|
||||
name: install_helm
|
||||
vars:
|
||||
helm_version: v4.0.0
|
||||
|
||||
- name: Install chart
|
||||
helm:
|
||||
binary_path: "{{ helm_tmp_dir }}/linux-amd64/helm"
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
@@ -59,7 +51,7 @@
|
||||
- name: uninstall chart again using recent version
|
||||
helm:
|
||||
state: absent
|
||||
binary_path: "{{ helm_tmp_dir }}/linux-amd64/helm"
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_name }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
wait: yes
|
||||
@@ -96,12 +88,6 @@
|
||||
- _info.status is undefined
|
||||
|
||||
always:
|
||||
- name: Delete temp directory
|
||||
file:
|
||||
path: "{{ helm_tmp_dir }}"
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove namespace
|
||||
k8s:
|
||||
kind: Namespace
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
- name: Test helm skip_schema_validation
|
||||
vars:
|
||||
helm_namespace: "{{ test_namespace[14] }}"
|
||||
chart_release_values:
|
||||
replica:
|
||||
replicaCount: 3
|
||||
master:
|
||||
count: 1
|
||||
kind: Deployment
|
||||
block:
|
||||
- name: 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 }}"
|
||||
skip_schema_validation: true
|
||||
register: install
|
||||
ignore_errors: true
|
||||
|
||||
- name: Debug install result
|
||||
debug:
|
||||
var: install
|
||||
|
||||
- name: Validate skip_schema_validation with helm >= 3.16.0 works
|
||||
assert:
|
||||
that:
|
||||
- install is changed
|
||||
- "'--skip-schema-validation' in install.command"
|
||||
when: "helm_version is ansible.builtin.version('v3.16.0', '>=')"
|
||||
|
||||
- name: Validate skip_schema_validation with helm < 3.16.0 fails
|
||||
assert:
|
||||
that:
|
||||
- install is failed
|
||||
- "'skip_schema_validation requires helm >= 3.16.0' in install.msg"
|
||||
when: "helm_version is ansible.builtin.version('v3.16.0', '<')"
|
||||
|
||||
always:
|
||||
- name: Remove helm namespace
|
||||
k8s:
|
||||
api_version: v1
|
||||
kind: Namespace
|
||||
name: "{{ helm_namespace }}"
|
||||
state: absent
|
||||
@@ -30,9 +30,9 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
ignore_errors: yes
|
||||
ignore_errors: true
|
||||
register: install_fail
|
||||
|
||||
- name: "Assert that Install fail {{ chart_test }} from {{ source }}"
|
||||
@@ -46,7 +46,7 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
create_namespace: true
|
||||
register: install_check_mode
|
||||
@@ -57,24 +57,25 @@
|
||||
that:
|
||||
- install_check_mode is changed
|
||||
- install_check_mode.status is defined
|
||||
- install_check_mode.status.values is defined
|
||||
- install_check_mode.status.release_values is defined
|
||||
|
||||
- name: "Install {{ chart_test }} from {{ source }}"
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
create_namespace: true
|
||||
register: install
|
||||
|
||||
- name: "Assert that {{ chart_test }} chart is installed from {{ source }}"
|
||||
- name: "Assert that {{ chart_test }} chart version {{ chart_test_version }} is installed from {{ source }}"
|
||||
assert:
|
||||
that:
|
||||
- install is changed
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.release_values == {}
|
||||
|
||||
- name: Check helm_info content
|
||||
helm_info:
|
||||
@@ -92,7 +93,7 @@
|
||||
- deployed
|
||||
register: release_state_content_info
|
||||
|
||||
- name: "Assert that {{ chart_test }} is installed from {{ source }} with helm_info"
|
||||
- name: "Assert that {{ chart_test }} chart version {{ chart_test_version }} is installed from {{ source }} with helm_info"
|
||||
assert:
|
||||
that:
|
||||
- content_info.status.chart == chart_test+"-"+chart_test_version
|
||||
@@ -104,9 +105,10 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
register: install
|
||||
diff: true
|
||||
|
||||
- name: Assert idempotency
|
||||
assert:
|
||||
@@ -120,7 +122,7 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
values: "{{ chart_test_values }}"
|
||||
register: install
|
||||
@@ -131,17 +133,18 @@
|
||||
- install is changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- "install.status['values'].revisionHistoryLimit == 0"
|
||||
- install.status['release_values'] == chart_test_values
|
||||
|
||||
- name: Check idempotency after adding vars
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
values: "{{ chart_test_values }}"
|
||||
register: install
|
||||
diff: true
|
||||
|
||||
- name: Assert idempotency after add vars
|
||||
assert:
|
||||
@@ -149,14 +152,14 @@
|
||||
- install is not changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- "install.status['values'].revisionHistoryLimit == 0"
|
||||
- install.status['release_values'] == chart_test_values
|
||||
|
||||
- name: "Remove Vars to {{ chart_test }} from {{ source }}"
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
register: install
|
||||
|
||||
@@ -166,16 +169,17 @@
|
||||
- install is changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- install.status['values'] == {}
|
||||
- install.status['release_values'] == {}
|
||||
|
||||
- name: Check idempotency after removing vars
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
register: install
|
||||
diff: true
|
||||
|
||||
- name: Assert idempotency after removing vars
|
||||
assert:
|
||||
@@ -183,14 +187,14 @@
|
||||
- install is not changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- install.status['values'] == {}
|
||||
- install.status['release_values'] == {}
|
||||
|
||||
- name: "Upgrade {{ chart_test }} from {{ source }}"
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source_upgrade | default(chart_source) }}"
|
||||
chart_version: "{{ chart_source_version_upgrade | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version_upgrade }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
register: install
|
||||
|
||||
@@ -206,9 +210,10 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source_upgrade | default(chart_source) }}"
|
||||
chart_version: "{{ chart_source_version_upgrade | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version_upgrade }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
register: install
|
||||
diff: true
|
||||
|
||||
- name: Assert idempotency after upgrade
|
||||
assert:
|
||||
@@ -237,6 +242,7 @@
|
||||
name: "{{ chart_release_name }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
register: install
|
||||
diff: true
|
||||
|
||||
- name: Assert idempotency
|
||||
assert:
|
||||
@@ -249,7 +255,7 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_replaced_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
register: install
|
||||
|
||||
@@ -277,7 +283,7 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_replaced_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
replace: True
|
||||
register: install
|
||||
@@ -305,7 +311,7 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
values_files:
|
||||
- "{{ role_path }}/files/values.yaml"
|
||||
@@ -317,14 +323,14 @@
|
||||
- install is changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- "install.status['values'].revisionHistoryLimit == 0"
|
||||
- "install.status['release_values'].revisionHistoryLimit == 0"
|
||||
|
||||
- name: "Install {{ chart_test }} from {{ source }} with values_files (again)"
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
values_files:
|
||||
- "{{ role_path }}/files/values.yaml"
|
||||
@@ -346,7 +352,7 @@
|
||||
helm_template:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
output_dir: "{{ temp_dir }}"
|
||||
values_files:
|
||||
- "{{ role_path }}/files/values.yaml"
|
||||
@@ -372,7 +378,7 @@
|
||||
helm_template:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
disable_hook: True
|
||||
release_name: "myrelease"
|
||||
release_namespace: "myreleasenamespace"
|
||||
@@ -398,11 +404,11 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: "{{ chart_release_name }}"
|
||||
chart_ref: "{{ chart_source }}"
|
||||
chart_version: "{{ chart_source_version | default(omit) }}"
|
||||
chart_version: "{{ chart_test_version }}"
|
||||
namespace: "{{ helm_namespace }}"
|
||||
create_namespace: true
|
||||
context: does-not-exist
|
||||
ignore_errors: yes
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- name: Assert that release fails with non-existent context
|
||||
@@ -417,6 +423,7 @@
|
||||
state: absent
|
||||
path: "{{ temp_dir }}"
|
||||
ignore_errors: true
|
||||
when: temp_dir is defined
|
||||
|
||||
- name: Remove helm namespace
|
||||
k8s:
|
||||
|
||||
@@ -5,16 +5,36 @@
|
||||
name: test_helm
|
||||
repo_url: "{{ chart_test_repo }}"
|
||||
|
||||
- name: Install Chart from repository
|
||||
include_tasks: "../tests_chart.yml"
|
||||
vars:
|
||||
source: repository
|
||||
chart_source: "test_helm/{{ chart_test }}"
|
||||
chart_source_version: "{{ chart_test_version }}"
|
||||
chart_source_version_upgrade: "{{ chart_test_version_upgrade }}"
|
||||
helm_namespace: "{{ test_namespace[7] }}"
|
||||
- name: Create temporary file to save values in
|
||||
ansible.builtin.tempfile:
|
||||
suffix: .helm_values
|
||||
register: value_file
|
||||
|
||||
- name: Remove chart repo
|
||||
- vars:
|
||||
source: repository
|
||||
chart_test: k8status
|
||||
chart_source: "test_helm/k8status"
|
||||
chart_test_version: "0.16.1"
|
||||
chart_test_version_upgrade: "0.16.2"
|
||||
helm_namespace: "{{ test_namespace[7] }}"
|
||||
chart_test_values:
|
||||
replicaCount: 3
|
||||
block:
|
||||
- name: Save values into file
|
||||
ansible.builtin.copy:
|
||||
content: "{{ chart_test_values }}"
|
||||
dest: "{{ value_file.path }}"
|
||||
|
||||
- name: Install Chart from repository
|
||||
ansible.builtin.include_tasks: "../tests_chart.yml"
|
||||
|
||||
always:
|
||||
- name: Remove temporary file
|
||||
ansible.builtin.file:
|
||||
state: absent
|
||||
path: "{{ value_file.path }}"
|
||||
|
||||
- name: Remove chart repo
|
||||
helm_repository:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: test_helm
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
include_tasks: "../tests_chart.yml"
|
||||
vars:
|
||||
source: url
|
||||
chart_source: "https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-{{ chart_test_version }}/{{ chart_test }}-{{ chart_test_version }}.tgz"
|
||||
chart_source_upgrade: "https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-{{ chart_test_version_upgrade }}/{{ chart_test }}-{{ chart_test_version_upgrade }}.tgz"
|
||||
chart_test: "k8status"
|
||||
chart_test_values:
|
||||
replicaCount: 3
|
||||
chart_test_version: "0.16.1"
|
||||
chart_test_version_upgrade: "0.16.2"
|
||||
chart_source: https://github.com/stenic/k8status/releases/download/k8status-0.16.1/k8status-0.16.1.tgz
|
||||
chart_source_upgrade: https://github.com/stenic/k8status/releases/download/k8status-0.16.2/k8status-0.16.2.tgz
|
||||
helm_namespace: "{{ test_namespace[8] }}"
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
vars:
|
||||
chart_local_path: '{{ _tmpd.path }}/test-chart-deployment-time'
|
||||
chart_repo_path: 'testing'
|
||||
helm_binary_path: "{{ helm_binary }}"
|
||||
always:
|
||||
- name: Delete temporary directory
|
||||
ansible.builtin.file:
|
||||
|
||||
@@ -92,25 +92,11 @@
|
||||
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: Install old version of helm
|
||||
ansible.builtin.include_role:
|
||||
name: install_helm
|
||||
vars:
|
||||
helm_version: "v3.8.0"
|
||||
|
||||
- name: Upgrade helm release (with reset_then_reuse_values=true)
|
||||
kubernetes.core.helm:
|
||||
@@ -140,7 +126,7 @@
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/databus23/helm-diff
|
||||
plugin_version: "{{ helm_diff_old_version }}"
|
||||
plugin_version: "3.8.0"
|
||||
|
||||
- name: Upgrade helm release (with reset_then_reuse_values=true)
|
||||
kubernetes.core.helm:
|
||||
@@ -166,6 +152,11 @@
|
||||
- '"reset_then_reuse_values requires helm diff >= 3.9.12, current version is" in helm_upgrade.msg'
|
||||
|
||||
always:
|
||||
- name: Delete Helm folders
|
||||
file:
|
||||
path: /tmp/helm/
|
||||
state: absent
|
||||
|
||||
- name: Remove temporary directory
|
||||
file:
|
||||
path: "{{ helm_dir.path }}"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
---
|
||||
helm_binary: "/tmp/helm/{{ ansible_system | lower }}-amd64/helm"
|
||||
default_kubeconfig_path: "~/.kube/config"
|
||||
test_namespace:
|
||||
- "helm-in-memory-kubeconfig"
|
||||
- "helm-kubeconfig-with-ca-cert"
|
||||
- "helm-kubeconfig-with-insecure-skip-tls-verify"
|
||||
helm_versions:
|
||||
- v3.10.3
|
||||
- v3.16.4
|
||||
- v4.0.0
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
dependencies:
|
||||
- remove_namespace
|
||||
- install_helm
|
||||
@@ -57,7 +57,7 @@
|
||||
assert:
|
||||
that:
|
||||
- _install is failed
|
||||
- '"Error: Kubernetes cluster unreachable" in _install.msg'
|
||||
- '"error: kubernetes cluster unreachable" in _install.msg | lower()'
|
||||
|
||||
- name: Test helm modules using in-memory kubeconfig
|
||||
include_tasks: "tests_helm_auth.yml"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
assert:
|
||||
that:
|
||||
- _install is failed
|
||||
- '"Error: Kubernetes cluster unreachable" in _install.msg'
|
||||
- '"error: kubernetes cluster unreachable" in _install.msg | lower()'
|
||||
|
||||
- name: Test helm modules using in-memory kubeconfig
|
||||
include_tasks: "tests_helm_auth.yml"
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
---
|
||||
- name: Test helm with in-memory kubeconfig
|
||||
include_tasks: "from_in_memory_kubeconfig.yml"
|
||||
- ansible.builtin.include_tasks: run_tests.yml
|
||||
loop: "{{ helm_versions }}"
|
||||
loop_control:
|
||||
loop_var: test_helm_version
|
||||
with_items:
|
||||
- "v3.10.3"
|
||||
|
||||
- name: Test helm with custom kubeconfig and validate_certs=false
|
||||
include_tasks: "from_kubeconfig_with_validate_certs.yml"
|
||||
loop_control:
|
||||
loop_var: test_helm_version
|
||||
with_items:
|
||||
- "v3.10.3"
|
||||
|
||||
- name: Test helm with custom kubeconfig and ca_cert
|
||||
include_tasks: "from_kubeconfig_with_cacert.yml"
|
||||
loop_control:
|
||||
loop_var: test_helm_version
|
||||
with_items:
|
||||
- "v3.10.3"
|
||||
loop_var: helm_version
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Run tests with helm version "{{ helm_version }}"
|
||||
block:
|
||||
- name: "Install Helm"
|
||||
ansible.builtin.include_role:
|
||||
name: install_helm
|
||||
|
||||
- name: Test helm with in-memory kubeconfig
|
||||
ansible.builtin.include_tasks: "from_in_memory_kubeconfig.yml"
|
||||
|
||||
- name: Test helm with custom kubeconfig and validate_certs=false
|
||||
include_tasks: "from_kubeconfig_with_validate_certs.yml"
|
||||
|
||||
- name: Test helm with custom kubeconfig and ca_cert
|
||||
include_tasks: "from_kubeconfig_with_cacert.yml"
|
||||
@@ -5,16 +5,6 @@
|
||||
suffix: .helm
|
||||
register: _dir
|
||||
|
||||
- name: Install helm binary
|
||||
block:
|
||||
- name: "Install {{ test_helm_version }}"
|
||||
include_role:
|
||||
name: install_helm
|
||||
vars:
|
||||
helm_version: "{{ test_helm_version }}"
|
||||
|
||||
when: test_helm_version is defined
|
||||
|
||||
- set_fact:
|
||||
saved_kubeconfig_path: "{{ _dir.path }}/config"
|
||||
|
||||
@@ -44,6 +34,7 @@
|
||||
ca_cert: "{{ test_ca_cert | default(omit) }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/hydeenoble/helm-subenv
|
||||
verify: false
|
||||
register: plugin
|
||||
|
||||
- assert:
|
||||
|
||||
1
tests/integration/targets/helm_plain_http/.gitignore
vendored
Normal file
1
tests/integration/targets/helm_plain_http/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
redis*
|
||||
3
tests/integration/targets/helm_plain_http/aliases
Normal file
3
tests/integration/targets/helm_plain_http/aliases
Normal file
@@ -0,0 +1,3 @@
|
||||
helm_template
|
||||
helm_pull
|
||||
helm
|
||||
4
tests/integration/targets/helm_plain_http/inventory.ini
Normal file
4
tests/integration/targets/helm_plain_http/inventory.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[all]
|
||||
helm-3.12.3 helm_version=v3.12.3 test_namespace=helm-plain-http-v3-12-3 tests_should_failed=true
|
||||
helm-3.18.2 helm_version=v3.18.2 test_namespace=helm-plain-http-v3-18-2 tests_should_failed=false
|
||||
helm-4.0.0 helm_version=v4.0.0 test_namespace=helm-plain-http-v4-0-0 tests_should_failed=false
|
||||
@@ -0,0 +1,15 @@
|
||||
- name: Run test for helm plain http option
|
||||
hosts: all
|
||||
gather_facts: true
|
||||
strategy: free
|
||||
|
||||
vars:
|
||||
ansible_connection: local
|
||||
ansible_python_interpreter: "{{ ansible_playbook_python }}"
|
||||
chart_test_oci: "oci://registry-1.docker.io/bitnamicharts/redis"
|
||||
|
||||
roles:
|
||||
- role: setup_namespace
|
||||
|
||||
tasks:
|
||||
- ansible.builtin.include_tasks: tasks/test.yaml
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
- name: Run test for helm
|
||||
block:
|
||||
- name: Create temporary directory to install chart In
|
||||
ansible.builtin.tempfile:
|
||||
state: directory
|
||||
suffix: .helm
|
||||
register: install_path
|
||||
|
||||
- name: Install required helm version
|
||||
ansible.builtin.include_role:
|
||||
name: install_helm
|
||||
vars:
|
||||
helm_install_path: "{{ install_path.path }}"
|
||||
|
||||
# helm
|
||||
- name: Run helm with plain_http
|
||||
kubernetes.core.helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
chart_ref: "{{ chart_test_oci }}"
|
||||
release_name: test-secure
|
||||
release_namespace: "{{ test_namespace }}"
|
||||
create_namespace: true
|
||||
plain_http: true
|
||||
register: install_chart
|
||||
ignore_errors: true
|
||||
|
||||
- name: Ensure module failed as expected
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- install_chart is failed
|
||||
- '"plain_http requires helm >= 3.13.0" in install_chart.msg'
|
||||
when: tests_should_failed | bool
|
||||
|
||||
- name: Ensure the result command contains the expected option
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- install_chart is not failed
|
||||
- '"--plain-http" in install_chart.command'
|
||||
when: not (tests_should_failed | bool)
|
||||
|
||||
# helm_pull
|
||||
- name: Trying to download helm chart with option plain_http
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: "{{ chart_test_oci }}"
|
||||
destination: "{{ playbook_dir }}"
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plain_http: true
|
||||
register: pull_chart
|
||||
ignore_errors: true
|
||||
|
||||
- name: Ensure module failed as expected
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pull_chart is failed
|
||||
- '"plain_http requires helm >= 3.13.0" in pull_chart.msg'
|
||||
when: tests_should_failed | bool
|
||||
|
||||
- name: Ensure the result command contains the expected option
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pull_chart is not failed
|
||||
- '"--plain-http" in pull_chart.command'
|
||||
when: not (tests_should_failed | bool)
|
||||
|
||||
# helm_template
|
||||
- name: Test helm render template
|
||||
kubernetes.core.helm_template:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
chart_ref: "{{ chart_test_oci }}"
|
||||
output_dir: "{{ playbook_dir }}"
|
||||
plain_http: true
|
||||
register: template
|
||||
ignore_errors: true
|
||||
|
||||
- name: Ensure module failed as expected
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- template is failed
|
||||
- '"plain_http requires helm >= 3.13.0" in template.msg'
|
||||
when: tests_should_failed | bool
|
||||
|
||||
- name: Ensure the result command contains the expected option
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- template is not failed
|
||||
- '"--plain-http" in template.command'
|
||||
when: not (tests_should_failed | bool)
|
||||
|
||||
always:
|
||||
- name: Delete temporary file
|
||||
ansible.builtin.file:
|
||||
path: "{{ install_path.path }}"
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
4
tests/integration/targets/helm_plain_http/runme.sh
Executable file
4
tests/integration/targets/helm_plain_http/runme.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
export ANSIBLE_ROLES_PATH=../
|
||||
ansible-playbook playbooks/play.yaml -i inventory.ini "$@"
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
dependencies:
|
||||
- install_helm
|
||||
@@ -1,165 +1,8 @@
|
||||
---
|
||||
- name: Install env plugin in check mode
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/adamreese/helm-env
|
||||
register: check_install_env
|
||||
check_mode: true
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- check_install_env.changed
|
||||
|
||||
- name: Install env plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/adamreese/helm-env
|
||||
register: install_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- install_env.changed
|
||||
|
||||
- name: Gather info about all plugin
|
||||
helm_plugin_info:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
register: plugin_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- plugin_info.plugin_list is defined
|
||||
|
||||
- name: Install env plugin again
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/adamreese/helm-env
|
||||
register: install_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not install_env.changed
|
||||
|
||||
- name: Uninstall env plugin in check mode
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: env
|
||||
register: check_uninstall_env
|
||||
check_mode: true
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- check_uninstall_env.changed
|
||||
|
||||
- name: Uninstall env plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: env
|
||||
register: uninstall_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- uninstall_env.changed
|
||||
|
||||
- name: Uninstall env plugin again
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: env
|
||||
register: uninstall_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not uninstall_env.changed
|
||||
|
||||
# https://github.com/ansible-collections/community.kubernetes/issues/399
|
||||
- block:
|
||||
- name: Copy required plugin files
|
||||
copy:
|
||||
src: "files/sample_plugin"
|
||||
dest: "/tmp/helm_plugin_test/"
|
||||
|
||||
- name: Install sample_plugin from the directory
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: "/tmp/helm_plugin_test/sample_plugin"
|
||||
register: sample_plugin_output
|
||||
|
||||
- name: Assert that sample_plugin is installed or not
|
||||
assert:
|
||||
that:
|
||||
- sample_plugin_output.changed
|
||||
|
||||
- name: Gather Helm plugin info
|
||||
helm_plugin_info:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
register: r
|
||||
|
||||
- name: Set sample_plugin version
|
||||
set_fact:
|
||||
plugin_version: "{{ ( r.plugin_list | selectattr('name', 'equalto', plugin_name) | list )[0].version }}"
|
||||
vars:
|
||||
plugin_name: "sample_plugin"
|
||||
|
||||
- name: Assert if sample_plugin with multiline comment is installed
|
||||
assert:
|
||||
that:
|
||||
- plugin_version == "0.0.1"
|
||||
always:
|
||||
- name: Uninstall sample_plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: sample_plugin
|
||||
ignore_errors: yes
|
||||
|
||||
- block:
|
||||
- name: uninstall helm plugin secrets
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_name: secrets
|
||||
state: absent
|
||||
|
||||
- name: install helm-secrets on a specific version
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_path: https://github.com/jkroepke/helm-secrets
|
||||
plugin_version: 3.4.1
|
||||
state: present
|
||||
|
||||
- name: list helm plugin
|
||||
helm_plugin_info:
|
||||
plugin_name: secrets
|
||||
binary_path: "{{ helm_binary }}"
|
||||
register: plugin_list
|
||||
|
||||
- name: assert that secrets has been installed with specified version
|
||||
assert:
|
||||
that:
|
||||
- plugin_list.plugin_list[0].version == "3.4.1"
|
||||
|
||||
- name: Update helm plugin version to latest
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_name: secrets
|
||||
state: latest
|
||||
register: _update
|
||||
|
||||
- name: assert update was performed
|
||||
assert:
|
||||
that:
|
||||
- _update.changed
|
||||
- '"Updated plugin: secrets" in _update.stdout'
|
||||
|
||||
always:
|
||||
- name: Uninstall sample_plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: secrets
|
||||
ignore_errors: yes
|
||||
- name: Run tests
|
||||
include_tasks: run_tests.yml
|
||||
loop_control:
|
||||
loop_var: helm_version
|
||||
with_items:
|
||||
- "v3.17.0"
|
||||
- "v4.0.0"
|
||||
|
||||
195
tests/integration/targets/helm_plugin/tasks/run_tests.yml
Normal file
195
tests/integration/targets/helm_plugin/tasks/run_tests.yml
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
- name: "Install {{ helm_version }}"
|
||||
include_role:
|
||||
name: install_helm
|
||||
|
||||
- block:
|
||||
- name: Install env plugin in check mode
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/adamreese/helm-env
|
||||
verify: false
|
||||
register: check_install_env
|
||||
check_mode: true
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- check_install_env.changed
|
||||
|
||||
- name: Install env plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/adamreese/helm-env
|
||||
verify: false
|
||||
register: install_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- install_env.changed
|
||||
|
||||
- name: Gather info about all plugin
|
||||
helm_plugin_info:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
register: plugin_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- plugin_info.plugin_list is defined
|
||||
|
||||
- name: Install env plugin again
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/adamreese/helm-env
|
||||
verify: false
|
||||
register: install_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not install_env.changed
|
||||
|
||||
- name: Uninstall env plugin in check mode
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: env
|
||||
verify: false
|
||||
register: check_uninstall_env
|
||||
check_mode: true
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- check_uninstall_env.changed
|
||||
|
||||
- name: Uninstall env plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: env
|
||||
register: uninstall_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- uninstall_env.changed
|
||||
|
||||
- name: Uninstall env plugin again
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: env
|
||||
register: uninstall_env
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not uninstall_env.changed
|
||||
|
||||
always:
|
||||
- name: Uninstall env plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: env
|
||||
|
||||
# https://github.com/ansible-collections/community.kubernetes/issues/399
|
||||
- block:
|
||||
- name: Copy required plugin files
|
||||
copy:
|
||||
src: "files/sample_plugin"
|
||||
dest: "/tmp/helm_plugin_test/"
|
||||
|
||||
- name: Install sample_plugin from the directory
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: present
|
||||
plugin_path: "/tmp/helm_plugin_test/sample_plugin"
|
||||
register: sample_plugin_output
|
||||
|
||||
- name: Assert that sample_plugin is installed or not
|
||||
assert:
|
||||
that:
|
||||
- sample_plugin_output.changed
|
||||
|
||||
- name: Gather Helm plugin info
|
||||
helm_plugin_info:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
register: r
|
||||
|
||||
- name: Set sample_plugin version
|
||||
set_fact:
|
||||
plugin_version: "{{ ( r.plugin_list | selectattr('name', 'equalto', plugin_name) | list )[0].version }}"
|
||||
vars:
|
||||
plugin_name: "sample_plugin"
|
||||
|
||||
- name: Assert if sample_plugin with multiline comment is installed
|
||||
assert:
|
||||
that:
|
||||
- plugin_version == "0.0.1"
|
||||
always:
|
||||
- name: Uninstall sample_plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: sample_plugin
|
||||
ignore_errors: true
|
||||
|
||||
- block:
|
||||
- name: uninstall helm plugin unittest
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_name: unittest
|
||||
state: absent
|
||||
|
||||
- name: install helm-unittest on a specific version
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_path: https://github.com/helm-unittest/helm-unittest
|
||||
plugin_version: v1.0.1
|
||||
verify: false
|
||||
state: present
|
||||
|
||||
- name: list helm plugin
|
||||
helm_plugin_info:
|
||||
plugin_name: unittest
|
||||
binary_path: "{{ helm_binary }}"
|
||||
register: plugin_list
|
||||
|
||||
- name: assert that unittest has been installed with specified version
|
||||
assert:
|
||||
that:
|
||||
- plugin_list.plugin_list[0].version == "1.0.1"
|
||||
|
||||
- name: Update helm plugin version to latest (check mode)
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_name: unittest
|
||||
state: latest
|
||||
register: _update_checkmode
|
||||
check_mode: true
|
||||
|
||||
- name: Assert that module reported change while running in check mode
|
||||
assert:
|
||||
that:
|
||||
- _update_checkmode.changed
|
||||
- '"Updated plugin: unittest" not in _update_checkmode.stdout'
|
||||
|
||||
- name: Update helm plugin version to latest
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_name: unittest
|
||||
state: latest
|
||||
register: _update
|
||||
|
||||
- name: assert update was performed
|
||||
assert:
|
||||
that:
|
||||
- _update.changed
|
||||
- '"Updated plugin: unittest" in _update.stdout'
|
||||
|
||||
always:
|
||||
- name: Uninstall sample_plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
state: absent
|
||||
plugin_name: unittest
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user