mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-11 20:12:18 +00:00
Compare commits
14 Commits
3.0.0
...
stable-2.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69304d1c3b | ||
|
|
fdddb6f78f | ||
|
|
e8cf1ef517 | ||
|
|
6dbc0d5b6d | ||
|
|
0a72c87d2c | ||
|
|
c149394556 | ||
|
|
e2dec91460 | ||
|
|
edf104d687 | ||
|
|
7b09c01d98 | ||
|
|
7409eaf993 | ||
|
|
321b6dcdd8 | ||
|
|
68d45af767 | ||
|
|
346808ec4b | ||
|
|
767aa10b1d |
@@ -1,6 +0,0 @@
|
||||
---
|
||||
profile: production
|
||||
|
||||
exclude_paths:
|
||||
- tests/integration
|
||||
- tests/sanity
|
||||
21
.github/workflows/changelog.yaml
vendored
21
.github/workflows/changelog.yaml
vendored
@@ -1,21 +0,0 @@
|
||||
---
|
||||
name: Changelog
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- labeled
|
||||
- unlabeled
|
||||
- synchronize
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
jobs:
|
||||
changelog:
|
||||
uses: ansible-network/github_actions/.github/workflows/changelog.yml@main
|
||||
14
.github/workflows/galaxy-import.yaml
vendored
14
.github/workflows/galaxy-import.yaml
vendored
@@ -1,14 +0,0 @@
|
||||
name: galaxy-import
|
||||
concurrency:
|
||||
group: ${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
jobs:
|
||||
galaxy_importer:
|
||||
uses: ansible-network/github_actions/.github/workflows/galaxy_importer.yml@main
|
||||
144
.github/workflows/integration-tests.yaml
vendored
144
.github/workflows/integration-tests.yaml
vendored
@@ -1,144 +0,0 @@
|
||||
name: Integration tests
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- labeled
|
||||
- unlabeled
|
||||
- synchronize
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
jobs:
|
||||
splitter:
|
||||
env:
|
||||
source_dir: "./source"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
test_targets: ${{ steps.splitter.outputs.test_targets }}
|
||||
test_targets_json: ${{ steps.splitter.outputs.test_targets_json }}
|
||||
test_jobs: ${{ steps.splitter.outputs.test_jobs }}
|
||||
steps:
|
||||
- name: Checkout the collection repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: ${{ env.source_dir }}
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: list changes for pull request
|
||||
id: splitter
|
||||
uses: ansible-network/github_actions/.github/actions/ansible_test_splitter@main
|
||||
with:
|
||||
collections_to_test: ${{ env.source_dir }}
|
||||
total_jobs: 8
|
||||
|
||||
- name: Display splitter output
|
||||
run: |
|
||||
echo "test_targets=${{ steps.splitter.outputs.test_targets }}"
|
||||
echo "test_targets_json=${{ steps.splitter.outputs.test_targets_json }}"
|
||||
echo "test_jobs=${{ steps.splitter.outputs.test_jobs }}"
|
||||
shell: bash
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
needs:
|
||||
- splitter
|
||||
if: ${{ needs.splitter.outputs.test_targets != '' }}
|
||||
env:
|
||||
source: "./source"
|
||||
cloud_common: "./cloudcommon"
|
||||
ansible_posix: "./ansible_posix"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ansible-version:
|
||||
- milestone
|
||||
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 }}"
|
||||
steps:
|
||||
- name: Read target
|
||||
id: read-targets
|
||||
run: |
|
||||
import json, os
|
||||
with open(os.environ.get('GITHUB_OUTPUT'), "a", encoding="utf-8") as fh:
|
||||
fh.write(f'ansible_test_targets={json.loads(os.environ.get("ALL_TEST_TARGETS")).get(os.environ.get("WORKFLOW_ID"))}\n')
|
||||
shell: python
|
||||
env:
|
||||
ALL_TEST_TARGETS: ${{ needs.splitter.outputs.test_targets_json }}
|
||||
WORKFLOW_ID: ${{ matrix.workflow-id }}
|
||||
|
||||
- name: Display ansible test targets
|
||||
run: |
|
||||
echo "ansible_test_targets -> ${{ steps.read-targets.outputs.ansible_test_targets }}"
|
||||
|
||||
- name: Checkout kubernetes.core repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: ${{ env.source }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
# install ansible
|
||||
- name: Install ansible-core (${{ matrix.ansible-version }})
|
||||
run: >-
|
||||
python3 -m pip install
|
||||
https://github.com/ansible/ansible/archive/${{ matrix.ansible-version }}.tar.gz
|
||||
--disable-pip-version-check
|
||||
shell: bash
|
||||
|
||||
- name: Build and install collection
|
||||
id: install-src
|
||||
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
|
||||
with:
|
||||
install_python_dependencies: true
|
||||
source_path: ${{ env.source }}
|
||||
|
||||
- name: checkout ansible-collections/cloud.common
|
||||
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
|
||||
with:
|
||||
repository: ansible-collections/cloud.common
|
||||
path: ${{ env.cloud_common }}
|
||||
ref: main
|
||||
|
||||
- name: checkout ansible-collections/ansible.posix
|
||||
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
|
||||
with:
|
||||
repository: ansible-collections/ansible.posix
|
||||
path: ${{ env.ansible_posix }}
|
||||
ref: main
|
||||
|
||||
- name: install cloud.common collection
|
||||
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
|
||||
with:
|
||||
install_python_dependencies: true
|
||||
source_path: ${{ env.cloud_common }}
|
||||
|
||||
- name: install ansible.posix collection
|
||||
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
|
||||
with:
|
||||
install_python_dependencies: true
|
||||
source_path: ${{ env.ansible_posix }}
|
||||
|
||||
- name: create kubernetes cluster
|
||||
uses: helm/kind-action@v1.8.0
|
||||
|
||||
- name: Run integration tests
|
||||
uses: ansible-network/github_actions/.github/actions/ansible_test_integration@main
|
||||
with:
|
||||
collection_path: ${{ steps.install-src.outputs.collection_path }}
|
||||
python_version: ${{ matrix.python-version }}
|
||||
ansible_version: ${{ matrix.ansible-version }}
|
||||
ansible_test_targets: ${{ steps.read-targets.outputs.ansible_test_targets }}
|
||||
ansible_test_environment: |
|
||||
ENABLE_TURBO_MODE=${{ matrix.enable-turbo-mode }}
|
||||
24
.github/workflows/linters.yaml
vendored
24
.github/workflows/linters.yaml
vendored
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Linters
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
linters:
|
||||
uses: ansible-network/github_actions/.github/workflows/tox-linters.yml@main
|
||||
ansible-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run ansible-lint
|
||||
uses: ansible/ansible-lint@v6.21.0
|
||||
15
.github/workflows/sanity-tests.yaml
vendored
15
.github/workflows/sanity-tests.yaml
vendored
@@ -1,15 +0,0 @@
|
||||
---
|
||||
name: Sanity tests
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
jobs:
|
||||
sanity:
|
||||
uses: ansible-network/github_actions/.github/workflows/sanity.yml@main
|
||||
14
.github/workflows/unit-tests.yaml
vendored
14
.github/workflows/unit-tests.yaml
vendored
@@ -1,14 +0,0 @@
|
||||
name: Unit tests
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
jobs:
|
||||
unit-source:
|
||||
uses: ansible-network/github_actions/.github/workflows/unit_source.yml@main
|
||||
118
CHANGELOG.rst
118
CHANGELOG.rst
@@ -5,136 +5,22 @@ Kubernetes Collection Release Notes
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
v3.0.0
|
||||
v2.3.2
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This major release drops support for ansible-core versions lower than 2.14, Python versions lower than 3.9 and updates python kubernetes library to 24.2.0, helm/kind-action to 1.8.0, kubernetes >= 1.24, along with bug fixes and minor changes.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- helm - add ``reuse_values`` and ``reset_values`` support to helm module (https://github.com/ansible-collections/kubernetes.core/issues/394).
|
||||
- k8s - add new option ``delete_all`` to support deletion of all resources when state is set to ``absent``. (https://github.com/ansible-collections/kubernetes.core/issues/504)
|
||||
- k8s, k8s_info - add a hidden_fields option to allow fields to be hidden in the results of k8s and k8s_info
|
||||
- k8s_drain - add ability to filter the list of pods to be drained by a pod label selector (https://github.com/ansible-collections/kubernetes.core/issues/474).
|
||||
|
||||
Breaking Changes / Porting Guide
|
||||
--------------------------------
|
||||
|
||||
- Remove support for ansible-core < 2.14
|
||||
- Update python kubernetes library to 24.2.0, helm/kind-action to 1.8.0, kubernetes >= 1.24.
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- k8s - the ``k8s`` inventory plugin has been deprecated and will be removed in release 4.0.0 (https://github.com/ansible-collections/kubernetes.core/issues/31).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- helm - Put the chart_ref into quotes when running ``helm show chart``, ``helm upgrade`` and ``helm dependency update`` commands (https://github.com/ansible-collections/kubernetes.core/issues/653).
|
||||
- helm - delete temporary file created when deploying chart with option ``release_values`` set (https://github.com/ansible-collections/kubernetes.core/issues/530).
|
||||
- helm - fix issue occurring when uninstalling chart with statues others than ``deployed`` (https://github.com/ansible-collections/kubernetes.core/issues/319).
|
||||
- helm - fix post_renderer argument breaking the helm deploy_command (https://github.com/ansible-collections/kubernetes.core/pull/586).
|
||||
- helm - use post_renderer when checking ``changed`` status for a helm release (https://github.com/ansible-collections/kubernetes.core/pull/588).
|
||||
- k8s_scale - clean handling of ResourceTimeout exception (https://github.com/ansible-collections/kubernetes.core/issues/583).
|
||||
- k8s_scale - fix issue when scaling StatefulSets with ``updateStrategy=OnDelete`` (https://github.com/ansible-collections/kubernetes.core/issues/579).
|
||||
|
||||
v2.4.0
|
||||
======
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- refactor K8sAnsibleMixin into module_utils/k8s/ (https://github.com/ansible-collections/kubernetes.core/pull/481).
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Adjust k8s_user_impersonation tests to be compatible with Kubernetes 1.24 (https://github.com/ansible-collections/kubernetes.core/pull/520).
|
||||
- add support for dry run with kubernetes client version >=18.20 (https://github.com/ansible-collections/kubernetes.core/pull/245).
|
||||
- added ignore.txt for Ansible 2.14 devel branch.
|
||||
- fixed module_defaults by removing routing hacks from runtime.yml (https://github.com/ansible-collections/kubernetes.core/pull/347).
|
||||
- helm - add support for -set-file, -set-json, -set and -set-string options when running helm install (https://github.com/ansible-collections/kubernetes.core/issues/533).
|
||||
- helm - add support for helm dependency update (https://github.com/ansible-collections/kubernetes.core/pull/208).
|
||||
- helm - add support for post-renderer flag (https://github.com/ansible-collections/kubernetes.core/issues/30).
|
||||
- helm - add support for timeout cli parameter to allow setting Helm timeout independent of wait (https://github.com/ansible-collections/kubernetes.core/issues/67).
|
||||
- helm - add support for wait parameter for helm uninstall command. (https://github.com/ansible-collections/kubernetes/core/issues/33).
|
||||
- helm - support repo location for helm diff (https://github.com/ansible-collections/kubernetes.core/issues/174).
|
||||
- helm - when ansible is executed in check mode, return the diff between what's deployed and what will be deployed.
|
||||
- helm, helm_plugin, helm_info, helm_plugin_info, kubectl - add support for in-memory kubeconfig. (https://github.com/ansible-collections/kubernetes.core/issues/492).
|
||||
- helm_info - add hooks, notes and manifest as part of returned information (https://github.com/ansible-collections/kubernetes.core/pull/546).
|
||||
- helm_info - add release state as a module argument (https://github.com/ansible-collections/kubernetes.core/issues/377).
|
||||
- helm_info - added possibility to get all values by adding get_all_values parameter (https://github.com/ansible-collections/kubernetes.core/pull/531).
|
||||
- helm_plugin - Add plugin_version parameter to the helm_plugin module (https://github.com/ansible-collections/kubernetes.core/issues/157).
|
||||
- helm_plugin - Add support for helm plugin update using state=update.
|
||||
- helm_repository - Ability to replace (overwrite) the repo if it already exists by forcing (https://github.com/ansible-collections/kubernetes.core/issues/491).
|
||||
- helm_repository - add support for pass-credentials cli parameter (https://github.com/ansible-collections/kubernetes.core/pull/282).
|
||||
- helm_repository - added support for ``host``, ``api_key``, ``validate_certs``, and ``ca_cert``.
|
||||
- helm_repository - mark `pass_credentials` as no_log=True to silence false warning (https://github.com/ansible-collections/kubernetes.core/issues/412).
|
||||
- helm_template - add name (NAME of release) and disable_hook as optional module arguments (https://github.com/ansible-collections/kubernetes.core/issues/313).
|
||||
- helm_template - add show_only and release_namespace as module arguments (https://github.com/ansible-collections/kubernetes.core/issues/313).
|
||||
- helm_template - add support for -set-file, -set-json, -set and -set-string options when running helm template (https://github.com/ansible-collections/kubernetes.core/pull/546).
|
||||
- k8s - add no_proxy support to k8s* (https://github.com/ansible-collections/kubernetes.core/pull/272).
|
||||
- k8s - add support for server_side_apply. (https://github.com/ansible-collections/kubernetes.core/issues/87).
|
||||
- k8s - add support for user impersonation. (https://github.com/ansible-collections/kubernetes/core/issues/40).
|
||||
- k8s - allow resource definition using metadata.generateName (https://github.com/ansible-collections/kubernetes.core/issues/35).
|
||||
- k8s lookup plugin - Enable turbo mode via environment variable (https://github.com/ansible-collections/kubernetes.core/issues/291).
|
||||
- k8s, k8s_scale, k8s_service - add support for resource definition as manifest via. (https://github.com/ansible-collections/kubernetes.core/issues/451).
|
||||
- k8s_cp - remove dependency with 'find' executable on remote pod when state=from_pod (https://github.com/ansible-collections/kubernetes.core/issues/486).
|
||||
- k8s_drain - Adds ``delete_emptydir_data`` option to ``k8s_drain.delete_options`` to evict pods with an ``emptyDir`` volume attached (https://github.com/ansible-collections/kubernetes.core/pull/322).
|
||||
- k8s_exec - select first container from the pod if none specified (https://github.com/ansible-collections/kubernetes.core/issues/358).
|
||||
- k8s_exec - update deprecation warning for `return_code` (https://github.com/ansible-collections/kubernetes.core/issues/417).
|
||||
- k8s_json_patch - minor typo fix in the example section (https://github.com/ansible-collections/kubernetes.core/issues/411).
|
||||
- k8s_log - add the ``all_containers`` for retrieving all containers' logs in the pod(s).
|
||||
- k8s_log - added the `previous` parameter for retrieving the previously terminated pod logs (https://github.com/ansible-collections/kubernetes.core/issues/437).
|
||||
- k8s_log - added the `tail_lines` parameter to limit the number of lines to be retrieved from the end of the logs (https://github.com/ansible-collections/kubernetes.core/issues/488).
|
||||
- k8s_rollback - add support for check_mode. (https://github.com/ansible-collections/kubernetes/core/issues/243).
|
||||
- k8s_scale - add support for check_mode. (https://github.com/ansible-collections/kubernetes/core/issues/244).
|
||||
- kubectl - wait for dd command to complete before proceeding (https://github.com/ansible-collections/kubernetes.core/pull/321).
|
||||
- kubectl.py - replace distutils.spawn.find_executable with shutil.which in the kubectl connection plugin (https://github.com/ansible-collections/kubernetes.core/pull/456).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix dry_run logic - Pass the value dry_run=All instead of dry_run=True to the client, add conditional check on kubernetes client version as this feature is supported only for kubernetes >= 18.20.0 (https://github.com/ansible-collections/kubernetes.core/pull/561).
|
||||
- Fix kubeconfig parameter when multiple config files are provided (https://github.com/ansible-collections/kubernetes.core/issues/435).
|
||||
- Helm - Fix issue with alternative kubeconfig provided with validate_certs=False (https://github.com/ansible-collections/kubernetes.core/issues/538).
|
||||
- Various modules and plugins - use vendored version of ``distutils.version`` instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/kubernetes.core/pull/314).
|
||||
- add missing documentation for filter plugin kubernetes.core.k8s_config_resource_name (https://github.com/ansible-collections/kubernetes.core/issues/558).
|
||||
- common - Ensure the label_selectors parameter of _wait_for method is optional.
|
||||
- common - handle ``aliases`` passed from inventory and lookup plugins.
|
||||
- helm_template - evaluate release_values after values_files, insuring highest precedence (now same behavior as in helm module). (https://github.com/ansible-collections/kubernetes.core/pull/348)
|
||||
- import exception from ``kubernetes.client.rest``.
|
||||
- k8s - Fix issue with check_mode when using server side apply (https://github.com/ansible-collections/kubernetes.core/issues/547).
|
||||
- k8s - Fix issue with server side apply with kubernetes release '25.3.0' (https://github.com/ansible-collections/kubernetes.core/issues/548).
|
||||
- k8s_cp - add support for check_mode (https://github.com/ansible-collections/kubernetes.core/issues/380).
|
||||
- k8s_drain - fix error caused by accessing an undefined variable when pods have local storage (https://github.com/ansible-collections/kubernetes.core/issues/292).
|
||||
- k8s_info - don't wait on empty List resources (https://github.com/ansible-collections/kubernetes.core/pull/253).
|
||||
- k8s_info - fix issue when module returns successful true after the resource cache has been established during periods where communication to the api-server is not possible (https://github.com/ansible-collections/kubernetes.core/issues/508).
|
||||
- k8s_log - Fix module traceback when no resource found (https://github.com/ansible-collections/kubernetes.core/issues/479).
|
||||
- k8s_log - fix exception raised when the name is not provided for resources requiring. (https://github.com/ansible-collections/kubernetes.core/issues/514)
|
||||
- k8s_scale - fix waiting on statefulset when scaled down to 0 replicas (https://github.com/ansible-collections/kubernetes.core/issues/203).
|
||||
- module_utils.common - change default opening mode to read-bytes to avoid bad interpretation of non ascii characters and strings, often present in 3rd party manifests.
|
||||
- module_utils/k8s/client.py - fix issue when trying to authenticate with host, client_cert and client_key parameters only.
|
||||
- remove binary file from k8s_cp test suite (https://github.com/ansible-collections/kubernetes.core/pull/298).
|
||||
- use resource prefix when finding resource and apiVersion is v1 (https://github.com/ansible-collections/kubernetes.core/issues/351).
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- helm_pull - download a chart from a repository and (optionally) unpack it in local directory.
|
||||
|
||||
v2.3.1
|
||||
======
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Catch expectation raised when the process is waiting for resources (https://github.com/ansible-collections/kubernetes.core/issues/407).
|
||||
- Catch exception raised when the process is waiting for resources (https://github.com/ansible-collections/kubernetes.core/issues/407).
|
||||
- Remove `omit` placeholder when defining resource using template parameter (https://github.com/ansible-collections/kubernetes.core/issues/431).
|
||||
- k8s - fix the issue when trying to delete resources using label_selectors options (https://github.com/ansible-collections/kubernetes.core/issues/433).
|
||||
- k8s_cp - fix issue when using parameter local_path with file on managed node. (https://github.com/ansible-collections/kubernetes.core/issues/421).
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
# Also needs to be updated in galaxy.yml
|
||||
VERSION = 3.0.0
|
||||
VERSION = 2.3.2
|
||||
|
||||
TEST_ARGS ?= ""
|
||||
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`
|
||||
|
||||
13
README.md
13
README.md
@@ -9,10 +9,10 @@ The collection includes a variety of Ansible content to help automate the manage
|
||||
<!--start requires_ansible-->
|
||||
## Ansible version compatibility
|
||||
|
||||
This collection has been tested against following Ansible versions: **>=2.14.0**.
|
||||
This collection has been tested against following Ansible versions: **>=2.9.17**.
|
||||
|
||||
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`).
|
||||
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.
|
||||
@@ -20,13 +20,13 @@ PEP440 is the schema used to describe the versions of Ansible.
|
||||
|
||||
## Python Support
|
||||
|
||||
* Collection supports 3.9+
|
||||
* Collection supports 3.6+
|
||||
|
||||
Note: Python2 is deprecated from [1st January 2020](https://www.python.org/doc/sunset-python-2/). Please switch to Python3.
|
||||
|
||||
## Kubernetes Version Support
|
||||
|
||||
This collection supports Kubernetes versions >= 1.24.
|
||||
This collection supports Kubernetes versions >=1.19.
|
||||
|
||||
## Included content
|
||||
|
||||
@@ -61,7 +61,6 @@ Name | Description
|
||||
[kubernetes.core.helm_info](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_info_module.rst)|Get information from Helm package deployed inside the cluster
|
||||
[kubernetes.core.helm_plugin](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_plugin_module.rst)|Manage Helm plugins
|
||||
[kubernetes.core.helm_plugin_info](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_plugin_info_module.rst)|Gather information about Helm plugins
|
||||
[kubernetes.core.helm_pull](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_pull_module.rst)|download a chart from a repository and (optionally) unpack it in local directory.
|
||||
[kubernetes.core.helm_repository](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_repository_module.rst)|Manage Helm repositories.
|
||||
[kubernetes.core.helm_template](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_template_module.rst)|Render chart templates
|
||||
[kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_module.rst)|Manage Kubernetes (K8s) objects
|
||||
@@ -93,7 +92,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
|
||||
---
|
||||
collections:
|
||||
- name: kubernetes.core
|
||||
version: 3.0.0
|
||||
version: 2.3.2
|
||||
```
|
||||
|
||||
### Installing the Kubernetes Python Library
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
kubernetes-client [platform:fedora]
|
||||
openshift-clients [platform:rhel-8]
|
||||
openshift-clients [platform:rhel-9]
|
||||
|
||||
@@ -587,230 +587,14 @@ releases:
|
||||
- 432-fix-issue-when-using-template-parameter.yaml
|
||||
- 434-fix-k8s-delete-using-label_selector.yaml
|
||||
release_date: '2022-05-02'
|
||||
2.4.0:
|
||||
2.3.2:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Fix dry_run logic - Pass the value dry_run=All instead of dry_run=True to
|
||||
the client, add conditional check on kubernetes client version as this feature
|
||||
is supported only for kubernetes >= 18.20.0 (https://github.com/ansible-collections/kubernetes.core/pull/561).
|
||||
- Fix kubeconfig parameter when multiple config files are provided (https://github.com/ansible-collections/kubernetes.core/issues/435).
|
||||
- Helm - Fix issue with alternative kubeconfig provided with validate_certs=False
|
||||
(https://github.com/ansible-collections/kubernetes.core/issues/538).
|
||||
- Various modules and plugins - use vendored version of ``distutils.version``
|
||||
instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/kubernetes.core/pull/314).
|
||||
- add missing documentation for filter plugin kubernetes.core.k8s_config_resource_name
|
||||
(https://github.com/ansible-collections/kubernetes.core/issues/558).
|
||||
- common - Ensure the label_selectors parameter of _wait_for method is optional.
|
||||
- common - handle ``aliases`` passed from inventory and lookup plugins.
|
||||
- helm_template - evaluate release_values after values_files, insuring highest
|
||||
precedence (now same behavior as in helm module). (https://github.com/ansible-collections/kubernetes.core/pull/348)
|
||||
- import exception from ``kubernetes.client.rest``.
|
||||
- k8s - Fix issue with check_mode when using server side apply (https://github.com/ansible-collections/kubernetes.core/issues/547).
|
||||
- k8s - Fix issue with server side apply with kubernetes release '25.3.0' (https://github.com/ansible-collections/kubernetes.core/issues/548).
|
||||
- k8s_cp - add support for check_mode (https://github.com/ansible-collections/kubernetes.core/issues/380).
|
||||
- k8s_drain - fix error caused by accessing an undefined variable when pods
|
||||
have local storage (https://github.com/ansible-collections/kubernetes.core/issues/292).
|
||||
- k8s_info - don't wait on empty List resources (https://github.com/ansible-collections/kubernetes.core/pull/253).
|
||||
- k8s_info - fix issue when module returns successful true after the resource
|
||||
cache has been established during periods where communication to the api-server
|
||||
is not possible (https://github.com/ansible-collections/kubernetes.core/issues/508).
|
||||
- k8s_log - Fix module traceback when no resource found (https://github.com/ansible-collections/kubernetes.core/issues/479).
|
||||
- k8s_log - fix exception raised when the name is not provided for resources
|
||||
requiring. (https://github.com/ansible-collections/kubernetes.core/issues/514)
|
||||
- k8s_scale - fix waiting on statefulset when scaled down to 0 replicas (https://github.com/ansible-collections/kubernetes.core/issues/203).
|
||||
- module_utils.common - change default opening mode to read-bytes to avoid bad
|
||||
interpretation of non ascii characters and strings, often present in 3rd party
|
||||
manifests.
|
||||
- module_utils/k8s/client.py - fix issue when trying to authenticate with host,
|
||||
client_cert and client_key parameters only.
|
||||
- remove binary file from k8s_cp test suite (https://github.com/ansible-collections/kubernetes.core/pull/298).
|
||||
- use resource prefix when finding resource and apiVersion is v1 (https://github.com/ansible-collections/kubernetes.core/issues/351).
|
||||
major_changes:
|
||||
- refactor K8sAnsibleMixin into module_utils/k8s/ (https://github.com/ansible-collections/kubernetes.core/pull/481).
|
||||
minor_changes:
|
||||
- Adjust k8s_user_impersonation tests to be compatible with Kubernetes 1.24
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/520).
|
||||
- add support for dry run with kubernetes client version >=18.20 (https://github.com/ansible-collections/kubernetes.core/pull/245).
|
||||
- added ignore.txt for Ansible 2.14 devel branch.
|
||||
- fixed module_defaults by removing routing hacks from runtime.yml (https://github.com/ansible-collections/kubernetes.core/pull/347).
|
||||
- helm - add support for -set-file, -set-json, -set and -set-string options
|
||||
when running helm install (https://github.com/ansible-collections/kubernetes.core/issues/533).
|
||||
- helm - add support for helm dependency update (https://github.com/ansible-collections/kubernetes.core/pull/208).
|
||||
- helm - add support for post-renderer flag (https://github.com/ansible-collections/kubernetes.core/issues/30).
|
||||
- helm - add support for timeout cli parameter to allow setting Helm timeout
|
||||
independent of wait (https://github.com/ansible-collections/kubernetes.core/issues/67).
|
||||
- helm - add support for wait parameter for helm uninstall command. (https://github.com/ansible-collections/kubernetes/core/issues/33).
|
||||
- helm - support repo location for helm diff (https://github.com/ansible-collections/kubernetes.core/issues/174).
|
||||
- helm - when ansible is executed in check mode, return the diff between what's
|
||||
deployed and what will be deployed.
|
||||
- helm, helm_plugin, helm_info, helm_plugin_info, kubectl - add support for
|
||||
in-memory kubeconfig. (https://github.com/ansible-collections/kubernetes.core/issues/492).
|
||||
- helm_info - add hooks, notes and manifest as part of returned information
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/546).
|
||||
- helm_info - add release state as a module argument (https://github.com/ansible-collections/kubernetes.core/issues/377).
|
||||
- helm_info - added possibility to get all values by adding get_all_values parameter
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/531).
|
||||
- helm_plugin - Add plugin_version parameter to the helm_plugin module (https://github.com/ansible-collections/kubernetes.core/issues/157).
|
||||
- helm_plugin - Add support for helm plugin update using state=update.
|
||||
- helm_repository - Ability to replace (overwrite) the repo if it already exists
|
||||
by forcing (https://github.com/ansible-collections/kubernetes.core/issues/491).
|
||||
- helm_repository - add support for pass-credentials cli parameter (https://github.com/ansible-collections/kubernetes.core/pull/282).
|
||||
- helm_repository - added support for ``host``, ``api_key``, ``validate_certs``,
|
||||
and ``ca_cert``.
|
||||
- helm_repository - mark `pass_credentials` as no_log=True to silence false
|
||||
warning (https://github.com/ansible-collections/kubernetes.core/issues/412).
|
||||
- helm_template - add name (NAME of release) and disable_hook as optional module
|
||||
arguments (https://github.com/ansible-collections/kubernetes.core/issues/313).
|
||||
- helm_template - add show_only and release_namespace as module arguments (https://github.com/ansible-collections/kubernetes.core/issues/313).
|
||||
- helm_template - add support for -set-file, -set-json, -set and -set-string
|
||||
options when running helm template (https://github.com/ansible-collections/kubernetes.core/pull/546).
|
||||
- k8s - add no_proxy support to k8s* (https://github.com/ansible-collections/kubernetes.core/pull/272).
|
||||
- k8s - add support for server_side_apply. (https://github.com/ansible-collections/kubernetes.core/issues/87).
|
||||
- k8s - add support for user impersonation. (https://github.com/ansible-collections/kubernetes/core/issues/40).
|
||||
- k8s - allow resource definition using metadata.generateName (https://github.com/ansible-collections/kubernetes.core/issues/35).
|
||||
- k8s lookup plugin - Enable turbo mode via environment variable (https://github.com/ansible-collections/kubernetes.core/issues/291).
|
||||
- k8s, k8s_scale, k8s_service - add support for resource definition as manifest
|
||||
via. (https://github.com/ansible-collections/kubernetes.core/issues/451).
|
||||
- k8s_cp - remove dependency with 'find' executable on remote pod when state=from_pod
|
||||
(https://github.com/ansible-collections/kubernetes.core/issues/486).
|
||||
- k8s_drain - Adds ``delete_emptydir_data`` option to ``k8s_drain.delete_options``
|
||||
to evict pods with an ``emptyDir`` volume attached (https://github.com/ansible-collections/kubernetes.core/pull/322).
|
||||
- k8s_exec - select first container from the pod if none specified (https://github.com/ansible-collections/kubernetes.core/issues/358).
|
||||
- k8s_exec - update deprecation warning for `return_code` (https://github.com/ansible-collections/kubernetes.core/issues/417).
|
||||
- k8s_json_patch - minor typo fix in the example section (https://github.com/ansible-collections/kubernetes.core/issues/411).
|
||||
- k8s_log - add the ``all_containers`` for retrieving all containers' logs in
|
||||
the pod(s).
|
||||
- k8s_log - added the `previous` parameter for retrieving the previously terminated
|
||||
pod logs (https://github.com/ansible-collections/kubernetes.core/issues/437).
|
||||
- k8s_log - added the `tail_lines` parameter to limit the number of lines to
|
||||
be retrieved from the end of the logs (https://github.com/ansible-collections/kubernetes.core/issues/488).
|
||||
- k8s_rollback - add support for check_mode. (https://github.com/ansible-collections/kubernetes/core/issues/243).
|
||||
- k8s_scale - add support for check_mode. (https://github.com/ansible-collections/kubernetes/core/issues/244).
|
||||
- kubectl - wait for dd command to complete before proceeding (https://github.com/ansible-collections/kubernetes.core/pull/321).
|
||||
- kubectl.py - replace distutils.spawn.find_executable with shutil.which in
|
||||
the kubectl connection plugin (https://github.com/ansible-collections/kubernetes.core/pull/456).
|
||||
fragments:
|
||||
- 0-copy_ignore_txt.yml
|
||||
- 208-add-dependency-update.yaml
|
||||
- 226-add-version-parameter-to-helm_plugin.yml
|
||||
- 231-helm-add-timeout-parameter.yaml
|
||||
- 238-helm-add-support-for-helm-uninstall-wait.yaml
|
||||
- 238-k8s-add-support-for-generate_name.yml
|
||||
- 245-add-dry-run.yaml
|
||||
- 250-k8s-add-support-for-impersonation.yaml
|
||||
- 253-dont-wait-on-list-resources.yaml
|
||||
- 255-k8s_scale-k8s_rollback-add-support-for-check_mode.yml
|
||||
- 260-k8s-add-support-for-server_side_apply.yml
|
||||
- 272-k8s-add-support-no_proxy.yaml
|
||||
- 282-helm-repository-add-pass-credentials.yaml
|
||||
- 290-returns-diff-in-check-mode.yaml
|
||||
- 295-fix-k8s-drain-variable-declaration.yaml
|
||||
- 298-remove-binary-file.yaml
|
||||
- 30-helm-add-post-renderer-support.yml
|
||||
- 308-fix-for-common-non-ascii-characters-in-resources.yaml
|
||||
- 313-helm-template-add-support-for-name-and-disablehook.yml
|
||||
- 313-helm-template-add-support-for-show-only-and-release-namespace.yml
|
||||
- 321-kubectl_sleep.yml
|
||||
- 322-Add-delete_emptydir_data-to-drain-delete_options.yaml
|
||||
- 335-k8s-lookup-add-support-for-turbo-mode.yml
|
||||
- 347-routing.yml
|
||||
- 348-helm_template-fix-precedence-of-release-values-over-values-files.yaml
|
||||
- 358-k8s_exec.yml
|
||||
- 364-use-resource-prefix.yaml
|
||||
- 377-helm-info-state.yml
|
||||
- 389-helm-add-support-chart_repo_url-on-helm_diff.yml
|
||||
- 391-fix-statefulset-wait.yaml
|
||||
- 411_k8s_json_patch.yml
|
||||
- 412_pass_creds.yml
|
||||
- 417_deprecation.yml
|
||||
- 428-fix-kubeconfig-parameter-with-multiple-config-files.yaml
|
||||
- 437-k8s-add-support-for-previous-logs.yaml
|
||||
- 456-replace-distutils.yml
|
||||
- 478-add-support-for-manifest-url.yaml
|
||||
- 481-refactor-common.yml
|
||||
- 488-add-support-for-tail-logs.yaml
|
||||
- 493-k8s_log-fix-module-when-pod-does-exist.yaml
|
||||
- 497-helm-add-support-for-in-memory-kubeconfig.yml
|
||||
- 498-k8s-honor-aliases.yaml
|
||||
- 505-add-from-yaml-all-example.yml
|
||||
- 509-helm-repo-add-force_update-argument.yaml
|
||||
- 512-k8s_cp-add-support-for-check_mode-update-command-for-listing-files-into-pod.yaml
|
||||
- 515-update-sanity-for-2-15.yml
|
||||
- 522-fix-helm-tests.yml
|
||||
- 523-helm_info-get-all-values.yaml
|
||||
- 528-k8s_log-support-all_containers-options.yml
|
||||
- 532-k8s_crd-fix-integration-test.yml
|
||||
- 546-helm-install-add-support-for-set-options.yaml
|
||||
- 549-fix-server-side-apply.yaml
|
||||
- 552-k8s_cp-fix-issue-when-copying-item-with-space-in-its-name.yml
|
||||
- 561-fix-dry-run.yml
|
||||
- 562-helm-fix-issue-when-alternative-kubeconfig-is-provided.yaml
|
||||
- 571-k8s_info-fix-issue-with-api-server.yaml
|
||||
- _wait_for_label_selector_optional.yaml
|
||||
- disutils.version.yml
|
||||
- exception.yml
|
||||
- fix-ci-unit-tests.yaml
|
||||
- helm_repository.yml
|
||||
- ignore_2.14.yml
|
||||
- k8s_config_resource_name-add-missing-documentation.yml
|
||||
- k8s_rollback_reduce_tmeouts.yaml
|
||||
- k8s_user_impersonation_k8s_1_24.yaml
|
||||
- minor-tests-duration.yaml
|
||||
modules:
|
||||
- description: download a chart from a repository and (optionally) unpack it in
|
||||
local directory.
|
||||
name: helm_pull
|
||||
namespace: ''
|
||||
release_date: '2023-01-24'
|
||||
3.0.0:
|
||||
changes:
|
||||
breaking_changes:
|
||||
- Remove support for ansible-core < 2.14
|
||||
- Update python kubernetes library to 24.2.0, helm/kind-action to 1.8.0, kubernetes
|
||||
>= 1.24.
|
||||
bugfixes:
|
||||
- helm - Put the chart_ref into quotes when running ``helm show chart``, ``helm
|
||||
upgrade`` and ``helm dependency update`` commands (https://github.com/ansible-collections/kubernetes.core/issues/653).
|
||||
- helm - delete temporary file created when deploying chart with option ``release_values``
|
||||
set (https://github.com/ansible-collections/kubernetes.core/issues/530).
|
||||
- helm - fix issue occurring when uninstalling chart with statues others than
|
||||
``deployed`` (https://github.com/ansible-collections/kubernetes.core/issues/319).
|
||||
- helm - fix post_renderer argument breaking the helm deploy_command (https://github.com/ansible-collections/kubernetes.core/pull/586).
|
||||
- helm - use post_renderer when checking ``changed`` status for a helm release
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/588).
|
||||
- k8s_scale - clean handling of ResourceTimeout exception (https://github.com/ansible-collections/kubernetes.core/issues/583).
|
||||
- k8s_scale - fix issue when scaling StatefulSets with ``updateStrategy=OnDelete``
|
||||
(https://github.com/ansible-collections/kubernetes.core/issues/579).
|
||||
deprecated_features:
|
||||
- k8s - the ``k8s`` inventory plugin has been deprecated and will be removed
|
||||
in release 4.0.0 (https://github.com/ansible-collections/kubernetes.core/issues/31).
|
||||
minor_changes:
|
||||
- helm - add ``reuse_values`` and ``reset_values`` support to helm module (https://github.com/ansible-collections/kubernetes.core/issues/394).
|
||||
- k8s - add new option ``delete_all`` to support deletion of all resources when
|
||||
state is set to ``absent``. (https://github.com/ansible-collections/kubernetes.core/issues/504)
|
||||
- k8s, k8s_info - add a hidden_fields option to allow fields to be hidden in
|
||||
the results of k8s and k8s_info
|
||||
- k8s_drain - add ability to filter the list of pods to be drained by a pod
|
||||
label selector (https://github.com/ansible-collections/kubernetes.core/issues/474).
|
||||
release_summary: This major release drops support for ansible-core versions
|
||||
lower than 2.14, Python versions lower than 3.9 and updates python kubernetes
|
||||
library to 24.2.0, helm/kind-action to 1.8.0, kubernetes >= 1.24, along with
|
||||
bug fixes and minor changes.
|
||||
fragments:
|
||||
- 20230206-deprecate-k8s-inventory.yml
|
||||
- 20231110-helm-quote-ref.yaml
|
||||
- 517-k8s-make-name-optional.yaml
|
||||
- 575-helm-add-support-for-reuse_values-and-reset_values.yml
|
||||
- 579-k8s_scale-fix-issue-with-scaling-statefulsets.yml
|
||||
- 583-k8s_scale-clean-handling-of-ResourceTimeout-exception.yaml
|
||||
- 586-helm-fix-post-renderer-arg.yml
|
||||
- 588-helm-use-post-renderer-for-helmdiff.yml
|
||||
- 589-helm-uninstall-chart-releases-with-statuses-different-than-deployed.yaml
|
||||
- 606-k8s_drain-add-pod_selectors-parameter.yaml
|
||||
- 612-fix-helm-tests.yaml
|
||||
- 629-add-hidden-fields-option.yaml
|
||||
- gha-sanity-fixes.yaml
|
||||
- helm-delete-temporary-file-created-when-using-option-release_values.yaml
|
||||
- remove_ansible_2_13.yaml
|
||||
- update_supported_versions.yaml
|
||||
release_date: '2023-11-17'
|
||||
release_date: '2022-06-09'
|
||||
|
||||
@@ -105,27 +105,6 @@ Parameters
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: kube_context</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>get_all_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.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>Set to <code>True</code> if you want to get all (computed) values of the release.</div>
|
||||
<div>When <code>False</code> (default), only user supplied values are returned.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -148,7 +127,7 @@ Parameters
|
||||
<b>kubeconfig</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">raw</span>
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -156,7 +135,6 @@ Parameters
|
||||
<td>
|
||||
<div>Helm option to specify kubeconfig path to use.</div>
|
||||
<div>If the value is not specified in the task, the value of environment variable <code>K8S_AUTH_KUBECONFIG</code> will be used instead.</div>
|
||||
<div>The configuration can be provided as dictionary. Added in version 2.4.0.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: kubeconfig_path</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -206,7 +184,6 @@ Parameters
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>Show releases as per their states.</div>
|
||||
@@ -325,42 +302,6 @@ 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>hooks</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
/ <span style="color: purple">elements=dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Hooks of the release</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>manifest</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
/ <span style="color: purple">elements=dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Manifest of the release</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
@@ -393,23 +334,6 @@ 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>notes</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>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Notes of the release</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
|
||||
@@ -36,12 +36,12 @@ Parameters
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="2">Parameter</th>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>api_key</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -57,7 +57,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>atomic</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -76,7 +76,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>binary_path</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -91,7 +91,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>ca_cert</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -108,7 +108,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_ref</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -127,7 +127,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_repo_url</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -142,7 +142,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_version</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -157,7 +157,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>context</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -174,7 +174,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>create_namespace</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -194,32 +194,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>dependency_update</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 2.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>Run standalone <code>helm dependency update CHART</code> before the operation.</div>
|
||||
<div>Run inline <code>--dependency-update</code> with <code>helm install</code> command. This feature is not supported yet with the <code>helm upgrade</code> command.</div>
|
||||
<div>So we should consider to use <em>dependency_update</em> options with <em>replace</em> option enabled when specifying <em>chart_repo_url</em>.</div>
|
||||
<div>The <em>dependency_update</em> option require the add of <code>dependencies</code> block in <code>Chart.yaml/requirements.yaml</code> file.</div>
|
||||
<div>For more information please visit <a href='https://helm.sh/docs/helm/helm_dependency/'>https://helm.sh/docs/helm/helm_dependency/</a></div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: dep_up</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>disable_hook</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -238,7 +213,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>force</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -257,7 +232,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>history_max</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -274,7 +249,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>host</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -290,12 +265,12 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>kubeconfig</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">raw</span>
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -303,28 +278,11 @@ Parameters
|
||||
<td>
|
||||
<div>Helm option to specify kubeconfig path to use.</div>
|
||||
<div>If the value is not specified in the task, the value of environment variable <code>K8S_AUTH_KUBECONFIG</code> will be used instead.</div>
|
||||
<div>The configuration can be provided as dictionary. Added in version 2.4.0.</div>
|
||||
<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>post_renderer</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an executable to be used for post rendering.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>purge</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -343,7 +301,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_name</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -360,7 +318,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_namespace</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -377,7 +335,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_state</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -397,7 +355,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -414,7 +372,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>replace</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -436,108 +394,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>reset_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.5.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>When upgrading package, reset the values to the ones built into the chart.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>reuse_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.5.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>no</li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>When upgrading package, specifies wether to reuse the last release's values and merge in any overrides from parameters <em>release_values</em>, <em>values_files</em> or <em>set_values</em>.</div>
|
||||
<div>If <em>reset_values</em> is set to <code>True</code>, this is ignored.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>set_values</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=dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Values to pass to chart configuration</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>value</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Value to pass to chart configuration (e.g phase=prod).</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>value_type</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">-</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>raw</b> ←</div></li>
|
||||
<li>string</li>
|
||||
<li>json</li>
|
||||
<li>file</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use <code>raw</code> set individual value.</div>
|
||||
<div>Use <code>string</code> to force a string for an individual value.</div>
|
||||
<div>Use <code>file</code> to set individual values from a file when the value itself is too long for the command line or is dynamically generated.</div>
|
||||
<div>Use <code>json</code> to set json values (scalars/objects/arrays). This feature requires helm>=3.10.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>skip_crds</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -557,7 +414,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>timeout</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -575,7 +432,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>update_repo_cache</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -594,7 +451,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>validate_certs</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -615,7 +472,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>values_files</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -636,7 +493,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>wait</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -656,7 +513,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>wait_timeout</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -737,15 +594,6 @@ Examples
|
||||
state: absent
|
||||
update_repo_cache: true
|
||||
|
||||
- name: Deploy Grafana chart using set values on target
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: stable/grafana
|
||||
release_namespace: monitoring
|
||||
set_values:
|
||||
- value: phase=prod
|
||||
value_type: string
|
||||
|
||||
# From git
|
||||
- name: Git clone stable repo on HEAD
|
||||
ansible.builtin.git:
|
||||
@@ -791,17 +639,6 @@ Examples
|
||||
logging:
|
||||
enabled: True
|
||||
|
||||
# Deploy latest version
|
||||
- name: Deploy latest version of Grafana chart using reuse_values
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: stable/grafana
|
||||
release_namespace: monitoring
|
||||
reuse_values: true
|
||||
values:
|
||||
replicas: 2
|
||||
version: 3e8ec0b2dffa40fb97d5342e4af887de95faa8c61a62480dd7f8aa03dffcf533
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
|
||||
@@ -126,7 +126,7 @@ Parameters
|
||||
<b>kubeconfig</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">raw</span>
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -134,7 +134,6 @@ Parameters
|
||||
<td>
|
||||
<div>Helm option to specify kubeconfig path to use.</div>
|
||||
<div>If the value is not specified in the task, the value of environment variable <code>K8S_AUTH_KUBECONFIG</code> will be used instead.</div>
|
||||
<div>The configuration can be provided as dictionary. Added in version 2.4.0.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: kubeconfig_path</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -126,7 +126,7 @@ Parameters
|
||||
<b>kubeconfig</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">raw</span>
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -134,7 +134,6 @@ Parameters
|
||||
<td>
|
||||
<div>Helm option to specify kubeconfig path to use.</div>
|
||||
<div>If the value is not specified in the task, the value of environment variable <code>K8S_AUTH_KUBECONFIG</code> will be used instead.</div>
|
||||
<div>The configuration can be provided as dictionary. Added in version 2.4.0.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: kubeconfig_path</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,467 +0,0 @@
|
||||
.. _kubernetes.core.helm_pull_module:
|
||||
|
||||
|
||||
*************************
|
||||
kubernetes.core.helm_pull
|
||||
*************************
|
||||
|
||||
**download a chart from a repository and (optionally) unpack it in local directory.**
|
||||
|
||||
|
||||
Version added: 2.4.0
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
- Retrieve a package from a package repository, and download it locally.
|
||||
- It can also be used to perform cryptographic verification of a chart without installing the chart.
|
||||
- There are options for unpacking the chart after download.
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm >= 3.0 (https://github.com/helm/helm/releases)
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>binary_path</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The path of a helm binary to use.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_ca_cert</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Verify certificates of HTTPS-enabled servers using this CA bundle.</div>
|
||||
<div>Requires helm >= 3.1.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_devel</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>no</li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use development versions, too. Equivalent to version '>0.0.0-0'.</div>
|
||||
<div>Mutually exclusive with <code>chart_version</code>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_ref</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>chart name on chart repository.</div>
|
||||
<div>absolute URL.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_ssl_cert_file</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Identify HTTPS client using this SSL certificate file.</div>
|
||||
<div>Requires helm >= 3.1.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_ssl_key_file</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Identify HTTPS client using this SSL key file</div>
|
||||
<div>Requires helm >= 3.1.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_version</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Specify a version constraint for the chart version to use.</div>
|
||||
<div>This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0).</div>
|
||||
<div>Mutually exclusive with <code>chart_devel</code>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>destination</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">path</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>location to write the chart.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>pass_credentials</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</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>Pass credentials to all domains.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>provenance</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</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>Fetch the provenance file, but don't perform verification.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>repo_password</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Chart repository password where to locate the requested chart.</div>
|
||||
<div>Required if <code>repo_username</code> is specified.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: password, chart_repo_password</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>repo_url</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>chart repository url where to locate the requested chart.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: url, chart_repo_url</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>repo_username</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Chart repository username where to locate the requested chart.</div>
|
||||
<div>Required if <code>repo_password</code> is specified.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: username, chart_repo_username</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>skip_tls_certs_check</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</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>Whether or not to check tls certificate for the chart download.</div>
|
||||
<div>Requires helm >= 3.3.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>untar_chart</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</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>if set to true, will untar the chart after downloading it.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>verify_chart</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</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>Verify the package before using it.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>verify_chart_keyring</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">path</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>location of public keys used for verification.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- name: Download chart using chart url
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz
|
||||
destination: /path/to/chart
|
||||
|
||||
- name: Download Chart using chart_name and repo_url
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
untar_chart: yes
|
||||
destination: /path/to/chart
|
||||
|
||||
- name: Download Chart (skip tls certificate check)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
untar_chart: yes
|
||||
destination: /path/to/chart
|
||||
skip_tls_certs_check: yes
|
||||
|
||||
- name: Download Chart using chart registry credentials
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
untar_chart: yes
|
||||
destination: /path/to/chart
|
||||
username: myuser
|
||||
password: mypassword123
|
||||
|
||||
|
||||
|
||||
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:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Key</th>
|
||||
<th>Returned</th>
|
||||
<th width="100%">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>command</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>always</td>
|
||||
<td>
|
||||
<div>Full `helm pull` command built by this module, in case you want to re-run the command outside the module or debug a problem.</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;">helm pull --repo test ...</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>rc</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">integer</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Helm pull command return code</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;">1</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>stderr</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>always</td>
|
||||
<td>
|
||||
<div>Full `helm pull` command stderr, in case you want to display it or examine the event log</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>stdout</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>always</td>
|
||||
<td>
|
||||
<div>Full `helm pull` command stdout, in case you want to display it or examine the event log</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/><br/>
|
||||
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
- Aubin Bikouo (@abikouo)
|
||||
@@ -88,45 +88,6 @@ Parameters
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>context</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Helm option to specify which kubeconfig context to use.</div>
|
||||
<div>If the value is not specified in the task, the value of environment variable <code>K8S_AUTH_CONTEXT</code> will be used instead.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: kube_context</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>force_update</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 2.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>Whether or not to replace (overwrite) the repo if it already exists.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: force</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -143,25 +104,6 @@ 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>kubeconfig</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 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Helm option to specify kubeconfig path to use.</div>
|
||||
<div>If the value is not specified in the task, the value of environment variable <code>K8S_AUTH_KUBECONFIG</code> will be used instead.</div>
|
||||
<div>The configuration can be provided as dictionary.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: kubeconfig_path</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
|
||||
@@ -28,12 +28,12 @@ Parameters
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="2">Parameter</th>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>binary_path</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -48,7 +48,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_ref</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -67,7 +67,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_repo_url</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -82,7 +82,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_version</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -97,50 +97,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>dependency_update</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 2.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>Run helm dependency update before the operation.</div>
|
||||
<div>The <em>dependency_update</em> option require the add of <code>dependencies</code> block in <code>Chart.yaml/requirements.yaml</code> file.</div>
|
||||
<div>For more information please visit <a href='https://helm.sh/docs/helm/helm_dependency/'>https://helm.sh/docs/helm/helm_dependency/</a></div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: dep_up</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>disable_hook</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 2.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>Prevent hooks from running during install.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>include_crds</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -159,7 +116,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>output_dir</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -175,31 +132,14 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_name</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Release name to use in rendered templates.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: name</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_namespace</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
@@ -208,7 +148,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -225,67 +165,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>set_values</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=dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Values to pass to chart configuration.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>value</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Value to pass to chart configuration (e.g phase=prod).</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>value_type</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">-</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>raw</b> ←</div></li>
|
||||
<li>string</li>
|
||||
<li>json</li>
|
||||
<li>file</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use <code>raw</code> set individual value.</div>
|
||||
<div>Use <code>string</code> to force a string for an individual value.</div>
|
||||
<div>Use <code>file</code> to set individual values from a file when the value itself is too long for the command line or is dynamically generated.</div>
|
||||
<div>Use <code>json</code> to set json values (scalars/objects/arrays). This feature requires helm>=3.10.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>show_only</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -293,17 +173,16 @@ Parameters
|
||||
<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 2.4.0</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>Only show manifests rendered from the given templates.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>update_repo_cache</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -322,7 +201,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>values_files</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
|
||||
@@ -27,8 +27,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -203,7 +203,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -25,8 +25,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -215,7 +215,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -558,7 +557,7 @@ Examples
|
||||
state: from_pod
|
||||
|
||||
# copy content into a file in the remote pod
|
||||
- name: Copy content into a file in the remote pod
|
||||
- name: Copy /tmp/foo from a remote pod to /tmp/bar locally
|
||||
kubernetes.core.k8s_cp:
|
||||
state: to_pod
|
||||
namespace: some-namespace
|
||||
|
||||
@@ -29,8 +29,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -132,7 +132,6 @@ Parameters
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">{}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>Specify options to delete pods.</div>
|
||||
@@ -336,7 +335,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -414,25 +412,6 @@ Parameters
|
||||
<div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>pod_selectors</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 2.5.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Label selector to filter pods on the node.</div>
|
||||
<div>This option has effect only when <code>state</code> is set to <em>drain</em>.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: label_selectors</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -602,7 +581,7 @@ Examples
|
||||
state: drain
|
||||
name: foo
|
||||
delete_options:
|
||||
terminate_grace_period: 900
|
||||
terminate_grace_period: 900
|
||||
|
||||
- name: Mark node "foo" as schedulable.
|
||||
kubernetes.core.k8s_drain:
|
||||
@@ -614,14 +593,6 @@ Examples
|
||||
state: cordon
|
||||
name: foo
|
||||
|
||||
- name: Drain node "foo" using label selector to filter the list of pods to be drained.
|
||||
kubernetes.core.k8s_drain:
|
||||
state: drain
|
||||
name: foo
|
||||
pod_selectors:
|
||||
- 'app!=csi-attacher'
|
||||
- 'app!=csi-provisioner'
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
|
||||
@@ -25,8 +25,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -215,7 +215,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -28,8 +28,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -153,31 +153,11 @@ Parameters
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of field selectors to use to filter results</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 2.5.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -260,7 +240,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -275,7 +254,6 @@ Parameters
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of label selectors to use to filter results</div>
|
||||
|
||||
@@ -13,15 +13,6 @@ kubernetes.core.k8s
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
DEPRECATED
|
||||
----------
|
||||
:Removed in collection release after
|
||||
:Why: As discussed in https://github.com/ansible-collections/kubernetes.core/issues/31, we decided to
|
||||
remove the k8s inventory plugin in release 4.0.0.
|
||||
|
||||
:Alternative: Use :ref:`kubernetes.core.k8s_info <kubernetes.core.k8s_info_module>` and :ref:`ansible.builtin.add_host <ansible.builtin.add_host_module>` instead.
|
||||
|
||||
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
@@ -36,8 +27,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the local Ansible controller node that executes this inventory.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -331,24 +322,24 @@ Examples
|
||||
|
||||
# File must be named k8s.yaml or k8s.yml
|
||||
|
||||
- name: Authenticate with token, and return all pods and services for all namespaces
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- host: https://192.168.64.4:8443
|
||||
api_key: xxxxxxxxxxxxxxxx
|
||||
validate_certs: false
|
||||
# Authenticate with token, and return all pods and services for all namespaces
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- host: https://192.168.64.4:8443
|
||||
api_key: xxxxxxxxxxxxxxxx
|
||||
validate_certs: false
|
||||
|
||||
- name: Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- namespaces:
|
||||
- testing
|
||||
# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- namespaces:
|
||||
- testing
|
||||
|
||||
- name: Use a custom config file, and a specific context.
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- kubeconfig: /path/to/config
|
||||
context: 'awx/192-168-64-4:8443/developer'
|
||||
# Use a custom config file, and a specific context.
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- kubeconfig: /path/to/config
|
||||
context: 'awx/192-168-64-4:8443/developer'
|
||||
|
||||
|
||||
|
||||
@@ -357,10 +348,6 @@ Status
|
||||
------
|
||||
|
||||
|
||||
- This inventory will be removed in version 4.0.0. *[deprecated]*
|
||||
- For more information see `DEPRECATED`_.
|
||||
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
- jsonpatch
|
||||
|
||||
@@ -220,7 +220,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -614,7 +613,7 @@ Examples
|
||||
path: /metadata/labels/app
|
||||
value: myapp
|
||||
- op: replace
|
||||
path: /spec/containers/0/image
|
||||
patch: /spec/containers/0/image
|
||||
value: nginx
|
||||
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -44,27 +44,6 @@ Parameters
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>all_containers</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 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>no</li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>If set to <code>true</code>, retrieve all containers' logs in the pod(s).</div>
|
||||
<div>mutually exclusive with <code>container</code>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -162,8 +141,7 @@ Parameters
|
||||
<td>
|
||||
<div>Use to specify the container within a pod to grab the log from.</div>
|
||||
<div>If there is only one container, this will default to that container.</div>
|
||||
<div>If there is more than one container, this option is required or set <em>all_containers</em> to <code>true</code>.</div>
|
||||
<div>mutually exclusive with <code>all_containers</code>.</div>
|
||||
<div>If there is more than one container, this option is required.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -262,7 +240,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -277,7 +254,6 @@ Parameters
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of label selectors to use to filter results</div>
|
||||
@@ -377,26 +353,6 @@ Parameters
|
||||
<div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>previous</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 2.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>If <code>true</code>, print the logs for the previous instance of the container in a pod if it exists.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -498,22 +454,6 @@ Parameters
|
||||
<div>A relative time in seconds before the current time from which to show logs.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>tail_lines</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">integer</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>A number of lines from the end of the logs to retrieve.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -598,16 +538,8 @@ Examples
|
||||
kind: DeploymentConfig
|
||||
namespace: testing
|
||||
name: example
|
||||
tail_lines: 100
|
||||
register: log
|
||||
|
||||
# This will get the logs from all containers in Pod
|
||||
- name: Get the logs from all containers in pod
|
||||
kubernetes.core.k8s_log:
|
||||
namespace: testing
|
||||
name: some-pod
|
||||
all_containers: true
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
|
||||
@@ -26,8 +26,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the local Ansible controller node that executes this lookup.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
- jsonpatch
|
||||
|
||||
@@ -208,30 +208,6 @@ Parameters
|
||||
<div>This has no effect on the validation step which is controlled by the <code>validate.fail_on_error</code> parameter.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>delete_all</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 2.5.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>When this option is set to <em>true</em> and <em>state=absent</em>, module will delete all resources of the specified resource type in the requested namespace.</div>
|
||||
<div>Ignored when <code>state</code> is not set to <em>absent</em> or when one of (src), <code>name</code> or <code>resource_definition</code> is provided.</div>
|
||||
<div>Parameter <code>kind</code> is required to use this option.</div>
|
||||
<div>This parameter can be used with <code>label_selectors</code> to restrict the resources to be deleted.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: all</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -380,25 +356,6 @@ Parameters
|
||||
<div>mutually exclusive with <code>name</code>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<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 2.5.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -480,7 +437,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -794,7 +750,6 @@ Parameters
|
||||
<td>
|
||||
<div>Provide a path to a file containing a valid YAML definition of an object or objects to be created or updated. Mutually exclusive with <em>resource_definition</em>. NOTE: <em>kind</em>, <em>api_version</em>, <em>name</em>, and <em>namespace</em> will be overwritten by corresponding values found in the configuration read in from the <em>src</em> file.</div>
|
||||
<div>Reads from the local file system. To read from the Ansible controller's file system, including vaulted files, use the file lookup plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to <em>resource_definition</em>. See Examples below.</div>
|
||||
<div>The URL to manifest files that can be used to create the resource. Added in version 2.4.0.</div>
|
||||
<div>Mutually exclusive with <em>template</em> in case of <span class='module'>kubernetes.core.k8s</span> module.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -1157,14 +1112,6 @@ Examples
|
||||
state: present
|
||||
definition: "{{ lookup('file', '/testing/deployment.yml') | from_yaml }}"
|
||||
|
||||
- name: >-
|
||||
(Alternative) Read definition file from the Ansible controller file system.
|
||||
In this case, the definition file contains multiple YAML documents, separated by ---.
|
||||
If the definition file has been encrypted with Ansible Vault it will automatically be decrypted.
|
||||
kubernetes.core.k8s:
|
||||
state: present
|
||||
definition: "{{ lookup('file', '/testing/deployment.yml') | from_yaml_all }}"
|
||||
|
||||
- name: Read definition template file from the Ansible controller file system
|
||||
kubernetes.core.k8s:
|
||||
state: present
|
||||
@@ -1182,10 +1129,10 @@ Examples
|
||||
kubernetes.core.k8s:
|
||||
state: present
|
||||
template:
|
||||
- path: '/testing/deployment_one.j2'
|
||||
- path: '/testing/deployment_two.j2'
|
||||
variable_start_string: '[['
|
||||
variable_end_string: ']]'
|
||||
- path: '/testing/deployment_one.j2'
|
||||
- path: '/testing/deployment_two.j2'
|
||||
variable_start_string: '[['
|
||||
variable_end_string: ']]'
|
||||
|
||||
- name: fail on validation errors
|
||||
kubernetes.core.k8s:
|
||||
@@ -1269,14 +1216,6 @@ Examples
|
||||
server_side_apply:
|
||||
field_manager: ansible
|
||||
|
||||
# Delete all Deployment from specified namespace
|
||||
- name: Delete all Deployment from specified namespace
|
||||
kubernetes.core.k8s:
|
||||
api_version: apps/v1
|
||||
namespace: testing
|
||||
kind: Deployment
|
||||
delete_all: true
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
|
||||
@@ -27,8 +27,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -152,7 +152,6 @@ Parameters
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of field selectors to use to filter results.</div>
|
||||
@@ -239,7 +238,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -254,7 +252,6 @@ Parameters
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of label selectors to use to filter results.</div>
|
||||
|
||||
@@ -25,8 +25,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -255,7 +255,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -271,7 +270,6 @@ Parameters
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of label selectors to use to filter results.</div>
|
||||
@@ -518,7 +516,6 @@ Parameters
|
||||
<td>
|
||||
<div>Provide a path to a file containing a valid YAML definition of an object or objects to be created or updated. Mutually exclusive with <em>resource_definition</em>. NOTE: <em>kind</em>, <em>api_version</em>, <em>name</em>, and <em>namespace</em> will be overwritten by corresponding values found in the configuration read in from the <em>src</em> file.</div>
|
||||
<div>Reads from the local file system. To read from the Ansible controller's file system, including vaulted files, use the file lookup plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to <em>resource_definition</em>. See Examples below.</div>
|
||||
<div>The URL to manifest files that can be used to create the resource. Added in version 2.4.0.</div>
|
||||
<div>Mutually exclusive with <em>template</em> in case of <span class='module'>kubernetes.core.k8s</span> module.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -24,8 +24,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -220,7 +220,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -488,7 +487,6 @@ Parameters
|
||||
<td>
|
||||
<div>Provide a path to a file containing a valid YAML definition of an object or objects to be created or updated. Mutually exclusive with <em>resource_definition</em>. NOTE: <em>kind</em>, <em>api_version</em>, <em>name</em>, and <em>namespace</em> will be overwritten by corresponding values found in the configuration read in from the <em>src</em> file.</div>
|
||||
<div>Reads from the local file system. To read from the Ansible controller's file system, including vaulted files, use the file lookup plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to <em>resource_definition</em>. See Examples below.</div>
|
||||
<div>The URL to manifest files that can be used to create the resource. Added in version 2.4.0.</div>
|
||||
<div>Mutually exclusive with <em>template</em> in case of <span class='module'>kubernetes.core.k8s</span> module.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -26,8 +26,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -182,7 +182,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
|
||||
<div>Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.</div>
|
||||
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -208,7 +208,6 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to a kubectl config file. Defaults to <em>~/.kube/config</em></div>
|
||||
<div>The configuration can be provided as dictionary. Added in version 2.4.0.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -9,8 +9,8 @@ authors:
|
||||
- mmazur (https://github.com/mmazur)
|
||||
- jamescassell (https://github.com/jamescassell)
|
||||
description: Kubernetes Collection for Ansible.
|
||||
documentation: ""
|
||||
homepage: ""
|
||||
documentation: ''
|
||||
homepage: ''
|
||||
issues: https://github.com/ansible-collections/kubernetes.core/issues
|
||||
license_file: LICENSE
|
||||
namespace: kubernetes
|
||||
@@ -25,7 +25,7 @@ tags:
|
||||
- openshift
|
||||
- okd
|
||||
- cluster
|
||||
version: 3.0.0
|
||||
version: 2.3.2
|
||||
build_ignore:
|
||||
- .DS_Store
|
||||
- "*.tar.gz"
|
||||
- '*.tar.gz'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
requires_ansible: '>=2.14.0'
|
||||
requires_ansible: '>=2.9.17'
|
||||
|
||||
action_groups:
|
||||
helm:
|
||||
@@ -20,12 +20,6 @@ plugin_routing:
|
||||
inventory:
|
||||
openshift:
|
||||
redirect: community.okd.openshift
|
||||
k8s:
|
||||
deprecation:
|
||||
removal_version: 4.0.0
|
||||
warning_text: >-
|
||||
The k8s inventory plugin has been deprecated and
|
||||
will be removed in release 4.0.0.
|
||||
modules:
|
||||
k8s_auth:
|
||||
redirect: community.okd.k8s_auth
|
||||
|
||||
@@ -8,21 +8,20 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import copy
|
||||
import os
|
||||
import platform
|
||||
import traceback
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
|
||||
from ansible.config.manager import ensure_type
|
||||
from ansible.errors import (
|
||||
AnsibleAction,
|
||||
AnsibleActionFail,
|
||||
AnsibleError,
|
||||
AnsibleFileNotFound,
|
||||
AnsibleAction,
|
||||
AnsibleActionFail,
|
||||
)
|
||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.module_utils.six import iteritems, string_types
|
||||
from ansible.module_utils.six import string_types, iteritems
|
||||
from ansible.module_utils._text import to_text, to_bytes, to_native
|
||||
from ansible.plugins.action import ActionBase
|
||||
|
||||
|
||||
@@ -51,10 +50,8 @@ class RemoveOmit(object):
|
||||
return [self.remove_omit(d) for d in self.data]
|
||||
|
||||
|
||||
ENV_KUBECONFIG_PATH_SEPARATOR = ";" if platform.system() == "Windows" else ":"
|
||||
|
||||
|
||||
class ActionModule(ActionBase):
|
||||
|
||||
TRANSFERS_FILES = True
|
||||
DEFAULT_NEWLINE_SEQUENCE = "\n"
|
||||
|
||||
@@ -311,15 +308,11 @@ class ActionModule(ActionBase):
|
||||
if not remote_transport:
|
||||
# kubeconfig is local
|
||||
# find in expected paths
|
||||
configs = []
|
||||
for config in kubeconfig.split(ENV_KUBECONFIG_PATH_SEPARATOR):
|
||||
config = self._find_needle("files", config)
|
||||
kubeconfig = self._find_needle("files", kubeconfig)
|
||||
|
||||
# decrypt kubeconfig found
|
||||
configs.append(self._loader.get_real_file(config, decrypt=True))
|
||||
new_module_args["kubeconfig"] = ENV_KUBECONFIG_PATH_SEPARATOR.join(
|
||||
configs
|
||||
)
|
||||
# decrypt kubeconfig found
|
||||
actual_file = self._loader.get_real_file(kubeconfig, decrypt=True)
|
||||
new_module_args["kubeconfig"] = actual_file
|
||||
|
||||
elif isinstance(kubeconfig, dict):
|
||||
new_module_args["kubeconfig"] = kubeconfig
|
||||
@@ -358,7 +351,7 @@ class ActionModule(ActionBase):
|
||||
# find the file in the expected search path
|
||||
src = self._task.args.get("src", None)
|
||||
|
||||
if src and not src.startswith(("http://", "https://", "ftp://")):
|
||||
if src:
|
||||
if remote_transport:
|
||||
# src is on remote node
|
||||
result.update(
|
||||
|
||||
@@ -75,7 +75,6 @@ DOCUMENTATION = r"""
|
||||
kubectl_kubeconfig:
|
||||
description:
|
||||
- Path to a kubectl config file. Defaults to I(~/.kube/config)
|
||||
- The configuration can be provided as dictionary. Added in version 2.4.0.
|
||||
default: ''
|
||||
vars:
|
||||
- name: ansible_kubectl_kubeconfig
|
||||
@@ -172,18 +171,16 @@ DOCUMENTATION = r"""
|
||||
aliases: [ kubectl_verify_ssl ]
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleFileNotFound
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.plugins.connection import BUFSIZE, ConnectionBase
|
||||
from ansible.errors import AnsibleError, AnsibleFileNotFound
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.plugins.connection import ConnectionBase, BUFSIZE
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
@@ -225,12 +222,6 @@ class Connection(ConnectionBase):
|
||||
self.transport_cmd = kwargs.get(cmd_arg, shutil.which(self.transport))
|
||||
if not self.transport_cmd:
|
||||
raise AnsibleError("{0} command not found in PATH".format(self.transport))
|
||||
self._file_to_delete = None
|
||||
|
||||
def delete_temporary_file(self):
|
||||
if self._file_to_delete is not None:
|
||||
os.remove(self._file_to_delete)
|
||||
self._file_to_delete = None
|
||||
|
||||
def _build_exec_cmd(self, cmd):
|
||||
"""Build the local kubectl exec command to run cmd on remote_host"""
|
||||
@@ -253,18 +244,6 @@ class Connection(ConnectionBase):
|
||||
self.connection_options[key], str(skip_verify_ssl).lower()
|
||||
)
|
||||
)
|
||||
elif key.endswith("kubeconfig") and self.get_option(key) != "":
|
||||
kubeconfig_path = self.get_option(key)
|
||||
if isinstance(kubeconfig_path, dict):
|
||||
fd, tmpfile = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as fp:
|
||||
json.dump(kubeconfig_path, fp)
|
||||
kubeconfig_path = tmpfile
|
||||
self._file_to_delete = tmpfile
|
||||
|
||||
cmd_arg = self.connection_options[key]
|
||||
local_cmd += [cmd_arg, kubeconfig_path]
|
||||
censored_local_cmd += [cmd_arg, kubeconfig_path]
|
||||
elif (
|
||||
not key.endswith("container")
|
||||
and self.get_option(key)
|
||||
@@ -332,7 +311,6 @@ class Connection(ConnectionBase):
|
||||
)
|
||||
|
||||
stdout, stderr = p.communicate(in_data)
|
||||
self.delete_temporary_file()
|
||||
return (p.returncode, stdout, stderr)
|
||||
|
||||
def _prefix_login_path(self, remote_path):
|
||||
@@ -385,7 +363,6 @@ class Connection(ConnectionBase):
|
||||
"kubectl connection requires dd command in the container to put files"
|
||||
)
|
||||
stdout, stderr = p.communicate()
|
||||
self.delete_temporary_file()
|
||||
|
||||
if p.returncode != 0:
|
||||
raise AnsibleError(
|
||||
@@ -424,7 +401,6 @@ class Connection(ConnectionBase):
|
||||
)
|
||||
)
|
||||
stdout, stderr = p.communicate()
|
||||
self.delete_temporary_file()
|
||||
|
||||
if p.returncode != 0:
|
||||
raise AnsibleError(
|
||||
|
||||
@@ -12,6 +12,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
binary_path:
|
||||
@@ -29,8 +30,7 @@ options:
|
||||
description:
|
||||
- Helm option to specify kubeconfig path to use.
|
||||
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_KUBECONFIG) will be used instead.
|
||||
- The configuration can be provided as dictionary. Added in version 2.4.0.
|
||||
type: raw
|
||||
type: path
|
||||
aliases: [ kubeconfig_path ]
|
||||
host:
|
||||
description:
|
||||
|
||||
@@ -11,6 +11,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
host:
|
||||
@@ -27,7 +28,6 @@ options:
|
||||
options are provided, the Kubernetes client will attempt to load the default
|
||||
configuration file from I(~/.kube/config). Can also be specified via K8S_AUTH_KUBECONFIG environment
|
||||
variable.
|
||||
- Multiple Kubernetes config file can be provided using separator ';' for Windows platform or ':' for others platforms.
|
||||
- The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.
|
||||
type: raw
|
||||
context:
|
||||
|
||||
@@ -11,6 +11,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
delete_options:
|
||||
|
||||
@@ -11,6 +11,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
api_version:
|
||||
|
||||
@@ -11,6 +11,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
resource_definition:
|
||||
@@ -28,7 +29,6 @@ options:
|
||||
- Reads from the local file system. To read from the Ansible controller's file system, including vaulted files, use the file lookup
|
||||
plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to
|
||||
I(resource_definition). See Examples below.
|
||||
- The URL to manifest files that can be used to create the resource. Added in version 2.4.0.
|
||||
- Mutually exclusive with I(template) in case of M(kubernetes.core.k8s) module.
|
||||
type: path
|
||||
"""
|
||||
|
||||
@@ -11,6 +11,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
replicas:
|
||||
|
||||
@@ -11,6 +11,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
state:
|
||||
|
||||
@@ -11,6 +11,7 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
wait:
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
# 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)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
DOCUMENTATION:
|
||||
name: k8s_config_resource_name
|
||||
short_description: Generate resource name for the given resource of type ConfigMap, Secret
|
||||
description:
|
||||
- Generate resource name for the given resource of type ConfigMap, Secret.
|
||||
- Resource must have a C(metadata.name) key to generate a resource name
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- A valid YAML definition for a ConfigMap or a Secret.
|
||||
type: dict
|
||||
required: true
|
||||
author:
|
||||
- ansible cloud team
|
||||
|
||||
EXAMPLES: |
|
||||
# Dump generated name for a configmap into a variable
|
||||
- set_fact:
|
||||
generated_name: '{{ definition | kubernetes.core.k8s_config_resource_name }}'
|
||||
vars:
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: myconfigmap
|
||||
namespace: mynamespace
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Generated resource name.
|
||||
type: str
|
||||
@@ -19,13 +19,6 @@ DOCUMENTATION = """
|
||||
- Uses the kubectl connection plugin to access the Kubernetes cluster.
|
||||
- Uses k8s.(yml|yaml) YAML configuration file to set parameter values.
|
||||
|
||||
deprecated:
|
||||
removed_in: 4.0.0
|
||||
why: |
|
||||
As discussed in U(https://github.com/ansible-collections/kubernetes.core/issues/31), we decided to
|
||||
remove the k8s inventory plugin in release 4.0.0.
|
||||
alternative: "Use M(kubernetes.core.k8s_info) and M(ansible.builtin.add_host) instead."
|
||||
|
||||
options:
|
||||
plugin:
|
||||
description: token that ensures this is a source file for the 'k8s' plugin.
|
||||
@@ -93,45 +86,44 @@ DOCUMENTATION = """
|
||||
to access.
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
EXAMPLES = """
|
||||
# File must be named k8s.yaml or k8s.yml
|
||||
|
||||
- name: Authenticate with token, and return all pods and services for all namespaces
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- host: https://192.168.64.4:8443
|
||||
api_key: xxxxxxxxxxxxxxxx
|
||||
validate_certs: false
|
||||
# Authenticate with token, and return all pods and services for all namespaces
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- host: https://192.168.64.4:8443
|
||||
api_key: xxxxxxxxxxxxxxxx
|
||||
validate_certs: false
|
||||
|
||||
- name: Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- namespaces:
|
||||
- testing
|
||||
# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- namespaces:
|
||||
- testing
|
||||
|
||||
- name: Use a custom config file, and a specific context.
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- kubeconfig: /path/to/config
|
||||
context: 'awx/192-168-64-4:8443/developer'
|
||||
# Use a custom config file, and a specific context.
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- kubeconfig: /path/to/config
|
||||
context: 'awx/192-168-64-4:8443/developer'
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
HAS_K8S_MODULE_HELPER,
|
||||
k8s_import_exception,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.exceptions import DynamicApiError
|
||||
@@ -154,7 +146,7 @@ class K8sInventoryException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleMixin):
|
||||
NAME = "kubernetes.core.k8s"
|
||||
|
||||
connection_plugin = "kubernetes.core.kubectl"
|
||||
@@ -162,12 +154,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
def parse(self, inventory, loader, path, cache=True):
|
||||
super(InventoryModule, self).parse(inventory, loader, path)
|
||||
|
||||
self.display.deprecated(
|
||||
"The 'k8s' inventory plugin has been deprecated and will be removed in release 4.0.0",
|
||||
version="4.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
cache_key = self._get_cache_prefix(path)
|
||||
config_data = self._read_config_data(path)
|
||||
self.setup(config_data, cache, cache_key)
|
||||
@@ -193,6 +179,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
self.fetch_objects(connections)
|
||||
|
||||
def fetch_objects(self, connections):
|
||||
|
||||
if connections:
|
||||
if not isinstance(connections, list):
|
||||
raise K8sInventoryException("Expecting connections to be a list.")
|
||||
|
||||
@@ -114,8 +114,8 @@ DOCUMENTATION = """
|
||||
aliases: [ verify_ssl ]
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -179,12 +179,11 @@ import os
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common._collections_compat import KeysView
|
||||
from ansible.module_utils.common.validation import check_type_bool
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
|
||||
create_definitions,
|
||||
)
|
||||
|
||||
try:
|
||||
enable_turbo_mode = check_type_bool(os.environ.get("ENABLE_TURBO_MODE"))
|
||||
@@ -211,8 +210,9 @@ except ImportError as e:
|
||||
k8s_import_exception = e
|
||||
|
||||
|
||||
class KubernetesLookup(object):
|
||||
class KubernetesLookup(K8sAnsibleMixin):
|
||||
def __init__(self):
|
||||
|
||||
if not HAS_K8S_MODULE_HELPER:
|
||||
raise Exception(
|
||||
"Requires the Kubernetes Python client. Try `pip install kubernetes`. Detail: {0}".format(
|
||||
@@ -240,7 +240,7 @@ class KubernetesLookup(object):
|
||||
|
||||
cluster_info = kwargs.get("cluster_info")
|
||||
if cluster_info == "version":
|
||||
return [self.client.client.version]
|
||||
return [self.client.version]
|
||||
if cluster_info == "api_groups":
|
||||
if isinstance(self.client.resources.api_groups, KeysView):
|
||||
return [list(self.client.resources.api_groups)]
|
||||
@@ -257,12 +257,7 @@ class KubernetesLookup(object):
|
||||
resource_definition = kwargs.get("resource_definition")
|
||||
src = kwargs.get("src")
|
||||
if src:
|
||||
definitions = create_definitions(params=dict(src=src))
|
||||
if definitions:
|
||||
self.kind = definitions[0].kind
|
||||
self.name = definitions[0].name
|
||||
self.namespace = definitions[0].namespace
|
||||
self.api_version = definitions[0].api_version or "v1"
|
||||
resource_definition = self.load_resource_definitions(src)[0]
|
||||
if resource_definition:
|
||||
self.kind = resource_definition.get("kind", self.kind)
|
||||
self.api_version = resource_definition.get("apiVersion", self.api_version)
|
||||
@@ -277,15 +272,14 @@ class KubernetesLookup(object):
|
||||
"using the 'resource_definition' parameter."
|
||||
)
|
||||
|
||||
resource = self.client.resource(self.kind, self.api_version)
|
||||
resource = self.find_resource(self.kind, self.api_version, fail=True)
|
||||
try:
|
||||
params = dict(
|
||||
k8s_obj = resource.get(
|
||||
name=self.name,
|
||||
namespace=self.namespace,
|
||||
label_selector=self.label_selector,
|
||||
field_selector=self.field_selector,
|
||||
)
|
||||
k8s_obj = self.client.get(resource, **params)
|
||||
except NotFoundError:
|
||||
return []
|
||||
|
||||
|
||||
@@ -64,11 +64,12 @@ RETURN = """
|
||||
key1: val1
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_binary_from_path(name, opt_dirs=None):
|
||||
|
||||
@@ -14,9 +14,9 @@ except TypeError:
|
||||
|
||||
if enable_turbo_mode:
|
||||
try:
|
||||
from ansible_collections.cloud.common.plugins.module_utils.turbo.module import ( # noqa: F401
|
||||
from ansible_collections.cloud.common.plugins.module_utils.turbo.module import (
|
||||
AnsibleTurboModule as AnsibleModule,
|
||||
)
|
||||
) # noqa: F401
|
||||
|
||||
AnsibleModule.collection_name = "kubernetes.core"
|
||||
except ImportError:
|
||||
|
||||
@@ -17,19 +17,13 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.exceptions import (
|
||||
ApplyException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
gather_versions,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.exceptions import NotFoundError
|
||||
@@ -137,10 +131,7 @@ def k8s_apply(resource, definition, **kwargs):
|
||||
existing, desired = apply_object(resource, definition)
|
||||
server_side = kwargs.get("server_side", False)
|
||||
if server_side:
|
||||
versions = gather_versions()
|
||||
body = definition
|
||||
if LooseVersion(versions["kubernetes"]) < LooseVersion("25.0.0"):
|
||||
body = json.dumps(definition).encode()
|
||||
body = json.dumps(definition).encode()
|
||||
# server_side_apply is forces content_type to 'application/apply-patch+yaml'
|
||||
return resource.server_side_apply(
|
||||
body=body,
|
||||
@@ -148,7 +139,6 @@ def k8s_apply(resource, definition, **kwargs):
|
||||
namespace=definition["metadata"].get("namespace"),
|
||||
force_conflicts=kwargs.get("force_conflicts"),
|
||||
field_manager=kwargs.get("field_manager"),
|
||||
dry_run=kwargs.get("dry_run"),
|
||||
)
|
||||
if not existing:
|
||||
return resource.create(
|
||||
|
||||
@@ -13,18 +13,15 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
from collections import defaultdict
|
||||
import hashlib
|
||||
import tempfile
|
||||
from functools import partial
|
||||
|
||||
import kubernetes.dynamic
|
||||
import kubernetes.dynamic.discovery
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.client.resource import (
|
||||
ResourceList,
|
||||
)
|
||||
from kubernetes import __version__
|
||||
from kubernetes.dynamic.exceptions import (
|
||||
ResourceNotFoundError,
|
||||
@@ -32,6 +29,10 @@ from kubernetes.dynamic.exceptions import (
|
||||
ServiceUnavailableError,
|
||||
)
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.client.resource import (
|
||||
ResourceList,
|
||||
)
|
||||
|
||||
|
||||
class Discoverer(kubernetes.dynamic.discovery.Discoverer):
|
||||
def __init__(self, client, cache_file):
|
||||
|
||||
@@ -20,20 +20,16 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import traceback
|
||||
import sys
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.module_utils.six import iteritems, string_types
|
||||
from ansible.module_utils.urls import Request
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_MAP,
|
||||
AUTH_ARG_SPEC,
|
||||
@@ -45,23 +41,26 @@ from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.selector import (
|
||||
LabelSelectorFilter,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.six import iteritems, string_types
|
||||
from ansible.module_utils._text import to_native, to_bytes, to_text
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
|
||||
K8S_IMP_ERR = None
|
||||
try:
|
||||
import kubernetes
|
||||
from kubernetes.dynamic.exceptions import (
|
||||
BadRequestError,
|
||||
ConflictError,
|
||||
DynamicApiError,
|
||||
ForbiddenError,
|
||||
KubernetesValidateMissing,
|
||||
MethodNotAllowedError,
|
||||
NotFoundError,
|
||||
ResourceNotFoundError,
|
||||
ResourceNotUniqueError,
|
||||
DynamicApiError,
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
MethodNotAllowedError,
|
||||
BadRequestError,
|
||||
KubernetesValidateMissing,
|
||||
)
|
||||
|
||||
HAS_K8S_MODULE_HELPER = True
|
||||
@@ -131,7 +130,6 @@ except ImportError as e:
|
||||
|
||||
|
||||
def configuration_digest(configuration, **kwargs):
|
||||
"""This function has been deprecated and will be removed in version 4.0.0."""
|
||||
m = hashlib.sha256()
|
||||
for k in AUTH_ARG_MAP:
|
||||
if not hasattr(configuration, k):
|
||||
@@ -152,8 +150,6 @@ def configuration_digest(configuration, **kwargs):
|
||||
|
||||
|
||||
class unique_string(str):
|
||||
"""This function has been deprecated and will be removed in version 4.0.0."""
|
||||
|
||||
_low = None
|
||||
|
||||
def __hash__(self):
|
||||
@@ -173,7 +169,6 @@ class unique_string(str):
|
||||
|
||||
|
||||
def get_api_client(module=None, **kwargs):
|
||||
"""This function has been deprecated and will be removed in version 4.0.0. Please use module_utils.k8s.client.get_api_client() instead."""
|
||||
auth = {}
|
||||
|
||||
def _raise_or_fail(exc, msg):
|
||||
@@ -343,35 +338,8 @@ def get_api_client(module=None, **kwargs):
|
||||
get_api_client._pool = {}
|
||||
|
||||
|
||||
def fetch_file_from_url(module, url):
|
||||
# Download file
|
||||
bufsize = 65536
|
||||
file_name, file_ext = os.path.splitext(str(url.rsplit("/", 1)[1]))
|
||||
temp_file = NamedTemporaryFile(
|
||||
dir=module.tmpdir, prefix=file_name, suffix=file_ext, delete=False
|
||||
)
|
||||
module.add_cleanup_file(temp_file.name)
|
||||
try:
|
||||
rsp = Request().open("GET", url)
|
||||
if not rsp:
|
||||
module.fail_json(msg="Failure downloading %s" % url)
|
||||
data = rsp.read(bufsize)
|
||||
while data:
|
||||
temp_file.write(data)
|
||||
data = rsp.read(bufsize)
|
||||
temp_file.close()
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Failure downloading %s, %s" % (url, to_native(e)))
|
||||
return temp_file.name
|
||||
|
||||
|
||||
class K8sAnsibleMixin(object):
|
||||
def __init__(self, module, pyyaml_required=True, *args, **kwargs):
|
||||
module.deprecate(
|
||||
msg="The K8sAnsibleMixin class has been deprecated and refactored into the module_utils/k8s/ directory.",
|
||||
version="4.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
if not HAS_K8S_MODULE_HELPER:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib("kubernetes"),
|
||||
@@ -552,15 +520,8 @@ class K8sAnsibleMixin(object):
|
||||
if alias in self.params:
|
||||
self.params.pop(alias)
|
||||
|
||||
def load_resource_definitions(self, src, module=None):
|
||||
def load_resource_definitions(self, src):
|
||||
"""Load the requested src path"""
|
||||
if module and (
|
||||
src.startswith("https://")
|
||||
or src.startswith("http://")
|
||||
or src.startswith("ftp://")
|
||||
):
|
||||
src = fetch_file_from_url(module, src)
|
||||
|
||||
result = None
|
||||
path = os.path.normpath(src)
|
||||
if not os.path.exists(path):
|
||||
@@ -775,7 +736,7 @@ class K8sAnsibleMixin(object):
|
||||
|
||||
src = module.params.get("src")
|
||||
if src:
|
||||
self.resource_definitions = self.load_resource_definitions(src, module)
|
||||
self.resource_definitions = self.load_resource_definitions(src)
|
||||
try:
|
||||
self.resource_definitions = [
|
||||
item for item in self.resource_definitions if item
|
||||
@@ -796,8 +757,8 @@ class K8sAnsibleMixin(object):
|
||||
self.resource_definitions = [implicit_definition]
|
||||
|
||||
def check_library_version(self):
|
||||
if LooseVersion(self.kubernetes_version) < LooseVersion("24.2.0"):
|
||||
self.fail_json(msg="kubernetes >= 24.2.0 is required")
|
||||
if LooseVersion(self.kubernetes_version) < LooseVersion("12.0.0"):
|
||||
self.fail_json(msg="kubernetes >= 12.0.0 is required")
|
||||
|
||||
def flatten_list_kind(self, list_resource, definitions):
|
||||
flattened = []
|
||||
@@ -818,7 +779,7 @@ class K8sAnsibleMixin(object):
|
||||
try:
|
||||
self.client = get_api_client(self.module)
|
||||
# Hopefully the kubernetes client will provide its own exception class one day
|
||||
except urllib3.exceptions.RequestError as e:
|
||||
except (urllib3.exceptions.RequestError) as e:
|
||||
self.fail_json(msg="Couldn't connect to Kubernetes: %s" % str(e))
|
||||
|
||||
flattened_definitions = []
|
||||
@@ -836,7 +797,7 @@ class K8sAnsibleMixin(object):
|
||||
resource = self.find_resource(kind, api_version, fail=True)
|
||||
flattened_definitions.append((resource, definition))
|
||||
|
||||
for resource, definition in flattened_definitions:
|
||||
for (resource, definition) in flattened_definitions:
|
||||
kind = definition.get("kind", self.kind)
|
||||
api_version = definition.get("apiVersion", self.api_version)
|
||||
definition = self.set_defaults(resource, definition)
|
||||
@@ -883,9 +844,9 @@ class K8sAnsibleMixin(object):
|
||||
definition["apiVersion"] = resource.group_version
|
||||
metadata = definition.get("metadata", {})
|
||||
if not metadata.get("name") and not metadata.get("generateName"):
|
||||
if hasattr(self, "name") and self.name:
|
||||
if self.name:
|
||||
metadata["name"] = self.name
|
||||
elif hasattr(self, "generate_name") and self.generate_name:
|
||||
elif self.generate_name:
|
||||
metadata["generateName"] = self.generate_name
|
||||
if resource.namespaced and self.namespace and not metadata.get("namespace"):
|
||||
metadata["namespace"] = self.namespace
|
||||
@@ -1435,7 +1396,7 @@ class K8sAnsibleMixin(object):
|
||||
if merge_type == "json":
|
||||
self.module.deprecate(
|
||||
msg="json as a merge_type value is deprecated. Please use the k8s_json_patch module instead.",
|
||||
version="4.0.0",
|
||||
version="3.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
try:
|
||||
|
||||
@@ -18,26 +18,22 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import tarfile
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from tempfile import TemporaryFile, NamedTemporaryFile
|
||||
from select import select
|
||||
from tempfile import NamedTemporaryFile, TemporaryFile
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import tarfile
|
||||
|
||||
# from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import AnsibleModule
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
from kubernetes.client.api import core_v1_api
|
||||
from kubernetes.stream import stream
|
||||
from kubernetes.stream.ws_client import (
|
||||
ABNF,
|
||||
ERROR_CHANNEL,
|
||||
STDERR_CHANNEL,
|
||||
STDOUT_CHANNEL,
|
||||
STDERR_CHANNEL,
|
||||
ERROR_CHANNEL,
|
||||
ABNF,
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -65,51 +61,6 @@ class K8SCopy(metaclass=ABCMeta):
|
||||
self.container_arg = {}
|
||||
if module.params.get("container"):
|
||||
self.container_arg["container"] = module.params.get("container")
|
||||
self.check_mode = self.module.check_mode
|
||||
|
||||
def _run_from_pod(self, cmd):
|
||||
try:
|
||||
resp = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=cmd,
|
||||
async_req=False,
|
||||
stderr=True,
|
||||
stdin=False,
|
||||
stdout=True,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg,
|
||||
)
|
||||
|
||||
stderr, stdout = [], []
|
||||
while resp.is_open():
|
||||
resp.update(timeout=1)
|
||||
if resp.peek_stdout():
|
||||
stdout.extend(resp.read_stdout().rstrip("\n").split("\n"))
|
||||
if resp.peek_stderr():
|
||||
stderr.extend(resp.read_stderr().rstrip("\n").split("\n"))
|
||||
error = resp.read_channel(ERROR_CHANNEL)
|
||||
resp.close()
|
||||
error = yaml.safe_load(error)
|
||||
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(
|
||||
self.namespace, self.name, cmd, to_native(e)
|
||||
)
|
||||
)
|
||||
|
||||
def is_directory_path_from_pod(self, file_path, failed_if_not_exists=True):
|
||||
# check if file exists
|
||||
error, out, err = self._run_from_pod(cmd=["test", "-e", file_path])
|
||||
if error.get("status") != "Success":
|
||||
if failed_if_not_exists:
|
||||
return None, "%s does not exist in remote pod filesystem" % file_path
|
||||
return False, None
|
||||
error, out, err = self._run_from_pod(cmd=["test", "-d", file_path])
|
||||
return error.get("status") == "Success", None
|
||||
|
||||
@abstractmethod
|
||||
def run(self):
|
||||
@@ -124,80 +75,56 @@ class K8SCopyFromPod(K8SCopy):
|
||||
def __init__(self, module, client):
|
||||
super(K8SCopyFromPod, self).__init__(module, client)
|
||||
self.is_remote_path_dir = None
|
||||
self.files_to_copy = []
|
||||
self._shellname = None
|
||||
|
||||
@property
|
||||
def pod_shell(self):
|
||||
if self._shellname is None:
|
||||
for s in ("/bin/sh", "/bin/bash"):
|
||||
error, out, err = self._run_from_pod(s)
|
||||
if error.get("status") == "Success":
|
||||
self._shellname = s
|
||||
break
|
||||
return self._shellname
|
||||
|
||||
def listfiles_with_find(self, path):
|
||||
find_cmd = ["find", path, "-type", "f"]
|
||||
error, files, err = self._run_from_pod(cmd=find_cmd)
|
||||
if error.get("status") != "Success":
|
||||
self.module.fail_json(msg=error.get("message"))
|
||||
return files
|
||||
|
||||
def listfile_with_echo(self, path):
|
||||
echo_cmd = [
|
||||
self.pod_shell,
|
||||
"-c",
|
||||
"echo {path}/* {path}/.*".format(
|
||||
path=path.translate(str.maketrans({" ": r"\ "}))
|
||||
),
|
||||
]
|
||||
error, out, err = self._run_from_pod(cmd=echo_cmd)
|
||||
if error.get("status") != "Success":
|
||||
self.module.fail_json(msg=error.get("message"))
|
||||
|
||||
files = []
|
||||
if out:
|
||||
output = out[0] + " "
|
||||
files = [
|
||||
os.path.join(path, p[:-1])
|
||||
for p in output.split(f"{path}/")
|
||||
if p and p[:-1] not in (".", "..")
|
||||
]
|
||||
|
||||
result = []
|
||||
for f in files:
|
||||
is_dir, err = self.is_directory_path_from_pod(f)
|
||||
if err:
|
||||
continue
|
||||
if not is_dir:
|
||||
result.append(f)
|
||||
continue
|
||||
result += self.listfile_with_echo(f)
|
||||
return result
|
||||
self.files_to_copy = list()
|
||||
|
||||
def list_remote_files(self):
|
||||
"""
|
||||
This method will check if the remote path is a dir or file
|
||||
if it is a directory the file list will be updated accordingly
|
||||
"""
|
||||
# check is remote path exists and is a file or directory
|
||||
is_dir, error = self.is_directory_path_from_pod(self.remote_path)
|
||||
if error:
|
||||
self.module.fail_json(msg=error)
|
||||
|
||||
if not is_dir:
|
||||
return [self.remote_path]
|
||||
else:
|
||||
# find executable to list dir with
|
||||
executables = dict(
|
||||
find=self.listfiles_with_find,
|
||||
echo=self.listfile_with_echo,
|
||||
try:
|
||||
find_cmd = ["find", self.remote_path, "-type", "f", "-name", "*"]
|
||||
response = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=find_cmd,
|
||||
stdout=True,
|
||||
stderr=True,
|
||||
stdin=False,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg
|
||||
)
|
||||
except Exception as e:
|
||||
self.module.fail_json(
|
||||
msg="Failed to execute on pod {0}/{1} due to : {2}".format(
|
||||
self.namespace, self.name, to_native(e)
|
||||
)
|
||||
)
|
||||
stderr = []
|
||||
while response.is_open():
|
||||
response.update(timeout=1)
|
||||
if response.peek_stdout():
|
||||
self.files_to_copy.extend(
|
||||
response.read_stdout().rstrip("\n").split("\n")
|
||||
)
|
||||
if response.peek_stderr():
|
||||
err = response.read_stderr()
|
||||
if "No such file or directory" in err:
|
||||
self.module.fail_json(
|
||||
msg="{0} does not exist in remote pod filesystem".format(
|
||||
self.remote_path
|
||||
)
|
||||
)
|
||||
stderr.append(err)
|
||||
error = response.read_channel(ERROR_CHANNEL)
|
||||
response.close()
|
||||
error = yaml.safe_load(error)
|
||||
if error["status"] != "Success":
|
||||
self.module.fail_json(
|
||||
msg="Failed to execute on Pod due to: {0}".format(error)
|
||||
)
|
||||
for item in executables:
|
||||
error, out, err = self._run_from_pod(item)
|
||||
if error.get("status") == "Success":
|
||||
return executables.get(item)(self.remote_path)
|
||||
|
||||
def read(self):
|
||||
self.stdout = None
|
||||
@@ -232,42 +159,40 @@ class K8SCopyFromPod(K8SCopy):
|
||||
if is_remote_path_dir and os.path.isdir(self.local_path):
|
||||
relpath_start = os.path.dirname(self.remote_path)
|
||||
|
||||
if not self.check_mode:
|
||||
for remote_file in self.files_to_copy:
|
||||
dest_file = self.local_path
|
||||
if is_remote_path_dir:
|
||||
dest_file = os.path.join(
|
||||
self.local_path,
|
||||
os.path.relpath(remote_file, start=relpath_start),
|
||||
)
|
||||
# create directory to copy file in
|
||||
os.makedirs(os.path.dirname(dest_file), exist_ok=True)
|
||||
|
||||
pod_command = ["cat", remote_file]
|
||||
self.response = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=pod_command,
|
||||
stderr=True,
|
||||
stdin=True,
|
||||
stdout=True,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg,
|
||||
for remote_file in self.files_to_copy:
|
||||
dest_file = self.local_path
|
||||
if is_remote_path_dir:
|
||||
dest_file = os.path.join(
|
||||
self.local_path, os.path.relpath(remote_file, start=relpath_start)
|
||||
)
|
||||
# create directory to copy file in
|
||||
os.makedirs(os.path.dirname(dest_file), exist_ok=True)
|
||||
|
||||
pod_command = ["cat", remote_file]
|
||||
self.response = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=pod_command,
|
||||
stderr=True,
|
||||
stdin=True,
|
||||
stdout=True,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg
|
||||
)
|
||||
errors = []
|
||||
with open(dest_file, "wb") as fh:
|
||||
while self.response._connected:
|
||||
self.read()
|
||||
if self.stdout:
|
||||
fh.write(self.stdout)
|
||||
if self.stderr:
|
||||
errors.append(self.stderr)
|
||||
if errors:
|
||||
self.module.fail_json(
|
||||
msg="Failed to copy file from Pod: {0}".format("".join(errors))
|
||||
)
|
||||
errors = []
|
||||
with open(dest_file, "wb") as fh:
|
||||
while self.response._connected:
|
||||
self.read()
|
||||
if self.stdout:
|
||||
fh.write(self.stdout)
|
||||
if self.stderr:
|
||||
errors.append(self.stderr)
|
||||
if errors:
|
||||
self.module.fail_json(
|
||||
msg="Failed to copy file from Pod: {0}".format("".join(errors))
|
||||
)
|
||||
self.module.exit_json(
|
||||
changed=True,
|
||||
result="{0} successfully copied locally into {1}".format(
|
||||
@@ -276,7 +201,7 @@ class K8SCopyFromPod(K8SCopy):
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.files_to_copy = self.list_remote_files()
|
||||
self.list_remote_files()
|
||||
if self.files_to_copy == []:
|
||||
self.module.exit_json(
|
||||
changed=False,
|
||||
@@ -296,6 +221,56 @@ class K8SCopyToPod(K8SCopy):
|
||||
super(K8SCopyToPod, self).__init__(module, client)
|
||||
self.files_to_copy = list()
|
||||
|
||||
def run_from_pod(self, command):
|
||||
response = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=command,
|
||||
stderr=True,
|
||||
stdin=False,
|
||||
stdout=True,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg
|
||||
)
|
||||
errors = []
|
||||
while response.is_open():
|
||||
response.update(timeout=1)
|
||||
if response.peek_stderr():
|
||||
errors.append(response.read_stderr())
|
||||
response.close()
|
||||
err = response.read_channel(ERROR_CHANNEL)
|
||||
err = yaml.safe_load(err)
|
||||
response.close()
|
||||
if err["status"] != "Success":
|
||||
self.module.fail_json(
|
||||
msg="Failed to run {0} on Pod.".format(command), errors=errors
|
||||
)
|
||||
|
||||
def is_remote_path_dir(self):
|
||||
pod_command = ["test", "-d", self.remote_path]
|
||||
response = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=pod_command,
|
||||
stdout=True,
|
||||
stderr=True,
|
||||
stdin=False,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg
|
||||
)
|
||||
while response.is_open():
|
||||
response.update(timeout=1)
|
||||
err = response.read_channel(ERROR_CHANNEL)
|
||||
err = yaml.safe_load(err)
|
||||
response.close()
|
||||
if err["status"] == "Success":
|
||||
return True
|
||||
return False
|
||||
|
||||
def close_temp_file(self):
|
||||
if self.named_temp_file:
|
||||
self.named_temp_file.close()
|
||||
@@ -318,12 +293,7 @@ class K8SCopyToPod(K8SCopy):
|
||||
if not os.access(self.local_path, os.R_OK):
|
||||
self.module.fail_json(msg="{0} not readable".format(self.local_path))
|
||||
|
||||
is_dir, err = self.is_directory_path_from_pod(
|
||||
self.remote_path, failed_if_not_exists=False
|
||||
)
|
||||
if err:
|
||||
self.module.fail_json(msg=err)
|
||||
if is_dir:
|
||||
if self.is_remote_path_dir():
|
||||
if self.content:
|
||||
self.module.fail_json(
|
||||
msg="When content is specified, remote path should not be an existing directory"
|
||||
@@ -331,67 +301,66 @@ class K8SCopyToPod(K8SCopy):
|
||||
else:
|
||||
dest_file = os.path.join(dest_file, os.path.basename(src_file))
|
||||
|
||||
if not self.check_mode:
|
||||
if self.no_preserve:
|
||||
tar_command = [
|
||||
"tar",
|
||||
"--no-same-permissions",
|
||||
"--no-same-owner",
|
||||
"-xmf",
|
||||
"-",
|
||||
]
|
||||
else:
|
||||
tar_command = ["tar", "-xmf", "-"]
|
||||
if self.no_preserve:
|
||||
tar_command = [
|
||||
"tar",
|
||||
"--no-same-permissions",
|
||||
"--no-same-owner",
|
||||
"-xmf",
|
||||
"-",
|
||||
]
|
||||
else:
|
||||
tar_command = ["tar", "-xmf", "-"]
|
||||
|
||||
if dest_file.startswith("/"):
|
||||
tar_command.extend(["-C", "/"])
|
||||
if dest_file.startswith("/"):
|
||||
tar_command.extend(["-C", "/"])
|
||||
|
||||
response = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=tar_command,
|
||||
stderr=True,
|
||||
stdin=True,
|
||||
stdout=True,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg,
|
||||
)
|
||||
with TemporaryFile() as tar_buffer:
|
||||
with tarfile.open(fileobj=tar_buffer, mode="w") as tar:
|
||||
tar.add(src_file, dest_file)
|
||||
tar_buffer.seek(0)
|
||||
commands = []
|
||||
# push command in chunk mode
|
||||
size = 1024 * 1024
|
||||
while True:
|
||||
data = tar_buffer.read(size)
|
||||
if not data:
|
||||
break
|
||||
commands.append(data)
|
||||
response = stream(
|
||||
self.api_instance.connect_get_namespaced_pod_exec,
|
||||
self.name,
|
||||
self.namespace,
|
||||
command=tar_command,
|
||||
stderr=True,
|
||||
stdin=True,
|
||||
stdout=True,
|
||||
tty=False,
|
||||
_preload_content=False,
|
||||
**self.container_arg
|
||||
)
|
||||
with TemporaryFile() as tar_buffer:
|
||||
with tarfile.open(fileobj=tar_buffer, mode="w") as tar:
|
||||
tar.add(src_file, dest_file)
|
||||
tar_buffer.seek(0)
|
||||
commands = []
|
||||
# push command in chunk mode
|
||||
size = 1024 * 1024
|
||||
while True:
|
||||
data = tar_buffer.read(size)
|
||||
if not data:
|
||||
break
|
||||
commands.append(data)
|
||||
|
||||
stderr, stdout = [], []
|
||||
while response.is_open():
|
||||
if response.peek_stdout():
|
||||
stdout.append(response.read_stdout().rstrip("\n"))
|
||||
if response.peek_stderr():
|
||||
stderr.append(response.read_stderr().rstrip("\n"))
|
||||
if commands:
|
||||
cmd = commands.pop(0)
|
||||
response.write_stdin(cmd)
|
||||
else:
|
||||
break
|
||||
response.close()
|
||||
if stderr:
|
||||
self.close_temp_file()
|
||||
self.module.fail_json(
|
||||
command=tar_command,
|
||||
msg="Failed to copy local file/directory into Pod due to: {0}".format(
|
||||
"".join(stderr)
|
||||
),
|
||||
)
|
||||
self.close_temp_file()
|
||||
stderr, stdout = [], []
|
||||
while response.is_open():
|
||||
if response.peek_stdout():
|
||||
stdout.append(response.read_stdout().rstrip("\n"))
|
||||
if response.peek_stderr():
|
||||
stderr.append(response.read_stderr().rstrip("\n"))
|
||||
if commands:
|
||||
cmd = commands.pop(0)
|
||||
response.write_stdin(cmd)
|
||||
else:
|
||||
break
|
||||
response.close()
|
||||
if stderr:
|
||||
self.close_temp_file()
|
||||
self.module.fail_json(
|
||||
command=tar_command,
|
||||
msg="Failed to copy local file/directory into Pod due to: {0}".format(
|
||||
"".join(stderr)
|
||||
),
|
||||
)
|
||||
self.close_temp_file()
|
||||
if self.content:
|
||||
self.module.exit_json(
|
||||
changed=True,
|
||||
@@ -407,17 +376,12 @@ class K8SCopyToPod(K8SCopy):
|
||||
)
|
||||
|
||||
|
||||
def check_pod(svc):
|
||||
module = svc.module
|
||||
def check_pod(k8s_ansible_mixin, module):
|
||||
resource = k8s_ansible_mixin.find_resource("Pod", None, True)
|
||||
namespace = module.params.get("namespace")
|
||||
name = module.params.get("pod")
|
||||
container = module.params.get("container")
|
||||
|
||||
try:
|
||||
resource = svc.find_resource("Pod", None, True)
|
||||
except CoreException as e:
|
||||
module.fail_json(msg=to_native(e))
|
||||
|
||||
def _fail(exc):
|
||||
arg = {}
|
||||
if hasattr(exc, "body"):
|
||||
@@ -434,7 +398,7 @@ def check_pod(svc):
|
||||
module.fail_json(msg=msg, **arg)
|
||||
|
||||
try:
|
||||
result = svc.client.get(resource, name=name, namespace=namespace)
|
||||
result = resource.get(name=name, namespace=namespace)
|
||||
containers = [
|
||||
c["name"] for c in result.to_dict()["status"]["containerStatuses"]
|
||||
]
|
||||
|
||||
@@ -19,8 +19,8 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import hashlib
|
||||
|
||||
try:
|
||||
import string
|
||||
@@ -37,7 +37,7 @@ except ImportError:
|
||||
|
||||
def sorted_dict(unsorted_dict):
|
||||
result = OrderedDict()
|
||||
for k, v in sorted(unsorted_dict.items()):
|
||||
for (k, v) in sorted(unsorted_dict.items()):
|
||||
if isinstance(v, dict):
|
||||
v = sorted_dict(v)
|
||||
result[k] = v
|
||||
|
||||
@@ -7,30 +7,131 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
import copy
|
||||
import json
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import traceback
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
HAS_YAML = True
|
||||
YAML_IMP_ERR = None
|
||||
except ImportError:
|
||||
YAML_IMP_ERR = traceback.format_exc()
|
||||
HAS_YAML = False
|
||||
|
||||
|
||||
def parse_helm_plugin_list(output=None):
|
||||
@contextmanager
|
||||
def prepare_helm_environ_update(module):
|
||||
environ_update = {}
|
||||
file_to_cleam_up = None
|
||||
kubeconfig_path = module.params.get("kubeconfig")
|
||||
if module.params.get("context") is not None:
|
||||
environ_update["HELM_KUBECONTEXT"] = module.params.get("context")
|
||||
if module.params.get("release_namespace"):
|
||||
environ_update["HELM_NAMESPACE"] = module.params.get("release_namespace")
|
||||
if module.params.get("api_key"):
|
||||
environ_update["HELM_KUBETOKEN"] = module.params["api_key"]
|
||||
if module.params.get("host"):
|
||||
environ_update["HELM_KUBEAPISERVER"] = module.params["host"]
|
||||
if module.params.get("validate_certs") is False or module.params.get("ca_cert"):
|
||||
kubeconfig_path = write_temp_kubeconfig(
|
||||
module.params["host"],
|
||||
validate_certs=module.params["validate_certs"],
|
||||
ca_cert=module.params["ca_cert"],
|
||||
)
|
||||
file_to_cleam_up = kubeconfig_path
|
||||
if kubeconfig_path is not None:
|
||||
environ_update["KUBECONFIG"] = kubeconfig_path
|
||||
|
||||
try:
|
||||
yield environ_update
|
||||
finally:
|
||||
if file_to_cleam_up:
|
||||
os.remove(file_to_cleam_up)
|
||||
|
||||
|
||||
def run_helm(module, command, fails_on_error=True):
|
||||
if not HAS_YAML:
|
||||
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
||||
|
||||
with prepare_helm_environ_update(module) as environ_update:
|
||||
rc, out, err = module.run_command(command, environ_update=environ_update)
|
||||
if fails_on_error and rc != 0:
|
||||
module.fail_json(
|
||||
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(
|
||||
rc, out, err
|
||||
),
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
command=command,
|
||||
)
|
||||
return rc, out, err
|
||||
|
||||
|
||||
def get_values(module, command, release_name):
|
||||
"""
|
||||
Get Values from deployed release
|
||||
"""
|
||||
if not HAS_YAML:
|
||||
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
||||
|
||||
get_command = command + " get values --output=yaml " + release_name
|
||||
|
||||
rc, out, err = run_helm(module, get_command)
|
||||
# Helm 3 return "null" string when no values are set
|
||||
if out.rstrip("\n") == "null":
|
||||
return {}
|
||||
return yaml.safe_load(out)
|
||||
|
||||
|
||||
def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None):
|
||||
# Workaround until https://github.com/helm/helm/pull/8622 is merged
|
||||
content = {
|
||||
"apiVersion": "v1",
|
||||
"kind": "Config",
|
||||
"clusters": [{"cluster": {"server": server}, "name": "generated-cluster"}],
|
||||
"contexts": [
|
||||
{"context": {"cluster": "generated-cluster"}, "name": "generated-context"}
|
||||
],
|
||||
"current-context": "generated-context",
|
||||
}
|
||||
|
||||
if not validate_certs:
|
||||
content["clusters"][0]["cluster"]["insecure-skip-tls-verify"] = True
|
||||
if ca_cert:
|
||||
content["clusters"][0]["cluster"]["certificate-authority"] = ca_cert
|
||||
|
||||
_fd, file_name = tempfile.mkstemp()
|
||||
with os.fdopen(_fd, "w") as fp:
|
||||
yaml.dump(content, fp)
|
||||
return file_name
|
||||
|
||||
|
||||
def get_helm_plugin_list(module, helm_bin=None):
|
||||
"""
|
||||
Return `helm plugin list`
|
||||
"""
|
||||
if not helm_bin:
|
||||
return []
|
||||
helm_plugin_list = helm_bin + " list"
|
||||
rc, out, err = run_helm(module, helm_plugin_list)
|
||||
if rc != 0 or (out == "" and err == ""):
|
||||
module.fail_json(
|
||||
msg="Failed to get Helm plugin info",
|
||||
command=helm_plugin_list,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
)
|
||||
return (rc, out, err)
|
||||
|
||||
|
||||
def parse_helm_plugin_list(module, output=None):
|
||||
"""
|
||||
Parse `helm plugin list`, return list of plugins
|
||||
"""
|
||||
@@ -52,246 +153,12 @@ def parse_helm_plugin_list(output=None):
|
||||
return ret
|
||||
|
||||
|
||||
def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None, kubeconfig=None):
|
||||
# Workaround until https://github.com/helm/helm/pull/8622 is merged
|
||||
content = {
|
||||
"apiVersion": "v1",
|
||||
"kind": "Config",
|
||||
"clusters": [{"cluster": {"server": server}, "name": "generated-cluster"}],
|
||||
"contexts": [
|
||||
{"context": {"cluster": "generated-cluster"}, "name": "generated-context"}
|
||||
],
|
||||
"current-context": "generated-context",
|
||||
}
|
||||
if kubeconfig:
|
||||
content = copy.deepcopy(kubeconfig)
|
||||
def get_helm_version(module, helm_bin):
|
||||
|
||||
for cluster in content["clusters"]:
|
||||
if server:
|
||||
cluster["cluster"]["server"] = server
|
||||
if not validate_certs:
|
||||
cluster["cluster"]["insecure-skip-tls-verify"] = True
|
||||
if ca_cert:
|
||||
cluster["cluster"]["certificate-authority"] = ca_cert
|
||||
return content
|
||||
|
||||
|
||||
class AnsibleHelmModule(object):
|
||||
|
||||
"""
|
||||
An Ansible module class for Kubernetes.core helm modules
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._module = None
|
||||
if "module" in kwargs:
|
||||
self._module = kwargs.get("module")
|
||||
else:
|
||||
self._module = AnsibleModule(**kwargs)
|
||||
|
||||
self.helm_env = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._module, name)
|
||||
|
||||
@property
|
||||
def params(self):
|
||||
return self._module.params
|
||||
|
||||
def _prepare_helm_environment(self):
|
||||
param_to_env_mapping = [
|
||||
("context", "HELM_KUBECONTEXT"),
|
||||
("release_namespace", "HELM_NAMESPACE"),
|
||||
("api_key", "HELM_KUBETOKEN"),
|
||||
("host", "HELM_KUBEAPISERVER"),
|
||||
]
|
||||
|
||||
env_update = {}
|
||||
for p, env in param_to_env_mapping:
|
||||
if self.params.get(p):
|
||||
env_update[env] = self.params.get(p)
|
||||
|
||||
kubeconfig_content = None
|
||||
kubeconfig = self.params.get("kubeconfig")
|
||||
if kubeconfig:
|
||||
if isinstance(kubeconfig, string_types):
|
||||
with open(kubeconfig) as fd:
|
||||
kubeconfig_content = yaml.safe_load(fd)
|
||||
elif isinstance(kubeconfig, dict):
|
||||
kubeconfig_content = kubeconfig
|
||||
|
||||
if self.params.get("ca_cert"):
|
||||
ca_cert = self.params.get("ca_cert")
|
||||
if LooseVersion(self.get_helm_version()) < LooseVersion("3.5.0"):
|
||||
# update certs from kubeconfig
|
||||
kubeconfig_content = write_temp_kubeconfig(
|
||||
server=self.params.get("host"),
|
||||
ca_cert=ca_cert,
|
||||
kubeconfig=kubeconfig_content,
|
||||
)
|
||||
else:
|
||||
env_update["HELM_KUBECAFILE"] = ca_cert
|
||||
|
||||
if self.params.get("validate_certs") is False:
|
||||
validate_certs = self.params.get("validate_certs")
|
||||
if LooseVersion(self.get_helm_version()) < LooseVersion("3.10.0"):
|
||||
# update certs from kubeconfig
|
||||
kubeconfig_content = write_temp_kubeconfig(
|
||||
server=self.params.get("host"),
|
||||
validate_certs=validate_certs,
|
||||
kubeconfig=kubeconfig_content,
|
||||
)
|
||||
else:
|
||||
env_update["HELM_KUBEINSECURE_SKIP_TLS_VERIFY"] = "true"
|
||||
|
||||
if kubeconfig_content:
|
||||
fd, kubeconfig_path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as fp:
|
||||
json.dump(kubeconfig_content, fp)
|
||||
|
||||
env_update["KUBECONFIG"] = kubeconfig_path
|
||||
self.add_cleanup_file(kubeconfig_path)
|
||||
|
||||
return env_update
|
||||
|
||||
@property
|
||||
def env_update(self):
|
||||
if self.helm_env is None:
|
||||
self.helm_env = self._prepare_helm_environment()
|
||||
return self.helm_env
|
||||
|
||||
def run_helm_command(self, command, fails_on_error=True):
|
||||
if not HAS_YAML:
|
||||
self.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
||||
|
||||
rc, out, err = self.run_command(command, environ_update=self.env_update)
|
||||
if fails_on_error and rc != 0:
|
||||
self.fail_json(
|
||||
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(
|
||||
rc, out, err
|
||||
),
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
command=command,
|
||||
)
|
||||
return rc, out, err
|
||||
|
||||
def get_helm_binary(self):
|
||||
return self.params.get("binary_path") or self.get_bin_path(
|
||||
"helm", required=True
|
||||
)
|
||||
|
||||
def get_helm_version(self):
|
||||
command = self.get_helm_binary() + " version"
|
||||
rc, out, err = self.run_command(command)
|
||||
helm_version_command = helm_bin + " version"
|
||||
rc, out, err = module.run_command(helm_version_command)
|
||||
if rc == 0:
|
||||
m = re.match(r'version.BuildInfo{Version:"v([0-9\.]*)",', out)
|
||||
if m:
|
||||
return m.group(1)
|
||||
m = re.match(r'Client: &version.Version{SemVer:"v([0-9\.]*)", ', out)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
def get_values(self, release_name, get_all=False):
|
||||
"""
|
||||
Get Values from deployed release
|
||||
"""
|
||||
if not HAS_YAML:
|
||||
self.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
||||
|
||||
get_command = (
|
||||
self.get_helm_binary() + " get values --output=yaml " + release_name
|
||||
)
|
||||
|
||||
if get_all:
|
||||
get_command += " -a"
|
||||
|
||||
rc, out, err = self.run_helm_command(get_command)
|
||||
# Helm 3 return "null" string when no values are set
|
||||
if out.rstrip("\n") == "null":
|
||||
return {}
|
||||
return yaml.safe_load(out)
|
||||
|
||||
def parse_yaml_content(self, content):
|
||||
if not HAS_YAML:
|
||||
self.fail_json(msg=missing_required_lib("yaml"), exception=HAS_YAML)
|
||||
|
||||
try:
|
||||
return list(yaml.safe_load_all(content))
|
||||
except (IOError, yaml.YAMLError) as exc:
|
||||
self.fail_json(
|
||||
msg="Error parsing YAML content: {0}".format(exc), raw_data=content
|
||||
)
|
||||
|
||||
def get_manifest(self, release_name):
|
||||
command = [
|
||||
self.get_helm_binary(),
|
||||
"get",
|
||||
"manifest",
|
||||
release_name,
|
||||
]
|
||||
rc, out, err = self.run_helm_command(" ".join(command))
|
||||
if rc != 0:
|
||||
self.fail_json(msg=err)
|
||||
return self.parse_yaml_content(out)
|
||||
|
||||
def get_notes(self, release_name):
|
||||
command = [
|
||||
self.get_helm_binary(),
|
||||
"get",
|
||||
"notes",
|
||||
release_name,
|
||||
]
|
||||
rc, out, err = self.run_helm_command(" ".join(command))
|
||||
if rc != 0:
|
||||
self.fail_json(msg=err)
|
||||
return out
|
||||
|
||||
def get_hooks(self, release_name):
|
||||
command = [
|
||||
self.get_helm_binary(),
|
||||
"get",
|
||||
"hooks",
|
||||
release_name,
|
||||
]
|
||||
rc, out, err = self.run_helm_command(" ".join(command))
|
||||
if rc != 0:
|
||||
self.fail_json(msg=err)
|
||||
return self.parse_yaml_content(out)
|
||||
|
||||
def get_helm_plugin_list(self):
|
||||
"""
|
||||
Return `helm plugin list`
|
||||
"""
|
||||
helm_plugin_list = self.get_helm_binary() + " plugin list"
|
||||
rc, out, err = self.run_helm_command(helm_plugin_list)
|
||||
if rc != 0 or (out == "" and err == ""):
|
||||
self.fail_json(
|
||||
msg="Failed to get Helm plugin info",
|
||||
command=helm_plugin_list,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
)
|
||||
return (rc, out, err, helm_plugin_list)
|
||||
|
||||
def get_helm_set_values_args(self, set_values):
|
||||
if any(v.get("value_type") == "json" for v in set_values):
|
||||
if LooseVersion(self.get_helm_version()) < LooseVersion("3.10.0"):
|
||||
self.fail_json(
|
||||
msg="This module requires helm >= 3.10.0, to use set_values parameter with value type set to 'json'. current version is {0}".format(
|
||||
self.get_helm_version()
|
||||
)
|
||||
)
|
||||
|
||||
options = []
|
||||
for opt in set_values:
|
||||
value_type = opt.get("value_type", "raw")
|
||||
value = opt.get("value")
|
||||
|
||||
if value_type == "raw":
|
||||
options.append("--set " + value)
|
||||
else:
|
||||
options.append("--set-{0} '{1}'".format(value_type, value))
|
||||
|
||||
return " ".join(options)
|
||||
return None
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ansible.module_utils.basic import env_fallback
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
HELM_AUTH_ARG_SPEC = dict(
|
||||
binary_path=dict(type="path"),
|
||||
context=dict(
|
||||
type="str",
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
),
|
||||
kubeconfig=dict(
|
||||
type="raw",
|
||||
aliases=["kubeconfig_path"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_KUBECONFIG"]),
|
||||
),
|
||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||
ca_cert=dict(
|
||||
type="path",
|
||||
aliases=["ssl_ca_cert"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_SSL_CA_CERT"]),
|
||||
),
|
||||
validate_certs=dict(
|
||||
type="bool",
|
||||
default=True,
|
||||
aliases=["verify_ssl"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_VERIFY_SSL"]),
|
||||
),
|
||||
api_key=dict(
|
||||
type="str",
|
||||
no_log=True,
|
||||
fallback=(env_fallback, ["K8S_AUTH_API_KEY"]),
|
||||
),
|
||||
)
|
||||
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE = [
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
]
|
||||
@@ -1,367 +0,0 @@
|
||||
# Copyright: (c) 2021, Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
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,
|
||||
AUTH_PROXY_HEADERS_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
requires as _requires,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
|
||||
try:
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils import (
|
||||
k8sdynamicclient,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.client.discovery import (
|
||||
LazyDiscoverer,
|
||||
)
|
||||
except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
try:
|
||||
import kubernetes
|
||||
from kubernetes.dynamic.exceptions import (
|
||||
ResourceNotFoundError,
|
||||
ResourceNotUniqueError,
|
||||
)
|
||||
from kubernetes.dynamic.resource import Resource
|
||||
except ImportError:
|
||||
# kubernetes import error is handled in module setup
|
||||
# This is defined only for the sake of Ansible's checked import requirement
|
||||
Resource = Any # type: ignore
|
||||
|
||||
try:
|
||||
import urllib3
|
||||
|
||||
urllib3.disable_warnings()
|
||||
except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
|
||||
_pool = {}
|
||||
|
||||
|
||||
class unique_string(str):
|
||||
_low = None
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
def lower(self):
|
||||
if self._low is None:
|
||||
lower = str.lower(self)
|
||||
if str.__eq__(lower, self):
|
||||
self._low = self
|
||||
else:
|
||||
self._low = unique_string(lower)
|
||||
return self._low
|
||||
|
||||
|
||||
def _create_auth_spec(module=None, **kwargs) -> Dict:
|
||||
auth: Dict = {}
|
||||
# If authorization variables aren't defined, look for them in environment variables
|
||||
for true_name, arg_name in AUTH_ARG_MAP.items():
|
||||
if module and module.params.get(arg_name) is not None:
|
||||
auth[true_name] = module.params.get(arg_name)
|
||||
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
|
||||
auth[true_name] = kwargs.get(arg_name)
|
||||
elif true_name in kwargs and kwargs.get(true_name) is not None:
|
||||
# Aliases in kwargs
|
||||
auth[true_name] = kwargs.get(true_name)
|
||||
elif arg_name == "proxy_headers":
|
||||
# specific case for 'proxy_headers' which is a dictionary
|
||||
proxy_headers = {}
|
||||
for key in AUTH_PROXY_HEADERS_SPEC.keys():
|
||||
env_value = os.getenv(
|
||||
"K8S_AUTH_PROXY_HEADERS_{0}".format(key.upper()), None
|
||||
)
|
||||
if env_value is not None:
|
||||
if AUTH_PROXY_HEADERS_SPEC[key].get("type") == "bool":
|
||||
env_value = env_value.lower() not in ["0", "false", "no"]
|
||||
proxy_headers[key] = env_value
|
||||
if proxy_headers is not {}:
|
||||
auth[true_name] = proxy_headers
|
||||
else:
|
||||
env_value = os.getenv(
|
||||
"K8S_AUTH_{0}".format(arg_name.upper()), None
|
||||
) or os.getenv("K8S_AUTH_{0}".format(true_name.upper()), None)
|
||||
if env_value is not None:
|
||||
if AUTH_ARG_SPEC[arg_name].get("type") == "bool":
|
||||
env_value = env_value.lower() not in ["0", "false", "no"]
|
||||
auth[true_name] = env_value
|
||||
|
||||
return auth
|
||||
|
||||
|
||||
def _load_config(auth: Dict) -> None:
|
||||
kubeconfig = auth.get("kubeconfig")
|
||||
optional_arg = {
|
||||
"context": auth.get("context"),
|
||||
"persist_config": auth.get("persist_config"),
|
||||
}
|
||||
if kubeconfig:
|
||||
if isinstance(kubeconfig, string_types):
|
||||
kubernetes.config.load_kube_config(config_file=kubeconfig, **optional_arg)
|
||||
elif isinstance(kubeconfig, dict):
|
||||
kubernetes.config.load_kube_config_from_dict(
|
||||
config_dict=kubeconfig, **optional_arg
|
||||
)
|
||||
else:
|
||||
kubernetes.config.load_kube_config(config_file=None, **optional_arg)
|
||||
|
||||
|
||||
def _create_configuration(auth: Dict):
|
||||
def auth_set(*names: list) -> bool:
|
||||
return all(auth.get(name) for name in names)
|
||||
|
||||
if auth_set("host"):
|
||||
# Removing trailing slashes if any from hostname
|
||||
auth["host"] = auth.get("host").rstrip("/")
|
||||
|
||||
if (
|
||||
auth_set("username", "password", "host")
|
||||
or auth_set("api_key", "host")
|
||||
or auth_set("cert_file", "key_file", "host")
|
||||
):
|
||||
# We have enough in the parameters to authenticate, no need to load incluster or kubeconfig
|
||||
pass
|
||||
elif auth_set("kubeconfig") or auth_set("context"):
|
||||
try:
|
||||
_load_config(auth)
|
||||
except Exception as err:
|
||||
raise err
|
||||
|
||||
else:
|
||||
# First try to do incluster config, then kubeconfig
|
||||
try:
|
||||
kubernetes.config.load_incluster_config()
|
||||
except kubernetes.config.ConfigException:
|
||||
try:
|
||||
_load_config(auth)
|
||||
except Exception as err:
|
||||
raise err
|
||||
|
||||
# Override any values in the default configuration with Ansible parameters
|
||||
# As of kubernetes-client v12.0.0, get_default_copy() is required here
|
||||
try:
|
||||
configuration = kubernetes.client.Configuration().get_default_copy()
|
||||
except AttributeError:
|
||||
configuration = kubernetes.client.Configuration()
|
||||
|
||||
for key, value in iteritems(auth):
|
||||
if key in AUTH_ARG_MAP.keys() and value is not None:
|
||||
if key == "api_key":
|
||||
setattr(
|
||||
configuration, key, {"authorization": "Bearer {0}".format(value)}
|
||||
)
|
||||
elif key == "proxy_headers":
|
||||
headers = urllib3.util.make_headers(**value)
|
||||
setattr(configuration, key, headers)
|
||||
else:
|
||||
setattr(configuration, key, value)
|
||||
|
||||
return configuration
|
||||
|
||||
|
||||
def _create_headers(module=None, **kwargs):
|
||||
header_map = {
|
||||
"impersonate_user": "Impersonate-User",
|
||||
"impersonate_groups": "Impersonate-Group",
|
||||
}
|
||||
|
||||
headers = {}
|
||||
for arg_name, header_name in header_map.items():
|
||||
value = None
|
||||
if module and module.params.get(arg_name) is not None:
|
||||
value = module.params.get(arg_name)
|
||||
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
|
||||
value = kwargs.get(arg_name)
|
||||
else:
|
||||
value = os.getenv("K8S_AUTH_{0}".format(arg_name.upper()), None)
|
||||
if value is not None:
|
||||
if AUTH_ARG_SPEC[arg_name].get("type") == "list":
|
||||
value = [x for x in value.split(",") if x != ""]
|
||||
if value:
|
||||
headers[header_name] = value
|
||||
return headers
|
||||
|
||||
|
||||
def _configuration_digest(configuration, **kwargs) -> str:
|
||||
m = hashlib.sha256()
|
||||
for k in AUTH_ARG_MAP:
|
||||
if not hasattr(configuration, k):
|
||||
v = None
|
||||
else:
|
||||
v = getattr(configuration, k)
|
||||
if v and k in ["ssl_ca_cert", "cert_file", "key_file"]:
|
||||
with open(str(v), "r") as fd:
|
||||
content = fd.read()
|
||||
m.update(content.encode())
|
||||
else:
|
||||
m.update(str(v).encode())
|
||||
for k, v in kwargs.items():
|
||||
content = "{0}: {1}".format(k, v)
|
||||
m.update(content.encode())
|
||||
digest = m.hexdigest()
|
||||
|
||||
return digest
|
||||
|
||||
|
||||
def _set_header(client, header, value):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
client.set_default_header(header_name=unique_string(header), header_value=v)
|
||||
else:
|
||||
client.set_default_header(header_name=header, header_value=value)
|
||||
|
||||
|
||||
def cache(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
client = None
|
||||
hashable_kwargs = {}
|
||||
for k, v in kwargs.items():
|
||||
if isinstance(v, list):
|
||||
hashable_kwargs[k] = ",".join(sorted(v))
|
||||
else:
|
||||
hashable_kwargs[k] = v
|
||||
digest = _configuration_digest(*args, **hashable_kwargs)
|
||||
if digest in _pool:
|
||||
client = _pool[digest]
|
||||
else:
|
||||
client = func(*args, **kwargs)
|
||||
_pool[digest] = client
|
||||
|
||||
return client
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@cache
|
||||
def create_api_client(configuration, **headers):
|
||||
client = kubernetes.client.ApiClient(configuration)
|
||||
for header, value in headers.items():
|
||||
_set_header(client, header, value)
|
||||
return k8sdynamicclient.K8SDynamicClient(client, discoverer=LazyDiscoverer)
|
||||
|
||||
|
||||
class K8SClient:
|
||||
"""A Client class for K8S modules.
|
||||
|
||||
This class has the primary purpose to proxy the kubernetes client and resource objects.
|
||||
If there is a need for other methods or attributes to be proxied, they can be added here.
|
||||
"""
|
||||
|
||||
K8S_SERVER_DRY_RUN = "All"
|
||||
|
||||
def __init__(self, configuration, client, dry_run: bool = False) -> None:
|
||||
self.configuration = configuration
|
||||
self.client = client
|
||||
self.dry_run = dry_run
|
||||
|
||||
@property
|
||||
def resources(self) -> List[Any]:
|
||||
return self.client.resources
|
||||
|
||||
def _find_resource_with_prefix(
|
||||
self, prefix: str, kind: str, api_version: str
|
||||
) -> Resource:
|
||||
for attribute in ["kind", "name", "singular_name"]:
|
||||
try:
|
||||
return self.client.resources.get(
|
||||
**{"prefix": prefix, "api_version": api_version, attribute: kind}
|
||||
)
|
||||
except (ResourceNotFoundError, ResourceNotUniqueError):
|
||||
pass
|
||||
return self.client.resources.get(
|
||||
prefix=prefix, api_version=api_version, short_names=[kind]
|
||||
)
|
||||
|
||||
def resource(self, kind: str, api_version: str) -> Resource:
|
||||
"""Fetch a kubernetes client resource.
|
||||
|
||||
This will attempt to find a kubernetes resource trying, in order, kind,
|
||||
name, singular_name and short_names.
|
||||
"""
|
||||
try:
|
||||
if api_version == "v1":
|
||||
return self._find_resource_with_prefix("api", kind, api_version)
|
||||
except ResourceNotFoundError:
|
||||
pass
|
||||
return self._find_resource_with_prefix(None, kind, api_version)
|
||||
|
||||
def _ensure_dry_run(self, params: Dict) -> Dict:
|
||||
if self.dry_run:
|
||||
params["dry_run"] = self.K8S_SERVER_DRY_RUN
|
||||
return params
|
||||
|
||||
def validate(
|
||||
self, resource, version: Optional[str] = None, strict: Optional[bool] = False
|
||||
):
|
||||
return self.client.validate(resource, version, strict)
|
||||
|
||||
def get(self, resource, **params):
|
||||
return resource.get(**params)
|
||||
|
||||
def delete(self, resource, **params):
|
||||
return resource.delete(**self._ensure_dry_run(params))
|
||||
|
||||
def apply(self, resource, definition, namespace, **params):
|
||||
return resource.apply(
|
||||
definition, namespace=namespace, **self._ensure_dry_run(params)
|
||||
)
|
||||
|
||||
def create(self, resource, definition, **params):
|
||||
return resource.create(definition, **self._ensure_dry_run(params))
|
||||
|
||||
def replace(self, resource, definition, **params):
|
||||
return resource.replace(definition, **self._ensure_dry_run(params))
|
||||
|
||||
def patch(self, resource, definition, **params):
|
||||
return resource.patch(definition, **self._ensure_dry_run(params))
|
||||
|
||||
|
||||
def get_api_client(module=None, **kwargs: Optional[Any]) -> K8SClient:
|
||||
auth_spec = _create_auth_spec(module, **kwargs)
|
||||
if module:
|
||||
requires = module.requires
|
||||
else:
|
||||
requires = _requires
|
||||
if isinstance(auth_spec.get("kubeconfig"), dict):
|
||||
requires("kubernetes", "17.17.0", "to use in-memory config")
|
||||
if auth_spec.get("no_proxy"):
|
||||
requires("kubernetes", "19.15.0", "to use the no_proxy feature")
|
||||
|
||||
try:
|
||||
configuration = _create_configuration(auth_spec)
|
||||
headers = _create_headers(module, **kwargs)
|
||||
client = create_api_client(configuration, **headers)
|
||||
except kubernetes.config.ConfigException as e:
|
||||
msg = "Could not create API client: {0}".format(e)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
dry_run = False
|
||||
if module and module.server_side_dry_run:
|
||||
dry_run = True
|
||||
|
||||
k8s_client = K8SClient(
|
||||
configuration=configuration,
|
||||
client=client,
|
||||
dry_run=dry_run,
|
||||
)
|
||||
|
||||
return k8s_client
|
||||
@@ -1,172 +0,0 @@
|
||||
import traceback
|
||||
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.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
class AnsibleK8SModule:
|
||||
"""A base module class for K8S modules.
|
||||
|
||||
This class should be used instead of directly using AnsibleModule. If there
|
||||
is a need for other methods or attributes to be proxied, they can be added
|
||||
here.
|
||||
"""
|
||||
|
||||
default_settings = {
|
||||
"check_k8s": True,
|
||||
"check_pyyaml": True,
|
||||
"module_class": AnsibleModule,
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
local_settings = {}
|
||||
for key in AnsibleK8SModule.default_settings:
|
||||
try:
|
||||
local_settings[key] = kwargs.pop(key)
|
||||
except KeyError:
|
||||
local_settings[key] = AnsibleK8SModule.default_settings[key]
|
||||
self.settings = local_settings
|
||||
|
||||
self._module = self.settings["module_class"](**kwargs)
|
||||
|
||||
if self.settings["check_k8s"]:
|
||||
self.requires("kubernetes")
|
||||
self.has_at_least("kubernetes", "24.2.0", warn=True)
|
||||
|
||||
if self.settings["check_pyyaml"]:
|
||||
self.requires("pyyaml")
|
||||
|
||||
@property
|
||||
def check_mode(self):
|
||||
return self._module.check_mode
|
||||
|
||||
@property
|
||||
def server_side_dry_run(self):
|
||||
return self.check_mode and self.has_at_least("kubernetes", "18.20.0")
|
||||
|
||||
@property
|
||||
def _diff(self):
|
||||
return self._module._diff
|
||||
|
||||
@property
|
||||
def _name(self):
|
||||
return self._module._name
|
||||
|
||||
@property
|
||||
def params(self):
|
||||
return self._module.params
|
||||
|
||||
def warn(self, *args, **kwargs):
|
||||
return self._module.warn(*args, **kwargs)
|
||||
|
||||
def deprecate(self, *args, **kwargs):
|
||||
return self._module.deprecate(*args, **kwargs)
|
||||
|
||||
def debug(self, *args, **kwargs):
|
||||
return self._module.debug(*args, **kwargs)
|
||||
|
||||
def exit_json(self, *args, **kwargs):
|
||||
return self._module.exit_json(*args, **kwargs)
|
||||
|
||||
def fail_json(self, *args, **kwargs):
|
||||
return self._module.fail_json(*args, **kwargs)
|
||||
|
||||
def fail_from_exception(self, exception):
|
||||
msg = to_text(exception)
|
||||
tb = "".join(
|
||||
traceback.format_exception(None, exception, exception.__traceback__)
|
||||
)
|
||||
return self.fail_json(msg=msg, exception=tb)
|
||||
|
||||
def has_at_least(
|
||||
self, dependency: str, minimum: Optional[str] = None, warn: bool = False
|
||||
) -> bool:
|
||||
supported = has_at_least(dependency, minimum)
|
||||
if not supported and warn:
|
||||
self.warn(
|
||||
"{0}<{1} is not supported or tested. Some features may not work.".format(
|
||||
dependency, minimum
|
||||
)
|
||||
)
|
||||
return supported
|
||||
|
||||
def requires(
|
||||
self,
|
||||
dependency: str,
|
||||
minimum: Optional[str] = None,
|
||||
reason: Optional[str] = None,
|
||||
) -> None:
|
||||
try:
|
||||
requires(dependency, minimum, reason=reason)
|
||||
except Exception as e:
|
||||
self.fail_json(msg=to_text(e))
|
||||
|
||||
|
||||
def gather_versions() -> dict:
|
||||
versions = {}
|
||||
try:
|
||||
import jsonpatch
|
||||
|
||||
versions["jsonpatch"] = jsonpatch.__version__
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
import kubernetes
|
||||
|
||||
versions["kubernetes"] = kubernetes.__version__
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
import kubernetes_validate
|
||||
|
||||
versions["kubernetes-validate"] = kubernetes_validate.__version__
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
versions["pyyaml"] = yaml.__version__
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
def has_at_least(dependency: str, minimum: Optional[str] = None) -> bool:
|
||||
"""Check if a specific dependency is present at a minimum version.
|
||||
|
||||
If a minimum version is not specified it will check only that the
|
||||
dependency is present.
|
||||
"""
|
||||
dependencies = gather_versions()
|
||||
current = dependencies.get(dependency)
|
||||
if current is not None:
|
||||
if minimum is None:
|
||||
return True
|
||||
supported = LooseVersion(current) >= LooseVersion(minimum)
|
||||
return supported
|
||||
return False
|
||||
|
||||
|
||||
def requires(
|
||||
dependency: str, minimum: Optional[str] = None, reason: Optional[str] = None
|
||||
) -> None:
|
||||
"""Fail if a specific dependency is not present at a minimum version.
|
||||
|
||||
If a minimum version is not specified it will require only that the
|
||||
dependency is present. This function raises an exception when the
|
||||
dependency is not found at the required version.
|
||||
"""
|
||||
if not has_at_least(dependency, minimum):
|
||||
if minimum is not None:
|
||||
lib = "{0}>={1}".format(dependency, minimum)
|
||||
else:
|
||||
lib = dependency
|
||||
raise Exception(missing_required_lib(lib, reason=reason))
|
||||
@@ -1,12 +0,0 @@
|
||||
# Copyright: (c) 2021, Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
|
||||
class CoreException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ResourceTimeout(CoreException):
|
||||
def __init__(self, message="", result=None):
|
||||
self.result = result or {}
|
||||
super().__init__(message)
|
||||
@@ -1,134 +0,0 @@
|
||||
# Copyright: (c) 2021, Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
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:
|
||||
import yaml
|
||||
except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
|
||||
class ResourceDefinition(dict):
|
||||
"""Representation of a resource definition.
|
||||
|
||||
This is a thin wrapper around a dictionary representation of a resource
|
||||
definition, with a few properties defined for conveniently accessing the
|
||||
commonly used fields.
|
||||
"""
|
||||
|
||||
@property
|
||||
def kind(self) -> Optional[str]:
|
||||
return self.get("kind")
|
||||
|
||||
@property
|
||||
def api_version(self) -> Optional[str]:
|
||||
return self.get("apiVersion")
|
||||
|
||||
@property
|
||||
def namespace(self) -> Optional[str]:
|
||||
metadata = self.get("metadata", {})
|
||||
return metadata.get("namespace")
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
metadata = self.get("metadata", {})
|
||||
return metadata.get("name")
|
||||
|
||||
|
||||
def create_definitions(params: Dict) -> List[ResourceDefinition]:
|
||||
"""Create a list of ResourceDefinitions from module inputs.
|
||||
|
||||
This will take the module's inputs and return a list of ResourceDefintion
|
||||
objects. The resource definitions returned by this function should be as
|
||||
complete a definition as we can create based on the input. Any *List kinds
|
||||
will be removed and replaced by the resources contained in it.
|
||||
"""
|
||||
if params.get("resource_definition"):
|
||||
d = cast(Union[str, List, Dict], params.get("resource_definition"))
|
||||
definitions = from_yaml(d)
|
||||
elif params.get("src"):
|
||||
d = cast(str, params.get("src"))
|
||||
if hasattr(d, "startswith") and d.startswith(("https://", "http://", "ftp://")):
|
||||
data = Request().open("GET", d).read().decode("utf8")
|
||||
definitions = from_yaml(data)
|
||||
else:
|
||||
definitions = from_file(d)
|
||||
else:
|
||||
# We'll create an empty definition and let merge_params set values
|
||||
# from the module parameters.
|
||||
definitions = [{}]
|
||||
|
||||
resource_definitions: List[Dict] = []
|
||||
for definition in definitions:
|
||||
merge_params(definition, params)
|
||||
kind = cast(Optional[str], definition.get("kind"))
|
||||
if kind and kind.endswith("List"):
|
||||
resource_definitions += flatten_list_kind(definition, params)
|
||||
else:
|
||||
resource_definitions.append(definition)
|
||||
return list(map(ResourceDefinition, resource_definitions))
|
||||
|
||||
|
||||
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):
|
||||
definitions += yaml.safe_load_all(definition)
|
||||
elif isinstance(definition, list):
|
||||
for item in definition:
|
||||
if isinstance(item, string_types):
|
||||
definitions += yaml.safe_load_all(item)
|
||||
else:
|
||||
definitions.append(item)
|
||||
else:
|
||||
definition = cast(Dict, definition)
|
||||
definitions.append(definition)
|
||||
return filter(None, definitions)
|
||||
|
||||
|
||||
def from_file(filepath: str) -> Iterable[Dict]:
|
||||
"""Load resource definitions from a path to a yaml file."""
|
||||
path = os.path.normpath(filepath)
|
||||
with open(path, "rb") as f:
|
||||
definitions = list(yaml.safe_load_all(f))
|
||||
return filter(None, definitions)
|
||||
|
||||
|
||||
def merge_params(definition: Dict, params: Dict) -> Dict:
|
||||
"""Merge module parameters with the resource definition.
|
||||
|
||||
Fields in the resource definition take precedence over module parameters.
|
||||
"""
|
||||
definition.setdefault("kind", params.get("kind"))
|
||||
definition.setdefault("apiVersion", params.get("api_version"))
|
||||
metadata = definition.setdefault("metadata", {})
|
||||
# The following should only be set if we have values for them
|
||||
if params.get("namespace"):
|
||||
metadata.setdefault("namespace", params.get("namespace"))
|
||||
if params.get("name"):
|
||||
metadata.setdefault("name", params.get("name"))
|
||||
if params.get("generate_name"):
|
||||
metadata.setdefault("generateName", params.get("generate_name"))
|
||||
return definition
|
||||
|
||||
|
||||
def flatten_list_kind(definition: Dict, params: Dict) -> List[Dict]:
|
||||
"""Replace *List kind with the items it contains.
|
||||
|
||||
This will take a definition for a *List resource and return a list of
|
||||
definitions for the items contained within the List.
|
||||
"""
|
||||
items = []
|
||||
kind = cast(str, definition.get("kind"))[:-4]
|
||||
api_version = definition.get("apiVersion")
|
||||
for item in definition.get("items", []):
|
||||
item.setdefault("kind", kind)
|
||||
item.setdefault("apiVersion", api_version)
|
||||
items.append(merge_params(item, params))
|
||||
return items
|
||||
@@ -1,233 +0,0 @@
|
||||
# Copyright: (c) 2021, Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
ResourceTimeout,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
|
||||
create_definitions,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
diff_objects,
|
||||
hide_fields,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import exists
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.selector import (
|
||||
LabelSelectorFilter,
|
||||
)
|
||||
|
||||
|
||||
def validate(client, module, resource):
|
||||
def _prepend_resource_info(resource, msg):
|
||||
return "%s %s: %s" % (resource["kind"], resource["metadata"]["name"], msg)
|
||||
|
||||
module.requires("kubernetes-validate")
|
||||
|
||||
warnings, errors = client.validate(
|
||||
resource,
|
||||
module.params["validate"].get("version"),
|
||||
module.params["validate"].get("strict"),
|
||||
)
|
||||
|
||||
if errors and module.params["validate"]["fail_on_error"]:
|
||||
module.fail_json(
|
||||
msg="\n".join([_prepend_resource_info(resource, error) for error in errors])
|
||||
)
|
||||
return [_prepend_resource_info(resource, msg) for msg in warnings + errors]
|
||||
|
||||
|
||||
def get_definitions(svc, params):
|
||||
try:
|
||||
definitions = create_definitions(params)
|
||||
except Exception as e:
|
||||
msg = "Failed to load resource definition: {0}".format(e)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
delete_all = params.get("delete_all")
|
||||
src = params.get("src")
|
||||
resource_definition = params.get("resource_definition")
|
||||
name = params.get("name")
|
||||
state = params.get("state")
|
||||
|
||||
if (
|
||||
delete_all
|
||||
and state == "absent"
|
||||
and name is None
|
||||
and resource_definition is None
|
||||
and src is None
|
||||
):
|
||||
# Delete all resources in the namespace for the specified resource type
|
||||
if params.get("kind") is None:
|
||||
raise CoreException(
|
||||
"'kind' option is required to specify the resource type."
|
||||
)
|
||||
|
||||
resource = svc.find_resource(
|
||||
params.get("kind"), params.get("api_version"), fail=True
|
||||
)
|
||||
definitions = svc.retrieve_all(
|
||||
resource,
|
||||
params.get("namespace"),
|
||||
params.get("label_selectors"),
|
||||
)
|
||||
|
||||
return definitions
|
||||
|
||||
|
||||
def run_module(module) -> None:
|
||||
results = []
|
||||
changed = False
|
||||
client = get_api_client(module)
|
||||
svc = K8sService(client, module)
|
||||
|
||||
definitions = get_definitions(svc, module.params)
|
||||
|
||||
for definition in definitions:
|
||||
result = {"changed": False, "result": {}}
|
||||
warnings = []
|
||||
|
||||
if module.params.get("validate") is not None:
|
||||
warnings = validate(client, module, definition)
|
||||
|
||||
try:
|
||||
result = perform_action(svc, definition, module.params)
|
||||
except Exception as e:
|
||||
try:
|
||||
error = e.result
|
||||
except AttributeError:
|
||||
error = {}
|
||||
try:
|
||||
error["reason"] = e.__cause__.reason
|
||||
except AttributeError:
|
||||
pass
|
||||
error["msg"] = to_native(e)
|
||||
if warnings:
|
||||
error.setdefault("warnings", []).extend(warnings)
|
||||
|
||||
if module.params.get("continue_on_error"):
|
||||
result["error"] = error
|
||||
else:
|
||||
module.fail_json(**error)
|
||||
|
||||
if warnings:
|
||||
result.setdefault("warnings", []).extend(warnings)
|
||||
changed |= result["changed"]
|
||||
results.append(result)
|
||||
|
||||
if len(results) == 1:
|
||||
module.exit_json(**results[0])
|
||||
|
||||
module.exit_json(**{"changed": changed, "result": {"results": results}})
|
||||
|
||||
|
||||
def perform_action(svc, definition: Dict, params: Dict) -> Dict:
|
||||
origin_name = definition["metadata"].get("name")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
label_selectors = params.get("label_selectors")
|
||||
state = params.get("state", None)
|
||||
kind = definition.get("kind")
|
||||
api_version = definition.get("apiVersion")
|
||||
hidden_fields = params.get("hidden_fields")
|
||||
|
||||
result = {"changed": False, "result": {}}
|
||||
instance = {}
|
||||
|
||||
resource = svc.find_resource(kind, api_version, fail=True)
|
||||
definition["kind"] = resource.kind
|
||||
definition["apiVersion"] = resource.group_version
|
||||
existing = svc.retrieve(resource, definition)
|
||||
|
||||
if state == "absent":
|
||||
if exists(existing) and existing.kind.endswith("List"):
|
||||
instance = []
|
||||
for item in existing.items:
|
||||
r = svc.delete(resource, item, existing)
|
||||
instance.append(r)
|
||||
else:
|
||||
instance = svc.delete(resource, definition, existing)
|
||||
result["method"] = "delete"
|
||||
if exists(existing):
|
||||
result["changed"] = True
|
||||
else:
|
||||
if label_selectors:
|
||||
filter_selector = LabelSelectorFilter(label_selectors)
|
||||
if not filter_selector.isMatching(definition):
|
||||
result["changed"] = False
|
||||
result["msg"] = (
|
||||
"resource 'kind={kind},name={name},namespace={namespace}' "
|
||||
"filtered by label_selectors.".format(
|
||||
kind=kind,
|
||||
name=origin_name,
|
||||
namespace=namespace,
|
||||
)
|
||||
)
|
||||
return result
|
||||
|
||||
if params.get("apply"):
|
||||
instance = svc.apply(resource, definition, existing)
|
||||
result["method"] = "apply"
|
||||
elif not existing:
|
||||
if state == "patched":
|
||||
result.setdefault("warnings", []).append(
|
||||
"resource 'kind={kind},name={name}' was not found but will not be "
|
||||
"created as 'state' parameter has been set to '{state}'".format(
|
||||
kind=kind, name=definition["metadata"].get("name"), state=state
|
||||
)
|
||||
)
|
||||
return result
|
||||
instance = svc.create(resource, definition)
|
||||
result["method"] = "create"
|
||||
result["changed"] = True
|
||||
elif params.get("force", False):
|
||||
instance = svc.replace(resource, definition, existing)
|
||||
result["method"] = "replace"
|
||||
else:
|
||||
instance = svc.update(resource, definition, existing)
|
||||
result["method"] = "update"
|
||||
|
||||
# If needed, wait and/or create diff
|
||||
success = True
|
||||
|
||||
if result["method"] == "delete":
|
||||
# wait logic is a bit different for delete as `instance` may be a status object
|
||||
if params.get("wait") and not svc.module.check_mode:
|
||||
success, waited, duration = svc.wait(resource, definition)
|
||||
result["duration"] = duration
|
||||
else:
|
||||
if params.get("wait") and not svc.module.check_mode:
|
||||
success, instance, duration = svc.wait(resource, instance)
|
||||
result["duration"] = duration
|
||||
|
||||
if result["method"] not in ("create", "delete"):
|
||||
if existing:
|
||||
existing = existing.to_dict()
|
||||
else:
|
||||
existing = {}
|
||||
match, diffs = diff_objects(existing, instance, hidden_fields)
|
||||
if match and diffs:
|
||||
result.setdefault("warnings", []).append(
|
||||
"No meaningful diff was generated, but the API may not be idempotent "
|
||||
"(only metadata.generation or metadata.resourceVersion were changed)"
|
||||
)
|
||||
result["changed"] = not match
|
||||
if svc.module._diff:
|
||||
result["diff"] = diffs
|
||||
|
||||
result["result"] = hide_fields(instance, hidden_fields)
|
||||
if not success:
|
||||
raise ResourceTimeout(
|
||||
'"{0}" "{1}": Timed out waiting on resource'.format(
|
||||
definition["kind"], origin_name
|
||||
),
|
||||
result,
|
||||
)
|
||||
|
||||
return result
|
||||
@@ -1,545 +0,0 @@
|
||||
# Copyright: (c) 2021, Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
import copy
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
|
||||
generate_hash,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import requires
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
|
||||
Waiter,
|
||||
exists,
|
||||
get_waiter,
|
||||
resource_absent,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.exceptions import (
|
||||
BadRequestError,
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
MethodNotAllowedError,
|
||||
NotFoundError,
|
||||
ResourceNotFoundError,
|
||||
ResourceNotUniqueError,
|
||||
)
|
||||
except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.resource import Resource, ResourceInstance
|
||||
except ImportError:
|
||||
# These are defined only for the sake of Ansible's checked import requirement
|
||||
Resource = Any # type: ignore
|
||||
ResourceInstance = Any # type: ignore
|
||||
|
||||
try:
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.apply import (
|
||||
apply_object,
|
||||
)
|
||||
except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
try:
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.apply import (
|
||||
recursive_diff,
|
||||
)
|
||||
except ImportError:
|
||||
from ansible.module_utils.common.dict_transformations import recursive_diff
|
||||
|
||||
|
||||
try:
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
_encode_stringdata,
|
||||
)
|
||||
except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
|
||||
class K8sService:
|
||||
"""A Service class for K8S modules.
|
||||
This class has the primary purpose is to perform work on the cluster (e.g., create, apply, replace, update, delete).
|
||||
"""
|
||||
|
||||
def __init__(self, client, module) -> None:
|
||||
self.client = client
|
||||
self.module = module
|
||||
|
||||
@property
|
||||
def _client_side_dry_run(self):
|
||||
return self.module.check_mode and not self.client.dry_run
|
||||
|
||||
def find_resource(
|
||||
self, kind: str, api_version: str, fail: bool = False
|
||||
) -> Optional[Resource]:
|
||||
try:
|
||||
return self.client.resource(kind, api_version)
|
||||
except (ResourceNotFoundError, ResourceNotUniqueError):
|
||||
if fail:
|
||||
raise CoreException(
|
||||
"Failed to find exact match for %s.%s by [kind, name, singularName, shortNames]"
|
||||
% (api_version, kind)
|
||||
)
|
||||
|
||||
def wait(
|
||||
self, resource: Resource, instance: Dict
|
||||
) -> Tuple[bool, Optional[Dict], int]:
|
||||
wait_sleep = self.module.params.get("wait_sleep")
|
||||
wait_timeout = self.module.params.get("wait_timeout")
|
||||
wait_condition = None
|
||||
if self.module.params.get("wait_condition") and self.module.params[
|
||||
"wait_condition"
|
||||
].get("type"):
|
||||
wait_condition = self.module.params["wait_condition"]
|
||||
state = "present"
|
||||
if self.module.params.get("state") == "absent":
|
||||
state = "absent"
|
||||
label_selectors = self.module.params.get("label_selectors")
|
||||
|
||||
waiter = get_waiter(
|
||||
self.client, resource, condition=wait_condition, state=state
|
||||
)
|
||||
return waiter.wait(
|
||||
timeout=wait_timeout,
|
||||
sleep=wait_sleep,
|
||||
name=instance["metadata"].get("name"),
|
||||
namespace=instance["metadata"].get("namespace"),
|
||||
label_selectors=label_selectors,
|
||||
)
|
||||
|
||||
def create_project_request(self, definition: Dict) -> Dict:
|
||||
definition["kind"] = "ProjectRequest"
|
||||
results = {"changed": False, "result": {}}
|
||||
resource = self.find_resource(
|
||||
"ProjectRequest", definition["apiVersion"], fail=True
|
||||
)
|
||||
if not self.module.check_mode:
|
||||
try:
|
||||
k8s_obj = self.client.create(resource, definition)
|
||||
results["result"] = k8s_obj.to_dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to create object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
results["changed"] = True
|
||||
|
||||
return results
|
||||
|
||||
def patch_resource(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
name: str,
|
||||
namespace: str,
|
||||
merge_type: str = None,
|
||||
) -> Dict:
|
||||
if merge_type == "json":
|
||||
self.module.deprecate(
|
||||
msg="json as a merge_type value is deprecated. Please use the k8s_json_patch module instead.",
|
||||
version="4.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
try:
|
||||
params = dict(name=name, namespace=namespace)
|
||||
if merge_type:
|
||||
params["content_type"] = "application/{0}-patch+json".format(merge_type)
|
||||
return self.client.patch(resource, definition, **params).to_dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to patch object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
def retrieve(self, resource: Resource, definition: Dict) -> ResourceInstance:
|
||||
state = self.module.params.get("state", None)
|
||||
append_hash = self.module.params.get("append_hash", False)
|
||||
name = definition["metadata"].get("name")
|
||||
generate_name = definition["metadata"].get("generateName")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
label_selectors = self.module.params.get("label_selectors")
|
||||
existing: ResourceInstance = None
|
||||
|
||||
try:
|
||||
# ignore append_hash for resources other than ConfigMap and Secret
|
||||
if append_hash and definition["kind"] in ["ConfigMap", "Secret"]:
|
||||
if name:
|
||||
name = "%s-%s" % (name, generate_hash(definition))
|
||||
definition["metadata"]["name"] = name
|
||||
elif generate_name:
|
||||
definition["metadata"]["generateName"] = "%s-%s" % (
|
||||
generate_name,
|
||||
generate_hash(definition),
|
||||
)
|
||||
params = {}
|
||||
if name:
|
||||
params["name"] = name
|
||||
if namespace:
|
||||
params["namespace"] = namespace
|
||||
if label_selectors:
|
||||
params["label_selector"] = ",".join(label_selectors)
|
||||
if "name" in params or "label_selector" in params:
|
||||
existing = self.client.get(resource, **params)
|
||||
except (NotFoundError, MethodNotAllowedError):
|
||||
pass
|
||||
except ForbiddenError as e:
|
||||
if (
|
||||
definition["kind"] in ["Project", "ProjectRequest"]
|
||||
and state != "absent"
|
||||
):
|
||||
return self.create_project_request(definition)
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to retrieve requested object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to retrieve requested object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
return existing
|
||||
|
||||
def retrieve_all(
|
||||
self, resource: Resource, namespace: str, label_selectors: List[str] = None
|
||||
) -> List[Dict]:
|
||||
definitions: List[ResourceInstance] = []
|
||||
|
||||
try:
|
||||
params = dict(namespace=namespace)
|
||||
if label_selectors:
|
||||
params["label_selector"] = ",".join(label_selectors)
|
||||
resource_list = self.client.get(resource, **params)
|
||||
for item in resource_list.items:
|
||||
existing = self.client.get(
|
||||
resource, name=item.metadata.name, namespace=namespace
|
||||
)
|
||||
definitions.append(existing.to_dict())
|
||||
except (NotFoundError, MethodNotAllowedError):
|
||||
pass
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to retrieve requested object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
return definitions
|
||||
|
||||
def find(
|
||||
self,
|
||||
kind: str,
|
||||
api_version: str,
|
||||
name: str = None,
|
||||
namespace: Optional[str] = None,
|
||||
label_selectors: Optional[List[str]] = None,
|
||||
field_selectors: Optional[List[str]] = None,
|
||||
wait: Optional[bool] = False,
|
||||
wait_sleep: Optional[int] = 5,
|
||||
wait_timeout: Optional[int] = 120,
|
||||
state: Optional[str] = "present",
|
||||
condition: Optional[Dict] = None,
|
||||
hidden_fields: Optional[List] = None,
|
||||
) -> Dict:
|
||||
resource = self.find_resource(kind, api_version)
|
||||
api_found = bool(resource)
|
||||
if not api_found:
|
||||
return dict(
|
||||
resources=[],
|
||||
msg='Failed to find API for resource with apiVersion "{0}" and kind "{1}"'.format(
|
||||
api_version, kind
|
||||
),
|
||||
api_found=False,
|
||||
)
|
||||
|
||||
if not label_selectors:
|
||||
label_selectors = []
|
||||
if not field_selectors:
|
||||
field_selectors = []
|
||||
|
||||
result = {"resources": [], "api_found": True}
|
||||
|
||||
# With a timeout of 0 the waiter will do a single check and return, effectively not waiting.
|
||||
if not wait:
|
||||
wait_timeout = 0
|
||||
|
||||
if state == "present":
|
||||
predicate = exists
|
||||
else:
|
||||
predicate = resource_absent
|
||||
|
||||
waiter = Waiter(self.client, resource, predicate)
|
||||
|
||||
# This is an initial check to get the resource or resources that we then need to wait on individually.
|
||||
try:
|
||||
success, resources, duration = waiter.wait(
|
||||
timeout=wait_timeout,
|
||||
sleep=wait_sleep,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
label_selectors=label_selectors,
|
||||
field_selectors=field_selectors,
|
||||
)
|
||||
except BadRequestError:
|
||||
return result
|
||||
except CoreException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise CoreException(
|
||||
"Exception '{0}' raised while trying to get resource using (name={1}, namespace={2}, label_selectors={3}, field_selectors={4})".format(
|
||||
e, name, namespace, label_selectors, field_selectors
|
||||
)
|
||||
)
|
||||
|
||||
# There is either no result or there is a List resource with no items
|
||||
if (
|
||||
not resources
|
||||
or resources["kind"].endswith("List")
|
||||
and not resources.get("items")
|
||||
):
|
||||
return result
|
||||
|
||||
instances = resources.get("items") or [resources]
|
||||
|
||||
if not wait:
|
||||
result["resources"] = [
|
||||
hide_fields(instance, hidden_fields) for instance in instances
|
||||
]
|
||||
return result
|
||||
|
||||
# Now wait for the specified state of any resource instances we have found.
|
||||
waiter = get_waiter(self.client, resource, state=state, condition=condition)
|
||||
for instance in instances:
|
||||
name = instance["metadata"].get("name")
|
||||
namespace = instance["metadata"].get("namespace")
|
||||
success, res, duration = waiter.wait(
|
||||
timeout=wait_timeout,
|
||||
sleep=wait_sleep,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
)
|
||||
if not success:
|
||||
raise CoreException(
|
||||
"Failed to gather information about %s(s) even"
|
||||
" after waiting for %s seconds" % (res.get("kind"), duration)
|
||||
)
|
||||
result["resources"].append(hide_fields(res, hidden_fields))
|
||||
return result
|
||||
|
||||
def create(self, resource: Resource, definition: Dict) -> Dict:
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
name = definition["metadata"].get("name")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
k8s_obj = _encode_stringdata(definition)
|
||||
else:
|
||||
try:
|
||||
k8s_obj = self.client.create(
|
||||
resource, definition, namespace=namespace
|
||||
).to_dict()
|
||||
except ConflictError:
|
||||
# Some resources, like ProjectRequests, can't be created multiple times,
|
||||
# because the resources that they create don't match their kind
|
||||
# In this case we'll mark it as unchanged and warn the user
|
||||
self.module.warn(
|
||||
"{0} was not found, but creating it returned a 409 Conflict error. This can happen \
|
||||
if the resource you are creating does not directly create a resource of the same kind.".format(
|
||||
name
|
||||
)
|
||||
)
|
||||
return dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to create object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
return k8s_obj
|
||||
|
||||
def apply(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
existing: Optional[ResourceInstance] = None,
|
||||
) -> Dict:
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
|
||||
server_side_apply = self.module.params.get("server_side_apply")
|
||||
if server_side_apply:
|
||||
requires("kubernetes", "19.15.0", reason="to use server side apply")
|
||||
if self._client_side_dry_run:
|
||||
ignored, patch = apply_object(resource, _encode_stringdata(definition))
|
||||
if existing:
|
||||
k8s_obj = dict_merge(existing.to_dict(), patch)
|
||||
else:
|
||||
k8s_obj = patch
|
||||
else:
|
||||
try:
|
||||
params = {}
|
||||
if server_side_apply:
|
||||
params["server_side"] = True
|
||||
params.update(server_side_apply)
|
||||
k8s_obj = self.client.apply(
|
||||
resource, definition, namespace=namespace, **params
|
||||
).to_dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to apply object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
return k8s_obj
|
||||
|
||||
def replace(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
existing: ResourceInstance,
|
||||
) -> Dict:
|
||||
append_hash = self.module.params.get("append_hash", False)
|
||||
name = definition["metadata"].get("name")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
k8s_obj = _encode_stringdata(definition)
|
||||
else:
|
||||
try:
|
||||
k8s_obj = self.client.replace(
|
||||
resource,
|
||||
definition,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
append_hash=append_hash,
|
||||
).to_dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to replace object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
return k8s_obj
|
||||
|
||||
def update(
|
||||
self, resource: Resource, definition: Dict, existing: ResourceInstance
|
||||
) -> Dict:
|
||||
name = definition["metadata"].get("name")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
k8s_obj = dict_merge(existing.to_dict(), _encode_stringdata(definition))
|
||||
else:
|
||||
exception = None
|
||||
for merge_type in self.module.params.get("merge_type") or [
|
||||
"strategic-merge",
|
||||
"merge",
|
||||
]:
|
||||
try:
|
||||
k8s_obj = self.patch_resource(
|
||||
resource,
|
||||
definition,
|
||||
name,
|
||||
namespace,
|
||||
merge_type=merge_type,
|
||||
)
|
||||
exception = None
|
||||
except CoreException as e:
|
||||
exception = e
|
||||
continue
|
||||
break
|
||||
if exception:
|
||||
raise exception
|
||||
return k8s_obj
|
||||
|
||||
def delete(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
existing: Optional[ResourceInstance] = None,
|
||||
) -> Dict:
|
||||
delete_options = self.module.params.get("delete_options")
|
||||
label_selectors = self.module.params.get("label_selectors")
|
||||
name = definition["metadata"].get("name")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
params = {}
|
||||
|
||||
if not exists(existing):
|
||||
return {}
|
||||
|
||||
# Delete the object
|
||||
if self._client_side_dry_run:
|
||||
return {}
|
||||
|
||||
if name:
|
||||
params["name"] = name
|
||||
|
||||
if namespace:
|
||||
params["namespace"] = namespace
|
||||
|
||||
if label_selectors:
|
||||
params["label_selector"] = ",".join(label_selectors)
|
||||
|
||||
if delete_options:
|
||||
body = {
|
||||
"apiVersion": "v1",
|
||||
"kind": "DeleteOptions",
|
||||
}
|
||||
body.update(delete_options)
|
||||
params["body"] = body
|
||||
|
||||
try:
|
||||
k8s_obj = self.client.delete(resource, **params).to_dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to delete object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
return k8s_obj
|
||||
|
||||
|
||||
def diff_objects(
|
||||
existing: Dict, new: Dict, hidden_fields: Optional[list] = None
|
||||
) -> Tuple[bool, Dict]:
|
||||
result = {}
|
||||
diff = recursive_diff(existing, new)
|
||||
if not diff:
|
||||
return True, result
|
||||
|
||||
result["before"] = diff[0]
|
||||
result["after"] = diff[1]
|
||||
|
||||
if list(result["after"].keys()) != ["metadata"] or list(
|
||||
result["before"].keys()
|
||||
) != ["metadata"]:
|
||||
return False, result
|
||||
|
||||
# If only metadata.generation and metadata.resourceVersion changed, ignore it
|
||||
ignored_keys = set(["generation", "resourceVersion"])
|
||||
|
||||
if not set(result["after"]["metadata"].keys()).issubset(ignored_keys):
|
||||
return False, result
|
||||
if not set(result["before"]["metadata"].keys()).issubset(ignored_keys):
|
||||
return False, result
|
||||
|
||||
result["before"] = hide_fields(result["before"], hidden_fields)
|
||||
result["after"] = hide_fields(result["after"], hidden_fields)
|
||||
|
||||
return True, result
|
||||
|
||||
|
||||
def hide_fields(definition: dict, hidden_fields: Optional[list]) -> dict:
|
||||
if not hidden_fields:
|
||||
return definition
|
||||
result = copy.deepcopy(definition)
|
||||
for hidden_field in hidden_fields:
|
||||
result = hide_field(result, hidden_field)
|
||||
return result
|
||||
|
||||
|
||||
# hide_field is not hugely sophisticated and designed to cope
|
||||
# with e.g. status or metadata.managedFields rather than e.g.
|
||||
# spec.template.spec.containers[0].env[3].value
|
||||
def hide_field(definition: dict, hidden_field: str) -> dict:
|
||||
split = hidden_field.split(".", 1)
|
||||
if split[0] in definition:
|
||||
if len(split) == 2:
|
||||
definition[split[0]] = hide_field(definition[split[0]], split[1])
|
||||
else:
|
||||
del definition[split[0]]
|
||||
return definition
|
||||
@@ -1,244 +0,0 @@
|
||||
import time
|
||||
from functools import partial
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
|
||||
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.exceptions import NotFoundError
|
||||
from kubernetes.dynamic.resource import Resource, ResourceField, ResourceInstance
|
||||
except ImportError:
|
||||
# These are defined only for the sake of Ansible's checked import requirement
|
||||
Resource = Any # type: ignore
|
||||
ResourceInstance = Any # type: ignore
|
||||
pass
|
||||
|
||||
try:
|
||||
from urllib3.exceptions import HTTPError
|
||||
except ImportError:
|
||||
# Handled during module setup
|
||||
pass
|
||||
|
||||
|
||||
def deployment_ready(deployment: ResourceInstance) -> bool:
|
||||
# FIXME: frustratingly bool(deployment.status) is True even if status is empty
|
||||
# Furthermore deployment.status.availableReplicas == deployment.status.replicas == None if status is empty
|
||||
# deployment.status.replicas is None is perfectly ok if desired replicas == 0
|
||||
# Scaling up means that we also need to check that we're not in a
|
||||
# situation where status.replicas == status.availableReplicas
|
||||
# but spec.replicas != status.replicas
|
||||
return bool(
|
||||
deployment.status
|
||||
and deployment.spec.replicas == (deployment.status.replicas or 0)
|
||||
and deployment.status.availableReplicas == deployment.status.replicas
|
||||
and deployment.status.observedGeneration == deployment.metadata.generation
|
||||
and not deployment.status.unavailableReplicas
|
||||
)
|
||||
|
||||
|
||||
def pod_ready(pod: ResourceInstance) -> bool:
|
||||
return bool(
|
||||
pod.status
|
||||
and pod.status.containerStatuses is not None
|
||||
and all(container.ready for container in pod.status.containerStatuses)
|
||||
)
|
||||
|
||||
|
||||
def daemonset_ready(daemonset: ResourceInstance) -> bool:
|
||||
return bool(
|
||||
daemonset.status
|
||||
and daemonset.status.desiredNumberScheduled is not None
|
||||
and daemonset.status.updatedNumberScheduled
|
||||
== daemonset.status.desiredNumberScheduled
|
||||
and daemonset.status.numberReady == daemonset.status.desiredNumberScheduled
|
||||
and daemonset.status.observedGeneration == daemonset.metadata.generation
|
||||
and not daemonset.status.unavailableReplicas
|
||||
)
|
||||
|
||||
|
||||
def statefulset_ready(statefulset: ResourceInstance) -> bool:
|
||||
if statefulset.spec.updateStrategy.type == "OnDelete":
|
||||
return bool(
|
||||
statefulset.status
|
||||
and statefulset.status.observedGeneration
|
||||
== (statefulset.metadata.generation or 0)
|
||||
and statefulset.status.replicas == statefulset.spec.replicas
|
||||
)
|
||||
# These may be None
|
||||
updated_replicas = statefulset.status.updatedReplicas or 0
|
||||
ready_replicas = statefulset.status.readyReplicas or 0
|
||||
return bool(
|
||||
statefulset.status
|
||||
and statefulset.spec.updateStrategy.type == "RollingUpdate"
|
||||
and statefulset.status.observedGeneration
|
||||
== (statefulset.metadata.generation or 0)
|
||||
and statefulset.status.updateRevision == statefulset.status.currentRevision
|
||||
and updated_replicas == statefulset.spec.replicas
|
||||
and ready_replicas == statefulset.spec.replicas
|
||||
and statefulset.status.replicas == statefulset.spec.replicas
|
||||
)
|
||||
|
||||
|
||||
def custom_condition(condition: Dict, resource: ResourceInstance) -> bool:
|
||||
if not resource.status or not resource.status.conditions:
|
||||
return False
|
||||
matches = [x for x in resource.status.conditions if x.type == condition["type"]]
|
||||
if not matches:
|
||||
return False
|
||||
# There should never be more than one condition of a specific type
|
||||
match: ResourceField = matches[0]
|
||||
if match.status == "Unknown":
|
||||
if match.status == condition["status"]:
|
||||
if "reason" not in condition:
|
||||
return True
|
||||
if condition["reason"]:
|
||||
return match.reason == condition["reason"]
|
||||
return False
|
||||
status = True if match.status == "True" else False
|
||||
if status == boolean(condition["status"], strict=False):
|
||||
if condition.get("reason"):
|
||||
return match.reason == condition["reason"]
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def resource_absent(resource: ResourceInstance) -> bool:
|
||||
return not exists(resource)
|
||||
|
||||
|
||||
def exists(resource: Optional[ResourceInstance]) -> bool:
|
||||
"""Simple predicate to check for existence of a resource.
|
||||
|
||||
While a List type resource technically always exists, this will only return
|
||||
true if the List contains items."""
|
||||
return bool(resource) and not empty_list(resource)
|
||||
|
||||
|
||||
RESOURCE_PREDICATES = {
|
||||
"DaemonSet": daemonset_ready,
|
||||
"Deployment": deployment_ready,
|
||||
"Pod": pod_ready,
|
||||
"StatefulSet": statefulset_ready,
|
||||
}
|
||||
|
||||
|
||||
def empty_list(resource: ResourceInstance) -> bool:
|
||||
return resource["kind"].endswith("List") and not resource.get("items")
|
||||
|
||||
|
||||
def clock(total: int, interval: int) -> Iterator[int]:
|
||||
start = time.monotonic()
|
||||
yield 0
|
||||
while (time.monotonic() - start) < total:
|
||||
time.sleep(interval)
|
||||
yield int(time.monotonic() - start)
|
||||
|
||||
|
||||
class Waiter:
|
||||
def __init__(
|
||||
self, client, resource: Resource, predicate: Callable[[ResourceInstance], bool]
|
||||
):
|
||||
self.client = client
|
||||
self.resource = resource
|
||||
self.predicate = predicate
|
||||
|
||||
def wait(
|
||||
self,
|
||||
timeout: int,
|
||||
sleep: int,
|
||||
name: Optional[str] = None,
|
||||
namespace: Optional[str] = None,
|
||||
label_selectors: Optional[List[str]] = None,
|
||||
field_selectors: Optional[List[str]] = None,
|
||||
) -> Tuple[bool, Dict, int]:
|
||||
params = {}
|
||||
|
||||
if name:
|
||||
params["name"] = name
|
||||
|
||||
if namespace:
|
||||
params["namespace"] = namespace
|
||||
|
||||
if label_selectors:
|
||||
params["label_selector"] = ",".join(label_selectors)
|
||||
|
||||
if field_selectors:
|
||||
params["field_selector"] = ",".join(field_selectors)
|
||||
|
||||
instance = {}
|
||||
response = None
|
||||
elapsed = 0
|
||||
for i in clock(timeout, sleep):
|
||||
exception = None
|
||||
elapsed = i
|
||||
try:
|
||||
response = self.client.get(self.resource, **params)
|
||||
except NotFoundError:
|
||||
response = None
|
||||
# Retry connection errors as it may be intermittent network issues
|
||||
except HTTPError as e:
|
||||
exception = e
|
||||
if self.predicate(response):
|
||||
break
|
||||
if exception:
|
||||
msg = (
|
||||
"Exception '{0}' raised while trying to get resource using {1}".format(
|
||||
exception, params
|
||||
)
|
||||
)
|
||||
raise CoreException(msg) from exception
|
||||
if response:
|
||||
instance = response.to_dict()
|
||||
return self.predicate(response), instance, elapsed
|
||||
|
||||
|
||||
class DummyWaiter:
|
||||
"""A no-op waiter that simply returns the item being waited on.
|
||||
|
||||
No API call will be made with this waiter; the function returns
|
||||
immediately. This waiter is useful for waiting on resource instances in
|
||||
check mode, for example.
|
||||
"""
|
||||
|
||||
def wait(
|
||||
self,
|
||||
definition: Dict,
|
||||
timeout: int,
|
||||
sleep: int,
|
||||
label_selectors: Optional[List[str]] = None,
|
||||
) -> Tuple[bool, Optional[Dict], int]:
|
||||
return True, definition, 0
|
||||
|
||||
|
||||
# The better solution would be typing.Protocol, but this is only in 3.8+
|
||||
SupportsWait = Union[Waiter, DummyWaiter]
|
||||
|
||||
|
||||
def get_waiter(
|
||||
client,
|
||||
resource: Resource,
|
||||
state: str = "present",
|
||||
condition: Optional[Dict] = None,
|
||||
check_mode: Optional[bool] = False,
|
||||
) -> SupportsWait:
|
||||
"""Create a Waiter object based on the specified resource.
|
||||
|
||||
This is a convenience method for creating a waiter from a resource.
|
||||
Based on the arguments and the kind of resource, an appropriate waiter
|
||||
will be returned. A waiter can also be created directly, of course.
|
||||
"""
|
||||
if check_mode:
|
||||
return DummyWaiter()
|
||||
if state == "present":
|
||||
if condition:
|
||||
predicate: Callable[[ResourceInstance], bool] = partial(
|
||||
custom_condition, condition
|
||||
)
|
||||
else:
|
||||
predicate = RESOURCE_PREDICATES.get(resource.kind, exists)
|
||||
else:
|
||||
predicate = resource_absent
|
||||
return Waiter(client, resource, predicate)
|
||||
@@ -18,11 +18,12 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from kubernetes.dynamic import DynamicClient
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.apply import k8s_apply
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.exceptions import (
|
||||
ApplyException,
|
||||
)
|
||||
from kubernetes.dynamic import DynamicClient
|
||||
|
||||
|
||||
class K8SDynamicClient(DynamicClient):
|
||||
|
||||
@@ -16,6 +16,7 @@ import re
|
||||
|
||||
|
||||
class Selector(object):
|
||||
|
||||
equality_based_operators = ("==", "!=", "=")
|
||||
|
||||
def __init__(self, data):
|
||||
|
||||
@@ -51,17 +51,6 @@ options:
|
||||
- Chart version to install. If this is not specified, the latest version is installed.
|
||||
required: false
|
||||
type: str
|
||||
dependency_update:
|
||||
description:
|
||||
- Run standalone C(helm dependency update CHART) before the operation.
|
||||
- Run inline C(--dependency-update) with C(helm install) command. This feature is not supported yet with the C(helm upgrade) command.
|
||||
- So we should consider to use I(dependency_update) options with I(replace) option enabled when specifying I(chart_repo_url).
|
||||
- The I(dependency_update) option require the add of C(dependencies) block in C(Chart.yaml/requirements.yaml) file.
|
||||
- For more information please visit U(https://helm.sh/docs/helm/helm_dependency/)
|
||||
default: false
|
||||
type: bool
|
||||
aliases: [ dep_up ]
|
||||
version_added: "2.4.0"
|
||||
release_name:
|
||||
description:
|
||||
- Release name to manage.
|
||||
@@ -105,46 +94,6 @@ options:
|
||||
- Run C(helm repo update) before the operation. Can be run as part of the package installation or as a separate step (see Examples).
|
||||
default: false
|
||||
type: bool
|
||||
set_values:
|
||||
description:
|
||||
- Values to pass to chart configuration
|
||||
required: false
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
value:
|
||||
description:
|
||||
- Value to pass to chart configuration (e.g phase=prod).
|
||||
type: str
|
||||
required: true
|
||||
value_type:
|
||||
description:
|
||||
- Use C(raw) set individual value.
|
||||
- Use C(string) to force a string for an individual value.
|
||||
- Use C(file) to set individual values from a file when the value itself is too long for the command line or is dynamically generated.
|
||||
- Use C(json) to set json values (scalars/objects/arrays). This feature requires helm>=3.10.0.
|
||||
default: raw
|
||||
choices:
|
||||
- raw
|
||||
- string
|
||||
- json
|
||||
- file
|
||||
version_added: '2.4.0'
|
||||
reuse_values:
|
||||
description:
|
||||
- When upgrading package, specifies wether to reuse the last release's values and merge in any overrides from parameters I(release_values),
|
||||
I(values_files) or I(set_values).
|
||||
- If I(reset_values) is set to C(True), this is ignored.
|
||||
type: bool
|
||||
required: false
|
||||
version_added: '2.5.0'
|
||||
reset_values:
|
||||
description:
|
||||
- When upgrading package, reset the values to the ones built into the chart.
|
||||
type: bool
|
||||
required: false
|
||||
default: True
|
||||
version_added: '2.5.0'
|
||||
|
||||
#Helm options
|
||||
disable_hook:
|
||||
@@ -193,11 +142,6 @@ options:
|
||||
type: bool
|
||||
default: False
|
||||
version_added: "0.11.1"
|
||||
post_renderer:
|
||||
description:
|
||||
- Path to an executable to be used for post rendering.
|
||||
type: str
|
||||
version_added: "2.4.0"
|
||||
replace:
|
||||
description:
|
||||
- Reuse the given name, only if that name is a deleted release which remains in the history.
|
||||
@@ -272,15 +216,6 @@ EXAMPLES = r"""
|
||||
state: absent
|
||||
update_repo_cache: true
|
||||
|
||||
- name: Deploy Grafana chart using set values on target
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: stable/grafana
|
||||
release_namespace: monitoring
|
||||
set_values:
|
||||
- value: phase=prod
|
||||
value_type: string
|
||||
|
||||
# From git
|
||||
- name: Git clone stable repo on HEAD
|
||||
ansible.builtin.git:
|
||||
@@ -325,17 +260,6 @@ EXAMPLES = r"""
|
||||
enabled: True
|
||||
logging:
|
||||
enabled: True
|
||||
|
||||
# Deploy latest version
|
||||
- name: Deploy latest version of Grafana chart using reuse_values
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: stable/grafana
|
||||
release_namespace: monitoring
|
||||
reuse_values: true
|
||||
values:
|
||||
replicas: 2
|
||||
version: 3e8ec0b2dffa40fb97d5342e4af887de95faa8c61a62480dd7f8aa03dffcf533
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
@@ -393,11 +317,8 @@ command:
|
||||
sample: helm upgrade ...
|
||||
"""
|
||||
|
||||
import copy
|
||||
import re
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
@@ -406,18 +327,17 @@ try:
|
||||
import yaml
|
||||
|
||||
IMP_YAML = True
|
||||
IMP_YAML_ERR = None
|
||||
except ImportError:
|
||||
IMP_YAML_ERR = traceback.format_exc()
|
||||
IMP_YAML = False
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
run_helm,
|
||||
get_values,
|
||||
get_helm_plugin_list,
|
||||
parse_helm_plugin_list,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
get_helm_version,
|
||||
)
|
||||
|
||||
|
||||
@@ -433,62 +353,45 @@ def get_release(state, release_name):
|
||||
return None
|
||||
|
||||
|
||||
def get_release_status(module, release_name, all_status=False):
|
||||
def get_release_status(module, command, release_name):
|
||||
"""
|
||||
Get Release state from all release status (deployed, failed, pending-install, etc)
|
||||
Get Release state from deployed release
|
||||
"""
|
||||
|
||||
list_command = [
|
||||
module.get_helm_binary(),
|
||||
"list",
|
||||
"--output=yaml",
|
||||
"--filter",
|
||||
release_name,
|
||||
]
|
||||
if all_status:
|
||||
list_command.append("--all")
|
||||
list_command = command + " list --output=yaml --filter " + release_name
|
||||
|
||||
rc, out, err = module.run_helm_command(list_command)
|
||||
rc, out, err = run_helm(module, list_command)
|
||||
|
||||
release = get_release(yaml.safe_load(out), release_name)
|
||||
|
||||
if release is None: # not install
|
||||
return None
|
||||
|
||||
release["values"] = module.get_values(release_name)
|
||||
release["values"] = get_values(module, command, release_name)
|
||||
|
||||
return release
|
||||
|
||||
|
||||
def run_repo_update(module):
|
||||
def run_repo_update(module, command):
|
||||
"""
|
||||
Run Repo update
|
||||
"""
|
||||
repo_update_command = module.get_helm_binary() + " repo update"
|
||||
rc, out, err = module.run_helm_command(repo_update_command)
|
||||
|
||||
|
||||
def run_dep_update(module, chart_ref):
|
||||
"""
|
||||
Run dependency update
|
||||
"""
|
||||
dep_update = module.get_helm_binary() + f" dependency update '{chart_ref}'"
|
||||
rc, out, err = module.run_helm_command(dep_update)
|
||||
repo_update_command = command + " repo update"
|
||||
rc, out, err = run_helm(module, repo_update_command)
|
||||
|
||||
|
||||
def fetch_chart_info(module, command, chart_ref):
|
||||
"""
|
||||
Get chart info
|
||||
"""
|
||||
inspect_command = command + f" show chart '{chart_ref}'"
|
||||
inspect_command = command + " show chart " + chart_ref
|
||||
|
||||
rc, out, err = module.run_helm_command(inspect_command)
|
||||
rc, out, err = run_helm(module, inspect_command)
|
||||
|
||||
return yaml.safe_load(out)
|
||||
|
||||
|
||||
def deploy(
|
||||
module,
|
||||
command,
|
||||
release_name,
|
||||
release_values,
|
||||
@@ -502,13 +405,8 @@ def deploy(
|
||||
atomic=False,
|
||||
create_namespace=False,
|
||||
replace=False,
|
||||
post_renderer=None,
|
||||
skip_crds=False,
|
||||
timeout=None,
|
||||
dependency_update=None,
|
||||
set_value_args=None,
|
||||
reuse_values=None,
|
||||
reset_values=True,
|
||||
):
|
||||
"""
|
||||
Install/upgrade/rollback release chart
|
||||
@@ -516,15 +414,11 @@ def deploy(
|
||||
if replace:
|
||||
# '--replace' is not supported by 'upgrade -i'
|
||||
deploy_command = command + " install"
|
||||
if dependency_update:
|
||||
deploy_command += " --dependency-update"
|
||||
else:
|
||||
deploy_command = command + " upgrade -i" # install/upgrade
|
||||
if reset_values:
|
||||
deploy_command += " --reset-values"
|
||||
|
||||
if reuse_values is not None:
|
||||
deploy_command += " --reuse-values=" + str(reuse_values)
|
||||
# Always reset values to keep release_values equal to values released
|
||||
deploy_command += " --reset-values"
|
||||
|
||||
if wait:
|
||||
deploy_command += " --wait"
|
||||
@@ -558,10 +452,6 @@ def deploy(
|
||||
with open(path, "w") as yaml_file:
|
||||
yaml.dump(release_values, yaml_file, default_flow_style=False)
|
||||
deploy_command += " -f=" + path
|
||||
module.add_cleanup_file(path)
|
||||
|
||||
if post_renderer:
|
||||
deploy_command += " --post-renderer=" + post_renderer
|
||||
|
||||
if skip_crds:
|
||||
deploy_command += " --skip-crds"
|
||||
@@ -569,10 +459,7 @@ def deploy(
|
||||
if history_max is not None:
|
||||
deploy_command += " --history-max=%s" % str(history_max)
|
||||
|
||||
if set_value_args:
|
||||
deploy_command += " " + set_value_args
|
||||
|
||||
deploy_command += " " + release_name + f" '{chart_name}'"
|
||||
deploy_command += " " + release_name + " " + chart_name
|
||||
return deploy_command
|
||||
|
||||
|
||||
@@ -612,13 +499,14 @@ def load_values_files(values_files):
|
||||
return values
|
||||
|
||||
|
||||
def get_plugin_version(plugin):
|
||||
def get_plugin_version(command, plugin):
|
||||
"""
|
||||
Check if helm plugin is installed and return corresponding version
|
||||
"""
|
||||
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
out = parse_helm_plugin_list(output=output.splitlines())
|
||||
cmd = command + " plugin"
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=cmd)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
return None
|
||||
@@ -631,6 +519,7 @@ def get_plugin_version(plugin):
|
||||
|
||||
def helmdiff_check(
|
||||
module,
|
||||
helm_cmd,
|
||||
release_name,
|
||||
chart_ref,
|
||||
release_values,
|
||||
@@ -638,12 +527,11 @@ def helmdiff_check(
|
||||
chart_version=None,
|
||||
replace=False,
|
||||
chart_repo_url=None,
|
||||
post_renderer=False,
|
||||
):
|
||||
"""
|
||||
Use helm diff to determine if a release would change by upgrading a chart.
|
||||
"""
|
||||
cmd = module.get_helm_binary() + " diff upgrade"
|
||||
cmd = helm_cmd + " diff upgrade"
|
||||
cmd += " " + release_name
|
||||
cmd += " " + chart_ref
|
||||
|
||||
@@ -653,21 +541,18 @@ def helmdiff_check(
|
||||
cmd += " " + "--version=" + chart_version
|
||||
if not replace:
|
||||
cmd += " " + "--reset-values"
|
||||
if post_renderer:
|
||||
cmd += " --post-renderer=" + post_renderer
|
||||
|
||||
if release_values != {}:
|
||||
fd, path = tempfile.mkstemp(suffix=".yml")
|
||||
with open(path, "w") as yaml_file:
|
||||
yaml.dump(release_values, yaml_file, default_flow_style=False)
|
||||
cmd += " -f=" + path
|
||||
module.add_cleanup_file(path)
|
||||
|
||||
if values_files:
|
||||
for values_file in values_files:
|
||||
cmd += " -f=" + values_file
|
||||
|
||||
rc, out, err = module.run_helm_command(cmd)
|
||||
rc, out, err = run_helm(module, cmd)
|
||||
return (len(out.strip()) > 0, out.strip())
|
||||
|
||||
|
||||
@@ -695,14 +580,14 @@ def default_check(release_status, chart_info, values=None, values_files=None):
|
||||
)
|
||||
|
||||
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
def main():
|
||||
global module
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
chart_ref=dict(type="path"),
|
||||
chart_repo_url=dict(type="str"),
|
||||
chart_version=dict(type="str"),
|
||||
dependency_update=dict(type="bool", default=False, aliases=["dep_up"]),
|
||||
release_name=dict(type="str", required=True, aliases=["name"]),
|
||||
release_namespace=dict(type="str", required=True, aliases=["namespace"]),
|
||||
release_state=dict(
|
||||
@@ -711,36 +596,52 @@ def argument_spec():
|
||||
release_values=dict(type="dict", default={}, aliases=["values"]),
|
||||
values_files=dict(type="list", default=[], elements="str"),
|
||||
update_repo_cache=dict(type="bool", default=False),
|
||||
# Helm options
|
||||
disable_hook=dict(type="bool", default=False),
|
||||
force=dict(type="bool", default=False),
|
||||
context=dict(
|
||||
type="str",
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
),
|
||||
kubeconfig=dict(
|
||||
type="path",
|
||||
aliases=["kubeconfig_path"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_KUBECONFIG"]),
|
||||
),
|
||||
purge=dict(type="bool", default=True),
|
||||
wait=dict(type="bool", default=False),
|
||||
wait_timeout=dict(type="str"),
|
||||
timeout=dict(type="str"),
|
||||
atomic=dict(type="bool", default=False),
|
||||
create_namespace=dict(type="bool", default=False),
|
||||
post_renderer=dict(type="str"),
|
||||
replace=dict(type="bool", default=False),
|
||||
skip_crds=dict(type="bool", default=False),
|
||||
history_max=dict(type="int"),
|
||||
set_values=dict(type="list", elements="dict"),
|
||||
reuse_values=dict(type="bool"),
|
||||
reset_values=dict(type="bool", default=True),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec(),
|
||||
# Generic auth key
|
||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||
ca_cert=dict(
|
||||
type="path",
|
||||
aliases=["ssl_ca_cert"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_SSL_CA_CERT"]),
|
||||
),
|
||||
validate_certs=dict(
|
||||
type="bool",
|
||||
default=True,
|
||||
aliases=["verify_ssl"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_VERIFY_SSL"]),
|
||||
),
|
||||
api_key=dict(
|
||||
type="str", no_log=True, fallback=(env_fallback, ["K8S_AUTH_API_KEY"])
|
||||
),
|
||||
),
|
||||
required_if=[
|
||||
("release_state", "present", ["release_name", "chart_ref"]),
|
||||
("release_state", "absent", ["release_name"]),
|
||||
],
|
||||
mutually_exclusive=[
|
||||
("context", "ca_cert"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("replace", "history_max"),
|
||||
("wait_timeout", "timeout"),
|
||||
],
|
||||
@@ -752,10 +653,10 @@ def main():
|
||||
|
||||
changed = False
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
chart_ref = module.params.get("chart_ref")
|
||||
chart_repo_url = module.params.get("chart_repo_url")
|
||||
chart_version = module.params.get("chart_version")
|
||||
dependency_update = module.params.get("dependency_update")
|
||||
release_name = module.params.get("release_name")
|
||||
release_state = module.params.get("release_state")
|
||||
release_values = module.params.get("release_values")
|
||||
@@ -770,44 +671,44 @@ def main():
|
||||
wait_timeout = module.params.get("wait_timeout")
|
||||
atomic = module.params.get("atomic")
|
||||
create_namespace = module.params.get("create_namespace")
|
||||
post_renderer = module.params.get("post_renderer")
|
||||
replace = module.params.get("replace")
|
||||
skip_crds = module.params.get("skip_crds")
|
||||
history_max = module.params.get("history_max")
|
||||
timeout = module.params.get("timeout")
|
||||
set_values = module.params.get("set_values")
|
||||
reuse_values = module.params.get("reuse_values")
|
||||
reset_values = module.params.get("reset_values")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = module.get_bin_path("helm", required=True)
|
||||
|
||||
if update_repo_cache:
|
||||
run_repo_update(module)
|
||||
run_repo_update(module, helm_cmd_common)
|
||||
|
||||
# Get real/deployed release status
|
||||
all_status = release_state == "absent"
|
||||
release_status = get_release_status(module, release_name, all_status=all_status)
|
||||
release_status = get_release_status(module, helm_cmd_common, release_name)
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
# keep helm_cmd_common for get_release_status in module_exit_json
|
||||
helm_cmd = helm_cmd_common
|
||||
opt_result = {}
|
||||
if release_state == "absent" and release_status is not None:
|
||||
# skip release statuses 'uninstalled' and 'uninstalling'
|
||||
if not release_status["status"].startswith("uninstall"):
|
||||
if replace:
|
||||
module.fail_json(msg="replace is not applicable when state is absent")
|
||||
if replace:
|
||||
module.fail_json(msg="replace is not applicable when state is absent")
|
||||
|
||||
if wait:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.7.0"):
|
||||
opt_result["warnings"] = []
|
||||
opt_result["warnings"].append(
|
||||
"helm uninstall support option --wait for helm release >= 3.7.0"
|
||||
)
|
||||
wait = False
|
||||
if wait:
|
||||
helm_version = get_helm_version(module, helm_cmd_common)
|
||||
if LooseVersion(helm_version) < LooseVersion("3.7.0"):
|
||||
opt_result["warnings"] = []
|
||||
opt_result["warnings"].append(
|
||||
"helm uninstall support option --wait for helm release >= 3.7.0"
|
||||
)
|
||||
wait = False
|
||||
|
||||
helm_cmd = delete(
|
||||
helm_cmd, release_name, purge, disable_hook, wait, wait_timeout
|
||||
)
|
||||
changed = True
|
||||
helm_cmd = delete(
|
||||
helm_cmd, release_name, purge, disable_hook, wait, wait_timeout
|
||||
)
|
||||
changed = True
|
||||
elif release_state == "present":
|
||||
|
||||
if chart_version is not None:
|
||||
helm_cmd += " --version=" + chart_version
|
||||
|
||||
@@ -817,43 +718,8 @@ 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)
|
||||
|
||||
if dependency_update:
|
||||
if chart_info.get("dependencies"):
|
||||
# Can't use '--dependency-update' with 'helm upgrade' that is the
|
||||
# default chart install method, so if chart_repo_url is defined
|
||||
# we can't use the dependency update command. But, in the near future
|
||||
# we can get rid of this method and use only '--dependency-update'
|
||||
# option. Please see https://github.com/helm/helm/pull/8810
|
||||
if not chart_repo_url and not re.fullmatch(
|
||||
r"^http[s]*://[\w.:/?&=-]+$", chart_ref
|
||||
):
|
||||
run_dep_update(module, chart_ref)
|
||||
|
||||
# To not add --dependency-update option in the deploy function
|
||||
dependency_update = False
|
||||
else:
|
||||
module.warn(
|
||||
"This is a not stable feature with 'chart_repo_url'. Please consider to use dependency update with on-disk charts"
|
||||
)
|
||||
if not replace:
|
||||
msg_fail = (
|
||||
"'--dependency-update' hasn't been supported yet with 'helm upgrade'. "
|
||||
"Please use 'helm install' instead by adding 'replace' option"
|
||||
)
|
||||
module.fail_json(msg=msg_fail)
|
||||
else:
|
||||
module.warn(
|
||||
"There is no dependencies block defined in Chart.yaml. Dependency update will not be performed. "
|
||||
"Please consider add dependencies block or disable dependency_update to remove this warning."
|
||||
)
|
||||
|
||||
if release_status is None: # Not installed
|
||||
set_value_args = None
|
||||
if set_values:
|
||||
set_value_args = module.get_helm_set_values_args(set_values)
|
||||
|
||||
helm_cmd = deploy(
|
||||
module,
|
||||
helm_cmd,
|
||||
release_name,
|
||||
release_values,
|
||||
@@ -865,20 +731,16 @@ def main():
|
||||
values_files=values_files,
|
||||
atomic=atomic,
|
||||
create_namespace=create_namespace,
|
||||
post_renderer=post_renderer,
|
||||
replace=replace,
|
||||
dependency_update=dependency_update,
|
||||
skip_crds=skip_crds,
|
||||
history_max=history_max,
|
||||
timeout=timeout,
|
||||
set_value_args=set_value_args,
|
||||
reuse_values=reuse_values,
|
||||
reset_values=reset_values,
|
||||
)
|
||||
changed = True
|
||||
|
||||
else:
|
||||
helm_diff_version = get_plugin_version("diff")
|
||||
|
||||
helm_diff_version = get_plugin_version(helm_cmd_common, "diff")
|
||||
if helm_diff_version and (
|
||||
not chart_repo_url
|
||||
or (
|
||||
@@ -888,6 +750,7 @@ def main():
|
||||
):
|
||||
(would_change, prepared) = helmdiff_check(
|
||||
module,
|
||||
helm_cmd_common,
|
||||
release_name,
|
||||
chart_ref,
|
||||
release_values,
|
||||
@@ -895,7 +758,6 @@ def main():
|
||||
chart_version,
|
||||
replace,
|
||||
chart_repo_url,
|
||||
post_renderer,
|
||||
)
|
||||
if would_change and module._diff:
|
||||
opt_result["diff"] = {"prepared": prepared}
|
||||
@@ -909,12 +771,7 @@ def main():
|
||||
)
|
||||
|
||||
if force or would_change:
|
||||
set_value_args = None
|
||||
if set_values:
|
||||
set_value_args = module.get_helm_set_values_args(set_values)
|
||||
|
||||
helm_cmd = deploy(
|
||||
module,
|
||||
helm_cmd,
|
||||
release_name,
|
||||
release_values,
|
||||
@@ -926,15 +783,10 @@ def main():
|
||||
values_files=values_files,
|
||||
atomic=atomic,
|
||||
create_namespace=create_namespace,
|
||||
post_renderer=post_renderer,
|
||||
replace=replace,
|
||||
skip_crds=skip_crds,
|
||||
history_max=history_max,
|
||||
timeout=timeout,
|
||||
dependency_update=dependency_update,
|
||||
set_value_args=set_value_args,
|
||||
reuse_values=reuse_values,
|
||||
reset_values=reset_values,
|
||||
)
|
||||
changed = True
|
||||
|
||||
@@ -962,13 +814,13 @@ def main():
|
||||
**opt_result,
|
||||
)
|
||||
|
||||
rc, out, err = module.run_helm_command(helm_cmd)
|
||||
rc, out, err = run_helm(module, helm_cmd)
|
||||
|
||||
module.exit_json(
|
||||
changed=changed,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
status=get_release_status(module, release_name, all_status=True),
|
||||
status=get_release_status(module, helm_cmd_common, release_name),
|
||||
command=helm_cmd,
|
||||
**opt_result,
|
||||
)
|
||||
|
||||
@@ -52,16 +52,7 @@ options:
|
||||
- If set to C(uninstalling), show releases that are currently being uninstalled.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
version_added: "2.3.0"
|
||||
get_all_values:
|
||||
description:
|
||||
- Set to C(True) if you want to get all (computed) values of the release.
|
||||
- When C(False) (default), only user supplied values are returned.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
version_added: "2.4.0"
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.helm_common_options
|
||||
"""
|
||||
@@ -118,44 +109,22 @@ status:
|
||||
type: str
|
||||
returned: always
|
||||
description: Dict of Values used to deploy
|
||||
hooks:
|
||||
type: list
|
||||
elements: dict
|
||||
description: Hooks of the release
|
||||
returned: always
|
||||
version_added: "2.4.0"
|
||||
notes:
|
||||
type: str
|
||||
description: Notes of the release
|
||||
returned: always
|
||||
version_added: "2.4.0"
|
||||
manifest:
|
||||
type: list
|
||||
elements: dict
|
||||
description: Manifest of the release
|
||||
returned: always
|
||||
version_added: "2.4.0"
|
||||
"""
|
||||
|
||||
import copy
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
IMP_YAML = True
|
||||
IMP_YAML_ERR = None
|
||||
except ImportError:
|
||||
IMP_YAML_ERR = traceback.format_exc()
|
||||
IMP_YAML = False
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
run_helm,
|
||||
get_values,
|
||||
)
|
||||
|
||||
|
||||
@@ -169,8 +138,8 @@ def get_release(state, release_name):
|
||||
|
||||
|
||||
# Get Release state from deployed release
|
||||
def get_release_status(module, release_name, release_state, get_all_values=False):
|
||||
list_command = module.get_helm_binary() + " list --output=yaml"
|
||||
def get_release_status(module, command, release_name, release_state):
|
||||
list_command = command + " list --output=yaml"
|
||||
|
||||
valid_release_states = [
|
||||
"all",
|
||||
@@ -187,7 +156,7 @@ def get_release_status(module, release_name, release_state, get_all_values=False
|
||||
list_command += " --%s" % local_release_state
|
||||
|
||||
list_command += " --filter " + release_name
|
||||
rc, out, err = module.run_helm_command(list_command)
|
||||
rc, out, err = run_helm(module, list_command)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(
|
||||
@@ -202,45 +171,71 @@ 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["manifest"] = module.get_manifest(release_name)
|
||||
release["notes"] = module.get_notes(release_name)
|
||||
release["hooks"] = module.get_hooks(release_name)
|
||||
release["values"] = get_values(module, command, release_name)
|
||||
|
||||
return release
|
||||
|
||||
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
release_name=dict(type="str", required=True, aliases=["name"]),
|
||||
release_namespace=dict(type="str", required=True, aliases=["namespace"]),
|
||||
release_state=dict(type="list", default=[], elements="str"),
|
||||
get_all_values=dict(type="bool", required=False, default=False),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec(),
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
release_name=dict(type="str", required=True, aliases=["name"]),
|
||||
release_namespace=dict(type="str", required=True, aliases=["namespace"]),
|
||||
# Helm options
|
||||
context=dict(
|
||||
type="str",
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
),
|
||||
kubeconfig=dict(
|
||||
type="path",
|
||||
aliases=["kubeconfig_path"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_KUBECONFIG"]),
|
||||
),
|
||||
# Generic auth key
|
||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||
ca_cert=dict(
|
||||
type="path",
|
||||
aliases=["ssl_ca_cert"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_SSL_CA_CERT"]),
|
||||
),
|
||||
validate_certs=dict(
|
||||
type="bool",
|
||||
default=True,
|
||||
aliases=["verify_ssl"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_VERIFY_SSL"]),
|
||||
),
|
||||
api_key=dict(
|
||||
type="str", no_log=True, fallback=(env_fallback, ["K8S_AUTH_API_KEY"])
|
||||
),
|
||||
release_state=dict(type="list", default=[], elements="str"),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("kubeconfig", "validate_certs"),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
release_name = module.params.get("release_name")
|
||||
release_state = module.params.get("release_state")
|
||||
get_all_values = module.params.get("get_all_values")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = module.get_bin_path("helm", required=True)
|
||||
|
||||
release_status = get_release_status(
|
||||
module, release_name, release_state, get_all_values
|
||||
module, helm_cmd_common, release_name, release_state
|
||||
)
|
||||
|
||||
if release_status is not None:
|
||||
|
||||
@@ -108,22 +108,21 @@ rc:
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
run_helm,
|
||||
get_helm_plugin_list,
|
||||
parse_helm_plugin_list,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
|
||||
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
state=dict(
|
||||
type="str", default="present", choices=["present", "absent", "latest"]
|
||||
),
|
||||
plugin_path=dict(
|
||||
type="str",
|
||||
),
|
||||
@@ -133,37 +132,60 @@ def argument_spec():
|
||||
plugin_version=dict(
|
||||
type="str",
|
||||
),
|
||||
state=dict(
|
||||
# Helm options
|
||||
context=dict(
|
||||
type="str",
|
||||
default="present",
|
||||
choices=["present", "absent", "latest"],
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def mutually_exclusive():
|
||||
mutually_ex = copy.deepcopy(HELM_AUTH_MUTUALLY_EXCLUSIVE)
|
||||
mutually_ex.append(("plugin_name", "plugin_path"))
|
||||
return mutually_ex
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec(),
|
||||
kubeconfig=dict(
|
||||
type="path",
|
||||
aliases=["kubeconfig_path"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_KUBECONFIG"]),
|
||||
),
|
||||
# Generic auth key
|
||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||
ca_cert=dict(
|
||||
type="path",
|
||||
aliases=["ssl_ca_cert"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_SSL_CA_CERT"]),
|
||||
),
|
||||
validate_certs=dict(
|
||||
type="bool",
|
||||
default=True,
|
||||
aliases=["verify_ssl"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_VERIFY_SSL"]),
|
||||
),
|
||||
api_key=dict(
|
||||
type="str", no_log=True, fallback=(env_fallback, ["K8S_AUTH_API_KEY"])
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
("state", "present", ("plugin_path",)),
|
||||
("state", "absent", ("plugin_name",)),
|
||||
("state", "latest", ("plugin_name",)),
|
||||
],
|
||||
mutually_exclusive=mutually_exclusive(),
|
||||
mutually_exclusive=[
|
||||
("plugin_name", "plugin_path"),
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("kubeconfig", "validate_certs"),
|
||||
],
|
||||
)
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
state = module.params.get("state")
|
||||
|
||||
helm_cmd_common = module.get_helm_binary() + " plugin"
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = "helm"
|
||||
|
||||
helm_cmd_common = module.get_bin_path(helm_cmd_common, required=True)
|
||||
|
||||
helm_cmd_common += " plugin"
|
||||
|
||||
if state == "present":
|
||||
helm_cmd_common += " install %s" % module.params.get("plugin_path")
|
||||
@@ -171,9 +193,7 @@ def main():
|
||||
if plugin_version is not None:
|
||||
helm_cmd_common += " --version=%s" % plugin_version
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_cmd_common, fails_on_error=False
|
||||
)
|
||||
rc, out, err = run_helm(module, helm_cmd_common, fails_on_error=False)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
@@ -207,15 +227,15 @@ 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())
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found or is already uninstalled",
|
||||
command=command,
|
||||
command=helm_cmd_common + " list",
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
@@ -231,7 +251,7 @@ def main():
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found or is already uninstalled",
|
||||
command=command,
|
||||
command=helm_cmd_common + " list",
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
@@ -239,9 +259,7 @@ def main():
|
||||
|
||||
helm_uninstall_cmd = "%s uninstall %s" % (helm_cmd_common, plugin_name)
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_uninstall_cmd, fails_on_error=False
|
||||
)
|
||||
rc, out, err = run_helm(module, helm_uninstall_cmd, fails_on_error=False)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
@@ -263,15 +281,15 @@ 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())
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found",
|
||||
command=command,
|
||||
command=helm_cmd_common + " list",
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
@@ -287,7 +305,7 @@ def main():
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found",
|
||||
command=command,
|
||||
command=helm_cmd_common + " list",
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
@@ -295,9 +313,7 @@ def main():
|
||||
|
||||
helm_update_cmd = "%s update %s" % (helm_cmd_common, plugin_name)
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_update_cmd, fails_on_error=False
|
||||
)
|
||||
rc, out, err = run_helm(module, helm_update_cmd, fails_on_error=False)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
|
||||
@@ -70,41 +70,75 @@ rc:
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
get_helm_plugin_list,
|
||||
parse_helm_plugin_list,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
argument_spec.update(
|
||||
dict(
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
plugin_name=dict(
|
||||
type="str",
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
# Helm options
|
||||
context=dict(
|
||||
type="str",
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
),
|
||||
kubeconfig=dict(
|
||||
type="path",
|
||||
aliases=["kubeconfig_path"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_KUBECONFIG"]),
|
||||
),
|
||||
# Generic auth key
|
||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||
ca_cert=dict(
|
||||
type="path",
|
||||
aliases=["ssl_ca_cert"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_SSL_CA_CERT"]),
|
||||
),
|
||||
validate_certs=dict(
|
||||
type="bool",
|
||||
default=True,
|
||||
aliases=["verify_ssl"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_VERIFY_SSL"]),
|
||||
),
|
||||
api_key=dict(
|
||||
type="str", no_log=True, fallback=(env_fallback, ["K8S_AUTH_API_KEY"])
|
||||
),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("kubeconfig", "validate_certs"),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = "helm"
|
||||
|
||||
helm_cmd_common = module.get_bin_path(helm_cmd_common, required=True)
|
||||
|
||||
helm_cmd_common += " plugin"
|
||||
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
|
||||
plugin_list = []
|
||||
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
|
||||
out = parse_helm_plugin_list(output=output.splitlines())
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
for line in out:
|
||||
if plugin_name is None:
|
||||
@@ -121,7 +155,7 @@ def main():
|
||||
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
command=command,
|
||||
command=helm_cmd_common + " list",
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
|
||||
@@ -1,302 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2022, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
module: helm_pull
|
||||
short_description: download a chart from a repository and (optionally) unpack it in local directory.
|
||||
version_added: "2.4.0"
|
||||
author:
|
||||
- Aubin Bikouo (@abikouo)
|
||||
description:
|
||||
- Retrieve a package from a package repository, and download it locally.
|
||||
- It can also be used to perform cryptographic verification of a chart without installing the chart.
|
||||
- There are options for unpacking the chart after download.
|
||||
|
||||
requirements:
|
||||
- "helm >= 3.0 (https://github.com/helm/helm/releases)"
|
||||
|
||||
options:
|
||||
chart_ref:
|
||||
description:
|
||||
- chart name on chart repository.
|
||||
- absolute URL.
|
||||
required: true
|
||||
type: str
|
||||
chart_version:
|
||||
description:
|
||||
- Specify a version constraint for the chart version to use.
|
||||
- This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0).
|
||||
- Mutually exclusive with C(chart_devel).
|
||||
type: str
|
||||
verify_chart:
|
||||
description:
|
||||
- Verify the package before using it.
|
||||
default: False
|
||||
type: bool
|
||||
verify_chart_keyring:
|
||||
description:
|
||||
- location of public keys used for verification.
|
||||
type: path
|
||||
provenance:
|
||||
description:
|
||||
- Fetch the provenance file, but don't perform verification.
|
||||
type: bool
|
||||
default: False
|
||||
repo_url:
|
||||
description:
|
||||
- chart repository url where to locate the requested chart.
|
||||
type: str
|
||||
aliases: [ url, chart_repo_url ]
|
||||
repo_username:
|
||||
description:
|
||||
- Chart repository username where to locate the requested chart.
|
||||
- Required if C(repo_password) is specified.
|
||||
type: str
|
||||
aliases: [ username, chart_repo_username ]
|
||||
repo_password:
|
||||
description:
|
||||
- Chart repository password where to locate the requested chart.
|
||||
- Required if C(repo_username) is specified.
|
||||
type: str
|
||||
aliases: [ password, chart_repo_password ]
|
||||
pass_credentials:
|
||||
description:
|
||||
- Pass credentials to all domains.
|
||||
default: False
|
||||
type: bool
|
||||
skip_tls_certs_check:
|
||||
description:
|
||||
- Whether or not to check tls certificate for the chart download.
|
||||
- Requires helm >= 3.3.0.
|
||||
type: bool
|
||||
default: False
|
||||
chart_devel:
|
||||
description:
|
||||
- Use development versions, too. Equivalent to version '>0.0.0-0'.
|
||||
- Mutually exclusive with C(chart_version).
|
||||
type: bool
|
||||
untar_chart:
|
||||
description:
|
||||
- if set to true, will untar the chart after downloading it.
|
||||
type: bool
|
||||
default: False
|
||||
destination:
|
||||
description:
|
||||
- location to write the chart.
|
||||
type: path
|
||||
required: True
|
||||
chart_ca_cert:
|
||||
description:
|
||||
- Verify certificates of HTTPS-enabled servers using this CA bundle.
|
||||
- Requires helm >= 3.1.0.
|
||||
type: path
|
||||
chart_ssl_cert_file:
|
||||
description:
|
||||
- Identify HTTPS client using this SSL certificate file.
|
||||
- Requires helm >= 3.1.0.
|
||||
type: path
|
||||
chart_ssl_key_file:
|
||||
description:
|
||||
- Identify HTTPS client using this SSL key file
|
||||
- Requires helm >= 3.1.0.
|
||||
type: path
|
||||
binary_path:
|
||||
description:
|
||||
- The path of a helm binary to use.
|
||||
required: false
|
||||
type: path
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Download chart using chart url
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz
|
||||
destination: /path/to/chart
|
||||
|
||||
- name: Download Chart using chart_name and repo_url
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
untar_chart: yes
|
||||
destination: /path/to/chart
|
||||
|
||||
- name: Download Chart (skip tls certificate check)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
untar_chart: yes
|
||||
destination: /path/to/chart
|
||||
skip_tls_certs_check: yes
|
||||
|
||||
- name: Download Chart using chart registry credentials
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
untar_chart: yes
|
||||
destination: /path/to/chart
|
||||
username: myuser
|
||||
password: mypassword123
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
stdout:
|
||||
type: str
|
||||
description: Full `helm pull` command stdout, in case you want to display it or examine the event log
|
||||
returned: always
|
||||
sample: ''
|
||||
stderr:
|
||||
type: str
|
||||
description: Full `helm pull` command stderr, in case you want to display it or examine the event log
|
||||
returned: always
|
||||
sample: ''
|
||||
command:
|
||||
type: str
|
||||
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 ...
|
||||
rc:
|
||||
type: int
|
||||
description: Helm pull command return code
|
||||
returned: always
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argspec = dict(
|
||||
chart_ref=dict(type="str", required=True),
|
||||
chart_version=dict(type="str"),
|
||||
verify_chart=dict(type="bool", default=False),
|
||||
verify_chart_keyring=dict(type="path"),
|
||||
provenance=dict(type="bool", default=False),
|
||||
repo_url=dict(type="str", aliases=["url", "chart_repo_url"]),
|
||||
repo_username=dict(type="str", aliases=["username", "chart_repo_username"]),
|
||||
repo_password=dict(
|
||||
type="str", no_log=True, aliases=["password", "chart_repo_password"]
|
||||
),
|
||||
pass_credentials=dict(type="bool", default=False),
|
||||
skip_tls_certs_check=dict(type="bool", default=False),
|
||||
chart_devel=dict(type="bool"),
|
||||
untar_chart=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"),
|
||||
)
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argspec,
|
||||
supports_check_mode=True,
|
||||
required_by=dict(
|
||||
repo_username=("repo_password"),
|
||||
repo_password=("repo_username"),
|
||||
),
|
||||
mutually_exclusive=[("chart_version", "chart_devel")],
|
||||
)
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
def test_version_requirement(opt):
|
||||
req_version = helm_pull_opt_versionning.get(opt)
|
||||
if req_version and LooseVersion(helm_version) < LooseVersion(req_version):
|
||||
module.fail_json(
|
||||
msg="Parameter {0} requires helm >= {1}, current version is {2}".format(
|
||||
opt, req_version, helm_version
|
||||
)
|
||||
)
|
||||
|
||||
# Set `helm pull` arguments requiring values
|
||||
helm_pull_opts = []
|
||||
|
||||
helm_value_args = dict(
|
||||
chart_version="version",
|
||||
verify_chart_keyring="keyring",
|
||||
repo_url="repo",
|
||||
repo_username="username",
|
||||
repo_password="password",
|
||||
destination="destination",
|
||||
chart_ca_cert="ca-file",
|
||||
chart_ssl_cert_file="cert-file",
|
||||
chart_ssl_key_file="key-file",
|
||||
)
|
||||
|
||||
for opt, cmdkey in helm_value_args.items():
|
||||
if module.params.get(opt):
|
||||
test_version_requirement(opt)
|
||||
helm_pull_opts.append("--{0} {1}".format(cmdkey, module.params.get(opt)))
|
||||
|
||||
# Set `helm pull` arguments flags
|
||||
helm_flag_args = dict(
|
||||
verify_chart=dict(key="verify"),
|
||||
provenance=dict(key="prov"),
|
||||
pass_credentials=dict(key="pass-credentials"),
|
||||
skip_tls_certs_check=dict(key="insecure-skip-tls-verify"),
|
||||
chart_devel=dict(key="devel"),
|
||||
untar_chart=dict(key="untar"),
|
||||
)
|
||||
|
||||
for k, v in helm_flag_args.items():
|
||||
if module.params.get(k):
|
||||
test_version_requirement(k)
|
||||
helm_pull_opts.append("--{0}".format(v["key"]))
|
||||
|
||||
helm_cmd_common = "{0} pull {1} {2}".format(
|
||||
module.get_helm_binary(),
|
||||
module.params.get("chart_ref"),
|
||||
" ".join(helm_pull_opts),
|
||||
)
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_helm_command(helm_cmd_common, fails_on_error=False)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
if rc == 0:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=True,
|
||||
command=helm_cmd_common,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
)
|
||||
else:
|
||||
module.fail_json(
|
||||
msg="Failure when executing Helm command.",
|
||||
command=helm_cmd_common,
|
||||
changed=False,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -97,28 +97,6 @@ options:
|
||||
type: path
|
||||
aliases: [ ssl_ca_cert ]
|
||||
version_added: "2.3.0"
|
||||
context:
|
||||
description:
|
||||
- Helm option to specify which kubeconfig context to use.
|
||||
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_CONTEXT) will be used instead.
|
||||
type: str
|
||||
aliases: [ kube_context ]
|
||||
version_added: "2.4.0"
|
||||
kubeconfig:
|
||||
description:
|
||||
- Helm option to specify kubeconfig path to use.
|
||||
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_KUBECONFIG) will be used instead.
|
||||
- The configuration can be provided as dictionary.
|
||||
type: raw
|
||||
aliases: [ kubeconfig_path ]
|
||||
version_added: "2.4.0"
|
||||
force_update:
|
||||
description:
|
||||
- Whether or not to replace (overwrite) the repo if it already exists.
|
||||
type: bool
|
||||
aliases: [ force ]
|
||||
default: False
|
||||
version_added: "2.4.0"
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -166,26 +144,18 @@ msg:
|
||||
sample: 'Repository already have a repository named bitnami'
|
||||
"""
|
||||
|
||||
import copy
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
IMP_YAML = True
|
||||
IMP_YAML_ERR = None
|
||||
except ImportError:
|
||||
IMP_YAML_ERR = traceback.format_exc()
|
||||
IMP_YAML = False
|
||||
|
||||
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.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback, missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm
|
||||
|
||||
|
||||
# Get repository from all repositories added
|
||||
@@ -198,10 +168,10 @@ def get_repository(state, repo_name):
|
||||
|
||||
|
||||
# Get repository status
|
||||
def get_repository_status(module, repository_name):
|
||||
list_command = module.get_helm_binary() + " repo list --output=yaml"
|
||||
def get_repository_status(module, command, repository_name):
|
||||
list_command = command + " repo list --output=yaml"
|
||||
|
||||
rc, out, err = module.run_helm_command(list_command, fails_on_error=False)
|
||||
rc, out, err = run_helm(module, list_command, fails_on_error=False)
|
||||
|
||||
# no repo => rc=1 and 'no repositories to show' in output
|
||||
if rc == 1 and "no repositories to show" in err:
|
||||
@@ -225,7 +195,6 @@ def install_repository(
|
||||
repository_username,
|
||||
repository_password,
|
||||
pass_credentials,
|
||||
force_update,
|
||||
):
|
||||
install_command = command + " repo add " + repository_name + " " + repository_url
|
||||
|
||||
@@ -236,9 +205,6 @@ def install_repository(
|
||||
if pass_credentials:
|
||||
install_command += " --pass-credentials"
|
||||
|
||||
if force_update:
|
||||
install_command += " --force-update"
|
||||
|
||||
return install_command
|
||||
|
||||
|
||||
@@ -249,10 +215,12 @@ def delete_repository(command, repository_name):
|
||||
return remove_command
|
||||
|
||||
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
repo_name=dict(type="str", aliases=["name"], required=True),
|
||||
repo_url=dict(type="str", aliases=["url"]),
|
||||
repo_username=dict(type="str", aliases=["username"]),
|
||||
@@ -261,20 +229,25 @@ def argument_spec():
|
||||
default="present", choices=["present", "absent"], aliases=["state"]
|
||||
),
|
||||
pass_credentials=dict(type="bool", default=False, no_log=True),
|
||||
force_update=dict(type="bool", default=False, aliases=["force"]),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec(),
|
||||
# Generic auth key
|
||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||
ca_cert=dict(
|
||||
type="path",
|
||||
aliases=["ssl_ca_cert"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_SSL_CA_CERT"]),
|
||||
),
|
||||
validate_certs=dict(
|
||||
type="bool",
|
||||
default=True,
|
||||
aliases=["verify_ssl"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_VERIFY_SSL"]),
|
||||
),
|
||||
api_key=dict(
|
||||
type="str", no_log=True, fallback=(env_fallback, ["K8S_AUTH_API_KEY"])
|
||||
),
|
||||
),
|
||||
required_together=[["repo_username", "repo_password"]],
|
||||
required_if=[("repo_state", "present", ["repo_url"])],
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@@ -283,23 +256,26 @@ def main():
|
||||
|
||||
changed = False
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
repo_name = module.params.get("repo_name")
|
||||
repo_url = module.params.get("repo_url")
|
||||
repo_username = module.params.get("repo_username")
|
||||
repo_password = module.params.get("repo_password")
|
||||
repo_state = module.params.get("repo_state")
|
||||
pass_credentials = module.params.get("pass_credentials")
|
||||
force_update = module.params.get("force_update")
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
if bin_path is not None:
|
||||
helm_cmd = bin_path
|
||||
else:
|
||||
helm_cmd = module.get_bin_path("helm", required=True)
|
||||
|
||||
repository_status = get_repository_status(module, repo_name)
|
||||
repository_status = get_repository_status(module, helm_cmd, repo_name)
|
||||
|
||||
if repo_state == "absent" and repository_status is not None:
|
||||
helm_cmd = delete_repository(helm_cmd, repo_name)
|
||||
changed = True
|
||||
elif repo_state == "present":
|
||||
if repository_status is None or force_update:
|
||||
if repository_status is None:
|
||||
helm_cmd = install_repository(
|
||||
helm_cmd,
|
||||
repo_name,
|
||||
@@ -307,7 +283,6 @@ def main():
|
||||
repo_username,
|
||||
repo_password,
|
||||
pass_credentials,
|
||||
force_update,
|
||||
)
|
||||
changed = True
|
||||
elif repository_status["url"] != repo_url:
|
||||
@@ -320,7 +295,7 @@ def main():
|
||||
elif not changed:
|
||||
module.exit_json(changed=False, repo_name=repo_name, repo_url=repo_url)
|
||||
|
||||
rc, out, err = module.run_helm_command(helm_cmd)
|
||||
rc, out, err = run_helm(module, helm_cmd)
|
||||
|
||||
if repo_password is not None:
|
||||
helm_cmd = helm_cmd.replace(repo_password, "******")
|
||||
|
||||
@@ -45,21 +45,6 @@ options:
|
||||
- Chart version to use. If this is not specified, the latest version is installed.
|
||||
required: false
|
||||
type: str
|
||||
dependency_update:
|
||||
description:
|
||||
- Run helm dependency update before the operation.
|
||||
- The I(dependency_update) option require the add of C(dependencies) block in C(Chart.yaml/requirements.yaml) file.
|
||||
- For more information please visit U(https://helm.sh/docs/helm/helm_dependency/)
|
||||
default: false
|
||||
type: bool
|
||||
aliases: [ dep_up ]
|
||||
version_added: "2.4.0"
|
||||
disable_hook:
|
||||
description:
|
||||
- Prevent hooks from running during install.
|
||||
default: False
|
||||
type: bool
|
||||
version_added: 2.4.0
|
||||
include_crds:
|
||||
description:
|
||||
- Include custom resource descriptions in rendered templates.
|
||||
@@ -72,19 +57,12 @@ options:
|
||||
- If the directory already exists, it will be overwritten.
|
||||
required: false
|
||||
type: path
|
||||
release_name:
|
||||
description:
|
||||
- Release name to use in rendered templates.
|
||||
required: false
|
||||
aliases: [ name ]
|
||||
type: str
|
||||
version_added: 2.4.0
|
||||
release_namespace:
|
||||
description:
|
||||
- namespace scope for this request.
|
||||
required: false
|
||||
type: str
|
||||
version_added: 2.4.0
|
||||
version_added: 2.3.0
|
||||
release_values:
|
||||
description:
|
||||
- Values to pass to chart.
|
||||
@@ -98,8 +76,7 @@ options:
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
version_added: 2.4.0
|
||||
version_added: 2.3.0
|
||||
values_files:
|
||||
description:
|
||||
- Value files to pass to chart.
|
||||
@@ -115,31 +92,6 @@ options:
|
||||
- Run C(helm repo update) before the operation. Can be run as part of the template generation or as a separate step.
|
||||
default: false
|
||||
type: bool
|
||||
set_values:
|
||||
description:
|
||||
- Values to pass to chart configuration.
|
||||
required: false
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
value:
|
||||
description:
|
||||
- Value to pass to chart configuration (e.g phase=prod).
|
||||
type: str
|
||||
required: true
|
||||
value_type:
|
||||
description:
|
||||
- Use C(raw) set individual value.
|
||||
- Use C(string) to force a string for an individual value.
|
||||
- Use C(file) to set individual values from a file when the value itself is too long for the command line or is dynamically generated.
|
||||
- Use C(json) to set json values (scalars/objects/arrays). This feature requires helm>=3.10.0.
|
||||
default: raw
|
||||
choices:
|
||||
- raw
|
||||
- string
|
||||
- json
|
||||
- file
|
||||
version_added: '2.4.0'
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -202,15 +154,12 @@ try:
|
||||
import yaml
|
||||
|
||||
IMP_YAML = True
|
||||
IMP_YAML_ERR = None
|
||||
except ImportError:
|
||||
IMP_YAML_ERR = traceback.format_exc()
|
||||
IMP_YAML = False
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm
|
||||
|
||||
|
||||
def template(
|
||||
@@ -218,26 +167,14 @@ def template(
|
||||
chart_ref,
|
||||
chart_repo_url=None,
|
||||
chart_version=None,
|
||||
dependency_update=None,
|
||||
disable_hook=None,
|
||||
output_dir=None,
|
||||
show_only=None,
|
||||
release_name=None,
|
||||
release_namespace=None,
|
||||
release_values=None,
|
||||
release_namespace=None,
|
||||
values_files=None,
|
||||
include_crds=False,
|
||||
set_values=None,
|
||||
):
|
||||
cmd += " template "
|
||||
|
||||
if release_name:
|
||||
cmd += release_name + " "
|
||||
|
||||
cmd += chart_ref
|
||||
|
||||
if dependency_update:
|
||||
cmd += " --dependency-update"
|
||||
cmd += " template " + chart_ref
|
||||
|
||||
if chart_repo_url:
|
||||
cmd += " --repo=" + chart_repo_url
|
||||
@@ -245,9 +182,6 @@ def template(
|
||||
if chart_version:
|
||||
cmd += " --version=" + chart_version
|
||||
|
||||
if disable_hook:
|
||||
cmd += " --no-hooks"
|
||||
|
||||
if output_dir:
|
||||
cmd += " --output-dir=" + output_dir
|
||||
|
||||
@@ -271,82 +205,64 @@ def template(
|
||||
if include_crds:
|
||||
cmd += " --include-crds"
|
||||
|
||||
if set_values:
|
||||
cmd += " " + set_values
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleHelmModule(
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
chart_ref=dict(type="path", required=True),
|
||||
chart_repo_url=dict(type="str"),
|
||||
chart_version=dict(type="str"),
|
||||
dependency_update=dict(type="bool", default=False, aliases=["dep_up"]),
|
||||
disable_hook=dict(type="bool", default=False),
|
||||
include_crds=dict(type="bool", default=False),
|
||||
release_name=dict(type="str", aliases=["name"]),
|
||||
output_dir=dict(type="path"),
|
||||
release_namespace=dict(type="str"),
|
||||
release_values=dict(type="dict", default={}, aliases=["values"]),
|
||||
show_only=dict(type="list", default=[], elements="str"),
|
||||
values_files=dict(type="list", default=[], elements="str"),
|
||||
update_repo_cache=dict(type="bool", default=False),
|
||||
set_values=dict(type="list", elements="dict"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_mode = module.check_mode
|
||||
bin_path = module.params.get("binary_path")
|
||||
chart_ref = module.params.get("chart_ref")
|
||||
chart_repo_url = module.params.get("chart_repo_url")
|
||||
chart_version = module.params.get("chart_version")
|
||||
dependency_update = module.params.get("dependency_update")
|
||||
disable_hook = module.params.get("disable_hook")
|
||||
include_crds = module.params.get("include_crds")
|
||||
release_name = module.params.get("release_name")
|
||||
output_dir = module.params.get("output_dir")
|
||||
show_only = module.params.get("show_only")
|
||||
release_namespace = module.params.get("release_namespace")
|
||||
release_values = module.params.get("release_values")
|
||||
values_files = module.params.get("values_files")
|
||||
update_repo_cache = module.params.get("update_repo_cache")
|
||||
set_values = module.params.get("set_values")
|
||||
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
helm_cmd = bin_path or module.get_bin_path("helm", required=True)
|
||||
|
||||
if update_repo_cache:
|
||||
update_cmd = helm_cmd + " repo update"
|
||||
module.run_helm_command(update_cmd)
|
||||
|
||||
set_values_args = None
|
||||
if set_values:
|
||||
set_values_args = module.get_helm_set_values_args(set_values)
|
||||
run_helm(module, update_cmd)
|
||||
|
||||
tmpl_cmd = template(
|
||||
helm_cmd,
|
||||
chart_ref,
|
||||
dependency_update=dependency_update,
|
||||
chart_repo_url=chart_repo_url,
|
||||
chart_version=chart_version,
|
||||
disable_hook=disable_hook,
|
||||
release_name=release_name,
|
||||
output_dir=output_dir,
|
||||
release_namespace=release_namespace,
|
||||
release_values=release_values,
|
||||
show_only=show_only,
|
||||
values_files=values_files,
|
||||
include_crds=include_crds,
|
||||
set_values=set_values_args,
|
||||
)
|
||||
|
||||
if not check_mode:
|
||||
rc, out, err = module.run_helm_command(tmpl_cmd)
|
||||
rc, out, err = run_helm(module, tmpl_cmd)
|
||||
else:
|
||||
out = err = ""
|
||||
rc = 0
|
||||
|
||||
@@ -172,31 +172,10 @@ options:
|
||||
- When set to True, server-side apply will force the changes against conflicts.
|
||||
type: bool
|
||||
default: False
|
||||
delete_all:
|
||||
description:
|
||||
- When this option is set to I(true) and I(state=absent),
|
||||
module will delete all resources of the specified resource type in the requested namespace.
|
||||
- Ignored when C(state) is not set to I(absent) or when one of (src),
|
||||
C(name) or C(resource_definition) is provided.
|
||||
- Parameter C(kind) is required to use this option.
|
||||
- This parameter can be used with C(label_selectors) to restrict the resources to be deleted.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 2.5.0
|
||||
aliases:
|
||||
- all
|
||||
hidden_fields:
|
||||
description:
|
||||
- Hide fields matching this option in the result
|
||||
- An example might be C(hidden_fields=[metadata.managedFields])
|
||||
- Only field definitions that don't reference list items are supported (so V(spec.containers[0]) would not work)
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 2.5.0
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
- "jsonpatch"
|
||||
"""
|
||||
@@ -253,14 +232,6 @@ EXAMPLES = r"""
|
||||
state: present
|
||||
definition: "{{ lookup('file', '/testing/deployment.yml') | from_yaml }}"
|
||||
|
||||
- name: >-
|
||||
(Alternative) Read definition file from the Ansible controller file system.
|
||||
In this case, the definition file contains multiple YAML documents, separated by ---.
|
||||
If the definition file has been encrypted with Ansible Vault it will automatically be decrypted.
|
||||
kubernetes.core.k8s:
|
||||
state: present
|
||||
definition: "{{ lookup('file', '/testing/deployment.yml') | from_yaml_all }}"
|
||||
|
||||
- name: Read definition template file from the Ansible controller file system
|
||||
kubernetes.core.k8s:
|
||||
state: present
|
||||
@@ -278,10 +249,10 @@ EXAMPLES = r"""
|
||||
kubernetes.core.k8s:
|
||||
state: present
|
||||
template:
|
||||
- path: '/testing/deployment_one.j2'
|
||||
- path: '/testing/deployment_two.j2'
|
||||
variable_start_string: '[['
|
||||
variable_end_string: ']]'
|
||||
- path: '/testing/deployment_one.j2'
|
||||
- path: '/testing/deployment_two.j2'
|
||||
variable_start_string: '[['
|
||||
variable_end_string: ']]'
|
||||
|
||||
- name: fail on validation errors
|
||||
kubernetes.core.k8s:
|
||||
@@ -364,14 +335,6 @@ EXAMPLES = r"""
|
||||
apply: yes
|
||||
server_side_apply:
|
||||
field_manager: ansible
|
||||
|
||||
# Delete all Deployment from specified namespace
|
||||
- name: Delete all Deployment from specified namespace
|
||||
kubernetes.core.k8s:
|
||||
api_version: apps/v1
|
||||
namespace: testing
|
||||
kind: Deployment
|
||||
delete_all: true
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
@@ -423,19 +386,10 @@ from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule impo
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
DELETE_OPTS_ARG_SPEC,
|
||||
WAIT_ARG_SPEC,
|
||||
NAME_ARG_SPEC,
|
||||
RESOURCE_ARG_SPEC,
|
||||
WAIT_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import (
|
||||
run_module,
|
||||
DELETE_OPTS_ARG_SPEC,
|
||||
)
|
||||
|
||||
|
||||
@@ -479,12 +433,32 @@ def argspec():
|
||||
argument_spec["server_side_apply"] = dict(
|
||||
type="dict", default=None, options=server_apply_spec()
|
||||
)
|
||||
argument_spec["delete_all"] = dict(type="bool", default=False, aliases=["all"])
|
||||
argument_spec["hidden_fields"] = dict(type="list", elements="str")
|
||||
|
||||
return argument_spec
|
||||
|
||||
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
k8s_ansible_mixin.module = module
|
||||
k8s_ansible_mixin.argspec = module.argument_spec
|
||||
k8s_ansible_mixin.check_mode = k8s_ansible_mixin.module.check_mode
|
||||
k8s_ansible_mixin.params = k8s_ansible_mixin.module.params
|
||||
k8s_ansible_mixin.fail_json = k8s_ansible_mixin.module.fail_json
|
||||
k8s_ansible_mixin.fail = k8s_ansible_mixin.module.fail_json
|
||||
k8s_ansible_mixin.exit_json = k8s_ansible_mixin.module.exit_json
|
||||
k8s_ansible_mixin.warn = k8s_ansible_mixin.module.warn
|
||||
k8s_ansible_mixin.warnings = []
|
||||
|
||||
k8s_ansible_mixin.kind = k8s_ansible_mixin.params.get("kind")
|
||||
k8s_ansible_mixin.api_version = k8s_ansible_mixin.params.get("api_version")
|
||||
k8s_ansible_mixin.name = k8s_ansible_mixin.params.get("name")
|
||||
k8s_ansible_mixin.generate_name = k8s_ansible_mixin.params.get("generate_name")
|
||||
k8s_ansible_mixin.namespace = k8s_ansible_mixin.params.get("namespace")
|
||||
|
||||
k8s_ansible_mixin.check_library_version()
|
||||
k8s_ansible_mixin.set_resource_definitions(module)
|
||||
k8s_ansible_mixin.execute_module()
|
||||
|
||||
|
||||
def main():
|
||||
mutually_exclusive = [
|
||||
("resource_definition", "src"),
|
||||
@@ -493,17 +467,19 @@ def main():
|
||||
("template", "src"),
|
||||
("name", "generate_name"),
|
||||
]
|
||||
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
module = AnsibleModule(
|
||||
argument_spec=argspec(),
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
try:
|
||||
run_module(module)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -34,8 +34,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -141,32 +141,36 @@ apis:
|
||||
|
||||
|
||||
import copy
|
||||
import traceback
|
||||
from collections import defaultdict
|
||||
|
||||
HAS_K8S = False
|
||||
try:
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.client.resource import (
|
||||
ResourceList,
|
||||
)
|
||||
except ImportError:
|
||||
# Handled during module setup
|
||||
pass
|
||||
|
||||
HAS_K8S = True
|
||||
except ImportError as e:
|
||||
K8S_IMP_ERR = e
|
||||
K8S_IMP_EXC = traceback.format_exc()
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
|
||||
|
||||
def execute_module(module, client):
|
||||
if module.params.get("invalidate_cache"):
|
||||
invalidate_cache = boolean(
|
||||
module.params.get("invalidate_cache", True), strict=False
|
||||
)
|
||||
if invalidate_cache:
|
||||
client.resources.invalidate_cache()
|
||||
results = defaultdict(dict)
|
||||
for resource in list(client.resources):
|
||||
@@ -200,7 +204,7 @@ def execute_module(module, client):
|
||||
|
||||
version_info = {
|
||||
"client": version,
|
||||
"server": client.client.version,
|
||||
"server": client.version,
|
||||
}
|
||||
module.exit_json(
|
||||
changed=False, apis=results, connection=connection, version=version_info
|
||||
@@ -214,18 +218,18 @@ def argspec():
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
)
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
if not HAS_K8S:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib("kubernetes"),
|
||||
exception=K8S_IMP_EXC,
|
||||
error=to_native(K8S_IMP_ERR),
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
try:
|
||||
execute_module(module, client=get_api_client(module=module))
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
execute_module(module, client=get_api_client(module=module))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -26,8 +26,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
|
||||
options:
|
||||
namespace:
|
||||
@@ -119,7 +119,7 @@ EXAMPLES = r"""
|
||||
state: from_pod
|
||||
|
||||
# copy content into a file in the remote pod
|
||||
- name: Copy content into a file in the remote pod
|
||||
- name: Copy /tmp/foo from a remote pod to /tmp/bar locally
|
||||
kubernetes.core.k8s_cp:
|
||||
state: to_pod
|
||||
namespace: some-namespace
|
||||
@@ -139,7 +139,6 @@ result:
|
||||
|
||||
import copy
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
@@ -151,18 +150,7 @@ from ansible_collections.kubernetes.core.plugins.module_utils.copy import (
|
||||
K8SCopyToPod,
|
||||
check_pod,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
)
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
def argspec():
|
||||
@@ -183,9 +171,23 @@ def argspec():
|
||||
|
||||
|
||||
def execute_module(module):
|
||||
client = get_api_client(module=module)
|
||||
svc = K8sService(client, module)
|
||||
containers = check_pod(svc)
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module, pyyaml_required=False)
|
||||
k8s_ansible_mixin.check_library_version()
|
||||
|
||||
k8s_ansible_mixin.module = module
|
||||
k8s_ansible_mixin.argspec = module.argument_spec
|
||||
k8s_ansible_mixin.params = k8s_ansible_mixin.module.params
|
||||
k8s_ansible_mixin.fail_json = k8s_ansible_mixin.module.fail_json
|
||||
k8s_ansible_mixin.fail = k8s_ansible_mixin.module.fail_json
|
||||
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
containers = check_pod(k8s_ansible_mixin, module)
|
||||
if len(containers) > 1 and module.params.get("container") is None:
|
||||
module.fail_json(
|
||||
msg="Pod contains more than 1 container, option 'container' should be set"
|
||||
@@ -193,9 +195,9 @@ def execute_module(module):
|
||||
|
||||
state = module.params.get("state")
|
||||
if state == "to_pod":
|
||||
k8s_copy = K8SCopyToPod(module, client.client)
|
||||
k8s_copy = K8SCopyToPod(module, k8s_ansible_mixin.client)
|
||||
else:
|
||||
k8s_copy = K8SCopyFromPod(module, client.client)
|
||||
k8s_copy = K8SCopyFromPod(module, k8s_ansible_mixin.client)
|
||||
|
||||
try:
|
||||
k8s_copy.run()
|
||||
@@ -204,20 +206,15 @@ def execute_module(module):
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
module = AnsibleModule(
|
||||
argument_spec=argspec(),
|
||||
check_pyyaml=False,
|
||||
mutually_exclusive=[("local_path", "content")],
|
||||
required_if=[("state", "from_pod", ["local_path"])],
|
||||
required_one_of=[["local_path", "content"]],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
try:
|
||||
execute_module(module)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
execute_module(module)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -41,18 +41,8 @@ options:
|
||||
- The name of the node.
|
||||
required: true
|
||||
type: str
|
||||
pod_selectors:
|
||||
description:
|
||||
- Label selector to filter pods on the node.
|
||||
- This option has effect only when C(state) is set to I(drain).
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 2.5.0
|
||||
aliases:
|
||||
- label_selectors
|
||||
delete_options:
|
||||
type: dict
|
||||
default: {}
|
||||
description:
|
||||
- Specify options to delete pods.
|
||||
- This option has effect only when C(state) is set to I(drain).
|
||||
@@ -97,8 +87,8 @@ options:
|
||||
type: int
|
||||
|
||||
requirements:
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -113,7 +103,7 @@ EXAMPLES = r"""
|
||||
state: drain
|
||||
name: foo
|
||||
delete_options:
|
||||
terminate_grace_period: 900
|
||||
terminate_grace_period: 900
|
||||
|
||||
- name: Mark node "foo" as schedulable.
|
||||
kubernetes.core.k8s_drain:
|
||||
@@ -125,13 +115,6 @@ EXAMPLES = r"""
|
||||
state: cordon
|
||||
name: foo
|
||||
|
||||
- name: Drain node "foo" using label selector to filter the list of pods to be drained.
|
||||
kubernetes.core.k8s_drain:
|
||||
state: drain
|
||||
name: foo
|
||||
pod_selectors:
|
||||
- 'app!=csi-attacher'
|
||||
- 'app!=csi-provisioner'
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
@@ -145,29 +128,20 @@ result:
|
||||
import copy
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from datetime import datetime
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
from kubernetes.client.api import core_v1_api
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
from kubernetes.client.models import V1DeleteOptions, V1ObjectMeta
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
@@ -270,9 +244,30 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
|
||||
|
||||
class K8sDrainAnsible(object):
|
||||
def __init__(self, module, client):
|
||||
def __init__(self, module):
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
self._module = module
|
||||
self._api_instance = core_v1_api.CoreV1Api(client.client)
|
||||
self._k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
self._k8s_ansible_mixin.client = get_api_client(module=self._module)
|
||||
|
||||
self._k8s_ansible_mixin.module = self._module
|
||||
self._k8s_ansible_mixin.argspec = self._module.argument_spec
|
||||
self._k8s_ansible_mixin.check_mode = self._module.check_mode
|
||||
self._k8s_ansible_mixin.params = self._module.params
|
||||
self._k8s_ansible_mixin.fail_json = self._module.fail_json
|
||||
self._k8s_ansible_mixin.fail = self._module.fail_json
|
||||
self._k8s_ansible_mixin.exit_json = self._module.exit_json
|
||||
self._k8s_ansible_mixin.warn = self._module.warn
|
||||
self._k8s_ansible_mixin.warnings = []
|
||||
|
||||
self._api_instance = core_v1_api.CoreV1Api(
|
||||
self._k8s_ansible_mixin.client.client
|
||||
)
|
||||
self._k8s_ansible_mixin.check_library_version()
|
||||
|
||||
# delete options
|
||||
self._drain_options = module.params.get("delete_options", {})
|
||||
@@ -344,17 +339,6 @@ class K8sDrainAnsible(object):
|
||||
)
|
||||
)
|
||||
|
||||
def list_pods(self):
|
||||
params = {
|
||||
"field_selector": "spec.nodeName={name}".format(
|
||||
name=self._module.params.get("name")
|
||||
)
|
||||
}
|
||||
pod_selectors = self._module.params.get("pod_selectors")
|
||||
if pod_selectors:
|
||||
params["label_selector"] = ",".join(pod_selectors)
|
||||
return self._api_instance.list_pod_for_all_namespaces(**params)
|
||||
|
||||
def delete_or_evict_pods(self, node_unschedulable):
|
||||
# Mark node as unschedulable
|
||||
result = []
|
||||
@@ -377,7 +361,12 @@ class K8sDrainAnsible(object):
|
||||
self.patch_node(unschedulable=False)
|
||||
|
||||
try:
|
||||
pod_list = self.list_pods()
|
||||
field_selector = "spec.nodeName={name}".format(
|
||||
name=self._module.params.get("name")
|
||||
)
|
||||
pod_list = self._api_instance.list_pod_for_all_namespaces(
|
||||
field_selector=field_selector
|
||||
)
|
||||
# Filter pods
|
||||
force = self._drain_options.get("force", False)
|
||||
ignore_daemonset = self._drain_options.get("ignore_daemonsets", False)
|
||||
@@ -428,6 +417,7 @@ class K8sDrainAnsible(object):
|
||||
return dict(result=" ".join(result))
|
||||
|
||||
def patch_node(self, unschedulable):
|
||||
|
||||
body = {"spec": {"unschedulable": unschedulable}}
|
||||
try:
|
||||
self._api_instance.patch_node(
|
||||
@@ -439,6 +429,7 @@ class K8sDrainAnsible(object):
|
||||
)
|
||||
|
||||
def execute_module(self):
|
||||
|
||||
state = self._module.params.get("state")
|
||||
name = self._module.params.get("name")
|
||||
try:
|
||||
@@ -506,18 +497,13 @@ def argspec():
|
||||
wait_sleep=dict(type="int", default=5),
|
||||
),
|
||||
),
|
||||
pod_selectors=dict(
|
||||
type="list",
|
||||
elements="str",
|
||||
aliases=["label_selectors"],
|
||||
),
|
||||
)
|
||||
)
|
||||
return argument_spec
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(module_class=AnsibleModule, argument_spec=argspec())
|
||||
module = AnsibleModule(argument_spec=argspec())
|
||||
|
||||
if not HAS_EVICTION_API:
|
||||
module.fail_json(
|
||||
@@ -526,12 +512,8 @@ def main():
|
||||
error=to_native(k8s_import_exception),
|
||||
)
|
||||
|
||||
try:
|
||||
client = get_api_client(module=module)
|
||||
k8s_drain = K8sDrainAnsible(module, client.client)
|
||||
k8s_drain.execute_module()
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
k8s_drain = K8sDrainAnsible(module)
|
||||
k8s_drain.execute_module()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -26,8 +26,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
|
||||
notes:
|
||||
@@ -131,27 +131,18 @@ except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
AUTH_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.client.apis import core_v1_api
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
from kubernetes.stream import stream
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
@@ -166,9 +157,10 @@ def argspec():
|
||||
return spec
|
||||
|
||||
|
||||
def execute_module(module, client):
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
|
||||
# Load kubernetes.client.Configuration
|
||||
api = core_v1_api.CoreV1Api(client.client)
|
||||
api = core_v1_api.CoreV1Api(k8s_ansible_mixin.client.client)
|
||||
|
||||
# hack because passing the container as None breaks things
|
||||
optional_kwargs = {}
|
||||
@@ -220,8 +212,7 @@ def execute_module(module, client):
|
||||
rc = int(err["details"]["causes"][0]["message"])
|
||||
|
||||
module.deprecate(
|
||||
"The 'return_code' return key is being renamed to 'rc'. "
|
||||
"Both keys are being returned for now to allow users to migrate their automation.",
|
||||
"The 'return_code' return key is deprecated. Please use 'rc' instead.",
|
||||
version="4.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
@@ -236,18 +227,18 @@ def execute_module(module, client):
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
check_pyyaml=False,
|
||||
module = AnsibleModule(
|
||||
argument_spec=argspec(),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
try:
|
||||
client = get_api_client(module)
|
||||
execute_module(module, client.client)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -38,20 +38,10 @@ options:
|
||||
description: List of label selectors to use to filter results
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
field_selectors:
|
||||
description: List of field selectors to use to filter results
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
hidden_fields:
|
||||
description:
|
||||
- Hide fields matching any of the field definitions in the result
|
||||
- An example might be C(hidden_fields=[metadata.managedFields])
|
||||
- Only field definitions that don't reference list items are supported (so V(spec.containers[0]) would not work)
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 2.5.0
|
||||
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
@@ -59,8 +49,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_wait_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -165,22 +155,10 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
|
||||
AUTH_ARG_SPEC,
|
||||
WAIT_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
)
|
||||
|
||||
|
||||
def execute_module(module, svc):
|
||||
facts = svc.find(
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
facts = k8s_ansible_mixin.kubernetes_facts(
|
||||
module.params["kind"],
|
||||
module.params["api_version"],
|
||||
name=module.params["name"],
|
||||
@@ -191,7 +169,6 @@ def execute_module(module, svc):
|
||||
wait_sleep=module.params["wait_sleep"],
|
||||
wait_timeout=module.params["wait_timeout"],
|
||||
condition=module.params["wait_condition"],
|
||||
hidden_fields=module.params["hidden_fields"],
|
||||
)
|
||||
module.exit_json(changed=False, **facts)
|
||||
|
||||
@@ -207,22 +184,25 @@ def argspec():
|
||||
namespace=dict(),
|
||||
label_selectors=dict(type="list", elements="str", default=[]),
|
||||
field_selectors=dict(type="list", elements="str", default=[]),
|
||||
hidden_fields=dict(type="list", elements="str"),
|
||||
)
|
||||
)
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
try:
|
||||
client = get_api_client(module)
|
||||
svc = K8sService(client, module)
|
||||
execute_module(module, svc)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
k8s_ansible_mixin.fail_json = module.fail_json
|
||||
k8s_ansible_mixin.fail = module.fail_json
|
||||
k8s_ansible_mixin.exit_json = module.exit_json
|
||||
k8s_ansible_mixin.warn = module.warn
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -62,8 +62,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_wait_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
- "jsonpatch"
|
||||
"""
|
||||
@@ -79,7 +79,7 @@ EXAMPLES = r"""
|
||||
path: /metadata/labels/app
|
||||
value: myapp
|
||||
- op: replace
|
||||
path: /spec/containers/0/image
|
||||
patch: /spec/containers/0/image
|
||||
value: nginx
|
||||
"""
|
||||
|
||||
@@ -127,8 +127,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._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
@@ -136,20 +136,9 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
|
||||
AUTH_ARG_SPEC,
|
||||
WAIT_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
diff_objects,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
|
||||
get_waiter,
|
||||
K8sAnsibleMixin,
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -196,7 +185,7 @@ def json_patch(existing, patch):
|
||||
return None, error
|
||||
|
||||
|
||||
def execute_module(module, client):
|
||||
def execute_module(k8s_module, module):
|
||||
kind = module.params.get("kind")
|
||||
api_version = module.params.get("api_version")
|
||||
name = module.params.get("name")
|
||||
@@ -211,14 +200,19 @@ def execute_module(module, client):
|
||||
"type"
|
||||
):
|
||||
wait_condition = module.params["wait_condition"]
|
||||
# definition is needed for wait
|
||||
definition = {
|
||||
"kind": kind,
|
||||
"metadata": {"name": name, "namespace": namespace},
|
||||
}
|
||||
|
||||
def build_error_msg(kind, name, msg):
|
||||
return "%s %s: %s" % (kind, name, msg)
|
||||
|
||||
resource = client.resource(kind, api_version)
|
||||
resource = k8s_module.find_resource(kind, api_version, fail=True)
|
||||
|
||||
try:
|
||||
existing = client.get(resource, name=name, namespace=namespace)
|
||||
existing = resource.get(name=name, namespace=namespace)
|
||||
except DynamicApiError as exc:
|
||||
msg = "Failed to retrieve requested object: {0}".format(exc.body)
|
||||
module.fail_json(
|
||||
@@ -233,7 +227,7 @@ def execute_module(module, client):
|
||||
msg=build_error_msg(kind, name, msg), error="", status="", reason=""
|
||||
)
|
||||
|
||||
if module.check_mode and not client.dry_run:
|
||||
if module.check_mode and not k8s_module.supports_dry_run:
|
||||
obj, error = json_patch(existing.to_dict(), patch)
|
||||
if error:
|
||||
module.fail_json(**error)
|
||||
@@ -242,8 +236,7 @@ def execute_module(module, client):
|
||||
if module.check_mode:
|
||||
params["dry_run"] = "All"
|
||||
try:
|
||||
obj = client.patch(
|
||||
resource,
|
||||
obj = resource.patch(
|
||||
patch,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
@@ -262,11 +255,10 @@ def execute_module(module, client):
|
||||
success = True
|
||||
result = {"result": obj}
|
||||
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
|
||||
success, result["result"], result["duration"] = k8s_module.wait(
|
||||
resource, definition, wait_sleep, wait_timeout, condition=wait_condition
|
||||
)
|
||||
match, diffs = diff_objects(existing.to_dict(), obj)
|
||||
match, diffs = k8s_module.diff_objects(existing.to_dict(), obj)
|
||||
result["changed"] = not match
|
||||
if module._diff:
|
||||
result["diff"] = diffs
|
||||
@@ -282,14 +274,13 @@ def main():
|
||||
args = copy.deepcopy(AUTH_ARG_SPEC)
|
||||
args.update(copy.deepcopy(WAIT_ARG_SPEC))
|
||||
args.update(JSON_PATCH_ARGS)
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=args, supports_check_mode=True
|
||||
)
|
||||
try:
|
||||
client = get_api_client(module)
|
||||
execute_module(module, client)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
module = AnsibleModule(argument_spec=args, supports_check_mode=True)
|
||||
k8s_module = K8sAnsibleMixin(module)
|
||||
k8s_module.params = module.params
|
||||
k8s_module.check_library_version()
|
||||
client = get_api_client(module)
|
||||
k8s_module.client = client
|
||||
execute_module(k8s_module, module)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -47,13 +47,11 @@ options:
|
||||
- Only one of I(name) or I(label_selectors) may be provided.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
container:
|
||||
description:
|
||||
- Use to specify the container within a pod to grab the log from.
|
||||
- If there is only one container, this will default to that container.
|
||||
- If there is more than one container, this option is required or set I(all_containers) to C(true).
|
||||
- mutually exclusive with C(all_containers).
|
||||
- If there is more than one container, this option is required.
|
||||
required: no
|
||||
type: str
|
||||
since_seconds:
|
||||
@@ -62,29 +60,10 @@ options:
|
||||
required: no
|
||||
type: str
|
||||
version_added: '2.2.0'
|
||||
previous:
|
||||
description:
|
||||
- If C(true), print the logs for the previous instance of the container in a pod if it exists.
|
||||
required: no
|
||||
type: bool
|
||||
default: False
|
||||
version_added: '2.4.0'
|
||||
tail_lines:
|
||||
description:
|
||||
- A number of lines from the end of the logs to retrieve.
|
||||
required: no
|
||||
type: int
|
||||
version_added: '2.4.0'
|
||||
all_containers:
|
||||
description:
|
||||
- If set to C(true), retrieve all containers' logs in the pod(s).
|
||||
- mutually exclusive with C(container).
|
||||
type: bool
|
||||
version_added: '2.4.0'
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -120,15 +99,7 @@ EXAMPLES = r"""
|
||||
kind: DeploymentConfig
|
||||
namespace: testing
|
||||
name: example
|
||||
tail_lines: 100
|
||||
register: log
|
||||
|
||||
# This will get the logs from all containers in Pod
|
||||
- name: Get the logs from all containers in pod
|
||||
kubernetes.core.k8s_log:
|
||||
namespace: testing
|
||||
name: some-pod
|
||||
all_containers: true
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
@@ -146,33 +117,16 @@ log_lines:
|
||||
|
||||
|
||||
import copy
|
||||
import json
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
from ansible.module_utils.six import PY2
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
NAME_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
|
||||
|
||||
def argspec():
|
||||
@@ -184,61 +138,37 @@ def argspec():
|
||||
container=dict(),
|
||||
since_seconds=dict(),
|
||||
label_selectors=dict(type="list", elements="str", default=[]),
|
||||
previous=dict(type="bool", default=False),
|
||||
tail_lines=dict(type="int"),
|
||||
all_containers=dict(type="bool"),
|
||||
)
|
||||
)
|
||||
return args
|
||||
|
||||
|
||||
def get_exception_message(exc):
|
||||
try:
|
||||
d = json.loads(exc.body.decode("utf8"))
|
||||
return d["message"]
|
||||
except Exception:
|
||||
return exc
|
||||
|
||||
|
||||
def list_containers_in_pod(svc, resource, namespace, name):
|
||||
try:
|
||||
result = svc.client.get(resource, name=name, namespace=namespace)
|
||||
containers = [
|
||||
c["name"] for c in result.to_dict()["status"]["containerStatuses"]
|
||||
]
|
||||
return containers
|
||||
except Exception as exc:
|
||||
raise CoreException(
|
||||
"Unable to retrieve log from Pod due to: {0}".format(
|
||||
get_exception_message(exc)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def execute_module(svc, params):
|
||||
name = params.get("name")
|
||||
namespace = params.get("namespace")
|
||||
label_selector = ",".join(params.get("label_selectors", {}))
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
name = module.params.get("name")
|
||||
namespace = module.params.get("namespace")
|
||||
label_selector = ",".join(module.params.get("label_selectors", {}))
|
||||
if name and label_selector:
|
||||
raise CoreException("Only one of name or label_selectors can be provided")
|
||||
module.fail(msg="Only one of name or label_selectors can be provided")
|
||||
|
||||
resource = svc.find_resource(params["kind"], params["api_version"], fail=True)
|
||||
v1_pods = svc.find_resource("Pod", "v1", fail=True)
|
||||
resource = k8s_ansible_mixin.find_resource(
|
||||
module.params["kind"], module.params["api_version"], fail=True
|
||||
)
|
||||
v1_pods = k8s_ansible_mixin.find_resource("Pod", "v1", fail=True)
|
||||
|
||||
if "log" not in resource.subresources:
|
||||
if not name:
|
||||
raise CoreException(
|
||||
"name must be provided for resources that do not support the log subresource"
|
||||
module.fail(
|
||||
msg="name must be provided for resources that do not support the log subresource"
|
||||
)
|
||||
instance = resource.get(name=name, namespace=namespace)
|
||||
label_selector = ",".join(extract_selectors(instance))
|
||||
label_selector = ",".join(extract_selectors(module, instance))
|
||||
resource = v1_pods
|
||||
|
||||
if label_selector:
|
||||
instances = v1_pods.get(namespace=namespace, label_selector=label_selector)
|
||||
if not instances.items:
|
||||
raise CoreException(
|
||||
"No pods in namespace {0} matched selector {1}".format(
|
||||
module.fail(
|
||||
msg="No pods in namespace {0} matched selector {1}".format(
|
||||
namespace, label_selector
|
||||
)
|
||||
)
|
||||
@@ -246,60 +176,29 @@ def execute_module(svc, params):
|
||||
name = instances.items[0].metadata.name
|
||||
resource = v1_pods
|
||||
|
||||
if "base" not in resource.log.urls and not name:
|
||||
raise CoreException(
|
||||
"name must be provided for resources that do not support namespaced base url"
|
||||
)
|
||||
|
||||
kwargs = {}
|
||||
if params.get("container"):
|
||||
kwargs["query_params"] = {"container": params["container"]}
|
||||
if module.params.get("container"):
|
||||
kwargs["query_params"] = dict(container=module.params["container"])
|
||||
|
||||
if params.get("since_seconds"):
|
||||
if module.params.get("since_seconds"):
|
||||
kwargs.setdefault("query_params", {}).update(
|
||||
{"sinceSeconds": params["since_seconds"]}
|
||||
{"sinceSeconds": module.params["since_seconds"]}
|
||||
)
|
||||
|
||||
if params.get("previous"):
|
||||
kwargs.setdefault("query_params", {}).update({"previous": params["previous"]})
|
||||
log = serialize_log(
|
||||
resource.log.get(name=name, namespace=namespace, serialize=False, **kwargs)
|
||||
)
|
||||
|
||||
if params.get("tail_lines"):
|
||||
kwargs.setdefault("query_params", {}).update(
|
||||
{"tailLines": params["tail_lines"]}
|
||||
)
|
||||
|
||||
pod_containers = [None]
|
||||
if params.get("all_containers"):
|
||||
pod_containers = list_containers_in_pod(svc, resource, namespace, name)
|
||||
|
||||
log = ""
|
||||
try:
|
||||
for container in pod_containers:
|
||||
if container is not None:
|
||||
kwargs.setdefault("query_params", {}).update({"container": container})
|
||||
response = resource.log.get(
|
||||
name=name, namespace=namespace, serialize=False, **kwargs
|
||||
)
|
||||
log += response.data.decode("utf8")
|
||||
except ApiException as exc:
|
||||
if exc.reason == "Not Found":
|
||||
raise CoreException("Pod {0}/{1} not found.".format(namespace, name))
|
||||
raise CoreException(
|
||||
"Unable to retrieve log from Pod due to: {0}".format(
|
||||
get_exception_message(exc)
|
||||
)
|
||||
)
|
||||
|
||||
return {"changed": False, "log": log, "log_lines": log.split("\n")}
|
||||
module.exit_json(changed=False, log=log, log_lines=log.split("\n"))
|
||||
|
||||
|
||||
def extract_selectors(instance):
|
||||
def extract_selectors(module, instance):
|
||||
# Parses selectors on an object based on the specifications documented here:
|
||||
# https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
|
||||
selectors = []
|
||||
if not instance.spec.selector:
|
||||
raise CoreException(
|
||||
"{0} {1} does not support the log subresource directly, and no Pod selector was found on the object".format(
|
||||
module.fail(
|
||||
msg="{0} {1} does not support the log subresource directly, and no Pod selector was found on the object".format(
|
||||
"/".join(instance.group, instance.apiVersion), instance.kind
|
||||
)
|
||||
)
|
||||
@@ -333,8 +232,8 @@ def extract_selectors(instance):
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise CoreException(
|
||||
"The k8s_log module does not support the {0} matchExpression operator".format(
|
||||
module.fail(
|
||||
msg="The k8s_log module does not support the {0} matchExpression operator".format(
|
||||
operator.lower()
|
||||
)
|
||||
)
|
||||
@@ -342,21 +241,22 @@ def extract_selectors(instance):
|
||||
return selectors
|
||||
|
||||
|
||||
def serialize_log(response):
|
||||
if PY2:
|
||||
return response.data
|
||||
return response.data.decode("utf8")
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
argument_spec=argspec(),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[("container", "all_containers")],
|
||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
try:
|
||||
client = get_api_client(module=module)
|
||||
svc = K8sService(client, module)
|
||||
result = execute_module(svc, module.params)
|
||||
module.exit_json(**result)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -24,18 +24,16 @@ options:
|
||||
description: List of label selectors to use to filter results.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
field_selectors:
|
||||
description: List of field selectors to use to filter results.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
- kubernetes.core.k8s_name_options
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -88,23 +86,12 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
|
||||
AUTH_ARG_SPEC,
|
||||
NAME_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
)
|
||||
|
||||
|
||||
def get_managed_resource(kind):
|
||||
def get_managed_resource(module):
|
||||
managed_resource = {}
|
||||
|
||||
kind = module.params["kind"]
|
||||
if kind == "DaemonSet":
|
||||
managed_resource["kind"] = "ControllerRevision"
|
||||
managed_resource["api_version"] = "apps/v1"
|
||||
@@ -112,17 +99,14 @@ def get_managed_resource(kind):
|
||||
managed_resource["kind"] = "ReplicaSet"
|
||||
managed_resource["api_version"] = "apps/v1"
|
||||
else:
|
||||
raise CoreException(
|
||||
"Cannot perform rollback on resource of kind {0}".format(kind)
|
||||
)
|
||||
module.fail(msg="Cannot perform rollback on resource of kind {0}".format(kind))
|
||||
return managed_resource
|
||||
|
||||
|
||||
def execute_module(svc):
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
results = []
|
||||
module = svc.module
|
||||
|
||||
resources = svc.find(
|
||||
resources = k8s_ansible_mixin.kubernetes_facts(
|
||||
module.params["kind"],
|
||||
module.params["api_version"],
|
||||
module.params["name"],
|
||||
@@ -133,16 +117,14 @@ def execute_module(svc):
|
||||
|
||||
changed = False
|
||||
for resource in resources["resources"]:
|
||||
result = perform_action(svc, resource)
|
||||
result = perform_action(module, k8s_ansible_mixin, resource)
|
||||
changed = result["changed"] or changed
|
||||
results.append(result)
|
||||
|
||||
module.exit_json(**{"changed": changed, "rollback_info": results})
|
||||
|
||||
|
||||
def perform_action(svc, resource):
|
||||
module = svc.module
|
||||
|
||||
def perform_action(module, k8s_ansible_mixin, resource):
|
||||
if module.params["kind"] == "DaemonSet":
|
||||
current_revision = resource["metadata"]["generation"]
|
||||
elif module.params["kind"] == "Deployment":
|
||||
@@ -150,8 +132,8 @@ def perform_action(svc, resource):
|
||||
"deployment.kubernetes.io/revision"
|
||||
]
|
||||
|
||||
managed_resource = get_managed_resource(module.params["kind"])
|
||||
managed_resources = svc.find(
|
||||
managed_resource = get_managed_resource(module)
|
||||
managed_resources = k8s_ansible_mixin.kubernetes_facts(
|
||||
managed_resource["kind"],
|
||||
managed_resource["api_version"],
|
||||
"",
|
||||
@@ -203,7 +185,7 @@ def perform_action(svc, resource):
|
||||
|
||||
rollback = resource
|
||||
if not module.check_mode:
|
||||
rollback = svc.client.client.request(
|
||||
rollback = k8s_ansible_mixin.client.request(
|
||||
"PATCH",
|
||||
"/apis/{0}/namespaces/{1}/{2}/{3}".format(
|
||||
module.params["api_version"],
|
||||
@@ -260,16 +242,15 @@ def get_previous_revision(all_resources, current_revision):
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
try:
|
||||
client = get_api_client(module=module)
|
||||
svc = K8sService(client, module)
|
||||
execute_module(svc)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
@@ -35,7 +36,6 @@ options:
|
||||
description: List of label selectors to use to filter results.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
version_added: 2.0.0
|
||||
continue_on_error:
|
||||
description:
|
||||
@@ -45,8 +45,8 @@ options:
|
||||
version_added: 2.0.0
|
||||
|
||||
requirements:
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -143,40 +143,15 @@ result:
|
||||
|
||||
import copy
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.exceptions import NotFoundError
|
||||
except ImportError:
|
||||
# Handled in module setup
|
||||
pass
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
NAME_ARG_SPEC,
|
||||
RESOURCE_ARG_SPEC,
|
||||
NAME_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
ResourceTimeout,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
|
||||
create_definitions,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
diff_objects,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
|
||||
get_waiter,
|
||||
)
|
||||
|
||||
|
||||
SCALE_ARG_SPEC = {
|
||||
"replicas": {"type": "int", "required": True},
|
||||
@@ -188,20 +163,27 @@ SCALE_ARG_SPEC = {
|
||||
}
|
||||
|
||||
|
||||
def execute_module(client, module):
|
||||
current_replicas = module.params.get("current_replicas")
|
||||
replicas = module.params.get("replicas")
|
||||
resource_version = module.params.get("resource_version")
|
||||
definitions = create_definitions(module.params)
|
||||
definition = definitions[0]
|
||||
name = definition["metadata"].get("name")
|
||||
def execute_module(
|
||||
module,
|
||||
k8s_ansible_mixin,
|
||||
):
|
||||
k8s_ansible_mixin.set_resource_definitions(module)
|
||||
|
||||
definition = k8s_ansible_mixin.resource_definitions[0]
|
||||
|
||||
name = definition["metadata"]["name"]
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
api_version = definition["apiVersion"]
|
||||
kind = definition["kind"]
|
||||
current_replicas = module.params.get("current_replicas")
|
||||
replicas = module.params.get("replicas")
|
||||
resource_version = module.params.get("resource_version")
|
||||
|
||||
label_selectors = module.params.get("label_selectors")
|
||||
if not label_selectors:
|
||||
label_selectors = []
|
||||
continue_on_error = module.params.get("continue_on_error")
|
||||
|
||||
wait = module.params.get("wait")
|
||||
wait_time = module.params.get("wait_timeout")
|
||||
wait_sleep = module.params.get("wait_sleep")
|
||||
@@ -213,7 +195,12 @@ def execute_module(client, module):
|
||||
if wait:
|
||||
return_attributes["duration"] = 0
|
||||
|
||||
resource = client.resource(kind, api_version)
|
||||
resource = k8s_ansible_mixin.find_resource(kind, api_version, fail=True)
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
NotFoundError,
|
||||
)
|
||||
|
||||
multiple_scale = False
|
||||
try:
|
||||
existing = resource.get(
|
||||
@@ -224,10 +211,11 @@ def execute_module(client, module):
|
||||
multiple_scale = len(existing_items) > 1
|
||||
else:
|
||||
existing_items = [existing]
|
||||
except NotFoundError as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to retrieve requested object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
except NotFoundError as exc:
|
||||
module.fail_json(
|
||||
msg="Failed to retrieve requested object: {0}".format(exc),
|
||||
error=exc.value.get("status"),
|
||||
)
|
||||
|
||||
if multiple_scale:
|
||||
# when scaling multiple resource, the 'result' is changed to 'results' and is a list
|
||||
@@ -249,7 +237,7 @@ def execute_module(client, module):
|
||||
module.exit_json(warning=warn, **return_attributes)
|
||||
|
||||
for existing in existing_items:
|
||||
if kind.lower() == "job":
|
||||
if module.params["kind"].lower() == "job":
|
||||
existing_count = existing.spec.parallelism
|
||||
elif hasattr(existing.spec, "replicas"):
|
||||
existing_count = existing.spec.replicas
|
||||
@@ -284,34 +272,29 @@ def execute_module(client, module):
|
||||
continue
|
||||
|
||||
if existing_count != replicas:
|
||||
if kind.lower() == "job":
|
||||
if module.params["kind"].lower() == "job":
|
||||
existing.spec.parallelism = replicas
|
||||
result = {"changed": True}
|
||||
if module.check_mode:
|
||||
result["result"] = existing.to_dict()
|
||||
else:
|
||||
result["result"] = client.patch(
|
||||
resource, existing.to_dict()
|
||||
).to_dict()
|
||||
result["result"] = resource.patch(existing.to_dict()).to_dict()
|
||||
else:
|
||||
try:
|
||||
result = scale(
|
||||
client,
|
||||
module,
|
||||
resource,
|
||||
existing,
|
||||
replicas,
|
||||
wait,
|
||||
wait_time,
|
||||
wait_sleep,
|
||||
)
|
||||
except CoreException as e:
|
||||
module.fail_json(msg=to_native(e))
|
||||
result = scale(
|
||||
module,
|
||||
k8s_ansible_mixin,
|
||||
resource,
|
||||
existing,
|
||||
replicas,
|
||||
wait,
|
||||
wait_time,
|
||||
wait_sleep,
|
||||
)
|
||||
changed = changed or result["changed"]
|
||||
else:
|
||||
name = existing.metadata.name
|
||||
namespace = existing.metadata.namespace
|
||||
existing = client.get(resource, name=name, namespace=namespace)
|
||||
existing = resource.get(name=name, namespace=namespace)
|
||||
result = {"changed": False, "result": existing.to_dict()}
|
||||
if module._diff:
|
||||
result["diff"] = {}
|
||||
@@ -337,8 +320,8 @@ def argspec():
|
||||
|
||||
|
||||
def scale(
|
||||
client,
|
||||
module,
|
||||
k8s_ansible_mixin,
|
||||
resource,
|
||||
existing_object,
|
||||
replicas,
|
||||
@@ -351,8 +334,8 @@ def scale(
|
||||
kind = existing_object.kind
|
||||
|
||||
if not hasattr(resource, "scale"):
|
||||
raise CoreException(
|
||||
"Cannot perform scale on resource of kind {0}".format(resource.kind)
|
||||
module.fail_json(
|
||||
msg="Cannot perform scale on resource of kind {0}".format(resource.kind)
|
||||
)
|
||||
|
||||
scale_obj = {
|
||||
@@ -361,37 +344,32 @@ def scale(
|
||||
"spec": {"replicas": replicas},
|
||||
}
|
||||
|
||||
existing = client.get(resource, name=name, namespace=namespace)
|
||||
existing = resource.get(name=name, namespace=namespace)
|
||||
|
||||
result = dict()
|
||||
if module.check_mode:
|
||||
k8s_obj = copy.deepcopy(existing.to_dict())
|
||||
k8s_obj["spec"]["replicas"] = replicas
|
||||
match, diffs = k8s_ansible_mixin.diff_objects(existing.to_dict(), k8s_obj)
|
||||
if wait:
|
||||
result["duration"] = 0
|
||||
result["result"] = k8s_obj
|
||||
else:
|
||||
try:
|
||||
resource.scale.patch(body=scale_obj)
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Scale request failed: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
except Exception as exc:
|
||||
module.fail_json(msg="Scale request failed: {0}".format(exc))
|
||||
|
||||
k8s_obj = client.get(resource, name=name, namespace=namespace).to_dict()
|
||||
k8s_obj = resource.get(name=name, namespace=namespace).to_dict()
|
||||
result["result"] = k8s_obj
|
||||
if wait:
|
||||
waiter = get_waiter(client, resource)
|
||||
success, result["result"], result["duration"] = waiter.wait(
|
||||
timeout=wait_time,
|
||||
sleep=wait_sleep,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
if wait and not module.check_mode:
|
||||
success, result["result"], result["duration"] = k8s_ansible_mixin.wait(
|
||||
resource, scale_obj, wait_sleep, wait_time
|
||||
)
|
||||
if not success:
|
||||
raise ResourceTimeout("Resource scaling timed out", result)
|
||||
module.fail_json(msg="Resource scaling timed out", **result)
|
||||
|
||||
match, diffs = diff_objects(existing.to_dict(), result["result"])
|
||||
match, diffs = k8s_ansible_mixin.diff_objects(existing.to_dict(), k8s_obj)
|
||||
result["changed"] = not match
|
||||
if module._diff:
|
||||
result["diff"] = diffs
|
||||
@@ -403,18 +381,19 @@ def main():
|
||||
mutually_exclusive = [
|
||||
("resource_definition", "src"),
|
||||
]
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
module = AnsibleModule(
|
||||
argument_spec=argspec(),
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
try:
|
||||
client = get_api_client(module=module)
|
||||
execute_module(client, module)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -83,8 +83,8 @@ options:
|
||||
type: bool
|
||||
|
||||
requirements:
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -143,6 +143,7 @@ result:
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
@@ -153,24 +154,6 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
|
||||
COMMON_ARG_SPEC,
|
||||
RESOURCE_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
|
||||
create_definitions,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import (
|
||||
perform_action,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
)
|
||||
|
||||
SERVICE_ARG_SPEC = {
|
||||
"apply": {"type": "bool", "default": False},
|
||||
@@ -196,7 +179,7 @@ def merge_dicts(x, y):
|
||||
if isinstance(x[k], dict) and isinstance(y[k], dict):
|
||||
yield (k, dict(merge_dicts(x[k], y[k])))
|
||||
else:
|
||||
yield (k, y[k] if y[k] else x[k])
|
||||
yield (k, y[k])
|
||||
elif k in x:
|
||||
yield (k, x[k])
|
||||
else:
|
||||
@@ -212,9 +195,10 @@ def argspec():
|
||||
return argument_spec
|
||||
|
||||
|
||||
def execute_module(svc):
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
"""Module execution"""
|
||||
module = svc.module
|
||||
k8s_ansible_mixin.set_resource_definitions(module)
|
||||
|
||||
api_version = "v1"
|
||||
selector = module.params.get("selector")
|
||||
service_type = module.params.get("type")
|
||||
@@ -234,29 +218,28 @@ def execute_module(svc):
|
||||
def_meta["name"] = module.params.get("name")
|
||||
def_meta["namespace"] = module.params.get("namespace")
|
||||
|
||||
definitions = create_definitions(module.params)
|
||||
|
||||
# 'resource_definition:' has lower priority than module parameters
|
||||
definition = dict(merge_dicts(definitions[0], definition))
|
||||
definition = dict(
|
||||
merge_dicts(k8s_ansible_mixin.resource_definitions[0], definition)
|
||||
)
|
||||
|
||||
result = perform_action(svc, definition, module.params)
|
||||
resource = k8s_ansible_mixin.find_resource("Service", api_version, fail=True)
|
||||
definition = k8s_ansible_mixin.set_defaults(resource, definition)
|
||||
result = k8s_ansible_mixin.perform_action(resource, definition)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
argument_spec=argspec(),
|
||||
supports_check_mode=True,
|
||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
try:
|
||||
client = get_api_client(module=module)
|
||||
svc = K8sService(client, module)
|
||||
execute_module(svc)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -60,8 +60,8 @@ options:
|
||||
default: false
|
||||
type: bool
|
||||
requirements:
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -126,28 +126,22 @@ result:
|
||||
|
||||
import copy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
|
||||
AnsibleK8SModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.client.api import core_v1_api
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
except ImportError:
|
||||
# ImportErrors are handled during module setup
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
|
||||
|
||||
@@ -197,9 +191,21 @@ def argspec():
|
||||
|
||||
|
||||
class K8sTaintAnsible:
|
||||
def __init__(self, module, client):
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.api_instance = core_v1_api.CoreV1Api(client.client)
|
||||
self.k8s_ansible_mixin = K8sAnsibleMixin(module=self.module)
|
||||
self.k8s_ansible_mixin.client = get_api_client(module=self.module)
|
||||
self.k8s_ansible_mixin.module = self.module
|
||||
self.k8s_ansible_mixin.argspec = self.module.argument_spec
|
||||
self.k8s_ansible_mixin.check_mode = self.module.check_mode
|
||||
self.k8s_ansible_mixin.params = self.module.params
|
||||
self.k8s_ansible_mixin.fail_json = self.module.fail_json
|
||||
self.k8s_ansible_mixin.fail = self.module.fail_json
|
||||
self.k8s_ansible_mixin.exit_json = self.module.exit_json
|
||||
self.k8s_ansible_mixin.warn = self.module.warn
|
||||
self.k8s_ansible_mixin.warnings = []
|
||||
self.api_instance = core_v1_api.CoreV1Api(self.k8s_ansible_mixin.client.client)
|
||||
self.k8s_ansible_mixin.check_library_version()
|
||||
self.changed = False
|
||||
|
||||
def get_node(self, name):
|
||||
@@ -295,17 +301,12 @@ class K8sTaintAnsible:
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
module = AnsibleModule(
|
||||
argument_spec=argspec(),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
try:
|
||||
client = get_api_client(module)
|
||||
k8s_taint = K8sTaintAnsible(module, client.client)
|
||||
k8s_taint.execute_module()
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
k8s_taint = K8sTaintAnsible(module)
|
||||
k8s_taint.execute_module()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
kubernetes>=24.2.0
|
||||
kubernetes>=12.0.0
|
||||
requests-oauthlib
|
||||
jsonpatch
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[flake8]
|
||||
max-line-length = 160
|
||||
ignore = W503,E402
|
||||
exclude = .cache,.git,.tox,tests/output
|
||||
exclude = .cache
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
kubernetes-validate
|
||||
coverage==4.5.4
|
||||
mock
|
||||
pytest
|
||||
pytest-xdist
|
||||
pytest-mock
|
||||
pytest-forked
|
||||
virtualenv
|
||||
pytest-ansible
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
modules:
|
||||
python_requires: ">=3.6"
|
||||
@@ -1,4 +1,8 @@
|
||||
time=100
|
||||
# slow - 11min
|
||||
slow
|
||||
time=313
|
||||
helm_info
|
||||
helm_plugin
|
||||
helm_plugin_info
|
||||
helm_repository
|
||||
helm_template
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
helm_default_archive_name: "helm-{{ helm_version }}-{{ ansible_system | lower }}-amd64.tar.gz"
|
||||
helm_archive_name: "helm-{{ helm_version }}-{{ ansible_system | lower }}-amd64.tar.gz"
|
||||
helm_binary: "/tmp/helm/{{ ansible_system | lower }}-amd64/helm"
|
||||
|
||||
chart_test: "ingress-nginx"
|
||||
chart_test_local_path: "nginx-ingress"
|
||||
chart_test_version: 4.2.4
|
||||
chart_test_version: 3.8.0
|
||||
chart_test_version_local_path: 1.32.0
|
||||
chart_test_version_upgrade: 4.2.5
|
||||
chart_test_version_upgrade: 3.9.0
|
||||
chart_test_version_upgrade_local_path: 1.33.0
|
||||
chart_test_repo: "https://kubernetes.github.io/ingress-nginx"
|
||||
chart_test_git_repo: "http://github.com/helm/charts.git"
|
||||
@@ -15,14 +15,13 @@ chart_test_values:
|
||||
myValue: "changed"
|
||||
|
||||
test_namespace:
|
||||
- "helm-test-crds"
|
||||
- "helm-diff"
|
||||
- "helm-envvars"
|
||||
- "helm-uninstall"
|
||||
- "helm-read-envvars"
|
||||
- "helm-dep-update"
|
||||
- "helm-not-installed"
|
||||
- "helm-crd"
|
||||
- "helm-url"
|
||||
- "helm-repository"
|
||||
- "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"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: dep_up
|
||||
description: A Helm chart for molecule test
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "default"
|
||||
dependencies:
|
||||
- name: test-chart
|
||||
repository: file://../test-chart
|
||||
version: "0.1.0"
|
||||
@@ -1,2 +0,0 @@
|
||||
chart-test:
|
||||
myValue: helm update dependency test
|
||||
@@ -1,130 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2023, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
module: helm_test_pending
|
||||
short_description: created pending-install release
|
||||
author:
|
||||
- Aubin Bikouo (@abikouo)
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
description:
|
||||
- This module is used to create a pending install release for integration testing
|
||||
- The scope of this module is the integration testing of the kubernetes.core collection only.
|
||||
options:
|
||||
binary_path:
|
||||
description:
|
||||
- The path of a helm binary to use.
|
||||
required: true
|
||||
type: path
|
||||
chart_ref:
|
||||
description:
|
||||
- chart reference on chart repository (e.g. my-repo/my-chart-ref)
|
||||
required: true
|
||||
type: str
|
||||
chart_release:
|
||||
description:
|
||||
- Release name to manage.
|
||||
required: true
|
||||
type: str
|
||||
chart_release_namespace:
|
||||
description:
|
||||
- Kubernetes namespace where the chart should be installed.
|
||||
required: true
|
||||
type: str
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
class HelmReleaseNotFoundError(Exception):
|
||||
def __init__(self, message):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
def create_pending_install_release(helm_binary, chart_ref, chart_release, namespace):
|
||||
# create pending-install release
|
||||
command = [
|
||||
helm_binary,
|
||||
"install",
|
||||
chart_release,
|
||||
chart_ref,
|
||||
"--namespace",
|
||||
namespace,
|
||||
"--wait",
|
||||
]
|
||||
proc = subprocess.Popen(command)
|
||||
time.sleep(2)
|
||||
proc.kill()
|
||||
# ensure release status is pending-install
|
||||
command = [
|
||||
helm_binary,
|
||||
"list",
|
||||
"--all",
|
||||
"--output=json",
|
||||
"--namespace",
|
||||
namespace,
|
||||
"--filter",
|
||||
chart_release,
|
||||
]
|
||||
cmd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = cmd.communicate()
|
||||
|
||||
data = json.loads(out)
|
||||
if not data:
|
||||
error = "Release %s not found." % chart_release
|
||||
raise HelmReleaseNotFoundError(message=error)
|
||||
return data[0]["status"] == "pending-install", data[0]["status"]
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path", required=True),
|
||||
chart_ref=dict(type="str", required=True),
|
||||
chart_release=dict(type="str", required=True),
|
||||
chart_release_namespace=dict(type="str", required=True),
|
||||
),
|
||||
)
|
||||
|
||||
params = dict(
|
||||
helm_binary=module.params.get("binary_path"),
|
||||
chart_release=module.params.get("chart_release"),
|
||||
chart_ref=module.params.get("chart_ref"),
|
||||
namespace=module.params.get("chart_release_namespace"),
|
||||
)
|
||||
|
||||
try:
|
||||
result, status = create_pending_install_release(**params)
|
||||
if not result:
|
||||
module.fail_json(
|
||||
msg="unable to create pending-install release, current status is %s"
|
||||
% status
|
||||
)
|
||||
module.exit_json(changed=True, msg="Release created with status '%s'" % status)
|
||||
except HelmReleaseNotFoundError as err:
|
||||
module.fail_json(
|
||||
msg="Error while trying to create pending-install release due to '%s'" % err
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -52,12 +52,12 @@ result:
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
- connection: local
|
||||
gather_facts: true
|
||||
hosts: localhost
|
||||
|
||||
roles:
|
||||
- helm
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
export ANSIBLE_CALLBACKS_ENABLED=profile_tasks
|
||||
export ANSIBLE_ROLES_PATH=../
|
||||
ansible-playbook playbook.yaml "$@"
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
- name: Unarchive Helm binary
|
||||
unarchive:
|
||||
src: 'https://get.helm.sh/{{ helm_archive_name | default(helm_default_archive_name) }}'
|
||||
src: 'https://get.helm.sh/{{ helm_archive_name }}'
|
||||
dest: /tmp/helm/
|
||||
remote_src: yes
|
||||
retries: 10
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user