mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-11 20:12:18 +00:00
Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90bc4c4b3b | ||
|
|
1590c6a4cc | ||
|
|
76eccacab6 | ||
|
|
620abbac26 | ||
|
|
66a820e03a | ||
|
|
a3f2438e9d | ||
|
|
0709ea31c9 | ||
|
|
44ab1fc478 | ||
|
|
80c33c5573 | ||
|
|
73dc94be68 | ||
|
|
1bc65230bb | ||
|
|
91427a5b6a | ||
|
|
1d1a23b1ab | ||
|
|
6edc84678d | ||
|
|
66bd8620e4 | ||
|
|
94b43dc582 | ||
|
|
3c4f9cee60 | ||
|
|
a54b4a13b6 | ||
|
|
e16b3c46b0 | ||
|
|
bc60523372 | ||
|
|
64f3f07c5a | ||
|
|
d6492b66d9 | ||
|
|
4d5388ddf8 | ||
|
|
6dadd06252 | ||
|
|
69dc7d5c5d | ||
|
|
2fa1c2c6e5 | ||
|
|
f2623dd3cf | ||
|
|
8772d8c997 | ||
|
|
a32108f64f | ||
|
|
365e5e9159 | ||
|
|
448a68da3d | ||
|
|
46acab9c4c | ||
|
|
fdb77182c8 | ||
|
|
fa3d94f793 | ||
|
|
9ec27cf37c | ||
|
|
10cb241256 | ||
|
|
92e1f581fe | ||
|
|
7f69aff0d6 | ||
|
|
8c65ac066d | ||
|
|
1d962fb932 | ||
|
|
27ce23aa72 | ||
|
|
993652b581 | ||
|
|
9bd9d22db3 | ||
|
|
0593426918 | ||
|
|
8fa5b201a4 | ||
|
|
94c1f57f36 | ||
|
|
d0b97319a5 | ||
|
|
38d5c81051 | ||
|
|
914a16ec5c | ||
|
|
cb2070c93f | ||
|
|
b594d35931 | ||
|
|
00699ac3e5 | ||
|
|
d329e7ee42 | ||
|
|
d4fc22c74e | ||
|
|
b648f45e90 | ||
|
|
2cb5d6c316 | ||
|
|
0e7229cf8d | ||
|
|
9ec6912325 | ||
|
|
7cdf0d03f5 | ||
|
|
91df2f10bc | ||
|
|
1943dfc3d9 | ||
|
|
eb731cd3a5 | ||
|
|
ecc64cace1 | ||
|
|
bc0de24cba | ||
|
|
9f60b151ba | ||
|
|
159a63af97 | ||
|
|
6efabd3418 | ||
|
|
aee847431a | ||
|
|
6609abdd5a | ||
|
|
219c747a24 | ||
|
|
7559b65946 | ||
|
|
c8a33c7180 | ||
|
|
52f2cb5587 | ||
|
|
513ff66fcf | ||
|
|
fca0dc0485 | ||
|
|
cd686316e9 | ||
|
|
b8e9873f64 | ||
|
|
4c305e73f0 | ||
|
|
c8a9326306 | ||
|
|
445d367059 | ||
|
|
fdb8af7ca9 | ||
|
|
a89f19b4e5 | ||
|
|
5bc53dba7c | ||
|
|
b07fbd6271 | ||
|
|
44a2fc392a | ||
|
|
6265a3e7ce | ||
|
|
0afd257dd0 | ||
|
|
d192157ed8 | ||
|
|
6a04f42d0b | ||
|
|
5064d722c3 | ||
|
|
fb80d973c4 | ||
|
|
8363a4debf | ||
|
|
0c5233a650 | ||
|
|
c0666a5137 | ||
|
|
072a08091b | ||
|
|
cbadbe32f9 | ||
|
|
966fa7e906 | ||
|
|
485eae3b10 | ||
|
|
a4c1bd8541 | ||
|
|
8858b19121 | ||
|
|
6360763098 | ||
|
|
ac943e9890 | ||
|
|
0408aa9328 | ||
|
|
874fbfedd5 | ||
|
|
d8d9133912 | ||
|
|
86d9a3f45f | ||
|
|
fb25ff44f1 | ||
|
|
600c10dffb | ||
|
|
9f7c865c9c | ||
|
|
23e94b60c1 | ||
|
|
1955989278 | ||
|
|
7c4ec3b982 | ||
|
|
8d15489ec2 | ||
|
|
3dcdcbc85d | ||
|
|
fe9c12326d | ||
|
|
1a601213eb | ||
|
|
abb9e0b6d5 | ||
|
|
bdd429981c | ||
|
|
6956a77f8c | ||
|
|
1670e35cd8 | ||
|
|
b44fdd3f05 | ||
|
|
b066a2dda3 | ||
|
|
9e9962bc6c | ||
|
|
9ca13c3799 | ||
|
|
318529abaa | ||
|
|
6d0a3af311 | ||
|
|
54d8193972 | ||
|
|
a624251bba | ||
|
|
869f06f1e4 | ||
|
|
7919231df1 | ||
|
|
ea28cbaa59 | ||
|
|
560e0e3d40 | ||
|
|
ed09047699 | ||
|
|
2d1ec22405 | ||
|
|
71599e2fde | ||
|
|
8687994e9f | ||
|
|
8640c16cd4 | ||
|
|
deb4859f19 | ||
|
|
fb2af07583 | ||
|
|
151ed8245f | ||
|
|
09a3c837c3 | ||
|
|
31c1ccf962 | ||
|
|
031cc7c40d | ||
|
|
22764492d2 | ||
|
|
3d313cf837 | ||
|
|
deaf8ee4f3 | ||
|
|
0f7963beb9 | ||
|
|
e6ac874098 | ||
|
|
8ed4d4b6ed | ||
|
|
af7c24cba7 | ||
|
|
804b9ab57c | ||
|
|
26cd550bc0 | ||
|
|
0b4fda7585 | ||
|
|
42ee210ecf | ||
|
|
c073eea5b3 | ||
|
|
979b492233 | ||
|
|
646eb18806 | ||
|
|
b967b55a16 | ||
|
|
1b66dbbd8b | ||
|
|
2a3862b67a | ||
|
|
2092d921cd | ||
|
|
29c75fa1c6 | ||
|
|
0e86fe0b7b | ||
|
|
43ad31d936 | ||
|
|
093d06ab55 | ||
|
|
454d0efe0a | ||
|
|
08596fd05b | ||
|
|
5e48c6973c | ||
|
|
a3a5f3cf4b | ||
|
|
5ff3566f30 | ||
|
|
c4c12ca2c3 | ||
|
|
7f7008fecc | ||
|
|
09d54919e3 | ||
|
|
58cbbf6364 | ||
|
|
7d0f0449ae | ||
|
|
9f51fc0ef0 | ||
|
|
14fe6f1c55 | ||
|
|
4f1623fe9c | ||
|
|
adf3503d4e | ||
|
|
beb53652db | ||
|
|
92785f58da | ||
|
|
25644ac192 | ||
|
|
3bf147580f | ||
|
|
193a0cb68c | ||
|
|
58a0fb1605 | ||
|
|
b62ea00ebf | ||
|
|
349e9f473a | ||
|
|
61faa1079e | ||
|
|
08a3d951d0 | ||
|
|
8171c994df | ||
|
|
f5a0dd5946 | ||
|
|
9aa20f0fbe | ||
|
|
afa6a74178 | ||
|
|
e2e3f71ecf | ||
|
|
346e303084 | ||
|
|
d68da5bbdd | ||
|
|
e2f54d3431 | ||
|
|
f168a3f67f | ||
|
|
7fb89a7b6f | ||
|
|
42644ee26e | ||
|
|
2a9d894c90 | ||
|
|
3729b8bb5b | ||
|
|
531a9fe3ac | ||
|
|
77dd2496d0 | ||
|
|
95e2add65b | ||
|
|
b5cfc854cb | ||
|
|
5662fa777c | ||
|
|
4fa1fb966b | ||
|
|
f2f4b66d77 | ||
|
|
67c808d934 | ||
|
|
882e672bc5 | ||
|
|
1d05cf54f0 | ||
|
|
764e4499b5 | ||
|
|
2c96b70702 | ||
|
|
c475117bee | ||
|
|
d311ac718e | ||
|
|
7c71436f3b | ||
|
|
0299aa8807 | ||
|
|
f418353e44 | ||
|
|
074f0a6555 |
9
.ansible-lint
Normal file
9
.ansible-lint
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
profile: production
|
||||
|
||||
exclude_paths:
|
||||
- .ansible/
|
||||
- .github/
|
||||
- tests/integration
|
||||
- tests/unit
|
||||
- tests/sanity
|
||||
6
.ansible-lint-ignore
Normal file
6
.ansible-lint-ignore
Normal file
@@ -0,0 +1,6 @@
|
||||
# https://docs.ansible.com/ansible-lint/docs/rules/
|
||||
# no-changed-when is not requried for examples
|
||||
plugins/connection/kubectl.py no-changed-when
|
||||
# false positive result
|
||||
plugins/connection/kubectl.py var-naming[no-reserved]
|
||||
plugins/connection/kubectl.py jinja[invalid]
|
||||
60
.github/stale.yml
vendored
60
.github/stale.yml
vendored
@@ -1,60 +0,0 @@
|
||||
---
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 90
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale
|
||||
# label is closed. Set to false to disable. If disabled, issues still need to be
|
||||
# closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 30
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale.
|
||||
# Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set
|
||||
# to `[]` to disable
|
||||
exemptLabels:
|
||||
- security
|
||||
- planned
|
||||
- priority/critical
|
||||
- lifecycle/frozen
|
||||
- verified
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: true
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: lifecycle/stale
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
pulls:
|
||||
markComment: |-
|
||||
PRs go stale after 90 days of inactivity.
|
||||
If there is no further activity, the PR will be closed in another 30 days.
|
||||
|
||||
unmarkComment: >-
|
||||
This pull request is no longer stale.
|
||||
|
||||
closeComment: >-
|
||||
This pull request has been closed due to inactivity.
|
||||
|
||||
issues:
|
||||
markComment: |-
|
||||
Issues go stale after 90 days of inactivity.
|
||||
If there is no further activity, the issue will be closed in another 30 days.
|
||||
|
||||
unmarkComment: >-
|
||||
This issue is no longer stale.
|
||||
|
||||
closeComment: >-
|
||||
This issue has been closed due to inactivity.
|
||||
21
.github/workflows/changelog.yaml
vendored
Normal file
21
.github/workflows/changelog.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
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
Normal file
14
.github/workflows/galaxy-import.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
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
|
||||
164
.github/workflows/integration-tests.yaml
vendored
Normal file
164
.github/workflows/integration-tests.yaml
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
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"
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- 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"
|
||||
community_general: "./community_general"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Ref must match a branch/tag on github.com/ansible/ansible (e.g. stable-2.18, not 2.18).
|
||||
ansible-version: ["stable-2.18", "milestone"]
|
||||
enable-turbo-mode: [true, false]
|
||||
exclude:
|
||||
- ansible-version: "milestone"
|
||||
enable-turbo-mode: true
|
||||
python-version:
|
||||
- "3.12"
|
||||
workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }}
|
||||
name: "integration-py${{ matrix.python-version }}-${{ matrix.ansible-version }}-${{ matrix.workflow-id }}-enable_turbo=${{ matrix.enable-turbo-mode }}"
|
||||
steps:
|
||||
- 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
|
||||
if: ${{ matrix.enable-turbo-mode == true }}
|
||||
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: checkout ansible-collections/community.general
|
||||
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
|
||||
with:
|
||||
repository: ansible-collections/community.general
|
||||
path: ${{ env.community_general }}
|
||||
ref: main
|
||||
|
||||
- name: install cloud.common collection
|
||||
if: ${{ matrix.enable-turbo-mode == true }}
|
||||
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: install community.general collection
|
||||
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
|
||||
with:
|
||||
install_python_dependencies: false
|
||||
source_path: ${{ env.community_general }}
|
||||
|
||||
- name: create kubernetes cluster
|
||||
uses: helm/kind-action@v1.8.0
|
||||
with:
|
||||
node_image: "kindest/node:v1.29.2"
|
||||
|
||||
- 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 }}
|
||||
28
.github/workflows/label-new-prs.yaml
vendored
Normal file
28
.github/workflows/label-new-prs.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: label new prs
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- converted_to_draft
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
if: github.actor != 'patchback[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Add 'needs_triage' label if the pr is not a draft
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
if: github.event.pull_request.draft == false
|
||||
with:
|
||||
labels: needs_triage
|
||||
|
||||
- name: Remove 'needs_triage' label if the pr is a draft
|
||||
uses: actions-ecosystem/action-remove-labels@v1
|
||||
if: github.event.pull_request.draft == true
|
||||
with:
|
||||
labels: needs_triage
|
||||
25
.github/workflows/linters.yaml
vendored
Normal file
25
.github/workflows/linters.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
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@main
|
||||
with:
|
||||
gh_action_ref: "v25.5.0"
|
||||
15
.github/workflows/sanity-tests.yaml
vendored
Normal file
15
.github/workflows/sanity-tests.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
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
Normal file
14
.github/workflows/unit-tests.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
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
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -13,10 +13,15 @@ changelogs/.plugin-cache.yaml
|
||||
tests/output
|
||||
tests/integration/cloud-config-*
|
||||
.cache
|
||||
.ansible
|
||||
|
||||
# Helm charts
|
||||
tests/integration/*-chart-*.tgz
|
||||
tests/integration/targets/*/*.tgz
|
||||
|
||||
# ansible-test generated file
|
||||
tests/integration/inventory
|
||||
tests/integration/*-*.yml
|
||||
|
||||
# VS Code settings
|
||||
.vscode/
|
||||
|
||||
@@ -5,16 +5,25 @@ rules:
|
||||
braces:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
|
||||
brackets:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
comments:
|
||||
min-spaces-from-content: 1
|
||||
comments-indentation: false
|
||||
document-start: disable
|
||||
line-length: disable
|
||||
truthy: disable
|
||||
indentation:
|
||||
spaces: 2
|
||||
indent-sequences: consistent
|
||||
octal-values:
|
||||
forbid-implicit-octal: true
|
||||
forbid-explicit-octal: true
|
||||
ignore: |
|
||||
.cache
|
||||
.tox
|
||||
.ansible
|
||||
tests/output
|
||||
plugins/connection/kubectl.py
|
||||
|
||||
468
CHANGELOG.rst
468
CHANGELOG.rst
@@ -4,6 +4,472 @@ Kubernetes Collection Release Notes
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
v6.4.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release adds Helm v4 compatibility across the Helm modules and improves ``k8s_drain`` with check mode. When you explicitly allow evicting unmanaged pods, pods with local storage, or pods managed by a ``DaemonSet``, those cases are reported as informational output instead of module warnings.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- helm_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_pull - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - add new option plain_http to allow insecure http connection when running ``helm registry login`` (https://github.com/ansible-collections/kubernetes.core/pull/1090).
|
||||
- helm_repository - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- k8s_drain - Add support for ``check_mode`` (https://github.com/ansible-collections/kubernetes.core/pull/1086).
|
||||
- k8s_drain - Convert module warnings into informational displays when users explicitly request the deletion of unmanaged pods, pods with local storage, or those managed by a `DaemonSet` (https://github.com/ansible-collections/kubernetes.core/issues/1037).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Helm - Allow taking ownership of existing Kubernetes resources on the first installation of a Helm release. Previously, the ``take_ownership`` parameter was always disabled during the initial install, preventing resource adoption (https://github.com/ansible-collections/kubernetes.core/pull/1034).
|
||||
|
||||
v6.3.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release includes bugfixes such as implementing idempotency for the ``helm_pull`` module as well as a security update for selectively redacting sensitive information from kubeconfig.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Remove deprecated import from ``ansible.module_utils._text`` (https://github.com/ansible-collections/kubernetes.core/pull/1053).
|
||||
- helm - add ``release_values`` key to ``status`` return value that can be accessed using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
- helm_info - add ``release_values`` key to ``status`` return value that can be accessed using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- helm - the ``status.values`` return value has been deprecated and will be removed in a release after 2027-01-08. Use ``status.release_values`` instead (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
- helm_info - the ``status.values`` return value has been deprecated and will be removed in a release after 2027-01-08. Use ``status.release_values`` instead (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
|
||||
Security Fixes
|
||||
--------------
|
||||
|
||||
- Selectively redact sensitive info from kubeconfig instead of applying blanket ``no_log=True`` (https://github.com/ansible-collections/kubernetes.core/pull/1014).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Add idempotency for ``helm_pull`` module (https://github.com/ansible-collections/kubernetes.core/pull/1055).
|
||||
- Fixed a bug where setting ``K8S_AUTH_VERIFY_SSL=true`` (or any string value) caused the value to be treated as a separate ``kubectl`` command argument. (https://github.com/ansible-collections/kubernetes.core/pull/1049).
|
||||
- Limit supported versions of Helm to <4.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/1039).
|
||||
- Replace passing ``warnings`` to ``exit_json`` with ``AnsibleModule.warn`` in the ``k8s_drain``, ``k8s_rollback.py`` and ``k8s_scale.py`` modules as it deprecated in ``ansible-core>=2.19.0`` and will be removed in ``ansible-core>=2.23.0`` (https://github.com/ansible-collections/kubernetes.core/pull/1033).
|
||||
- k8s - Fix return block from the module documentation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
- meta - Add ``k8s_cluster_info``, ``k8s_json_patch`` and ``k8s_rollback`` to k8s action group (https://github.com/ansible-collections/kubernetes.core/pull/992).
|
||||
|
||||
v6.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release adds minor changes and bugfixes, including support of skip-schema-validation in ``helm`` module and removing deprecated ``ansible.module_utils.six`` imports.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Add support of skip-schema-validation in ``helm`` module (https://github.com/ansible-collections/kubernetes.core/pull/995)
|
||||
- kustomize - Add support of local environ (https://github.com/ansible-collections/kubernetes.core/pull/786).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Remove ``ansible.module_utils.six`` imports to avoid warnings (https://github.com/ansible-collections/kubernetes.core/pull/998).
|
||||
- Update the ``k8s_cp`` module to also work for init containers (https://github.com/ansible-collections/kubernetes.core/pull/971).
|
||||
|
||||
v6.1.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release adds ``plain_http`` and ``take_ownership`` parameters for helm modules, support for ``hidden_fields`` in ``k8s_json_patch``, documented lack of idempotency support in ``helm_registry_auth`` with ``helm ≥ 3.18.0``, and improved ``k8s_rollback`` test coverage.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Module helm_registry_auth do not support idempotency with `helm >= 3.18.0` (https://github.com/ansible-collections/kubernetes.core/pull/946)
|
||||
- Module k8s_json_patch - Add support for `hidden_fields` (https://github.com/ansible-collections/kubernetes.core/pull/964).
|
||||
- helm - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm - Parameter take_ownership added (https://github.com/ansible-collections/kubernetes.core/pull/957).
|
||||
- helm_pull - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm_template - Parameter plain_http added for working with insecure OCI registries (https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
|
||||
|
||||
v6.0.0
|
||||
======
|
||||
|
||||
Breaking Changes / Porting Guide
|
||||
--------------------------------
|
||||
|
||||
- Remove deprecated ``k8s`` invetory plugin (https://github.com/ansible-collections/kubernetes.core/pull/867).
|
||||
- Remove support for ``ansible-core<2.16`` (https://github.com/ansible-collections/kubernetes.core/pull/867).
|
||||
|
||||
v5.4.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release includes bugfixes for k8s service field handling, k8s_cp init containers support, and removes deprecated ansible.module_utils.six imports.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Remove ``ansible.module_utils.six`` imports to avoid warnings (https://github.com/ansible-collections/kubernetes.core/pull/998).
|
||||
- Update the `k8s_cp` module to also work for init containers (https://github.com/ansible-collections/kubernetes.core/pull/971).
|
||||
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
|
||||
|
||||
v5.4.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release updates the ``helm_registry_auth`` module to match the behavior of ``helm >= 3.18.0`` which reports a successful logout regardless of the current state (i.e., no idempotency).
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Module ``helm_registry_auth`` does not support idempotency with ``helm >= 3.18.0`` (https://github.com/ansible-collections/kubernetes.core/pull/946).
|
||||
|
||||
v5.3.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release includes minor changes, bug fixes and also bumps ``ansible-lint`` version to ``25.1.2``.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Bump version of ``ansible-lint`` to 25.1.2 (https://github.com/ansible-collections/kubernetes.core/pull/919).
|
||||
- action/k8s_info - update templating mechanism with changes from ``ansible-core 2.19`` (https://github.com/ansible-collections/kubernetes.core/pull/888).
|
||||
- helm - add ``reset_then_reuse_values`` support to helm module (https://github.com/ansible-collections/kubernetes.core/issues/803).
|
||||
- helm - add support for ``insecure_skip_tls_verify`` option to helm and helm_repository(https://github.com/ansible-collections/kubernetes.core/issues/694).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- module_utils/k8s/service - fix issue when trying to delete resource using ``delete_options`` and ``check_mode=true`` (https://github.com/ansible-collections/kubernetes.core/issues/892).
|
||||
|
||||
v5.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release adds more functionality to the hidden_fields option and support for waiting on ClusterOperators to reach a ready state.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- k8s - Extend hidden_fields to allow the expression of more complex field types to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
|
||||
- k8s_info - Extend hidden_fields to allow the expression of more complex field types to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
|
||||
- waiter.py - add ClusterOperator support. The module can now check OpenShift cluster health by verifying ClusterOperator status requiring 'Available: True', 'Degraded: False', and 'Progressing: False' for success. (https://github.com/ansible-collections/kubernetes.core/issues/869)
|
||||
|
||||
v5.1.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release came with new module ``helm_registry_auth``, improvements to the error messages in the k8s_drain module, new parameter ``insecure_registry`` for ``helm_template`` module and several bug fixes.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Bump version of ``ansible-lint`` to minimum 24.7.0 (https://github.com/ansible-collections/kubernetes.core/pull/765).
|
||||
- Parameter insecure_registry added to helm_template as equivalent of insecure-skip-tls-verify (https://github.com/ansible-collections/kubernetes.core/pull/805).
|
||||
- k8s_drain - Improve error message for pod disruption budget when draining a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- helm - Helm version checks did not support RC versions. They now accept any version tags. (https://github.com/ansible-collections/kubernetes.core/pull/745).
|
||||
- helm_pull - Apply no_log=True to pass_credentials to silence false positive warning. (https://github.com/ansible-collections/kubernetes.core/pull/796).
|
||||
- k8s_drain - Fix k8s_drain does not wait for single pod (https://github.com/ansible-collections/kubernetes.core/issues/769).
|
||||
- k8s_drain - Fix k8s_drain runs into a timeout when evicting a pod which is part of a stateful set (https://github.com/ansible-collections/kubernetes.core/issues/792).
|
||||
- kubeconfig option should not appear in module invocation log (https://github.com/ansible-collections/kubernetes.core/issues/782).
|
||||
- kustomize - kustomize plugin fails with deprecation warnings (https://github.com/ansible-collections/kubernetes.core/issues/639).
|
||||
- waiter - Fix waiting for daemonset when desired number of pods is 0. (https://github.com/ansible-collections/kubernetes.core/pull/756).
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- helm_registry_auth - Helm registry authentication module
|
||||
|
||||
v5.0.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This major release drops support for ``ansible-core<2.15``.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- connection/kubectl.py - Added an example of using the kubectl connection plugin to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/734).
|
||||
|
||||
Breaking Changes / Porting Guide
|
||||
--------------------------------
|
||||
|
||||
- Remove support for ``ansible-core<2.15`` (https://github.com/ansible-collections/kubernetes.core/pull/737).
|
||||
|
||||
v4.0.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This major release brings several bug fixes. We have also removed support for ``ansible-core<2.15`` and deprecated functions and class from ``module_utils/common.py``.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
|
||||
- k8s - The module and K8sService were changed so warnings returned by the K8S API are now displayed to the user.
|
||||
|
||||
Removed Features (previously deprecated)
|
||||
----------------------------------------
|
||||
|
||||
- k8s - Support for ``merge_type=json`` has been removed in version 4.0.0. Please use ``kubernetes.core.k8s_json_patch`` instead (https://github.com/ansible-collections/kubernetes.core/pull/722).
|
||||
- k8s_exec - the previously deprecated ``result.return_code`` return value has been removed, consider using ``result.rc`` instead (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``K8sAnsibleMixin`` class has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``configuration_digest()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``get_api_client()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``unique_string()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Resolve Collections util resource discovery fails when complex subresources present (https://github.com/ansible-collections/kubernetes.core/pull/676).
|
||||
- align `helmdiff_check()` function commandline rendering with the `deploy()` function (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
- avoid unsafe conditions in integration tests (https://github.com/ansible-collections/kubernetes.core/pull/665).
|
||||
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
|
||||
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
|
||||
v3.3.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release fixes the CI issues with the ``linters`` workflow.
|
||||
|
||||
v3.3.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release comes with improvements to the error messages in the k8s_drain module and several bug fixes.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- k8s_drain - Improve error message for pod disruption budget when draining a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- helm - Helm version checks did not support RC versions. They now accept any version tags. (https://github.com/ansible-collections/kubernetes.core/pull/745).
|
||||
- helm_pull - Apply no_log=True to pass_credentials to silence false positive warning. (https://github.com/ansible-collections/kubernetes.core/pull/796).
|
||||
- k8s_drain - Fix k8s_drain does not wait for single pod (https://github.com/ansible-collections/kubernetes.core/issues/769).
|
||||
- k8s_drain - Fix k8s_drain runs into a timeout when evicting a pod which is part of a stateful set (https://github.com/ansible-collections/kubernetes.core/issues/792).
|
||||
- kubeconfig option should not appear in module invocation log (https://github.com/ansible-collections/kubernetes.core/issues/782).
|
||||
- kustomize - kustomize plugin fails with deprecation warnings (https://github.com/ansible-collections/kubernetes.core/issues/639).
|
||||
- waiter - Fix waiting for daemonset when desired number of pods is 0. (https://github.com/ansible-collections/kubernetes.core/pull/756).
|
||||
|
||||
v3.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release comes with documentation updates.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- connection/kubectl.py - Added an example of using the kubectl connection plugin to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/734).
|
||||
|
||||
v3.1.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release comes with some bugfixes and documentation updates. It also adds new features to the kubectl connection plugin and the kustomize lookup plugin.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- kubectl - added support of local enviroment variable that will be used for kubectl and may be requried for establishing connections ifself (https://github.com/ansible-collections/kubernetes.core/pull/702)
|
||||
- kustomize - new parameter added to --enable-helm (https://github.com/ansible-collections/kubernetes.core/issues/568)
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- helm - expand kubeconfig path with user's home directory for consistency with k8s
|
||||
- k8s_json_patch - rename action symlink to ensure k8s action plugin is used (https://github.com/ansible-collections/kubernetes.core/pull/652).
|
||||
|
||||
v3.0.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release fixes issue with resources discovery when complex subresources are present, and fixes issues with `reuse-values` parameter for helm module.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Resolve Collections util resource discovery fails when complex subresources present (https://github.com/ansible-collections/kubernetes.core/pull/676).
|
||||
- align `helmdiff_check()` function commandline rendering with the `deploy()` function (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
|
||||
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
|
||||
v3.0.0
|
||||
======
|
||||
|
||||
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
|
||||
======
|
||||
@@ -11,7 +477,7 @@ v2.3.1
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Catch exception raised when the process is waiting for resources (https://github.com/ansible-collections/kubernetes.core/issues/407).
|
||||
- Catch expectation 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).
|
||||
|
||||
@@ -48,7 +48,7 @@ Where modules have multiple parameters we recommend running through the 4-step m
|
||||
|
||||
For general information on running the integration tests see the
|
||||
[Integration Tests page of the Module Development Guide](https://docs.ansible.com/ansible/devel/dev_guide/testing_integration.html#testing-integration),
|
||||
especially the section on configuration for cloud tests. For questions about writing tests the Ansible Kubernetes community can be found on Libera.Chat IRC as detailed below.
|
||||
especially the section on configuration for cloud tests.
|
||||
|
||||
### Updating documentation
|
||||
|
||||
@@ -70,11 +70,3 @@ Review the changes and create a pull request using updated files.
|
||||
The `kubernetes.core` collection follows the Ansible project's
|
||||
[Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html).
|
||||
Please read and familiarize yourself with this document.
|
||||
|
||||
### IRC
|
||||
Our IRC channels may require you to register your nickname. If you receive an error when you connect, see
|
||||
[Libera.Chat's Nickname Registration guide](https://libera.chat/guides/registration) for instructions.
|
||||
|
||||
The `#ansible-kubernetes` channel on [libera.chat](https://libera.chat/) IRC is the main and official place to discuss use and development of the `kubernetes.core` collection.
|
||||
|
||||
For more information about Ansible's Kubernetes integration, browse the resources in the [Kubernetes Working Group](https://github.com/ansible/community/wiki/Kubernetes) Community wiki page.
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
# Also needs to be updated in galaxy.yml
|
||||
VERSION = 2.3.1
|
||||
VERSION = 6.4.0
|
||||
|
||||
TEST_ARGS ?= ""
|
||||
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`
|
||||
|
||||
48
PSF-license.txt
Normal file
48
PSF-license.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
91
README.md
91
README.md
@@ -1,34 +1,50 @@
|
||||
# Kubernetes Collection for Ansible
|
||||
|
||||
[](https://github.com/ansible-collections/kubernetes.core/actions) [](https://codecov.io/gh/ansible-collections/kubernetes.core)
|
||||
|
||||
This repository hosts the `kubernetes.core` (formerly known as `community.kubernetes`) Ansible Collection.
|
||||
|
||||
## Description
|
||||
|
||||
The collection includes a variety of Ansible content to help automate the management of applications in Kubernetes and OpenShift clusters, as well as the provisioning and maintenance of clusters themselves.
|
||||
|
||||
## Communication
|
||||
|
||||
* Join the Ansible forum:
|
||||
* [Get Help](https://forum.ansible.com/c/help/6): get help or help others.
|
||||
* [Posts tagged with 'kubernetes'](https://forum.ansible.com/tag/kubernetes): subscribe to participate in collection-related conversations.
|
||||
* [Social Spaces](https://forum.ansible.com/c/chat/4): gather and interact with fellow enthusiasts.
|
||||
* [News & Announcements](https://forum.ansible.com/c/news/5): track project-wide announcements including social events.
|
||||
|
||||
* The Ansible [Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn): used to announce releases and important changes.
|
||||
|
||||
For more information about communication, see the [Ansible communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
|
||||
|
||||
## Requirements
|
||||
|
||||
<!--start requires_ansible-->
|
||||
## Ansible version compatibility
|
||||
|
||||
This collection has been tested against following Ansible versions: **>=2.9.17**.
|
||||
This collection has been tested against the following Ansible versions: **>=2.16.0**.
|
||||
|
||||
For collections that support Ansible 2.9, please ensure you update your `network_os` to use the
|
||||
fully qualified collection name (for example, `cisco.ios.ios`).
|
||||
Plugins and modules within a collection may be tested with only specific Ansible versions.
|
||||
A collection may contain metadata that identifies these versions.
|
||||
PEP440 is the schema used to describe the versions of Ansible.
|
||||
<!--end requires_ansible-->
|
||||
|
||||
## Python Support
|
||||
### Helm Version Compatibility
|
||||
|
||||
* Collection supports 3.6+
|
||||
This collection supports Helm v3.x and newer. Please note that specific modules or certain parameters may have additional version requirements.
|
||||
|
||||
### Python Support
|
||||
|
||||
* Collection supports 3.9+
|
||||
|
||||
Note: Python2 is deprecated from [1st January 2020](https://www.python.org/doc/sunset-python-2/). Please switch to Python3.
|
||||
|
||||
## Kubernetes Version Support
|
||||
### Kubernetes Version Support
|
||||
|
||||
This collection supports Kubernetes versions >=1.19.
|
||||
This collection supports Kubernetes versions >= 1.24.
|
||||
|
||||
## Included content
|
||||
### Included Content
|
||||
|
||||
Click on the name of a plugin or module to view that content's documentation:
|
||||
|
||||
@@ -43,11 +59,6 @@ Name | Description
|
||||
--- | ---
|
||||
kubernetes.core.k8s_config_resource_name|Generate resource name for the given resource of type ConfigMap, Secret
|
||||
|
||||
### Inventory plugins
|
||||
Name | Description
|
||||
--- | ---
|
||||
[kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_inventory.rst)|Kubernetes (K8s) inventory source
|
||||
|
||||
### Lookup plugins
|
||||
Name | Description
|
||||
--- | ---
|
||||
@@ -61,6 +72,8 @@ 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_registry_auth](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_registry_auth_module.rst)|Helm registry authentication module
|
||||
[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
|
||||
@@ -78,9 +91,7 @@ Name | Description
|
||||
|
||||
<!--end collection content-->
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
### Installing the Collection from Ansible Galaxy
|
||||
## Installation
|
||||
|
||||
Before using the Kubernetes collection, you need to install it with the Ansible Galaxy CLI:
|
||||
|
||||
@@ -92,7 +103,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
|
||||
---
|
||||
collections:
|
||||
- name: kubernetes.core
|
||||
version: 2.3.1
|
||||
version: 6.4.0
|
||||
```
|
||||
|
||||
### Installing the Kubernetes Python Library
|
||||
@@ -101,7 +112,7 @@ Content in this collection requires the [Kubernetes Python client](https://pypi.
|
||||
|
||||
pip3 install kubernetes
|
||||
|
||||
### Using modules from the Kubernetes Collection in your playbooks
|
||||
## Use Cases
|
||||
|
||||
It's preferable to use content in this collection using their Fully Qualified Collection Namespace (FQCN), for example `kubernetes.core.k8s_info`:
|
||||
|
||||
@@ -169,8 +180,7 @@ If upgrading older playbooks which were built prior to Ansible 2.10 and this col
|
||||
|
||||
For documentation on how to use individual modules and other content included in this collection, please see the links in the 'Included content' section earlier in this README.
|
||||
|
||||
## Ansible Turbo mode Tech Preview
|
||||
|
||||
## Ansible Turbo Mode Tech Preview
|
||||
|
||||
The ``kubernetes.core`` collection supports Ansible Turbo mode as a tech preview via the ``cloud.common`` collection. By default, this feature is disabled. To enable Turbo mode for modules, set the environment variable `ENABLE_TURBO_MODE=1` on the managed node. For example:
|
||||
|
||||
@@ -188,12 +198,16 @@ defined in the playbook using `environment` keyword as above, you must set it us
|
||||
|
||||
Please read more about Ansible Turbo mode - [here](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/ansible_turbo_mode.rst).
|
||||
|
||||
## Testing and Development
|
||||
## Contributing to this Collection
|
||||
|
||||
If you want to develop new content for this collection or improve what's already here, the easiest way to work on the collection is to clone it into one of the configured [`COLLECTIONS_PATHS`](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#collections-paths), and work on it there.
|
||||
|
||||
See [Contributing to kubernetes.core](CONTRIBUTING.md).
|
||||
|
||||
## Testing
|
||||
|
||||
[](https://github.com/ansible-collections/kubernetes.core/actions/workflows/linters.yaml) [](https://github.com/ansible-collections/kubernetes.core/actions/workflows/integration-tests.yaml) [](https://github.com/ansible-collections/kubernetes.core/actions/workflows/sanity-tests.yaml) [](https://github.com/ansible-collections/kubernetes.core/actions/workflows/unit-tests.yaml) [](https://app.codecov.io/gh/ansible-collections/kubernetes.core)
|
||||
|
||||
### Testing with `ansible-test`
|
||||
|
||||
The `tests` directory contains configuration for running sanity and integration tests using [`ansible-test`](https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html).
|
||||
@@ -206,7 +220,7 @@ You can run the collection's test suites with the commands:
|
||||
|
||||
### Testing with `molecule`
|
||||
|
||||
There are also integration tests in the `molecule` directory which are meant to be run against a local Kubernetes cluster, e.g. using [KinD](https://kind.sigs.k8s.io) or [Minikube](https://minikube.sigs.k8s.io). To setup a local cluster using KinD and run Molecule:
|
||||
There are also integration tests in the `molecule` directory which are meant to be run against a local Kubernetes cluster, e.g. using [KinD](https://kind.sigs.k8s.io) or [Minikube](https://minikube.sigs.k8s.io). To set up a local cluster using KinD and run Molecule:
|
||||
|
||||
kind create cluster
|
||||
make test-molecule
|
||||
@@ -230,12 +244,35 @@ After the version is published, verify it exists on the [Kubernetes Collection G
|
||||
|
||||
The process for uploading a supported release to Automation Hub is documented separately.
|
||||
|
||||
## More Information
|
||||
## Support
|
||||
|
||||
For more information about Ansible's Kubernetes integration, join the `#ansible-kubernetes` channel on [libera.chat](https://libera.chat/) IRC, and browse the resources in the [Kubernetes Working Group](https://github.com/ansible/community/wiki/Kubernetes) Community wiki page.
|
||||
<!--List available communication channels. In addition to channels specific to your collection, we also recommend to use the following ones.-->
|
||||
|
||||
> **Note:** The `stable-4` branch, which handles all `4.x.y` releases of this collection, is no longer supported. This means that no backports nor releases will be performed on the `stable-4` branch.
|
||||
|
||||
We announce releases and important changes through Ansible's [The Bullhorn newsletter](https://github.com/ansible/community/wiki/News#the-bullhorn). Be sure you are [subscribed](https://eepurl.com/gZmiEP).
|
||||
|
||||
We take part in the global quarterly [Ansible Contributor Summit](https://github.com/ansible/community/wiki/Contributor-Summit) virtually or in-person. Track [The Bullhorn newsletter](https://eepurl.com/gZmiEP) and join us.
|
||||
|
||||
For more information about communication, refer to the [Ansible Communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
|
||||
|
||||
For the latest supported versions, refer to the release notes below.
|
||||
|
||||
If you encounter issues or have questions, you can submit a support request through the following channels:
|
||||
- GitHub Issues: Report bugs, request features, or ask questions by opening an issue in the [GitHub repository](https://github.com/ansible-collections/kubernetes.core/).
|
||||
|
||||
## Release Notes
|
||||
|
||||
See the [raw generated changelog](https://github.com/ansible-collections/kubernetes.core/blob/main/CHANGELOG.rst).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
We follow the [Ansible Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html) in all our interactions within this project.
|
||||
|
||||
If you encounter abusive behavior, please refer to the [policy violations](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html#policy-violations) section of the Code for information on how to raise a complaint.
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0 or later
|
||||
|
||||
See LICENCE to see the full text.
|
||||
See LICENSE to see the full text.
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
kubernetes-client [platform:fedora]
|
||||
openshift-clients [platform:rhel-8]
|
||||
openshift-clients [platform:rhel-9]
|
||||
|
||||
@@ -587,3 +587,619 @@ 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:
|
||||
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'
|
||||
3.0.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Resolve Collections util resource discovery fails when complex subresources
|
||||
present (https://github.com/ansible-collections/kubernetes.core/pull/676).
|
||||
- align `helmdiff_check()` function commandline rendering with the `deploy()`
|
||||
function (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
|
||||
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability
|
||||
with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
release_summary: This release fixes issue with resources discovery when complex
|
||||
subresources are present, and fixes issues with `reuse-values` parameter for
|
||||
helm module.
|
||||
fragments:
|
||||
- 20240117-fix-helm-diff-cmd-line-rendering.yml
|
||||
- 20240222-Collections-util-resource-discovery-fails-when-complex-subresources-present.yml
|
||||
- 20240228-fix-helm-diff-with-reuse-values.yml
|
||||
- 3.0.1.yml
|
||||
release_date: '2024-03-01'
|
||||
3.1.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- helm - expand kubeconfig path with user's home directory for consistency with
|
||||
k8s
|
||||
- k8s_json_patch - rename action symlink to ensure k8s action plugin is used
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/652).
|
||||
minor_changes:
|
||||
- kubectl - added support of local enviroment variable that will be used for
|
||||
kubectl and may be requried for establishing connections ifself (https://github.com/ansible-collections/kubernetes.core/pull/702)
|
||||
- kustomize - new parameter added to --enable-helm (https://github.com/ansible-collections/kubernetes.core/issues/568)
|
||||
release_summary: This release comes with some bugfixes and documentation updates.
|
||||
It also adds new features to the kubectl connection plugin and the kustomize
|
||||
lookup plugin.
|
||||
fragments:
|
||||
- 20240426-add-support-of-kubectl-local-env-vars-for-connection-plugin.yml
|
||||
- 3.1.0.yml
|
||||
- 592-kustomize-helm-support.yml
|
||||
- 652-fix-json-patch-action.yml
|
||||
- 654-helm-expand-user.yml
|
||||
release_date: '2024-05-16'
|
||||
3.2.0:
|
||||
changes:
|
||||
minor_changes:
|
||||
- connection/kubectl.py - Added an example of using the kubectl connection plugin
|
||||
to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/734).
|
||||
release_summary: This release comes with documentation updates.
|
||||
fragments:
|
||||
- 20240530-defer-removal-and-ansible-core-support-update.yaml
|
||||
- 20240601-doc-example-of-using-kubectl.yaml
|
||||
- 3.2.0.yml
|
||||
- inventory-update_removal_date.yml
|
||||
release_date: '2024-06-14'
|
||||
3.3.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- helm - Helm version checks did not support RC versions. They now accept any
|
||||
version tags. (https://github.com/ansible-collections/kubernetes.core/pull/745).
|
||||
- helm_pull - Apply no_log=True to pass_credentials to silence false positive
|
||||
warning. (https://github.com/ansible-collections/kubernetes.core/pull/796).
|
||||
- k8s_drain - Fix k8s_drain does not wait for single pod (https://github.com/ansible-collections/kubernetes.core/issues/769).
|
||||
- k8s_drain - Fix k8s_drain runs into a timeout when evicting a pod which is
|
||||
part of a stateful set (https://github.com/ansible-collections/kubernetes.core/issues/792).
|
||||
- kubeconfig option should not appear in module invocation log (https://github.com/ansible-collections/kubernetes.core/issues/782).
|
||||
- kustomize - kustomize plugin fails with deprecation warnings (https://github.com/ansible-collections/kubernetes.core/issues/639).
|
||||
- waiter - Fix waiting for daemonset when desired number of pods is 0. (https://github.com/ansible-collections/kubernetes.core/pull/756).
|
||||
minor_changes:
|
||||
- k8s_drain - Improve error message for pod disruption budget when draining
|
||||
a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
|
||||
release_summary: This release comes with improvements to the error messages
|
||||
in the k8s_drain module and several bug fixes.
|
||||
fragments:
|
||||
- 20240530-ansible-core-support-update.yaml
|
||||
- 20240611-helm-rc-version.yaml
|
||||
- 20240620-fix-kustomize-plugin-fails-with-deprecation-warnings.yml
|
||||
- 20241102-fix-ci-post-2.18-issue.yaml
|
||||
- 20241213-kubeconfig-set-no_log-true.yaml
|
||||
- 756-fix-daemonset-waiting.yaml
|
||||
- 770-fix-k8s-drain-doesnt-wait-for-single-pod.yaml
|
||||
- 793-fix-k8s-drain-runs-into-timeout.yaml
|
||||
- 796-false-positive-helmull.yaml
|
||||
- 798-drain-pdb-error-message.yaml
|
||||
- readme_template_update.yml
|
||||
release_date: '2025-01-20'
|
||||
3.3.1:
|
||||
changes:
|
||||
release_summary: This release fixes the CI issues with the ``linters`` workflow.
|
||||
fragments:
|
||||
- release_summary.yml
|
||||
release_date: '2025-03-26'
|
||||
4.0.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Resolve Collections util resource discovery fails when complex subresources
|
||||
present (https://github.com/ansible-collections/kubernetes.core/pull/676).
|
||||
- align `helmdiff_check()` function commandline rendering with the `deploy()`
|
||||
function (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
- avoid unsafe conditions in integration tests (https://github.com/ansible-collections/kubernetes.core/pull/665).
|
||||
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
|
||||
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability
|
||||
with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
minor_changes:
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
|
||||
- k8s - The module and K8sService were changed so warnings returned by the K8S
|
||||
API are now displayed to the user.
|
||||
release_summary: This major release brings several bug fixes. We have also removed
|
||||
support for ``ansible-core<2.15`` and deprecated functions and class from
|
||||
``module_utils/common.py``.
|
||||
removed_features:
|
||||
- k8s - Support for ``merge_type=json`` has been removed in version 4.0.0. Please
|
||||
use ``kubernetes.core.k8s_json_patch`` instead (https://github.com/ansible-collections/kubernetes.core/pull/722).
|
||||
- k8s_exec - the previously deprecated ``result.return_code`` return value has
|
||||
been removed, consider using ``result.rc`` instead (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``K8sAnsibleMixin`` class
|
||||
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``configuration_digest()``
|
||||
function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``get_api_client()`` function
|
||||
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``unique_string()`` function
|
||||
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
fragments:
|
||||
- 20231206-fix-unsafe-condition-in-integration.yml
|
||||
- 20240117-fix-helm-diff-cmd-line-rendering.yml
|
||||
- 20240222-Collections-util-resource-discovery-fails-when-complex-subresources-present.yml
|
||||
- 20240228-fix-helm-diff-with-reuse-values.yml
|
||||
- 20240423-k8s-display-warnings-to-users.yml
|
||||
- 4.0.0.yaml
|
||||
- inventory-update_removal_date.yml
|
||||
- k8s-merge_type-removed.yml
|
||||
- module_utils-common-remove-deprecated-functions-and-class.yaml
|
||||
release_date: '2024-05-24'
|
||||
5.0.0:
|
||||
changes:
|
||||
breaking_changes:
|
||||
- Remove support for ``ansible-core<2.15`` (https://github.com/ansible-collections/kubernetes.core/pull/737).
|
||||
minor_changes:
|
||||
- connection/kubectl.py - Added an example of using the kubectl connection plugin
|
||||
to the documentation (https://github.com/ansible-collections/kubernetes.core/pull/741).
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/734).
|
||||
release_summary: This major release drops support for ``ansible-core<2.15``.
|
||||
fragments:
|
||||
- 20240530-ansible-core-support-update.yaml
|
||||
- 20240530-defer-removal-and-ansible-core-support-update.yaml
|
||||
- 5.0.0.yml
|
||||
release_date: '2024-05-31'
|
||||
5.1.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- helm - Helm version checks did not support RC versions. They now accept any
|
||||
version tags. (https://github.com/ansible-collections/kubernetes.core/pull/745).
|
||||
- helm_pull - Apply no_log=True to pass_credentials to silence false positive
|
||||
warning. (https://github.com/ansible-collections/kubernetes.core/pull/796).
|
||||
- k8s_drain - Fix k8s_drain does not wait for single pod (https://github.com/ansible-collections/kubernetes.core/issues/769).
|
||||
- k8s_drain - Fix k8s_drain runs into a timeout when evicting a pod which is
|
||||
part of a stateful set (https://github.com/ansible-collections/kubernetes.core/issues/792).
|
||||
- kubeconfig option should not appear in module invocation log (https://github.com/ansible-collections/kubernetes.core/issues/782).
|
||||
- kustomize - kustomize plugin fails with deprecation warnings (https://github.com/ansible-collections/kubernetes.core/issues/639).
|
||||
- waiter - Fix waiting for daemonset when desired number of pods is 0. (https://github.com/ansible-collections/kubernetes.core/pull/756).
|
||||
minor_changes:
|
||||
- Bump version of ``ansible-lint`` to minimum 24.7.0 (https://github.com/ansible-collections/kubernetes.core/pull/765).
|
||||
- Parameter insecure_registry added to helm_template as equivalent of insecure-skip-tls-verify
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/805).
|
||||
- k8s_drain - Improve error message for pod disruption budget when draining
|
||||
a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
|
||||
release_summary: This release came with new module ``helm_registry_auth``, improvements
|
||||
to the error messages in the k8s_drain module, new parameter ``insecure_registry``
|
||||
for ``helm_template`` module and several bug fixes.
|
||||
fragments:
|
||||
- 0-readme.yml
|
||||
- 20240601-doc-example-of-using-kubectl.yaml
|
||||
- 20240611-helm-rc-version.yaml
|
||||
- 20240620-fix-kustomize-plugin-fails-with-deprecation-warnings.yml
|
||||
- 20241102-fix-ci-post-2.18-issue.yaml
|
||||
- 20241103-completly-remove-obsolate-communication-channel.yaml
|
||||
- 20241207-add-insecure-skip-tls-verify-to-helm-template.yaml
|
||||
- 20241213-kubeconfig-set-no_log-true.yaml
|
||||
- 756-fix-daemonset-waiting.yaml
|
||||
- 765-bump-ansible-lint-version.yml
|
||||
- 770-fix-k8s-drain-doesnt-wait-for-single-pod.yaml
|
||||
- 793-fix-k8s-drain-runs-into-timeout.yaml
|
||||
- 796-false-positive-helmull.yaml
|
||||
- 798-drain-pdb-error-message.yaml
|
||||
- readme_template_update.yml
|
||||
modules:
|
||||
- description: Helm registry authentication module
|
||||
name: helm_registry_auth
|
||||
namespace: ''
|
||||
release_date: '2025-01-20'
|
||||
5.2.0:
|
||||
changes:
|
||||
minor_changes:
|
||||
- k8s - Extend hidden_fields to allow the expression of more complex field types
|
||||
to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
|
||||
- k8s_info - Extend hidden_fields to allow the expression of more complex field
|
||||
types to be hidden (https://github.com/ansible-collections/kubernetes.core/pull/872)
|
||||
- 'waiter.py - add ClusterOperator support. The module can now check OpenShift
|
||||
cluster health by verifying ClusterOperator status requiring ''Available:
|
||||
True'', ''Degraded: False'', and ''Progressing: False'' for success. (https://github.com/ansible-collections/kubernetes.core/issues/869)'
|
||||
release_summary: This release adds more functionality to the hidden_fields option
|
||||
and support for waiting on ClusterOperators to reach a ready state.
|
||||
fragments:
|
||||
- 5.2.0.yml
|
||||
- 643-extend-hidden-fields.yaml
|
||||
- 879-clusteroperator-waiter.py.yaml
|
||||
release_date: '2025-03-27'
|
||||
5.3.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- module_utils/k8s/service - fix issue when trying to delete resource using
|
||||
``delete_options`` and ``check_mode=true`` (https://github.com/ansible-collections/kubernetes.core/issues/892).
|
||||
minor_changes:
|
||||
- Bump version of ``ansible-lint`` to 25.1.2 (https://github.com/ansible-collections/kubernetes.core/pull/919).
|
||||
- action/k8s_info - update templating mechanism with changes from ``ansible-core
|
||||
2.19`` (https://github.com/ansible-collections/kubernetes.core/pull/888).
|
||||
- helm - add ``reset_then_reuse_values`` support to helm module (https://github.com/ansible-collections/kubernetes.core/issues/803).
|
||||
- helm - add support for ``insecure_skip_tls_verify`` option to helm and helm_repository(https://github.com/ansible-collections/kubernetes.core/issues/694).
|
||||
release_summary: This release includes minor changes, bug fixes and also bumps
|
||||
``ansible-lint`` version to ``25.1.2``.
|
||||
fragments:
|
||||
- 20250324-k8s_info-templating.yaml
|
||||
- 5.3.0.yml
|
||||
- 694-add-insecure-skip-tls-verify.yml
|
||||
- 800-helm-add-reset_then_reuse_values-support.yml
|
||||
- 898-k8s-dont-delete-in-check-mode.yaml
|
||||
- 919-update-ansible-lint-version.yaml
|
||||
release_date: '2025-05-16'
|
||||
5.4.0:
|
||||
changes:
|
||||
minor_changes:
|
||||
- Module ``helm_registry_auth`` does not support idempotency with ``helm >=
|
||||
3.18.0`` (https://github.com/ansible-collections/kubernetes.core/pull/946).
|
||||
release_summary: This release updates the ``helm_registry_auth`` module to match
|
||||
the behavior of ``helm >= 3.18.0`` which reports a successful logout regardless
|
||||
of the current state (i.e., no idempotency).
|
||||
fragments:
|
||||
- 20250411-kubeconfig-no_log-revert.yaml
|
||||
- 20250503-fix-unit-tests.yml
|
||||
- 20250605-fix-helm_registry_auth-integration_test.yaml
|
||||
- 5.4.0.yml
|
||||
release_date: '2025-08-12'
|
||||
5.4.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Remove ``ansible.module_utils.six`` imports to avoid warnings (https://github.com/ansible-collections/kubernetes.core/pull/998).
|
||||
- Update the ``k8s_cp`` module to also work for init containers (https://github.com/ansible-collections/kubernetes.core/pull/971).
|
||||
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
|
||||
release_summary: This release includes bugfixes for k8s service field handling,
|
||||
k8s_cp init containers support, and removes deprecated ansible.module_utils.six
|
||||
imports.
|
||||
fragments:
|
||||
- 20250428-k8s-service-hide-fields-first.yaml
|
||||
- 20250731-fix-k8s_cp-initcontainers.yaml
|
||||
- 20250922-remove-ansible-six-imports.yaml
|
||||
- 5.4.1.yml
|
||||
release_date: '2025-10-07'
|
||||
6.0.0:
|
||||
changes:
|
||||
breaking_changes:
|
||||
- Remove deprecated ``k8s`` invetory plugin (https://github.com/ansible-collections/kubernetes.core/pull/867).
|
||||
- Remove support for ``ansible-core<2.16`` (https://github.com/ansible-collections/kubernetes.core/pull/867).
|
||||
fragments:
|
||||
- 20250121-breaking-changes-6.0.0.yml
|
||||
release_date: '2025-05-19'
|
||||
6.1.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- module_utils/k8s/service - hide fields first before creating diffs (https://github.com/ansible-collections/kubernetes.core/pull/915).
|
||||
minor_changes:
|
||||
- Module ``helm_registry_auth`` does not support idempotency with `helm >= 3.18.0`
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/946).
|
||||
- Module k8s_json_patch - Add support for ``hidden_fields`` (https://github.com/ansible-collections/kubernetes.core/pull/964).
|
||||
- helm - Parameter plain_http added for working with insecure OCI registries
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm - Parameter take_ownership added (https://github.com/ansible-collections/kubernetes.core/pull/957).
|
||||
- helm_pull - Parameter plain_http added for working with insecure OCI registries
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
- helm_template - Parameter plain_http added for working with insecure OCI registries
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/934).
|
||||
release_summary: "This release adds ``plain_http`` and ``take_ownership`` parameters
|
||||
for helm modules, support for ``hidden_fields`` in ``k8s_json_patch``, documented
|
||||
lack of idempotency support in ``helm_registry_auth`` with ``helm \u2265 3.18.0``,
|
||||
and improved ``k8s_rollback`` test coverage."
|
||||
fragments:
|
||||
- 20250411-kubeconfig-no_log-revert.yaml
|
||||
- 20250428-k8s-service-hide-fields-first.yaml
|
||||
- 20250522-add-plain-http-for-oci-registries.yaml
|
||||
- 20250605-fix-helm_registry_auth-integration_test.yaml
|
||||
- 20250704-k8s-rollback-integration-test-coverage.yaml
|
||||
- 20250720-k8s-patch-add-hidden-fields.yaml
|
||||
- 20250911-add-support-helm-take-ownership.yaml
|
||||
- release_summary.yml
|
||||
release_date: '2025-08-12'
|
||||
6.2.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Remove ``ansible.module_utils.six`` imports to avoid warnings (https://github.com/ansible-collections/kubernetes.core/pull/998).
|
||||
- Update the `k8s_cp` module to also work for init containers (https://github.com/ansible-collections/kubernetes.core/pull/971).
|
||||
minor_changes:
|
||||
- Add support of skip-schema-validation in ``helm`` module (https://github.com/ansible-collections/kubernetes.core/pull/995)
|
||||
- kustomize - Add support of local environ (https://github.com/ansible-collections/kubernetes.core/pull/786).
|
||||
release_summary: This release adds minor changes and bugfixes, including support
|
||||
of skip-schema-validation in ``helm`` module and removing deprecated ``ansible.module_utils.six``
|
||||
imports.
|
||||
fragments:
|
||||
- 20241030-support-of-evrion-for-kustomize-lookup-plugin.yaml
|
||||
- 20250731-fix-k8s_cp-initcontainers.yaml
|
||||
- 20250916-skip-schema-validation.yaml
|
||||
- 20250922-remove-ansible-six-imports.yaml
|
||||
- 6_2_0.yml
|
||||
release_date: '2025-10-07'
|
||||
6.3.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Add idempotency for ``helm_pull`` module (https://github.com/ansible-collections/kubernetes.core/pull/1055).
|
||||
- Fixed a bug where setting ``K8S_AUTH_VERIFY_SSL=true`` (or any string value)
|
||||
caused the value to be treated as a separate ``kubectl`` command argument.
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/1049).
|
||||
- Limit supported versions of Helm to <4.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/1039).
|
||||
- Replace passing ``warnings`` to ``exit_json`` with ``AnsibleModule.warn``
|
||||
in the ``k8s_drain``, ``k8s_rollback.py`` and ``k8s_scale.py`` modules as
|
||||
it deprecated in ``ansible-core>=2.19.0`` and will be removed in ``ansible-core>=2.23.0``
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/1033).
|
||||
- k8s - Fix return block from the module documentation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
- meta - Add ``k8s_cluster_info``, ``k8s_json_patch`` and ``k8s_rollback`` to
|
||||
k8s action group (https://github.com/ansible-collections/kubernetes.core/pull/992).
|
||||
deprecated_features:
|
||||
- helm - the ``status.values`` return value has been deprecated and will be
|
||||
removed in a release after 2027-01-08. Use ``status.release_values`` instead
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
- helm_info - the ``status.values`` return value has been deprecated and will
|
||||
be removed in a release after 2027-01-08. Use ``status.release_values`` instead
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
minor_changes:
|
||||
- Remove deprecated import from ``ansible.module_utils._text`` (https://github.com/ansible-collections/kubernetes.core/pull/1053).
|
||||
- helm - add ``release_values`` key to ``status`` return value that can be accessed
|
||||
using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
- helm_info - add ``release_values`` key to ``status`` return value that can
|
||||
be accessed using Jinja2 dot notation (https://github.com/ansible-collections/kubernetes.core/pull/1056).
|
||||
release_summary: This release includes bugfixes such as implementing idempotency
|
||||
for the ``helm_pull`` module as well as a security update for selectively
|
||||
redacting sensitive information from kubeconfig.
|
||||
security_fixes:
|
||||
- Selectively redact sensitive info from kubeconfig instead of applying blanket
|
||||
``no_log=True`` (https://github.com/ansible-collections/kubernetes.core/pull/1014).
|
||||
fragments:
|
||||
- 1033-warnings-deprecations.yaml
|
||||
- 20251002-fix-k8s-actiongroup.yaml
|
||||
- 20251007-selective-kubeconfig-redaction.yaml
|
||||
- 20251115-limit-versions-of-helm.yaml
|
||||
- 20251220-fix-K8S_AUTH_VERIFY_SSL-in-kubectl-connecton-plugion.yaml
|
||||
- 20251228-1053-remove-deprecated-import.yaml
|
||||
- 20260107-add-idempodency-for-helm-pull.yaml
|
||||
- 20260108-fix-sanity-failures.yml
|
||||
- 6-3-0.yaml
|
||||
release_date: '2026-02-03'
|
||||
6.4.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Helm - Allow taking ownership of existing Kubernetes resources on the first
|
||||
installation of a Helm release. Previously, the ``take_ownership`` parameter
|
||||
was always disabled during the initial install, preventing resource adoption
|
||||
(https://github.com/ansible-collections/kubernetes.core/pull/1034).
|
||||
minor_changes:
|
||||
- helm_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_plugin_info - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_pull - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- helm_registry_auth - add new option plain_http to allow insecure http connection
|
||||
when running ``helm registry login`` (https://github.com/ansible-collections/kubernetes.core/pull/1090).
|
||||
- helm_repository - Ensure compatibility with Helm v4 (https://github.com/ansible-collections/kubernetes.core/issues/1038).
|
||||
- k8s_drain - Add support for ``check_mode`` (https://github.com/ansible-collections/kubernetes.core/pull/1086).
|
||||
- k8s_drain - Convert module warnings into informational displays when users
|
||||
explicitly request the deletion of unmanaged pods, pods with local storage,
|
||||
or those managed by a ``DaemonSet`` (https://github.com/ansible-collections/kubernetes.core/issues/1037).
|
||||
release_summary: This release adds Helm v4 compatibility across the Helm modules
|
||||
and improves ``k8s_drain`` with check mode. When you explicitly allow evicting
|
||||
unmanaged pods, pods with local storage, or pods managed by a ``DaemonSet``,
|
||||
those cases are reported as informational output instead of module warnings.
|
||||
fragments:
|
||||
- 20251224-take-ownership-helm-initialization.yaml
|
||||
- 20260203-k8s_drain-warning-fixes.yaml
|
||||
- 20260213-support-helm-v4-for-helm-plugin-modules.yaml
|
||||
- release-6-4-0.yml
|
||||
release_date: '2026-04-22'
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
.. _ansible_turbo_mode:
|
||||
|
||||
|
||||
******************
|
||||
Ansible Turbo mode
|
||||
******************
|
||||
|
||||
Following document provides overview of Ansible Turbo mode in ``kubernetes.core`` collection.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
- A brief introduction about Ansible Turbo mode in ``kuberentes.core`` collection.
|
||||
- Ansible Turbo mode is an optional performance optimization. It can be enabled by installing the cloud.common collection and setting the ``ENABLE_TURBO_MODE`` environment variable.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The following requirement is needed on the host that executes this module.
|
||||
|
||||
- The ``cloud.common`` collection (https://github.com/ansible-collections/cloud.common)
|
||||
|
||||
You will also need to set the environment variable ``ENABLE_TURBO_MODE=1`` on the managed host. This can be done in the same ways you would usually do so, for example::
|
||||
|
||||
---
|
||||
- hosts: remote
|
||||
environment:
|
||||
ENABLE_TURBO_MODE: 1
|
||||
tasks:
|
||||
...
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install ``cloud.common`` collection using following command::
|
||||
|
||||
# ansible-galaxy collection install cloud.common
|
||||
|
||||
|
||||
Current situation without Ansible Turbo mode
|
||||
============================================
|
||||
|
||||
The traditional execution flow of an Ansible module includes the following steps:
|
||||
|
||||
- Upload of a ZIP archive with the module and its dependencies
|
||||
- Execution of the module
|
||||
- Ansible collects the results once the script is finished
|
||||
|
||||
These steps happen for each task of a playbook, and on every host.
|
||||
|
||||
Most of the time, the execution of a module is fast enough for
|
||||
the user. However, sometime the module requires significant amount of time,
|
||||
just to initialize itself. This is a common situation with the API based modules.
|
||||
|
||||
A classic initialization involves the following steps:
|
||||
|
||||
- Load a Python library to access the remote resource (via SDK)
|
||||
- Open a client
|
||||
- Load a bunch of Python modules.
|
||||
- Request a new TCP connection.
|
||||
- Create a session.
|
||||
- Authenticate the client.
|
||||
|
||||
All these steps are time consuming and the same operations will be running again and again.
|
||||
|
||||
For instance, here:
|
||||
|
||||
- ``import openstack``: takes 0.569s
|
||||
- ``client = openstack.connect()``: takes 0.065s
|
||||
- ``client.authorize()``: takes 1.360s,
|
||||
|
||||
These numbers are from test running against VexxHost public cloud.
|
||||
|
||||
In this case, it's a 2s-ish overhead per task. If the playbook
|
||||
comes with 10 tasks, the execution time cannot go below 20s.
|
||||
|
||||
How Ansible Turbo Module improve the situation
|
||||
==============================================
|
||||
|
||||
``AnsibleTurboModule`` is actually a class that inherites from
|
||||
the standard ``AnsibleModule`` class that your modules probably
|
||||
already use.
|
||||
The big difference is that when a module starts, it also spawns
|
||||
a little Python daemon. If a daemon already exists, it will just
|
||||
reuse it.
|
||||
All the module logic is run inside this Python daemon. This means:
|
||||
|
||||
- Python modules are actually loaded one time
|
||||
- Ansible module can reuse an existing authenticated session.
|
||||
|
||||
The background service
|
||||
======================
|
||||
|
||||
The daemon kills itself after 15s, and communication are done
|
||||
through an Unix socket.
|
||||
It runs in one single process and uses ``asyncio`` internally.
|
||||
Consequently you can use the ``async`` keyword in your Ansible module.
|
||||
This will be handy if you interact with a lot of remote systems
|
||||
at the same time.
|
||||
|
||||
Security impact
|
||||
===============
|
||||
|
||||
``ansible_module.turbo`` open an Unix socket to interact with the background service.
|
||||
We use this service to open the connection toward the different target systems.
|
||||
|
||||
This is similar to what SSH does with the sockets.
|
||||
|
||||
Keep in mind that:
|
||||
|
||||
- All the modules can access the same cache. Soon an isolation will be done at the collection level (https://github.com/ansible-collections/cloud.common/pull/17)
|
||||
- A task can load a different version of a library and impact the next tasks.
|
||||
- If the same user runs two ``ansible-playbook`` at the same time, they will have access to the same cache.
|
||||
|
||||
When a module stores a session in a cache, it's a good idea to use a hash of the authentication information to identify the session.
|
||||
|
||||
Error management
|
||||
================
|
||||
|
||||
``ansible_module.turbo`` uses exceptions to communicate a result back to the module.
|
||||
|
||||
- ``EmbeddedModuleFailure`` is raised when ``json_fail()`` is called.
|
||||
- ``EmbeddedModuleSuccess`` is raised in case of success and returns the result to the origin module process.
|
||||
|
||||
These exceptions are defined in ``ansible_collections.cloud.common.plugins.module_utils.turbo.exceptions``.
|
||||
You can raise ``EmbeddedModuleFailure`` exception yourself, for instance from a module in ``module_utils``.
|
||||
|
||||
.. note:: Be careful with the ``except Exception:`` blocks.
|
||||
Not only they are bad practice, but also may interface with this
|
||||
mechanism.
|
||||
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
You may want to manually start the server. This can be done with the following command:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
PYTHONPATH=$HOME/.ansible/collections python -m ansible_collections.cloud.common.plugins.module_utils.turbo.server --socket-path $HOME/.ansible/tmp/turbo_mode.kubernetes.core.socket
|
||||
|
||||
You can use the ``--help`` argument to get a list of the optional parameters.
|
||||
@@ -17,7 +17,7 @@ Requirements
|
||||
|
||||
To use the modules, you'll need the following:
|
||||
|
||||
- Ansible 2.9.17 or latest installed
|
||||
- Ansible 2.16.0 or latest installed
|
||||
- `Kubernetes Python client <https://pypi.org/project/kubernetes/>`_ installed on the host that will execute the modules.
|
||||
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
.. _ansible_collections.kubernetes.core.docsite.k8s_ansible_inventory:
|
||||
|
||||
*****************************************
|
||||
Using Kubernetes dynamic inventory plugin
|
||||
*****************************************
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Kubernetes dynamic inventory plugin
|
||||
===================================
|
||||
|
||||
|
||||
The best way to interact with your Pods is to use the Kubernetes dynamic inventory plugin, which queries Kubernetes APIs using ``kubectl`` command line available on controller node and tells Ansible what Pods can be managed.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
To use the Kubernetes dynamic inventory plugins, you must install `Kubernetes Python client <https://github.com/kubernetes-client/python>`_, `kubectl <https://github.com/kubernetes/kubectl>`_ on your control node (the host running Ansible).
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install kubernetes
|
||||
|
||||
Please refer to Kubernetes official documentation for `installing kubectl <https://kubernetes.io/docs/tasks/tools/install-kubectl/>`_ on the given operating systems.
|
||||
|
||||
To use this Kubernetes dynamic inventory plugin, you need to enable it first by specifying the following in the ``ansible.cfg`` file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[inventory]
|
||||
enable_plugins = kubernetes.core.k8s
|
||||
|
||||
Then, create a file that ends in ``.k8s.yml`` or ``.k8s.yaml`` in your working directory.
|
||||
|
||||
The ``kubernetes.core.k8s`` inventory plugin takes in the same authentication information as any other Kubernetes modules.
|
||||
|
||||
Here's an example of a valid inventory file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: kubernetes.core.k8s
|
||||
|
||||
Executing ``ansible-inventory --list -i <filename>.k8s.yml`` will create a list of Pods that are ready to be configured using Ansible.
|
||||
|
||||
You can also provide the namespace to gather information about specific pods from the given namespace. For example, to gather information about Pods under the ``test`` namespace you will specify the ``namespaces`` parameter:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- namespaces:
|
||||
- test
|
||||
|
||||
Using vaulted configuration files
|
||||
=================================
|
||||
|
||||
Since the inventory configuration file contains Kubernetes related sensitive information in plain text, a security risk, you may want to
|
||||
encrypt your entire inventory configuration file.
|
||||
|
||||
You can encrypt a valid inventory configuration file as follows:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ansible-vault encrypt <filename>.k8s.yml
|
||||
New Vault password:
|
||||
Confirm New Vault password:
|
||||
Encryption successful
|
||||
|
||||
$ echo "MySuperSecretPassw0rd!" > /path/to/vault_password_file
|
||||
|
||||
And you can use this vaulted inventory configuration file using:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ansible-inventory -i <filename>.k8s.yml --list --vault-password-file=/path/to/vault_password_file
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Kubernetes Python client - Issue Tracker <https://github.com/kubernetes-client/python/issues>`_
|
||||
The issue tracker for Kubernetes Python client
|
||||
`Kubectl installation <https://kubernetes.io/docs/tasks/tools/install-kubectl/>`_
|
||||
Installation guide for installing Kubectl
|
||||
:ref:`working_with_playbooks`
|
||||
An introduction to playbooks
|
||||
:ref:`playbooks_vault`
|
||||
Using Vault in playbooks
|
||||
@@ -13,6 +13,5 @@ To get started, please select one of the following topics.
|
||||
:maxdepth: 1
|
||||
|
||||
kubernetes_scenarios/k8s_intro
|
||||
kubernetes_scenarios/k8s_inventory
|
||||
kubernetes_scenarios/k8s_scenarios
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
@@ -105,6 +105,27 @@ 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>
|
||||
@@ -127,7 +148,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">path</span>
|
||||
<span style="color: purple">raw</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -135,6 +156,7 @@ 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>
|
||||
@@ -184,6 +206,7 @@ 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>
|
||||
@@ -245,7 +268,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -302,6 +325,42 @@ 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">
|
||||
@@ -334,6 +393,40 @@ 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">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.3.0</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
@@ -389,12 +482,13 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<b>values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy</div>
|
||||
<div>This return value has been deprecated and will be removed in a release after 2027-01-08. Use RV(status.release_values) instead.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ Parameters
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th colspan="2">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<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="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>create_namespace</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -194,7 +194,32 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<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">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>disable_hook</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -213,7 +238,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>force</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -232,7 +257,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>history_max</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -249,7 +274,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>host</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -265,12 +290,35 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>insecure_skip_tls_verify</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 5.3.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Skip tls certificate checks for the chart download.</div>
|
||||
<div>Do not confuse with the <code>validate_certs</code> option.</div>
|
||||
<div>This option is only available for helm >= 3.16.0.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: skip_tls_certs_check</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>kubeconfig</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: purple">raw</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -278,11 +326,49 @@ 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="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plain_http</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use HTTP instead of HTTPS when working with OCI registries</div>
|
||||
<div>Requires Helm >= 3.13.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<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">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>purge</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -301,7 +387,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_name</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -318,7 +404,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_namespace</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -335,7 +421,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_state</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -355,7 +441,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -372,7 +458,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>replace</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -394,7 +480,130 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>reset_then_reuse_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.0.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>When upgrading package, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from parameters O(release_values), O(values_files) or O(set_values).</div>
|
||||
<div>If O(reset_values) or O(reuse_values) is set to V(True), this is ignored.</div>
|
||||
<div>This feature requires helm diff >= 3.9.12.</div>
|
||||
</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 3.0.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 3.0.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>
|
||||
@@ -414,7 +623,49 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>skip_schema_validation</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.2.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Disables JSON schema validation for Chart and values.</div>
|
||||
<div>This feature requires helm >= 3.16.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>take_ownership</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Helm will ignore the check for helm annotations and take ownership of the existing resources</div>
|
||||
<div>This feature requires helm >= 3.17.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>timeout</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -432,7 +683,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>update_repo_cache</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -451,7 +702,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>validate_certs</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -472,7 +723,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>values_files</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -493,7 +744,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>wait</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -513,7 +764,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>wait_timeout</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -594,6 +845,15 @@ 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:
|
||||
@@ -613,6 +873,12 @@ Examples
|
||||
chart_ref: "https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz"
|
||||
release_namespace: monitoring
|
||||
|
||||
- name: Deploy Bitnami's MongoDB latest chart from OCI registry
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: "oci://registry-1.docker.io/bitnamicharts/mongodb"
|
||||
release_namespace: database
|
||||
|
||||
# Using complex Values
|
||||
- name: Deploy new-relic client chart
|
||||
kubernetes.core.helm:
|
||||
@@ -639,11 +905,22 @@ 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
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -749,6 +1026,23 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.3.0</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"> </td>
|
||||
<td colspan="1">
|
||||
@@ -804,12 +1098,13 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
||||
<b>values</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Dict of Values used to deploy</div>
|
||||
<div>Dict of Values used to deploy.</div>
|
||||
<div>This return value has been deprecated and will be removed in a release after 2027-01-08. Use RV(status.release_values) instead.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -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">path</span>
|
||||
<span style="color: purple">raw</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -134,6 +134,7 @@ 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>
|
||||
@@ -195,7 +196,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -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">path</span>
|
||||
<span style="color: purple">raw</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -134,6 +134,7 @@ 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>
|
||||
@@ -230,6 +231,28 @@ Parameters
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>verify</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>no</li>
|
||||
<li><div style="color: blue"><b>yes</b> ←</div></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Verify the plugin signature before installing.</div>
|
||||
<div>This option requires helm version >= 4.0.0</div>
|
||||
<div>Used with <em>state=present</em>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
@@ -271,7 +294,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
545
docs/kubernetes.core.helm_pull_module.rst
Normal file
545
docs/kubernetes.core.helm_pull_module.rst
Normal file
@@ -0,0 +1,545 @@
|
||||
.. _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.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>force</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.3.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Force download of the chart even if it already exists in the destination directory.</div>
|
||||
<div>By default, the module will skip downloading if the chart with the same version already exists for idempotency.</div>
|
||||
<div>When used with O(untar_chart=true), will remove any existing chart directory before extracting.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<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>plain_http</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use HTTP instead of HTTPS when working with OCI registries</div>
|
||||
<div>Requires Helm >= 3.13.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<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. Alias <code>insecure_skip_tls_verify</code> added in 5.3.0.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: insecure_skip_tls_verify</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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
|
||||
|
||||
- name: Download Chart (force re-download even if exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
force: yes
|
||||
|
||||
- name: Download and untar chart (force re-extraction even if directory exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
untar_chart: yes
|
||||
force: yes
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/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>msg</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>when chart already exists</td>
|
||||
<td>
|
||||
<div>A message indicating the result of the operation.</div>
|
||||
<br/>
|
||||
<div style="font-size: smaller"><b>Sample:</b></div>
|
||||
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">Chart redis version 17.0.0 already exists in destination directory</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<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)
|
||||
354
docs/kubernetes.core.helm_registry_auth_module.rst
Normal file
354
docs/kubernetes.core.helm_registry_auth_module.rst
Normal file
@@ -0,0 +1,354 @@
|
||||
.. _kubernetes.core.helm_registry_auth_module:
|
||||
|
||||
|
||||
**********************************
|
||||
kubernetes.core.helm_registry_auth
|
||||
**********************************
|
||||
|
||||
**Helm registry authentication module**
|
||||
|
||||
|
||||
Version added: 5.1.0
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
- Helm registry authentication module allows you to login ``helm registry login`` and logout ``helm registry logout`` from a Helm registry.
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases) >= 3.8.0
|
||||
|
||||
|
||||
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>ca_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>Path to the CA certificate SSL file for verify registry server certificate.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>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>Path to the client certificate SSL file for identify registry client using this certificate file.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>host</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>Provide a URL for accessing the registry.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: registry_url</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>insecure</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>Allow connections to SSL sites without certs.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>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>Path to the client key SSL file for identify registry client using this key file.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>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>Password for the registry.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: repo_password</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plain_http</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use insecure HTTP connections for <code>helm registry login</code>.</div>
|
||||
<div>Requires Helm >= 3.18.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>state</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>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>present</b> ←</div></li>
|
||||
<li>absent</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Desired state of the registry.</div>
|
||||
<div>If set to V(present) attempt to log in to the remote registry server using the URL specified in O(host).</div>
|
||||
<div>If set to V(absent) attempt to log out from the remote registry server using the URL specified in O(host).</div>
|
||||
<div>As helm >= 3.18.0 reports successful logout even if the user is not logged in, this module will report a change regardless of the current state.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>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>Username for the registry.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: repo_username</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- name: Login to remote registry
|
||||
kubernetes.core.helm_registry_auth:
|
||||
username: admin
|
||||
password: "sample_password"
|
||||
host: localhost:5000
|
||||
|
||||
- name: Logout from remote registry
|
||||
kubernetes.core.helm_registry_auth:
|
||||
state: absent
|
||||
host: localhost:5000
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<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 <code>helm</code> command executed</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;">/usr/local/bin/helm registry login oci-registry.domain.example --username=admin --password-stdin --insecure</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>failed</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Indicate if the <code>helm</code> command failed</div>
|
||||
<br/>
|
||||
</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 <code>helm</code> command stderr, in case you want to display it or examine the event log. Please be note that helm binnary may print messages to stderr even if the command is successful.</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;">Login Succeeded\n</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||
<b>stderr_lines</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Full <code>helm</code> 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 <code>helm</code> command stdout, 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>stout_lines</b>
|
||||
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>always</td>
|
||||
<td>
|
||||
<div>Full <code>helm</code> command stdout, in case you want to display it or examine the event log</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/><br/>
|
||||
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
- Yuriy Novostavskiy (@yurnov)
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm (https://github.com/helm/helm/releases)
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
@@ -88,6 +88,45 @@ 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>
|
||||
@@ -104,6 +143,46 @@ Parameters
|
||||
<div>Provide a URL for accessing the API. Can also be specified via <code>K8S_AUTH_HOST</code> environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>insecure_skip_tls_verify</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 5.3.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Skip tls certificate checks for the repository url.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: skip_tls_certs_check</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<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>
|
||||
@@ -257,7 +336,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -20,6 +20,13 @@ Synopsis
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- helm >= 3.0.0 (https://github.com/helm/helm/releases)
|
||||
- yaml (https://pypi.org/project/PyYAML/)
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -28,12 +35,12 @@ Parameters
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th colspan="2">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>binary_path</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -48,7 +55,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_ref</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -67,7 +74,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_repo_url</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -82,7 +89,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>chart_version</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -97,7 +104,50 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<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">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>include_crds</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -116,7 +166,27 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>insecure_registry</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 5.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Skip TLS certificate checks for the chart download</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>output_dir</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -132,14 +202,52 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plain_http</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">boolean</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li><div style="color: blue"><b>no</b> ←</div></li>
|
||||
<li>yes</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Use HTTP instead of HTTPS when working with OCI registries</div>
|
||||
<div>Requires Helm >= 3.13.0</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<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">
|
||||
<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.3.0</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
@@ -148,7 +256,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>release_values</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -165,7 +273,67 @@ 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>
|
||||
@@ -173,16 +341,17 @@ 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.3.0</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.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="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>update_repo_cache</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -201,7 +370,7 @@ Parameters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>values_files</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
@@ -268,7 +437,7 @@ Examples
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
Common return values are documented `here <https://docs.ansible.com/projects/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -203,6 +203,7 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -215,6 +215,7 @@ 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>
|
||||
@@ -511,6 +512,7 @@ Notes
|
||||
|
||||
.. note::
|
||||
- the tar binary is required on the container when copying from local filesystem to pod.
|
||||
- the (init) container has to be started before you copy files or directories to it.
|
||||
- To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
|
||||
|
||||
|
||||
@@ -557,7 +559,7 @@ Examples
|
||||
state: from_pod
|
||||
|
||||
# copy content into a file in the remote pod
|
||||
- name: Copy /tmp/foo from a remote pod to /tmp/bar locally
|
||||
- name: Copy content into a file in the remote pod
|
||||
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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -132,6 +132,7 @@ Parameters
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">{}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>Specify options to delete pods.</div>
|
||||
@@ -335,6 +336,7 @@ 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>
|
||||
@@ -412,6 +414,25 @@ 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 3.0.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>
|
||||
@@ -574,14 +595,15 @@ Examples
|
||||
kubernetes.core.k8s_drain:
|
||||
state: drain
|
||||
name: foo
|
||||
force: yes
|
||||
delete_options:
|
||||
force: yes
|
||||
|
||||
- name: Drain node "foo", but abort if there are pods not managed by a ReplicationController, Job, or DaemonSet, and use a grace period of 15 minutes.
|
||||
kubernetes.core.k8s_drain:
|
||||
state: drain
|
||||
name: foo
|
||||
delete_options:
|
||||
terminate_grace_period: 900
|
||||
terminate_grace_period: 900
|
||||
|
||||
- name: Mark node "foo" as schedulable.
|
||||
kubernetes.core.k8s_drain:
|
||||
@@ -593,6 +615,14 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -215,6 +215,7 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -153,11 +153,30 @@ 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 3.0.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> or V(hidden_fields=[spec.containers[0].env[3].value]) or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -240,6 +259,7 @@ 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,6 +274,7 @@ 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>
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
.. _kubernetes.core.k8s_inventory:
|
||||
|
||||
|
||||
*******************
|
||||
kubernetes.core.k8s
|
||||
*******************
|
||||
|
||||
**Kubernetes (K8s) inventory source**
|
||||
|
||||
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
- Fetch containers and services for one or more clusters.
|
||||
- Groups by cluster name, namespace, namespace_services, namespace_pods, and labels.
|
||||
- Uses the kubectl connection plugin to access the Kubernetes cluster.
|
||||
- Uses k8s.(yml|yaml) YAML configuration file to set parameter values.
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
The below requirements are needed on the local Ansible controller node that executes this inventory.
|
||||
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="2">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th>Configuration</th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>connections</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Optional list of cluster connection settings. If no connections are provided, the default <em>~/.kube/config</em> and active context will be used, and objects will be returned for all namespaces the active user is authorized to access.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>api_key</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>ca_cert</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to a CA certificate used to authenticate with the API. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>client_cert</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>client_key</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<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">-</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>host</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<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">-</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>name</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Optional name to assign to the cluster. If not provided, a name is constructed from the server and port.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>namespaces</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized to access.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>password</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>username</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>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="elbow-placeholder"></td>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>validate_certs</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>
|
||||
</td>
|
||||
<td>
|
||||
<div>Whether or not to verify the API server's SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>plugin</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">-</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>kubernetes.core.k8s</li>
|
||||
<li>k8s</li>
|
||||
<li>community.kubernetes.k8s</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>token that ensures this is a source file for the 'k8s' plugin.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# File must be named k8s.yaml or k8s.yml
|
||||
|
||||
# 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
|
||||
|
||||
# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- namespaces:
|
||||
- testing
|
||||
|
||||
# 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'
|
||||
|
||||
|
||||
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
- Chris Houseknecht (@chouseknecht)
|
||||
- Fabian von Feilitzsch (@fabianvf)
|
||||
|
||||
|
||||
.. hint::
|
||||
Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
|
||||
@@ -27,8 +27,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
- jsonpatch
|
||||
|
||||
@@ -140,6 +140,25 @@ Parameters
|
||||
<div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>hidden_fields</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">list</span>
|
||||
/ <span style="color: purple">elements=string</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">[]</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>List of fields to hide from the diff output.</div>
|
||||
<div>This is useful for fields that are not relevant to the patch operation, such as `metadata.managedFields`.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -220,6 +239,7 @@ 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>
|
||||
@@ -613,7 +633,7 @@ Examples
|
||||
path: /metadata/labels/app
|
||||
value: myapp
|
||||
- op: replace
|
||||
patch: /spec/containers/0/image
|
||||
path: /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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -44,6 +44,27 @@ 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>
|
||||
@@ -141,7 +162,8 @@ 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.</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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -240,6 +262,7 @@ 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,6 +277,7 @@ 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>
|
||||
@@ -353,6 +377,26 @@ 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>
|
||||
@@ -454,6 +498,22 @@ 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>
|
||||
@@ -538,8 +598,16 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ Requirements
|
||||
------------
|
||||
The below requirements are needed on the host that executes this module.
|
||||
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
- jsonpatch
|
||||
|
||||
@@ -121,7 +121,7 @@ Parameters
|
||||
<td>
|
||||
<div><code>apply</code> compares the desired resource definition with the previously supplied resource definition, ignoring properties that are automatically generated</div>
|
||||
<div><code>apply</code> works better with Services than 'force=yes'</div>
|
||||
<div>mutually exclusive with <code>merge_type</code></div>
|
||||
<div>Mutually exclusive with <code>merge_type</code>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -208,6 +208,30 @@ 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 3.0.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>
|
||||
@@ -356,6 +380,24 @@ 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 3.0.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> or V(hidden_fields=[spec.containers[0].env[3].value]) or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -437,6 +479,7 @@ 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>
|
||||
@@ -469,18 +512,17 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>json</li>
|
||||
<li>merge</li>
|
||||
<li>strategic-merge</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Whether to override the default patch merge approach with a specific type. By default, the strategic merge will typically be used.</div>
|
||||
<div>For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may want to use <code>merge</code> if you see "strategic merge patch format is not supported"</div>
|
||||
<div>For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may want to use <code>merge</code> if you see "strategic merge patch format is not supported".</div>
|
||||
<div>See <a href='https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment'>https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment</a></div>
|
||||
<div>If more than one <code>merge_type</code> is given, the merge_types will be tried in order. This defaults to <code>['strategic-merge', 'merge']</code>, which is ideal for using the same parameters on resource kinds that combine Custom Resources and built-in resources.</div>
|
||||
<div>mutually exclusive with <code>apply</code></div>
|
||||
<div><em>merge_type=json</em> is deprecated and will be removed in version 3.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div>
|
||||
<div>Mutually exclusive with <code>apply</code>.</div>
|
||||
<div><em>merge_type=json</em> has been removed in version 4.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -750,6 +792,7 @@ 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>
|
||||
@@ -1112,6 +1155,14 @@ 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
|
||||
@@ -1129,10 +1180,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:
|
||||
@@ -1216,6 +1267,14 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ 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>
|
||||
@@ -238,6 +239,7 @@ 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>
|
||||
@@ -252,6 +254,7 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
- PyYAML >= 3.11
|
||||
|
||||
|
||||
@@ -255,6 +255,7 @@ 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>
|
||||
@@ -270,6 +271,7 @@ 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>
|
||||
@@ -516,6 +518,7 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -220,6 +220,7 @@ 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>
|
||||
@@ -487,6 +488,7 @@ 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.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
|
||||
|
||||
Parameters
|
||||
@@ -182,6 +182,7 @@ 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,6 +208,28 @@ 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>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>kubectl_local_env_vars</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">dictionary</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 3.1.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">{}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>var: ansible_kubectl_local_env_vars</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>Local enviromantal variable to be passed locally to the kubectl command line.</div>
|
||||
<div>Please be aware that this passes information directly on the command line and it could expose sensitive data.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -343,6 +365,82 @@ Parameters
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- name: Run a command in a pod using local kubectl with kubeconfig file ~/.kube/config
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
vars:
|
||||
ansible_connection: kubernetes.core.kubectl
|
||||
ansible_kubectl_namespace: my-namespace
|
||||
ansible_kubectl_pod: my-pod
|
||||
ansible_kubectl_container: my-container
|
||||
tasks:
|
||||
# be aware that the command is executed as the user that started the container
|
||||
# and requires python to be installed in the image
|
||||
- name: Run a command in a pod
|
||||
ansible.builtin.command: echo "Hello, World!"
|
||||
|
||||
- name: Run a command in a pod using local kubectl with inventory variables
|
||||
# Example inventory:
|
||||
# k8s:
|
||||
# hosts:
|
||||
# foo.example.com:
|
||||
# ansible_connection: kubernetes.core.kubectl
|
||||
# ansible_kubectl_kubeconfig: /root/.kube/foo.example.com.config
|
||||
# ansible_kubectl_pod: my-foo-pod
|
||||
# ansible_kubectl_container: my-foo-container
|
||||
# ansible_kubectl_namespace: my-foo-namespace
|
||||
# bar.example.com:
|
||||
# ansible_connection: kubernetes.core.kubectl
|
||||
# ansible_kubectl_kubeconfig: /root/.kube/bar.example.com.config
|
||||
# ansible_kubectl_pod: my-bar-pod
|
||||
# ansible_kubectl_container: my-bar-container
|
||||
# ansible_kubectl_namespace: my-bar-namespace
|
||||
hosts: k8s
|
||||
gather_facts: no
|
||||
tasks:
|
||||
# be aware that the command is executed as the user that started the container
|
||||
# and requires python to be installed in the image
|
||||
- name: Run a command in a pod
|
||||
ansible.builtin.command: echo "Hello, World!"
|
||||
|
||||
- name: Run a command in a pod using dynamic inventory
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
vars:
|
||||
kubeconfig: /root/.kube/config
|
||||
namespace: my-namespace
|
||||
my_app: my-app
|
||||
tasks:
|
||||
- name: Get My App pod info based on label
|
||||
kubernetes.core.k8s_info:
|
||||
kubeconfig: "{{ kubeconfig }}"
|
||||
namespace: "{{ namespace }}"
|
||||
kind: Pod
|
||||
label_selectors: app.kubernetes.io/name = "{{ my_app }}"
|
||||
register: my_app_pod
|
||||
|
||||
- name: Get My App pod name
|
||||
ansible.builtin.set_fact:
|
||||
my_app_pod_name: "{{ my_app_pod.resources[0].metadata.name }}"
|
||||
|
||||
- name: Add My App pod to inventory
|
||||
ansible.builtin.add_host:
|
||||
name: "{{ my_app_pod_name }}"
|
||||
ansible_connection: kubernetes.core.kubectl
|
||||
ansible_kubectl_kubeconfig: "{{ kubeconfig }}"
|
||||
ansible_kubectl_pod: "{{ my_app_pod_name }}"
|
||||
ansible_kubectl_namespace: "{{ namespace }}"
|
||||
|
||||
- name: Run a command in My App pod
|
||||
# be aware that the command is executed as the user that started the container
|
||||
# and requires python to be installed in the image
|
||||
ansible.builtin.command: echo "Hello, World!"
|
||||
delegate_to: "{{ my_app_pod_name }}"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -77,6 +77,44 @@ Parameters
|
||||
<div>If omitted, '.' is assumed.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>enable_helm</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>
|
||||
<b>Default:</b><br/><div style="color: blue">"False"</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Enable the helm chart inflation generator</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>environment</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">raw</span>
|
||||
</div>
|
||||
<div style="font-style: italic; font-size: small; color: darkgreen">added in 6.2.0</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">{}</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The environment variables to pass to the kustomize or kubectl command.</div>
|
||||
<div>This can be a dictionary or a string in the format key=value, multiple pairs separated by space.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
@@ -112,17 +150,29 @@ Examples
|
||||
.. code-block:: yaml
|
||||
|
||||
- name: Run lookup using kustomize
|
||||
set_fact:
|
||||
ansible.builtin.set_fact:
|
||||
resources: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kustomize') }}"
|
||||
|
||||
- name: Run lookup using kubectl kustomize
|
||||
set_fact:
|
||||
ansible.builtin.set_fact:
|
||||
resources: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output
|
||||
k8s:
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', dir='/path/to/kustomization') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with `--enable-helm` set
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', dir='/path/to/kustomization', enable_helm=True) }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in string format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment='HTTP_PROXY=http://proxy.example.com:3128') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in dict format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment={'HTTP_PROXY': 'http://proxy.example.com:3128'}) }}"
|
||||
|
||||
|
||||
|
||||
Return Values
|
||||
|
||||
@@ -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: 2.3.1
|
||||
version: 6.4.0
|
||||
build_ignore:
|
||||
- .DS_Store
|
||||
- '*.tar.gz'
|
||||
- "*.tar.gz"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
requires_ansible: '>=2.9.17'
|
||||
requires_ansible: '>=2.16.0'
|
||||
|
||||
action_groups:
|
||||
helm:
|
||||
@@ -8,18 +8,26 @@ action_groups:
|
||||
- helm_repository
|
||||
k8s:
|
||||
- k8s
|
||||
- k8s_exec
|
||||
- k8s_info
|
||||
- k8s_log
|
||||
- k8s_scale
|
||||
- k8s_service
|
||||
- k8s_cluster_info
|
||||
- k8s_cp
|
||||
- k8s_drain
|
||||
- k8s_exec
|
||||
- k8s_info
|
||||
- k8s_json_patch
|
||||
- k8s_log
|
||||
- k8s_rollback
|
||||
- k8s_scale
|
||||
- k8s_service
|
||||
|
||||
plugin_routing:
|
||||
inventory:
|
||||
openshift:
|
||||
redirect: community.okd.openshift
|
||||
k8s:
|
||||
tombstone:
|
||||
removal_version: 6.0.0
|
||||
warning_text: >-
|
||||
The k8s inventory plugin was slated for deprecation in 3.3.0 and has been removed in release 6.0.0. Use kubernetes.core.k8s_info and ansible.builtin.add_host instead.
|
||||
modules:
|
||||
k8s_auth:
|
||||
redirect: community.okd.k8s_auth
|
||||
|
||||
@@ -8,50 +8,40 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import copy
|
||||
import traceback
|
||||
import os
|
||||
import platform
|
||||
import traceback
|
||||
from contextlib import contextmanager
|
||||
|
||||
from ansible.config.manager import ensure_type
|
||||
from ansible.errors import (
|
||||
AnsibleError,
|
||||
AnsibleFileNotFound,
|
||||
AnsibleAction,
|
||||
AnsibleActionFail,
|
||||
AnsibleError,
|
||||
AnsibleFileNotFound,
|
||||
)
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.module_utils.six import string_types, iteritems
|
||||
from ansible.module_utils._text import to_text, to_bytes, to_native
|
||||
from ansible.plugins.action import ActionBase
|
||||
|
||||
try:
|
||||
from ansible.template import trust_as_template
|
||||
except ImportError:
|
||||
trust_as_template = None
|
||||
|
||||
class RemoveOmit(object):
|
||||
def __init__(self, buffer, omit_value):
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
raise AnsibleError("Failed to import the required Python library (PyYAML).")
|
||||
self.data = yaml.safe_load_all(buffer)
|
||||
self.omit = omit_value
|
||||
|
||||
def remove_omit(self, data):
|
||||
if isinstance(data, dict):
|
||||
result = dict()
|
||||
for key, value in iteritems(data):
|
||||
if value == self.omit:
|
||||
continue
|
||||
result[key] = self.remove_omit(value)
|
||||
return result
|
||||
if isinstance(data, list):
|
||||
return [self.remove_omit(v) for v in data if v != self.omit]
|
||||
return data
|
||||
def _from_yaml_to_definition(buffer):
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
raise AnsibleError("Failed to import the required Python library (PyYAML).")
|
||||
return list(yaml.safe_load_all(buffer))
|
||||
|
||||
def output(self):
|
||||
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"
|
||||
|
||||
@@ -109,7 +99,7 @@ class ActionModule(ActionBase):
|
||||
"trim_blocks": True,
|
||||
"lstrip_blocks": False,
|
||||
}
|
||||
if isinstance(template, string_types):
|
||||
if isinstance(template, str):
|
||||
# treat this as raw_params
|
||||
template_param["path"] = template
|
||||
elif isinstance(template, dict):
|
||||
@@ -129,7 +119,7 @@ class ActionModule(ActionBase):
|
||||
):
|
||||
if s_type in template_args:
|
||||
value = ensure_type(template_args[s_type], "string")
|
||||
if value is not None and not isinstance(value, string_types):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise AnsibleActionFail(
|
||||
"%s is expected to be a string, but got %s instead"
|
||||
% (s_type, type(value))
|
||||
@@ -204,9 +194,8 @@ class ActionModule(ActionBase):
|
||||
"'template' is only a supported parameter for the 'k8s' module."
|
||||
)
|
||||
|
||||
omit_value = task_vars.get("omit")
|
||||
template_params = []
|
||||
if isinstance(template, string_types) or isinstance(template, dict):
|
||||
if isinstance(template, str) or isinstance(template, dict):
|
||||
template_params.append(self.get_template_args(template))
|
||||
elif isinstance(template, list):
|
||||
for element in template:
|
||||
@@ -227,17 +216,18 @@ class ActionModule(ActionBase):
|
||||
old_vars = self._templar.available_variables
|
||||
|
||||
default_environment = {}
|
||||
for key in (
|
||||
"newline_sequence",
|
||||
"variable_start_string",
|
||||
"variable_end_string",
|
||||
"block_start_string",
|
||||
"block_end_string",
|
||||
"trim_blocks",
|
||||
"lstrip_blocks",
|
||||
):
|
||||
if hasattr(self._templar.environment, key):
|
||||
default_environment[key] = getattr(self._templar.environment, key)
|
||||
if trust_as_template is None:
|
||||
for key in (
|
||||
"newline_sequence",
|
||||
"variable_start_string",
|
||||
"variable_end_string",
|
||||
"block_start_string",
|
||||
"block_end_string",
|
||||
"trim_blocks",
|
||||
"lstrip_blocks",
|
||||
):
|
||||
if hasattr(self._templar.environment, key):
|
||||
default_environment[key] = getattr(self._templar.environment, key)
|
||||
for template_item in template_params:
|
||||
# We need to convert unescaped sequences to proper escaped sequences for Jinja2
|
||||
newline_sequence = template_item["newline_sequence"]
|
||||
@@ -254,26 +244,35 @@ class ActionModule(ActionBase):
|
||||
with self.get_template_data(template_item["path"]) as template_data:
|
||||
# add ansible 'template' vars
|
||||
temp_vars = copy.deepcopy(task_vars)
|
||||
for key, value in iteritems(template_item):
|
||||
overrides = {}
|
||||
for key, value in template_item.items():
|
||||
if hasattr(self._templar.environment, key):
|
||||
if value is not None:
|
||||
setattr(self._templar.environment, key, value)
|
||||
else:
|
||||
overrides[key] = value
|
||||
if trust_as_template is None:
|
||||
setattr(self._templar.environment, key, value)
|
||||
elif trust_as_template is None:
|
||||
setattr(
|
||||
self._templar.environment,
|
||||
key,
|
||||
default_environment.get(key),
|
||||
)
|
||||
self._templar.available_variables = temp_vars
|
||||
result = self._templar.do_template(
|
||||
template_data,
|
||||
preserve_trailing_newlines=True,
|
||||
escape_backslashes=False,
|
||||
)
|
||||
if omit_value is not None:
|
||||
result_template.extend(RemoveOmit(result, omit_value).output())
|
||||
if trust_as_template:
|
||||
template_data = trust_as_template(template_data)
|
||||
result = self._templar.template(
|
||||
template_data,
|
||||
preserve_trailing_newlines=True,
|
||||
escape_backslashes=False,
|
||||
overrides=overrides,
|
||||
)
|
||||
else:
|
||||
result_template.append(result)
|
||||
result = self._templar.do_template(
|
||||
template_data,
|
||||
preserve_trailing_newlines=True,
|
||||
escape_backslashes=False,
|
||||
)
|
||||
result_template.extend(_from_yaml_to_definition(result))
|
||||
self._templar.available_variables = old_vars
|
||||
resource_definition = self._task.args.get("definition", None)
|
||||
if not resource_definition:
|
||||
@@ -303,16 +302,20 @@ class ActionModule(ActionBase):
|
||||
)
|
||||
|
||||
def get_kubeconfig(self, kubeconfig, remote_transport, new_module_args):
|
||||
if isinstance(kubeconfig, string_types):
|
||||
if isinstance(kubeconfig, str):
|
||||
# find the kubeconfig in the expected search path
|
||||
if not remote_transport:
|
||||
# kubeconfig is local
|
||||
# find in expected paths
|
||||
kubeconfig = self._find_needle("files", kubeconfig)
|
||||
configs = []
|
||||
for config in kubeconfig.split(ENV_KUBECONFIG_PATH_SEPARATOR):
|
||||
config = self._find_needle("files", config)
|
||||
|
||||
# decrypt kubeconfig found
|
||||
actual_file = self._loader.get_real_file(kubeconfig, decrypt=True)
|
||||
new_module_args["kubeconfig"] = actual_file
|
||||
# decrypt kubeconfig found
|
||||
configs.append(self._loader.get_real_file(config, decrypt=True))
|
||||
new_module_args["kubeconfig"] = ENV_KUBECONFIG_PATH_SEPARATOR.join(
|
||||
configs
|
||||
)
|
||||
|
||||
elif isinstance(kubeconfig, dict):
|
||||
new_module_args["kubeconfig"] = kubeconfig
|
||||
@@ -351,7 +354,7 @@ class ActionModule(ActionBase):
|
||||
# find the file in the expected search path
|
||||
src = self._task.args.get("src", None)
|
||||
|
||||
if src:
|
||||
if src and not src.startswith(("http://", "https://", "ftp://")):
|
||||
if remote_transport:
|
||||
# src is on remote node
|
||||
result.update(
|
||||
|
||||
@@ -72,9 +72,19 @@ DOCUMENTATION = r"""
|
||||
- name: ansible_kubectl_extra_args
|
||||
env:
|
||||
- name: K8S_AUTH_EXTRA_ARGS
|
||||
kubectl_local_env_vars:
|
||||
description:
|
||||
- Local enviromantal variable to be passed locally to the kubectl command line.
|
||||
- Please be aware that this passes information directly on the command line and it could expose sensitive data.
|
||||
default: {}
|
||||
type: dict
|
||||
version_added: 3.1.0
|
||||
vars:
|
||||
- name: ansible_kubectl_local_env_vars
|
||||
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
|
||||
@@ -171,16 +181,94 @@ DOCUMENTATION = r"""
|
||||
aliases: [ kubectl_verify_ssl ]
|
||||
"""
|
||||
|
||||
import distutils.spawn
|
||||
EXAMPLES = r"""
|
||||
|
||||
- name: Run a command in a pod using local kubectl with kubeconfig file ~/.kube/config
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
vars:
|
||||
ansible_connection: kubernetes.core.kubectl
|
||||
ansible_kubectl_namespace: my-namespace
|
||||
ansible_kubectl_pod: my-pod
|
||||
ansible_kubectl_container: my-container
|
||||
tasks:
|
||||
# be aware that the command is executed as the user that started the container
|
||||
# and requires python to be installed in the image
|
||||
- name: Run a command in a pod
|
||||
ansible.builtin.command: echo "Hello, World!"
|
||||
|
||||
- name: Run a command in a pod using local kubectl with inventory variables
|
||||
# Example inventory:
|
||||
# k8s:
|
||||
# hosts:
|
||||
# foo.example.com:
|
||||
# ansible_connection: kubernetes.core.kubectl
|
||||
# ansible_kubectl_kubeconfig: /root/.kube/foo.example.com.config
|
||||
# ansible_kubectl_pod: my-foo-pod
|
||||
# ansible_kubectl_container: my-foo-container
|
||||
# ansible_kubectl_namespace: my-foo-namespace
|
||||
# bar.example.com:
|
||||
# ansible_connection: kubernetes.core.kubectl
|
||||
# ansible_kubectl_kubeconfig: /root/.kube/bar.example.com.config
|
||||
# ansible_kubectl_pod: my-bar-pod
|
||||
# ansible_kubectl_container: my-bar-container
|
||||
# ansible_kubectl_namespace: my-bar-namespace
|
||||
hosts: k8s
|
||||
gather_facts: no
|
||||
tasks:
|
||||
# be aware that the command is executed as the user that started the container
|
||||
# and requires python to be installed in the image
|
||||
- name: Run a command in a pod
|
||||
ansible.builtin.command: echo "Hello, World!"
|
||||
|
||||
- name: Run a command in a pod using dynamic inventory
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
vars:
|
||||
kubeconfig: /root/.kube/config
|
||||
namespace: my-namespace
|
||||
my_app: my-app
|
||||
tasks:
|
||||
- name: Get My App pod info based on label
|
||||
kubernetes.core.k8s_info:
|
||||
kubeconfig: "{{ kubeconfig }}"
|
||||
namespace: "{{ namespace }}"
|
||||
kind: Pod
|
||||
label_selectors: app.kubernetes.io/name = "{{ my_app }}"
|
||||
register: my_app_pod
|
||||
|
||||
- name: Get My App pod name
|
||||
ansible.builtin.set_fact:
|
||||
my_app_pod_name: "{{ my_app_pod.resources[0].metadata.name }}"
|
||||
|
||||
- name: Add My App pod to inventory
|
||||
ansible.builtin.add_host:
|
||||
name: "{{ my_app_pod_name }}"
|
||||
ansible_connection: kubernetes.core.kubectl
|
||||
ansible_kubectl_kubeconfig: "{{ kubeconfig }}"
|
||||
ansible_kubectl_pod: "{{ my_app_pod_name }}"
|
||||
ansible_kubectl_namespace: "{{ namespace }}"
|
||||
|
||||
- name: Run a command in My App pod
|
||||
# be aware that the command is executed as the user that started the container
|
||||
# and requires python to be installed in the image
|
||||
ansible.builtin.command: echo "Hello, World!"
|
||||
delegate_to: "{{ my_app_pod_name }}"
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.errors import AnsibleError, AnsibleFileNotFound
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.plugins.connection import ConnectionBase, BUFSIZE
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.plugins.connection import BUFSIZE, ConnectionBase
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
@@ -219,14 +307,15 @@ class Connection(ConnectionBase):
|
||||
# Note: kubectl runs commands as the user that started the container.
|
||||
# It is impossible to set the remote user for a kubectl connection.
|
||||
cmd_arg = "{0}_command".format(self.transport)
|
||||
if cmd_arg in kwargs:
|
||||
self.transport_cmd = kwargs[cmd_arg]
|
||||
else:
|
||||
self.transport_cmd = distutils.spawn.find_executable(self.transport)
|
||||
if not self.transport_cmd:
|
||||
raise AnsibleError(
|
||||
"{0} command not found in PATH".format(self.transport)
|
||||
)
|
||||
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"""
|
||||
@@ -236,9 +325,12 @@ class Connection(ConnectionBase):
|
||||
# Build command options based on doc string
|
||||
doc_yaml = AnsibleLoader(self.documentation).get_single_data()
|
||||
for key in doc_yaml.get("options"):
|
||||
if key.endswith("verify_ssl") and self.get_option(key) != "":
|
||||
# Translate verify_ssl to skip_verify_ssl, and output as string
|
||||
skip_verify_ssl = not self.get_option(key)
|
||||
if key == "validate_certs" and self.get_option(key) != "":
|
||||
# Translate validate_certs to --insecure-skip-tls-verify flag
|
||||
# validate_certs=True means verify certs (don't skip verification)
|
||||
# validate_certs=False means don't verify certs (skip verification)
|
||||
validate_certs_value = boolean(self.get_option(key), strict=False)
|
||||
skip_verify_ssl = not validate_certs_value
|
||||
local_cmd.append(
|
||||
"{0}={1}".format(
|
||||
self.connection_options[key], str(skip_verify_ssl).lower()
|
||||
@@ -249,6 +341,18 @@ 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)
|
||||
@@ -285,6 +389,19 @@ class Connection(ConnectionBase):
|
||||
|
||||
return local_cmd, censored_local_cmd
|
||||
|
||||
def _local_env(self):
|
||||
"""Return a dict of local environment variables to pass to the kubectl command"""
|
||||
local_env = {}
|
||||
local_local_env_vars_name = "{0}_local_env_vars".format(self.transport)
|
||||
local_env_vars = self.get_option(local_local_env_vars_name)
|
||||
if local_env_vars:
|
||||
if isinstance(local_env_vars, dict):
|
||||
local_env_vars = json.dumps(local_env_vars)
|
||||
local_env = os.environ.copy()
|
||||
local_env.update(json.loads(local_env_vars))
|
||||
return local_env
|
||||
return None
|
||||
|
||||
def _connect(self, port=None):
|
||||
"""Connect to the container. Nothing to do"""
|
||||
super(Connection, self)._connect()
|
||||
@@ -313,9 +430,11 @@ class Connection(ConnectionBase):
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=self._local_env(),
|
||||
)
|
||||
|
||||
stdout, stderr = p.communicate(in_data)
|
||||
self.delete_temporary_file()
|
||||
return (p.returncode, stdout, stderr)
|
||||
|
||||
def _prefix_login_path(self, remote_path):
|
||||
@@ -361,13 +480,18 @@ class Connection(ConnectionBase):
|
||||
args = [to_bytes(i, errors="surrogate_or_strict") for i in args]
|
||||
try:
|
||||
p = subprocess.Popen(
|
||||
args, stdin=in_file, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
args,
|
||||
stdin=in_file,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=self._local_env(),
|
||||
)
|
||||
except OSError:
|
||||
raise AnsibleError(
|
||||
"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(
|
||||
@@ -397,7 +521,11 @@ class Connection(ConnectionBase):
|
||||
) as out_file:
|
||||
try:
|
||||
p = subprocess.Popen(
|
||||
args, stdin=subprocess.PIPE, stdout=out_file, stderr=subprocess.PIPE
|
||||
args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=out_file,
|
||||
stderr=subprocess.PIPE,
|
||||
env=self._local_env(),
|
||||
)
|
||||
except OSError:
|
||||
raise AnsibleError(
|
||||
@@ -406,6 +534,7 @@ class Connection(ConnectionBase):
|
||||
)
|
||||
)
|
||||
stdout, stderr = p.communicate()
|
||||
self.delete_temporary_file()
|
||||
|
||||
if p.returncode != 0:
|
||||
raise AnsibleError(
|
||||
|
||||
@@ -12,7 +12,6 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
binary_path:
|
||||
@@ -30,18 +29,19 @@ 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.
|
||||
type: path
|
||||
- The configuration can be provided as dictionary. Added in version 2.4.0.
|
||||
type: raw
|
||||
aliases: [ kubeconfig_path ]
|
||||
host:
|
||||
description:
|
||||
- Provide a URL for accessing the API. Can also be specified via C(K8S_AUTH_HOST) environment variable.
|
||||
type: str
|
||||
version_added: "1.2.0"
|
||||
version_added: 1.2.0
|
||||
api_key:
|
||||
description:
|
||||
- Token used to authenticate with the API. Can also be specified via C(K8S_AUTH_API_KEY) environment variable.
|
||||
type: str
|
||||
version_added: "1.2.0"
|
||||
version_added: 1.2.0
|
||||
validate_certs:
|
||||
description:
|
||||
- Whether or not to verify the API server's SSL certificates. Can also be specified via C(K8S_AUTH_VERIFY_SSL)
|
||||
@@ -49,12 +49,12 @@ options:
|
||||
type: bool
|
||||
aliases: [ verify_ssl ]
|
||||
default: True
|
||||
version_added: "1.2.0"
|
||||
version_added: 1.2.0
|
||||
ca_cert:
|
||||
description:
|
||||
- Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to
|
||||
avoid certificate validation errors. Can also be specified via C(K8S_AUTH_SSL_CA_CERT) environment variable.
|
||||
type: path
|
||||
aliases: [ ssl_ca_cert ]
|
||||
version_added: "1.2.0"
|
||||
version_added: 1.2.0
|
||||
"""
|
||||
|
||||
@@ -11,7 +11,6 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
host:
|
||||
@@ -28,6 +27,7 @@ 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,12 +11,11 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
delete_options:
|
||||
type: dict
|
||||
version_added: '1.2.0'
|
||||
version_added: 1.2.0
|
||||
description:
|
||||
- Configure behavior when deleting an object.
|
||||
- Only used when I(state=absent).
|
||||
|
||||
@@ -11,7 +11,6 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
api_version:
|
||||
|
||||
@@ -11,7 +11,6 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
resource_definition:
|
||||
@@ -29,6 +28,7 @@ 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,7 +11,6 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
replicas:
|
||||
|
||||
@@ -11,7 +11,6 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
state:
|
||||
|
||||
@@ -11,7 +11,6 @@ __metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
options:
|
||||
wait:
|
||||
|
||||
36
plugins/filter/k8s_config_resource_name.yml
Normal file
36
plugins/filter/k8s_config_resource_name.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
# 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
|
||||
@@ -1,463 +0,0 @@
|
||||
# Copyright (c) 2018 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 = """
|
||||
name: k8s
|
||||
author:
|
||||
- Chris Houseknecht (@chouseknecht)
|
||||
- Fabian von Feilitzsch (@fabianvf)
|
||||
|
||||
short_description: Kubernetes (K8s) inventory source
|
||||
|
||||
description:
|
||||
- Fetch containers and services for one or more clusters.
|
||||
- Groups by cluster name, namespace, namespace_services, namespace_pods, and labels.
|
||||
- Uses the kubectl connection plugin to access the Kubernetes cluster.
|
||||
- Uses k8s.(yml|yaml) YAML configuration file to set parameter values.
|
||||
|
||||
options:
|
||||
plugin:
|
||||
description: token that ensures this is a source file for the 'k8s' plugin.
|
||||
required: True
|
||||
choices: ['kubernetes.core.k8s', 'k8s', 'community.kubernetes.k8s']
|
||||
connections:
|
||||
description:
|
||||
- Optional list of cluster connection settings. If no connections are provided, the default
|
||||
I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces
|
||||
the active user is authorized to access.
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Optional name to assign to the cluster. If not provided, a name is constructed from the server
|
||||
and port.
|
||||
kubeconfig:
|
||||
description:
|
||||
- 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 I(~/.kube/config). Can also be specified via K8S_AUTH_KUBECONFIG
|
||||
environment variable.
|
||||
context:
|
||||
description:
|
||||
- The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment
|
||||
variable.
|
||||
host:
|
||||
description:
|
||||
- Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
|
||||
api_key:
|
||||
description:
|
||||
- Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment
|
||||
variable.
|
||||
username:
|
||||
description:
|
||||
- Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME
|
||||
environment variable.
|
||||
password:
|
||||
description:
|
||||
- Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD
|
||||
environment variable.
|
||||
client_cert:
|
||||
description:
|
||||
- Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
|
||||
environment variable.
|
||||
aliases: [ cert_file ]
|
||||
client_key:
|
||||
description:
|
||||
- Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE
|
||||
environment variable.
|
||||
aliases: [ key_file ]
|
||||
ca_cert:
|
||||
description:
|
||||
- Path to a CA certificate used to authenticate with the API. Can also be specified via
|
||||
K8S_AUTH_SSL_CA_CERT environment variable.
|
||||
aliases: [ ssl_ca_cert ]
|
||||
validate_certs:
|
||||
description:
|
||||
- "Whether or not to verify the API server's SSL certificates. Can also be specified via
|
||||
K8S_AUTH_VERIFY_SSL environment variable."
|
||||
type: bool
|
||||
aliases: [ verify_ssl ]
|
||||
namespaces:
|
||||
description:
|
||||
- List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized
|
||||
to access.
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# File must be named k8s.yaml or k8s.yml
|
||||
|
||||
# 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
|
||||
|
||||
# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
|
||||
plugin: kubernetes.core.k8s
|
||||
connections:
|
||||
- namespaces:
|
||||
- testing
|
||||
|
||||
# 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_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
HAS_K8S_MODULE_HELPER,
|
||||
k8s_import_exception,
|
||||
get_api_client,
|
||||
)
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.exceptions import DynamicApiError
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def format_dynamic_api_exc(exc):
|
||||
if exc.body:
|
||||
if exc.headers and exc.headers.get("Content-Type") == "application/json":
|
||||
message = json.loads(exc.body).get("message")
|
||||
if message:
|
||||
return message
|
||||
return exc.body
|
||||
else:
|
||||
return "%s Reason: %s" % (exc.status, exc.reason)
|
||||
|
||||
|
||||
class K8sInventoryException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleMixin):
|
||||
NAME = "kubernetes.core.k8s"
|
||||
|
||||
connection_plugin = "kubernetes.core.kubectl"
|
||||
transport = "kubectl"
|
||||
|
||||
def parse(self, inventory, loader, path, cache=True):
|
||||
super(InventoryModule, self).parse(inventory, loader, path)
|
||||
cache_key = self._get_cache_prefix(path)
|
||||
config_data = self._read_config_data(path)
|
||||
self.setup(config_data, cache, cache_key)
|
||||
|
||||
def setup(self, config_data, cache, cache_key):
|
||||
connections = config_data.get("connections")
|
||||
|
||||
if not HAS_K8S_MODULE_HELPER:
|
||||
raise K8sInventoryException(
|
||||
"This module requires the Kubernetes Python client. Try `pip install kubernetes`. Detail: {0}".format(
|
||||
k8s_import_exception
|
||||
)
|
||||
)
|
||||
|
||||
source_data = None
|
||||
if cache and cache_key in self._cache:
|
||||
try:
|
||||
source_data = self._cache[cache_key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if not source_data:
|
||||
self.fetch_objects(connections)
|
||||
|
||||
def fetch_objects(self, connections):
|
||||
|
||||
if connections:
|
||||
if not isinstance(connections, list):
|
||||
raise K8sInventoryException("Expecting connections to be a list.")
|
||||
|
||||
for connection in connections:
|
||||
if not isinstance(connection, dict):
|
||||
raise K8sInventoryException(
|
||||
"Expecting connection to be a dictionary."
|
||||
)
|
||||
client = get_api_client(**connection)
|
||||
name = connection.get(
|
||||
"name", self.get_default_host_name(client.configuration.host)
|
||||
)
|
||||
if connection.get("namespaces"):
|
||||
namespaces = connection["namespaces"]
|
||||
else:
|
||||
namespaces = self.get_available_namespaces(client)
|
||||
for namespace in namespaces:
|
||||
self.get_pods_for_namespace(client, name, namespace)
|
||||
self.get_services_for_namespace(client, name, namespace)
|
||||
else:
|
||||
client = get_api_client()
|
||||
name = self.get_default_host_name(client.configuration.host)
|
||||
namespaces = self.get_available_namespaces(client)
|
||||
for namespace in namespaces:
|
||||
self.get_pods_for_namespace(client, name, namespace)
|
||||
self.get_services_for_namespace(client, name, namespace)
|
||||
|
||||
@staticmethod
|
||||
def get_default_host_name(host):
|
||||
return (
|
||||
host.replace("https://", "")
|
||||
.replace("http://", "")
|
||||
.replace(".", "-")
|
||||
.replace(":", "_")
|
||||
)
|
||||
|
||||
def get_available_namespaces(self, client):
|
||||
v1_namespace = client.resources.get(api_version="v1", kind="Namespace")
|
||||
try:
|
||||
obj = v1_namespace.get()
|
||||
except DynamicApiError as exc:
|
||||
self.display.debug(exc)
|
||||
raise K8sInventoryException(
|
||||
"Error fetching Namespace list: %s" % format_dynamic_api_exc(exc)
|
||||
)
|
||||
return [namespace.metadata.name for namespace in obj.items]
|
||||
|
||||
def get_pods_for_namespace(self, client, name, namespace):
|
||||
v1_pod = client.resources.get(api_version="v1", kind="Pod")
|
||||
try:
|
||||
obj = v1_pod.get(namespace=namespace)
|
||||
except DynamicApiError as exc:
|
||||
self.display.debug(exc)
|
||||
raise K8sInventoryException(
|
||||
"Error fetching Pod list: %s" % format_dynamic_api_exc(exc)
|
||||
)
|
||||
|
||||
namespace_group = "namespace_{0}".format(namespace)
|
||||
namespace_pods_group = "{0}_pods".format(namespace_group)
|
||||
|
||||
self.inventory.add_group(name)
|
||||
self.inventory.add_group(namespace_group)
|
||||
self.inventory.add_child(name, namespace_group)
|
||||
self.inventory.add_group(namespace_pods_group)
|
||||
self.inventory.add_child(namespace_group, namespace_pods_group)
|
||||
|
||||
for pod in obj.items:
|
||||
pod_name = pod.metadata.name
|
||||
pod_groups = []
|
||||
pod_annotations = (
|
||||
{} if not pod.metadata.annotations else dict(pod.metadata.annotations)
|
||||
)
|
||||
|
||||
if pod.metadata.labels:
|
||||
# create a group for each label_value
|
||||
for key, value in pod.metadata.labels:
|
||||
group_name = "label_{0}_{1}".format(key, value)
|
||||
if group_name not in pod_groups:
|
||||
pod_groups.append(group_name)
|
||||
self.inventory.add_group(group_name)
|
||||
pod_labels = dict(pod.metadata.labels)
|
||||
else:
|
||||
pod_labels = {}
|
||||
|
||||
if not pod.status.containerStatuses:
|
||||
continue
|
||||
|
||||
for container in pod.status.containerStatuses:
|
||||
# add each pod_container to the namespace group, and to each label_value group
|
||||
container_name = "{0}_{1}".format(pod.metadata.name, container.name)
|
||||
self.inventory.add_host(container_name)
|
||||
self.inventory.add_child(namespace_pods_group, container_name)
|
||||
if pod_groups:
|
||||
for group in pod_groups:
|
||||
self.inventory.add_child(group, container_name)
|
||||
|
||||
# Add hostvars
|
||||
self.inventory.set_variable(container_name, "object_type", "pod")
|
||||
self.inventory.set_variable(container_name, "labels", pod_labels)
|
||||
self.inventory.set_variable(
|
||||
container_name, "annotations", pod_annotations
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "cluster_name", pod.metadata.clusterName
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "pod_node_name", pod.spec.nodeName
|
||||
)
|
||||
self.inventory.set_variable(container_name, "pod_name", pod.spec.name)
|
||||
self.inventory.set_variable(
|
||||
container_name, "pod_host_ip", pod.status.hostIP
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "pod_phase", pod.status.phase
|
||||
)
|
||||
self.inventory.set_variable(container_name, "pod_ip", pod.status.podIP)
|
||||
self.inventory.set_variable(
|
||||
container_name, "pod_self_link", pod.metadata.selfLink
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "pod_resource_version", pod.metadata.resourceVersion
|
||||
)
|
||||
self.inventory.set_variable(container_name, "pod_uid", pod.metadata.uid)
|
||||
self.inventory.set_variable(
|
||||
container_name, "container_name", container.image
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "container_image", container.image
|
||||
)
|
||||
if container.state.running:
|
||||
self.inventory.set_variable(
|
||||
container_name, "container_state", "Running"
|
||||
)
|
||||
if container.state.terminated:
|
||||
self.inventory.set_variable(
|
||||
container_name, "container_state", "Terminated"
|
||||
)
|
||||
if container.state.waiting:
|
||||
self.inventory.set_variable(
|
||||
container_name, "container_state", "Waiting"
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "container_ready", container.ready
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "ansible_remote_tmp", "/tmp/"
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "ansible_connection", self.connection_plugin
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name, "ansible_{0}_pod".format(self.transport), pod_name
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name,
|
||||
"ansible_{0}_container".format(self.transport),
|
||||
container.name,
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
container_name,
|
||||
"ansible_{0}_namespace".format(self.transport),
|
||||
namespace,
|
||||
)
|
||||
|
||||
def get_services_for_namespace(self, client, name, namespace):
|
||||
v1_service = client.resources.get(api_version="v1", kind="Service")
|
||||
try:
|
||||
obj = v1_service.get(namespace=namespace)
|
||||
except DynamicApiError as exc:
|
||||
self.display.debug(exc)
|
||||
raise K8sInventoryException(
|
||||
"Error fetching Service list: %s" % format_dynamic_api_exc(exc)
|
||||
)
|
||||
|
||||
namespace_group = "namespace_{0}".format(namespace)
|
||||
namespace_services_group = "{0}_services".format(namespace_group)
|
||||
|
||||
self.inventory.add_group(name)
|
||||
self.inventory.add_group(namespace_group)
|
||||
self.inventory.add_child(name, namespace_group)
|
||||
self.inventory.add_group(namespace_services_group)
|
||||
self.inventory.add_child(namespace_group, namespace_services_group)
|
||||
|
||||
for service in obj.items:
|
||||
service_name = service.metadata.name
|
||||
service_labels = (
|
||||
{} if not service.metadata.labels else dict(service.metadata.labels)
|
||||
)
|
||||
service_annotations = (
|
||||
{}
|
||||
if not service.metadata.annotations
|
||||
else dict(service.metadata.annotations)
|
||||
)
|
||||
|
||||
self.inventory.add_host(service_name)
|
||||
|
||||
if service.metadata.labels:
|
||||
# create a group for each label_value
|
||||
for key, value in service.metadata.labels:
|
||||
group_name = "label_{0}_{1}".format(key, value)
|
||||
self.inventory.add_group(group_name)
|
||||
self.inventory.add_child(group_name, service_name)
|
||||
|
||||
try:
|
||||
self.inventory.add_child(namespace_services_group, service_name)
|
||||
except AnsibleError:
|
||||
raise
|
||||
|
||||
ports = [
|
||||
{
|
||||
"name": port.name,
|
||||
"port": port.port,
|
||||
"protocol": port.protocol,
|
||||
"targetPort": port.targetPort,
|
||||
"nodePort": port.nodePort,
|
||||
}
|
||||
for port in service.spec.ports or []
|
||||
]
|
||||
|
||||
# add hostvars
|
||||
self.inventory.set_variable(service_name, "object_type", "service")
|
||||
self.inventory.set_variable(service_name, "labels", service_labels)
|
||||
self.inventory.set_variable(
|
||||
service_name, "annotations", service_annotations
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
service_name, "cluster_name", service.metadata.clusterName
|
||||
)
|
||||
self.inventory.set_variable(service_name, "ports", ports)
|
||||
self.inventory.set_variable(service_name, "type", service.spec.type)
|
||||
self.inventory.set_variable(
|
||||
service_name, "self_link", service.metadata.selfLink
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
service_name, "resource_version", service.metadata.resourceVersion
|
||||
)
|
||||
self.inventory.set_variable(service_name, "uid", service.metadata.uid)
|
||||
|
||||
if service.spec.externalTrafficPolicy:
|
||||
self.inventory.set_variable(
|
||||
service_name,
|
||||
"external_traffic_policy",
|
||||
service.spec.externalTrafficPolicy,
|
||||
)
|
||||
if service.spec.externalIPs:
|
||||
self.inventory.set_variable(
|
||||
service_name, "external_ips", service.spec.externalIPs
|
||||
)
|
||||
|
||||
if service.spec.externalName:
|
||||
self.inventory.set_variable(
|
||||
service_name, "external_name", service.spec.externalName
|
||||
)
|
||||
|
||||
if service.spec.healthCheckNodePort:
|
||||
self.inventory.set_variable(
|
||||
service_name,
|
||||
"health_check_node_port",
|
||||
service.spec.healthCheckNodePort,
|
||||
)
|
||||
if service.spec.loadBalancerIP:
|
||||
self.inventory.set_variable(
|
||||
service_name, "load_balancer_ip", service.spec.loadBalancerIP
|
||||
)
|
||||
if service.spec.selector:
|
||||
self.inventory.set_variable(
|
||||
service_name, "selector", dict(service.spec.selector)
|
||||
)
|
||||
|
||||
if (
|
||||
hasattr(service.status.loadBalancer, "ingress")
|
||||
and service.status.loadBalancer.ingress
|
||||
):
|
||||
load_balancer = [
|
||||
{"hostname": ingress.hostname, "ip": ingress.ip}
|
||||
for ingress in service.status.loadBalancer.ingress
|
||||
]
|
||||
self.inventory.set_variable(
|
||||
service_name, "load_balancer", load_balancer
|
||||
)
|
||||
@@ -86,14 +86,48 @@ DOCUMENTATION = """
|
||||
description:
|
||||
- Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment
|
||||
variable.
|
||||
no_proxy:
|
||||
description:
|
||||
- The comma separated list of hosts/domains/IP/CIDR that shouldn't go through proxy.
|
||||
Can also be specified via K8S_AUTH_NO_PROXY environment variable.
|
||||
- Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).
|
||||
- This feature requires kubernetes>=19.15.0.
|
||||
When kubernetes library is less than 19.15.0, it fails even if no_proxy is set correctly.
|
||||
type: str
|
||||
password:
|
||||
description:
|
||||
- Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment
|
||||
variable.
|
||||
proxy:
|
||||
description:
|
||||
- The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.
|
||||
- Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).
|
||||
type: str
|
||||
proxy_headers:
|
||||
description:
|
||||
- The Header used for the HTTP proxy.
|
||||
- Documentation can be found here
|
||||
U(https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers).
|
||||
type: dict
|
||||
suboptions:
|
||||
proxy_basic_auth:
|
||||
type: str
|
||||
description:
|
||||
- Colon-separated username:password for proxy basic authentication header.
|
||||
- Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.
|
||||
basic_auth:
|
||||
type: str
|
||||
description:
|
||||
- Colon-separated username:password for basic authentication header.
|
||||
- Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.
|
||||
user_agent:
|
||||
type: str
|
||||
description:
|
||||
- String representing the user-agent you want, such as foo/1.0.
|
||||
- Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.
|
||||
client_cert:
|
||||
description:
|
||||
- Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
|
||||
environment
|
||||
- Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment
|
||||
variable.
|
||||
aliases: [ cert_file ]
|
||||
client_key:
|
||||
@@ -114,8 +148,8 @@ DOCUMENTATION = """
|
||||
aliases: [ verify_ssl ]
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -179,11 +213,12 @@ 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.common import (
|
||||
K8sAnsibleMixin,
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
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"))
|
||||
@@ -210,9 +245,8 @@ except ImportError as e:
|
||||
k8s_import_exception = e
|
||||
|
||||
|
||||
class KubernetesLookup(K8sAnsibleMixin):
|
||||
class KubernetesLookup(object):
|
||||
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 +274,7 @@ class KubernetesLookup(K8sAnsibleMixin):
|
||||
|
||||
cluster_info = kwargs.get("cluster_info")
|
||||
if cluster_info == "version":
|
||||
return [self.client.version]
|
||||
return [self.client.client.version]
|
||||
if cluster_info == "api_groups":
|
||||
if isinstance(self.client.resources.api_groups, KeysView):
|
||||
return [list(self.client.resources.api_groups)]
|
||||
@@ -257,7 +291,12 @@ class KubernetesLookup(K8sAnsibleMixin):
|
||||
resource_definition = kwargs.get("resource_definition")
|
||||
src = kwargs.get("src")
|
||||
if src:
|
||||
resource_definition = self.load_resource_definitions(src)[0]
|
||||
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"
|
||||
if resource_definition:
|
||||
self.kind = resource_definition.get("kind", self.kind)
|
||||
self.api_version = resource_definition.get("apiVersion", self.api_version)
|
||||
@@ -272,14 +311,15 @@ class KubernetesLookup(K8sAnsibleMixin):
|
||||
"using the 'resource_definition' parameter."
|
||||
)
|
||||
|
||||
resource = self.find_resource(self.kind, self.api_version, fail=True)
|
||||
resource = self.client.resource(self.kind, self.api_version)
|
||||
try:
|
||||
k8s_obj = resource.get(
|
||||
params = dict(
|
||||
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 []
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ DOCUMENTATION = """
|
||||
|
||||
short_description: Build a set of kubernetes resources using a 'kustomization.yaml' file.
|
||||
|
||||
version_added: "2.2.0"
|
||||
version_added: 2.2.0
|
||||
|
||||
author:
|
||||
- Aubin Bikouo (@abikouo)
|
||||
@@ -30,6 +30,17 @@ DOCUMENTATION = """
|
||||
opt_dirs:
|
||||
description:
|
||||
- An optional list of directories to search for the executable in addition to PATH.
|
||||
enable_helm:
|
||||
description:
|
||||
- Enable the helm chart inflation generator
|
||||
default: "False"
|
||||
environment:
|
||||
description:
|
||||
- The environment variables to pass to the kustomize or kubectl command.
|
||||
- This can be a dictionary or a string in the format key=value, multiple pairs separated by space.
|
||||
type: raw
|
||||
default: {}
|
||||
version_added: 6.2.0
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
@@ -37,16 +48,28 @@ DOCUMENTATION = """
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Run lookup using kustomize
|
||||
set_fact:
|
||||
ansible.builtin.set_fact:
|
||||
resources: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kustomize') }}"
|
||||
|
||||
- name: Run lookup using kubectl kustomize
|
||||
set_fact:
|
||||
ansible.builtin.set_fact:
|
||||
resources: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output
|
||||
k8s:
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', dir='/path/to/kustomization') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with `--enable-helm` set
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', dir='/path/to/kustomization', enable_helm=True) }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in string format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment='HTTP_PROXY=http://proxy.example.com:3128') }}"
|
||||
|
||||
- name: Create kubernetes resources for lookup output with environment variables in dict format
|
||||
kubernetes.core.k8s:
|
||||
definition: "{{ lookup('kubernetes.core.kustomize', binary_path='/path/to/kubectl', environment={'HTTP_PROXY': 'http://proxy.example.com:3128'}) }}"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
@@ -64,13 +87,13 @@ RETURN = """
|
||||
key1: val1
|
||||
"""
|
||||
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
def get_binary_from_path(name, opt_dirs=None):
|
||||
opt_arg = {}
|
||||
@@ -85,14 +108,25 @@ def get_binary_from_path(name, opt_dirs=None):
|
||||
return None
|
||||
|
||||
|
||||
def run_command(command):
|
||||
cmd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return cmd.communicate()
|
||||
def run_command(command, environ=None):
|
||||
cmd = subprocess.Popen(
|
||||
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=environ
|
||||
)
|
||||
stdout, stderr = cmd.communicate()
|
||||
return cmd.returncode, stdout, stderr
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def run(
|
||||
self, terms, variables=None, dir=".", binary_path=None, opt_dirs=None, **kwargs
|
||||
self,
|
||||
terms,
|
||||
variables=None,
|
||||
dir=".",
|
||||
binary_path=None,
|
||||
opt_dirs=None,
|
||||
enable_helm=False,
|
||||
environment=None,
|
||||
**kwargs
|
||||
):
|
||||
executable_path = binary_path
|
||||
if executable_path is None:
|
||||
@@ -123,9 +157,35 @@ class LookupModule(LookupBase):
|
||||
)
|
||||
)
|
||||
|
||||
(out, err) = run_command(command)
|
||||
if err:
|
||||
raise AnsibleLookupError(
|
||||
"kustomize command failed with: {0}".format(err.decode("utf-8"))
|
||||
)
|
||||
if enable_helm:
|
||||
command += ["--enable-helm"]
|
||||
|
||||
environ = None
|
||||
if environment:
|
||||
environ = os.environ.copy()
|
||||
if isinstance(environment, str):
|
||||
if not all(env.count("=") == 1 for env in environment.split(" ")):
|
||||
raise AnsibleLookupError(
|
||||
"environment should be dict or string in the format key=value, multiple pairs separated by space"
|
||||
)
|
||||
for env in environment.split(" "):
|
||||
key, value = env.split("=")
|
||||
environ[key] = value
|
||||
if isinstance(environment, dict):
|
||||
environ.update(environment)
|
||||
|
||||
(ret, out, err) = run_command(command, environ=environ)
|
||||
if ret != 0:
|
||||
if err:
|
||||
raise AnsibleLookupError(
|
||||
"kustomize command failed. exit code: {0}, error: {1}".format(
|
||||
ret, err.decode("utf-8")
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise AnsibleLookupError(
|
||||
"kustomize command failed with unknown error. exit code: {0}".format(
|
||||
ret
|
||||
)
|
||||
)
|
||||
return [out.decode("utf-8")]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Implements multiple version numbering conventions for the
|
||||
# Python Module Distribution Utilities.
|
||||
#
|
||||
# PSF License (see licenses/PSF-license.txt or https://opensource.org/licenses/Python-2.0)
|
||||
# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0)
|
||||
#
|
||||
|
||||
"""Provides classes to represent module version numbers (one class for
|
||||
|
||||
@@ -14,9 +14,9 @@ except TypeError:
|
||||
|
||||
if enable_turbo_mode:
|
||||
try:
|
||||
from ansible_collections.cloud.common.plugins.module_utils.turbo.module import (
|
||||
from ansible_collections.cloud.common.plugins.module_utils.turbo.module import ( # noqa: F401
|
||||
AnsibleTurboModule as AnsibleModule,
|
||||
) # noqa: F401
|
||||
)
|
||||
|
||||
AnsibleModule.collection_name = "kubernetes.core"
|
||||
except ImportError:
|
||||
|
||||
@@ -17,13 +17,19 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
|
||||
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
|
||||
@@ -131,7 +137,10 @@ def k8s_apply(resource, definition, **kwargs):
|
||||
existing, desired = apply_object(resource, definition)
|
||||
server_side = kwargs.get("server_side", False)
|
||||
if server_side:
|
||||
body = json.dumps(definition).encode()
|
||||
versions = gather_versions()
|
||||
body = definition
|
||||
if LooseVersion(versions["kubernetes"]) < LooseVersion("25.0.0"):
|
||||
body = json.dumps(definition).encode()
|
||||
# server_side_apply is forces content_type to 'application/apply-patch+yaml'
|
||||
return resource.server_side_apply(
|
||||
body=body,
|
||||
@@ -139,6 +148,8 @@ 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"),
|
||||
serialize=kwargs.get("serialize"),
|
||||
)
|
||||
if not existing:
|
||||
return resource.create(
|
||||
@@ -148,6 +159,7 @@ def k8s_apply(resource, definition, **kwargs):
|
||||
return resource.get(
|
||||
name=definition["metadata"]["name"],
|
||||
namespace=definition["metadata"].get("namespace"),
|
||||
**kwargs
|
||||
)
|
||||
return resource.patch(
|
||||
body=desired,
|
||||
|
||||
@@ -1,16 +1,64 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ansible.module_utils.six import string_types
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import warnings
|
||||
|
||||
|
||||
def list_dict_str(value):
|
||||
if isinstance(value, (list, dict, string_types)):
|
||||
if isinstance(value, (list, dict, str)):
|
||||
return value
|
||||
raise TypeError
|
||||
|
||||
|
||||
def extract_sensitive_values_from_kubeconfig(kubeconfig_data):
|
||||
"""
|
||||
Extract only sensitive string values from kubeconfig data for no_log_values.
|
||||
|
||||
:arg kubeconfig_data: Dictionary containing kubeconfig data
|
||||
:returns: Set of sensitive string values to be added to no_log_values
|
||||
"""
|
||||
values = set()
|
||||
sensitive_fields = {
|
||||
"token",
|
||||
"password",
|
||||
"secret",
|
||||
"client-key-data",
|
||||
"client-certificate-data",
|
||||
"certificate-authority-data",
|
||||
"api_key",
|
||||
"access-token",
|
||||
"refresh-token",
|
||||
}
|
||||
|
||||
# Check API version and warn if not v1
|
||||
if isinstance(kubeconfig_data, dict):
|
||||
api_version = kubeconfig_data.get("apiVersion", "v1")
|
||||
if api_version != "v1":
|
||||
warnings.warn(
|
||||
f"Kubeconfig API version '{api_version}' is not 'v1'. "
|
||||
f"Sensitive field redaction is only guaranteed for API version 'v1'. "
|
||||
f"Some sensitive data may not be properly redacted from the logs.",
|
||||
UserWarning,
|
||||
)
|
||||
|
||||
def _extract_recursive(data, current_path=""):
|
||||
if isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
path = f"{current_path}.{key}" if current_path else key
|
||||
if key in sensitive_fields:
|
||||
if isinstance(value, str):
|
||||
values.add(value)
|
||||
else:
|
||||
_extract_recursive(value, path)
|
||||
elif isinstance(data, list):
|
||||
for i, item in enumerate(data):
|
||||
_extract_recursive(item, f"{current_path}[{i}]")
|
||||
|
||||
_extract_recursive(kubeconfig_data)
|
||||
return values
|
||||
|
||||
|
||||
AUTH_PROXY_HEADERS_SPEC = dict(
|
||||
proxy_basic_auth=dict(type="str", no_log=True),
|
||||
basic_auth=dict(type="str", no_log=True),
|
||||
|
||||
@@ -13,15 +13,18 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from collections import defaultdict
|
||||
import hashlib
|
||||
import tempfile
|
||||
from collections import defaultdict
|
||||
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,
|
||||
@@ -29,10 +32,6 @@ 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):
|
||||
@@ -114,7 +113,7 @@ class Discoverer(kubernetes.dynamic.discovery.Discoverer):
|
||||
filter(lambda resource: "/" in resource["name"], resources_response)
|
||||
)
|
||||
for subresource in subresources_raw:
|
||||
resource, name = subresource["name"].split("/")
|
||||
resource, name = subresource["name"].split("/", 1)
|
||||
subresources[resource][name] = subresource
|
||||
|
||||
for resource in resources_raw:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,22 +18,26 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
from tempfile import TemporaryFile, NamedTemporaryFile
|
||||
from select import select
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import tarfile
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from select import select
|
||||
from tempfile import NamedTemporaryFile, TemporaryFile
|
||||
|
||||
from ansible.module_utils.common.text.converters 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.k8s.exceptions import (
|
||||
CoreException,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.client.api import core_v1_api
|
||||
from kubernetes.stream import stream
|
||||
from kubernetes.stream.ws_client import (
|
||||
STDOUT_CHANNEL,
|
||||
STDERR_CHANNEL,
|
||||
ERROR_CHANNEL,
|
||||
ABNF,
|
||||
ERROR_CHANNEL,
|
||||
STDERR_CHANNEL,
|
||||
STDOUT_CHANNEL,
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -61,6 +65,51 @@ 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 {0}/{1} command='{2}' : {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):
|
||||
@@ -75,56 +124,80 @@ class K8SCopyFromPod(K8SCopy):
|
||||
def __init__(self, module, client):
|
||||
super(K8SCopyFromPod, self).__init__(module, client)
|
||||
self.is_remote_path_dir = None
|
||||
self.files_to_copy = list()
|
||||
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
|
||||
|
||||
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
|
||||
"""
|
||||
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)
|
||||
# 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,
|
||||
)
|
||||
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
|
||||
@@ -159,40 +232,42 @@ class K8SCopyFromPod(K8SCopy):
|
||||
if is_remote_path_dir and os.path.isdir(self.local_path):
|
||||
relpath_start = os.path.dirname(self.remote_path)
|
||||
|
||||
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)
|
||||
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
|
||||
)
|
||||
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))
|
||||
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))
|
||||
)
|
||||
self.module.exit_json(
|
||||
changed=True,
|
||||
result="{0} successfully copied locally into {1}".format(
|
||||
@@ -201,13 +276,17 @@ class K8SCopyFromPod(K8SCopy):
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.list_remote_files()
|
||||
self.files_to_copy = self.list_remote_files()
|
||||
if self.files_to_copy == []:
|
||||
# Using warn method instead of passing warnings to exit_json as it is
|
||||
# deprecated in ansible-core>=2.19.0
|
||||
self._module.warn(
|
||||
"No file found from directory '{0}' into remote Pod.".format(
|
||||
self.remote_path
|
||||
)
|
||||
)
|
||||
self.module.exit_json(
|
||||
changed=False,
|
||||
warning="No file found from directory '{0}' into remote Pod.".format(
|
||||
self.remote_path
|
||||
),
|
||||
)
|
||||
self.copy()
|
||||
|
||||
@@ -221,56 +300,6 @@ 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()
|
||||
@@ -293,7 +322,12 @@ 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))
|
||||
|
||||
if self.is_remote_path_dir():
|
||||
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.content:
|
||||
self.module.fail_json(
|
||||
msg="When content is specified, remote path should not be an existing directory"
|
||||
@@ -301,66 +335,67 @@ class K8SCopyToPod(K8SCopy):
|
||||
else:
|
||||
dest_file = os.path.join(dest_file, os.path.basename(src_file))
|
||||
|
||||
if self.no_preserve:
|
||||
tar_command = [
|
||||
"tar",
|
||||
"--no-same-permissions",
|
||||
"--no-same-owner",
|
||||
"-xmf",
|
||||
"-",
|
||||
]
|
||||
else:
|
||||
tar_command = ["tar", "-xmf", "-"]
|
||||
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 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,
|
||||
@@ -376,12 +411,17 @@ class K8SCopyToPod(K8SCopy):
|
||||
)
|
||||
|
||||
|
||||
def check_pod(k8s_ansible_mixin, module):
|
||||
resource = k8s_ansible_mixin.find_resource("Pod", None, True)
|
||||
def check_pod(svc):
|
||||
module = svc.module
|
||||
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"):
|
||||
@@ -398,12 +438,22 @@ def check_pod(k8s_ansible_mixin, module):
|
||||
module.fail_json(msg=msg, **arg)
|
||||
|
||||
try:
|
||||
result = resource.get(name=name, namespace=namespace)
|
||||
containers = [
|
||||
c["name"] for c in result.to_dict()["status"]["containerStatuses"]
|
||||
]
|
||||
if container and container not in containers:
|
||||
result = svc.client.get(resource, name=name, namespace=namespace)
|
||||
containers = dict(
|
||||
{
|
||||
c["name"]: c
|
||||
for cl in ["initContainerStatuses", "containerStatuses"]
|
||||
for c in result.to_dict()["status"].get(cl, [])
|
||||
}
|
||||
)
|
||||
if container and container not in containers.keys():
|
||||
module.fail_json(msg="Pod has no container {0}".format(container))
|
||||
return containers
|
||||
if (
|
||||
container
|
||||
and container in containers
|
||||
and not bool(containers[container].get("started", False))
|
||||
):
|
||||
module.fail_json(msg="Pod container {0} is not started".format(container))
|
||||
return containers.keys()
|
||||
except Exception as exc:
|
||||
_fail(exc)
|
||||
|
||||
@@ -19,8 +19,8 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
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,89 +7,58 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from contextlib import contextmanager
|
||||
import copy
|
||||
import json
|
||||
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_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
extract_sensitive_values_from_kubeconfig,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
HAS_YAML = True
|
||||
YAML_IMP_ERR = None
|
||||
except ImportError:
|
||||
YAML_IMP_ERR = traceback.format_exc()
|
||||
HAS_YAML = False
|
||||
|
||||
|
||||
@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):
|
||||
def parse_helm_plugin_list(output=None):
|
||||
"""
|
||||
Get Values from deployed release
|
||||
Parse `helm plugin list`, return list of plugins
|
||||
"""
|
||||
if not HAS_YAML:
|
||||
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
||||
ret = []
|
||||
if not output:
|
||||
return ret
|
||||
|
||||
get_command = command + " get values --output=yaml " + release_name
|
||||
parsing_grammar = None
|
||||
for line in output:
|
||||
if line.startswith("NAME"):
|
||||
parsing_grammar = [s.strip().lower() for s in line.split("\t")]
|
||||
continue
|
||||
if parsing_grammar is None:
|
||||
continue
|
||||
plugin = {
|
||||
parsing_grammar[i]: v.strip()
|
||||
for i, v in enumerate(line.split("\t", len(parsing_grammar)))
|
||||
}
|
||||
if plugin["name"] == "":
|
||||
continue
|
||||
ret.append(plugin)
|
||||
|
||||
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)
|
||||
return ret
|
||||
|
||||
|
||||
def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None):
|
||||
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",
|
||||
@@ -100,65 +69,275 @@ def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None):
|
||||
],
|
||||
"current-context": "generated-context",
|
||||
}
|
||||
if kubeconfig:
|
||||
content = copy.deepcopy(kubeconfig)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
def get_helm_plugin_list(module, helm_bin=None):
|
||||
class AnsibleHelmModule(object):
|
||||
"""
|
||||
Return `helm plugin list`
|
||||
An Ansible module class for Kubernetes.core helm modules
|
||||
"""
|
||||
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,
|
||||
|
||||
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, str):
|
||||
with open(os.path.expanduser(kubeconfig)) as fd:
|
||||
kubeconfig_content = yaml.safe_load(fd)
|
||||
elif isinstance(kubeconfig, dict):
|
||||
kubeconfig_content = kubeconfig
|
||||
|
||||
# Redact sensitive fields from kubeconfig for logging purposes
|
||||
if kubeconfig_content:
|
||||
# Add original sensitive values to no_log_values to prevent them from appearing in logs
|
||||
self._module.no_log_values.update(
|
||||
extract_sensitive_values_from_kubeconfig(kubeconfig_content)
|
||||
)
|
||||
|
||||
if self.params.get("ca_cert"):
|
||||
ca_cert = self.params.get("ca_cert")
|
||||
if LooseVersion(self.get_helm_version()) < LooseVersion("3.5.0"):
|
||||
# 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, data=None):
|
||||
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, data=data
|
||||
)
|
||||
return (rc, out, err)
|
||||
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 parse_helm_plugin_list(module, output=None):
|
||||
"""
|
||||
Parse `helm plugin list`, return list of plugins
|
||||
"""
|
||||
ret = []
|
||||
if not output:
|
||||
return ret
|
||||
|
||||
for line in output:
|
||||
if line.startswith("NAME"):
|
||||
continue
|
||||
name, version, description = line.split("\t", 3)
|
||||
name = name.strip()
|
||||
version = version.strip()
|
||||
description = description.strip()
|
||||
if name == "":
|
||||
continue
|
||||
ret.append((name, version, description))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_helm_version(module, helm_bin):
|
||||
|
||||
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)
|
||||
def get_helm_version(self):
|
||||
command = self.get_helm_binary() + " version"
|
||||
rc, out, err = self.run_command(command)
|
||||
m = re.match(r'version.BuildInfo{Version:"v(.*?)",', out)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return None
|
||||
m = re.match(r'Client: &version.Version{SemVer:"v(.*?)", ', out)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
def is_helm_v4(self):
|
||||
helm_version = self.get_helm_version()
|
||||
if helm_version is None:
|
||||
return False
|
||||
return LooseVersion(helm_version) >= LooseVersion("4.0.0")
|
||||
|
||||
def is_helm_version_compatible_with_helm_diff(self, helm_diff_version):
|
||||
"""
|
||||
Return true if the helm version is compatible with the helm diff version
|
||||
Helm v4 requires helm diff v3.14.0
|
||||
"""
|
||||
if not helm_diff_version:
|
||||
return False
|
||||
if self.is_helm_v4():
|
||||
return LooseVersion(helm_diff_version) >= LooseVersion("3.14.0")
|
||||
return True
|
||||
|
||||
def validate_helm_version(self, version="3.0.0"):
|
||||
"""
|
||||
Validate that Helm version is >= version (default version=3.0.0).
|
||||
"""
|
||||
helm_version = self.get_helm_version()
|
||||
if helm_version is None:
|
||||
self.fail_json(msg="Unable to determine Helm version")
|
||||
|
||||
if LooseVersion(helm_version) < LooseVersion(version):
|
||||
self.fail_json(
|
||||
msg="Helm version must be >= {0}, current version is {1}".format(
|
||||
version, helm_version
|
||||
)
|
||||
)
|
||||
|
||||
def get_values(self, release_name, get_all=False):
|
||||
"""
|
||||
Get Values from deployed release
|
||||
"""
|
||||
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)
|
||||
|
||||
42
plugins/module_utils/helm_args_common.py
Normal file
42
plugins/module_utils/helm_args_common.py
Normal file
@@ -0,0 +1,42 @@
|
||||
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"),
|
||||
]
|
||||
366
plugins/module_utils/k8s/client.py
Normal file
366
plugins/module_utils/k8s/client.py
Normal file
@@ -0,0 +1,366 @@
|
||||
# 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_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, str):
|
||||
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 auth.items():
|
||||
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
|
||||
184
plugins/module_utils/k8s/core.py
Normal file
184
plugins/module_utils/k8s/core.py
Normal file
@@ -0,0 +1,184 @@
|
||||
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.args_common import (
|
||||
extract_sensitive_values_from_kubeconfig,
|
||||
)
|
||||
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)
|
||||
|
||||
# Apply kubeconfig redaction for logging purposes
|
||||
if hasattr(self._module, "params") and hasattr(self._module, "no_log_values"):
|
||||
kubeconfig = self._module.params.get("kubeconfig")
|
||||
if kubeconfig and isinstance(kubeconfig, dict):
|
||||
# Add sensitive values to no_log_values to prevent them from appearing in logs
|
||||
self._module.no_log_values.update(
|
||||
extract_sensitive_values_from_kubeconfig(kubeconfig)
|
||||
)
|
||||
|
||||
if self.settings["check_k8s"]:
|
||||
self.requires("kubernetes")
|
||||
self.has_at_least("kubernetes", "24.2.0", warn=True)
|
||||
|
||||
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))
|
||||
12
plugins/module_utils/k8s/exceptions.py
Normal file
12
plugins/module_utils/k8s/exceptions.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# 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)
|
||||
133
plugins/module_utils/k8s/resource.py
Normal file
133
plugins/module_utils/k8s/resource.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# 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.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, str):
|
||||
definitions += yaml.safe_load_all(definition)
|
||||
elif isinstance(definition, list):
|
||||
for item in definition:
|
||||
if isinstance(item, str):
|
||||
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
|
||||
237
plugins/module_utils/k8s/runner.py
Normal file
237
plugins/module_utils/k8s/runner.py
Normal file
@@ -0,0 +1,237 @@
|
||||
# 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.common.text.converters 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 = {}
|
||||
warnings = []
|
||||
|
||||
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, warnings = 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, warnings = svc.create(resource, definition)
|
||||
result["method"] = "create"
|
||||
result["changed"] = True
|
||||
elif params.get("force", False):
|
||||
instance, warnings = svc.replace(resource, definition, existing)
|
||||
result["method"] = "replace"
|
||||
else:
|
||||
instance, warnings = svc.update(resource, definition, existing)
|
||||
result["method"] = "update"
|
||||
|
||||
if warnings:
|
||||
result["warnings"] = warnings
|
||||
|
||||
# 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
|
||||
711
plugins/module_utils/k8s/service.py
Normal file
711
plugins/module_utils/k8s/service.py
Normal file
@@ -0,0 +1,711 @@
|
||||
# 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 json import loads
|
||||
from re import compile
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
|
||||
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,
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
try:
|
||||
params = dict(name=name, namespace=namespace, serialize=False)
|
||||
if merge_type:
|
||||
params["content_type"] = "application/{0}-patch+json".format(merge_type)
|
||||
return decode_response(self.client.patch(resource, definition, **params))
|
||||
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) -> Tuple[Dict, List[str]]:
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
name = definition["metadata"].get("name")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
return _encode_stringdata(definition), []
|
||||
|
||||
try:
|
||||
return decode_response(
|
||||
self.client.create(
|
||||
resource, definition, namespace=namespace, serialize=False
|
||||
)
|
||||
)
|
||||
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
|
||||
|
||||
def apply(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
existing: Optional[ResourceInstance] = None,
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
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:
|
||||
return dict_merge(existing.to_dict(), patch), []
|
||||
else:
|
||||
return patch, []
|
||||
|
||||
try:
|
||||
params = {}
|
||||
if server_side_apply:
|
||||
params["server_side"] = True
|
||||
params.update(server_side_apply)
|
||||
return decode_response(
|
||||
self.client.apply(
|
||||
resource, definition, namespace=namespace, serialize=False, **params
|
||||
)
|
||||
)
|
||||
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
|
||||
|
||||
def replace(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
existing: ResourceInstance,
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
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:
|
||||
return _encode_stringdata(definition), []
|
||||
|
||||
try:
|
||||
return decode_response(
|
||||
self.client.replace(
|
||||
resource,
|
||||
definition,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
append_hash=append_hash,
|
||||
serialize=False,
|
||||
)
|
||||
)
|
||||
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
|
||||
|
||||
def update(
|
||||
self, resource: Resource, definition: Dict, existing: ResourceInstance
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
name = definition["metadata"].get("name")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
return dict_merge(existing.to_dict(), _encode_stringdata(definition)), []
|
||||
|
||||
exception = None
|
||||
for merge_type in self.module.params.get("merge_type") or [
|
||||
"strategic-merge",
|
||||
"merge",
|
||||
]:
|
||||
try:
|
||||
return self.patch_resource(
|
||||
resource,
|
||||
definition,
|
||||
name,
|
||||
namespace,
|
||||
merge_type=merge_type,
|
||||
)
|
||||
except CoreException as e:
|
||||
exception = e
|
||||
continue
|
||||
raise exception
|
||||
|
||||
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 and not self.module.check_mode:
|
||||
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"] = hide_fields(diff[0], hidden_fields)
|
||||
result["after"] = hide_fields(diff[1], hidden_fields)
|
||||
|
||||
if list(result["after"].keys()) == ["metadata"] and list(
|
||||
result["before"].keys()
|
||||
) == ["metadata"]:
|
||||
# If only metadata.generation and metadata.resourceVersion changed, ignore it
|
||||
ignored_keys = set(["generation", "resourceVersion"])
|
||||
|
||||
if set(result["after"]["metadata"].keys()).issubset(ignored_keys) and set(
|
||||
result["before"]["metadata"].keys()
|
||||
).issubset(ignored_keys):
|
||||
return True, result
|
||||
|
||||
return False, result
|
||||
|
||||
|
||||
def hide_field_tree(hidden_field: str) -> List[str]:
|
||||
result = []
|
||||
key, rest = hide_field_split2(hidden_field)
|
||||
result.append(key)
|
||||
while rest:
|
||||
key, rest = hide_field_split2(rest)
|
||||
result.append(key)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def build_hidden_field_tree(hidden_fields: List[str]) -> Dict[str, Any]:
|
||||
"""Group hidden field targeting the same json key
|
||||
Example:
|
||||
Input: ['env[3]', 'env[0]']
|
||||
Output: {'env': [0, 3]}
|
||||
"""
|
||||
output = {}
|
||||
for hidden_field in hidden_fields:
|
||||
current = output
|
||||
tree = hide_field_tree(hidden_field)
|
||||
for idx, key in enumerate(tree):
|
||||
if current.get(key, "") is None:
|
||||
break
|
||||
if idx == (len(tree) - 1):
|
||||
current[key] = None
|
||||
elif key not in current:
|
||||
current[key] = {}
|
||||
current = current[key]
|
||||
return output
|
||||
|
||||
|
||||
# hide_field should be able to cope with simple or more complicated
|
||||
# field definitions
|
||||
# e.g. status or metadata.managedFields or
|
||||
# spec.template.spec.containers[0].env[3].value or
|
||||
# metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]
|
||||
def hide_field(
|
||||
definition: Union[Dict[str, Any], List[Any]], hidden_field: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
def dict_contains_key(obj: Dict[str, Any], key: str) -> bool:
|
||||
return key in obj
|
||||
|
||||
def list_contains_key(obj: List[Any], key: str) -> bool:
|
||||
return int(key) < len(obj)
|
||||
|
||||
hidden_keys = list(hidden_field.keys())
|
||||
field_contains_key = dict_contains_key
|
||||
field_get_key = str
|
||||
if isinstance(definition, list):
|
||||
# Sort with reverse=true so that when we delete an item from the list, the order is not changed
|
||||
hidden_keys = sorted(
|
||||
[k for k in hidden_field.keys() if k.isdecimal()], reverse=True
|
||||
)
|
||||
field_contains_key = list_contains_key
|
||||
field_get_key = int
|
||||
|
||||
for key in hidden_keys:
|
||||
if field_contains_key(definition, key):
|
||||
value = hidden_field.get(key)
|
||||
convert_key = field_get_key(key)
|
||||
if value is None:
|
||||
del definition[convert_key]
|
||||
else:
|
||||
definition[convert_key] = hide_field(definition[convert_key], value)
|
||||
if (
|
||||
definition[convert_key] == dict()
|
||||
or definition[convert_key] == list()
|
||||
):
|
||||
del definition[convert_key]
|
||||
|
||||
return definition
|
||||
|
||||
|
||||
def hide_fields(
|
||||
definition: Dict[str, Any], hidden_fields: Optional[List[str]]
|
||||
) -> Dict[str, Any]:
|
||||
if not hidden_fields:
|
||||
return definition
|
||||
result = copy.deepcopy(definition)
|
||||
hidden_field_tree = build_hidden_field_tree(hidden_fields)
|
||||
return hide_field(result, hidden_field_tree)
|
||||
|
||||
|
||||
def decode_response(resp) -> Tuple[Dict, List[str]]:
|
||||
"""
|
||||
This function decodes unserialized responses from the Kubernetes python
|
||||
client and decodes the RFC2616 14.46 warnings found in the response
|
||||
headers.
|
||||
"""
|
||||
obj = ResourceInstance(None, loads(resp.data.decode("utf8"))).to_dict()
|
||||
warnings = []
|
||||
if (
|
||||
resp.headers is not None
|
||||
and "warning" in resp.headers
|
||||
and resp.headers["warning"] is not None
|
||||
):
|
||||
warnings = resp.headers["warning"].split(", ")
|
||||
return obj, decode_warnings(warnings)
|
||||
|
||||
|
||||
def decode_warnings(warnings: str) -> List[str]:
|
||||
"""
|
||||
This function decodes RFC2616 14.46 warnings in a simplified way, where
|
||||
only the warn-texts are returned in a list.
|
||||
"""
|
||||
p = compile('\\d{3} .+ (".+")')
|
||||
|
||||
decoded = []
|
||||
for warning in warnings:
|
||||
m = p.match(warning)
|
||||
if m:
|
||||
try:
|
||||
parsed, unused = parse_quoted_string(m.group(1))
|
||||
decoded.append(parsed)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
return decoded
|
||||
|
||||
|
||||
def parse_quoted_string(quoted_string: str) -> Tuple[str, str]:
|
||||
"""
|
||||
This function was adapted from:
|
||||
https://github.com/kubernetes/apimachinery/blob/bb8822152cabfb4f34dbc26270f874ce53db50de/pkg/util/net/http.go#L609
|
||||
"""
|
||||
if len(quoted_string) == 0:
|
||||
raise ValueError("invalid quoted string: 0-length")
|
||||
|
||||
if quoted_string[0] != '"':
|
||||
raise ValueError("invalid quoted string: missing initial quote")
|
||||
|
||||
quoted_string = quoted_string[1:]
|
||||
remainder = ""
|
||||
escaping = False
|
||||
closed_quote = False
|
||||
result = []
|
||||
|
||||
for i, b in enumerate(quoted_string):
|
||||
if b == '"':
|
||||
if escaping:
|
||||
result.append(b)
|
||||
escaping = False
|
||||
else:
|
||||
closed_quote = True
|
||||
remainder_start = i + 1
|
||||
remainder = quoted_string[remainder_start:].strip()
|
||||
break
|
||||
elif b == "\\":
|
||||
if escaping:
|
||||
result.append(b)
|
||||
escaping = False
|
||||
else:
|
||||
escaping = True
|
||||
else:
|
||||
result.append(b)
|
||||
escaping = False
|
||||
|
||||
if not closed_quote:
|
||||
raise ValueError("invalid quoted string: missing closing quote")
|
||||
|
||||
return "".join(result), remainder
|
||||
|
||||
|
||||
# hide_field_split2 returns the first key in hidden_field and the rest of the hidden_field
|
||||
# We expect the first key to either be in brackets, to be terminated by the start of a left
|
||||
# bracket, or to be terminated by a dot.
|
||||
|
||||
# examples would be:
|
||||
# field.another.next -> (field, another.next)
|
||||
# field[key].value -> (field, [key].value)
|
||||
# [key].value -> (key, value)
|
||||
# [one][two] -> (one, [two])
|
||||
|
||||
|
||||
def hide_field_split2(hidden_field: str) -> Tuple[str, str]:
|
||||
lbracket = hidden_field.find("[")
|
||||
rbracket = hidden_field.find("]")
|
||||
dot = hidden_field.find(".")
|
||||
|
||||
if lbracket == 0:
|
||||
# skip past right bracket and any following dot
|
||||
rest = hidden_field[rbracket + 1 :] # noqa: E203
|
||||
if rest and rest[0] == ".":
|
||||
rest = rest[1:]
|
||||
return (hidden_field[lbracket + 1 : rbracket], rest) # noqa: E203
|
||||
|
||||
if lbracket != -1 and (dot == -1 or lbracket < dot):
|
||||
return (hidden_field[:lbracket], hidden_field[lbracket:])
|
||||
|
||||
split = hidden_field.split(".", 1)
|
||||
if len(split) == 1:
|
||||
return split[0], ""
|
||||
return split
|
||||
267
plugins/module_utils/k8s/waiter.py
Normal file
267
plugins/module_utils/k8s/waiter.py
Normal file
@@ -0,0 +1,267 @@
|
||||
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 or 0)
|
||||
== 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)
|
||||
|
||||
|
||||
def cluster_operator_ready(resource: ResourceInstance) -> bool:
|
||||
"""
|
||||
Predicate to check if a single ClusterOperator is healthy.
|
||||
Returns True if:
|
||||
- "Available" is True
|
||||
- "Degraded" is False
|
||||
- "Progressing" is False
|
||||
"""
|
||||
if not resource:
|
||||
return False
|
||||
|
||||
# Extract conditions from the resource's status
|
||||
conditions = resource.get("status", {}).get("conditions", [])
|
||||
|
||||
status = {x.get("type", ""): x.get("status") for x in conditions}
|
||||
return (
|
||||
(status.get("Degraded") == "False")
|
||||
and (status.get("Progressing") == "False")
|
||||
and (status.get("Available") == "True")
|
||||
)
|
||||
|
||||
|
||||
RESOURCE_PREDICATES = {
|
||||
"DaemonSet": daemonset_ready,
|
||||
"Deployment": deployment_ready,
|
||||
"Pod": pod_ready,
|
||||
"StatefulSet": statefulset_ready,
|
||||
"ClusterOperator": cluster_operator_ready,
|
||||
}
|
||||
|
||||
|
||||
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,12 +18,11 @@ 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,7 +16,6 @@ import re
|
||||
|
||||
|
||||
class Selector(object):
|
||||
|
||||
equality_based_operators = ("==", "!=", "=")
|
||||
|
||||
def __init__(self, data):
|
||||
|
||||
@@ -14,14 +14,14 @@ module: helm
|
||||
|
||||
short_description: Manages Kubernetes packages with the Helm package manager
|
||||
|
||||
version_added: "0.11.0"
|
||||
version_added: 0.11.0
|
||||
|
||||
author:
|
||||
- Lucas Boisserie (@LucasBoisserie)
|
||||
- Matthieu Diehr (@d-matt)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
description:
|
||||
@@ -51,6 +51,17 @@ 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.
|
||||
@@ -88,12 +99,62 @@ options:
|
||||
default: []
|
||||
type: list
|
||||
elements: str
|
||||
version_added: '1.1.0'
|
||||
version_added: 1.1.0
|
||||
update_repo_cache:
|
||||
description:
|
||||
- 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: 3.0.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: 3.0.0
|
||||
reset_then_reuse_values:
|
||||
description:
|
||||
- When upgrading package, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from
|
||||
parameters O(release_values), O(values_files) or O(set_values).
|
||||
- If O(reset_values) or O(reuse_values) is set to V(True), this is ignored.
|
||||
- This feature requires helm diff >= 3.9.12.
|
||||
type: bool
|
||||
required: false
|
||||
default: False
|
||||
version_added: 6.0.0
|
||||
|
||||
#Helm options
|
||||
disable_hook:
|
||||
@@ -130,7 +191,7 @@ options:
|
||||
- similar to C(wait_timeout) but does not required C(wait) to be activated.
|
||||
- Mutually exclusive with C(wait_timeout).
|
||||
type: str
|
||||
version_added: "2.3.0"
|
||||
version_added: 2.3.0
|
||||
atomic:
|
||||
description:
|
||||
- If set, the installation process deletes the installation on failure.
|
||||
@@ -141,7 +202,12 @@ options:
|
||||
- Create the release namespace if not present.
|
||||
type: bool
|
||||
default: False
|
||||
version_added: "0.11.1"
|
||||
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.
|
||||
@@ -149,19 +215,49 @@ options:
|
||||
- mutually exclusive with with C(history_max).
|
||||
type: bool
|
||||
default: False
|
||||
version_added: "1.11.0"
|
||||
version_added: 1.11.0
|
||||
skip_crds:
|
||||
description:
|
||||
- Skip custom resource definitions when installing or upgrading.
|
||||
type: bool
|
||||
default: False
|
||||
version_added: "1.2.0"
|
||||
version_added: 1.2.0
|
||||
history_max:
|
||||
description:
|
||||
- Limit the maximum number of revisions saved per release.
|
||||
- mutually exclusive with with C(replace).
|
||||
type: int
|
||||
version_added: "2.2.0"
|
||||
version_added: 2.2.0
|
||||
insecure_skip_tls_verify:
|
||||
description:
|
||||
- Skip tls certificate checks for the chart download.
|
||||
- Do not confuse with the C(validate_certs) option.
|
||||
- This option is only available for helm >= 3.16.0.
|
||||
type: bool
|
||||
default: False
|
||||
aliases: [ skip_tls_certs_check ]
|
||||
version_added: 5.3.0
|
||||
plain_http:
|
||||
description:
|
||||
- Use HTTP instead of HTTPS when working with OCI registries
|
||||
- Requires Helm >= 3.13.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
take_ownership:
|
||||
description:
|
||||
- Helm will ignore the check for helm annotations and take ownership of the existing resources
|
||||
- This feature requires helm >= 3.17.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
skip_schema_validation:
|
||||
description:
|
||||
- Disables JSON schema validation for Chart and values.
|
||||
- This feature requires helm >= 3.16.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.2.0
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.helm_common_options
|
||||
"""
|
||||
@@ -216,6 +312,15 @@ 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:
|
||||
@@ -235,6 +340,12 @@ EXAMPLES = r"""
|
||||
chart_ref: "https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz"
|
||||
release_namespace: monitoring
|
||||
|
||||
- name: Deploy Bitnami's MongoDB latest chart from OCI registry
|
||||
kubernetes.core.helm:
|
||||
name: test
|
||||
chart_ref: "oci://registry-1.docker.io/bitnamicharts/mongodb"
|
||||
release_namespace: database
|
||||
|
||||
# Using complex Values
|
||||
- name: Deploy new-relic client chart
|
||||
kubernetes.core.helm:
|
||||
@@ -260,6 +371,17 @@ 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"""
|
||||
@@ -297,9 +419,18 @@ status:
|
||||
returned: always
|
||||
description: The Date of last update
|
||||
values:
|
||||
type: str
|
||||
type: dict
|
||||
returned: always
|
||||
description: Dict of Values used to deploy
|
||||
description:
|
||||
- Dict of Values used to deploy.
|
||||
- This return value has been deprecated and will be removed in a release after
|
||||
2027-01-08. Use RV(status.release_values) instead.
|
||||
release_values:
|
||||
type: dict
|
||||
returned: always
|
||||
description:
|
||||
- Dict of Values used to deploy.
|
||||
version_added: 6.3.0
|
||||
stdout:
|
||||
type: str
|
||||
description: Full `helm` command stdout, in case you want to display it or examine the event log
|
||||
@@ -317,8 +448,11 @@ command:
|
||||
sample: helm upgrade ...
|
||||
"""
|
||||
|
||||
import copy
|
||||
import re
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
@@ -327,17 +461,18 @@ 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 AnsibleModule, missing_required_lib, env_fallback
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_values,
|
||||
get_helm_plugin_list,
|
||||
AnsibleHelmModule,
|
||||
parse_helm_plugin_list,
|
||||
get_helm_version,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
)
|
||||
|
||||
|
||||
@@ -353,45 +488,83 @@ def get_release(state, release_name):
|
||||
return None
|
||||
|
||||
|
||||
def get_release_status(module, command, release_name):
|
||||
def get_release_status(module, release_name, all_status=False):
|
||||
"""
|
||||
Get Release state from deployed release
|
||||
Get Release state from all release status (deployed, failed, pending-install, etc)
|
||||
"""
|
||||
|
||||
list_command = command + " list --output=yaml --filter " + release_name
|
||||
|
||||
rc, out, err = run_helm(module, list_command)
|
||||
list_command = [
|
||||
module.get_helm_binary(),
|
||||
"list",
|
||||
"--output=yaml",
|
||||
"--filter",
|
||||
release_name,
|
||||
]
|
||||
if all_status and not module.is_helm_v4():
|
||||
# --all has been removed from `helm list` command on helm v4
|
||||
list_command.append("--all")
|
||||
elif not all_status:
|
||||
# The default behavior to display only deployed releases has been removed from
|
||||
# Helm v4
|
||||
list_command.append("--deployed")
|
||||
rc, out, err = module.run_helm_command(list_command)
|
||||
|
||||
release = get_release(yaml.safe_load(out), release_name)
|
||||
|
||||
if release is None: # not install
|
||||
return None
|
||||
|
||||
release["values"] = get_values(module, command, release_name)
|
||||
release["release_values"] = module.get_values(release_name)
|
||||
release["values"] = release["release_values"]
|
||||
|
||||
return release
|
||||
|
||||
|
||||
def run_repo_update(module, command):
|
||||
def run_repo_update(module):
|
||||
"""
|
||||
Run Repo update
|
||||
"""
|
||||
repo_update_command = command + " repo update"
|
||||
rc, out, err = run_helm(module, repo_update_command)
|
||||
repo_update_command = module.get_helm_binary() + " repo update"
|
||||
rc, out, err = module.run_helm_command(repo_update_command)
|
||||
|
||||
|
||||
def fetch_chart_info(module, command, chart_ref):
|
||||
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)
|
||||
|
||||
|
||||
def fetch_chart_info(
|
||||
module, command, chart_ref, insecure_skip_tls_verify=False, plain_http=False
|
||||
):
|
||||
"""
|
||||
Get chart info
|
||||
"""
|
||||
inspect_command = command + " show chart " + chart_ref
|
||||
inspect_command = command + f" show chart '{chart_ref}'"
|
||||
|
||||
rc, out, err = run_helm(module, inspect_command)
|
||||
if insecure_skip_tls_verify:
|
||||
inspect_command += " --insecure-skip-tls-verify"
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
inspect_command += " --plain-http"
|
||||
|
||||
rc, out, err = module.run_helm_command(inspect_command)
|
||||
|
||||
return yaml.safe_load(out)
|
||||
|
||||
|
||||
def deploy(
|
||||
module,
|
||||
command,
|
||||
release_name,
|
||||
release_values,
|
||||
@@ -405,8 +578,18 @@ 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,
|
||||
reset_then_reuse_values=False,
|
||||
insecure_skip_tls_verify=False,
|
||||
plain_http=False,
|
||||
take_ownership=False,
|
||||
skip_schema_validation=False,
|
||||
):
|
||||
"""
|
||||
Install/upgrade/rollback release chart
|
||||
@@ -414,11 +597,28 @@ 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 take_ownership:
|
||||
deploy_command += " --take-ownership"
|
||||
|
||||
# Always reset values to keep release_values equal to values released
|
||||
deploy_command += " --reset-values"
|
||||
if reuse_values is not None:
|
||||
deploy_command += " --reuse-values=" + str(reuse_values)
|
||||
|
||||
if reset_then_reuse_values:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.14.0"):
|
||||
module.fail_json(
|
||||
msg="reset_then_reuse_values requires helm >= 3.14.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
deploy_command += " --reset-then-reuse-values"
|
||||
|
||||
if wait:
|
||||
deploy_command += " --wait"
|
||||
@@ -443,6 +643,20 @@ def deploy(
|
||||
if create_namespace:
|
||||
deploy_command += " --create-namespace"
|
||||
|
||||
if insecure_skip_tls_verify:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
|
||||
module.fail_json(
|
||||
msg="insecure_skip_tls_verify requires helm >= 3.16.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
deploy_command += " --insecure-skip-tls-verify"
|
||||
|
||||
if plain_http:
|
||||
deploy_command += " --plain-http"
|
||||
|
||||
if values_files:
|
||||
for value_file in values_files:
|
||||
deploy_command += " --values=" + value_file
|
||||
@@ -452,6 +666,10 @@ 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"
|
||||
@@ -459,7 +677,21 @@ def deploy(
|
||||
if history_max is not None:
|
||||
deploy_command += " --history-max=%s" % str(history_max)
|
||||
|
||||
deploy_command += " " + release_name + " " + chart_name
|
||||
if set_value_args:
|
||||
deploy_command += " " + set_value_args
|
||||
|
||||
if skip_schema_validation:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
|
||||
module.fail_json(
|
||||
msg="skip_schema_validation requires helm >= 3.16.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
deploy_command += " --skip-schema-validation"
|
||||
|
||||
deploy_command += " " + release_name + f" '{chart_name}'"
|
||||
return deploy_command
|
||||
|
||||
|
||||
@@ -499,27 +731,25 @@ def load_values_files(values_files):
|
||||
return values
|
||||
|
||||
|
||||
def get_plugin_version(command, plugin):
|
||||
def get_plugin_version(plugin):
|
||||
"""
|
||||
Check if helm plugin is installed and return corresponding version
|
||||
"""
|
||||
|
||||
cmd = command + " plugin"
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=cmd)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
out = parse_helm_plugin_list(output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
return None
|
||||
|
||||
for line in out:
|
||||
if line[0] == plugin:
|
||||
return line[1]
|
||||
if line["name"] == plugin:
|
||||
return line["version"]
|
||||
return None
|
||||
|
||||
|
||||
def helmdiff_check(
|
||||
module,
|
||||
helm_cmd,
|
||||
release_name,
|
||||
chart_ref,
|
||||
release_values,
|
||||
@@ -527,11 +757,19 @@ def helmdiff_check(
|
||||
chart_version=None,
|
||||
replace=False,
|
||||
chart_repo_url=None,
|
||||
post_renderer=False,
|
||||
set_value_args=None,
|
||||
reuse_values=None,
|
||||
reset_values=True,
|
||||
reset_then_reuse_values=False,
|
||||
insecure_skip_tls_verify=False,
|
||||
plain_http=False,
|
||||
skip_schema_validation=False,
|
||||
):
|
||||
"""
|
||||
Use helm diff to determine if a release would change by upgrading a chart.
|
||||
"""
|
||||
cmd = helm_cmd + " diff upgrade"
|
||||
cmd = module.get_helm_binary() + " diff upgrade"
|
||||
cmd += " " + release_name
|
||||
cmd += " " + chart_ref
|
||||
|
||||
@@ -540,19 +778,71 @@ def helmdiff_check(
|
||||
if chart_version is not None:
|
||||
cmd += " " + "--version=" + chart_version
|
||||
if not replace:
|
||||
cmd += " " + "--reset-values"
|
||||
cmd += " " + "--reset-values=" + str(reset_values)
|
||||
if post_renderer:
|
||||
cmd += " --post-renderer=" + post_renderer
|
||||
|
||||
if values_files:
|
||||
for value_file in values_files:
|
||||
cmd += " --values=" + value_file
|
||||
|
||||
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
|
||||
if set_value_args:
|
||||
cmd += " " + set_value_args
|
||||
|
||||
rc, out, err = run_helm(module, cmd)
|
||||
if reuse_values:
|
||||
cmd += " --reuse-values"
|
||||
|
||||
if reset_then_reuse_values:
|
||||
helm_diff_version = get_plugin_version("diff")
|
||||
helm_version = module.get_helm_version()
|
||||
fail_msg = ""
|
||||
if LooseVersion(helm_diff_version) < LooseVersion("3.9.12"):
|
||||
fail_msg = "reset_then_reuse_values requires helm diff >= 3.9.12, current version is {0}\n".format(
|
||||
helm_diff_version
|
||||
)
|
||||
if LooseVersion(helm_version) < LooseVersion("3.14.0"):
|
||||
fail_msg += "reset_then_reuse_values requires helm >= 3.14.0, current version is {0}\n".format(
|
||||
helm_version
|
||||
)
|
||||
|
||||
if fail_msg:
|
||||
module.fail_json(msg=fail_msg)
|
||||
else:
|
||||
cmd += " --reset-then-reuse-values"
|
||||
|
||||
if insecure_skip_tls_verify:
|
||||
cmd += " --insecure-skip-tls-verify"
|
||||
|
||||
if skip_schema_validation:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
|
||||
module.fail_json(
|
||||
msg="skip_schema_validation requires helm >= 3.16.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
cmd += " --skip-schema-validation"
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
cmd += " --plain-http"
|
||||
|
||||
rc, out, err = module.run_helm_command(cmd)
|
||||
return (len(out.strip()) > 0, out.strip())
|
||||
|
||||
|
||||
@@ -580,14 +870,14 @@ def default_check(release_status, chart_info, values=None, values_files=None):
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
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(
|
||||
@@ -596,52 +886,43 @@ def main():
|
||||
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"),
|
||||
# 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"]),
|
||||
set_values=dict(type="list", elements="dict"),
|
||||
reuse_values=dict(type="bool"),
|
||||
reset_values=dict(type="bool", default=True),
|
||||
reset_then_reuse_values=dict(type="bool", default=False),
|
||||
insecure_skip_tls_verify=dict(
|
||||
type="bool", default=False, aliases=["skip_tls_certs_check"]
|
||||
),
|
||||
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"])
|
||||
),
|
||||
),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
take_ownership=dict(type="bool", default=False),
|
||||
skip_schema_validation=dict(type="bool", default=False),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec(),
|
||||
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"),
|
||||
],
|
||||
@@ -651,12 +932,15 @@ def main():
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
changed = False
|
||||
|
||||
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")
|
||||
@@ -671,44 +955,75 @@ 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")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = module.get_bin_path("helm", required=True)
|
||||
set_values = module.params.get("set_values")
|
||||
reuse_values = module.params.get("reuse_values")
|
||||
reset_values = module.params.get("reset_values")
|
||||
reset_then_reuse_values = module.params.get("reset_then_reuse_values")
|
||||
insecure_skip_tls_verify = module.params.get("insecure_skip_tls_verify")
|
||||
plain_http = module.params.get("plain_http")
|
||||
take_ownership = module.params.get("take_ownership")
|
||||
skip_schema_validation = module.params.get("skip_schema_validation")
|
||||
|
||||
if update_repo_cache:
|
||||
run_repo_update(module, helm_cmd_common)
|
||||
run_repo_update(module)
|
||||
|
||||
# Get real/deployed release status
|
||||
release_status = get_release_status(module, helm_cmd_common, release_name)
|
||||
all_status = release_state == "absent"
|
||||
release_status = get_release_status(module, release_name, all_status=all_status)
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
if take_ownership:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.17.0"):
|
||||
module.fail_json(
|
||||
msg="take_ownership requires helm >= 3.17.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
|
||||
if skip_schema_validation:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.16.0"):
|
||||
module.fail_json(
|
||||
msg="skip_schema_validation requires helm >= 3.16.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
|
||||
# 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:
|
||||
if replace:
|
||||
module.fail_json(msg="replace is not applicable when state is absent")
|
||||
# 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 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
|
||||
if wait:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.7.0"):
|
||||
module.warn(
|
||||
"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
|
||||
|
||||
@@ -716,10 +1031,47 @@ def main():
|
||||
helm_cmd += " --repo=" + chart_repo_url
|
||||
|
||||
# Fetch chart info to have real version and real name for chart_ref from archive, folder or url
|
||||
chart_info = fetch_chart_info(module, helm_cmd, chart_ref)
|
||||
chart_info = fetch_chart_info(
|
||||
module, helm_cmd, chart_ref, insecure_skip_tls_verify, plain_http
|
||||
)
|
||||
|
||||
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."
|
||||
)
|
||||
|
||||
set_value_args = None
|
||||
if set_values:
|
||||
set_value_args = module.get_helm_set_values_args(set_values)
|
||||
|
||||
if release_status is None: # Not installed
|
||||
helm_cmd = deploy(
|
||||
module,
|
||||
helm_cmd,
|
||||
release_name,
|
||||
release_values,
|
||||
@@ -731,26 +1083,41 @@ 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,
|
||||
reset_then_reuse_values=reset_then_reuse_values,
|
||||
insecure_skip_tls_verify=insecure_skip_tls_verify,
|
||||
plain_http=plain_http,
|
||||
take_ownership=take_ownership,
|
||||
skip_schema_validation=skip_schema_validation,
|
||||
)
|
||||
changed = True
|
||||
|
||||
else:
|
||||
|
||||
helm_diff_version = get_plugin_version(helm_cmd_common, "diff")
|
||||
if helm_diff_version and (
|
||||
not chart_repo_url
|
||||
or (
|
||||
chart_repo_url
|
||||
and LooseVersion(helm_diff_version) >= LooseVersion("3.4.1")
|
||||
helm_diff_version = get_plugin_version("diff")
|
||||
helm_version_compatible = module.is_helm_version_compatible_with_helm_diff(
|
||||
helm_diff_version
|
||||
)
|
||||
if (
|
||||
helm_diff_version
|
||||
and helm_version_compatible
|
||||
and (
|
||||
not chart_repo_url
|
||||
or (
|
||||
chart_repo_url
|
||||
and LooseVersion(helm_diff_version) >= LooseVersion("3.4.1")
|
||||
)
|
||||
)
|
||||
):
|
||||
(would_change, prepared) = helmdiff_check(
|
||||
would_change, prepared = helmdiff_check(
|
||||
module,
|
||||
helm_cmd_common,
|
||||
release_name,
|
||||
chart_ref,
|
||||
release_values,
|
||||
@@ -758,20 +1125,37 @@ def main():
|
||||
chart_version,
|
||||
replace,
|
||||
chart_repo_url,
|
||||
post_renderer,
|
||||
set_value_args,
|
||||
reuse_values=reuse_values,
|
||||
reset_values=reset_values,
|
||||
reset_then_reuse_values=reset_then_reuse_values,
|
||||
insecure_skip_tls_verify=insecure_skip_tls_verify,
|
||||
plain_http=plain_http,
|
||||
skip_schema_validation=skip_schema_validation,
|
||||
)
|
||||
if would_change and module._diff:
|
||||
opt_result["diff"] = {"prepared": prepared}
|
||||
else:
|
||||
module.warn(
|
||||
"The default idempotency check can fail to report changes in certain cases. "
|
||||
"Install helm diff >= 3.4.1 for better results."
|
||||
)
|
||||
if helm_diff_version and not helm_version_compatible:
|
||||
module.warn(
|
||||
"Idempotency checks are currently disabled due to a version mismatch."
|
||||
f" Helm version {module.get_helm_version()} requires helm-diff >= 3.14.0,"
|
||||
f" but the environment is currently running {helm_diff_version}."
|
||||
" Please align the plugin versions to restore standard behavior."
|
||||
)
|
||||
else:
|
||||
module.warn(
|
||||
"The default idempotency check can fail to report changes in certain cases. "
|
||||
"Install helm diff >= 3.4.1 for better results."
|
||||
)
|
||||
would_change = default_check(
|
||||
release_status, chart_info, release_values, values_files
|
||||
)
|
||||
|
||||
if force or would_change:
|
||||
helm_cmd = deploy(
|
||||
module,
|
||||
helm_cmd,
|
||||
release_name,
|
||||
release_values,
|
||||
@@ -783,18 +1167,33 @@ 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,
|
||||
reset_then_reuse_values=reset_then_reuse_values,
|
||||
insecure_skip_tls_verify=insecure_skip_tls_verify,
|
||||
plain_http=plain_http,
|
||||
take_ownership=take_ownership,
|
||||
skip_schema_validation=skip_schema_validation,
|
||||
)
|
||||
changed = True
|
||||
|
||||
if module.check_mode:
|
||||
check_status = {"values": {"current": {}, "declared": {}}}
|
||||
check_status = {
|
||||
"values": {"current": {}, "declared": {}},
|
||||
"release_values": {"current": {}, "declared": {}},
|
||||
}
|
||||
if release_status:
|
||||
check_status["values"]["current"] = release_status["values"]
|
||||
check_status["values"]["current"] = release_status["release_values"]
|
||||
check_status["values"]["declared"] = release_status
|
||||
check_status["release_values"]["current"] = release_status["release_values"]
|
||||
check_status["release_values"]["declared"] = release_status
|
||||
|
||||
module.exit_json(
|
||||
changed=changed,
|
||||
@@ -814,13 +1213,13 @@ def main():
|
||||
**opt_result,
|
||||
)
|
||||
|
||||
rc, out, err = run_helm(module, helm_cmd)
|
||||
rc, out, err = module.run_helm_command(helm_cmd)
|
||||
|
||||
module.exit_json(
|
||||
changed=changed,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
status=get_release_status(module, helm_cmd_common, release_name),
|
||||
status=get_release_status(module, release_name, all_status=True),
|
||||
command=helm_cmd,
|
||||
**opt_result,
|
||||
)
|
||||
|
||||
@@ -14,13 +14,13 @@ module: helm_info
|
||||
|
||||
short_description: Get information from Helm package deployed inside the cluster
|
||||
|
||||
version_added: "0.11.0"
|
||||
version_added: 0.11.0
|
||||
|
||||
author:
|
||||
- Lucas Boisserie (@LucasBoisserie)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
description:
|
||||
@@ -52,7 +52,16 @@ options:
|
||||
- If set to C(uninstalling), show releases that are currently being uninstalled.
|
||||
type: list
|
||||
elements: str
|
||||
version_added: "2.3.0"
|
||||
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
|
||||
"""
|
||||
@@ -106,25 +115,56 @@ status:
|
||||
returned: always
|
||||
description: The Date of last update
|
||||
values:
|
||||
type: str
|
||||
type: dict
|
||||
returned: always
|
||||
description: Dict of Values used to deploy
|
||||
description:
|
||||
- Dict of Values used to deploy
|
||||
- This return value has been deprecated and will be removed in a release after
|
||||
2027-01-08. Use RV(status.release_values) instead.
|
||||
release_values:
|
||||
type: dict
|
||||
returned: always
|
||||
description:
|
||||
- Dict of Values used to deploy.
|
||||
version_added: 6.3.0
|
||||
hooks:
|
||||
type: list
|
||||
elements: dict
|
||||
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 AnsibleModule, missing_required_lib, env_fallback
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_values,
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
|
||||
|
||||
@@ -138,8 +178,8 @@ def get_release(state, release_name):
|
||||
|
||||
|
||||
# Get Release state from deployed release
|
||||
def get_release_status(module, command, release_name, release_state):
|
||||
list_command = command + " list --output=yaml"
|
||||
def get_release_status(module, release_name, release_state, get_all_values=False):
|
||||
list_command = module.get_helm_binary() + " list --output=yaml"
|
||||
|
||||
valid_release_states = [
|
||||
"all",
|
||||
@@ -156,7 +196,7 @@ def get_release_status(module, command, release_name, release_state):
|
||||
list_command += " --%s" % local_release_state
|
||||
|
||||
list_command += " --filter " + release_name
|
||||
rc, out, err = run_helm(module, list_command)
|
||||
rc, out, err = module.run_helm_command(list_command)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(
|
||||
@@ -171,71 +211,49 @@ def get_release_status(module, command, release_name, release_state):
|
||||
if release is None: # not install
|
||||
return None
|
||||
|
||||
release["values"] = get_values(module, command, release_name)
|
||||
release["release_values"] = module.get_values(release_name, get_all_values)
|
||||
release["values"] = release["release_values"]
|
||||
release["manifest"] = module.get_manifest(release_name)
|
||||
release["notes"] = module.get_notes(release_name)
|
||||
release["hooks"] = module.get_hooks(release_name)
|
||||
|
||||
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 = 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"),
|
||||
],
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec(),
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
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")
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
release_name = module.params.get("release_name")
|
||||
release_state = module.params.get("release_state")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = module.get_bin_path("helm", required=True)
|
||||
get_all_values = module.params.get("get_all_values")
|
||||
|
||||
release_status = get_release_status(
|
||||
module, helm_cmd_common, release_name, release_state
|
||||
module, release_name, release_state, get_all_values
|
||||
)
|
||||
|
||||
if release_status is not None:
|
||||
|
||||
@@ -12,11 +12,11 @@ DOCUMENTATION = r"""
|
||||
---
|
||||
module: helm_plugin
|
||||
short_description: Manage Helm plugins
|
||||
version_added: "1.0.0"
|
||||
version_added: 1.0.0
|
||||
author:
|
||||
- Abhijeet Kasurde (@Akasurde)
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
description:
|
||||
- Manages Helm plugins.
|
||||
options:
|
||||
@@ -47,7 +47,15 @@ options:
|
||||
- Ignored when C(state=absent) or C(state=latest).
|
||||
required: false
|
||||
type: str
|
||||
version_added: "2.3.0"
|
||||
version_added: 2.3.0
|
||||
verify:
|
||||
description:
|
||||
- Verify the plugin signature before installing.
|
||||
- This option requires helm version >= 4.0.0
|
||||
- Used with I(state=present).
|
||||
type: bool
|
||||
default: true
|
||||
version_added: 6.4.0
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.helm_common_options
|
||||
"""
|
||||
@@ -108,21 +116,25 @@ rc:
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
import copy
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_helm_plugin_list,
|
||||
AnsibleHelmModule,
|
||||
parse_helm_plugin_list,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
state=dict(
|
||||
type="str", default="present", choices=["present", "absent", "latest"]
|
||||
),
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
plugin_path=dict(
|
||||
type="str",
|
||||
),
|
||||
@@ -132,68 +144,65 @@ def main():
|
||||
plugin_version=dict(
|
||||
type="str",
|
||||
),
|
||||
# Helm options
|
||||
context=dict(
|
||||
state=dict(
|
||||
type="str",
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
default="present",
|
||||
choices=["present", "absent", "latest"],
|
||||
),
|
||||
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(
|
||||
verify=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"])
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
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(),
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
("state", "present", ("plugin_path",)),
|
||||
("state", "absent", ("plugin_name",)),
|
||||
("state", "latest", ("plugin_name",)),
|
||||
],
|
||||
mutually_exclusive=[
|
||||
("plugin_name", "plugin_path"),
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("kubeconfig", "validate_certs"),
|
||||
],
|
||||
mutually_exclusive=mutually_exclusive(),
|
||||
)
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
# Validate helm version >= 3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
state = module.params.get("state")
|
||||
|
||||
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"
|
||||
helm_cmd_common = module.get_helm_binary() + " plugin"
|
||||
|
||||
if state == "present":
|
||||
helm_cmd_common += " install %s" % module.params.get("plugin_path")
|
||||
plugin_version = module.params.get("plugin_version")
|
||||
verify = module.params.get("verify")
|
||||
if plugin_version is not None:
|
||||
helm_cmd_common += " --version=%s" % plugin_version
|
||||
if not verify:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("4.0.0"):
|
||||
module.warn(
|
||||
"verify parameter requires helm >= 4.0.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
helm_cmd_common += " --verify=false"
|
||||
if not module.check_mode:
|
||||
rc, out, err = run_helm(module, helm_cmd_common, fails_on_error=False)
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_cmd_common, fails_on_error=False
|
||||
)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
@@ -227,31 +236,26 @@ def main():
|
||||
)
|
||||
elif state == "absent":
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
plugins = parse_helm_plugin_list(output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
if not plugins:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found or is already uninstalled",
|
||||
command=helm_cmd_common + " list",
|
||||
command=command,
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
)
|
||||
|
||||
found = False
|
||||
for line in out:
|
||||
if line[0] == plugin_name:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
if all(plugin["name"] != plugin_name for plugin in plugins):
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found or is already uninstalled",
|
||||
command=helm_cmd_common + " list",
|
||||
command=command,
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
@@ -259,7 +263,9 @@ def main():
|
||||
|
||||
helm_uninstall_cmd = "%s uninstall %s" % (helm_cmd_common, plugin_name)
|
||||
if not module.check_mode:
|
||||
rc, out, err = run_helm(module, helm_uninstall_cmd, fails_on_error=False)
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_uninstall_cmd, fails_on_error=False
|
||||
)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
@@ -281,31 +287,26 @@ def main():
|
||||
)
|
||||
elif state == "latest":
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
rc, output, err, command = module.get_helm_plugin_list()
|
||||
plugins = parse_helm_plugin_list(output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
if not plugins:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found",
|
||||
command=helm_cmd_common + " list",
|
||||
command=command,
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
)
|
||||
|
||||
found = False
|
||||
for line in out:
|
||||
if line[0] == plugin_name:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
if all(plugin["name"] != plugin_name for plugin in plugins):
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Plugin not found",
|
||||
command=helm_cmd_common + " list",
|
||||
command=command,
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
@@ -313,7 +314,9 @@ def main():
|
||||
|
||||
helm_update_cmd = "%s update %s" % (helm_cmd_common, plugin_name)
|
||||
if not module.check_mode:
|
||||
rc, out, err = run_helm(module, helm_update_cmd, fails_on_error=False)
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_update_cmd, fails_on_error=False
|
||||
)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ DOCUMENTATION = r"""
|
||||
---
|
||||
module: helm_plugin_info
|
||||
short_description: Gather information about Helm plugins
|
||||
version_added: "1.0.0"
|
||||
version_added: 1.0.0
|
||||
author:
|
||||
- Abhijeet Kasurde (@Akasurde)
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
description:
|
||||
- Gather information about Helm plugins installed in namespace.
|
||||
options:
|
||||
@@ -70,96 +70,52 @@ rc:
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
import copy
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
get_helm_plugin_list,
|
||||
AnsibleHelmModule,
|
||||
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():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
argument_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
argument_spec.update(
|
||||
dict(
|
||||
plugin_name=dict(
|
||||
type="str",
|
||||
),
|
||||
# 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"),
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
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"
|
||||
# Validate helm version >= 3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
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(module, output=output.splitlines())
|
||||
|
||||
for line in out:
|
||||
if plugin_name is None:
|
||||
plugin_list.append(
|
||||
{"name": line[0], "version": line[1], "description": line[2]}
|
||||
)
|
||||
continue
|
||||
|
||||
if plugin_name == line[0]:
|
||||
plugin_list.append(
|
||||
{"name": line[0], "version": line[1], "description": line[2]}
|
||||
)
|
||||
break
|
||||
plugins = parse_helm_plugin_list(output=output.splitlines())
|
||||
if plugin_name is not None:
|
||||
plugins = [plugin for plugin in plugins if plugin.get("name") == plugin_name]
|
||||
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
command=helm_cmd_common + " list",
|
||||
command=command,
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
plugin_list=plugin_list,
|
||||
plugin_list=plugins,
|
||||
)
|
||||
|
||||
|
||||
|
||||
528
plugins/modules/helm_pull.py
Normal file
528
plugins/modules/helm_pull.py
Normal file
@@ -0,0 +1,528 @@
|
||||
#!/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.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. Alias C(insecure_skip_tls_verify) added in 5.3.0.
|
||||
type: bool
|
||||
default: False
|
||||
aliases: [ insecure_skip_tls_verify ]
|
||||
chart_devel:
|
||||
description:
|
||||
- Use development versions, too. Equivalent to version '>0.0.0-0'.
|
||||
- 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
|
||||
force:
|
||||
description:
|
||||
- Force download of the chart even if it already exists in the destination directory.
|
||||
- By default, the module will skip downloading if the chart with the same version already exists for idempotency.
|
||||
- When used with O(untar_chart=true), will remove any existing chart directory before extracting.
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.3.0
|
||||
destination:
|
||||
description:
|
||||
- location to write the chart.
|
||||
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
|
||||
plain_http:
|
||||
description:
|
||||
- Use HTTP instead of HTTPS when working with OCI registries
|
||||
- Requires Helm >= 3.13.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- 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
|
||||
|
||||
- name: Download Chart (force re-download even if exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
force: yes
|
||||
|
||||
- name: Download and untar chart (force re-extraction even if directory exists)
|
||||
kubernetes.core.helm_pull:
|
||||
chart_ref: redis
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
chart_version: '17.0.0'
|
||||
destination: /path/to/chart
|
||||
untar_chart: yes
|
||||
force: yes
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
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 ...
|
||||
msg:
|
||||
type: str
|
||||
description: A message indicating the result of the operation.
|
||||
returned: when chart already exists
|
||||
sample: Chart redis version 17.0.0 already exists in destination directory
|
||||
rc:
|
||||
type: int
|
||||
description: Helm pull command return code
|
||||
returned: always
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tarfile
|
||||
import uuid
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
HAS_YAML = True
|
||||
except ImportError:
|
||||
HAS_YAML = False
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
def extract_chart_name(chart_ref):
|
||||
"""
|
||||
Extract chart name from chart reference.
|
||||
|
||||
Args:
|
||||
chart_ref (str): Chart reference (name, URL, or OCI reference)
|
||||
|
||||
Returns:
|
||||
str: Extracted chart name
|
||||
"""
|
||||
chart_name = chart_ref.split("/")[-1]
|
||||
# Remove any query parameters or fragments from URL-based refs
|
||||
if "?" in chart_name:
|
||||
chart_name = chart_name.split("?")[0]
|
||||
if "#" in chart_name:
|
||||
chart_name = chart_name.split("#")[0]
|
||||
# Remove .tgz extension if present
|
||||
if chart_name.endswith(".tgz"):
|
||||
chart_name = chart_name[:-4]
|
||||
return chart_name
|
||||
|
||||
|
||||
def chart_exists(destination, chart_ref, chart_version, untar_chart):
|
||||
"""
|
||||
Check if the chart already exists in the destination directory.
|
||||
|
||||
For untarred charts: check if directory exists with Chart.yaml matching version
|
||||
For tarred charts: check if .tgz file exists and contains matching version
|
||||
|
||||
Args:
|
||||
destination (str): Destination directory path
|
||||
chart_ref (str): Chart reference (name or URL)
|
||||
chart_version (str): Chart version to check for
|
||||
untar_chart (bool): Whether to check for untarred or tarred chart
|
||||
|
||||
Returns:
|
||||
bool: True if chart with matching version exists, False otherwise
|
||||
"""
|
||||
# YAML is required for version checking
|
||||
if not HAS_YAML:
|
||||
return False
|
||||
|
||||
# Without version, we can't reliably check
|
||||
if not chart_version:
|
||||
return False
|
||||
|
||||
# Extract chart name from chart_ref using shared helper
|
||||
chart_name = extract_chart_name(chart_ref)
|
||||
|
||||
if untar_chart:
|
||||
# Check for extracted directory
|
||||
chart_dir = os.path.join(destination, chart_name)
|
||||
chart_yaml_path = os.path.join(chart_dir, "Chart.yaml")
|
||||
|
||||
if os.path.isdir(chart_dir) and os.path.isfile(chart_yaml_path):
|
||||
try:
|
||||
with open(chart_yaml_path, "r", encoding="utf-8") as chart_file:
|
||||
chart_metadata = yaml.safe_load(chart_file)
|
||||
# Ensure chart_metadata is a dict and has a version that matches
|
||||
if (
|
||||
chart_metadata
|
||||
and isinstance(chart_metadata, dict)
|
||||
and chart_metadata.get("version") == chart_version
|
||||
and chart_metadata.get("name") == chart_name
|
||||
):
|
||||
return True
|
||||
except (yaml.YAMLError, IOError, OSError, TypeError):
|
||||
# If we can't read or parse the file, treat as non-existent
|
||||
pass
|
||||
else:
|
||||
# Check for .tgz file
|
||||
chart_file = os.path.join(destination, f"{chart_name}-{chart_version}.tgz")
|
||||
|
||||
if os.path.isfile(chart_file):
|
||||
try:
|
||||
# Verify it's a valid tarball with matching version
|
||||
with tarfile.open(chart_file, "r:gz") as tar:
|
||||
# Try to extract Chart.yaml to verify version
|
||||
# Look for Chart.yaml at the expected path: <chart-name>/Chart.yaml
|
||||
expected_chart_yaml = f"{chart_name}/Chart.yaml"
|
||||
try:
|
||||
member = tar.getmember(expected_chart_yaml)
|
||||
chart_yaml_file = tar.extractfile(member)
|
||||
if chart_yaml_file:
|
||||
try:
|
||||
chart_metadata = yaml.safe_load(chart_yaml_file)
|
||||
# Ensure chart_metadata is a dict and has a version that matches
|
||||
if (
|
||||
chart_metadata
|
||||
and isinstance(chart_metadata, dict)
|
||||
and chart_metadata.get("version") == chart_version
|
||||
and chart_metadata.get("name") == chart_name
|
||||
):
|
||||
return True
|
||||
except (yaml.YAMLError, TypeError):
|
||||
# If we can't parse the YAML, treat as non-existent
|
||||
pass
|
||||
finally:
|
||||
chart_yaml_file.close()
|
||||
except KeyError:
|
||||
# Chart.yaml not found at expected path
|
||||
pass
|
||||
except (tarfile.TarError, yaml.YAMLError, IOError, OSError, TypeError):
|
||||
# If we can't read or parse the tarball, treat as non-existent
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
argspec = dict(
|
||||
chart_ref=dict(type="str", required=True),
|
||||
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, no_log=False),
|
||||
skip_tls_certs_check=dict(
|
||||
type="bool", default=False, aliases=["insecure_skip_tls_verify"]
|
||||
),
|
||||
chart_devel=dict(type="bool"),
|
||||
untar_chart=dict(type="bool", default=False),
|
||||
force=dict(type="bool", default=False),
|
||||
destination=dict(type="path", required=True),
|
||||
chart_ca_cert=dict(type="path"),
|
||||
chart_ssl_cert_file=dict(type="path"),
|
||||
chart_ssl_key_file=dict(type="path"),
|
||||
binary_path=dict(type="path"),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
)
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argspec,
|
||||
supports_check_mode=True,
|
||||
required_by=dict(
|
||||
repo_username=("repo_password"),
|
||||
repo_password=("repo_username"),
|
||||
),
|
||||
mutually_exclusive=[("chart_version", "chart_devel")],
|
||||
)
|
||||
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
helm_version = module.get_helm_version()
|
||||
|
||||
helm_pull_opt_versionning = dict(
|
||||
skip_tls_certs_check="3.3.0",
|
||||
chart_ca_cert="3.1.0",
|
||||
chart_ssl_cert_file="3.1.0",
|
||||
chart_ssl_key_file="3.1.0",
|
||||
plain_http="3.13.0",
|
||||
)
|
||||
|
||||
def test_version_requirement(opt):
|
||||
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"),
|
||||
plain_http=dict(key="plain-http"),
|
||||
)
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
# Check if chart already exists (idempotency)
|
||||
if module.params.get("chart_version") and not module.params.get("force"):
|
||||
chart_exists_locally = chart_exists(
|
||||
module.params.get("destination"),
|
||||
module.params.get("chart_ref"),
|
||||
module.params.get("chart_version"),
|
||||
module.params.get("untar_chart"),
|
||||
)
|
||||
|
||||
if chart_exists_locally:
|
||||
module.exit_json(
|
||||
failed=False,
|
||||
changed=False,
|
||||
msg="Chart {0} version {1} already exists in destination directory".format(
|
||||
module.params.get("chart_ref"), module.params.get("chart_version")
|
||||
),
|
||||
command="",
|
||||
stdout="",
|
||||
stderr="",
|
||||
rc=0,
|
||||
)
|
||||
|
||||
# When both untar_chart and force are enabled, we need to remove the existing chart directory
|
||||
# BEFORE running helm pull to prevent helm's "directory already exists" error.
|
||||
# We do this by:
|
||||
# 1. Renaming the existing directory to a temporary name (if it exists)
|
||||
# 2. Running helm pull
|
||||
# 3. On success: remove the temporary directory
|
||||
# 4. On failure: restore the temporary directory and report the error
|
||||
chart_dir_renamed = False
|
||||
chart_dir = None
|
||||
chart_dir_backup = None
|
||||
|
||||
if module.params.get("untar_chart") and module.params.get("force"):
|
||||
chart_name = extract_chart_name(module.params.get("chart_ref"))
|
||||
chart_dir = os.path.join(module.params.get("destination"), chart_name)
|
||||
|
||||
# Check if directory exists and contains a Chart.yaml (to be safe)
|
||||
if os.path.isdir(chart_dir):
|
||||
chart_yaml_path = os.path.join(chart_dir, "Chart.yaml")
|
||||
# Only rename if it looks like a Helm chart directory (have Chart.yaml)
|
||||
if os.path.isfile(chart_yaml_path):
|
||||
if not module.check_mode:
|
||||
# Rename to temporary backup name using uuid for uniqueness
|
||||
backup_suffix = uuid.uuid4().hex[:8]
|
||||
chart_dir_backup = os.path.join(
|
||||
module.params.get("destination"),
|
||||
f".{chart_name}_backup_{backup_suffix}",
|
||||
)
|
||||
os.rename(chart_dir, chart_dir_backup)
|
||||
chart_dir_renamed = True
|
||||
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_helm_command(helm_cmd_common, fails_on_error=False)
|
||||
|
||||
# Handle cleanup/restore based on helm command result
|
||||
if chart_dir_renamed:
|
||||
if rc == 0:
|
||||
# Success: remove the backup directory
|
||||
if os.path.isdir(chart_dir_backup):
|
||||
shutil.rmtree(chart_dir_backup)
|
||||
else:
|
||||
# Failure: restore the backup directory
|
||||
if os.path.isdir(chart_dir_backup) and not os.path.exists(chart_dir):
|
||||
os.rename(chart_dir_backup, chart_dir)
|
||||
else:
|
||||
rc, out, err = (0, "", "")
|
||||
|
||||
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()
|
||||
284
plugins/modules/helm_registry_auth.py
Normal file
284
plugins/modules/helm_registry_auth.py
Normal file
@@ -0,0 +1,284 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: © Ericsson AB 2024
|
||||
# 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_registry_auth
|
||||
|
||||
short_description: Helm registry authentication module
|
||||
|
||||
version_added: 5.1.0
|
||||
|
||||
author:
|
||||
- Yuriy Novostavskiy (@yurnov)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases) >= 3.8.0"
|
||||
|
||||
description:
|
||||
- Helm registry authentication module allows you to login C(helm registry login) and logout C(helm registry logout) from a Helm registry.
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Desired state of the registry.
|
||||
- If set to V(present) attempt to log in to the remote registry server using the URL specified in O(host).
|
||||
- If set to V(absent) attempt to log out from the remote registry server using the URL specified in O(host).
|
||||
- As helm >= 3.18.0 reports successful logout even if the user is not logged in, this module will report a change regardless of the current state.
|
||||
required: false
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
type: str
|
||||
host:
|
||||
description:
|
||||
- Provide a URL for accessing the registry.
|
||||
required: true
|
||||
aliases: [ registry_url ]
|
||||
type: str
|
||||
insecure:
|
||||
description:
|
||||
- Allow connections to SSL sites without certs.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
username:
|
||||
description:
|
||||
- Username for the registry.
|
||||
required: false
|
||||
type: str
|
||||
aliases: [ repo_username ]
|
||||
password:
|
||||
description:
|
||||
- Password for the registry.
|
||||
required: false
|
||||
type: str
|
||||
aliases: [ repo_password ]
|
||||
key_file:
|
||||
description:
|
||||
- Path to the client key SSL file for identify registry client using this key file.
|
||||
required: false
|
||||
type: path
|
||||
cert_file:
|
||||
description:
|
||||
- Path to the client certificate SSL file for identify registry client using this certificate file.
|
||||
required: false
|
||||
type: path
|
||||
ca_file:
|
||||
description:
|
||||
- Path to the CA certificate SSL file for verify registry server certificate.
|
||||
required: false
|
||||
type: path
|
||||
plain_http:
|
||||
description:
|
||||
- Use insecure HTTP connections for C(helm registry login).
|
||||
- Requires Helm >= 3.18.0
|
||||
required: false
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.4.0
|
||||
binary_path:
|
||||
description:
|
||||
- The path of a helm binary to use.
|
||||
required: false
|
||||
type: path
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Login to remote registry
|
||||
kubernetes.core.helm_registry_auth:
|
||||
username: admin
|
||||
password: "sample_password"
|
||||
host: localhost:5000
|
||||
|
||||
- name: Logout from remote registry
|
||||
kubernetes.core.helm_registry_auth:
|
||||
state: absent
|
||||
host: localhost:5000
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
stdout:
|
||||
type: str
|
||||
description: Full C(helm) command stdout, in case you want to display it or examine the event log
|
||||
returned: always
|
||||
stout_lines:
|
||||
type: list
|
||||
description: Full C(helm) command stdout, in case you want to display it or examine the event log
|
||||
returned: always
|
||||
stderr:
|
||||
type: str
|
||||
description: >-
|
||||
Full C(helm) command stderr, in case you want to display it or examine the event log.
|
||||
Please be note that helm binnary may print messages to stderr even if the command is successful.
|
||||
returned: always
|
||||
sample: 'Login Succeeded\n'
|
||||
stderr_lines:
|
||||
type: list
|
||||
description: Full C(helm) command stderr, in case you want to display it or examine the event log
|
||||
returned: always
|
||||
command:
|
||||
type: str
|
||||
description: Full C(helm) command executed
|
||||
returned: always
|
||||
sample: '/usr/local/bin/helm registry login oci-registry.domain.example --username=admin --password-stdin --insecure'
|
||||
failed:
|
||||
type: bool
|
||||
description: Indicate if the C(helm) command failed
|
||||
returned: always
|
||||
sample: false
|
||||
"""
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
def arg_spec():
|
||||
return dict(
|
||||
binary_path=dict(type="path", required=False),
|
||||
host=dict(type="str", aliases=["registry_url"], required=True),
|
||||
state=dict(default="present", choices=["present", "absent"], required=False),
|
||||
insecure=dict(type="bool", default=False, required=False),
|
||||
username=dict(type="str", aliases=["repo_username"], required=False),
|
||||
password=dict(
|
||||
type="str", aliases=["repo_password"], no_log=True, required=False
|
||||
),
|
||||
key_file=dict(type="path", required=False),
|
||||
cert_file=dict(type="path", required=False),
|
||||
ca_file=dict(type="path", required=False),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
)
|
||||
|
||||
|
||||
def login(
|
||||
command,
|
||||
host,
|
||||
insecure,
|
||||
username,
|
||||
password,
|
||||
key_file,
|
||||
cert_file,
|
||||
ca_file,
|
||||
plain_http,
|
||||
):
|
||||
login_command = command + " registry login " + host
|
||||
|
||||
if username is not None and password is not None:
|
||||
login_command += " --username=" + username + " --password-stdin"
|
||||
|
||||
if insecure:
|
||||
login_command += " --insecure"
|
||||
|
||||
if key_file is not None:
|
||||
login_command += " --key-file=" + key_file
|
||||
|
||||
if cert_file is not None:
|
||||
login_command += " --cert-file=" + cert_file
|
||||
|
||||
if ca_file is not None:
|
||||
login_command += " --ca-file=" + ca_file
|
||||
if plain_http:
|
||||
login_command += " --plain-http"
|
||||
|
||||
return login_command
|
||||
|
||||
|
||||
def logout(command, host):
|
||||
return command + " registry logout " + host
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=arg_spec(),
|
||||
required_together=[["username", "password"]],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
# Validate Helm version >=3.8.0
|
||||
module.validate_helm_version(version="3.8.0")
|
||||
|
||||
changed = False
|
||||
|
||||
host = module.params.get("host")
|
||||
state = module.params.get("state")
|
||||
insecure = module.params.get("insecure")
|
||||
username = module.params.get("username")
|
||||
password = module.params.get("password")
|
||||
key_file = module.params.get("key_file")
|
||||
cert_file = module.params.get("cert_file")
|
||||
ca_file = module.params.get("ca_file")
|
||||
plain_http = module.params.get("plain_http")
|
||||
|
||||
helm_version = module.get_helm_version()
|
||||
|
||||
if plain_http:
|
||||
if LooseVersion(helm_version) < LooseVersion("3.18.0"):
|
||||
module.warn(
|
||||
"plain_http option requires helm >= 3.18.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
# reset option
|
||||
plain_http = False
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
|
||||
if state == "absent":
|
||||
helm_cmd = logout(helm_cmd, host)
|
||||
changed = True
|
||||
elif state == "present":
|
||||
helm_cmd = login(
|
||||
helm_cmd,
|
||||
host,
|
||||
insecure,
|
||||
username,
|
||||
password,
|
||||
key_file,
|
||||
cert_file,
|
||||
ca_file,
|
||||
plain_http,
|
||||
)
|
||||
changed = True
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=changed, command=helm_cmd)
|
||||
|
||||
rc, out, err = module.run_helm_command(
|
||||
helm_cmd, data=password, fails_on_error=False
|
||||
)
|
||||
|
||||
if rc != 0:
|
||||
if state == "absent" and "Error: not logged in" in err:
|
||||
changed = False
|
||||
else:
|
||||
module.fail_json(
|
||||
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(
|
||||
rc, out, err
|
||||
),
|
||||
stderr=err,
|
||||
command=helm_cmd,
|
||||
)
|
||||
|
||||
if LooseVersion(helm_version) >= LooseVersion("3.18.0") and state == "absent":
|
||||
# https://github.com/ansible-collections/kubernetes.core/issues/944
|
||||
module.warn(
|
||||
"The helm_registry_auth is not idempotent with helm >= 3.18.0, always report a change."
|
||||
)
|
||||
|
||||
module.exit_json(changed=changed, stdout=out, stderr=err, command=helm_cmd)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -14,13 +14,13 @@ module: helm_repository
|
||||
|
||||
short_description: Manage Helm repositories.
|
||||
|
||||
version_added: "0.11.0"
|
||||
version_added: 0.11.0
|
||||
|
||||
author:
|
||||
- Lucas Boisserie (@LucasBoisserie)
|
||||
|
||||
requirements:
|
||||
- "helm (https://github.com/helm/helm/releases)"
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
description:
|
||||
@@ -76,12 +76,12 @@ options:
|
||||
description:
|
||||
- Provide a URL for accessing the API. Can also be specified via C(K8S_AUTH_HOST) environment variable.
|
||||
type: str
|
||||
version_added: "2.3.0"
|
||||
version_added: 2.3.0
|
||||
api_key:
|
||||
description:
|
||||
- Token used to authenticate with the API. Can also be specified via C(K8S_AUTH_API_KEY) environment variable.
|
||||
type: str
|
||||
version_added: "2.3.0"
|
||||
version_added: 2.3.0
|
||||
validate_certs:
|
||||
description:
|
||||
- Whether or not to verify the API server's SSL certificates. Can also be specified via C(K8S_AUTH_VERIFY_SSL)
|
||||
@@ -89,14 +89,43 @@ options:
|
||||
type: bool
|
||||
aliases: [ verify_ssl ]
|
||||
default: True
|
||||
version_added: "2.3.0"
|
||||
version_added: 2.3.0
|
||||
ca_cert:
|
||||
description:
|
||||
- Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to
|
||||
avoid certificate validation errors. Can also be specified via C(K8S_AUTH_SSL_CA_CERT) environment variable.
|
||||
type: path
|
||||
aliases: [ ssl_ca_cert ]
|
||||
version_added: "2.3.0"
|
||||
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
|
||||
insecure_skip_tls_verify:
|
||||
description:
|
||||
- Skip tls certificate checks for the repository url.
|
||||
type: bool
|
||||
default: False
|
||||
aliases: [ skip_tls_certs_check ]
|
||||
version_added: "5.3.0"
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -144,18 +173,26 @@ 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 AnsibleModule, env_fallback, missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
# Get repository from all repositories added
|
||||
@@ -168,10 +205,10 @@ def get_repository(state, repo_name):
|
||||
|
||||
|
||||
# Get repository status
|
||||
def get_repository_status(module, command, repository_name):
|
||||
list_command = command + " repo list --output=yaml"
|
||||
def get_repository_status(module, repository_name):
|
||||
list_command = module.get_helm_binary() + " repo list --output=yaml"
|
||||
|
||||
rc, out, err = run_helm(module, list_command, fails_on_error=False)
|
||||
rc, out, err = module.run_helm_command(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:
|
||||
@@ -195,6 +232,8 @@ def install_repository(
|
||||
repository_username,
|
||||
repository_password,
|
||||
pass_credentials,
|
||||
force_update,
|
||||
insecure_skip_tls_verify,
|
||||
):
|
||||
install_command = command + " repo add " + repository_name + " " + repository_url
|
||||
|
||||
@@ -205,6 +244,12 @@ def install_repository(
|
||||
if pass_credentials:
|
||||
install_command += " --pass-credentials"
|
||||
|
||||
if force_update:
|
||||
install_command += " --force-update"
|
||||
|
||||
if insecure_skip_tls_verify:
|
||||
install_command += " --insecure-skip-tls-verify"
|
||||
|
||||
return install_command
|
||||
|
||||
|
||||
@@ -215,12 +260,10 @@ def delete_repository(command, repository_name):
|
||||
return remove_command
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
repo_name=dict(type="str", aliases=["name"], required=True),
|
||||
repo_url=dict(type="str", aliases=["url"]),
|
||||
repo_username=dict(type="str", aliases=["username"]),
|
||||
@@ -228,54 +271,53 @@ def main():
|
||||
repo_state=dict(
|
||||
default="present", choices=["present", "absent"], aliases=["state"]
|
||||
),
|
||||
pass_credentials=dict(type="bool", default=False),
|
||||
# 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"]),
|
||||
pass_credentials=dict(type="bool", default=False, no_log=True),
|
||||
force_update=dict(type="bool", default=False, aliases=["force"]),
|
||||
insecure_skip_tls_verify=dict(
|
||||
type="bool", default=False, aliases=["skip_tls_certs_check"]
|
||||
),
|
||||
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"])
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleHelmModule(
|
||||
argument_spec=argument_spec(),
|
||||
required_together=[["repo_username", "repo_password"]],
|
||||
required_if=[("repo_state", "present", ["repo_url"])],
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
# Validate Helm version >= 3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
changed = False
|
||||
|
||||
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")
|
||||
insecure_skip_tls_verify = module.params.get("insecure_skip_tls_verify")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd = bin_path
|
||||
else:
|
||||
helm_cmd = module.get_bin_path("helm", required=True)
|
||||
helm_cmd = module.get_helm_binary()
|
||||
|
||||
repository_status = get_repository_status(module, helm_cmd, repo_name)
|
||||
repository_status = get_repository_status(module, 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:
|
||||
if repository_status is None or force_update:
|
||||
helm_cmd = install_repository(
|
||||
helm_cmd,
|
||||
repo_name,
|
||||
@@ -283,6 +325,8 @@ def main():
|
||||
repo_username,
|
||||
repo_password,
|
||||
pass_credentials,
|
||||
force_update,
|
||||
insecure_skip_tls_verify,
|
||||
)
|
||||
changed = True
|
||||
elif repository_status["url"] != repo_url:
|
||||
@@ -295,7 +339,7 @@ def main():
|
||||
elif not changed:
|
||||
module.exit_json(changed=False, repo_name=repo_name, repo_url=repo_url)
|
||||
|
||||
rc, out, err = run_helm(module, helm_cmd)
|
||||
rc, out, err = module.run_helm_command(helm_cmd)
|
||||
|
||||
if repo_password is not None:
|
||||
helm_cmd = helm_cmd.replace(repo_password, "******")
|
||||
|
||||
@@ -21,6 +21,10 @@ author:
|
||||
description:
|
||||
- Render chart templates to an output directory or as text of concatenated yaml documents.
|
||||
|
||||
requirements:
|
||||
- "helm >= 3.0.0 (https://github.com/helm/helm/releases)"
|
||||
- "yaml (https://pypi.org/project/PyYAML/)"
|
||||
|
||||
options:
|
||||
binary_path:
|
||||
description:
|
||||
@@ -45,6 +49,21 @@ 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.
|
||||
@@ -57,12 +76,26 @@ options:
|
||||
- If the directory already exists, it will be overwritten.
|
||||
required: false
|
||||
type: path
|
||||
insecure_registry:
|
||||
description:
|
||||
- Skip TLS certificate checks for the chart download
|
||||
required: false
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 5.1.0
|
||||
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.3.0
|
||||
version_added: 2.4.0
|
||||
release_values:
|
||||
description:
|
||||
- Values to pass to chart.
|
||||
@@ -76,7 +109,8 @@ options:
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 2.3.0
|
||||
default: []
|
||||
version_added: 2.4.0
|
||||
values_files:
|
||||
description:
|
||||
- Value files to pass to chart.
|
||||
@@ -92,6 +126,38 @@ 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
|
||||
plain_http:
|
||||
description:
|
||||
- Use HTTP instead of HTTPS when working with OCI registries
|
||||
- Requires Helm >= 3.13.0
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 6.1.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -154,12 +220,18 @@ 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 AnsibleModule, missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
AnsibleHelmModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
|
||||
|
||||
def template(
|
||||
@@ -167,14 +239,28 @@ def template(
|
||||
chart_ref,
|
||||
chart_repo_url=None,
|
||||
chart_version=None,
|
||||
dependency_update=None,
|
||||
disable_hook=None,
|
||||
output_dir=None,
|
||||
insecure_registry=None,
|
||||
show_only=None,
|
||||
release_values=None,
|
||||
release_name=None,
|
||||
release_namespace=None,
|
||||
release_values=None,
|
||||
values_files=None,
|
||||
include_crds=False,
|
||||
set_values=None,
|
||||
plain_http=False,
|
||||
):
|
||||
cmd += " template " + chart_ref
|
||||
cmd += " template "
|
||||
|
||||
if release_name:
|
||||
cmd += release_name + " "
|
||||
|
||||
cmd += chart_ref
|
||||
|
||||
if dependency_update:
|
||||
cmd += " --dependency-update"
|
||||
|
||||
if chart_repo_url:
|
||||
cmd += " --repo=" + chart_repo_url
|
||||
@@ -182,9 +268,18 @@ def template(
|
||||
if chart_version:
|
||||
cmd += " --version=" + chart_version
|
||||
|
||||
if disable_hook:
|
||||
cmd += " --no-hooks"
|
||||
|
||||
if output_dir:
|
||||
cmd += " --output-dir=" + output_dir
|
||||
|
||||
if insecure_registry:
|
||||
cmd += " --insecure-skip-tls-verify"
|
||||
|
||||
if plain_http:
|
||||
cmd += " --plain-http"
|
||||
|
||||
if show_only:
|
||||
for template in show_only:
|
||||
cmd += " -s " + template
|
||||
@@ -205,64 +300,100 @@ def template(
|
||||
if include_crds:
|
||||
cmd += " --include-crds"
|
||||
|
||||
if set_values:
|
||||
cmd += " " + set_values
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
module = AnsibleHelmModule(
|
||||
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"),
|
||||
insecure_registry=dict(type="bool", default=False),
|
||||
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"),
|
||||
plain_http=dict(type="bool", default=False),
|
||||
),
|
||||
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")
|
||||
insecure_registry = module.params.get("insecure_registry")
|
||||
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")
|
||||
plain_http = module.params.get("plain_http")
|
||||
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
helm_cmd = bin_path or module.get_bin_path("helm", required=True)
|
||||
# Validate Helm version >=3.0.0
|
||||
module.validate_helm_version()
|
||||
|
||||
helm_cmd = module.get_helm_binary()
|
||||
|
||||
if plain_http:
|
||||
helm_version = module.get_helm_version()
|
||||
if LooseVersion(helm_version) < LooseVersion("3.13.0"):
|
||||
module.fail_json(
|
||||
msg="plain_http requires helm >= 3.13.0, current version is {0}".format(
|
||||
helm_version
|
||||
)
|
||||
)
|
||||
|
||||
if update_repo_cache:
|
||||
update_cmd = helm_cmd + " repo update"
|
||||
run_helm(module, update_cmd)
|
||||
module.run_helm_command(update_cmd)
|
||||
|
||||
set_values_args = None
|
||||
if set_values:
|
||||
set_values_args = module.get_helm_set_values_args(set_values)
|
||||
|
||||
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,
|
||||
insecure_registry=insecure_registry,
|
||||
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,
|
||||
plain_http=plain_http,
|
||||
)
|
||||
|
||||
if not check_mode:
|
||||
rc, out, err = run_helm(module, tmpl_cmd)
|
||||
rc, out, err = module.run_helm_command(tmpl_cmd)
|
||||
else:
|
||||
out = err = ""
|
||||
rc = 0
|
||||
|
||||
@@ -57,15 +57,14 @@ options:
|
||||
- Whether to override the default patch merge approach with a specific type. By default, the strategic
|
||||
merge will typically be used.
|
||||
- For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may
|
||||
want to use C(merge) if you see "strategic merge patch format is not supported"
|
||||
want to use C(merge) if you see "strategic merge patch format is not supported".
|
||||
- See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)
|
||||
- If more than one C(merge_type) is given, the merge_types will be tried in order. This defaults to
|
||||
C(['strategic-merge', 'merge']), which is ideal for using the same parameters on resource kinds that
|
||||
combine Custom Resources and built-in resources.
|
||||
- mutually exclusive with C(apply)
|
||||
- I(merge_type=json) is deprecated and will be removed in version 3.0.0. Please use M(kubernetes.core.k8s_json_patch) instead.
|
||||
- Mutually exclusive with C(apply).
|
||||
- I(merge_type=json) has been removed in version 4.0.0. Please use M(kubernetes.core.k8s_json_patch) instead.
|
||||
choices:
|
||||
- json
|
||||
- merge
|
||||
- strategic-merge
|
||||
type: list
|
||||
@@ -101,7 +100,7 @@ options:
|
||||
- C(apply) compares the desired resource definition with the previously supplied resource definition,
|
||||
ignoring properties that are automatically generated
|
||||
- C(apply) works better with Services than 'force=yes'
|
||||
- mutually exclusive with C(merge_type)
|
||||
- Mutually exclusive with C(merge_type).
|
||||
default: False
|
||||
type: bool
|
||||
template:
|
||||
@@ -172,10 +171,32 @@ 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: 3.0.0
|
||||
aliases:
|
||||
- all
|
||||
hidden_fields:
|
||||
description:
|
||||
- Hide fields matching this option in the result
|
||||
- An example might be C(hidden_fields=[metadata.managedFields])
|
||||
or V(hidden_fields=[spec.containers[0].env[3].value])
|
||||
or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 3.0.0
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
- "jsonpatch"
|
||||
"""
|
||||
@@ -232,6 +253,14 @@ 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
|
||||
@@ -249,10 +278,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:
|
||||
@@ -335,6 +364,14 @@ 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"""
|
||||
@@ -346,28 +383,24 @@ result:
|
||||
contains:
|
||||
api_version:
|
||||
description: The versioned schema of this representation of an object.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
kind:
|
||||
description: Represents the REST resource this object represents.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
metadata:
|
||||
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
spec:
|
||||
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
status:
|
||||
description: Current status details for the object.
|
||||
returned: success
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
items:
|
||||
description: Returned only when multiple yaml documents are passed to src or resource_definition
|
||||
returned: when resource_definition or src contains list of objects
|
||||
type: list
|
||||
duration:
|
||||
description: elapsed time of task in seconds
|
||||
returned: when C(wait) is true
|
||||
@@ -377,6 +410,46 @@ result:
|
||||
description: error while trying to create/delete the object.
|
||||
returned: error
|
||||
type: complex
|
||||
results:
|
||||
description: An array of created, patched, or otherwise present objects.
|
||||
returned: when O(resource_definition) or O(src) contains a list of objects.
|
||||
type: complex
|
||||
contains:
|
||||
api_version:
|
||||
description: The versioned schema of this representation of an object.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
kind:
|
||||
description: Represents the REST resource this object represents.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: str
|
||||
metadata:
|
||||
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
spec:
|
||||
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
status:
|
||||
description: Current status details for the object.
|
||||
returned: when O(resource_definition) or O(src) contains a single object.
|
||||
type: complex
|
||||
duration:
|
||||
description: elapsed time of task in seconds
|
||||
returned: when C(wait) is true
|
||||
type: int
|
||||
sample: 48
|
||||
error:
|
||||
description: error while trying to create/delete the object.
|
||||
returned: error
|
||||
type: complex
|
||||
method:
|
||||
description:
|
||||
- The method used to deploy the resource.
|
||||
returned: success
|
||||
type: str
|
||||
sample: create
|
||||
"""
|
||||
|
||||
import copy
|
||||
@@ -386,10 +459,19 @@ from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule impo
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
WAIT_ARG_SPEC,
|
||||
DELETE_OPTS_ARG_SPEC,
|
||||
NAME_ARG_SPEC,
|
||||
RESOURCE_ARG_SPEC,
|
||||
DELETE_OPTS_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,
|
||||
)
|
||||
|
||||
|
||||
@@ -414,7 +496,7 @@ def argspec():
|
||||
argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC))
|
||||
argument_spec.update(copy.deepcopy(WAIT_ARG_SPEC))
|
||||
argument_spec["merge_type"] = dict(
|
||||
type="list", elements="str", choices=["json", "merge", "strategic-merge"]
|
||||
type="list", elements="str", choices=["merge", "strategic-merge"]
|
||||
)
|
||||
argument_spec["validate"] = dict(type="dict", default=None, options=validate_spec())
|
||||
argument_spec["append_hash"] = dict(type="bool", default=False)
|
||||
@@ -433,32 +515,12 @@ 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"),
|
||||
@@ -467,19 +529,17 @@ def main():
|
||||
("template", "src"),
|
||||
("name", "generate_name"),
|
||||
]
|
||||
module = AnsibleModule(
|
||||
|
||||
module = AnsibleK8SModule(
|
||||
module_class=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,
|
||||
)
|
||||
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
try:
|
||||
run_module(module)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -11,7 +11,7 @@ __metaclass__ = type
|
||||
DOCUMENTATION = r"""
|
||||
module: k8s_cluster_info
|
||||
|
||||
version_added: "0.11.1"
|
||||
version_added: 0.11.1
|
||||
|
||||
short_description: Describe Kubernetes (K8s) cluster, APIs available and their respective versions
|
||||
|
||||
@@ -34,8 +34,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -141,36 +141,32 @@ 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):
|
||||
invalidate_cache = boolean(
|
||||
module.params.get("invalidate_cache", True), strict=False
|
||||
)
|
||||
if invalidate_cache:
|
||||
if module.params.get("invalidate_cache"):
|
||||
client.resources.invalidate_cache()
|
||||
results = defaultdict(dict)
|
||||
for resource in list(client.resources):
|
||||
@@ -204,7 +200,7 @@ def execute_module(module, client):
|
||||
|
||||
version_info = {
|
||||
"client": version,
|
||||
"server": client.version,
|
||||
"server": client.client.version,
|
||||
}
|
||||
module.exit_json(
|
||||
changed=False, apis=results, connection=connection, version=version_info
|
||||
@@ -218,18 +214,18 @@ def argspec():
|
||||
|
||||
|
||||
def main():
|
||||
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 (
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
)
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
execute_module(module, client=get_api_client(module=module))
|
||||
try:
|
||||
execute_module(module, client=get_api_client(module=module))
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -14,7 +14,7 @@ module: k8s_cp
|
||||
|
||||
short_description: Copy files and directories to and from pod.
|
||||
|
||||
version_added: "2.2.0"
|
||||
version_added: 2.2.0
|
||||
|
||||
author:
|
||||
- Aubin Bikouo (@abikouo)
|
||||
@@ -26,8 +26,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
|
||||
options:
|
||||
namespace:
|
||||
@@ -79,6 +79,7 @@ options:
|
||||
|
||||
notes:
|
||||
- the tar binary is required on the container when copying from local filesystem to pod.
|
||||
- the (init) container has to be started before you copy files or directories to it.
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -119,7 +120,7 @@ EXAMPLES = r"""
|
||||
state: from_pod
|
||||
|
||||
# copy content into a file in the remote pod
|
||||
- name: Copy /tmp/foo from a remote pod to /tmp/bar locally
|
||||
- name: Copy content into a file in the remote pod
|
||||
kubernetes.core.k8s_cp:
|
||||
state: to_pod
|
||||
namespace: some-namespace
|
||||
@@ -139,6 +140,7 @@ result:
|
||||
|
||||
import copy
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
@@ -150,7 +152,18 @@ from ansible_collections.kubernetes.core.plugins.module_utils.copy import (
|
||||
K8SCopyToPod,
|
||||
check_pod,
|
||||
)
|
||||
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.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 argspec():
|
||||
@@ -171,23 +184,9 @@ def argspec():
|
||||
|
||||
|
||||
def execute_module(module):
|
||||
|
||||
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)
|
||||
client = get_api_client(module=module)
|
||||
svc = K8sService(client, module)
|
||||
containers = check_pod(svc)
|
||||
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"
|
||||
@@ -195,9 +194,9 @@ def execute_module(module):
|
||||
|
||||
state = module.params.get("state")
|
||||
if state == "to_pod":
|
||||
k8s_copy = K8SCopyToPod(module, k8s_ansible_mixin.client)
|
||||
k8s_copy = K8SCopyToPod(module, client.client)
|
||||
else:
|
||||
k8s_copy = K8SCopyFromPod(module, k8s_ansible_mixin.client)
|
||||
k8s_copy = K8SCopyFromPod(module, client.client)
|
||||
|
||||
try:
|
||||
k8s_copy.run()
|
||||
@@ -206,15 +205,20 @@ def execute_module(module):
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
module = AnsibleK8SModule(
|
||||
module_class=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,
|
||||
)
|
||||
|
||||
execute_module(module)
|
||||
try:
|
||||
execute_module(module)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -15,7 +15,7 @@ module: k8s_drain
|
||||
|
||||
short_description: Drain, Cordon, or Uncordon node in k8s cluster
|
||||
|
||||
version_added: "2.2.0"
|
||||
version_added: 2.2.0
|
||||
|
||||
author: Aubin Bikouo (@abikouo)
|
||||
|
||||
@@ -41,8 +41,18 @@ 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: 3.0.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).
|
||||
@@ -87,8 +97,8 @@ options:
|
||||
type: int
|
||||
|
||||
requirements:
|
||||
- python >= 3.6
|
||||
- kubernetes >= 12.0.0
|
||||
- python >= 3.9
|
||||
- kubernetes >= 24.2.0
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -96,14 +106,15 @@ EXAMPLES = r"""
|
||||
kubernetes.core.k8s_drain:
|
||||
state: drain
|
||||
name: foo
|
||||
force: yes
|
||||
delete_options:
|
||||
force: yes
|
||||
|
||||
- name: Drain node "foo", but abort if there are pods not managed by a ReplicationController, Job, or DaemonSet, and use a grace period of 15 minutes.
|
||||
kubernetes.core.k8s_drain:
|
||||
state: drain
|
||||
name: foo
|
||||
delete_options:
|
||||
terminate_grace_period: 900
|
||||
terminate_grace_period: 900
|
||||
|
||||
- name: Mark node "foo" as schedulable.
|
||||
kubernetes.core.k8s_drain:
|
||||
@@ -115,6 +126,13 @@ 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"""
|
||||
@@ -126,22 +144,32 @@ result:
|
||||
"""
|
||||
|
||||
import copy
|
||||
import json
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
)
|
||||
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.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.models import V1DeleteOptions, V1ObjectMeta
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
from kubernetes.client.models import V1DeleteOptions, V1ObjectMeta
|
||||
except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
@@ -161,6 +189,17 @@ except ImportError:
|
||||
HAS_EVICTION_API = False
|
||||
|
||||
|
||||
def format_dynamic_api_exc(exc):
|
||||
if exc.body:
|
||||
if exc.headers and exc.headers.get("Content-Type") == "application/json":
|
||||
message = json.loads(exc.body).get("message")
|
||||
if message:
|
||||
return message
|
||||
return exc.body
|
||||
else:
|
||||
return "%s Reason: %s" % (exc.status, exc.reason)
|
||||
|
||||
|
||||
def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
k8s_kind_mirror = "kubernetes.io/config.mirror"
|
||||
daemonSet, unmanaged, mirror, localStorage, to_delete = [], [], [], [], []
|
||||
@@ -191,7 +230,7 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
else:
|
||||
to_delete.append((pod.metadata.namespace, pod.metadata.name))
|
||||
|
||||
warnings, errors = [], []
|
||||
warnings, errors, info = [], [], []
|
||||
if unmanaged:
|
||||
pod_names = ",".join([pod[0] + "/" + pod[1] for pod in unmanaged])
|
||||
if not force:
|
||||
@@ -203,7 +242,7 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
)
|
||||
else:
|
||||
# Pod not managed will be deleted as 'force' is true
|
||||
warnings.append(
|
||||
info.append(
|
||||
"Deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: {0}.".format(
|
||||
pod_names
|
||||
)
|
||||
@@ -225,7 +264,7 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
"cannot delete Pods with local storage: {0}.".format(pod_names)
|
||||
)
|
||||
else:
|
||||
warnings.append("Deleting Pods with local storage: {0}.".format(pod_names))
|
||||
info.append("Deleting Pods with local storage: {0}.".format(pod_names))
|
||||
for pod in localStorage:
|
||||
to_delete.append((pod[0], pod[1]))
|
||||
|
||||
@@ -239,35 +278,14 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
|
||||
)
|
||||
)
|
||||
else:
|
||||
warnings.append("Ignoring DaemonSet-managed Pods: {0}.".format(pod_names))
|
||||
return to_delete, warnings, errors
|
||||
info.append("Ignoring DaemonSet-managed Pods: {0}.".format(pod_names))
|
||||
return to_delete, warnings, errors, info
|
||||
|
||||
|
||||
class K8sDrainAnsible(object):
|
||||
def __init__(self, module):
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
def __init__(self, module, client):
|
||||
self._module = module
|
||||
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()
|
||||
self._api_instance = core_v1_api.CoreV1Api(client.client)
|
||||
|
||||
# delete options
|
||||
self._drain_options = module.params.get("delete_options", {})
|
||||
@@ -286,16 +304,19 @@ class K8sDrainAnsible(object):
|
||||
return (datetime.now() - start).seconds
|
||||
|
||||
response = None
|
||||
pod = pods.pop()
|
||||
pod = None
|
||||
while (_elapsed_time() < wait_timeout or wait_timeout == 0) and pods:
|
||||
if not pod:
|
||||
pod = pods.pop()
|
||||
pod = pods[-1]
|
||||
try:
|
||||
response = self._api_instance.read_namespaced_pod(
|
||||
namespace=pod[0], name=pod[1]
|
||||
)
|
||||
if not response:
|
||||
if not response or response.spec.node_name != self._module.params.get(
|
||||
"name"
|
||||
):
|
||||
pod = None
|
||||
del pods[-1]
|
||||
time.sleep(wait_sleep)
|
||||
except ApiException as exc:
|
||||
if exc.reason != "Not Found":
|
||||
@@ -303,6 +324,7 @@ class K8sDrainAnsible(object):
|
||||
msg="Exception raised: {0}".format(exc.reason)
|
||||
)
|
||||
pod = None
|
||||
del pods[-1]
|
||||
except Exception as e:
|
||||
self._module.fail_json(msg="Exception raised: {0}".format(to_native(e)))
|
||||
if not pods:
|
||||
@@ -312,24 +334,25 @@ class K8sDrainAnsible(object):
|
||||
def evict_pods(self, pods):
|
||||
for namespace, name in pods:
|
||||
try:
|
||||
if self._drain_options.get("disable_eviction"):
|
||||
self._api_instance.delete_namespaced_pod(
|
||||
name=name, namespace=namespace, body=self._delete_options
|
||||
)
|
||||
else:
|
||||
body = v1_eviction(
|
||||
delete_options=self._delete_options,
|
||||
metadata=V1ObjectMeta(name=name, namespace=namespace),
|
||||
)
|
||||
self._api_instance.create_namespaced_pod_eviction(
|
||||
name=name, namespace=namespace, body=body
|
||||
)
|
||||
if not self._module.check_mode:
|
||||
if self._drain_options.get("disable_eviction"):
|
||||
self._api_instance.delete_namespaced_pod(
|
||||
name=name, namespace=namespace, body=self._delete_options
|
||||
)
|
||||
else:
|
||||
body = v1_eviction(
|
||||
delete_options=self._delete_options,
|
||||
metadata=V1ObjectMeta(name=name, namespace=namespace),
|
||||
)
|
||||
self._api_instance.create_namespaced_pod_eviction(
|
||||
name=name, namespace=namespace, body=body
|
||||
)
|
||||
self._changed = True
|
||||
except ApiException as exc:
|
||||
if exc.reason != "Not Found":
|
||||
self._module.fail_json(
|
||||
msg="Failed to delete pod {0}/{1} due to: {2}".format(
|
||||
namespace, name, exc.reason
|
||||
namespace, name, to_native(format_dynamic_api_exc(exc))
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
@@ -339,11 +362,19 @@ class K8sDrainAnsible(object):
|
||||
)
|
||||
)
|
||||
|
||||
def list_pods(self):
|
||||
params = {"field_selector": "spec.nodeName=" + self._module.params.get("name")}
|
||||
pod_selectors = self._module.params.get("pod_selectors")
|
||||
if pod_selectors:
|
||||
params["label_selector"] = ",".join(pod_selectors)
|
||||
return self._api_instance.list_pod_for_all_namespaces(**params)
|
||||
|
||||
def delete_or_evict_pods(self, node_unschedulable):
|
||||
# Mark node as unschedulable
|
||||
result = []
|
||||
if not node_unschedulable:
|
||||
self.patch_node(unschedulable=True)
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=True)
|
||||
result.append(
|
||||
"node {0} marked unschedulable.".format(self._module.params.get("name"))
|
||||
)
|
||||
@@ -358,22 +389,18 @@ class K8sDrainAnsible(object):
|
||||
def _revert_node_patch():
|
||||
if self._changed:
|
||||
self._changed = False
|
||||
self.patch_node(unschedulable=False)
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=False)
|
||||
|
||||
try:
|
||||
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
|
||||
)
|
||||
pod_list = self.list_pods()
|
||||
# Filter pods
|
||||
force = self._drain_options.get("force", False)
|
||||
ignore_daemonset = self._drain_options.get("ignore_daemonsets", False)
|
||||
delete_emptydir_data = self._drain_options.get(
|
||||
"delete_emptydir_data", False
|
||||
)
|
||||
pods, warnings, errors = filter_pods(
|
||||
pods, warnings, errors, info = filter_pods(
|
||||
pod_list.items, force, ignore_daemonset, delete_emptydir_data
|
||||
)
|
||||
if errors:
|
||||
@@ -403,21 +430,28 @@ class K8sDrainAnsible(object):
|
||||
if pods:
|
||||
self.evict_pods(pods)
|
||||
number_pod = len(pods)
|
||||
if self._drain_options.get("wait_timeout") is not None:
|
||||
warn = self.wait_for_pod_deletion(
|
||||
pods,
|
||||
self._drain_options.get("wait_timeout"),
|
||||
self._drain_options.get("wait_sleep"),
|
||||
if self._module.check_mode:
|
||||
result.append(
|
||||
"Would have deleted {0} Pod(s) from node if not in check mode.".format(
|
||||
number_pod
|
||||
)
|
||||
)
|
||||
if warn:
|
||||
warnings.append(warn)
|
||||
result.append("{0} Pod(s) deleted from node.".format(number_pod))
|
||||
else:
|
||||
wait_timeout = self._drain_options.get("wait_timeout")
|
||||
wait_sleep = self._drain_options.get("wait_sleep")
|
||||
if wait_timeout is not None:
|
||||
warn = self.wait_for_pod_deletion(pods, wait_timeout, wait_sleep)
|
||||
if warn:
|
||||
warnings.append(warn)
|
||||
result.append("{0} Pod(s) deleted from node.".format(number_pod))
|
||||
if warnings:
|
||||
return dict(result=" ".join(result), warnings=warnings)
|
||||
for warning in warnings:
|
||||
self._module.warn(warning)
|
||||
for line in info:
|
||||
self._module.debug(line)
|
||||
return dict(result=" ".join(result))
|
||||
|
||||
def patch_node(self, unschedulable):
|
||||
|
||||
body = {"spec": {"unschedulable": unschedulable}}
|
||||
try:
|
||||
self._api_instance.patch_node(
|
||||
@@ -429,7 +463,6 @@ class K8sDrainAnsible(object):
|
||||
)
|
||||
|
||||
def execute_module(self):
|
||||
|
||||
state = self._module.params.get("state")
|
||||
name = self._module.params.get("name")
|
||||
try:
|
||||
@@ -456,7 +489,8 @@ class K8sDrainAnsible(object):
|
||||
self._module.exit_json(
|
||||
result="node {0} already marked unschedulable.".format(name)
|
||||
)
|
||||
self.patch_node(unschedulable=True)
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=True)
|
||||
result["result"] = "node {0} marked unschedulable.".format(name)
|
||||
self._changed = True
|
||||
|
||||
@@ -465,7 +499,8 @@ class K8sDrainAnsible(object):
|
||||
self._module.exit_json(
|
||||
result="node {0} already marked schedulable.".format(name)
|
||||
)
|
||||
self.patch_node(unschedulable=False)
|
||||
if not self._module.check_mode:
|
||||
self.patch_node(unschedulable=False)
|
||||
result["result"] = "node {0} marked schedulable.".format(name)
|
||||
self._changed = True
|
||||
|
||||
@@ -497,13 +532,20 @@ 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 = AnsibleModule(argument_spec=argspec())
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
)
|
||||
|
||||
if not HAS_EVICTION_API:
|
||||
module.fail_json(
|
||||
@@ -512,8 +554,12 @@ def main():
|
||||
error=to_native(k8s_import_exception),
|
||||
)
|
||||
|
||||
k8s_drain = K8sDrainAnsible(module)
|
||||
k8s_drain.execute_module()
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -15,7 +15,7 @@ module: k8s_exec
|
||||
|
||||
short_description: Execute command in Pod
|
||||
|
||||
version_added: "0.10.0"
|
||||
version_added: 0.10.0
|
||||
|
||||
author: "Tristan de Cacqueray (@tristanC)"
|
||||
|
||||
@@ -26,8 +26,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
|
||||
notes:
|
||||
@@ -131,18 +131,27 @@ except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
|
||||
from ansible.module_utils.common.text.converters 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 (
|
||||
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.apis import core_v1_api
|
||||
from kubernetes.stream import stream
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
from kubernetes.stream import stream
|
||||
except ImportError:
|
||||
# ImportError are managed by the common module already.
|
||||
pass
|
||||
@@ -157,10 +166,9 @@ def argspec():
|
||||
return spec
|
||||
|
||||
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
|
||||
def execute_module(module, client):
|
||||
# Load kubernetes.client.Configuration
|
||||
api = core_v1_api.CoreV1Api(k8s_ansible_mixin.client.client)
|
||||
api = core_v1_api.CoreV1Api(client.client)
|
||||
|
||||
# hack because passing the container as None breaks things
|
||||
optional_kwargs = {}
|
||||
@@ -211,11 +219,6 @@ def execute_module(module, k8s_ansible_mixin):
|
||||
else:
|
||||
rc = int(err["details"]["causes"][0]["message"])
|
||||
|
||||
module.deprecate(
|
||||
"The 'return_code' return key is deprecated. Please use 'rc' instead.",
|
||||
version="4.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
module.exit_json(
|
||||
# Some command might change environment, but ultimately failing at end
|
||||
changed=True,
|
||||
@@ -227,18 +230,18 @@ def execute_module(module, k8s_ansible_mixin):
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
check_pyyaml=False,
|
||||
argument_spec=argspec(),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
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)
|
||||
try:
|
||||
client = get_api_client(module)
|
||||
execute_module(module, client.client)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -38,10 +38,21 @@ 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])
|
||||
or V(hidden_fields=[spec.containers[0].env[3].value])
|
||||
or V(hidden_fields=[metadata.annotations[kubectl.kubernetes.io/last-applied-configuration]])
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 3.0.0
|
||||
|
||||
extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_auth_options
|
||||
@@ -49,8 +60,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_wait_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -155,10 +166,22 @@ 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, k8s_ansible_mixin):
|
||||
facts = k8s_ansible_mixin.kubernetes_facts(
|
||||
def execute_module(module, svc):
|
||||
facts = svc.find(
|
||||
module.params["kind"],
|
||||
module.params["api_version"],
|
||||
name=module.params["name"],
|
||||
@@ -169,6 +192,7 @@ def execute_module(module, k8s_ansible_mixin):
|
||||
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)
|
||||
|
||||
@@ -184,25 +208,22 @@ 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 = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
)
|
||||
|
||||
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)
|
||||
try:
|
||||
client = get_api_client(module)
|
||||
svc = K8sService(client, module)
|
||||
execute_module(module, svc)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -33,6 +33,14 @@ options:
|
||||
aliases:
|
||||
- api
|
||||
- version
|
||||
hidden_fields:
|
||||
description:
|
||||
- List of fields to hide from the diff output.
|
||||
- This is useful for fields that are not relevant to the patch operation, such as `metadata.managedFields`.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
version_added: 6.1.0
|
||||
kind:
|
||||
description:
|
||||
- Use to specify an object model.
|
||||
@@ -62,8 +70,8 @@ extends_documentation_fragment:
|
||||
- kubernetes.core.k8s_wait_options
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
- "jsonpatch"
|
||||
"""
|
||||
@@ -79,7 +87,7 @@ EXAMPLES = r"""
|
||||
path: /metadata/labels/app
|
||||
value: myapp
|
||||
- op: replace
|
||||
patch: /spec/containers/0/image
|
||||
path: /spec/containers/0/image
|
||||
value: nginx
|
||||
"""
|
||||
|
||||
@@ -128,7 +136,7 @@ import copy
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
@@ -136,9 +144,21 @@ 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.common import (
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
K8sAnsibleMixin,
|
||||
)
|
||||
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,
|
||||
hide_fields,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
|
||||
get_waiter,
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -163,6 +183,7 @@ JSON_PATCH_ARGS = {
|
||||
"namespace": {"type": "str"},
|
||||
"name": {"type": "str", "required": True},
|
||||
"patch": {"type": "list", "required": True, "elements": "dict"},
|
||||
"hidden_fields": {"type": "list", "elements": "str", "default": []},
|
||||
}
|
||||
|
||||
|
||||
@@ -185,13 +206,14 @@ def json_patch(existing, patch):
|
||||
return None, error
|
||||
|
||||
|
||||
def execute_module(k8s_module, module):
|
||||
def execute_module(module, client):
|
||||
kind = module.params.get("kind")
|
||||
api_version = module.params.get("api_version")
|
||||
name = module.params.get("name")
|
||||
namespace = module.params.get("namespace")
|
||||
patch = module.params.get("patch")
|
||||
|
||||
hidden_fields = module.params.get("hidden_fields")
|
||||
wait = module.params.get("wait")
|
||||
wait_sleep = module.params.get("wait_sleep")
|
||||
wait_timeout = module.params.get("wait_timeout")
|
||||
@@ -200,19 +222,14 @@ def execute_module(k8s_module, module):
|
||||
"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 = k8s_module.find_resource(kind, api_version, fail=True)
|
||||
resource = client.resource(kind, api_version)
|
||||
|
||||
try:
|
||||
existing = resource.get(name=name, namespace=namespace)
|
||||
existing = client.get(resource, name=name, namespace=namespace)
|
||||
except DynamicApiError as exc:
|
||||
msg = "Failed to retrieve requested object: {0}".format(exc.body)
|
||||
module.fail_json(
|
||||
@@ -227,7 +244,7 @@ def execute_module(k8s_module, module):
|
||||
msg=build_error_msg(kind, name, msg), error="", status="", reason=""
|
||||
)
|
||||
|
||||
if module.check_mode and not k8s_module.supports_dry_run:
|
||||
if module.check_mode and not client.dry_run:
|
||||
obj, error = json_patch(existing.to_dict(), patch)
|
||||
if error:
|
||||
module.fail_json(**error)
|
||||
@@ -236,7 +253,8 @@ def execute_module(k8s_module, module):
|
||||
if module.check_mode:
|
||||
params["dry_run"] = "All"
|
||||
try:
|
||||
obj = resource.patch(
|
||||
obj = client.patch(
|
||||
resource,
|
||||
patch,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
@@ -253,12 +271,13 @@ def execute_module(k8s_module, module):
|
||||
module.fail_json(msg=msg, error=to_native(exc), status="", reason="")
|
||||
|
||||
success = True
|
||||
result = {"result": obj}
|
||||
result = {"result": hide_fields(obj, hidden_fields)}
|
||||
if wait and not module.check_mode:
|
||||
success, result["result"], result["duration"] = k8s_module.wait(
|
||||
resource, definition, wait_sleep, wait_timeout, condition=wait_condition
|
||||
waiter = get_waiter(client, resource, condition=wait_condition)
|
||||
success, result["result"], result["duration"] = waiter.wait(
|
||||
wait_timeout, wait_sleep, name, namespace
|
||||
)
|
||||
match, diffs = k8s_module.diff_objects(existing.to_dict(), obj)
|
||||
match, diffs = diff_objects(existing.to_dict(), obj, hidden_fields)
|
||||
result["changed"] = not match
|
||||
if module._diff:
|
||||
result["diff"] = diffs
|
||||
@@ -274,13 +293,14 @@ def main():
|
||||
args = copy.deepcopy(AUTH_ARG_SPEC)
|
||||
args.update(copy.deepcopy(WAIT_ARG_SPEC))
|
||||
args.update(JSON_PATCH_ARGS)
|
||||
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)
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -14,7 +14,7 @@ module: k8s_log
|
||||
|
||||
short_description: Fetch logs from Kubernetes resources
|
||||
|
||||
version_added: "0.10.0"
|
||||
version_added: 0.10.0
|
||||
|
||||
author:
|
||||
- "Fabian von Feilitzsch (@fabianvf)"
|
||||
@@ -47,11 +47,13 @@ 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.
|
||||
- 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).
|
||||
required: no
|
||||
type: str
|
||||
since_seconds:
|
||||
@@ -59,11 +61,30 @@ options:
|
||||
- A relative time in seconds before the current time from which to show logs.
|
||||
required: no
|
||||
type: str
|
||||
version_added: '2.2.0'
|
||||
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.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -99,7 +120,15 @@ 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"""
|
||||
@@ -117,16 +146,33 @@ 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():
|
||||
@@ -138,37 +184,61 @@ 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 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:
|
||||
module.fail(msg="Only one of name or label_selectors can be provided")
|
||||
def get_exception_message(exc):
|
||||
try:
|
||||
d = json.loads(exc.body.decode("utf8"))
|
||||
return d["message"]
|
||||
except Exception:
|
||||
return exc
|
||||
|
||||
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)
|
||||
|
||||
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", {}))
|
||||
if name and label_selector:
|
||||
raise CoreException("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)
|
||||
|
||||
if "log" not in resource.subresources:
|
||||
if not name:
|
||||
module.fail(
|
||||
msg="name must be provided for resources that do not support the log subresource"
|
||||
raise CoreException(
|
||||
"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(module, instance))
|
||||
label_selector = ",".join(extract_selectors(instance))
|
||||
resource = v1_pods
|
||||
|
||||
if label_selector:
|
||||
instances = v1_pods.get(namespace=namespace, label_selector=label_selector)
|
||||
if not instances.items:
|
||||
module.fail(
|
||||
msg="No pods in namespace {0} matched selector {1}".format(
|
||||
raise CoreException(
|
||||
"No pods in namespace {0} matched selector {1}".format(
|
||||
namespace, label_selector
|
||||
)
|
||||
)
|
||||
@@ -176,29 +246,60 @@ def execute_module(module, k8s_ansible_mixin):
|
||||
name = instances.items[0].metadata.name
|
||||
resource = v1_pods
|
||||
|
||||
kwargs = {}
|
||||
if module.params.get("container"):
|
||||
kwargs["query_params"] = dict(container=module.params["container"])
|
||||
|
||||
if module.params.get("since_seconds"):
|
||||
kwargs.setdefault("query_params", {}).update(
|
||||
{"sinceSeconds": module.params["since_seconds"]}
|
||||
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"
|
||||
)
|
||||
|
||||
log = serialize_log(
|
||||
resource.log.get(name=name, namespace=namespace, serialize=False, **kwargs)
|
||||
)
|
||||
kwargs = {}
|
||||
if params.get("container"):
|
||||
kwargs["query_params"] = {"container": params["container"]}
|
||||
|
||||
module.exit_json(changed=False, log=log, log_lines=log.split("\n"))
|
||||
if params.get("since_seconds"):
|
||||
kwargs.setdefault("query_params", {}).update(
|
||||
{"sinceSeconds": params["since_seconds"]}
|
||||
)
|
||||
|
||||
if params.get("previous"):
|
||||
kwargs.setdefault("query_params", {}).update({"previous": params["previous"]})
|
||||
|
||||
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")}
|
||||
|
||||
|
||||
def extract_selectors(module, instance):
|
||||
def extract_selectors(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:
|
||||
module.fail(
|
||||
msg="{0} {1} does not support the log subresource directly, and no Pod selector was found on the object".format(
|
||||
raise CoreException(
|
||||
"{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
|
||||
)
|
||||
)
|
||||
@@ -232,8 +333,8 @@ def extract_selectors(module, instance):
|
||||
)
|
||||
)
|
||||
else:
|
||||
module.fail(
|
||||
msg="The k8s_log module does not support the {0} matchExpression operator".format(
|
||||
raise CoreException(
|
||||
"The k8s_log module does not support the {0} matchExpression operator".format(
|
||||
operator.lower()
|
||||
)
|
||||
)
|
||||
@@ -241,22 +342,21 @@ def extract_selectors(module, instance):
|
||||
return selectors
|
||||
|
||||
|
||||
def serialize_log(response):
|
||||
if PY2:
|
||||
return response.data
|
||||
return response.data.decode("utf8")
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule,
|
||||
argument_spec=argspec(),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[("container", "all_containers")],
|
||||
)
|
||||
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -12,7 +12,7 @@ __metaclass__ = type
|
||||
DOCUMENTATION = r"""
|
||||
module: k8s_rollback
|
||||
short_description: Rollback Kubernetes (K8S) Deployments and DaemonSets
|
||||
version_added: "1.0.0"
|
||||
version_added: 1.0.0
|
||||
author:
|
||||
- "Julien Huon (@julienhuon)"
|
||||
description:
|
||||
@@ -24,16 +24,18 @@ 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.6"
|
||||
- "kubernetes >= 12.0.0"
|
||||
- "python >= 3.9"
|
||||
- "kubernetes >= 24.2.0"
|
||||
- "PyYAML >= 3.11"
|
||||
"""
|
||||
|
||||
@@ -86,12 +88,23 @@ 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(module):
|
||||
def get_managed_resource(kind):
|
||||
managed_resource = {}
|
||||
|
||||
kind = module.params["kind"]
|
||||
if kind == "DaemonSet":
|
||||
managed_resource["kind"] = "ControllerRevision"
|
||||
managed_resource["api_version"] = "apps/v1"
|
||||
@@ -99,14 +112,17 @@ def get_managed_resource(module):
|
||||
managed_resource["kind"] = "ReplicaSet"
|
||||
managed_resource["api_version"] = "apps/v1"
|
||||
else:
|
||||
module.fail(msg="Cannot perform rollback on resource of kind {0}".format(kind))
|
||||
raise CoreException(
|
||||
"Cannot perform rollback on resource of kind {0}".format(kind)
|
||||
)
|
||||
return managed_resource
|
||||
|
||||
|
||||
def execute_module(module, k8s_ansible_mixin):
|
||||
def execute_module(svc):
|
||||
results = []
|
||||
module = svc.module
|
||||
|
||||
resources = k8s_ansible_mixin.kubernetes_facts(
|
||||
resources = svc.find(
|
||||
module.params["kind"],
|
||||
module.params["api_version"],
|
||||
module.params["name"],
|
||||
@@ -117,14 +133,16 @@ def execute_module(module, k8s_ansible_mixin):
|
||||
|
||||
changed = False
|
||||
for resource in resources["resources"]:
|
||||
result = perform_action(module, k8s_ansible_mixin, resource)
|
||||
result = perform_action(svc, resource)
|
||||
changed = result["changed"] or changed
|
||||
results.append(result)
|
||||
|
||||
module.exit_json(**{"changed": changed, "rollback_info": results})
|
||||
|
||||
|
||||
def perform_action(module, k8s_ansible_mixin, resource):
|
||||
def perform_action(svc, resource):
|
||||
module = svc.module
|
||||
|
||||
if module.params["kind"] == "DaemonSet":
|
||||
current_revision = resource["metadata"]["generation"]
|
||||
elif module.params["kind"] == "Deployment":
|
||||
@@ -132,8 +150,8 @@ def perform_action(module, k8s_ansible_mixin, resource):
|
||||
"deployment.kubernetes.io/revision"
|
||||
]
|
||||
|
||||
managed_resource = get_managed_resource(module)
|
||||
managed_resources = k8s_ansible_mixin.kubernetes_facts(
|
||||
managed_resource = get_managed_resource(module.params["kind"])
|
||||
managed_resources = svc.find(
|
||||
managed_resource["kind"],
|
||||
managed_resource["api_version"],
|
||||
"",
|
||||
@@ -150,7 +168,9 @@ def perform_action(module, k8s_ansible_mixin, resource):
|
||||
module.params["kind"],
|
||||
resource["metadata"]["name"],
|
||||
)
|
||||
result = {"changed": False, "warnings": [warn]}
|
||||
if warn:
|
||||
module.warn(warn)
|
||||
result = {"changed": False}
|
||||
return result
|
||||
|
||||
if module.params["kind"] == "Deployment":
|
||||
@@ -185,7 +205,7 @@ def perform_action(module, k8s_ansible_mixin, resource):
|
||||
|
||||
rollback = resource
|
||||
if not module.check_mode:
|
||||
rollback = k8s_ansible_mixin.client.request(
|
||||
rollback = svc.client.client.request(
|
||||
"PATCH",
|
||||
"/apis/{0}/namespaces/{1}/{2}/{3}".format(
|
||||
module.params["api_version"],
|
||||
@@ -242,15 +262,16 @@ def get_previous_revision(all_resources, current_revision):
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
K8sAnsibleMixin,
|
||||
get_api_client,
|
||||
module = AnsibleK8SModule(
|
||||
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
||||
)
|
||||
|
||||
k8s_ansible_mixin = K8sAnsibleMixin(module)
|
||||
k8s_ansible_mixin.client = get_api_client(module=module)
|
||||
execute_module(module, k8s_ansible_mixin)
|
||||
try:
|
||||
client = get_api_client(module=module)
|
||||
svc = K8sService(client, module)
|
||||
execute_module(svc)
|
||||
except CoreException as e:
|
||||
module.fail_from_exception(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user