Compare commits

198 Commits
2.2.0 ... 3.3.0

Author SHA1 Message Date
Yuriy Novostavskiy
8feb564b4d prepare release 3.3.0 (#863)
SUMMARY
Version 3.3.0 of ansible-collection kubernetes.core came with several improvements and bugfixes
ISSUE TYPE

New release pull request

Changelog
Minor Changes

k8s_drain - Improve error message for pod disruption budget when draining a node (#797).

Bugfixes

helm - Helm version checks did not support RC versions. They now accept any version tags. (#745).
helm_pull - Apply no_log=True to pass_credentials to silence false positive warning.. (#796).
k8s_drain - Fix k8s_drain does not wait for single pod (#769).
k8s_drain - Fix k8s_drain runs into a timeout when evicting a pod which is part of a stateful set  (#792).
kubeconfig option should not appear in module invocation log (#782).
kustomize - kustomize plugin fails with deprecation warnings (#639).
waiter - Fix waiting for daemonset when desired number of pods is 0. (#756).

ADDITIONAL INFORMATION
Collection kubernets.core version 3.3.0 is compatible with ansible-core>=2.14.0

Reviewed-by: Alina Buzachis
Reviewed-by: Yuriy Novostavskiy
Reviewed-by: Mike Graves <mgraves@redhat.com>
2025-01-20 13:55:22 +00:00
patchback[bot]
42832bc9d2 trivial doc: replace 2.5.0 with 3.0.0 (#831) (#855)
This is a backport of PR #831 as merged into main (bc0de24).
SUMMARY
Some parameters were added to the master in time where the latest version was 2.4.0 with version_added: 2.5.0, however the next version after 2.4.0 was a 3.0.0.
So, with this trivial doc PR (that most probably doesn't require a changelog fragment and including to changelog) I replacing  version_added: 2.5.0 to  version_added: 3.0.0 for:

reuse_values in kubernetes.core.helm module
reset_values in kubernetes.core.helm module
delete_all in  kubernetes.core.k8s module
hidden_fields  in  kubernetes.core.k8s module
hidden_fields   in  kubernetes.core.k8s_info module

All of them are introduced in kubernetes.core 3.0.0
ISSUE TYPE

Docs Pull Request

COMPONENT NAME

helm
k8s
8s_info


ADDITIONAL INFORMATION
PR to be backported to stable-3 and stable-5
2025-01-17 18:54:26 +00:00
patchback[bot]
19b2efd0dd Remove deprecated .github/stale.yml to address #837 (#838) (#859)
This is a backport of PR #838 as merged into main (eb731cd).
SUMMARY
I noticed that even config for probot/stale is present in the repo, but the old issues and PRs weren't marked as stale and not closed by the bot. Investigated and found that this bot was added to community.kubernetes as ansible-collections/community.kubernetes#53 but wasn't moved to kubernetes.core and never worked here.
Moreover, this bot is completely deprecated and down, ref: probot/stale#430
So, the config to be removed.
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
.github/stale.yml
ADDITIONAL INFORMATION
Closes #837
Trivial change that not require changelog
2025-01-17 16:51:18 +00:00
patchback[bot]
1c3bf95b74 helm_pull: Silence false no_log warning (#796) (#857)
This is a backport of PR #796 as merged into main (ecc64ca).
SUMMARY
Apply no_log=True to pass_credentials to silence false positive warning.
Fixes similar issue to: #423
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelog/fragements/796-false-positive-helmull.yaml
plugins/modules/helm_pull.py
2025-01-17 16:19:38 +00:00
Yuriy Novostavskiy
018c92429b Backport "fix linters in github actions (#848)" to stable-3 (#850)
Fix bug #846
within this commit ansible/ansible-lint updated to 24.12.2 and config moved to .config folder
(cherry picked from commit 159a63a)
This is cherry-pic of #848 as patchback failed here

Reviewed-by: Mike Graves <mgraves@redhat.com>
2025-01-17 15:52:54 +00:00
patchback[bot]
9267e03538 Clean up test namespace (#852) (#853)
This is a backport of PR #852 as merged into main (9f60b15).
SUMMARY

The helm_set_values test target did not clean up its namespace which is leading to unstable tests in the k8s_drain target.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION
2025-01-17 15:29:19 +00:00
Yuriy Novostavskiy
eb3ab99709 Backport to Stable-3: Fix helm tests (#827) and Fix helm integration tests (#830) (#829)
SUMMARY
Some of the charts we've used for testing are no longer available at the old helm repository urls, as they've been moved to oci registries. This updates those charts. In the longer term, we should find a better way to handle these kinds of test fixtures, probably by switching to local charts as much as possible.
ISSUE TYPE
Bugfix Pull Request
Docs Pull Request
Feature Pull Request
New Module Pull Request
COMPONENT NAME
ADDITIONAL INFORMATION
Reviewed-by: Helen Bailey hebailey@redhat.com
Reviewed-by: Yuriy Novostavskiy
SUMMARY


ISSUE TYPE


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

COMPONENT NAME


This is a backport of PR #830 as merged into main (7559b65).
SUMMARY
Fix charts ref on integration tests targets
ISSUE TYPE
Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
2025-01-16 19:03:45 +00:00
patchback[bot]
a1ac6b99dc Remove kubeconfig value from module invocation log (#826) (#839)
(cherry picked from commit 6efabd3418)

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2024-12-19 12:06:45 +01:00
Yuriy Novostavskiy
4ad57fdb6c Cherry-pic to stable-3: Update Readme to match the template (#767) (#822)
SUMMARY
Refer: https://issues.redhat.com/browse/ACA-1749
This PR updates the README doc to match the template ISSUE TYPE
Bugfix Pull Request
Docs Pull Request
Feature Pull Request
New Module Pull Request
COMPONENT NAME
ADDITIONAL INFORMATION
Reviewed-by: Alina Buzachis
(cherry picked from commit fdb8af7)
SUMMARY


ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-12-11 16:41:52 +00:00
patchback[bot]
ee9939c54f Make k8s_drain work when only one pod is present (#770) (#821)
This is a backport of PR #770 as merged into main (4c305e7).
SUMMARY
Fixes #769 .
k8s_drain was not checking if a pod has been deleted when there was only one pod on the node to be drained.
The list of pods, pods, was being "popped" before the first iteration of the while loop:
        pod = pods.pop()
        while (_elapsed_time() < wait_timeout or wait_timeout == 0) and pods:
When pods contains only one element, the while loop is skipped.


ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_drain
2024-12-11 16:24:33 +00:00
patchback[bot]
c1549db1bb Improve error message for pod disruption budget when draining a node (#798) (#815)
This is a backport of PR #798 as merged into main (52f2cb5).
SUMMARY
Closes #797 .
The error message "Too Many Requests" is confusing and is changed to a more meaningful message:
TASK [Drain node] *************************************************************************
Montag 25 November 2024  09:20:28 +0100 (0:00:00.014)       0:00:00.014 ******* 
fatal: [host -> localhost]: FAILED! => {"changed": false, "msg": "Failed to delete pod kube-public/draintest-6b84677b99-9jf7m due to: Cannot evict pod as it would violate the pod's disruption budget."}


The new task output would allow to deal with a pod disruption budget with the retries/until logic in a more controlled way:
---
- hosts: "{{ target }}"
  serial: 1
  gather_facts: false
  tasks:
    - name: Drain node
      kubernetes.core.k8s_drain:
        kubeconfig: "{{ kubeconfig_path }}"
        name: "{{ inventory_hostname }}"
        delete_options:
          ignore_daemonsets: true
          delete_emptydir_data: true
          wait_timeout: 100
          disable_eviction: false
          wait_sleep: 1
      delegate_to: localhost
      retries: 10
      delay: 5
      until: drain_result is success or 'disruption budget' not in drain_result.msg
      register: drain_result

ISSUE TYPE


Feature Pull Request

COMPONENT NAME
k8s_drain
2024-12-11 15:14:46 +00:00
patchback[bot]
c6bd8ab453 Fix k8s_drain runs into timeout with pods from stateful sets. (#793) (#807)
This is a backport of PR #793 as merged into main (fca0dc0).
SUMMARY
Fixes #792 .
The function wait_for_pod_deletion in k8s_drain never checks on which node a pod is actually running:
            try:
                response = self._api_instance.read_namespaced_pod(
                    namespace=pod[0], name=pod[1]
                )
                if not response:
                    pod = None
                time.sleep(wait_sleep)
This means that if a pod is successfully evicted and restarted with the same name on a new node, k8s_drain does not notice and thinks that the original pod is still running. This is the case for pods which are part of a stateful set.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME
k8s_drain
2024-12-11 14:12:32 +00:00
patchback[bot]
6299f242a2 Remove kubevirt integration test workflow (#806) (#809)
This is a backport of PR #806 as merged into main (513ff66).
SUMMARY

This removes the kubevirt integration tests. We don't maintain that collection or have any permissions on that repo, so there's no reason for these tests to be here.

ISSUE TYPE


Bugfix Pull Request


COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-12-10 18:08:31 +00:00
patchback[bot]
cd55f596ab [ci] fix github actions post 2.18 (#789) (#811)
This is a backport of PR #789 as merged into main (cd68631).
This PR includes a trivial fix for the GitHub Actions issue #788 and related to switching milestone and devel branches of ansible/ansible to version 2.19 and prepare repo to be ready to include test with Python 3.13 when ansible-network/github_actions/pull/162 is merged.
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
GitHub actions/test

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-12-10 17:28:59 +00:00
patchback[bot]
00bbf16cff fix: kustomize plugin fails with deprecation warnings (#728) (#763)
This is a backport of PR #728 as merged into main (5bc53db).
SUMMARY

error judgments are based on the exit codes of command execution, where 0 represents success and non-zero represents failure.
Optimize the run_command function to return a tuple like the run_command method of AnsibleModule.

Fixes #639
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

kustomize lookup plugin
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-07-15 14:06:29 +00:00
patchback[bot]
31fd4058f1 Fix waiting for daemonset when desired number of pods is 0 (#756) (#761)
This is a backport of PR #756 as merged into main (b07fbd6).
Fixes #755
SUMMARY
Because we don't have any node with non_exisiting_label (see code below) desired number of Pods will be 0. Kubernetes won't create .status.updatedNumberScheduled field (at least on version v1.27), because we still are not going to create any Pods. So that if .status.updatedNumberScheduled doesn't exist we should assume that number is 0
Code to reproduce:
- name: Create daemonset
  kubernetes.core.k8s:
    state: present
    wait: true
    definition:
      apiVersion: apps/v1
      kind: DaemonSet
      metadata:
        name: my-daemonset
        namespace: default
      spec:
        selector:
          matchLabels:
            app: my-app
        template:
          metadata:
            labels:
              app: my-app
          spec:
            containers:
              - name: my-container
                image: nginx
            nodeSelector:
              non_exisiting_label: 1
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
kubernetes.core.plugins.module_utils.k8s.waiter
ADDITIONAL INFORMATION



TASK [Create daemonset] **********************************************************************************************************************************
changed: [controlplane] => {"changed": true, "duration": 5, "method": "create", "result": {"apiVersion": "apps/v1", "kind": "DaemonSet", "metadata": {"annotations": {"deprecated.daemonset.template.generation": "1"}, "creationTimestamp": "2024-06-28T08:23:41Z", "generation": 1, "managedFields": [{"apiVersion": "apps/v1", "fieldsType": "FieldsV1", "fieldsV1": {"f:metadata": {"f:annotations": {".": {}, "f:deprecated.daemonset.template.generation": {}}}, "f:spec": {"f:revisionHistoryLimit": {}, "f:selector": {}, "f:template": {"f:metadata": {"f:labels": {".": {}, "f:app": {}}}, "f:spec": {"f:containers": {"k:{\"name\":\"my-container\"}": {".": {}, "f:image": {}, "f:imagePullPolicy": {}, "f:name": {}, "f:resources": {}, "f:terminationMessagePath": {}, "f:terminationMessagePolicy": {}}}, "f:dnsPolicy": {}, "f:nodeSelector": {}, "f:restartPolicy": {}, "f:schedulerName": {}, "f:securityContext": {}, "f:terminationGracePeriodSeconds": {}}}, "f:updateStrategy": {"f:rollingUpdate": {".": {}, "f:maxSurge": {}, "f:maxUnavailable": {}}, "f:type": {}}}}, "manager": "OpenAPI-Generator", "operation": "Update", "time": "2024-06-28T08:23:41Z"}, {"apiVersion": "apps/v1", "fieldsType": "FieldsV1", "fieldsV1": {"f:status": {"f:observedGeneration": {}}}, "manager": "kube-controller-manager", "operation": "Update", "subresource": "status", "time": "2024-06-28T08:23:41Z"}], "name": "my-daemonset", "namespace": "default", "resourceVersion": "1088421", "uid": "faafdbf7-4388-4cec-88d5-84657966312d"}, "spec": {"revisionHistoryLimit": 10, "selector": {"matchLabels": {"app": "my-app"}}, "template": {"metadata": {"creationTimestamp": null, "labels": {"app": "my-app"}}, "spec": {"containers": [{"image": "nginx", "imagePullPolicy": "Always", "name": "my-container", "resources": {}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File"}], "dnsPolicy": "ClusterFirst", "nodeSelector": {"non_exisiting_label": "1"}, "restartPolicy": "Always", "schedulerName": "default-scheduler", "securityContext": {}, "terminationGracePeriodSeconds": 30}}, "updateStrategy": {"rollingUpdate": {"maxSurge": 0, "maxUnavailable": 1}, "type": "RollingUpdate"}}, "status": {"currentNumberScheduled": 0, "desiredNumberScheduled": 0, "numberMisscheduled": 0, "numberReady": 0, "observedGeneration": 1}}}

~$ kubectl get ds
NAME           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR           AGE
my-daemonset   0         0         0       0            0           non_exisiting_label=1   30s

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-07-10 14:32:08 +00:00
patchback[bot]
5c748bd4e9 Merge pull request #757 from gravesm/gha-python-version (#758)
This is a backport of PR #757 as merged into main (44a2fc3).
SUMMARY

Ansible 2.17 is already included in the ubuntu-latest runner image, so there's no need for a separate install step. It was broken in any case because the python version being used was too low for ansible 2.18.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-07-10 13:09:23 +00:00
patchback[bot]
1c295560ba helm: Accept release candidate versions for compatibility checks (#745) (#753)
This is a backport of PR #745 as merged into main (6a04f42).
SUMMARY

If the helm CLI version includes -rc.1 for example, the version checks fails due to an incomplete regex.
The error can be triggered if you use helm v3.15.0-rc.1 for example, and apply a helm chart with wait: true 
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME
helm
helm_pull
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-06-18 13:05:53 +00:00
Yuriy Novostavskiy
615d3d8bd0 Prepare release 3.2.0 (#746)
SUMMARY
Prepare version 3.2.0
ISSUE TYPE

New Version Pull Request

ADDITIONAL INFORMATION
The current version of a major Ansible (Ansible 10) includes a collection version 3.1.0, meaning that the minor version (i.e. Ansible 10.1) will pick versions from the stable-3 branch. Hence, a recent documentation update will not be in Ansible and Ansible documentation site/kubernetes.core collection chapter until a new minor version released from stable-3 branch.
This PR also includes changes in README.md and Makefile that were missed in 3.1.0

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-06-14 15:31:50 +00:00
patchback[bot]
4854d7fbd9 Doc: add example of using kubectl connection plugin (#741) (#743)
[PR #741/fb80d973 backport][stable-3] Doc: add example of using kubectl connection plugin

This is a backport of PR #741 as merged into main (fb80d97).
SUMMARY
Currently documentation for collection don't include any examples of using kubenrenes.core.kubectl connection plugin and it's hard to start using that plugin.
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
kubenrenes.core.kubectl connection plugin
ADDITIONAL INFORMATION
This PR was inspired by #288 and based on feedback on that PR and my own experience. Thanks @tpo for his try and @geerlingguy for his Ansible for DevOps book

Reviewed-by: Yuriy Novostavskiy
Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-06-06 14:28:18 +00:00
patchback[bot]
baa59f388c Defer removal of inventory/k8s to 6.0.0 (#734) (#738)
Defer removal of inventory/k8s to 6.0.0

SUMMARY
Defer removal of inventory plugin k8s to release 6.0.0.

ISSUE TYPE

Feature Pull Request

Reviewed-by: Alina Buzachis
Reviewed-by: Mike Graves <mgraves@redhat.com>
(cherry picked from commit 0c5233a650)

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2024-05-31 09:59:45 +02:00
patchback[bot]
e071ae73b6 kubevirt.core collection cross testing (#731) (#735)
* Initial

* update python version

* update python version

* checkout local version of collection

* add integration job

* indent

* Set workflow as non blocking

(cherry picked from commit c0666a5137)

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2024-05-31 09:46:31 +02:00
patchback[bot]
00cb40094a Defer removal of k8s inventory plugin to version 5.0. (#723) (#724)
[PR #723/cbadbe32 backport][stable-3] Defer removal of k8s inventory plugin to version 5.0.

This is a backport of PR #723 as merged into main (cbadbe3).
SUMMARY

Defer removal of k8s inventory plugin to version 5.0.

ISSUE TYPE


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

COMPONENT NAME

inventory/k8s.py
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis
2024-05-22 11:56:29 +00:00
Mike Graves
ef829b8a56 Release 3.1.0 (#719)
Release 3.1.0

SUMMARY

Release prep for 3.1.0

ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis
Reviewed-by: Helen Bailey <hebailey@redhat.com>
2024-05-16 14:15:47 +00:00
patchback[bot]
d86a652c36 Update deprecation version for merge_type=json (#700) (#718)
[PR #700/a4c1bd85 backport][stable-3] Update deprecation version for merge_type=json

This is a backport of PR #700 as merged into main (a4c1bd8).
SUMMARY
When looking at the parts of plugins/module_utils/common.py and plugins/module_utils/k8s/service.py during the post 3.0.0 release (see https://github.com/ansible-collections/kubernetes.core/pull/663/files#diff-9ee2d0860a5643da4e1f35136e9e7c3a41c5f2fd2952c197e7e32b941e5a301c) that affect merge_type when set to json, I don't believe merge_type=json was deprecated for (and removed from) the k8s module, and instead the deprecation version has moved to 4.0.0. Hence, the documentation update.
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
k8s module

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-15 18:57:45 +00:00
patchback[bot]
a4242f0131 minor: doc: use the same style of version_added across repo (#703) (#715)
[PR #703/63607630 backport][stable-3] minor(doc): use the same style of version_added across repo

This is a backport of PR #703 as merged into main (6360763).
SUMMARY
Currently is no single style of version_added, in some places it's unquoted, somewhere single quote is used, in another places it's double quoted. Moreover, some file had different styles in one single file.
The aim of this PR is to update whole repo to single style for version_added
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
kustomize
helm
helm_info
helm_plugin
helm_plugin_info
helm_pull
helm_repository
helm_template
k8s_cluster_info
k8s_cp
k8s_drain
k8s_exec
k8s_log
k8s_rollback
k8s_taint
ADDITIONAL INFORMATION
The same style is proposed as used in amazon.aws collections

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-15 15:10:34 +00:00
Mike Graves
d75e9a0fa3 Fix unsafe text assertion in tests (#716) (#717)
[Manual backport/stable-3] Fix unsafe text assertion in tests (#716)

Fix unsafe text assertion in tests
SUMMARY
This fixes a problem with unsafe text in an assertion.
ISSUE TYPE
Bugfix Pull Request
COMPONENT NAME
ADDITIONAL INFORMATION
Reviewed-by: GomathiselviS
Reviewed-by: Bikouo Aubin
(cherry picked from commit 8858b19)
2024-05-15 14:35:14 +00:00
patchback[bot]
5044cfc030 fixed typo in filename of 'k8s_json_patch'-action (#652) (#714)
[PR #652/ac943e98 backport][stable-3] fixed typo in filename of 'k8s_json_patch'-action 

This is a backport of PR #652 as merged into main (ac943e9).
SUMMARY

The filename/symlink of the action for the 'k8s_json_patch'-module was wrong. Renamed file from 'ks8_json_patch.py' to ' k8s_json_patch.py'

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

k8s_json_patch
ADDITIONAL INFORMATION


Because of the wrong filename things like unvaulting kubeconfig files did not worked.

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-14 17:07:48 +00:00
patchback[bot]
ddd7e79a12 Update kustomize.py add --enable-helm support (#592) (#712)
[PR #592/0408aa93 backport][stable-3] Update kustomize.py add --enable-helm support

This is a backport of PR #592 as merged into main (0408aa9).
Add --enable-helm support
SUMMARY
Fixes #568
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
Lookup plugin: kubernetes.core.kustomize
ADDITIONAL INFORMATION
Current and maintained arg:
lookup('kubernetes.core.kustomize', dir=item)

Additional feature args:
lookup('kubernetes.core.kustomize', dir=item, enable_helm=false)
lookup('kubernetes.core.kustomize', dir=item, enable_helm=true)

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-13 17:58:34 +00:00
patchback[bot]
edc979ff11 add support of kubectl_local_env_vars (#698) (#702) (#705)
[PR #702/fb25ff44 backport][stable-3] add support of kubectl_local_env_vars (#698)

This is a backport of PR #702 as merged into main (fb25ff4).
SUMMARY
Support of local environmental variable that may be required to be set on Ansible Controller before the connection is set and may be used for kubectl command. This PR addressed for #698
The main idea is to have the support of  additional/extra local environmental variable that may be required for kubectl itself, i.e. for authorization in case of public clouds
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
kubernetes.core.kubectl connection plugin
ADDITIONAL INFORMATION
This PR attempts to implement local env support for the kubectl connection plugin that may be useful in case of using kubectl against public cloud kubernetes environment that uses some authorization (i.e. aws cli) additionally to kubeconfig file. More detail in #698
The output that shows that the connection plugin can use local environment variable for kubectl command (with some debug that used during development but removed then):
root@ubuntu-shell:/# cat test.yaml
- hosts: localhost
  gather_facts: no
  any_errors_fatal: yes
  vars:
    ansible_connection: "kubectl"
    ansible_kubectl_namespace: "test"
    ansible_kubectl_config: "/.kube/config"
    ansible_kubectl_pod: "ubuntu"
    ansible_kubectl_container: "ubuntu"
    ansible_kubectl_local_env_vars:
      TESTVAR1: "test"
      TESTVAR2: "test"
      TESTVAR3: "test"
  environment:
    TEST_ENV1: value1
    TEST_ENV2: value2

  tasks:
  - name: test
    ansible.builtin.shell: env
    register: result
  - debug:
      var: result.stdout_lines
root@ubuntu-shell:/# ansible-playbook test.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] **************************************************************************************************************************************

TASK [test] *******************************************************************************************************************************************
changed: [localhost]

TASK [debug] ******************************************************************************************************************************************
ok: [localhost] => {
    "result.stdout_lines": [
        "KUBERNETES_PORT=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_PORT=443",
        "HOSTNAME=ubuntu",
        "HOME=/root",
        "LC_CTYPE=C.UTF-8",
        "TEST_ENV1=value1",
        "TEST_ENV2=value2",
        "TERM=xterm",
        "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "KUBERNETES_PORT_443_TCP_PORT=443",
        "KUBERNETES_PORT_443_TCP_PROTO=tcp",
        "KUBERNETES_SERVICE_PORT_HTTPS=443",
        "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_HOST=10.96.0.1",
        "PWD=/"
    ]
}

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

root@ubuntu-shell:/# ansible-playbook test.yaml -vvv
ansible-playbook [core 2.14.5]
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.10/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible-playbook
  python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.1.3
  libyaml = True
No config file found; using defaults
host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
yaml declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
ini declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
toml declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.

PLAYBOOK: test.yaml ***********************************************************************************************************************************
1 plays in test.yaml

PLAY [localhost] **************************************************************************************************************************************

TASK [test] *******************************************************************************************************************************************
task path: /test.yaml:19
redirecting (type: connection) ansible.builtin.kubectl to kubernetes.core.kubectl
<127.0.0.1> ESTABLISH kubectl CONNECTION
<127.0.0.1> ENV: KUBERNETES_SERVICE_PORT_HTTPS=443
<127.0.0.1> ENV: KUBERNETES_SERVICE_PORT=443
<127.0.0.1> ENV: HOSTNAME=ubuntu-shell
<127.0.0.1> ENV: PWD=/
<127.0.0.1> ENV: HOME=/root
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
<127.0.0.1> ENV: LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
<127.0.0.1> ENV: TERM=xterm
<127.0.0.1> ENV: SHLVL=1
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP_PROTO=tcp
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
<127.0.0.1> ENV: KUBERNETES_SERVICE_HOST=10.96.0.1
<127.0.0.1> ENV: KUBERNETES_PORT=tcp://10.96.0.1:443
<127.0.0.1> ENV: KUBERNETES_PORT_443_TCP_PORT=443
<127.0.0.1> ENV: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
<127.0.0.1> ENV: _=/usr/local/bin/ansible-playbook
<127.0.0.1> ENV: LC_CTYPE=C.UTF-8
<127.0.0.1> ENV: TESTVAR1=test
<127.0.0.1> ENV: TESTVAR2=test
<127.0.0.1> ENV: TESTVAR3=test
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'echo ~ && sleep 0'"]
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', '/bin/sh -c \'( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133 `" && echo ansible-tmp-1713785852.548581-6866-69007595335133="` echo /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133 `" ) && sleep 0\'']
Using module file /usr/local/lib/python3.10/dist-packages/ansible/modules/command.py
<127.0.0.1> PUT /root/.ansible/tmp/ansible-local-6862s5_lr_wb/tmpxwmx0qeh TO /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/AnsiballZ_command.py
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/ /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/AnsiballZ_command.py && sleep 0'"]
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'TEST_ENV1=value1 TEST_ENV2=value2 /usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/AnsiballZ_command.py && sleep 0'"]
<127.0.0.1> EXEC ['/usr/local/bin/kubectl', '-n', 'test', '--kubeconfig', '/.kube/config', 'exec', '-i', 'ubuntu', '-c', 'ubuntu', '--', '/bin/sh', '-c', "/bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1713785852.548581-6866-69007595335133/ > /dev/null 2>&1 && sleep 0'"]
changed: [localhost] => {
    "changed": true,
    "cmd": "env",
    "delta": "0:00:00.005088",
    "end": "2024-04-22 11:37:33.655340",
    "invocation": {
        "module_args": {
            "_raw_params": "env",
            "_uses_shell": true,
            "argv": null,
            "chdir": null,
            "creates": null,
            "executable": null,
            "removes": null,
            "stdin": null,
            "stdin_add_newline": true,
            "strip_empty_ends": true
        }
    },
    "msg": "",
    "rc": 0,
    "start": "2024-04-22 11:37:33.650252",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "KUBERNETES_PORT=tcp://10.96.0.1:443\nKUBERNETES_SERVICE_PORT=443\nHOSTNAME=ubuntu\nHOME=/root\nLC_CTYPE=C.UTF-8\nTEST_ENV1=value1\nTEST_ENV2=value2\nTERM=xterm\nKUBERNETES_PORT_443_TCP_ADDR=10.96.0.1\nPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\nKUBERNETES_PORT_443_TCP_PORT=443\nKUBERNETES_PORT_443_TCP_PROTO=tcp\nKUBERNETES_SERVICE_PORT_HTTPS=443\nKUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443\nKUBERNETES_SERVICE_HOST=10.96.0.1\nPWD=/",
    "stdout_lines": [
        "KUBERNETES_PORT=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_PORT=443",
        "HOSTNAME=ubuntu",
        "HOME=/root",
        "LC_CTYPE=C.UTF-8",
        "TEST_ENV1=value1",
        "TEST_ENV2=value2",
        "TERM=xterm",
        "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "KUBERNETES_PORT_443_TCP_PORT=443",
        "KUBERNETES_PORT_443_TCP_PROTO=tcp",
        "KUBERNETES_SERVICE_PORT_HTTPS=443",
        "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_HOST=10.96.0.1",
        "PWD=/"
    ]
}

TASK [debug] ******************************************************************************************************************************************
task path: /test.yaml:22
redirecting (type: connection) ansible.builtin.kubectl to kubernetes.core.kubectl
ok: [localhost] => {
    "result.stdout_lines": [
        "KUBERNETES_PORT=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_PORT=443",
        "HOSTNAME=ubuntu",
        "HOME=/root",
        "LC_CTYPE=C.UTF-8",
        "TEST_ENV1=value1",
        "TEST_ENV2=value2",
        "TERM=xterm",
        "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "KUBERNETES_PORT_443_TCP_PORT=443",
        "KUBERNETES_PORT_443_TCP_PROTO=tcp",
        "KUBERNETES_SERVICE_PORT_HTTPS=443",
        "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
        "KUBERNETES_SERVICE_HOST=10.96.0.1",
        "PWD=/"
    ]
}

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

root@ubuntu-shell:/#

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-08 19:53:12 +00:00
patchback[bot]
37c1f911cb Add tests/sanity/ignore-2.18.txt (#704) (#706)
[PR #704/86d9a3f4 backport][stable-3] Add tests/sanity/ignore-2.18.txt

This is a backport of PR #704 as merged into main (86d9a3f).
SUMMARY

Add tests/sanity/ignore-2.18.txt

ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION
2024-05-08 19:00:55 +00:00
patchback[bot]
80a5bf623a Merge pull request #707 from gravesm/linting-fix (#708)
[PR #707/874fbfed backport][stable-3] Update ansible-lint GHA

This is a backport of PR #707 as merged into main (874fbfe).
SUMMARY

There seems to be a bug in older versions of ansible-lint where pinning to a version for the GHA still installs the main branch.
See ansible/ansible-lint#3762 for more info.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-05-08 15:32:59 +00:00
patchback[bot]
8f653558f6 helm - expand kubeconfig path with user's home dir (#654) (#695)
[PR #654/9f7c865c backport][stable-3] helm - expand kubeconfig path with user's home dir

This is a backport of PR #654 as merged into main (9f7c865).
SUMMARY

Currently the helm module fails when providing the default kubeconfig path explicitly, while the same path is fine for the k8s module.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

helm
ADDITIONAL INFORMATION



- name: Deploy kubelet-csr-approver
  delegate_to: client
  run_once: true
  kubernetes.core.helm:
    update_repo_cache: true
    kubeconfig: "~/.kube/config"
    state: present
    name: kubelet-csr-approver
    namespace: kubelet-csr-approver
    create_namespace: true
    chart_ref: kubelet-csr-approver/kubelet-csr-approver
    chart_version: 1.0.5
    values: "{{ lookup('template', 'values.yaml.j2') | from_yaml }}"
    atomic: true

Before change:
TASK [kubernetes/kubelet_csr_approver : Deploy kubelet-csr-approver] ***
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: FileNotFoundError: [Errno 2] No such file or directory: '~/.kube/config'
fatal: [node-1 -> client(192.168.121.56)]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n  File \"/home/vagrant/.ansible/tmp/ansible-tmp-1697293347.7135417-118207-9805169252135/AnsiballZ_helm.py\", line 107, in <module>\r\n    _ansiballz_main()\r\n  File \"/home/vagrant/.ansible/tmp/ansible-tmp-1697293347.7135417-118207-9805169252135/AnsiballZ_helm.py\", line 99, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/home/vagrant/.ansible/tmp/ansible-tmp-1697293347.7135417-118207-9805169252135/AnsiballZ_helm.py\", line 47, in invoke_module\r\n    runpy.run_module(mod_name='ansible_collections.kubernetes.core.plugins.modules.helm', init_globals=dict(_module_fqn='ansible_collections.kubernetes.core.plugins.modules.helm', _modlib_path=modlib_path),\r\n  File \"/usr/lib/python3.10/runpy.py\", line 224, in run_module\r\n    return _run_module_code(code, init_globals, run_name, mod_spec)\r\n  File \"/usr/lib/python3.10/runpy.py\", line 96, in _run_module_code\r\n    _run_code(code, mod_globals, init_globals,\r\n  File \"/usr/lib/python3.10/runpy.py\", line 86, in _run_code\r\n    exec(code, run_globals)\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/modules/helm.py\", line 924, in <module>\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/modules/helm.py\", line 737, in main\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/modules/helm.py\", line 435, in run_repo_update\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/module_utils/helm.py\", line 169, in run_helm_command\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/module_utils/helm.py\", line 162, in env_update\r\n  File \"/tmp/ansible_kubernetes.core.helm_payload_o8s36dti/ansible_kubernetes.core.helm_payload.zip/ansible_collections/kubernetes/core/plugins/module_utils/helm.py\", line 120, in _prepare_helm_environment\r\nFileNotFoundError: [Errno 2] No such file or directory: '~/.kube/config'\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

After change:
TASK [kubernetes/kubelet_csr_approver : Deploy kubelet-csr-approver] ***
changed: [node-1 -> client(192.168.121.56)]

Reviewed-by: Mike Graves <mgraves@redhat.com>
2024-03-13 13:57:36 +00:00
Bikouo Aubin
1c0734dd4e Prepare release 3.0.1 (#693) 2024-03-04 11:02:29 +01:00
patchback[bot]
e6d138dbf0 helm - Add reuse-values when running helm diff (#683) (#692)
helm - Add reuse-values when running helm diff

SUMMARY

closes #680

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

helm

Reviewed-by: GomathiselviS
Reviewed-by: Alina Buzachis
(cherry picked from commit 23e94b60c1)

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2024-03-01 18:01:13 +01:00
patchback[bot]
eed4ebb0af Align helmdiff_check behavior with the deploy function (#670) (#690)
Align `helmdiff_check` behavior with the `deploy` function

SUMMARY
Align helmdiff_check behavior with the deploy function

Fixes #638
helmdiff_check respects set_values parameter
Fixes #669
helmdiff_check command line parameters sequence aligned to the deploy function

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
kubernetes.core.helm

Reviewed-by: Mike Graves <mgraves@redhat.com>
(cherry picked from commit 7c4ec3b982)

Co-authored-by: psmolkin <pavel.smolkin@gmail.com>
2024-03-01 17:50:35 +01:00
patchback[bot]
d3031fcbd3 [PR #676/19559892 backport][stable-3] fix(Collection's util resource discovery fails when complex subresources present #659) (#687)
* fix(Collection's util resource discovery fails when complex subresources present #659) (#676)

* fix(Collection's util resource discovery fails when complex subresources present #659)

* fix(add changelog fragment)

* update node image

* Create discovery.yml

* Update main.yml

---------

Co-authored-by: Bastien Bosser <bastien.bosser@eviden.com>
Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
(cherry picked from commit 1955989278)

* adding sanity ignore file

* Remove refresh_ignore_files

* Update ignore files content

---------

Co-authored-by: bastienbosser <55381632+bastienbosser@users.noreply.github.com>
Co-authored-by: abikouo <abikouo@redhat.com>
2024-03-01 16:44:19 +01:00
GomathiselviS
e756cedf33 Prepare for release 3.0.0 (#662)
Prepare for release 3.0.0

SUMMARY



Updated version to 3.0.0
Executed antsibull-changelog to generate changelog

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis
Reviewed-by: Helen Bailey <hebailey@redhat.com>
Reviewed-by: GomathiselviS
2023-11-17 21:52:11 +00:00
Mike Graves
1a601213eb Merge pull request #660 from GomathiselviS/doc_update
Update python kubernetes library to 24.2.0 , helm/kind-action to 1.8.0
2023-11-16 11:51:48 -05:00
GomathiselviS
abb9e0b6d5 Change changelog type 2023-11-16 09:29:44 -05:00
GomathiselviS
bdd429981c Update version in ut 2023-11-15 15:56:13 -05:00
GomathiselviS
6956a77f8c Add changelog 2023-11-15 15:47:59 -05:00
GomathiselviS
1670e35cd8 Update python kubernetes library to 24.2.0 , helm/kind-action to 1.8.0 2023-11-15 15:43:15 -05:00
Bikouo Aubin
b44fdd3f05 helm - fix issue for helm command when chart contains space into its name (#657)
* fix issue for helm command when chart contains space into its name
2023-11-13 11:48:13 +01:00
GomathiselviS
b066a2dda3 Cleanup GitHub workflows (#655)
* Cleanup gha

* test by removing matrix excludes

* Rename sanity tests

* trigger integration tests

* Fix ansible-lint workflow

* Fix concurrency

* Add ansible-lint config

* Add ansible-lint config

* Fix integration and lint issues

* integration wf

* fix yamllint issues

* fix yamllint issues

* update readme and add ignore-2.16.txt

* fix ansible-doc

* Add version

* Use /dev/random to generate random data

The GHA environment has difficultly generating entropy. Trying to read
from /dev/urandom just blocks forever. We don't care if the random data
is cryptographically secure; it's just garbage data for the test. Read
from /dev/random, instead. This is only used during the k8s_copy test
target.

This also removes the custom test module that was being used to generate
the files. It's not worth maintaining this for two task that can be
replaced with some simple command/shell tasks.

* Fix saniry errors

* test github_action fix

* Address review comments

* Remove default types

* review comments

* isort fixes

* remove tags

* Add setuptools to venv

* Test gh changes

* update changelog

* update ignore-2.16

* Fix indentation in inventory plugin example

* Update .github/workflows/integration-tests.yaml

* Update integration-tests.yaml

---------

Co-authored-by: Mike Graves <mgraves@redhat.com>
Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
2023-11-10 16:33:40 +01:00
Will Thames
9e9962bc6c Provide a mechanism to hide fields from output (#629)
Provide a mechanism to hide fields from output

SUMMARY
The k8s and k8s_info modules can be a little noisy in verbose mode, and most of that is due to managedFields.
If we can provide a mechanism to hide managedFields, the output is a lot more useful.
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
k8s, k8s_info
ADDITIONAL INFORMATION
Before
ANSIBLE_COLLECTIONS_PATH=../../.. ansible -m k8s_info -a 'kind=ConfigMap name=hide-fields-cm namespace=hide-fields' localhost 
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
    "api_found": true,
    "changed": false,
    "resources": [
        {
            "apiVersion": "v1",
            "data": {
                "another": "value",
                "hello": "world"
            },
            "kind": "ConfigMap",
            "metadata": {
                "annotations": {
                    "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"another\":\"value\",\"hello\":\"world\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"hide-fields-cm\",\"namespace\":\"hide-fields\"}}\n"
                },
                "creationTimestamp": "2023-06-13T01:47:47Z",
                "managedFields": [
                    {
                        "apiVersion": "v1",
                        "fieldsType": "FieldsV1",
                        "fieldsV1": {
                            "f:data": {
                                ".": {},
                                "f:another": {},
                                "f:hello": {}
                            },
                            "f:metadata": {
                                "f:annotations": {
                                    ".": {},
                                    "f:kubectl.kubernetes.io/last-applied-configuration": {}
                                }
                            }
                        },
                        "manager": "kubectl-client-side-apply",
                        "operation": "Update",
                        "time": "2023-06-13T01:47:47Z"
                    }
                ],
                "name": "hide-fields-cm",
                "namespace": "hide-fields",
                "resourceVersion": "2557394",
                "uid": "f233da63-6374-4079-9825-3562c0ed123c"
            }
        }
    ]
}

After
ANSIBLE_COLLECTIONS_PATH=../../.. ansible -m k8s_info -a 'kind=ConfigMap name=hide-fields-cm namespace=hide-fields hidden_fields=metadata.managedFields' localhost
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
    "api_found": true,
    "changed": false,
    "resources": [
        {
            "apiVersion": "v1",
            "data": {
                "another": "value",
                "hello": "world"
            },
            "kind": "ConfigMap",
            "metadata": {
                "annotations": {
                    "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"another\":\"value\",\"hello\":\"world\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"hide-fields-cm\",\"namespace\":\"hide-fields\"}}\n"
                },
                "creationTimestamp": "2023-06-13T01:47:47Z",
                "name": "hide-fields-cm",
                "namespace": "hide-fields",
                "resourceVersion": "2557394",
                "uid": "f233da63-6374-4079-9825-3562c0ed123c"
            }
        }
    ]
}

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Will Thames
2023-06-21 07:57:53 +00:00
Bikouo Aubin
9ca13c3799 Remove black auto formatting workflow (#632)
Remove black auto formatting workflow

SUMMARY
We don't have a proper tool to help trigger CI when a commit is pushed on a pull request.
Remove the black/format workflow until we found a token with valid perms
ISSUE TYPE


CI

Reviewed-by: Alina Buzachis
Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-06-14 16:37:56 +00:00
Bikouo Aubin
318529abaa remove references to personnal repo and add galaxy importer job (#626) 2023-06-01 13:29:15 +02:00
Bikouo Aubin
6d0a3af311 add ability to filter the list of pods to be drained by a pod label selector (#606)
* add ability to filter the list of pods to be drained by a label selector
2023-05-31 09:12:09 +02:00
GomathiselviS
54d8193972 Add unit and sanity tests to GHA (#614)
* Add unit and sanity tests to GHA

Signed-off-by: GomathiselviS <gomathiselvi@gmail.com>

* Fix sanity issues

* Add sanity non voting

* Add changelog

* Fix typo

* Fix typo

* Use pytest-ansible

* Add support for pytest-ansible

---------

Signed-off-by: GomathiselviS <gomathiselvi@gmail.com>
2023-05-17 18:47:11 +02:00
Bikouo Aubin
a624251bba CI - increase the number of integration workflow (#620)
enable profile_tasks callback plugin for integration tests targets
2023-05-02 19:01:04 +02:00
Mark D
869f06f1e4 Update kubernetes.core.k8s_drain_module.rst for issue #615 (#616)
Update kubernetes.core.k8s_drain_module.rst for issue #615

SUMMARY
Quick documentation fix to the example section of the k8s_drain_module documentation to make the "force" option work. I also updated the formatting of the "grace_period" example to follow the two space formatting in the rest of the examples.
Fixes #615
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
k8s_drain_module.rst
ADDITIONAL INFORMATION
I have tested that the example works based on running ansible as shown:
# ansible --version
ansible [core 2.14.4]
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.11/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.11.3 (main, Apr  5 2023, 00:00:00) [GCC 12.2.1 20221121 (Red Hat 12.2.1-4)] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

Reviewed-by: Bikouo Aubin
2023-04-21 14:06:44 +00:00
Bikouo Aubin
7919231df1 add linters github action (#613) 2023-04-17 17:44:50 +02:00
Bikouo Aubin
ea28cbaa59 CI - changelog and ansible test splitter jobs (#612)
* ansible test splitter + changelog

* fix action name

* add integration tests

* fix tests

* add changelog

* fix ansible_test_integration action version

* add exclude for matrix

* fix step id

* remove additional libs to install

* minor updates on how splitter is called
2023-04-17 12:25:17 +02:00
Bikouo Aubin
560e0e3d40 automated changes for black formatting (#611) 2023-04-11 14:30:36 +02:00
Bikouo Aubin
ed09047699 remove changelog auto update (#610)
remove changelog auto update

Revert workflow
2023-04-07 14:57:14 +00:00
Paul Voss
2d1ec22405 use post_renderer when checking 'changed' status for a helm release (#588)
use post_renderer when checking 'changed' status for a helm release

SUMMARY

helmdiff_check needs to use --post-renderer if configured in order to detect changes correctly
idempotency still seems to work
ISSUE TYPE


Bugfix Pull Request (50%)
Feature Pull Request (50%)

COMPONENT NAME

kubernetes.core.helm
ADDITIONAL INFORMATION



- /snap/bin/helm diff upgrade myrelease some/chart --version=1.2.3 --reset-values -f=/tmp/tmpnn0rr50h.yml
+ /snap/bin/helm diff upgrade myrelease some/chart --version=1.2.3 --reset-values --post-renderer=/tmp/somescript.sh -f=/tmp/tmpnn0rr50h.yml

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-04-05 13:50:36 +00:00
Bikouo Aubin
71599e2fde Rename Github actions (#605) 2023-04-04 15:43:30 +02:00
Bikouo Aubin
8687994e9f auto push changes
push changes on black formating or changelog
2023-04-04 12:30:42 +02:00
Paul Voss
8640c16cd4 fix post_renderer argument breaking the helm deploy_command (#586)
fix post_renderer arguments breaking the helm deploy_command

SUMMARY

The post_renderer setting is broken and resets the deploy_command instead of appending an argument. Diff should be self explanatory.
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

kubernetes.core.helm
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-04-03 18:46:03 +00:00
Bikouo Aubin
deb4859f19 update github action (#602)
update github action

Update github action
2023-03-29 10:17:44 +00:00
Bikouo Aubin
fb2af07583 PR AutoFixing via GHA (#600)
auto commit changelog and black formatting fixes
2023-03-29 07:55:53 +02:00
Bikouo Aubin
151ed8245f make name optional to delete all resources for the specified resource type (#517)
make name optional to delete all resources for the specified resource type

SUMMARY

closes #504
k8s module should allow deleting all namespace resources for the specified resource type.

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

k8s
ADDITIONAL INFORMATION


Delete all Pods from namespace test

- k8s:
    namespace: test
    kind: Pod
    api_version: v1
    delete_all: true
    state: absent

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin
2023-03-23 15:43:22 +00:00
Bikouo Aubin
09a3c837c3 [helm] add the ability for the module to uninstall pending-install releases (#589)
[helm] add the ability for the module to uninstall pending-install releases

SUMMARY

closes #319

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin
2023-03-06 17:07:52 +00:00
Alina Buzachis
31c1ccf962 Deprecate inventory plugin (#582)
Deprecate inventory plugin

SUMMARY

Deprecate inventory plugin

ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin
2023-02-16 14:57:38 +00:00
Bikouo Aubin
031cc7c40d add reuse_values and reset_values support to helm module (#575)
helm - add reuse_values and reset_values support

SUMMARY

closes #394

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-02-16 07:47:33 +00:00
schwadim
22764492d2 Pass right amount of args to ResourceTimeout (#585)
Pass right amount of args to ResourceTimeout

SUMMARY
Pass right amount of args to ResourceTimeout
Fixes #583
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
k8s_scale
ADDITIONAL INFORMATION
ResourceTimeout constuructor does not accept variable argument length.
The passed result dict seems not to be used currently. One could also pass result["result"] or not pass result at all.

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-02-15 08:38:16 +00:00
Bikouo Aubin
3d313cf837 helm fix with release_values option set (#573)
helm - delete temporary file created when using option release_values

SUMMARY

closes #530

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

helm
2023-02-07 14:55:14 +00:00
Mandar Kulkarni
deaf8ee4f3 k8s_scale - handle scaling StatefulSets with 'updateStrategy=OnDelete' (#579)
k8s_scale - handle scaling StatefulSets with 'updateStrategy=OnDelete'

SUMMARY

Likely Fixes #503

Handle scaling StatefulSets with 'updateStrategy=OnDelete'
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_scale
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin <None>
2023-02-06 20:04:41 +00:00
Bikouo Aubin
0f7963beb9 Release 2.4.0 (#572) (#580)
Release 2.4.0 over main branch

SUMMARY


ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-02-03 13:52:08 +00:00
Bikouo Aubin
e6ac874098 k8s_info - fix issue with kubernetes-client caching when api-server was available (#571)
k8s_info - fix issue with kubernetes-client caching when api-server was available

SUMMARY
closes #508
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_info
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-01-24 10:43:42 +00:00
Bikouo Aubin
8ed4d4b6ed k8s_info - fix issue with kubernetes-client caching when api-server was available (#571)
k8s_info - fix issue with kubernetes-client caching when api-server was available

SUMMARY
closes #508
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_info
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-01-24 10:43:30 +00:00
Bikouo Aubin
af7c24cba7 helm - add support for -set options when running helm install (#546)
helm - add support for -set options when running helm install

SUMMARY

helm support setting options -set, -set-string, -set-file and -set-json when running helm install

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: Bikouo Aubin <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-01-23 16:19:42 +00:00
Bikouo Aubin
804b9ab57c Helm - Fix issue with alternative kubeconfig (#563)
Helm - Fix issue with alternative kubeconfig

SUMMARY

closes #538

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

helm modules

Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-01-12 09:46:42 +00:00
Bikouo Aubin
26cd550bc0 fix multiple issues with dry_run logic (#561)
fix multiple issues with dry_run logic

SUMMARY

Fix multiple issues with dry_run logic

The parameter value passed to the client set to dry_run=All instead of dry_run=True.
Add conditional check for Kubernetes release for the dry_run to be set
Add integration test that checks to ensure server side dry run is being used during check mode.


ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Jill R <None>
2023-01-11 07:57:39 +00:00
Bikouo Aubin
0b4fda7585 add documentation for filter plugin kubernetes.core.k8s_config_resource_name (#559)
k8s_config_resource_name filter plugin - add missing documentation

SUMMARY

Fixes #558

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

kubernetes.core.k8s_config_resource_name

Reviewed-by: Jill R <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2023-01-11 07:54:56 +00:00
Bikouo Aubin
42ee210ecf k8s_cp - fix issue when directory contains space in its name (#552)
k8s_cp - fix issue when directory contains space in its name

Depends-On: #549
SUMMARY

There is a remaining issue not addressed by  #512 when copying directory from Pod to local filesystem, if the directory contains space into its name, the directory was not copied

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_cp
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin <None>
2022-12-15 18:17:21 +00:00
Bikouo Aubin
c073eea5b3 k8s - fix issue with server side apply (#549)
k8s - fix issue with server side apply

SUMMARY

Fix #548 and #547

ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin <None>
2022-12-15 18:17:17 +00:00
Bikouo Aubin
979b492233 k8s_cp: add support for check_mode, fix doc issue, remove dependency with 'find' when state=from_pod (#512)
k8s_cp: add support for check_mode, fix doc issue, remove dependency with 'find' when state=from_pod

Depends-On: ansible/ansible-zuul-jobs#1635
Depends-On: ansible/ansible-zuul-jobs#1636
Depends-On: #518
Depends-On: #520
SUMMARY

add support for check_mode, closes #380
fix doc issue, closes #485
Remove dependency with 'find' executable when state=from_pod, closes #486

ISSUE TYPE


Bugfix Pull Request
Docs Pull Request
Feature Pull Request

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin <None>
2022-12-09 16:00:14 +00:00
Bikouo Aubin
646eb18806 fix ci jobs (#545)
fix ci jobs

Unit tests job is failing, trying to fix it
ISSUE TYPE


Bugfix Pull Request
2022-11-24 14:47:39 +00:00
Bikouo Aubin
b967b55a16 k8s_log - fix issue when required name is not provided, add all_containers support (#528)
k8s_log - fix issue when required name is not provided, add all_containers support

SUMMARY

Fixes issue when the required name is not provided, closes #514
all support for all_containers option

ISSUE TYPE


Bugfix Pull Request
Feature Pull Request

COMPONENT NAME

k8s_log
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Jill R <None>
2022-10-24 23:43:26 +00:00
Bikouo Aubin
1b66dbbd8b v1beta replaced with v1 for CRD (#532)
v1beta replaced with v1 for CRD

SUMMARY

fixes k8s_crd test

ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-10-20 06:40:32 +00:00
Fors1
2a3862b67a Added possibility to get all values by helm_info module (#531)
Added possibility to get all values by helm_info module

SUMMARY
Parameter get_all_values has been added, which is passed to function get_values. Default is False. Parameter is not required.
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
helm_info
ADDITIONAL INFORMATION
Unfortunately, helm_info module lacks functionality of getting all the values of a helm release, including the default ones. This restricts upgrade and config migration capabilities. Parameter get_all_values has been added. This parameter, if set, adds -a parameter to helm get values call. The parameter is not required and defaults to False, so backwards compability is complied.

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin <None>
2022-10-19 15:41:37 +00:00
Bikouo Aubin
2092d921cd helm - new module to perform helm pull (#410)
helm - new module to perform helm pull

Depends-On: ansible/ansible-zuul-jobs#1586
SUMMARY

#355
new module to manage chart downloading helm pull

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm_pull

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin <None>
2022-10-12 13:34:19 +00:00
Mike Graves
29c75fa1c6 Update for Ansible 2.15 sanity tests (#515)
Update for Ansible 2.15 sanity tests

Depends-On: ansible/ansible-zuul-jobs#1639
SUMMARY

Update for Ansible 2.15 sanity tests

Fixes #519
ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Bikouo Aubin <None>
2022-10-06 09:50:26 +00:00
Mor Cohen
0e86fe0b7b [helm] Add the force_update arg (#509)
[helm] Add the force_update arg

Depends-On: ansible/ansible-zuul-jobs#1648
Depends-On: #522
SUMMARY
Sometimes a Helm repo needs to be updated with a new URL. The helm repo add command allows for this with the --force-update flag:
      --force-update               replace (overwrite) the repo if it already exists


ISSUE TYPE

Feature Pull Request - Closes #491

COMPONENT NAME
kubernetes.core.helm_repository
ADDITIONAL INFORMATION

Reviewed-by: Mor Cohen <morcohen1201@gmail.com>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-10-03 15:00:44 +00:00
Mike Graves
43ad31d936 Fix helm test suite (#522)
Fix helm test suite

SUMMARY

The old version of the nginx ingress controller that was being used for helm testing is incompatible with the recent upgrade to k8s 1.25 in CI. This upgrades the version used for testing and fixes a few other related issues.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
2022-10-03 15:00:39 +00:00
Gonéri Le Bouder
093d06ab55 tests/k8s_user_impersonation: adjustement for k8s 1.24 (#520)
tests/k8s_user_impersonation: adjustement for k8s 1.24

In Kubernetes 1.24, ServiceAccount token secrets are no longer automatically generated.
See: KEP-2799

Reviewed-by: Bikouo Aubin <None>
2022-09-28 19:34:46 +00:00
Gonéri Le Bouder
454d0efe0a k8s_rollback/tests: speed up test (#518)
k8s_rollback/tests: speed up test

Set a low timeout for the tasks that are expected to fail fast to speed up the whole tests.

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-09-28 19:32:11 +00:00
Mauricio Teixeira
08596fd05b Add example usage of from_yaml_all (#505)
Add example usage of from_yaml_all

Depends-On: #513
SUMMARY
Sometimes one might want to use a single YAML file that contains multiple Kubernetes definitions. This PR updates the documentation to provide a simple example of how to accomplish that.
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
k8s
ADDITIONAL INFORMATION
I have not tested this solution against other modules, simply because I did not have use-case for those.

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-09-21 13:51:54 +00:00
Gonéri Le Bouder
5e48c6973c tests: adjust the target durations (#513)
tests: adjust the target durations

By default, the duration defined by the time=XX entry is in second.
The value set for k8s_info was way to low.
This commit also increase some other durations to be sure we don't hit
timeout.

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-09-20 19:35:09 +00:00
Bikouo Aubin
a3a5f3cf4b helm - add support for in-memory kubeconfig (#497)
helm - add support for in-memory kubeconfig

SUMMARY

closes #492

ISSUE TYPE


Feature Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Bikouo Aubin <None>
2022-09-12 09:13:19 +00:00
Bikouo Aubin
5ff3566f30 handle aliases for lookup and inventory plugins for authentication options (#500)
Honor aliases for lookup and inventory plugins

rebase and extend the following PR #71
ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-08-23 07:58:08 +00:00
Bikouo Aubin
c4c12ca2c3 Fix linters job failing following flake8 release # https://github.com/PyCQA/flake8/pull/1648 (#498)
[Fix linters] flake8 validation is failing

Merge and releasing of the following PR PyCQA/flake8#1648
ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-08-03 15:52:04 +00:00
Bikouo Aubin
7f7008fecc k8s_log - fix module traceback when resource not found (#493)
k8s_log - fix module traceback when resource not found

Depends-on: #495
SUMMARY

closes #479

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_log

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-07-28 14:24:35 +00:00
Mike Graves
09d54919e3 Update sanity ignores (#495)
Update sanity ignores

SUMMARY

A recent change in CI config means we need to add ignores for
unsupported versions of python (2.6, 2.7 and 3.5). These changes
effectively prevent sanity from doing anything when running on those
versions and Ansible < 2.12. In Ansible 2.12 and later, this can be
handled on a global level in tests/config.yml.

ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Bikouo Aubin <None>
2022-07-28 14:16:47 +00:00
kurokobo
58cbbf6364 feat: add new tail_lines parameter to k8s_log module (#488) (#489)
feat: add new tail_lines parameter to k8s_log module (#488)

SUMMARY

Add new tail_lines parameter to k8s_log module to limit the number of lines to be retrieved from the end of the logs.
Closes #488.

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

kubernetes.core.k8s_log
ADDITIONAL INFORMATION


Thanks for useful collection 😃
This is the first time to send PR to this collection, so please let me know if I'm on the wrong way.

The version_added is set to 2.4.0, but I'm not aware of the roadmap for this collection, so I'd like to know this is the right version to specify.
Changelog and simple integration test is also added.
It seems that the end of log_lines always contains an empty element, so if tail_lines is set to 5, the length of log_lines will be 6, as noted in the comment in the test. I've considered that truncating the trailing empty element, but decided not to for the following reasons.

It is inconsistent and unnatural to remove trailing empty elements only when tail_lines is specified.
Removing trailing empty elements always with or without tail_lines is a destructive change and should not be done because it would break backward compatibility.




Example tasks in playbook:
  tasks: 
    - name: create a job that has 10 lines of log
      kubernetes.core.k8s:
        state: present
        wait: yes
        wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
        wait_condition:
          type: Complete
          status: 'True'
        definition:
          apiVersion: batch/v1
          kind: Job
          metadata:
            name: multiline-log
            namespace: test
          spec:
            template:
              spec:
                containers:
                  - name: busybox
                    image: busybox
                    command: ['sh']
                    args: ['-c', 'for i in $(seq 0 9); do echo $i; done']
                restartPolicy: Never
            backoffLimit: 4

    - name: retrieve all logs from the job
      kubernetes.core.k8s_log:
        api_version: batch/v1
        kind: Job
        namespace: test
        name: multiline-log
      register: full_log

    - name: retrieve last 5 lines of log from the job
      kubernetes.core.k8s_log:
        api_version: batch/v1
        kind: Job
        namespace: test
        name: multiline-log
        tail_lines: 5
      register: tailed_log

    - ansible.builtin.debug:
        var: full_log.log_lines

    - ansible.builtin.debug:
        var: tailed_log.log_lines
Example output:
TASK [create a job that has 10 lines of log] *****************************************************************************************
ok: [localhost]

TASK [retrieve all logs from the job] ************************************************************************************************
ok: [localhost]

TASK [retrieve last 5 lines of log from the job] *************************************************************************************
ok: [localhost]

TASK [ansible.builtin.debug] *********************************************************************************************************
ok: [localhost] => 
  full_log.log_lines:
  - '0'
  - '1'
  - '2'
  - '3'
  - '4'
  - '5'
  - '6'
  - '7'
  - '8'
  - '9'
  - ''

TASK [ansible.builtin.debug] *********************************************************************************************************
ok: [localhost] => 
  tailed_log.log_lines:
  - '5'
  - '6'
  - '7'
  - '8'
  - '9'
  - ''

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

Reviewed-by: Bikouo Aubin <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-07-25 19:03:49 +00:00
Bikouo Aubin
7d0f0449ae Support resource definition using manifest URL (#478)
Support resource definition using manifest URL

SUMMARY

Closes #451

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

k8s
k8s_scale
k8s_service

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Bikouo Aubin <None>
2022-07-04 12:49:53 +00:00
Mike Graves
9f51fc0ef0 Merge pull request #481 from ansible-collections/2.x-refactor
Refactor common.py
2022-06-23 15:28:44 -04:00
Mike Graves
14fe6f1c55 Add changelog fragment 2022-06-17 10:46:57 -04:00
Mike Graves
4f1623fe9c Add deprecation notice 2022-06-17 10:39:52 -04:00
Mike Graves
adf3503d4e Migrate k8s_taint to refactored code (#477)
Migrate k8s_taint to refactored code

Depends-on: #476
SUMMARY

This changes the k8s_taint module to use the newly refactored code.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
2022-06-15 14:41:10 +00:00
Mike Graves
beb53652db Ensure CoreExceptions are handled gracefully (#476)
Ensure CoreExceptions are handled gracefully

SUMMARY

CoreExceptions, when raised, should have a reasonably helpful and
actionable message associated with them. This adds a final check in
module execution to gracefully fail from these exceptions. A new
fail_from_exception method is added both to simplify exiting the module,
and to ensure that any chained exceptions are available when using -vvv.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: Joseph Torcasso <None>
2022-06-15 13:26:24 +00:00
Mike Graves
92785f58da Port changes from main to refactored branch (#472)
Port changes from main to refactored branch

Depends-on: ansible/ansible-zuul-jobs#1563
SUMMARY

This PR contains several commits that complete the rebase of the 2.x-refactor branch onto main. Most of the changes here had to be manually backported after rebasing as the original changes were to code that will be deprecated. In addition, rather than trying to manually sort out conflicts and changes to the sanity ignores, I rewrote the refresh_ignore_files script to fully automate the management of ignore files. Previously, these files were both manually edited and auto-generated. This should no longer be the case, and these files should never be manually edited going forward.
For the purposes of reviewing and history, I kept all changes in separate commits tied to the original commit being backported.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Jill R <None>
2022-06-09 15:20:48 +00:00
Mike Graves
25644ac192 Move diff and wait to perform_action (#375)
This primarily moves the diff and wait logic from the various service
methods to perform_action to eliminate code duplication. I also moved
the diff_objects function out of the service object and moved most of
the find_resource logic to a new resource client method. We ended up
with several modules creating a service object just to use one of these
methods, so it seemed to make sense to make these more accessible.
2022-05-26 08:56:56 -04:00
Alina Buzachis
3bf147580f Migrate k8s (#311)
* Use refactored module_utils

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

* Fix runner

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

* Fix runner

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

* Update runner.py

* black runner

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

* Fix units

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

* Fix ResourceTimeout

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

* Attempt to fix 'Create custom resource'

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

* Update svc.find_resource(..., fail=True)

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

* Attempt to fix integration tests

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

* Fix apiVersion for Job

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

* Fix crd

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

* Add exception = None

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

* Fix apiVersion for definition

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

* Fix assert

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

* Fix returned results

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

* Update runner to return results accordingly

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

* Fix assert

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

* Add validate-missing

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

* Update client.py

* Fix failures

* Fix black formatting

Co-authored-by: Mike Graves <mgraves@redhat.com>
2022-05-26 08:15:45 -04:00
Bikouo Aubin
193a0cb68c Fix kubeconfig parameter when multiple config files are provided (#468)
Fix kubeconfig parameter when multiple config files are provided

SUMMARY

#435

ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-05-25 15:04:02 +00:00
Alina Buzachis
58a0fb1605 Refactor k8s_exec to use new module_utils code (#328)
* Refactor k8s_exec to use new module_utils code

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

* Fix client

Signed-off-by: Alina Buzachis <abuzachis@redhat.com>
2022-05-24 14:58:57 -04:00
Mike Graves
b62ea00ebf Refactor k8s_cluster_info to use new module_utils code (#325)
Refactor k8s_cluster_info to use new module_utils code

SUMMARY

Refactor k8s_cluster_info to use new module_utils code

ISSUE TYPE
COMPONENT NAME

k8s_cluster_info
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-05-24 14:56:59 -04:00
Alina Buzachis
349e9f473a Refactor k8s_service to use new module_utils code (#327)
Refactor k8s_service to use new module_utils code

SUMMARY

Refactor k8s_service to use new module_utils code

ISSUE TYPE

Feature Pull Request

COMPONENT NAME

k8s_service

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-05-24 14:56:57 -04:00
Alina Buzachis
61faa1079e [backport/2.2] Update k8s_scale to use module_utils refactored code 2022-05-24 14:53:37 -04:00
Mike Graves
08a3d951d0 Move module dependency functions outside of module (#342)
Move module dependency functions outside of module

SUMMARY

This moves the has_at_least and requires functions that had been on the
module to top level functions. The functions on the module now call
these with a few added bits of functionality.
Moving these functions to the top level and removing their requirement
on having a module makes them usable in situations where we may not yet
have a module, such as during client creation.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-05-24 14:28:30 -04:00
Mike Graves
8171c994df [backport/2.2] Migrate k8s_cp module to new refactored code (#329)
Co-authored-by: Alina Buzachis <abuzachis@redhat.com>
2022-05-24 14:28:27 -04:00
Alina Buzachis
f5a0dd5946 Update k8s_rollback to use refactored module_utils (#338)
Update k8s_rollback to use refactored module_utils

SUMMARY

Update k8s_rollback to use refactored module_utils

ISSUE TYPE

Feature Pull Request

COMPONENT NAME

k8s_rollback

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-05-24 13:46:34 -04:00
Alina Buzachis
9aa20f0fbe Migrate k8s_info (#310)
* Use refactored module_utils

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

* Update k8s_info.py

* Fix assertion

Signed-off-by: Alina Buzachis <abuzachis@redhat.com>
2022-05-24 13:31:46 -04:00
Mike Graves
afa6a74178 Migrate json_patch to use new refactored code (#339)
Migrate json_patch to use new refactored code

SUMMARY

Migrate json_patch to use new refactored code

ISSUE TYPE

COMPONENT NAME

k8s_json_patch
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-05-24 13:29:59 -04:00
Mike Graves
e2e3f71ecf [backport/2.2] Migrate k8s_log to new refactored code (#336) 2022-05-24 13:29:57 -04:00
Mike Graves
346e303084 [backport/2.x] Migrate k8s_drain to use refactored code (#330) 2022-05-24 12:17:37 -04:00
Alina Buzachis
d68da5bbdd k8s runner (#309)
k8s runner

SUMMARY

k8s runner
Requires: #307

ISSUE TYPE

New Module Pull Request

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-05-24 11:57:54 -04:00
Alina Buzachis
e2f54d3431 K8sService class (#307)
K8sService class

SUMMARY

This refactors the perform_action() logic from common.py into a separate K8sService class.
TODO:

 Unit tests.

ISSUE TYPE

New Module Pull Request

COMPONENT NAME

service.py

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-05-24 11:57:01 -04:00
Mike Graves
f168a3f67f Add new waiter (#306)
Add new waiter

SUMMARY

This refactors the waiter logic from common.py into a separate module.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: None <None>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-05-24 11:56:20 -04:00
Alina Buzachis
7fb89a7b6f Initial work K8S client class (#276)
Initial work K8S client class

SUMMARY

Initial work on K8SClient Class.

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-05-24 11:55:40 -04:00
Mike Graves
42644ee26e Add resource definition refactor (#278)
Add resource definition refactor

SUMMARY

This refactors most of the logic around creating a list of functional
resource definitions based on input parameters for the module.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-05-24 11:54:59 -04:00
Mike Graves
2a9d894c90 Add new AnsibleK8SModule class (#269)
* Add new AnsibleK8SModule class

This class is intended to replace part of the K8SAnsibleMixin class and
is part of a larger refactoring effort.

* Fix sanity errors

* Fix unit tests

* Add mock to test requirements
2022-05-24 11:53:58 -04:00
Mike Graves
3729b8bb5b Add missing PSF license (#463)
Add missing PSF license

SUMMARY

Add missing PSF license
Fixes #462

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Felix Fontein <felix@fontein.de>
2022-05-16 16:19:39 +00:00
Mike Graves
531a9fe3ac Remove distutils from connection plugin (#456)
Remove distutils from connection plugin

Depends-On: ansible/ansible-zuul-jobs#1527
SUMMARY

distutils.spawn.find_executable is deprecated and shutils.which is a
suitable replacement.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
Reviewed-by: Joseph Torcasso <None>
2022-05-11 18:56:27 +00:00
Mike Graves
77dd2496d0 Bring docs changes over from latest release (#455)
Bring docs changes over from latest release

Depends-On: ansible/ansible-zuul-jobs#1526
SUMMARY

Brings docs generation changes over from 2.3.1 release.

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
2022-05-10 15:14:23 +00:00
Christian von Stebut
95e2add65b Helm template add name and disable hook (#405)
Helm template add name and disable hook

SUMMARY
This PR adds "disable_hook" and "name" (NAME of the release)  as optional arguments to the helm_template module.
It contains the rest of my planned work towards #313.
ISSUE TYPE


Feature Pull Request

COMPONENT NAME

plugins/modules/helm_template.py
changelogs/fragments/313-helm-template-add-support-for-name-and-disablehook.yml
tests/unit/modules/test_helm_template.py
integration/targets/helm/tasks/tests_chart.yml
ADDITIONAL INFORMATION


The PR contains unit tests and an integration test for the new parameters added in this and the previous PR.
I limited the execution of the integration test to the local test chart, because the testing of the "show_only" parameter requires a known chart structure. As I think I do not have to test the workings of "helm template ..." itself, I hope this is sufficient.
Please adjust / comment as necessary.

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-04-29 13:43:09 +00:00
Mike Graves
b5cfc854cb Change line in doc fragment yaml (#439)
Change line in doc fragment yaml

SUMMARY

For whatever reason, the one line in this doc fragment leads to sanity
failures in the redhat.openshift collection, which uses this fragment.
The downstream build process for that collection creates yaml that
appears to be valid, but that fails to lint. I'm not sure exactly which
tool the problem is in, but the easiest solution is to just remove the
single quotes here.

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
2022-04-28 14:14:29 +00:00
Joshua Eason
5662fa777c Adding previous container log support (#436)
Adding previous container log support

Signed-off-by: Joshua Eason josh.eason@anchore.com
SUMMARY
Adds support for the previous parameter in kubectl logs. This allows for the retrieval of the previously terminated containers logs which is useful for troubleshooting.
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
k8s_log
ADDITIONAL INFORMATION
Adds the previous parameter (bool) to k8s_log module. This matches the documentation for kubectl logs --previous parameter. This parameter allows for retrieving the previously terminated containers logs.
Output of the module is identical with the exception being the logs returned are from the previously terminated container.

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Joshua Eason <None>
2022-04-26 15:31:44 +00:00
Wissem BEN CHAABANE
4fa1fb966b Add helm dependency update (#208)
Add helm dependency update

SUMMARY

Execute the helm dependency update under the hood when found dependencies block in Chart.yaml file.
Support the execution of:

Standalone dependency update by executing: helm dependency update CHART
Inline dependency update when specifying the helm chart_repo_url by adding --dependency-update to the helm install command.


ISSUE TYPE


Feature Pull Request #191

COMPONENT NAME

helm, helm_template
ADDITIONAL INFORMATION





There is a doc generated for history_max option for the helm module. I think that is not generated in the previous PR #164.


There is others changes affect the docs/ folder when I run the collection_prep_add_docs -p .  command. These changes are added in the last commit  64eab40. I let you decide rather we keep the commit or remove it.


The --dependency-update insertion option is tested used a local helm chart repository create via docker. So here are the tasks that test this feature.  Maybe if we create a GitHub repository for the helm chart, we can add this test code in the CI pipeline.


# Test The update dependency with chart_repo_url
- name: "Test chart without dependencies block and chart_repo_url defined"
  block:
    - name: "Test chart without dependencies block and chart_repo_url defined"
      helm:
        binary_path: "{{ helm_binary }}"
        name: test
        chart_ref: "ingress-nginx"
        chart_repo_url: https://kubernetes.github.io/ingress-nginx
        chart_version: "{{ chart_source_version | default(omit) }}"
        namespace: "{{ helm_namespace }}"
        create_namespace: yes
      register: release

    - assert:
        that:
          - "'--dependency-update' not in release.command"
          - "'upgrade' in release.command"
        success_msg: "Command does not contains '--dependency-update' options"
        fail_msg: "Command contains '--dependency-update' options"

- name: "Test chart with dependencies block and chart_repo_url defined and replace True"
  block:
    - name: "Test chart with dependencies block and chart_repo_url defined and replace True"
      helm:
        binary_path: "{{ helm_binary }}"
        name: test1
        chart_ref: "dep_up"
        chart_repo_url: http://repo:8080/charts
        chart_version: "{{ chart_source_version | default(omit) }}"
        namespace: "{{ helm_namespace }}"
        create_namespace: yes
        replace: true
      register: release
    - debug: var=release
    - assert:
        that:
          - "'--dependency-update' in release.command"
          - "'install' in release.command"
        success_msg: "Command contains '--dependency-update' options with helm install command"
        fail_msg: "Command not contains '--dependency-update' with helm install command"

- name: "Test chart with dependencies block and chart_repo_url defined and replace False fails"
  block:
    - name: "Test chart with dependencies block and chart_repo_url defined and replace False fails"
      helm:
        binary_path: "{{ helm_binary }}"
        name: test2
        chart_ref: "dep_up"
        chart_repo_url: http://repo:8080/charts
        chart_version: "{{ chart_source_version | default(omit) }}"
        namespace: "{{ helm_namespace }}"
        create_namespace: yes
        replace: false
      register: release
      ignore_errors: true

    - assert:
        that:
          - release.failed
          - release.msg == "'--dependency-update' hasn't been supported yet with 'helm upgrade'. Please use 'helm install' instead by adding 'replace' option"
        success_msg: "Command build fail when adding  '--dependency-update' with the helm upgrade command"

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Wissem BEN CHAABANE <benchaaben.wissem@gmail.com>
Reviewed-by: Bikouo Aubin <None>
2022-04-26 11:54:37 +00:00
Bikouo Aubin
f2f4b66d77 k8s - fix issue when try to delete resources using label_selectors option (#434)
k8s - fix issue when try to delete resources using label_selectors

SUMMARY

The kubernetes dynamic client has label_selector parameter for the delete method, however based on the documentation of REST API we cannot delete resources using labelSelector option, this fix update the way the resources are deleted. The list of resources are deleted one after another like in the kubectl go client.
Fixes #428

ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Abhijeet Kasurde <None>
2022-04-25 13:37:20 +00:00
Mandar Kulkarni
67c808d934 Update bindep.txt for adding rhel-9 (#438)
Update bindep.txt for adding rhel-9

SUMMARY

Add RHEL9 build for openshift-clients RPM.

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

bindep.txt

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-04-22 12:38:44 +00:00
Bikouo Aubin
882e672bc5 Remove omit from template resource (#432)
Remove ``omit`` value from template args

SUMMARY

While defining resource using template parameter, the code does not remove the omit value if any.
This fix adds a post process to remove any omit value from the resource definition.
fixes #431

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s*

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Abhijeet Kasurde <None>
2022-04-14 06:15:12 +00:00
abikouo
1d05cf54f0 k8s_cp - fix issue when using local_path (#422)
k8s_cp - fix issue when using local_path

SUMMARY

When copying from local path to pod, the file is found on the controller node instead of the managed node.
This PR aims to resolve this issue.
Fixes #421

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_cp

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-04-04 08:10:50 +00:00
Abhijeet Kasurde
764e4499b5 k8s_json_patch: Minor typo fix in Example section (#425)
k8s_json_patch: Minor typo fix in Example section

SUMMARY
Fixes: #411
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
changelogs/fragments/411_k8s_json_patch.yml
plugins/modules/k8s_json_patch.py

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-04-01 00:59:55 +00:00
Mohammed Naser
2c96b70702 Added support for Helm post-renderer (#403)
Added support for Helm post-renderer

SUMMARY

Add support for Helm post renderer
Fixes: #30

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm
ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mohammed Naser <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-03-31 18:55:45 +00:00
Abhijeet Kasurde
c475117bee Copy ignore-2.13.txt to ignore-2.14.txt (#427)
Copy ignore-2.13.txt to ignore-2.14.txt

SUMMARY
Address CI tests for 2.14.0.dev0
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
changelogs/fragments/ignore_2.14.yml
tests/sanity/ignore-2.14.txt
2022-03-31 09:10:01 +00:00
Abhijeet Kasurde
d311ac718e helm_repository: Silence false no_log warning (#423)
helm_repository: Silence false no_log warning

Depends-On: #424
SUMMARY
Apply no_log=True to pass_credentials to silence
false positive warning.
Fixes: #412
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/412_pass_creds.yml
plugins/modules/helm_repository.py

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-03-30 17:09:16 +00:00
Mike Graves
7c71436f3b Upgrade black version (#424)
Upgrade black version

SUMMARY

Move off of beta version of black and pin to current calendar year
version.
The only manual changes here are to tox.ini. Everything else is from running the new version of black.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
2022-03-30 15:02:46 +00:00
Abhijeet Kasurde
0299aa8807 k8s_exec: Update deprecation warning (#419)
k8s_exec: Update deprecation warning

SUMMARY
return_code is deprecated in favor of rc, update the
deprecation warning to tell user about the reason behind
this.
Fixes: #417
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/417_deprecation.yml
plugins/modules/k8s_exec.py

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-03-23 14:14:37 +00:00
abikouo
f418353e44 continue waiting when an exception is raised (#408)
Continue waiting when an exception is raised

SUMMARY
When an exception is raised and the wait_timeout is not reached, we should continue waiting as this may occurs due to temporary issue on cluster

Fixes #407

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Abhijeet Kasurde <None>
2022-03-23 14:12:26 +00:00
abikouo
074f0a6555 fix issue when using k8s_drain with disable_eviction set to yes (#418)
fix issue when using k8s_drain with disable_eviction set to yes

SUMMARY

fixes #416

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_drain
ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
2022-03-23 09:26:06 +00:00
Mike Graves
d68dec3b90 Fix waiting on StatefulSet scale down (#391)
Fix waiting on StatefulSet scale down

SUMMARY

When scaling a StatefulSet down to 0 replicas the wait will fail
because some properties of the status (readyReplicas, updatedReplicas)
will not exist. These are probably defined as omitempty in the API and
since the value is zero are not present in the response.

Fixes #203
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_scale
ADDITIONAL INFORMATION

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
2022-03-11 17:32:12 +00:00
Mike Graves
30e84faa24 Fix validation errors in plugin documentation (#399)
Fix validation errors in plugin documentation

Depends-On: ansible/ansible-zuul-jobs#1385
SUMMARY

This fixes validation errors in plugin documentation now that ansible
test for 2.13 is running validate-modules on all plugins. The kubectl
connection plugin validation is ignored because there seems to be a
requirement for the author field to have a github username, which we do
not have.

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

plugins/connection/kubectl
plugins/inventory/k8s
plugins/lookup/k8s
plugins/lookup/kustomize
ADDITIONAL INFORMATION

Reviewed-by: None <None>
2022-03-11 14:37:01 +00:00
abikouo
fd61f8b15d Move integration test suite from molecule to ansible-test (#392)
Move integration test suite from molecule to ansible-test

SUMMARY

molecule has been replaced with ansible-test
some test cases have been updated

k8s_apply : remove duplicated tasks increasing the running time of the test
helm: use different namespaces for different test cases in order to wait for the namespace deletion before moving to the next test.
all: remove wait: yes at the end of each test when deleting namespace, the role used to create namespace will ensure that it is deleted before if existing.


ISSUE TYPE


Feature Pull Request

COMPONENT NAME

integration testing

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
Reviewed-by: None <None>
2022-03-11 08:03:00 +00:00
Major Hayden
db78d3a505 Docs: Small fix for k8s example (#397)
Docs: Small fix for k8s example

SUMMARY
Update the k8s example to use kubernetes.core.k8s instead of the bare k8s and fix the indentation.
ISSUE TYPE

Docs Pull Request

COMPONENT NAME
kubernetes.core
ADDITIONAL INFORMATION
Just a small documentation fix. 😉

Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-03-08 16:06:10 +00:00
Christian von Stebut
73499d9a09 helm_template: add optional show_only and release_namespace arguments (#388)
helm_template: add optional show_only and release_namespace arguments

SUMMARY

This PR adds the "show_only" and "release_namespace" as optional arguments to the helm_template module.
It does some work towards #313.

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

changelogs/fragments/313-helm-template-add-support-for-show-only-and-release-namespace.yml
plugins/modules/helm_template.py
tests/unit/modules/test_helm_template.py
ADDITIONAL INFORMATION


The PR does include unit tests instead of integration test.
Reasoning:
The existing integration tests already include a task based on helm_template. So we know that the module does a proper job of using the command line generated inside the module to call helm.
As I trust helm itself to "do its job" correctly, all that should be necessary is to test the correct generation of the command line itself. The included unit tests hopefully do a proper job.
With regards of the pretty long testing times for the module, I really prefer unit tests, if at all possible.
Please let me know if this fits.

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-03-01 18:57:17 +00:00
abikouo
7031829897 helm - add support for repo location when running helm diff (#389)
helm - add support for repo location when running helm diff

SUMMARY

closes #174

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

helm

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: None <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-02-22 15:52:08 +00:00
abikouo
44c8cff78b k8s_cp turbo mode compliance (#254)
k8s_cp turbo mode compliance

SUMMARY

closes #237

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2022-02-22 12:01:19 +00:00
Abhijeet Kasurde
aae5960dce helm_info: add release_state argument (#379)
helm_info: add release_state argument

SUMMARY
Specify release state in helm list command as per helm cmdline flags.
Fixes: #377
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/377-helm-info-state.yml
molecule/default/roles/helm/tasks/tests_chart.yml
plugins/modules/helm_info.py

Reviewed-by: None <None>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-02-17 06:42:43 +00:00
abikouo
691f0cb235 add support for check_mode for modules k8s_scale and k8s_rollback (#255)
k8s_scale, k8s_rollback - add support for check_mode 

SUMMARY

closes #243 and #244

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_scale
k8s_rollback
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
2022-02-16 19:05:28 +00:00
Abhijeet Kasurde
951be74dc0 Import ApiException from single source (#384)
Import ApiException from single source

SUMMARY
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/exception.yml
plugins/modules/k8s_drain.py
plugins/modules/k8s_taint.py

Reviewed-by: None <None>
2022-02-15 14:47:33 +00:00
Abhijeet Kasurde
1f79a03edf Adding sleep 0 as workaround when copying files with kubectl exec (#378)
Adding sleep 0 as workaround when copying files with kubectl exec

SUMMARY
For all the commands executed remotely, ** && sleep 0** will be
appended as a workaround for all the commands to terminate properly:
16def8050a/lib/ansible/plugins/action/__init__.py (L1243)
Workaround will be applied in case of kubectl exec too:

  
    
      kubernetes.core/plugins/connection/kubectl.py
    
    
         Line 300
      in
      b19ff9d
    
  
  
    

        
          
           super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) 
        
    
  


That is not the case in the case of the file copy executed by using kubectl exec, therefore it is possible for the kubectl exec to
terminate before dd finishes properly causing the file to be truncated.
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/321-kubectl_sleep.yml
plugins/connection/kubectl.py
2022-02-14 06:21:25 +00:00
Abhijeet Kasurde
bf3fe91a5d k8s_exec: Select first container from the pod (#363)
k8s_exec: Select first container from the pod

SUMMARY
kubectl command select first container from the pod in order
to execute commands on. We replicate the same behavior in k8s_exec
module.
Fixes: #358
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/358-k8s_exec.yml
plugins/modules/k8s_exec.py

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-02-10 04:19:04 +00:00
Abhijeet Kasurde
791175daef Use resource prefix when apiVersion is v1 (#371)
Use resource prefix when apiVersion is v1

SUMMARY
When getting a resource from the core api group, the prefix was not
passed, leading the lookup to happen in all api groups. This broad
search is not really necessary and leads to problems in some corner
cases, for example, when an api is deleted after the api group list is
cached.
This fix uses the 'api' prefix when the apiVersion is 'v1', as this is
almost certainly what the user wants. As a fallback, to retain backwards
compatibility, the old behavior is used if the first lookup failed to
find a resource. Given that the module defaults to 'v1' for the
apiVersion, there are likely many cases where a resource, such as
StatefulSet, is used while failing to provide an apiVersion. While
technically incorrect, this has worked in most cases, so we probably
shouldn't break this behavior.
Fixes #351
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/364-use-resource-prefix.yaml
plugins/module_utils/common.py
2022-02-10 02:41:52 +00:00
Abhijeet Kasurde
e62a271faf helm_repository: Added support for common options (#370)
helm_repository: Added support for common options

SUMMARY
Added support for host, api_key, ca_cert,
and validate_certs in helm_repository module.
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/helm_repository.yml
plugins/modules/helm_repository.py
2022-02-09 18:27:20 +00:00
Abhijeet Kasurde
583de3217c Fix module_defaults by removing routing hack (#372)
Fix module_defaults by removing routing hack

SUMMARY
Fixes #202
Fixes ansible/ansible#76687
As mentioned here, I'm not sure what the redirection was originally solving, but this would be the ideal solution for module_defaults.
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/347-routing.yml
meta/runtime.yml
2022-02-09 16:31:51 +00:00
Abhijeet Kasurde
82565dad78 helm_template: change order of values_release and values_files (#373)
helm_template: change order of values_release and values_files

SUMMARY
This fix aligns precedence of release_values and values_files in kubernetes.core.helm_template with the one in kubernetes.core.helm.
The values in release_values are now processed last, thus with the highest precedence.
This allows overwriting of values in values_files with the values presented in release_values.
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/348-helm_template-fix-precedence-of-release-values-over-values-files.yaml
plugins/modules/helm_template.py
tests/unit/modules/test_helm_template.py
2022-02-09 15:47:17 +00:00
Abhijeet Kasurde
dde6eb3c06 k8scopy: rely on existing kubectl binary (#369)
k8scopy: rely on existing kubectl binary

SUMMARY
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
molecule/default/roles/k8scopy/defaults/main.yml
molecule/default/roles/k8scopy/tasks/main.yml
molecule/default/roles/k8scopy/tasks/test_copy_directory.yml
molecule/default/roles/k8scopy/tasks/test_copy_file.yml
molecule/default/roles/k8scopy/tasks/test_copy_large_file.yml
molecule/default/roles/k8scopy/tasks/test_multi_container_pod.yml
molecule/default/tasks/lookup_kustomize.yml
2022-02-07 16:58:22 +00:00
Gonéri Le Bouder
a122bad685 remove the .zuul.d directory (#367)
remove the .zuul.d directory

We now avoid as much as possible the local Zuul configuration because
it's easy to break them inadvertently.
2022-02-04 18:06:37 +00:00
Mike Graves
b54e9ef4ef Remove serial deletion of pods in template tests (#349)
Remove serial deletion of pods in template tests

SUMMARY

The template test suite deletes twelve pods in serial during cleanup
which is very slow and leads to frequent timeouts. There's no need to do
this since we delete the namespace the pods are in right after.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: None <None>
Reviewed-by: None <None>
2022-01-25 22:23:09 +00:00
abikouo
acb015c788 add patchback bot (#346)
Add patchback bot

Add the configs so that we can use the patchback bot for semi-automated backports.

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-01-24 19:42:00 +00:00
Abhijeet Kasurde
ed33d0b56e Prepare for distutils.version being removed in Python 3.12 (#314)
Prepare for distutils.version being removed in Python 3.12

SUMMARY
distutils has been deprecafed and will be removed from
Python's stdlib in Python 3.12 (see python.org/dev/peps/pep-0632).
This PR replaces the use of distutils.version.LooseVersion and distutils.version.StrictVersion
with LooseVersion from the vendored copy of distutils.version
included with ansible-core 2.12 (ansible/ansible#74644) if available,
and falls back to distutils.version for ansible-core 2.11 and before.
Since ansible-core 2.11 and earlier do not support Python 3.12 (since
they use LooseVersion itself in various places), this incomplete fix
should be OK for now. Also, the way this PR works (by adding a new
module_utils version that abstracts away where LooseVersion comes from),
it is easy to also fix this for ansible-core 2.11 and earlier later on.
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
changelogs/fragments/disutils.version.yml
molecule/default/roles/helm/library/helm_test_version.py
plugins/module_utils/common.py
plugins/module_utils/version.py
plugins/modules/helm.py

Reviewed-by: Felix Fontein <felix@fontein.de>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-01-18 13:03:14 +00:00
abikouo
10cffc5032 Enable turbo mode for k8s lookup plugin (#335)
Enable turbo mode for k8s lookup plugin

SUMMARY

Enable Turbo mode for k8s lookup plugin
This resolves partially #291

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

k8s lookup

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: None <None>
Reviewed-by: None <None>
2022-01-17 12:04:27 +00:00
Max Gautier
9a0b3fe30c Documentation update for kubernetes.core.helm (#317)
Documentation update for kubernetes.core.helm

Clarify usage of the module for doing helm repo update only.
I used collection_prep_add_docs as explained in CONTRIBUTING.md, not sure if
that's correct ?
Fixes #316
@Akasurde

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-01-13 14:47:17 +00:00
Jorn Eilander
c3ecb64b72 Add delete_emptydir_data to drain delete_options (#322)
Add delete_emptydir_data to drain delete_options

SUMMARY
Adds delete_emptydir_data option to k8s_drain.delete_options to evict pods with an emptyDir volume attached.
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
k8s_drain
ADDITIONAL INFORMATION
Be gentle, this is my first pull request 😨 
Basically adds the kubectl drain <node> --delete-emptydir-data feature, including tests.

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Jorn Eilander <None>
Reviewed-by: None <None>
Reviewed-by: None <None>
2022-01-13 13:40:06 +00:00
abikouo
50a1bd9db0 add support for community.okd.openshift_adm_groups_sync (#274)
add support for community.okd.openshift_adm_groups_sync

SUMMARY
new module community.okd.openshift_adm_group_sync requires action group
ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2022-01-13 10:34:06 +00:00
Abhijeet Kasurde
04e14c1f95 DNM: CI fix (#323)
CI fix

SUMMARY
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
molecule/default/tasks/taint.yml

Reviewed-by: None <None>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: None <None>
2022-01-12 08:07:03 +00:00
abikouo
b19ff9d70a k8s - add support for Server Side apply (#260)
k8s - add support for Server Side apply

SUMMARY

Server side apply is now support for k8s module with this Pull request.
The feature is not yet released on kubernetes-client, once this is done, we can merge this pull request.
closes #87

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

k8s
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
Reviewed-by: None <None>
2021-12-16 16:48:00 +00:00
Alessandro Rossi
526f0454ab Fix for common non-ASCII characters in CRDs (#308)
Fix for common non-ASCII characters in CRDs

This should keep the module safe from digesting non-ASCII chars like here (https://github.com/projectcalico/api/pull/46/files)
SUMMARY
Add support for non-ASCII chars in manifests.

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
core.k8s module failing if resources contain non ascii chars

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alessandro Rossi <None>
Reviewed-by: None <None>
2021-12-14 22:08:35 +00:00
Alina Buzachis
e77c8f1449 K8s_taint new module (#264)
K8s_taint new module

SUMMARY

k8s_taint - new module to apply/remove taints to/from nodes.

ISSUE TYPE


New Module Pull Request

COMPONENT NAME

k8s_taint

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
Reviewed-by: None <None>
2021-12-09 18:16:55 +00:00
Mandar Kulkarni
79699ba429 Add integration test to check handling of module_defaults (#296)
Add integration test to check handling of module_defaults 

SUMMARY

Add integration test to make sure that module_defaults are handled correctly in tasks.
Related to #126.

ISSUE TYPE


Bugfix Pull Request

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-12-07 21:33:26 +00:00
Mike Graves
fa65698362 Remove binary file from molecule test suite (#298)
Remove binary file from molecule test suite

SUMMARY

The binary file used to test k8s_cp is causing larger problems
downstream. There's no reason why the binary file needs to function as
all we care about is that the content of the file has not changed during
the copy process. This can be accomplished by comparing file hashes.

Fixes #297 #293
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: None <None>
2021-12-01 20:00:04 +00:00
Fabrice
4ae1856b5c Return diff in helm check mode (#290)
Return diff in helm check mode

When the helm module is executed in check mode with the helm diff plugin
installed, it now returns the diff.
SUMMARY
When the helm module is executed in check mode with the helm diff plugin
installed, it now returns the diff.
COMPONENT NAME
helm
ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Fabrice <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-11-30 17:46:29 +00:00
Mike Graves
ef46c352d0 Fix k8s_drain failing when pod has local storage (#295)
Fix k8s_drain failing when pod has local storage

SUMMARY

The module fails to define the pod_names variable before using it for
pods with local storage.

Fixes #292
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

k8s_drain
ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: None <None>
Reviewed-by: None <None>
2021-11-30 09:07:08 +00:00
Mike Graves
a62c42782f Show diff for black check (#289)
Show diff for black check

SUMMARY

Show diff for black check
This will make it easier to see from the CI logs what the actual problem
is.

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: None <None>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2021-11-30 06:11:10 +00:00
Nataliya Romanovich
ba5cb30305 helm: add pass-credentials key (#282)
helm: add pass-credentials key

SUMMARY
In helm version v3.6.1 when downloading charts from password protected repositories that served from a different domain than the repository, need to use --pass-credentials key.
Add possibility to use the pass-credentials key in helm_repository.py
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
helm_repository

Reviewed-by: None <None>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-11-19 18:20:16 +00:00
abikouo
39b6c43ab7 add support for user impersonation for k8s modules (#250)
add support for user impersonation for k8s modules

SUMMARY

k8s module should not allow user to perform operation using impersonation as describe here
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation
This pull request closes #40

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: None <None>
2021-11-17 13:25:06 +00:00
Gonéri Le Bouder
b0f1501cd4 turn network-ee-sanity-tests non-voting (#284)
turn network-ee-sanity-tests non-voting

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-11-16 20:52:58 +00:00
Mike Graves
1116056eeb Fix sanity tests (#283)
Fix sanity tests

SUMMARY


ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: None <None>
Reviewed-by: None <None>
2021-11-16 16:52:41 +00:00
Mike Graves
60933457e8 Add kubernetes support statement (#279)
Add kubernetes support statement

SUMMARY

Add kubernetes support statement

ISSUE TYPE


Docs Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Timothy Appnel <None>
Reviewed-by: None <None>
2021-11-11 18:25:47 +00:00
itaru2622
9e2d78404f add no_proxy support to k8s* (#272)
add no_proxy support to k8s*

SUMMARY

close #271

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

plugins/module_utils/args_common.py
plugins/modules/k8s*
ADDITIONAL INFORMATION


It requires latest kubernetes library(>=19.15.0) to use this feature.


pip install kubernetes>=19.15.0
then, use following snippet yaml:

  - k8s:
      state: present
      src: "deployment.yaml"
      proxy:      "http://proxy.yourdomain.com:8080/"
      no_proxy:   "localhost,.yourdomain.com,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192,168.0.0/16"

or use environment variable K8S_AUTH_NO_PROXY as well as K8S_AUTH_PROXY.

Reviewed-by: None <None>
Reviewed-by: None <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-11-10 18:25:30 +00:00
Mike Graves
bf26f5a3be Don't wait on *List resources for info module (#253)
Don't wait on *List resources for info module

SUMMARY

We can't use the same wait logic on *List resources because they lack
the same metadata that other resources have. We should ensure that we
are waiting on the items in the list, but not the list itself. Waiting
on the list itself results in unexpected behavior.
This fixes the waiting logic when waiting on a list to wait until the
list being queried contains one or more items, or the wait timeout has
been reached. Each item in the list can then be waited on with the usual
wait logic.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: None <None>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2021-11-08 18:12:25 +00:00
Abhijeet Kasurde
91b80b1d1d Enable black formatting test (#259)
Enable black formatting test

SUMMARY
Signed-off-by: Abhijeet Kasurde akasurde@redhat.com
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
plugins/action/k8s_info.py
plugins/connection/kubectl.py
plugins/doc_fragments/helm_common_options.py
plugins/doc_fragments/k8s_auth_options.py
plugins/doc_fragments/k8s_delete_options.py
plugins/doc_fragments/k8s_name_options.py
plugins/doc_fragments/k8s_resource_options.py
plugins/doc_fragments/k8s_scale_options.py
plugins/doc_fragments/k8s_state_options.py
plugins/doc_fragments/k8s_wait_options.py
plugins/filter/k8s.py
plugins/inventory/k8s.py
plugins/lookup/k8s.py
plugins/lookup/kustomize.py
plugins/module_utils/ansiblemodule.py
plugins/module_utils/apply.py
plugins/module_utils/args_common.py
plugins/module_utils/client/discovery.py
plugins/module_utils/client/resource.py
plugins/module_utils/common.py
plugins/module_utils/exceptions.py
plugins/module_utils/hashes.py
plugins/module_utils/helm.py
plugins/module_utils/k8sdynamicclient.py
plugins/module_utils/selector.py
plugins/modules/helm.py
plugins/modules/helm_info.py
plugins/modules/helm_plugin.py
plugins/modules/helm_plugin_info.py
plugins/modules/helm_repository.py
plugins/modules/helm_template.py
plugins/modules/k8s.py
plugins/modules/k8s_cluster_info.py
plugins/modules/k8s_cp.py
plugins/modules/k8s_drain.py
plugins/modules/k8s_exec.py
plugins/modules/k8s_info.py
plugins/modules/k8s_json_patch.py
plugins/modules/k8s_log.py
plugins/modules/k8s_rollback.py
plugins/modules/k8s_scale.py
plugins/modules/k8s_service.py
tests/integration/targets/kubernetes/library/test_tempfile.py
tests/unit/module_utils/test_apply.py
tests/unit/module_utils/test_common.py
tests/unit/module_utils/test_discoverer.py
tests/unit/module_utils/test_hashes.py
tests/unit/module_utils/test_marshal.py
tests/unit/module_utils/test_selector.py
tox.ini

Reviewed-by: None <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-10-18 15:32:05 +00:00
Mike Graves
4010987d1f Add support for dry run (#245)
Add support for dry run

SUMMARY

Kubernetes server-side dry run will be used when the kubernetes client
version is >=18.20.0. For older versions of the client, the existing
client side speculative change implementation will be used.
The effect of this change should be mostly transparent to the end user
and is reflected in the fact the tests have not changed but should still
pass. With this change, there are a few edge cases that will be
improved. One example of these edge cases is to use check mode on an
existing Service resource. With dry run this will correctly report no
changes, while the older client side implementation will erroneously
report changes to the port spec.

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
Reviewed-by: None <None>
2021-10-15 14:20:43 +00:00
Mike Graves
281ff563ed Use yaml.safe_load in unit tests (#265)
Use yaml.safe_load in unit tests

SUMMARY

The function signature in pyyaml 6 for yaml.load changed. Using
safe_load fixes this.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Jill R <None>
Reviewed-by: None <None>
Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
2021-10-14 19:48:21 +00:00
Mike Graves
ff43353de6 Remove molecule dependencies (#261)
Remove molecule dependencies

SUMMARY

Depends-on: ansible-collections/cloud.common#92
Molecule is overwriting the cloud.common dependency installed by zuul,
which is causing issues with the CI job for turbo mode. We still need to
find a way to test against the latest released version of cloud.common.

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
Reviewed-by: None <None>
2021-10-13 16:16:04 +00:00
Paul Belanger
d6c06a2078 Add openshift-clients to bindep.txt (#249)
Add openshift-clients to bindep.txt

For RHEL8 builds, we use openshift-clients RPM to install both kubectl /
oc clients.
Signed-off-by: Paul Belanger pabelanger@redhat.com

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-10-05 15:53:19 +00:00
abikouo
8436ad1341 Fix sanity test - devel drops support for python 2.6 (#251)
Fix sanity test - devel drops support for python 2.6

SUMMARY


ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: None <None>
2021-10-04 10:12:05 +00:00
abikouo
c65512357d k8s - allow resource definition using generateName (#238)
k8s - allow resource definition using generateName

SUMMARY

#35

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

k8s
ADDITIONAL INFORMATION



- name: create pod using generateName
  k8s:
    namespace: test
    generate_name: pod-
    definition:
       kind: Pod
       spec:
          containers:
          - name: py
            image: python:3.7-alpine

- name: create pod using generateName
  k8s:
    namespace: test
    definition:
       kind: Pod
       metadata:
          generateName: pod-
       spec:
          containers:
          - name: py
            image: python:3.7-alpine

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
Reviewed-by: None <None>
2021-09-30 14:58:50 +00:00
abikouo
8e46f92703 Helm uninstall now support wait parameter (#235)
Helm uninstall now support wait parameter

SUMMARY

closes #33

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION



- helm:
    chart_name: test
    state: absent
    wait: yes

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
Reviewed-by: None <None>
2021-09-29 16:21:37 +00:00
abikouo
ab0e38753b add plugin_version parameter for helm_plugin module (#226)
add plugin_version parameter for helm_plugin module

SUMMARY

closes #157

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
2021-09-29 11:06:33 +00:00
abikouo
6061586289 helm - allow setting timeout independent of wait parameter (#231)
helm - allow setting timeout independent of wait parameter

SUMMARY

closes #67

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: None <None>
2021-09-28 16:29:26 +00:00
Andrew Klychkov
45ba8b1a0d Copy ignore-2.12.txt to ignore-2.13.txt (#247)
Copy ignore-2.12.txt to ignore-2.13.txt

SUMMARY
Relates to ansible-collections/overview#45 (comment)

Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2021-09-28 16:03:34 +00:00
Gonéri Le Bouder
d01e4a6e4d molecule: retry the helm download (#232)
molecule: retry the helm download

Retry the helm download 10 times before giving up.

Reviewed-by: None <None>
2021-09-22 22:37:08 +00:00
Gonéri Le Bouder
938f7e12e8 common/_wait_for: ensure label_selectors is optional (#239)
common/_wait_for: ensure label_selectors is optional

Depends-On: ansible/ansible-zuul-jobs#1125
The label_selectors is a new parameter for _wait_for that was
introduced in #158.
The value is new and it can be set to None to make it optional. It should
not be mandatory a non optional parameter.

Reviewed-by: None <None>
Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
2021-09-22 22:37:06 +00:00
Mike Graves
24ac45741d Increase timeout on scale test (#242)
Increase timeout on scale test

SUMMARY

This test frequently fails with the default 20s timeout. Bumping up to
60s.

Fixes #241
Depends-on: ansible/ansible-zuul-jobs#1131
ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
Reviewed-by: None <None>
2021-09-22 22:33:35 +00:00
495 changed files with 23156 additions and 7132 deletions

View File

@@ -0,0 +1,3 @@
# no-changed-when is not requried for examples
plugins/connection/kubectl.py no-changed-when
meta/runtime.yml meta-runtime[unsupported-version]

8
.config/ansible-lint.yml Normal file
View File

@@ -0,0 +1,8 @@
---
profile: production
exclude_paths:
- .ansible/
- tests/integration
- tests/unit
- tests/sanity

4
.github/patchback.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
---
backport_branch_prefix: patchback/backports/
backport_label_prefix: backport-
target_branch_prefix: stable-

60
.github/stale.yml vendored
View File

@@ -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
View 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
View 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

146
.github/workflows/integration-tests.yaml vendored Normal file
View File

@@ -0,0 +1,146 @@
name: Integration tests
on:
pull_request:
types:
- opened
- reopened
- labeled
- unlabeled
- synchronize
branches:
- main
- stable-*
jobs:
splitter:
env:
source_dir: "./source"
runs-on: ubuntu-latest
outputs:
test_targets: ${{ steps.splitter.outputs.test_targets }}
test_targets_json: ${{ steps.splitter.outputs.test_targets_json }}
test_jobs: ${{ steps.splitter.outputs.test_jobs }}
steps:
- name: Checkout the collection repository
uses: actions/checkout@v3
with:
path: ${{ env.source_dir }}
fetch-depth: "0"
- name: list changes for pull request
id: splitter
uses: ansible-network/github_actions/.github/actions/ansible_test_splitter@main
with:
collections_to_test: ${{ env.source_dir }}
total_jobs: 8
- name: Display splitter output
run: |
echo "test_targets=${{ steps.splitter.outputs.test_targets }}"
echo "test_targets_json=${{ steps.splitter.outputs.test_targets_json }}"
echo "test_jobs=${{ steps.splitter.outputs.test_jobs }}"
shell: bash
integration:
runs-on: ubuntu-latest
timeout-minutes: 60
needs:
- splitter
if: ${{ needs.splitter.outputs.test_targets != '' }}
env:
source: "./source"
cloud_common: "./cloudcommon"
ansible_posix: "./ansible_posix"
strategy:
fail-fast: false
matrix:
ansible-version:
- milestone
python-version:
- "3.12"
enable-turbo-mode:
- true
- false
workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }}
name: "integration-py${{ matrix.python-version }}-${{ matrix.ansible-version }}-${{ matrix.workflow-id }}"
steps:
- name: Read target
id: read-targets
run: |
import json, os
with open(os.environ.get('GITHUB_OUTPUT'), "a", encoding="utf-8") as fh:
fh.write(f'ansible_test_targets={json.loads(os.environ.get("ALL_TEST_TARGETS")).get(os.environ.get("WORKFLOW_ID"))}\n')
shell: python
env:
ALL_TEST_TARGETS: ${{ needs.splitter.outputs.test_targets_json }}
WORKFLOW_ID: ${{ matrix.workflow-id }}
- name: Display ansible test targets
run: |
echo "ansible_test_targets -> ${{ steps.read-targets.outputs.ansible_test_targets }}"
- name: Checkout kubernetes.core repository
uses: actions/checkout@v3
with:
path: ${{ env.source }}
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
# install ansible
- name: Install ansible-core (${{ matrix.ansible-version }})
run: >-
python3 -m pip install
https://github.com/ansible/ansible/archive/${{ matrix.ansible-version }}.tar.gz
--disable-pip-version-check
shell: bash
- name: Build and install collection
id: install-src
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
with:
install_python_dependencies: true
source_path: ${{ env.source }}
- name: checkout ansible-collections/cloud.common
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
with:
repository: ansible-collections/cloud.common
path: ${{ env.cloud_common }}
ref: main
- name: checkout ansible-collections/ansible.posix
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
with:
repository: ansible-collections/ansible.posix
path: ${{ env.ansible_posix }}
ref: main
- name: install cloud.common collection
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
with:
install_python_dependencies: true
source_path: ${{ env.cloud_common }}
- name: install ansible.posix collection
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
with:
install_python_dependencies: true
source_path: ${{ env.ansible_posix }}
- name: create kubernetes cluster
uses: helm/kind-action@v1.8.0
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 }}

23
.github/workflows/linters.yaml vendored Normal file
View File

@@ -0,0 +1,23 @@
---
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@v24.12.2

15
.github/workflows/sanity-tests.yaml vendored Normal file
View 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
View 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

6
.gitignore vendored
View File

@@ -15,4 +15,8 @@ tests/integration/cloud-config-*
.cache
# Helm charts
molecule/default/*-chart-*.tgz
tests/integration/*-chart-*.tgz
# ansible-test generated file
tests/integration/inventory
tests/integration/*-*.yml

View File

@@ -4,6 +4,259 @@ Kubernetes Collection Release Notes
.. contents:: Topics
v3.3.0
======
Minor Changes
-------------
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 6.0.0 (https://github.com/ansible-collections/kubernetes.core/pull/734).
- k8s_drain - Improve error message for pod disruption budget when draining a node (https://github.com/ansible-collections/kubernetes.core/issues/797).
Bugfixes
--------
- helm - Helm version checks did not support RC versions. They now accept any version tags. (https://github.com/ansible-collections/kubernetes.core/pull/745).
- helm_pull - Apply no_log=True to pass_credentials to silence false positive warning.. (https://github.com/ansible-collections/kubernetes.core/pull/796).
- 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 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
======
Bugfixes
--------
- 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).
- k8s_drain - fix error occurring when trying to drain node with disable_eviction set to yes (https://github.com/ansible-collections/kubernetes.core/issues/416).
v2.3.0
======
Minor Changes
-------------
- add support for dry run with kubernetes client version >=18.20 (https://github.com/ansible-collections/kubernetes.core/pull/245).
- fixed module_defaults by removing routing hacks from runtime.yml (https://github.com/ansible-collections/kubernetes.core/pull/347).
- 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_info - add release state as a module argument (https://github.com/ansible-collections/kubernetes.core/issues/377).
- 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 - 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_template - add show_only and release_namespace as module arguments (https://github.com/ansible-collections/kubernetes.core/issues/313).
- 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_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_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).
Bugfixes
--------
- 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).
- common - Ensure the label_selectors parameter of _wait_for method is optional.
- 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_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_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.
- 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
-----------
- k8s_taint - Taint a node in a Kubernetes/OpenShift cluster
v2.2.0
======

View File

@@ -1,5 +1,5 @@
# Also needs to be updated in galaxy.yml
VERSION = 2.2.0
VERSION = 3.3.0
TEST_ARGS ?= ""
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`
@@ -22,10 +22,7 @@ test-sanity:
ansible-test sanity --docker -v --color --python $(PYTHON_VERSION) $(?TEST_ARGS)
test-integration:
ansible-test integration --docker -v --color --retry-on-error --python $(PYTHON_VERSION) --continue-on-error --diff --coverage $(?TEST_ARGS)
test-molecule:
molecule test
ansible-test integration --diff --no-temp-workdir --color --skip-tags False --retry-on-error --continue-on-error --python $(PYTHON_VERSION) -v --coverage $(?TEST_ARGS)
test-unit:
ansible-test units --docker -v --color --python $(PYTHON_VERSION) $(?TEST_ARGS)

48
PSF-license.txt Normal file
View 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.

View File

@@ -4,57 +4,68 @@
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.
## Requirements
<!--start requires_ansible-->
## Ansible version compatibility
### Ansible version compatibility
This collection has been tested against following Ansible versions: **>=2.9.17**.
This collection has been tested against following Ansible versions: **>=2.14.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
### Python Support
* Collection supports 3.6+
* Collection supports 3.9+
Note: Python2 is deprecated from [1st January 2020](https://www.python.org/doc/sunset-python-2/). Please switch to Python3.
## Included content
### Kubernetes Version Support
This collection supports Kubernetes versions >= 1.24.
### Included content
Click on the name of a plugin or module to view that content's documentation:
<!--start collection content-->
### Connection plugins
#### Connection plugins
Name | Description
--- | ---
[kubernetes.core.kubectl](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.kubectl_connection.rst)|Execute tasks in pods running on Kubernetes.
### K8s filter plugins
#### K8s filter plugins
Name | Description
--- | ---
kubernetes.core.k8s_config_resource_name|Generate resource name for the given resource of type ConfigMap, Secret
### Inventory plugins
#### Inventory plugins
Name | Description
--- | ---
[kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_inventory.rst)|Kubernetes (K8s) inventory source
### Lookup plugins
#### Lookup plugins
Name | Description
--- | ---
[kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_lookup.rst)|Query the K8s API
[kubernetes.core.kustomize](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.kustomize_lookup.rst)|Build a set of kubernetes resources using a 'kustomization.yaml' file.
### Modules
#### Modules
Name | Description
--- | ---
[kubernetes.core.helm](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_module.rst)|Manages Kubernetes packages with the Helm package manager
[kubernetes.core.helm_info](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_info_module.rst)|Get information from Helm package deployed inside the cluster
[kubernetes.core.helm_plugin](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_plugin_module.rst)|Manage Helm plugins
[kubernetes.core.helm_plugin_info](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_plugin_info_module.rst)|Gather information about Helm plugins
[kubernetes.core.helm_pull](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_pull_module.rst)|download a chart from a repository and (optionally) unpack it in local directory.
[kubernetes.core.helm_repository](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_repository_module.rst)|Manage Helm repositories.
[kubernetes.core.helm_template](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.helm_template_module.rst)|Render chart templates
[kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_module.rst)|Manage Kubernetes (K8s) objects
@@ -68,12 +79,11 @@ Name | Description
[kubernetes.core.k8s_rollback](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_rollback_module.rst)|Rollback Kubernetes (K8S) Deployments and DaemonSets
[kubernetes.core.k8s_scale](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_scale_module.rst)|Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job.
[kubernetes.core.k8s_service](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_service_module.rst)|Manage Services on Kubernetes
[kubernetes.core.k8s_taint](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_taint_module.rst)|Taint a node in a Kubernetes/OpenShift cluster
<!--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:
@@ -85,7 +95,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
---
collections:
- name: kubernetes.core
version: 2.2.0
version: 3.3.0
```
### Installing the Kubernetes Python Library
@@ -94,7 +104,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`:
@@ -165,7 +175,7 @@ For documentation on how to use individual modules and other content included in
## Ansible Turbo mode Tech Preview
The ``kubernetes.core`` collection supports Ansible Turbo mode as a tech preview via the ``cloud.common`` collection. By default, this feature is disabled. To enable Turbo mode, set the environment variable `ENABLE_TURBO_MODE=1` on the managed node. For example:
The ``kubernetes.core`` collection supports Ansible Turbo mode as a tech preview via the ``cloud.common`` collection. By default, this feature is disabled. To enable Turbo mode for modules, set the environment variable `ENABLE_TURBO_MODE=1` on the managed node. For example:
```yaml
---
@@ -176,14 +186,21 @@ The ``kubernetes.core`` collection supports Ansible Turbo mode as a tech preview
...
```
To enable Turbo mode for k8s lookup plugin, set the environment variable `ENABLE_TURBO_MODE=1` on the managed node. This is not working when
defined in the playbook using `environment` keyword as above, you must set it using `export ENABLE_TURBO_MODE=1`.
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
[![Linters](https://img.shields.io/github/actions/workflow/status/ansible-collections/kubernetes.core/linters.yaml?label=linters)](https://github.com/ansible-collections/kubernetes.core/actions/workflows/linters.yaml) [![Integration tests](https://img.shields.io/github/actions/workflow/status/ansible-collections/kubernetes.core/integration-tests.yaml?label=integration%20tests)](https://github.com/ansible-collections/kubernetes.core/actions/workflows/integration-tests.yaml) [![Sanity tests](https://img.shields.io/github/actions/workflow/status/ansible-collections/kubernetes.core/sanity-tests.yaml?label=sanity%20tests)](https://github.com/ansible-collections/kubernetes.core/actions/workflows/sanity-tests.yaml) [![Unit tests](https://img.shields.io/github/actions/workflow/status/ansible-collections/kubernetes.core/unit-tests.yaml?label=unit%20tests)](https://github.com/ansible-collections/kubernetes.core/actions/workflows/unit-tests.yaml) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/kubernetes.core)](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).
@@ -220,10 +237,36 @@ 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.
## Support
<!--List available communication channels. In addition to channels specific to your collection, we also recommend to use the following ones.-->
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).
## More Information
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.
## 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

View File

@@ -1 +1,3 @@
kubernetes-client [platform:fedora]
openshift-clients [platform:rhel-8]
openshift-clients [platform:rhel-9]

View File

@@ -486,3 +486,416 @@ releases:
name: kustomize
namespace: null
release_date: '2021-09-15'
2.3.0:
changes:
bugfixes:
- 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).
- common - Ensure the label_selectors parameter of _wait_for method is optional.
- 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_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_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.
- 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).
minor_changes:
- add support for dry run with kubernetes client version >=18.20 (https://github.com/ansible-collections/kubernetes.core/pull/245).
- fixed module_defaults by removing routing hacks from runtime.yml (https://github.com/ansible-collections/kubernetes.core/pull/347).
- 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_info - add release state as a module argument (https://github.com/ansible-collections/kubernetes.core/issues/377).
- 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 - 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_template - add show_only and release_namespace as module arguments (https://github.com/ansible-collections/kubernetes.core/issues/313).
- 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_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_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).
fragments:
- 0-copy_ignore_txt.yml
- 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
- 308-fix-for-common-non-ascii-characters-in-resources.yaml
- 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
- _wait_for_label_selector_optional.yaml
- disutils.version.yml
- exception.yml
- helm_repository.yml
modules:
- description: Taint a node in a Kubernetes/OpenShift cluster
name: k8s_taint
namespace: ''
release_date: '2022-03-11'
2.3.1:
changes:
bugfixes:
- 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).
- k8s_drain - fix error occurring when trying to drain node with disable_eviction
set to yes (https://github.com/ansible-collections/kubernetes.core/issues/416).
fragments:
- 408-fix-wait-on-exception.yml
- 417-fix-k8s-drain-delete-options.yaml
- 422-k8s_cp-fix-issue-when-issue-local_path.yaml
- 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 6.0.0
(https://github.com/ansible-collections/kubernetes.core/pull/734).
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
release_summary: This release comes with documentation updates.
fragments:
- 20240530-defer-removal-and-ansible-core-support-update.yaml
- 20240601-doc-example-of-using-kubectl.yaml
- inventory-update_removal_date.yml
- 3.2.0.yml
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-22'

View File

@@ -54,20 +54,20 @@ In this use case / example, we will create a Pod in the given Kubernetes Cluster
- kubernetes.core
tasks:
- name: Create a pod
k8s:
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Pod
metadata:
apiVersion: v1
kind: Pod
metadata:
name: "utilitypod-1"
namespace: default
labels:
app: galaxy
spec:
app: galaxy
spec:
containers:
- name: utilitypod
image: busybox
- name: utilitypod
image: busybox
Since Ansible utilizes the Kubernetes API to perform actions, in this use case we will be connecting directly to the Kubernetes cluster.

View File

@@ -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>&nbsp;&larr;</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>
@@ -172,6 +194,32 @@ Parameters
<div style="font-size: small; color: darkgreen"><br/>aliases: namespace</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>release_state</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
<b>Default:</b><br/><div style="color: blue">[]</div>
</td>
<td>
<div>Show releases as per their states.</div>
<div>Default value is <code>deployed</code> and <code>failed</code>.</div>
<div>If set to <code>all</code>, show all releases without any filter applied.</div>
<div>If set to <code>deployed</code>, show deployed releases.</div>
<div>If set to <code>failed</code>, show failed releases.</div>
<div>If set to <code>pending</code>, show pending releases.</div>
<div>If set to <code>superseded</code>, show superseded releases.</div>
<div>If set to <code>uninstalled</code>, show uninstalled releases, if <code>helm uninstall --keep-history</code> was used.</div>
<div>If set to <code>uninstalling</code>, show releases that are currently being uninstalled.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -204,11 +252,18 @@ Examples
.. code-block:: yaml
- name: Deploy latest version of Grafana chart inside monitoring namespace
- name: Gather information of Grafana chart inside monitoring namespace
kubernetes.core.helm_info:
name: test
release_namespace: monitoring
- name: Gather information about test-chart with pending state
kubernetes.core.helm_info:
name: test-chart
release_namespace: testenv
release_state:
- pending
Return Values
@@ -270,6 +325,42 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</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">&nbsp;</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">&nbsp;</td>
<td colspan="1">
@@ -302,6 +393,23 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</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">&nbsp;</td>
<td colspan="1">

View File

@@ -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>&nbsp;&larr;</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,12 @@ Parameters
</td>
</tr>
<tr>
<td colspan="1">
<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 +303,28 @@ 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>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 +343,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 +360,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 +377,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 +397,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 +414,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 +436,108 @@ Parameters
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>reset_values</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 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>&nbsp;&larr;</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&#x27;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>&nbsp;&larr;</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&gt;=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 +557,25 @@ Parameters
</td>
</tr>
<tr>
<td colspan="1">
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>timeout</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>
</td>
<td>
</td>
<td>
<div>A Go duration (described here <em>https://pkg.go.dev/time#ParseDuration</em>) value to wait for Kubernetes commands to complete. This defaults to 5m0s.</div>
<div>similar to <code>wait_timeout</code> but does not required <code>wait</code> to be activated.</div>
<div>Mutually exclusive with <code>wait_timeout</code>.</div>
</td>
</tr>
<tr>
<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>
@@ -429,11 +590,11 @@ Parameters
</ul>
</td>
<td>
<div>Run <code>helm repo update</code> before the operation. Can be run as part of the package installation or as a separate step.</div>
<div>Run <code>helm repo update</code> before the operation. Can be run as part of the package installation or as a separate step (see Examples).</div>
</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>
@@ -454,7 +615,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>
@@ -475,7 +636,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>
@@ -490,11 +651,12 @@ Parameters
</ul>
</td>
<td>
<div>Wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful.</div>
<div>When <em>release_state</em> is set to <code>present</code>, wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful.</div>
<div>When <em>release_state</em> is set to <code>absent</code>, will wait until all the resources are deleted before returning. It will wait for as long as <em>wait_timeout</em>. This feature requires helm&gt;=3.7.0. Added in version 2.3.0.</div>
</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>
@@ -506,12 +668,19 @@ Parameters
</td>
<td>
<div>Timeout when wait option is enabled (helm2 is a number of seconds, helm3 is a duration).</div>
<div>The use of <em>wait_timeout</em> to wait for kubernetes commands to complete has been deprecated and will be removed after 2022-12-01.</div>
</td>
</tr>
</table>
<br/>
Notes
-----
.. note::
- The default idempotency check can fail to report changes when ``release_state`` is set to ``present`` and ``chart_repo_url`` is defined. Install helm diff >= 3.4.1 for better results.
Examples
@@ -561,6 +730,22 @@ Examples
state: absent
wait: true
- name: Separately update the repository cache
kubernetes.core.helm:
name: dummy
namespace: kube-system
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:
@@ -606,6 +791,17 @@ 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

View File

@@ -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>

View File

@@ -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>
@@ -150,7 +151,7 @@ Parameters
</td>
<td>
<div>Name of Helm plugin.</div>
<div>Required only if <code>state=absent</code>.</div>
<div>Required only if <code>state=absent</code> or <code>state=latest</code>.</div>
</td>
</tr>
<tr>
@@ -170,6 +171,23 @@ Parameters
<div>Required only if <code>state=present</code>.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>plugin_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>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Plugin version to install. If this is not specified, the latest version is installed.</div>
<div>Ignored when <code>state=absent</code> or <code>state=latest</code>.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -183,10 +201,12 @@ Parameters
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>absent</li>
<li><div style="color: blue"><b>present</b>&nbsp;&larr;</div></li>
<li>latest</li>
</ul>
</td>
<td>
<div>If <code>state=present</code> the Helm plugin will be installed.</div>
<div>If <code>state=latest</code> the Helm plugin will be updated. Added in version 2.3.0.</div>
<div>If <code>state=absent</code> the Helm plugin will be removed.</div>
</td>
</tr>
@@ -237,6 +257,17 @@ Examples
plugin_name: env
state: absent
- name: Install Helm plugin with a specific version
kubernetes.core.helm_plugin:
plugin_version: 2.0.1
plugin_path: https://domain/path/to/plugin.tar.gz
state: present
- name: Update Helm plugin
kubernetes.core.helm_plugin:
plugin_name: secrets
state: latest
Return Values

View File

@@ -0,0 +1,467 @@
.. _kubernetes.core.helm_pull_module:
*************************
kubernetes.core.helm_pull
*************************
**download a chart from a repository and (optionally) unpack it in local directory.**
Version added: 2.4.0
.. contents::
:local:
:depth: 1
Synopsis
--------
- Retrieve a package from a package repository, and download it locally.
- It can also be used to perform cryptographic verification of a chart without installing the chart.
- There are options for unpacking the chart after download.
Requirements
------------
The below requirements are needed on the host that executes this module.
- helm >= 3.0 (https://github.com/helm/helm/releases)
Parameters
----------
.. raw:: html
<table border=0 cellpadding=0 class="documentation-table">
<tr>
<th colspan="1">Parameter</th>
<th>Choices/<font color="blue">Defaults</font></th>
<th width="100%">Comments</th>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>binary_path</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">path</span>
</div>
</td>
<td>
</td>
<td>
<div>The path of a helm binary to use.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>chart_ca_cert</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">path</span>
</div>
</td>
<td>
</td>
<td>
<div>Verify certificates of HTTPS-enabled servers using this CA bundle.</div>
<div>Requires helm &gt;= 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 &#x27;&gt;0.0.0-0&#x27;.</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 &gt;= 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 &gt;= 3.1.0.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>chart_version</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>
</td>
<td>
<div>Specify a version constraint for the chart version to use.</div>
<div>This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0).</div>
<div>Mutually exclusive with <code>chart_devel</code>.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>destination</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">path</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
<div>location to write the chart.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>pass_credentials</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Pass credentials to all domains.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>provenance</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Fetch the provenance file, but don&#x27;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>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Whether or not to check tls certificate for the chart download.</div>
<div>Requires helm &gt;= 3.3.0.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>untar_chart</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">boolean</span>
</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</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>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Verify the package before using it.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>verify_chart_keyring</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">path</span>
</div>
</td>
<td>
</td>
<td>
<div>location of public keys used for verification.</div>
</td>
</tr>
</table>
<br/>
Examples
--------
.. code-block:: yaml
- name: Download chart using chart url
kubernetes.core.helm_pull:
chart_ref: https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz
destination: /path/to/chart
- name: Download Chart using chart_name and repo_url
kubernetes.core.helm_pull:
chart_ref: redis
repo_url: https://charts.bitnami.com/bitnami
untar_chart: yes
destination: /path/to/chart
- name: Download Chart (skip tls certificate check)
kubernetes.core.helm_pull:
chart_ref: redis
repo_url: https://charts.bitnami.com/bitnami
untar_chart: yes
destination: /path/to/chart
skip_tls_certs_check: yes
- name: Download Chart using chart registry credentials
kubernetes.core.helm_pull:
chart_ref: redis
repo_url: https://charts.bitnami.com/bitnami
untar_chart: yes
destination: /path/to/chart
username: myuser
password: mypassword123
Return Values
-------------
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
.. raw:: html
<table border=0 cellpadding=0 class="documentation-table">
<tr>
<th colspan="1">Key</th>
<th>Returned</th>
<th width="100%">Description</th>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>command</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>always</td>
<td>
<div>Full `helm pull` command built by this module, in case you want to re-run the command outside the module or debug a problem.</div>
<br/>
<div style="font-size: smaller"><b>Sample:</b></div>
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">helm pull --repo test ...</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>rc</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">integer</span>
</div>
</td>
<td>always</td>
<td>
<div>Helm pull command return code</div>
<br/>
<div style="font-size: smaller"><b>Sample:</b></div>
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">1</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>stderr</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>always</td>
<td>
<div>Full `helm pull` command stderr, in case you want to display it or examine the event log</div>
<br/>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>stdout</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>always</td>
<td>
<div>Full `helm pull` command stdout, in case you want to display it or examine the event log</div>
<br/>
</td>
</tr>
</table>
<br/><br/>
Status
------
Authors
~~~~~~~
- Aubin Bikouo (@abikouo)

View File

@@ -40,6 +40,22 @@ Parameters
<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>api_key</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>
</td>
<td>
</td>
<td>
<div>Token used to authenticate with the API. Can also be specified via <code>K8S_AUTH_API_KEY</code> environment variable.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -55,6 +71,117 @@ Parameters
<div>The path of a helm binary to use.</div>
</td>
</tr>
<tr>
<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">path</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>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 <code>K8S_AUTH_SSL_CA_CERT</code> environment variable.</div>
<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>&nbsp;&larr;</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>
<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>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Provide a URL for accessing the API. Can also be specified via <code>K8S_AUTH_HOST</code> environment variable.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>kubeconfig</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">raw</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.4.0</div>
</td>
<td>
</td>
<td>
<div>Helm option to specify kubeconfig path to use.</div>
<div>If the value is not specified in the task, the value of environment variable <code>K8S_AUTH_KUBECONFIG</code> will be used instead.</div>
<div>The configuration can be provided as dictionary.</div>
<div style="font-size: small; color: darkgreen"><br/>aliases: kubeconfig_path</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<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>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Pass credentials to all domains.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -142,6 +269,27 @@ Parameters
<div style="font-size: small; color: darkgreen"><br/>aliases: username</div>
</td>
</tr>
<tr>
<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>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>no</li>
<li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
</ul>
</td>
<td>
<div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via <code>K8S_AUTH_VERIFY_SSL</code> environment variable.</div>
<div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
</td>
</tr>
</table>
<br/>

View File

@@ -28,12 +28,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 +48,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 +67,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 +82,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 +97,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>&nbsp;&larr;</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>&nbsp;&larr;</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 +159,7 @@ Parameters
</td>
</tr>
<tr>
<td colspan="1">
<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,7 +175,40 @@ 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>
<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.4.0</div>
</td>
<td>
</td>
<td>
<div>namespace scope for this request.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>release_values</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
@@ -149,7 +225,85 @@ 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>&nbsp;&larr;</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&gt;=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>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.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="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>update_repo_cache</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
@@ -168,7 +322,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>
@@ -213,6 +367,24 @@ Examples
dest: myfile.yaml
content: "{{ result.stdout }}"
- name: Render MutatingWebhooksConfiguration for revision tag "canary", rev "1-13-0"
kubernetes.core.helm_template:
chart_ref: istio/istiod
chart_version: "1.13.0"
release_namespace: "istio-system"
show_only:
- "templates/revision-tags.yaml"
release_values:
revision: "1-13-0"
revisionTags:
- "canary"
register: result
- name: Write templates to file
copy:
dest: myfile.yaml
content: "{{ result.stdout }}"
Return Values

View File

@@ -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
@@ -136,6 +136,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -168,9 +203,29 @@ 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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>

View File

@@ -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
@@ -167,6 +167,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -180,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -236,6 +272,25 @@ Parameters
<div>This option is ignored when <em>content</em> is set or when <em>state</em> is set to <code>from_pod</code>.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -503,7 +558,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

View File

@@ -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>
@@ -140,6 +141,27 @@ Parameters
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>delete_emptydir_data</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.3.0</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>Continue even if there are pods using emptyDir (local data that will be deleted when the node is drained).</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>disable_eviction</b>
@@ -266,6 +288,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -279,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -298,6 +356,25 @@ Parameters
<div>The name of the node.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -337,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>
@@ -506,7 +602,7 @@ Examples
state: drain
name: foo
delete_options:
terminate_grace_period: 900
terminate_grace_period: 900
- name: Mark node "foo" as schedulable.
kubernetes.core.k8s_drain:
@@ -518,6 +614,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

View File

@@ -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
@@ -117,7 +117,7 @@ Parameters
<td>
</td>
<td>
<div>The command to execute</div>
<div>The command to execute.</div>
</td>
</tr>
<tr>
@@ -134,6 +134,7 @@ Parameters
<td>
<div>The name of the container in the pod to connect to.</div>
<div>Defaults to only container if there is only one container in the pod.</div>
<div>If not specified, will choose the first container from the given pod as kubectl cmdline does.</div>
</td>
</tr>
<tr>
@@ -166,6 +167,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -179,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -195,7 +232,26 @@ Parameters
<td>
</td>
<td>
<div>The pod namespace name</div>
<div>The pod namespace name.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
@@ -250,7 +306,7 @@ Parameters
<td>
</td>
<td>
<div>The pod name</div>
<div>The pod name.</div>
</td>
</tr>
<tr>
@@ -267,7 +323,7 @@ Parameters
<td>
<div>The URL of an HTTP proxy to use for the connection.</div>
<div>Can also be specified via <em>K8S_AUTH_PROXY</em> environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (for example, HTTP_PROXY).</div>
</td>
</tr>
<tr>
@@ -414,6 +470,13 @@ Examples
msg: "cmd failed"
when: command_status.rc != 0
- name: Specify a container name to execute the command on
kubernetes.core.k8s_exec:
namespace: myproject
pod: busybox-test
container: manager
command: echo "hello"
Return Values

View File

@@ -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,31 @@ 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></div>
<div>Only field definitions that don&#x27;t reference list items are supported (so V(spec.containers[0]) would not work)</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -173,6 +193,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -205,6 +260,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -219,6 +275,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>
@@ -260,6 +317,25 @@ Parameters
<div>If <em>resource definition</em> is provided, the <em>metadata.namespace</em> value from the <em>resource_definition</em> will override this option.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>

View File

@@ -13,6 +13,15 @@ kubernetes.core.k8s
:local:
:depth: 1
DEPRECATED
----------
:Removed in collection release after
:Why: As discussed in https://github.com/ansible-collections/kubernetes.core/issues/31, we decided to
remove the k8s inventory plugin in release 6.0.0.
:Alternative: Use :ref:`kubernetes.core.k8s_info <kubernetes.core.k8s_info_module>` and :ref:`ansible.builtin.add_host <ansible.builtin.add_host_module>` instead.
Synopsis
--------
@@ -27,8 +36,8 @@ Requirements
------------
The below requirements are needed on the local Ansible controller node that executes this inventory.
- python >= 3.6
- kubernetes >= 12.0.0
- python >= 3.9
- kubernetes >= 24.2.0
- PyYAML >= 3.11
@@ -322,24 +331,24 @@ 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
- name: Authenticate with token, and return all pods and services for all namespaces
plugin: kubernetes.core.k8s
connections:
- host: https://192.168.64.4:8443
api_key: xxxxxxxxxxxxxxxx
validate_certs: false
# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
plugin: kubernetes.core.k8s
connections:
- namespaces:
- testing
- name: Use 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'
- name: Use a custom config file, and a specific context.
plugin: kubernetes.core.k8s
connections:
- kubeconfig: /path/to/config
context: 'awx/192-168-64-4:8443/developer'
@@ -348,11 +357,15 @@ Status
------
- This inventory will be removed in version 6.0.0. *[deprecated]*
- For more information see `DEPRECATED`_.
Authors
~~~~~~~
- Chris Houseknecht <@chouseknecht>
- Fabian von Feilitzsch <@fabianvf>
- Chris Houseknecht (@chouseknecht)
- Fabian von Feilitzsch (@fabianvf)
.. hint::

View File

@@ -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
@@ -155,6 +155,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -185,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -221,6 +257,25 @@ Parameters
<div>Use in conjunction with <em>api_version</em>, <em>kind</em>, and <em>name</em> to identify a specific object.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -559,7 +614,7 @@ Examples
path: /metadata/labels/app
value: myapp
- op: replace
patch: /spec/containers/0/image
path: /spec/containers/0/image
value: nginx

View File

@@ -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&#x27; 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>
@@ -174,6 +196,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -205,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -219,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>
@@ -260,6 +319,25 @@ Parameters
<div>If <em>resource definition</em> is provided, the <em>metadata.namespace</em> value from the <em>resource_definition</em> will override this option.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -299,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>&nbsp;&larr;</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>
@@ -400,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>
@@ -484,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

View File

@@ -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
@@ -438,106 +438,28 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
<table border=0 cellpadding=0 class="documentation-table">
<tr>
<th colspan="2">Key</th>
<th colspan="1">Key</th>
<th>Returned</th>
<th width="100%">Description</th>
</tr>
<tr>
<td colspan="2">
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>_list</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
<span style="color: purple">list</span>
/ <span style="color: purple">elements=dictionary</span>
</div>
</td>
<td></td>
<td>
<div>One ore more object definitions returned from the API.</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;">[{&#x27;kind&#x27;: &#x27;ConfigMap&#x27;, &#x27;apiVersion&#x27;: &#x27;v1&#x27;, &#x27;metadata&#x27;: {&#x27;creationTimestamp&#x27;: &#x27;2022-03-04T13:59:49Z&#x27;, &#x27;name&#x27;: &#x27;my-config-map&#x27;, &#x27;namespace&#x27;: &#x27;default&#x27;, &#x27;resourceVersion&#x27;: &#x27;418&#x27;, &#x27;uid&#x27;: &#x27;5714b011-d090-4eac-8272-a0ea82ec0abd&#x27;}, &#x27;data&#x27;: {&#x27;key1&#x27;: &#x27;val1&#x27;}}]</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>api_version</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>success</td>
<td>
<div>The versioned schema of this representation of an object.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>kind</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>success</td>
<td>
<div>Represents the REST resource this object represents.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>metadata</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>spec</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>status</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Current status details for the object.</div>
<br/>
</td>
</tr>
</table>
<br/><br/>
@@ -549,8 +471,8 @@ Status
Authors
~~~~~~~
- Chris Houseknecht <@chouseknecht>
- Fabian von Feilitzsch <@fabianvf>
- Chris Houseknecht (@chouseknecht)
- Fabian von Feilitzsch (@fabianvf)
.. hint::

View File

@@ -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
@@ -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>&nbsp;&larr;</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>
@@ -336,6 +360,45 @@ Parameters
<div>If set to <code>yes</code>, and <em>state</em> is <code>present</code>, an existing object will be replaced.</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>generate_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.3.0</div>
</td>
<td>
</td>
<td>
<div>Use to specify the basis of an object name and random characters will be added automatically on server to generate a unique name.</div>
<div>This option is ignored when <em>state</em> is not set to <code>present</code> or when <em>apply</em> is set to <code>yes</code>.</div>
<div>If <em>resource definition</em> is provided, the <em>metadata.generateName</em> value from the <em>resource_definition</em> will override this option.</div>
<div>If <em>resource definition</em> is provided, and contains <em>metadata.name</em>, this option is ignored.</div>
<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></div>
<div>Only field definitions that don&#x27;t reference list items are supported (so V(spec.containers[0]) would not work)</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -351,6 +414,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -382,6 +480,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -425,7 +524,7 @@ Parameters
<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>[&#x27;strategic-merge&#x27;, &#x27;merge&#x27;]</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><em>merge_type=json</em> is deprecated and will be removed in version 4.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div>
</td>
</tr>
<tr>
@@ -464,6 +563,25 @@ Parameters
<div>If <em>resource definition</em> is provided, the <em>metadata.namespace</em> value from the <em>resource_definition</em> will override this option.</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -605,6 +723,63 @@ Parameters
<div style="font-size: small; color: darkgreen"><br/>aliases: definition, inline</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>server_side_apply</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 2.3.0</div>
</td>
<td>
</td>
<td>
<div>When this option is set, apply runs in the server instead of the client.</div>
<div>Ignored if <code>apply</code> is not set or is set to False.</div>
<div>This option requires &quot;kubernetes &gt;= 19.15.0&quot;.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>field_manager</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>Name of the manager used to track field ownership.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>force_conflicts</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>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>A conflict is a special status error that occurs when an Server Side Apply operation tries to change a field, which another user also claims to manage.</div>
<div>When set to True, server-side apply will force the changes against conflicts.</div>
</td>
</tr>
<tr>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -619,7 +794,8 @@ 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&#x27;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>Mutually exclusive with <em>template</em> in case of <span class='module'>k8s</span> module.</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>
<tr>
@@ -981,6 +1157,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
@@ -998,10 +1182,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:
@@ -1058,6 +1242,41 @@ Examples
labels:
support: patch
# Create object using generateName
- name: create resource using name generated by the server
kubernetes.core.k8s:
state: present
generate_name: pod-
definition:
apiVersion: v1
kind: Pod
spec:
containers:
- name: py
image: python:3.7-alpine
imagePullPolicy: IfNotPresent
# Server side apply
- name: Create configmap using server side apply
kubernetes.core.k8s:
namespace: testing
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap
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 Values

View File

@@ -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>
@@ -172,6 +173,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -203,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -217,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>
@@ -258,6 +296,25 @@ Parameters
<div>If <em>resource definition</em> is provided, the <em>metadata.namespace</em> value from the <em>resource_definition</em> will override this option.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>

View File

@@ -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
@@ -189,6 +189,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -220,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -235,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>
@@ -276,6 +313,25 @@ Parameters
<div>If <em>resource definition</em> is provided, the <em>metadata.namespace</em> value from the <em>resource_definition</em> will override this option.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -462,7 +518,8 @@ 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&#x27;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>Mutually exclusive with <em>template</em> in case of <span class='module'>k8s</span> module.</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>
<tr>

View File

@@ -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
@@ -172,6 +172,41 @@ Parameters
<div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -185,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 &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
@@ -245,6 +281,25 @@ Parameters
<div>Use to specify a Service object namespace.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
@@ -433,7 +488,8 @@ 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&#x27;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>Mutually exclusive with <em>template</em> in case of <span class='module'>k8s</span> module.</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>
<tr>

View File

@@ -0,0 +1,661 @@
.. _kubernetes.core.k8s_taint_module:
*************************
kubernetes.core.k8s_taint
*************************
**Taint a node in a Kubernetes/OpenShift cluster**
Version added: 2.3.0
.. contents::
:local:
:depth: 1
Synopsis
--------
- Taint allows a node to refuse Pod to be scheduled unless that Pod has a matching toleration.
- Untaint will remove taints from nodes as needed.
Requirements
------------
The below requirements are needed on the host that executes this module.
- python >= 3.9
- kubernetes >= 24.2.0
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 width="100%">Comments</th>
</tr>
<tr>
<td colspan="2">
<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">string</span>
</div>
</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 colspan="2">
<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">path</span>
</div>
</td>
<td>
</td>
<td>
<div>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 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 colspan="2">
<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">path</span>
</div>
</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 colspan="2">
<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">path</span>
</div>
</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 colspan="2">
<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>
</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 colspan="2">
<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>
</div>
</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 colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_groups</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
</div>
<div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
</td>
<td>
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>impersonate_user</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>
</td>
<td>
</td>
<td>
<div>Username to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</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">raw</span>
</div>
</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>
<div>Multiple Kubernetes config file can be provided using separator &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
<div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
</td>
</tr>
<tr>
<td colspan="2">
<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">string</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
<div>The name of the node.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>no_proxy</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>
</td>
<td>
</td>
<td>
<div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
<div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
<div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
</td>
</tr>
<tr>
<td colspan="2">
<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>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
<div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>persist_config</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>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
<div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
<div>Default to false.</div>
<div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
<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>proxy</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>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
<div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>proxy_headers</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 2.0.0</div>
</td>
<td>
</td>
<td>
<div>The Header used for the HTTP proxy.</div>
<div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>basic_auth</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>Colon-separated username:password for basic authentication header.</div>
<div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>proxy_basic_auth</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>Colon-separated username:password for proxy basic authentication header.</div>
<div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>user_agent</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>String representing the user-agent you want, such as foo/1.0.</div>
<div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>replace</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>&nbsp;&larr;</div></li>
<li>yes</li>
</ul>
</td>
<td>
<div>If <code>true</code>, allow taints to be replaced.</div>
</td>
</tr>
<tr>
<td colspan="2">
<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>&nbsp;&larr;</div></li>
<li>absent</li>
</ul>
</td>
<td>
<div>Determines whether to add or remove taints.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>taints</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>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
<div>List containing the taints.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>effect</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>NoSchedule</li>
<li>NoExecute</li>
<li>PreferNoSchedule</li>
</ul>
</td>
<td>
<div>The effect of the taint on Pods that do not tolerate the taint.</div>
<div>Required when <em>state=present</em>.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>key</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>The taint key to be applied to a node.</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>
</div>
</td>
<td>
</td>
<td>
<div>The taint value corresponding to the taint key.</div>
</td>
</tr>
<tr>
<td colspan="2">
<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>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
<div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
</td>
</tr>
<tr>
<td colspan="2">
<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>
<div>Whether or not to verify the API server&#x27;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>
</table>
<br/>
Notes
-----
.. note::
- 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.
Examples
--------
.. code-block:: yaml
- name: Taint node "foo"
kubernetes.core.k8s_taint:
state: present
name: foo
taints:
- effect: NoExecute
key: "key1"
- name: Taint node "foo"
kubernetes.core.k8s_taint:
state: present
name: foo
taints:
- effect: NoExecute
key: "key1"
value: "value1"
- effect: NoSchedule
key: "key1"
value: "value1"
- name: Remove taint from "foo".
kubernetes.core.k8s_taint:
state: absent
name: foo
taints:
- effect: NoExecute
key: "key1"
value: "value1"
Return Values
-------------
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
.. raw:: html
<table border=0 cellpadding=0 class="documentation-table">
<tr>
<th colspan="2">Key</th>
<th>Returned</th>
<th width="100%">Description</th>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>result</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>The tainted Node object. Will be empty in the case of a deletion.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>api_version</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>success</td>
<td>
<div>The versioned schema of this representation of an object.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>kind</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>success</td>
<td>
<div>Represents the REST resource this object represents.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>metadata</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>spec</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>status</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Current status details for the object.</div>
<br/>
</td>
</tr>
</table>
<br/><br/>
Status
------
Authors
~~~~~~~
- Alina Buzachis (@alinabuzachis)

View File

@@ -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 }}"
@@ -354,7 +452,7 @@ Status
Authors
~~~~~~~
- xuxinkun
- xuxinkun (@xuxinkun)
.. hint::

View File

@@ -77,6 +77,24 @@ Parameters
<div>If omitted, &#x27;.&#x27; 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>
@@ -112,17 +130,21 @@ 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) }}"
Return Values
@@ -133,106 +155,27 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
<table border=0 cellpadding=0 class="documentation-table">
<tr>
<th colspan="2">Key</th>
<th colspan="1">Key</th>
<th>Returned</th>
<th width="100%">Description</th>
</tr>
<tr>
<td colspan="2">
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>_list</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
<span style="color: purple">string</span>
</div>
</td>
<td></td>
<td>
<div>One ore more object definitions returned from the tool execution.</div>
<div>YAML string for the object definitions returned from the tool execution.</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;">{&#x27;kind&#x27;: &#x27;ConfigMap&#x27;, &#x27;apiVersion&#x27;: &#x27;v1&#x27;, &#x27;metadata&#x27;: {&#x27;name&#x27;: &#x27;my-config-map&#x27;, &#x27;namespace&#x27;: &#x27;default&#x27;}, &#x27;data&#x27;: {&#x27;key1&#x27;: &#x27;val1&#x27;}}</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>api_version</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>success</td>
<td>
<div>The versioned schema of this representation of an object.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>kind</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>success</td>
<td>
<div>Represents the REST resource this object represents.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>metadata</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>spec</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
<br/>
</td>
</tr>
<tr>
<td class="elbow-placeholder">&nbsp;</td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="return-"></div>
<b>status</b>
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
<div style="font-size: small">
<span style="color: purple">complex</span>
</div>
</td>
<td>success</td>
<td>
<div>Current status details for the object.</div>
<br/>
</td>
</tr>
</table>
<br/><br/>
@@ -244,7 +187,7 @@ Status
Authors
~~~~~~~
- Aubin Bikouo <@abikouo>
- Aubin Bikouo (@abikouo)
.. hint::

View File

@@ -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.2.0
version: 3.3.0
build_ignore:
- .DS_Store
- '*.tar.gz'
- "*.tar.gz"

View File

@@ -1,5 +1,5 @@
---
requires_ansible: '>=2.9.17'
requires_ansible: '>=2.14.0'
action_groups:
helm:
@@ -9,7 +9,6 @@ action_groups:
k8s:
- k8s
- k8s_exec
- k8s_facts
- k8s_info
- k8s_log
- k8s_scale
@@ -18,40 +17,15 @@ action_groups:
- k8s_drain
plugin_routing:
action:
helm:
redirect: kubernetes.core.k8s_info
helm_info:
redirect: kubernetes.core.k8s_info
helm_plugin:
redirect: kubernetes.core.k8s_info
helm_plugin_info:
redirect: kubernetes.core.k8s_info
helm_repository:
redirect: kubernetes.core.k8s_info
k8s:
redirect: kubernetes.core.k8s_info
k8s_cluster_info:
redirect: kubernetes.core.k8s_info
k8s_cp:
redirect: kubernetes.core.k8s_info
k8s_drain:
redirect: kubernetes.core.k8s_info
k8s_event_info:
redirect: kubernetes.core.k8s_info
k8s_exec:
redirect: kubernetes.core.k8s_info
k8s_log:
redirect: kubernetes.core.k8s_info
k8s_rollback:
redirect: kubernetes.core.k8s_info
k8s_scale:
redirect: kubernetes.core.k8s_info
k8s_service:
redirect: kubernetes.core.k8s_info
inventory:
openshift:
redirect: community.okd.openshift
k8s:
deprecation:
removal_version: 6.0.0
warning_text: >-
The k8s inventory plugin has been deprecated and
will be removed in release 6.0.0.
modules:
k8s_auth:
redirect: community.okd.k8s_auth

View File

@@ -1,292 +0,0 @@
---
- name: Converge
hosts: localhost
connection: local
collections:
- kubernetes.core
vars_files:
- vars/main.yml
tasks:
- name: Verify cluster is working.
k8s_info:
namespace: kube-system
kind: Pod
register: pod_list
- name: Verify cluster has more than 5 pods running.
assert:
that: (pod_list.resources | count) > 5
- name: Include access_review.yml
include_tasks:
file: tasks/access_review.yml
apply:
tags: [ access_review, k8s ]
tags:
- always
- name: Include append_hash.yml
include_tasks:
file: tasks/append_hash.yml
apply:
tags: [ append_hash, k8s ]
tags:
- always
- name: Include apply.yml
include_tasks:
file: tasks/apply.yml
apply:
tags: [ apply, k8s ]
tags:
- always
- name: Include cluster_info.yml
include_tasks:
file: tasks/cluster_info.yml
apply:
tags: [ cluster_info, k8s ]
tags:
- always
- name: Include crd.yml
include_tasks:
file: tasks/crd.yml
apply:
tags: [ crd, k8s ]
tags:
- always
- name: Include delete.yml
include_tasks:
file: tasks/delete.yml
apply:
tags: [ delete, k8s ]
tags:
- always
- name: Include exec.yml
include_tasks:
file: tasks/exec.yml
apply:
tags: [ exec, k8s ]
tags:
- always
- name: Include full.yml
include_tasks:
file: tasks/full.yml
apply:
tags: [ full, k8s ]
tags:
- always
- name: Include gc.yml
include_tasks:
file: tasks/gc.yml
apply:
tags: [ gc, k8s ]
tags:
- always
- name: Include info.yml
include_tasks:
file: tasks/info.yml
apply:
tags: [ info, k8s ]
tags:
- always
- name: Include json_patch.yml
include_tasks:
file: tasks/json_patch.yml
apply:
tags: [ json_patch, k8s ]
tags:
- always
- name: Include lists.yml
include_tasks:
file: tasks/lists.yml
apply:
tags: [ lists, k8s ]
tags:
- always
- name: Include log.yml
include_tasks:
file: tasks/log.yml
apply:
tags: [ log, k8s ]
tags:
- always
- name: Include rollback.yml
include_tasks:
file: tasks/rollback.yml
apply:
tags: [ rollback, k8s ]
tags:
- always
- name: Include scale.yml
include_tasks:
file: tasks/scale.yml
apply:
tags: [ scale, k8s ]
tags:
- always
- name: Include template.yml
include_tasks:
file: tasks/template.yml
apply:
tags: [ template, k8s ]
tags:
- always
- name: Include validate.yml
include_tasks:
file: tasks/validate.yml
apply:
tags: [ validate, k8s ]
tags:
- always
- name: Include waiter.yml
include_tasks:
file: tasks/waiter.yml
apply:
tags: [ waiter, k8s ]
tags:
- always
- name: Include merge_type.yml
include_tasks:
file: tasks/merge_type.yml
apply:
tags: [ merge_type, k8s ]
tags:
- always
- name: Include patched.yml
include_tasks:
file: tasks/patched.yml
apply:
tags: [ patched, k8s ]
tags:
- always
- name: Include lookup_k8s.yml
include_tasks:
file: tasks/lookup_k8s.yml
apply:
tags: [ lookup_k8s, k8s ]
tags:
- always
- name: Include label_selectors.yml
include_tasks:
file: tasks/label_selectors.yml
apply:
tags: [ label_selectors, k8s ]
tags:
- always
- name: Include diff.yml
include_tasks:
file: tasks/diff.yml
apply:
tags: [ diff, k8s ]
tags:
- always
- name: Include lookup_kustomize.yml
include_tasks:
file: tasks/lookup_kustomize.yml
apply:
tags: [ lookup_kustomize, k8s ]
tags:
- always
roles:
- role: helm
tags:
- helm
- role: k8scopy
tags:
- copy
- k8s
post_tasks:
- name: Ensure namespace exists
k8s:
api_version: v1
kind: Namespace
name: inventory
- name: Add a deployment
k8s:
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory
namespace: inventory
spec:
replicas: 1
selector:
matchLabels:
app: "{{ k8s_pod_name }}"
template: "{{ k8s_pod_template }}"
wait: yes
wait_timeout: 120
vars:
k8s_pod_name: inventory
k8s_pod_image: python
k8s_pod_command:
- python
- '-m'
- http.server
k8s_pod_env:
- name: TEST
value: test
- meta: refresh_inventory
- name: Verify inventory and connection plugins
hosts: namespace_inventory_pods
gather_facts: no
vars:
file_content: |
Hello world
tasks:
- name: End play if host not running (TODO should we not add these to the inventory?)
meta: end_host
when: pod_phase != "Running"
- debug: var=hostvars
- setup:
- debug: var=ansible_facts
- name: Assert the TEST environment variable was retrieved
assert:
that: ansible_facts.env.TEST == 'test'
- name: Copy a file into the host
copy:
content: '{{ file_content }}'
dest: /tmp/test_file
- name: Retrieve the file from the host
slurp:
src: /tmp/test_file
register: slurped_file
- name: Assert the file content matches expectations
assert:
that: (slurped_file.content|b64decode) == file_content
- name: Delete inventory namespace
hosts: localhost
connection: local
gather_facts: no
tasks:
- name: Remove inventory namespace
k8s:
api_version: v1
kind: Namespace
name: inventory
state: absent

View File

@@ -1,39 +0,0 @@
---
driver:
name: delegated
options:
managed: false
login_cmd_template: 'docker exec -ti {instance} bash'
ansible_connection_options:
ansible_connection: docker
platforms:
- name: instance-kind
provisioner:
name: ansible
log: true
config_options:
inventory:
enable_plugins: kubernetes.core.k8s,yaml
lint: {}
inventory:
hosts:
plugin: kubernetes.core.k8s
host_vars:
localhost:
ansible_python_interpreter: '{{ ansible_playbook_python }}'
env:
ANSIBLE_FORCE_COLOR: 'true'
options:
vvv: True
scenario:
name: default
test_sequence:
- dependency
- syntax
- prepare
- converge
- verify
dependency:
name: galaxy
options:
requirements-file: requirements.yml

View File

@@ -1,12 +0,0 @@
---
- name: Prepare
hosts: localhost
connection: local
collections:
- kubernetes.core
tasks:
- name: Include drain.yml
include_tasks:
file: tasks/drain.yml

View File

@@ -1,19 +0,0 @@
---
helm_archive_name: "helm-{{ helm_version }}-{{ ansible_system | lower }}-amd64.tar.gz"
helm_binary: "/tmp/helm/{{ ansible_system | lower }}-amd64/helm"
helm_namespace: helm
tiller_namespace: tiller
tiller_cluster_role: cluster-admin
chart_test: "ingress-nginx"
chart_test_local_path: "nginx-ingress"
chart_test_version: 3.8.0
chart_test_version_local_path: 1.32.0
chart_test_version_upgrade: 3.9.0
chart_test_version_upgrade_local_path: 1.33.0
chart_test_repo: "https://kubernetes.github.io/ingress-nginx"
chart_test_git_repo: "http://github.com/helm/charts.git"
chart_test_values:
revisionHistoryLimit: 0
myValue: "changed"

View File

@@ -1,11 +0,0 @@
---
- name: Init Helm folders
file:
path: /tmp/helm/
state: directory
- name: Unarchive Helm binary
unarchive:
src: 'https://get.helm.sh/{{ helm_archive_name }}'
dest: /tmp/helm/
remote_src: yes

View File

@@ -1,7 +0,0 @@
---
- name: Install Chart from URL
include_tasks: "../tests_chart.yml"
vars:
source: url
chart_source: "https://github.com/kubernetes/ingress-nginx/releases/download/{{ chart_test }}-{{ chart_test_version }}/{{ chart_test }}-{{ chart_test_version }}.tgz"
chart_source_upgrade: "https://github.com/kubernetes/ingress-nginx/releases/download/{{ chart_test }}-{{ chart_test_version_upgrade }}/{{ chart_test }}-{{ chart_test_version_upgrade }}.tgz"

View File

@@ -1,153 +0,0 @@
---
- name: Test helm diff functionality
vars:
test_chart_ref: "/tmp/test-chart"
block:
- name: Install helm diff
helm_plugin:
binary_path: "{{ helm_binary }}"
state: present
plugin_path: https://github.com/databus23/helm-diff
- name: Copy test chart
copy:
src: "test-chart/"
dest: "{{ test_chart_ref }}"
- name: Install local chart
helm:
binary_path: "{{ helm_binary }}"
name: test-chart
namespace: "{{ helm_namespace }}"
chart_ref: "{{ test_chart_ref }}"
create_namespace: yes
register: install
- assert:
that:
- install is changed
- name: Modify local chart
blockinfile:
create: yes
path: "{{ test_chart_ref }}/templates/anothermap.yaml"
block: !unsafe |
apiVersion: v1
kind: ConfigMap
metadata:
name: test-chart-another-configmap
data:
foo: {{ .Values.foo | default "bar" }}
- name: Upgrade local chart with modifications
helm:
binary_path: "{{ helm_binary }}"
name: test-chart
namespace: "{{ helm_namespace }}"
chart_ref: "{{ test_chart_ref }}"
register: install
- assert:
that:
- install is changed
- name: Upgrade modified local chart idempotency check
helm:
binary_path: "{{ helm_binary }}"
name: test-chart
namespace: "{{ helm_namespace }}"
chart_ref: "{{ test_chart_ref }}"
register: install
- assert:
that:
- install is not changed
- name: Modify values
blockinfile:
create: yes
path: "{{ test_chart_ref }}/values.yml"
block: |
---
foo: baz
- name: Upgrade with values file
helm:
binary_path: "{{ helm_binary }}"
name: test-chart
namespace: "{{ helm_namespace }}"
chart_ref: "{{ test_chart_ref }}"
values_files:
- "{{ test_chart_ref }}/values.yml"
register: install
- assert:
that:
- install is changed
- name: Upgrade with values file idempotency check
helm:
binary_path: "{{ helm_binary }}"
name: test-chart
namespace: "{{ helm_namespace }}"
chart_ref: "{{ test_chart_ref }}"
values_files:
- "{{ test_chart_ref }}/values.yml"
register: install
- assert:
that:
- install is not changed
- name: Upgrade with values
helm:
binary_path: "{{ helm_binary }}"
name: test-chart
namespace: "{{ helm_namespace }}"
chart_ref: "{{ test_chart_ref }}"
values:
foo: gaz
register: install
- assert:
that:
- install is changed
- name: Upgrade with values idempotency check
helm:
binary_path: "{{ helm_binary }}"
name: test-chart
namespace: "{{ helm_namespace }}"
chart_ref: "{{ test_chart_ref }}"
values:
foo: gaz
register: install
- assert:
that:
- install is not changed
always:
- name: Remove chart directory
file:
path: "{{ test_chart_ref }}"
state: absent
ignore_errors: yes
- name: Uninstall helm diff
helm_plugin:
binary_path: "{{ helm_binary }}"
state: absent
plugin_name: diff
ignore_errors: yes
- name: Remove helm namespace
k8s:
api_version: v1
kind: Namespace
name: "{{ helm_namespace }}"
state: absent
wait: yes
wait_timeout: 180
ignore_errors: yes

View File

@@ -1,91 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Aubin Bikouo <@abikouo>
# 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: k8s_diff
short_description: Create large file with a defined size.
author:
- Aubin Bikouo (@abikouo)
description:
- This module is used to validate k8s_cp module.
options:
path:
description:
- The destination path for the file to create.
type: path
required: yes
size:
description:
- The size of the output file in MB.
type: int
default: 400
binary:
description:
- If this flag is set to yes, the generated file content binary data.
type: bool
default: False
'''
EXAMPLES = r'''
- name: create 150MB file
k8s_diff:
path: large_file.txt
size: 150
'''
RETURN = r'''
'''
import os
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
def execute_module(module):
try:
size = module.params.get('size') * 1024 * 1024
path = module.params.get('path')
write_mode = "w"
if module.params.get('binary'):
content = os.urandom(size)
write_mode = "wb"
else:
content = ""
count = 0
while len(content) < size:
content += "This file has been generated using ansible: {0}\n".format(count)
count += 1
with open(path, write_mode) as f:
f.write(content)
module.exit_json(changed=True, size=len(content))
except Exception as e:
module.fail_json(msg="failed to create file due to: {0}".format(to_native(e)))
def main():
argument_spec = {}
argument_spec['size'] = {'type': 'int', 'default': 400}
argument_spec['path'] = {'type': 'path', 'required': True}
argument_spec['binary'] = {'type': 'bool', 'default': False}
module = AnsibleModule(argument_spec=argument_spec)
execute_module(module)
if __name__ == '__main__':
main()

View File

@@ -1,217 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Aubin Bikouo <@abikouo>
# 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: kubectl_file_compare
short_description: Compare file and directory using kubectl
author:
- Aubin Bikouo (@abikouo)
description:
- This module is used to validate k8s_cp module.
- Compare the local file/directory with the remote pod version
notes:
- This module authenticates on kubernetes cluster using default kubeconfig only.
options:
namespace:
description:
- The pod namespace name
type: str
required: yes
pod:
description:
- The pod name
type: str
required: yes
container:
description:
- The container to retrieve files from.
type: str
remote_path:
description:
- Path of the file or directory on Pod.
type: path
required: yes
local_path:
description:
- Path of the local file or directory.
type: path
content:
description:
- local content to compare with remote file from pod.
- mutually exclusive with option I(local_path).
type: path
required: yes
args:
description:
- The file is considered to be an executable.
- The tool will be run locally and on pod and compare result from output and stderr.
type: list
kubectl_path:
description:
- Path to the kubectl executable, if not specified it will be download.
type: path
'''
EXAMPLES = r'''
- name: compare local /tmp/foo with /tmp/bar in a remote pod
kubectl_file_compare:
namespace: some-namespace
pod: some-pod
remote_path: /tmp/bar
local_path: /tmp/foo
kubectl_path: /tmp/test/kubectl
- name: Compare executable running help command
kubectl_file_compare:
namespace: some-namespace
pod: some-pod
remote_path: /tmp/test/kubectl
local_path: kubectl
kubectl_path: /tmp/test/kubectl
args:
- "--help"
'''
RETURN = r'''
'''
import os
import filecmp
from tempfile import NamedTemporaryFile, TemporaryDirectory
from ansible.module_utils.basic import AnsibleModule
def kubectl_get_content(module, dest_dir):
kubectl_path = module.params.get('kubectl_path')
if kubectl_path is None:
kubectl_path = module.get_bin_path('kubectl', required=True)
namespace = module.params.get('namespace')
pod = module.params.get('pod')
file = module.params.get('remote_path')
cmd = [
kubectl_path,
'cp',
"{0}/{1}:{2}".format(namespace, pod, file)
]
container = module.params.get('container')
if container:
cmd += ['-c', container]
local_file = os.path.join(dest_dir, os.path.basename(module.params.get('remote_path')))
cmd.append(local_file)
rc, out, err = module.run_command(cmd)
return local_file, err, rc, out
def kubectl_run_from_pod(module):
kubectl_path = module.params.get('kubectl_path')
if kubectl_path is None:
kubectl_path = module.get_bin_path('kubectl', required=True)
cmd = [
kubectl_path,
'exec',
module.params.get('pod'),
'-n',
module.params.get('namespace')
]
container = module.params.get('container')
if container:
cmd += ['-c', container]
cmd += ['--', module.params.get('remote_path')]
cmd += module.params.get('args')
return module.run_command(cmd)
def compare_directories(dir1, dir2):
test = filecmp.dircmp(dir1, dir2)
if any([len(test.left_only) > 0, len(test.right_only) > 0, len(test.funny_files) > 0]):
return False
(t, mismatch, errors) = filecmp.cmpfiles(dir1, dir2, test.common_files, shallow=False)
if len(mismatch) > 0 or len(errors) > 0:
return False
for common_dir in test.common_dirs:
new_dir1 = os.path.join(dir1, common_dir)
new_dir2 = os.path.join(dir2, common_dir)
if not compare_directories(new_dir1, new_dir2):
return False
return True
def execute_module(module):
args = module.params.get('args')
local_path = module.params.get('local_path')
namespace = module.params.get('namespace')
pod = module.params.get('pod')
file = module.params.get('remote_path')
content = module.params.get('content')
if args:
pod_rc, pod_out, pod_err = kubectl_run_from_pod(module)
rc, out, err = module.run_command([module.params.get('local_path')] + args)
if rc == pod_rc and out == pod_out:
module.exit_json(msg="{0} and {1}/{2}:{3} are same.".format(
local_path, namespace, pod, file
), rc=rc, stderr=err, stdout=out)
result = dict(local=dict(rc=rc, out=out, err=err), remote=dict(rc=pod_rc, out=pod_out, err=pod_err))
module.fail_json(msg=f"{local_path} and {namespace}/{pod}:{file} are same.", **result)
else:
with TemporaryDirectory() as tmpdirname:
file_from_pod, err, rc, out = kubectl_get_content(module=module, dest_dir=tmpdirname)
if not os.path.exists(file_from_pod):
module.fail_json(msg="failed to copy content from pod", error=err, output=out)
if content is not None:
with NamedTemporaryFile(mode="w") as tmp_file:
tmp_file.write(content)
tmp_file.flush()
if filecmp.cmp(file_from_pod, tmp_file.name):
module.exit_json(msg=f"defined content and {namespace}/{pod}:{file} are same.")
module.fail_json(msg=f"defined content and {namespace}/{pod}:{file} are same.")
if os.path.isfile(local_path):
if filecmp.cmp(file_from_pod, local_path):
module.exit_json(msg=f"{local_path} and {namespace}/{pod}:{file} are same.")
module.fail_json(msg=f"{local_path} and {namespace}/{pod}:{file} are same.")
if os.path.isdir(local_path):
if compare_directories(file_from_pod, local_path):
module.exit_json(msg=f"{local_path} and {namespace}/{pod}:{file} are same.")
module.fail_json(msg=f"{local_path} and {namespace}/{pod}:{file} are same.")
def main():
argument_spec = {}
argument_spec['namespace'] = {'type': 'str', 'required': True}
argument_spec['pod'] = {'type': 'str', 'required': True}
argument_spec['container'] = {}
argument_spec['remote_path'] = {'type': 'path', 'required': True}
argument_spec['local_path'] = {'type': 'path'}
argument_spec['content'] = {'type': 'str'}
argument_spec['kubectl_path'] = {'type': 'path'}
argument_spec['args'] = {'type': 'list'}
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=[('local_path', 'content')],
required_one_of=[['local_path', 'content']])
execute_module(module)
if __name__ == '__main__':
main()

View File

@@ -1,95 +0,0 @@
---
- block:
- set_fact:
delete_namespace: delete
- name: Ensure namespace exists
k8s:
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ delete_namespace }}"
- name: Add a daemonset
k8s:
definition:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: delete-daemonset
namespace: "{{ delete_namespace }}"
spec:
selector:
matchLabels:
app: "{{ k8s_pod_name }}"
template: "{{ k8s_pod_template }}"
wait: yes
wait_timeout: 180
vars:
k8s_pod_name: delete-ds
k8s_pod_image: gcr.io/kuar-demo/kuard-amd64:1
register: ds
- name: Check that daemonset wait worked
assert:
that:
- ds.result.status.currentNumberScheduled == ds.result.status.desiredNumberScheduled
- name: Check if pods exist
k8s_info:
namespace: "{{ delete_namespace }}"
kind: Pod
label_selectors:
- "app={{ k8s_pod_name }}"
vars:
k8s_pod_name: delete-ds
register: pods_create
- name: Assert that there are pods
assert:
that:
- pods_create.resources
- name: Remove the daemonset
k8s:
kind: DaemonSet
name: delete-daemonset
namespace: "{{ delete_namespace }}"
state: absent
wait: yes
- name: Show status of pods
k8s_info:
namespace: "{{ delete_namespace }}"
kind: Pod
label_selectors:
- "app={{ k8s_pod_name }}"
vars:
k8s_pod_name: delete-ds
- name: Wait for background deletion
pause:
seconds: 30
- name: Check if pods still exist
k8s_info:
namespace: "{{ delete_namespace }}"
kind: Pod
label_selectors:
- "app={{ k8s_pod_name }}"
vars:
k8s_pod_name: delete-ds
register: pods_delete
- name: Assert that deleting the daemonset deleted the pods
assert:
that:
- not pods_delete.resources
always:
- name: Remove namespace
k8s:
kind: Namespace
name: "{{ delete_namespace }}"
state: absent

View File

@@ -1,227 +0,0 @@
---
- block:
- name: Set common facts
set_fact:
drain_namespace: "drain"
drain_daemonset_name: "promotheus-dset"
drain_pod_name: "pod-drain"
- name: Create {{ drain_namespace }} namespace
k8s:
kind: Namespace
name: '{{ drain_namespace }}'
- name: list cluster nodes
k8s_info:
kind: node
register: nodes
- name: Select uncordoned nodes
set_fact:
uncordoned_nodes: "{{ nodes.resources | selectattr('spec.unschedulable', 'undefined') | map(attribute='metadata.name') | list}}"
- name: Assert that at least one node is schedulable
assert:
that:
- uncordoned_nodes | length > 0
- name: select node to drain
set_fact:
node_to_drain: '{{ uncordoned_nodes[0] }}'
- name: Deploy daemonset on cluster
k8s:
namespace: '{{ drain_namespace }}'
definition:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: '{{ drain_daemonset_name }}'
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- '{{ node_to_drain }}'
selector:
matchLabels:
name: prometheus-exporter
template:
metadata:
labels:
name: prometheus-exporter
spec:
containers:
- name: prometheus
image: prom/node-exporter
ports:
- containerPort: 80
- name: Create Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet.
k8s:
namespace: '{{ drain_namespace }}'
wait: yes
definition:
apiVersion: v1
kind: Pod
metadata:
name: '{{ drain_pod_name }}'
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- '{{ node_to_drain }}'
containers:
- name: c0
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
- name: Cordon node
k8s_drain:
state: cordon
name: '{{ node_to_drain }}'
register: cordon
- name: assert that cordon is changed
assert:
that:
- cordon is changed
- name: Test cordon idempotency
k8s_drain:
state: cordon
name: '{{ node_to_drain }}'
register: cordon
- name: assert that cordon is not changed
assert:
that:
- cordon is not changed
- name: Get pods
k8s_info:
kind: Pod
namespace: '{{ drain_namespace }}'
register: Pod
- name: assert that pods are running on cordoned node
assert:
that:
- "{{ Pod.resources | selectattr('status.phase', 'equalto', 'Running') | selectattr('spec.nodeName', 'equalto', node_to_drain) | list | length > 0 }}"
- name: Uncordon node
k8s_drain:
state: uncordon
name: '{{ node_to_drain }}'
register: uncordon
- name: assert that uncordon is changed
assert:
that:
- uncordon is changed
- name: Test uncordon idempotency
k8s_drain:
state: uncordon
name: '{{ node_to_drain }}'
register: uncordon
- name: assert that uncordon is not changed
assert:
that:
- uncordon is not changed
- name: Drain node
k8s_drain:
state: drain
name: '{{ node_to_drain }}'
ignore_errors: true
register: drain_result
- name: assert that drain failed due to DaemonSet managed Pods
assert:
that:
- drain_result is failed
- '"cannot delete DaemonSet-managed Pods" in drain_result.msg'
- '"cannot delete Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet" in drain_result.msg'
- name: Drain node using ignore_daemonsets and force options
k8s_drain:
state: drain
name: '{{ node_to_drain }}'
delete_options:
force: true
ignore_daemonsets: true
wait_timeout: 0
register: drain_result
- name: assert that node has been drained
assert:
that:
- drain_result is changed
- '"node {{ node_to_drain }} marked unschedulable." in drain_result.result'
- name: assert that unmanaged pod were deleted
k8s_info:
namespace: '{{ drain_namespace }}'
kind: Pod
name: '{{ drain_pod_name }}'
register: _result
failed_when: _result.resources
- name: Test drain idempotency
k8s_drain:
state: drain
name: '{{ node_to_drain }}'
delete_options:
force: true
ignore_daemonsets: true
register: drain_result
- name: Check idempotency
assert:
that:
- drain_result is not changed
- name: Get DaemonSet
k8s_info:
kind: DaemonSet
namespace: '{{ drain_namespace }}'
name: '{{ drain_daemonset_name }}'
register: dset_result
- name: assert that daemonset managed pods were not removed
assert:
that:
- dset_result.resources | list | length > 0
- name: Uncordon node
k8s_drain:
state: uncordon
name: '{{ node_to_drain }}'
always:
- name: Uncordon node
k8s_drain:
state: uncordon
name: '{{ node_to_drain }}'
when: node_to_drain is defined
ignore_errors: true
- name: delete namespace
k8s:
state: absent
kind: namespace
name: '{{ drain_namespace }}'

View File

@@ -1,65 +0,0 @@
---
- vars:
exec_namespace: k8s-exec
pod: sleep-pod
exec_pod_definition:
apiVersion: v1
kind: Pod
metadata:
name: "{{ pod }}"
namespace: "{{ exec_namespace }}"
spec:
containers:
- name: sleeper
image: busybox
command: ["sleep", "infinity"]
block:
- name: "Ensure that {{ exec_namespace }} namespace exists"
k8s:
kind: Namespace
name: "{{ exec_namespace }}"
- name: "Create a pod"
k8s:
definition: "{{ exec_pod_definition }}"
wait: yes
wait_sleep: 1
wait_timeout: 30
- name: "Execute a command"
k8s_exec:
pod: "{{ pod }}"
namespace: "{{ exec_namespace }}"
command: cat /etc/resolv.conf
register: output
- name: "Show k8s_exec output"
debug:
var: output
- name: "Assert k8s_exec output is correct"
assert:
that:
- "'nameserver' in output.stdout"
- name: Check if rc is returned for the given command
k8s_exec:
namespace: "{{ exec_namespace }}"
pod: "{{ pod }}"
command: 'false'
register: command_status
ignore_errors: True
- name: Check last command status
assert:
that:
- command_status.rc != 0
- command_status.return_code != 0
always:
- name: "Cleanup namespace"
k8s:
kind: Namespace
name: "{{ exec_namespace }}"
state: absent

View File

@@ -1,124 +0,0 @@
---
- block:
- name: ensure that k8s-log namespace exists
k8s:
kind: Namespace
name: k8s-log
- name: create hello-world deployment
k8s:
wait: yes
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
namespace: k8s-log
spec:
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- image: busybox
name: hello-world
command: ['sh']
args: ['-c', 'while true ; do echo "hello world" && sleep 10 ; done']
restartPolicy: Always
- name: retrieve the log by providing the deployment
k8s_log:
api_version: apps/v1
kind: Deployment
namespace: k8s-log
name: hello-world
register: deployment_log
- name: verify that the log can be retrieved via the deployment
assert:
that:
- "'hello world' in deployment_log.log"
- item == 'hello world' or item == ''
with_items: '{{ deployment_log.log_lines }}'
- name: retrieve the log with a label selector
k8s_log:
namespace: k8s-log
label_selectors:
- 'app=hello-world'
register: label_selector_log
- name: verify that the log can be retrieved via the label
assert:
that:
- "'hello world' in label_selector_log.log"
- item == 'hello world' or item == ''
with_items: '{{ label_selector_log.log_lines }}'
- name: get the hello-world pod
k8s_info:
kind: Pod
namespace: k8s-log
label_selectors:
- 'app=hello-world'
register: k8s_log_pods
- name: retrieve the log directly with the pod name
k8s_log:
namespace: k8s-log
name: '{{ k8s_log_pods.resources.0.metadata.name }}'
register: pod_log
- name: verify that the log can be retrieved via the pod name
assert:
that:
- "'hello world' in pod_log.log"
- item == 'hello world' or item == ''
with_items: '{{ pod_log.log_lines }}'
- name: Create a job that calculates 7
k8s:
state: present
wait: yes
wait_timeout: 120
wait_condition:
type: Complete
status: 'True'
definition:
apiVersion: batch/v1
kind: Job
metadata:
name: int-log
namespace: k8s-log
spec:
template:
spec:
containers:
- name: busybox
image: busybox
command: ["echo", "7"]
restartPolicy: Never
backoffLimit: 4
- name: retrieve logs from the job
k8s_log:
api_version: batch/v1
kind: Job
namespace: k8s-log
name: int-log
register: job_logs
- name: verify the log was successfully retrieved
assert:
that: job_logs.log_lines[0] == "7"
always:
- name: ensure that namespace is removed
k8s:
kind: Namespace
name: k8s-log
state: absent

View File

@@ -1,112 +0,0 @@
---
- block:
- set_fact:
pre_test1: "{{ lookup('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
pre_test2: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one') }}"
pre_test3: "{{ query('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
pre_test4: "{{ query('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one') }}"
# https://github.com/ansible-collections/kubernetes.core/issues/147
- name: Create a namespace with label
kubernetes.core.k8s:
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "app-development-one"
labels:
namespace_label: "app_development"
- set_fact:
test1: "{{ lookup('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development', wantlist=True) }}"
test2: "{{ query('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
test3: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one', wantlist=True) }}"
test4: "{{ query('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one') }}"
test5: "{{ lookup('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
test6: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one') }}"
test7: "{{ lookup('kubernetes.core.k8s', kind='Ingress', api_version='networking.k8s.io/vINVALID', errors='ignore') }}"
- set_fact:
test8: "{{ lookup('kubernetes.core.k8s', kind='Ingress', api_version='networking.k8s.io/vINVALID') }}"
ignore_errors: true
- name: Assert that every test is passed
assert:
that:
# Before creating object
- pre_test1 is sequence and pre_test1 is not string
- pre_test1 | length == 0
- pre_test2 is sequence and pre_test2 is not string
- pre_test2 | length == 0
- pre_test3 is sequence and pre_test3 is not string
- pre_test3 | length == 0
- pre_test4 is sequence and pre_test4 is not string
- pre_test4 | length == 0
# After creating object
- test1 is sequence and test1 is not string
- test1 | length == 1
- test2 is sequence and test2 is not string
- test2 | length == 1
- test3 is sequence and test3 is not string
- test3 | length == 1
- test4 is sequence and test4 is not string
- test4 | length == 1
# Without wantlist=True lookup should return mapping
- test5 is mapping
- test6 is mapping
# errors='ignore'
- test7 is string
- test8 is not defined
- name: Create another namespace with label
kubernetes.core.k8s:
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "app-development-two"
labels:
namespace_label: "app_development"
- set_fact:
test1: "{{ lookup('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development', wantlist=True) }}"
test2: "{{ query('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
test3: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one', wantlist=True) }}"
test4: "{{ query('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one') }}"
test5: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-two', wantlist=True) }}"
test6: "{{ query('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-two') }}"
test7: "{{ lookup('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
test8: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-one') }}"
test9: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name='app-development-two') }}"
- name: Assert that every test is passed after creating second object
assert:
that:
# After creating second object
- test1 is sequence and test1 is not string
- test1 | length == 2
- test2 is sequence and test2 is not string
- test2 | length == 2
- test3 is sequence and test3 is not string
- test3 | length == 1
- test4 is sequence and test4 is not string
- test4 | length == 1
- test5 is sequence and test5 is not string
- test5 | length == 1
- test6 is sequence and test6 is not string
- test6 | length == 1
# When label_selector is used it returns list irrespective of wantlist=True
- test7 is sequence and test7 is not string
# Without wantlist=True lookup should return mapping
- test8 is mapping
- test9 is mapping
always:
- name: Ensure that namespace is removed
k8s:
kind: Namespace
name: "app-development-{{ item }}"
state: absent
with_items:
- one
- two

View File

@@ -3,44 +3,77 @@
# Copyright (c) 2020, 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)
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
from ansible.errors import (
AnsibleAction,
AnsibleActionFail,
AnsibleError,
AnsibleFileNotFound,
)
from ansible.module_utils._text import to_bytes, to_native, to_text
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.module_utils.six import string_types, iteritems
from ansible.module_utils._text import to_text, to_bytes, to_native
from ansible.module_utils.six import iteritems, string_types
from ansible.plugins.action import ActionBase
class ActionModule(ActionBase):
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 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"
def _ensure_invocation(self, result):
# NOTE: adding invocation arguments here needs to be kept in sync with
# any no_log specified in the argument_spec in the module.
if 'invocation' not in result:
if "invocation" not in result:
if self._play_context.no_log:
result['invocation'] = "CENSORED: no_log is set"
result["invocation"] = "CENSORED: no_log is set"
else:
result['invocation'] = self._task.args.copy()
result['invocation']['module_args'] = self._task.args.copy()
result["invocation"] = self._task.args.copy()
result["invocation"]["module_args"] = self._task.args.copy()
return result
@contextmanager
def get_template_data(self, template_path):
try:
source = self._find_needle('templates', template_path)
source = self._find_needle("templates", template_path)
except AnsibleError as e:
raise AnsibleActionFail(to_text(e))
@@ -48,15 +81,19 @@ class ActionModule(ActionBase):
try:
tmp_source = self._loader.get_real_file(source)
except AnsibleFileNotFound as e:
raise AnsibleActionFail("could not find template=%s, %s" % (source, to_text(e)))
b_tmp_source = to_bytes(tmp_source, errors='surrogate_or_strict')
raise AnsibleActionFail(
"could not find template=%s, %s" % (source, to_text(e))
)
b_tmp_source = to_bytes(tmp_source, errors="surrogate_or_strict")
try:
with open(b_tmp_source, 'rb') as f:
with open(b_tmp_source, "rb") as f:
try:
template_data = to_text(f.read(), errors='surrogate_or_strict')
template_data = to_text(f.read(), errors="surrogate_or_strict")
except UnicodeError:
raise AnsibleActionFail("Template source files must be utf-8 encoded")
raise AnsibleActionFail(
"Template source files must be utf-8 encoded"
)
yield template_data
except AnsibleAction:
raise
@@ -73,63 +110,104 @@ class ActionModule(ActionBase):
"block_start_string": None,
"block_end_string": None,
"trim_blocks": True,
"lstrip_blocks": False
"lstrip_blocks": False,
}
if isinstance(template, string_types):
# treat this as raw_params
template_param['path'] = template
template_param["path"] = template
elif isinstance(template, dict):
template_args = template
template_path = template_args.get('path', None)
template_path = template_args.get("path", None)
if not template_path:
raise AnsibleActionFail("Please specify path for template.")
template_param['path'] = template_path
template_param["path"] = template_path
# Options type validation strings
for s_type in ('newline_sequence', 'variable_start_string', 'variable_end_string', 'block_start_string',
'block_end_string'):
for s_type in (
"newline_sequence",
"variable_start_string",
"variable_end_string",
"block_start_string",
"block_end_string",
):
if s_type in template_args:
value = ensure_type(template_args[s_type], 'string')
value = ensure_type(template_args[s_type], "string")
if value is not None and not isinstance(value, string_types):
raise AnsibleActionFail("%s is expected to be a string, but got %s instead" % (s_type, type(value)))
raise AnsibleActionFail(
"%s is expected to be a string, but got %s instead"
% (s_type, type(value))
)
try:
template_param.update({
"trim_blocks": boolean(template_args.get('trim_blocks', True), strict=False),
"lstrip_blocks": boolean(template_args.get('lstrip_blocks', False), strict=False)
})
template_param.update(
{
"trim_blocks": boolean(
template_args.get("trim_blocks", True), strict=False
),
"lstrip_blocks": boolean(
template_args.get("lstrip_blocks", False), strict=False
),
}
)
except TypeError as e:
raise AnsibleActionFail(to_native(e))
template_param.update({
"newline_sequence": template_args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE),
"variable_start_string": template_args.get('variable_start_string', None),
"variable_end_string": template_args.get('variable_end_string', None),
"block_start_string": template_args.get('block_start_string', None),
"block_end_string": template_args.get('block_end_string', None)
})
template_param.update(
{
"newline_sequence": template_args.get(
"newline_sequence", self.DEFAULT_NEWLINE_SEQUENCE
),
"variable_start_string": template_args.get(
"variable_start_string", None
),
"variable_end_string": template_args.get(
"variable_end_string", None
),
"block_start_string": template_args.get("block_start_string", None),
"block_end_string": template_args.get("block_end_string", None),
}
)
else:
raise AnsibleActionFail("Error while reading template file - "
"a string or dict for template expected, but got %s instead" % type(template))
raise AnsibleActionFail(
"Error while reading template file - "
"a string or dict for template expected, but got %s instead"
% type(template)
)
return template_param
def import_jinja2_lstrip(self, templates):
# Option `lstrip_blocks' was added in Jinja2 version 2.7.
if any(tmp['lstrip_blocks'] for tmp in templates):
if any(tmp["lstrip_blocks"] for tmp in templates):
try:
import jinja2.defaults
except ImportError:
raise AnsibleError('Unable to import Jinja2 defaults for determining Jinja2 features.')
raise AnsibleError(
"Unable to import Jinja2 defaults for determining Jinja2 features."
)
try:
jinja2.defaults.LSTRIP_BLOCKS
except AttributeError:
raise AnsibleError("Option `lstrip_blocks' is only available in Jinja2 versions >=2.7")
raise AnsibleError(
"Option `lstrip_blocks' is only available in Jinja2 versions >=2.7"
)
def load_template(self, template, new_module_args, task_vars):
# template is only supported by k8s module.
if self._task.action not in ('k8s', 'kubernetes.core.k8s', 'community.okd.k8s', 'redhat.openshift.k8s', 'community.kubernetes.k8s'):
raise AnsibleActionFail("'template' is only a supported parameter for the 'k8s' module.")
if self._task.action not in (
"k8s",
"kubernetes.core.k8s",
"community.okd.k8s",
"redhat.openshift.k8s",
"community.kubernetes.k8s",
"openshift_adm_groups_sync",
"community.okd.openshift_adm_groups_sync",
"redhat.openshift.openshift_adm_groups_sync",
):
raise AnsibleActionFail(
"'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):
template_params.append(self.get_template_args(template))
@@ -137,8 +215,11 @@ class ActionModule(ActionBase):
for element in template:
template_params.append(self.get_template_args(element))
else:
raise AnsibleActionFail("Error while reading template file - "
"a string or dict for template expected, but got %s instead" % type(template))
raise AnsibleActionFail(
"Error while reading template file - "
"a string or dict for template expected, but got %s instead"
% type(template)
)
self.import_jinja2_lstrip(template_params)
@@ -149,20 +230,31 @@ 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"):
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']
newline_sequence = template_item["newline_sequence"]
if newline_sequence in wrong_sequences:
template_item['newline_sequence'] = allowed_sequences[wrong_sequences.index(newline_sequence)]
template_item["newline_sequence"] = allowed_sequences[
wrong_sequences.index(newline_sequence)
]
elif newline_sequence not in allowed_sequences:
raise AnsibleActionFail("newline_sequence needs to be one of: \n, \r or \r\n")
raise AnsibleActionFail(
"newline_sequence needs to be one of: \n, \r or \r\n"
)
# template the source data locally & get ready to transfer
with self.get_template_data(template_item['path']) as template_data:
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):
@@ -170,29 +262,48 @@ class ActionModule(ActionBase):
if value is not None:
setattr(self._templar.environment, key, value)
else:
setattr(self._templar.environment, key, default_environment.get(key))
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)
result_template.append(result)
result = self._templar.do_template(
template_data,
preserve_trailing_newlines=True,
escape_backslashes=False,
)
if omit_value is not None:
result_template.extend(RemoveOmit(result, omit_value).output())
else:
result_template.append(result)
self._templar.available_variables = old_vars
resource_definition = self._task.args.get('definition', None)
resource_definition = self._task.args.get("definition", None)
if not resource_definition:
new_module_args.pop('template')
new_module_args['definition'] = result_template
new_module_args.pop("template")
new_module_args["definition"] = result_template
def get_file_realpath(self, local_path):
# local_path is only supported by k8s_cp module.
if self._task.action not in ('k8s_cp', 'kubernetes.core.k8s_cp', 'community.kubernetes.k8s_cp'):
raise AnsibleActionFail("'local_path' is only supported parameter for 'k8s_cp' module.")
if self._task.action not in (
"k8s_cp",
"kubernetes.core.k8s_cp",
"community.kubernetes.k8s_cp",
):
raise AnsibleActionFail(
"'local_path' is only supported parameter for 'k8s_cp' module."
)
if os.path.exists(local_path):
return local_path
try:
# find in expected paths
return self._find_needle('files', local_path)
return self._find_needle("files", local_path)
except AnsibleError:
raise AnsibleActionFail("%s does not exist in local filesystem" % local_path)
raise AnsibleActionFail(
"%s does not exist in local filesystem" % local_path
)
def get_kubeconfig(self, kubeconfig, remote_transport, new_module_args):
if isinstance(kubeconfig, string_types):
@@ -200,20 +311,26 @@ class ActionModule(ActionBase):
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
new_module_args["kubeconfig"] = kubeconfig
else:
raise AnsibleActionFail("Error while reading kubeconfig parameter - "
"a string or dict expected, but got %s instead" % type(kubeconfig))
raise AnsibleActionFail(
"Error while reading kubeconfig parameter - "
"a string or dict expected, but got %s instead" % type(kubeconfig)
)
def run(self, tmp=None, task_vars=None):
''' handler for k8s options '''
"""handler for k8s options"""
if task_vars is None:
task_vars = dict()
@@ -224,53 +341,61 @@ class ActionModule(ActionBase):
# look for kubeconfig and src
# 'local' => look files on Ansible Controller
# Transport other than 'local' => look files on remote node
remote_transport = self._connection.transport != 'local'
remote_transport = self._connection.transport != "local"
new_module_args = copy.deepcopy(self._task.args)
kubeconfig = self._task.args.get('kubeconfig', None)
kubeconfig = self._task.args.get("kubeconfig", None)
if kubeconfig:
try:
self.get_kubeconfig(kubeconfig, remote_transport, new_module_args)
except AnsibleError as e:
result['failed'] = True
result['msg'] = to_text(e)
result['exception'] = traceback.format_exc()
result["failed"] = True
result["msg"] = to_text(e)
result["exception"] = traceback.format_exc()
return result
# find the file in the expected search path
src = self._task.args.get('src', None)
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(self._execute_module(module_name=self._task.action, task_vars=task_vars))
result.update(
self._execute_module(
module_name=self._task.action, task_vars=task_vars
)
)
return self._ensure_invocation(result)
# src is local
try:
# find in expected paths
src = self._find_needle('files', src)
src = self._find_needle("files", src)
except AnsibleError as e:
result['failed'] = True
result['msg'] = to_text(e)
result['exception'] = traceback.format_exc()
result["failed"] = True
result["msg"] = to_text(e)
result["exception"] = traceback.format_exc()
return result
if src:
new_module_args['src'] = src
new_module_args["src"] = src
template = self._task.args.get('template', None)
template = self._task.args.get("template", None)
if template:
self.load_template(template, new_module_args, task_vars)
local_path = self._task.args.get('local_path')
state = self._task.args.get('state', None)
if local_path and state == 'to_pod':
new_module_args['local_path'] = self.get_file_realpath(local_path)
local_path = self._task.args.get("local_path")
state = self._task.args.get("state", None)
if local_path and state == "to_pod" and not remote_transport:
new_module_args["local_path"] = self.get_file_realpath(local_path)
# Execute the k8s_* module.
module_return = self._execute_module(module_name=self._task.action, module_args=new_module_args, task_vars=task_vars)
module_return = self._execute_module(
module_name=self._task.action,
module_args=new_module_args,
task_vars=task_vars,
)
# Delete tmp path
self._remove_tmp_path(self._connection._shell.tmpdir)

View File

@@ -17,14 +17,15 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r"""
author:
- xuxinkun
- xuxinkun (@xuxinkun)
connection: kubectl
name: kubectl
short_description: Execute tasks in pods running on Kubernetes.
@@ -71,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
@@ -170,41 +181,118 @@ 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.six.moves import shlex_quote
from ansible.module_utils._text import to_bytes
from ansible.plugins.connection import ConnectionBase, BUFSIZE
from ansible.module_utils.six.moves import shlex_quote
from ansible.parsing.yaml.loader import AnsibleLoader
from ansible.plugins.connection import BUFSIZE, ConnectionBase
from ansible.utils.display import Display
display = Display()
CONNECTION_TRANSPORT = 'kubectl'
CONNECTION_TRANSPORT = "kubectl"
CONNECTION_OPTIONS = {
'kubectl_container': '-c',
'kubectl_namespace': '-n',
'kubectl_kubeconfig': '--kubeconfig',
'kubectl_context': '--context',
'kubectl_host': '--server',
'kubectl_username': '--username',
'kubectl_password': '--password',
'client_cert': '--client-certificate',
'client_key': '--client-key',
'ca_cert': '--certificate-authority',
'validate_certs': '--insecure-skip-tls-verify',
'kubectl_token': '--token'
"kubectl_container": "-c",
"kubectl_namespace": "-n",
"kubectl_kubeconfig": "--kubeconfig",
"kubectl_context": "--context",
"kubectl_host": "--server",
"kubectl_username": "--username",
"kubectl_password": "--password",
"client_cert": "--client-certificate",
"client_key": "--client-key",
"ca_cert": "--certificate-authority",
"validate_certs": "--insecure-skip-tls-verify",
"kubectl_token": "--token",
}
class Connection(ConnectionBase):
''' Local kubectl based connections '''
"""Local kubectl based connections"""
transport = CONNECTION_TRANSPORT
connection_options = CONNECTION_OPTIONS
@@ -217,155 +305,246 @@ 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))
cmd_arg = "{0}_command".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
"""
"""Build the local kubectl exec command to run cmd on remote_host"""
local_cmd = [self.transport_cmd]
censored_local_cmd = [self.transport_cmd]
# 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) != '':
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)
local_cmd.append(u'{0}={1}'.format(self.connection_options[key], str(skip_verify_ssl).lower()))
censored_local_cmd.append(u'{0}={1}'.format(self.connection_options[key], str(skip_verify_ssl).lower()))
elif not key.endswith('container') and self.get_option(key) and self.connection_options.get(key):
local_cmd.append(
"{0}={1}".format(
self.connection_options[key], str(skip_verify_ssl).lower()
)
)
censored_local_cmd.append(
"{0}={1}".format(
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)
and self.connection_options.get(key)
):
cmd_arg = self.connection_options[key]
local_cmd += [cmd_arg, self.get_option(key)]
# Redact password and token from console log
if key.endswith(('_token', '_password')):
censored_local_cmd += [cmd_arg, '********']
if key.endswith(("_token", "_password")):
censored_local_cmd += [cmd_arg, "********"]
else:
censored_local_cmd += [cmd_arg, self.get_option(key)]
extra_args_name = u'{0}_extra_args'.format(self.transport)
extra_args_name = "{0}_extra_args".format(self.transport)
if self.get_option(extra_args_name):
local_cmd += self.get_option(extra_args_name).split(' ')
censored_local_cmd += self.get_option(extra_args_name).split(' ')
local_cmd += self.get_option(extra_args_name).split(" ")
censored_local_cmd += self.get_option(extra_args_name).split(" ")
pod = self.get_option(u'{0}_pod'.format(self.transport))
pod = self.get_option("{0}_pod".format(self.transport))
if not pod:
pod = self._play_context.remote_addr
# -i is needed to keep stdin open which allows pipelining to work
local_cmd += ['exec', '-i', pod]
censored_local_cmd += ['exec', '-i', pod]
local_cmd += ["exec", "-i", pod]
censored_local_cmd += ["exec", "-i", pod]
# if the pod has more than one container, then container is required
container_arg_name = u'{0}_container'.format(self.transport)
container_arg_name = "{0}_container".format(self.transport)
if self.get_option(container_arg_name):
local_cmd += ['-c', self.get_option(container_arg_name)]
censored_local_cmd += ['-c', self.get_option(container_arg_name)]
local_cmd += ["-c", self.get_option(container_arg_name)]
censored_local_cmd += ["-c", self.get_option(container_arg_name)]
local_cmd += ['--'] + cmd
censored_local_cmd += ['--'] + cmd
local_cmd += ["--"] + cmd
censored_local_cmd += ["--"] + cmd
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 """
"""Connect to the container. Nothing to do"""
super(Connection, self)._connect()
if not self._connected:
display.vvv(u"ESTABLISH {0} CONNECTION".format(self.transport), host=self._play_context.remote_addr)
display.vvv(
"ESTABLISH {0} CONNECTION".format(self.transport),
host=self._play_context.remote_addr,
)
self._connected = True
def exec_command(self, cmd, in_data=None, sudoable=False):
""" Run a command in the container """
"""Run a command in the container"""
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
local_cmd, censored_local_cmd = self._build_exec_cmd([self._play_context.executable, '-c', cmd])
local_cmd, censored_local_cmd = self._build_exec_cmd(
[self._play_context.executable, "-c", cmd]
)
display.vvv("EXEC %s" % (censored_local_cmd,), host=self._play_context.remote_addr)
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
display.vvv(
"EXEC %s" % (censored_local_cmd,), host=self._play_context.remote_addr
)
local_cmd = [to_bytes(i, errors="surrogate_or_strict") for i in local_cmd]
p = subprocess.Popen(
local_cmd,
shell=False,
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):
''' Make sure that we put files into a standard path
"""Make sure that we put files into a standard path
If a path is relative, then we need to choose where to put it.
ssh chooses $HOME but we aren't guaranteed that a home dir will
exist in any given chroot. So for now we're choosing "/" instead.
This also happens to be the former default.
If a path is relative, then we need to choose where to put it.
ssh chooses $HOME but we aren't guaranteed that a home dir will
exist in any given chroot. So for now we're choosing "/" instead.
This also happens to be the former default.
Can revisit using $HOME instead if it's a problem
'''
Can revisit using $HOME instead if it's a problem
"""
if not remote_path.startswith(os.path.sep):
remote_path = os.path.join(os.path.sep, remote_path)
return os.path.normpath(remote_path)
def put_file(self, in_path, out_path):
""" Transfer a file from local to the container """
"""Transfer a file from local to the container"""
super(Connection, self).put_file(in_path, out_path)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr)
display.vvv(
"PUT %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr
)
out_path = self._prefix_login_path(out_path)
if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')):
raise AnsibleFileNotFound(
"file or module does not exist: %s" % in_path)
if not os.path.exists(to_bytes(in_path, errors="surrogate_or_strict")):
raise AnsibleFileNotFound("file or module does not exist: %s" % in_path)
out_path = shlex_quote(out_path)
# kubectl doesn't have native support for copying files into
# running containers, so we use kubectl exec to implement this
with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as in_file:
with open(to_bytes(in_path, errors="surrogate_or_strict"), "rb") as in_file:
if not os.fstat(in_file.fileno()).st_size:
count = ' count=0'
count = " count=0"
else:
count = ''
args, dummy = self._build_exec_cmd([self._play_context.executable, "-c", "dd of=%s bs=%s%s" % (out_path, BUFSIZE, count)])
args = [to_bytes(i, errors='surrogate_or_strict') for i in args]
count = ""
args, dummy = self._build_exec_cmd(
[
self._play_context.executable,
"-c",
"dd of=%s bs=%s%s && sleep 0" % (out_path, BUFSIZE, count),
]
)
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)
p = subprocess.Popen(
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")
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("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
raise AnsibleError(
"failed to transfer file %s to %s:\n%s\n%s"
% (in_path, out_path, stdout, stderr)
)
def fetch_file(self, in_path, out_path):
""" Fetch a file from container to local. """
"""Fetch a file from container to local."""
super(Connection, self).fetch_file(in_path, out_path)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr)
display.vvv(
"FETCH %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr
)
in_path = self._prefix_login_path(in_path)
out_dir = os.path.dirname(out_path)
# kubectl doesn't have native support for fetching files from
# running containers, so we use kubectl exec to implement this
args, dummy = self._build_exec_cmd([self._play_context.executable, "-c", "dd if=%s bs=%s" % (in_path, BUFSIZE)])
args = [to_bytes(i, errors='surrogate_or_strict') for i in args]
args, dummy = self._build_exec_cmd(
[self._play_context.executable, "-c", "dd if=%s bs=%s" % (in_path, BUFSIZE)]
)
args = [to_bytes(i, errors="surrogate_or_strict") for i in args]
actual_out_path = os.path.join(out_dir, os.path.basename(in_path))
with open(to_bytes(actual_out_path, errors='surrogate_or_strict'), 'wb') as out_file:
with open(
to_bytes(actual_out_path, errors="surrogate_or_strict"), "wb"
) as out_file:
try:
p = subprocess.Popen(args, stdin=subprocess.PIPE,
stdout=out_file, stderr=subprocess.PIPE)
p = subprocess.Popen(
args,
stdin=subprocess.PIPE,
stdout=out_file,
stderr=subprocess.PIPE,
env=self._local_env(),
)
except OSError:
raise AnsibleError(
"{0} connection requires dd command in the container to fetch files".format(self.transport)
"{0} connection requires dd command in the container to fetch files".format(
self.transport
)
)
stdout, stderr = p.communicate()
self.delete_temporary_file()
if p.returncode != 0:
raise AnsibleError("failed to fetch file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
raise AnsibleError(
"failed to fetch file %s to %s:\n%s\n%s"
% (in_path, out_path, stdout, stderr)
)
if actual_out_path != out_path:
os.rename(to_bytes(actual_out_path, errors='strict'), to_bytes(out_path, errors='strict'))
os.rename(
to_bytes(actual_out_path, errors="strict"),
to_bytes(out_path, errors="strict"),
)
def close(self):
""" Terminate the connection. Nothing to do for kubectl"""
"""Terminate the connection. Nothing to do for kubectl"""
super(Connection, self).close()
self._connected = False

View File

@@ -6,13 +6,13 @@
# Options for common Helm modules
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
binary_path:
description:
@@ -29,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)
@@ -48,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
"""

View File

@@ -5,13 +5,13 @@
# Options for authenticating with the API.
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
host:
description:
@@ -27,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:
@@ -76,6 +77,14 @@ options:
- 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
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 no_proxy set in correct.
- example value is "localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
type: str
version_added: 2.3.0
proxy_headers:
description:
- The Header used for the HTTP proxy.
@@ -110,8 +119,21 @@ options:
- Please note that the current version of the k8s python client library does not support setting this flag to True yet.
- "The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169"
type: bool
impersonate_user:
description:
- Username to impersonate for the operation.
- Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.
type: str
version_added: 2.3.0
impersonate_groups:
description:
- Group(s) to impersonate for the operation.
- "Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2"
type: list
elements: str
version_added: 2.3.0
notes:
- "To avoid SSL certificate validation errors when C(validate_certs) is I(True), the full
certificate chain for the API server must be provided via C(ca_cert) or in the
kubeconfig file."
'''
"""

View File

@@ -5,17 +5,17 @@
# Options for specifying object wait
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
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).
@@ -48,4 +48,4 @@ options:
type: str
description:
- Specify the UID of the target object.
'''
"""

View File

@@ -5,13 +5,13 @@
# Options for selecting or identifying a specific K8s object
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
api_version:
description:
@@ -49,4 +49,4 @@ options:
- If I(resource definition) is provided, the I(metadata.namespace) value from the I(resource_definition)
will override this option.
type: str
'''
"""

View File

@@ -5,13 +5,13 @@
# Options for providing an object configuration
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
resource_definition:
description:
@@ -28,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.
- Mutually exclusive with I(template) in case of M(k8s) module.
- 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
'''
"""

View File

@@ -5,13 +5,13 @@
# Options used by scale modules.
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
replicas:
description:
@@ -46,4 +46,4 @@ options:
default: 5
type: int
version_added: 2.0.0
'''
"""

View File

@@ -5,13 +5,13 @@
# Options for specifying object state
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
state:
description:
@@ -27,4 +27,4 @@ options:
- If set to C(yes), and I(state) is C(present), an existing object will be replaced.
type: bool
default: no
'''
"""

View File

@@ -5,13 +5,13 @@
# Options for specifying object wait
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
wait:
description:
@@ -64,4 +64,4 @@ options:
- The possible reasons in a condition are specific to each resource type in Kubernetes.
- See the API documentation of the status field for a given resource to see possible choices.
type: dict
'''
"""

View File

@@ -2,12 +2,15 @@
# 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)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.errors import AnsibleFilterError
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import generate_hash
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
generate_hash,
)
def k8s_config_resource_name(resource):
@@ -15,15 +18,14 @@ def k8s_config_resource_name(resource):
Generate resource name for the given resource of type ConfigMap, Secret
"""
try:
return resource['metadata']['name'] + '-' + generate_hash(resource)
return resource["metadata"]["name"] + "-" + generate_hash(resource)
except KeyError:
raise AnsibleFilterError("resource must have a metadata.name key to generate a resource name")
raise AnsibleFilterError(
"resource must have a metadata.name key to generate a resource name"
)
# ---- Ansible filters ----
class FilterModule(object):
def filters(self):
return {
'k8s_config_resource_name': k8s_config_resource_name
}
return {"k8s_config_resource_name": k8s_config_resource_name}

View 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

View File

@@ -1,15 +1,15 @@
# 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)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
name: k8s
plugin_type: inventory
author:
- Chris Houseknecht <@chouseknecht>
- Fabian von Feilitzsch <@fabianvf>
- Chris Houseknecht (@chouseknecht)
- Fabian von Feilitzsch (@fabianvf)
short_description: Kubernetes (K8s) inventory source
@@ -19,6 +19,13 @@ DOCUMENTATION = '''
- Uses the kubectl connection plugin to access the Kubernetes cluster.
- Uses k8s.(yml|yaml) YAML configuration file to set parameter values.
deprecated:
removed_in: 6.0.0
why: |
As discussed in U(https://github.com/ansible-collections/kubernetes.core/issues/31), we decided to
remove the k8s inventory plugin in release 6.0.0.
alternative: "Use M(kubernetes.core.k8s_info) and M(ansible.builtin.add_host) instead."
options:
plugin:
description: token that ensures this is a source file for the 'k8s' plugin.
@@ -86,39 +93,45 @@ DOCUMENTATION = '''
to access.
requirements:
- "python >= 3.6"
- "kubernetes >= 12.0.0"
- "python >= 3.9"
- "kubernetes >= 24.2.0"
- "PyYAML >= 3.11"
'''
"""
EXAMPLES = '''
EXAMPLES = r"""
# 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
- name: Authenticate with token, and return all pods and services for all namespaces
plugin: kubernetes.core.k8s
connections:
- host: https://192.168.64.4:8443
api_key: xxxxxxxxxxxxxxxx
validate_certs: false
# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
plugin: kubernetes.core.k8s
connections:
- namespaces:
- testing
- name: Use 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'
'''
- name: Use a custom config file, and a specific context.
plugin: kubernetes.core.k8s
connections:
- kubeconfig: /path/to/config
context: 'awx/192-168-64-4:8443/developer'
"""
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
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
HAS_K8S_MODULE_HELPER,
k8s_import_exception,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
try:
from kubernetes.dynamic.exceptions import DynamicApiError
@@ -128,37 +141,45 @@ except ImportError:
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 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)
return "%s Reason: %s" % (exc.status, exc.reason)
class K8sInventoryException(Exception):
pass
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleMixin):
NAME = 'kubernetes.core.k8s'
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
NAME = "kubernetes.core.k8s"
connection_plugin = 'kubernetes.core.kubectl'
transport = 'kubectl'
connection_plugin = "kubernetes.core.kubectl"
transport = "kubectl"
def parse(self, inventory, loader, path, cache=True):
super(InventoryModule, self).parse(inventory, loader, path)
self.display.deprecated(
"The 'k8s' inventory plugin has been deprecated and will be removed in release 6.0.0",
version="6.0.0",
collection_name="kubernetes.core",
)
cache_key = self._get_cache_prefix(path)
config_data = self._read_config_data(path)
self.setup(config_data, cache, cache_key)
def setup(self, config_data, cache, cache_key):
connections = config_data.get('connections')
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)
"This module requires the Kubernetes Python client. Try `pip install kubernetes`. Detail: {0}".format(
k8s_import_exception
)
)
source_data = None
@@ -172,18 +193,21 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleM
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.")
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']
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:
@@ -199,27 +223,36 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleM
@staticmethod
def get_default_host_name(host):
return host.replace('https://', '').replace('http://', '').replace('.', '-').replace(':', '_')
return (
host.replace("https://", "")
.replace("http://", "")
.replace(".", "-")
.replace(":", "_")
)
def get_available_namespaces(self, client):
v1_namespace = client.resources.get(api_version='v1', kind='Namespace')
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))
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')
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))
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)
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)
@@ -230,12 +263,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleM
for pod in obj.items:
pod_name = pod.metadata.name
pod_groups = []
pod_annotations = {} if not pod.metadata.annotations else dict(pod.metadata.annotations)
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)
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)
@@ -248,7 +283,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleM
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)
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:
@@ -256,46 +291,85 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleM
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)
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')
self.inventory.set_variable(
container_name, "container_state", "Running"
)
if container.state.terminated:
self.inventory.set_variable(container_name, '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)
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')
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))
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)
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)
@@ -305,15 +379,21 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleM
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)
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)
group_name = "label_{0}_{1}".format(key, value)
self.inventory.add_group(group_name)
self.inventory.add_child(group_name, service_name)
@@ -322,42 +402,75 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleM
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 []]
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)
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)
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)
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)
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)
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)
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))
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)
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
)

View File

@@ -3,18 +3,18 @@
#
# 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)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
lookup: k8s
DOCUMENTATION = """
name: k8s
short_description: Query the K8s API
author:
- Chris Houseknecht <@chouseknecht>
- Fabian von Feilitzsch <@fabianvf>
- Chris Houseknecht (@chouseknecht)
- Fabian von Feilitzsch (@fabianvf)
description:
- Uses the Kubernetes Python client to fetch a specific object by name, all matching objects within a
@@ -114,10 +114,10 @@ DOCUMENTATION = '''
aliases: [ verify_ssl ]
requirements:
- "python >= 3.6"
- "kubernetes >= 12.0.0"
- "python >= 3.9"
- "kubernetes >= 24.2.0"
- "PyYAML >= 3.11"
'''
"""
EXAMPLES = """
- name: Fetch a list of namespaces
@@ -159,39 +159,51 @@ RETURN = """
_list:
description:
- One ore more object definitions returned from the API.
type: complex
contains:
api_version:
description: The versioned schema of this representation of an object.
returned: success
type: str
kind:
description: Represents the REST resource this object represents.
returned: success
type: str
metadata:
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
returned: success
type: complex
spec:
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
returned: success
type: complex
status:
description: Current status details for the object.
returned: success
type: complex
type: list
elements: dict
sample:
- kind: ConfigMap
apiVersion: v1
metadata:
creationTimestamp: "2022-03-04T13:59:49Z"
name: my-config-map
namespace: default
resourceVersion: "418"
uid: 5714b011-d090-4eac-8272-a0ea82ec0abd
data:
key1: val1
"""
import os
from ansible.errors import AnsibleError
from ansible.module_utils.common._collections_compat import KeysView
from ansible.plugins.lookup import LookupBase
from ansible.module_utils.common.validation import check_type_bool
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,
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import K8sAnsibleMixin, get_api_client
try:
enable_turbo_mode = check_type_bool(os.environ.get("ENABLE_TURBO_MODE"))
except TypeError:
enable_turbo_mode = False
if enable_turbo_mode:
try:
from ansible_collections.cloud.common.plugins.plugin_utils.turbo.lookup import (
TurboLookupBase as LookupBase,
)
except ImportError:
from ansible.plugins.lookup import LookupBase # noqa: F401
else:
from ansible.plugins.lookup import LookupBase # noqa: F401
try:
from kubernetes.dynamic.exceptions import NotFoundError
HAS_K8S_MODULE_HELPER = True
k8s_import_exception = None
except ImportError as e:
@@ -199,13 +211,13 @@ 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(k8s_import_exception)
"Requires the Kubernetes Python client. Try `pip install kubernetes`. Detail: {0}".format(
k8s_import_exception
)
)
self.kind = None
@@ -226,31 +238,38 @@ class KubernetesLookup(K8sAnsibleMixin):
self.params = kwargs
self.client = get_api_client(**kwargs)
cluster_info = kwargs.get('cluster_info')
if cluster_info == 'version':
return [self.client.version]
if cluster_info == 'api_groups':
cluster_info = kwargs.get("cluster_info")
if cluster_info == "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)]
return [self.client.resources.api_groups]
self.kind = kwargs.get('kind')
self.name = kwargs.get('resource_name')
self.namespace = kwargs.get('namespace')
self.api_version = kwargs.get('api_version', 'v1')
self.label_selector = kwargs.get('label_selector')
self.field_selector = kwargs.get('field_selector')
self.include_uninitialized = kwargs.get('include_uninitialized', False)
self.kind = kwargs.get("kind")
self.name = kwargs.get("resource_name")
self.namespace = kwargs.get("namespace")
self.api_version = kwargs.get("api_version", "v1")
self.label_selector = kwargs.get("label_selector")
self.field_selector = kwargs.get("field_selector")
self.include_uninitialized = kwargs.get("include_uninitialized", False)
resource_definition = kwargs.get('resource_definition')
src = kwargs.get('src')
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)
self.name = resource_definition.get('metadata', {}).get('name', self.name)
self.namespace = resource_definition.get('metadata', {}).get('namespace', self.namespace)
self.kind = resource_definition.get("kind", self.kind)
self.api_version = resource_definition.get("apiVersion", self.api_version)
self.name = resource_definition.get("metadata", {}).get("name", self.name)
self.namespace = resource_definition.get("metadata", {}).get(
"namespace", self.namespace
)
if not self.kind:
raise AnsibleError(
@@ -258,19 +277,26 @@ 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(name=self.name, namespace=self.namespace, label_selector=self.label_selector, field_selector=self.field_selector)
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 []
if self.name:
return [k8s_obj.to_dict()]
return k8s_obj.to_dict().get('items')
return k8s_obj.to_dict().get("items")
class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):
def _run(self, terms, variables=None, **kwargs):
return KubernetesLookup().run(terms, variables=variables, **kwargs)
run = _run if not hasattr(LookupBase, "run_on_daemon") else LookupBase.run_on_daemon

View File

@@ -3,15 +3,15 @@
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
lookup: kustomize
DOCUMENTATION = """
name: kustomize
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>
- Aubin Bikouo (@abikouo)
notes:
- If both kustomize and kubectl are part of the PATH, kustomize will be used by the plugin.
description:
@@ -30,60 +30,54 @@ 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"
requirements:
- "python >= 3.6"
'''
"""
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) }}"
"""
RETURN = """
_list:
description:
- One ore more object definitions returned from the tool execution.
type: complex
contains:
api_version:
description: The versioned schema of this representation of an object.
returned: success
type: str
kind:
description: Represents the REST resource this object represents.
returned: success
type: str
- YAML string for the object definitions returned from the tool execution.
type: str
sample:
kind: ConfigMap
apiVersion: v1
metadata:
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
returned: success
type: complex
spec:
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
returned: success
type: complex
status:
description: Current status details for the object.
returned: success
type: complex
name: my-config-map
namespace: default
data:
key1: val1
"""
from ansible.errors import AnsibleLookupError
from ansible.plugins.lookup import LookupBase
from ansible.module_utils.common.process import get_bin_path
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 = {}
@@ -91,7 +85,7 @@ def get_binary_from_path(name, opt_dirs=None):
if opt_dirs is not None:
if not isinstance(opt_dirs, list):
opt_dirs = [opt_dirs]
opt_arg['opt_dirs'] = opt_dirs
opt_arg["opt_dirs"] = opt_dirs
bin_path = get_bin_path(name, **opt_arg)
return bin_path
except ValueError:
@@ -100,34 +94,65 @@ def get_binary_from_path(name, opt_dirs=None):
def run_command(command):
cmd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return cmd.communicate()
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):
def run(
self,
terms,
variables=None,
dir=".",
binary_path=None,
opt_dirs=None,
enable_helm=False,
**kwargs
):
executable_path = binary_path
if executable_path is None:
executable_path = get_binary_from_path(name="kustomize", opt_dirs=opt_dirs)
if executable_path is None:
executable_path = get_binary_from_path(name="kubectl", opt_dirs=opt_dirs)
executable_path = get_binary_from_path(
name="kubectl", opt_dirs=opt_dirs
)
# validate that at least one tool was found
if executable_path is None:
raise AnsibleLookupError("Failed to find required executable 'kubectl' and 'kustomize' in paths")
raise AnsibleLookupError(
"Failed to find required executable 'kubectl' and 'kustomize' in paths"
)
# check input directory
kustomization_dir = dir
command = [executable_path]
if executable_path.endswith('kustomize'):
command += ['build', kustomization_dir]
elif executable_path.endswith('kubectl'):
command += ['kustomize', kustomization_dir]
if executable_path.endswith("kustomize"):
command += ["build", kustomization_dir]
elif executable_path.endswith("kubectl"):
command += ["kustomize", kustomization_dir]
else:
raise AnsibleLookupError("unexpected tool provided as parameter {0}, expected one of kustomize, kubectl.".format(executable_path))
raise AnsibleLookupError(
"unexpected tool provided as parameter {0}, expected one of kustomize, kubectl.".format(
executable_path
)
)
(out, err) = run_command(command)
if err:
raise AnsibleLookupError("kustomize command failed with: {0}".format(err.decode("utf-8")))
return [out.decode('utf-8')]
if enable_helm:
command += ["--enable-helm"]
(ret, out, err) = run_command(command)
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")]

View File

@@ -0,0 +1,344 @@
# Vendored copy of distutils/version.py from CPython 3.9.5
#
# Implements multiple version numbering conventions for the
# Python Module Distribution Utilities.
#
# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0)
#
"""Provides classes to represent module version numbers (one class for
each style of version numbering). There are currently two such classes
implemented: StrictVersion and LooseVersion.
Every version number class implements the following interface:
* the 'parse' method takes a string and parses it to some internal
representation; if the string is an invalid version number,
'parse' raises a ValueError exception
* the class constructor takes an optional string argument which,
if supplied, is passed to 'parse'
* __str__ reconstructs the string that was passed to 'parse' (or
an equivalent string -- ie. one that will generate an equivalent
version number instance)
* __repr__ generates Python code to recreate the version number instance
* _cmp compares the current instance with either another instance
of the same class or a string (which will be parsed to an instance
of the same class, thus must follow the same rules)
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import re
try:
RE_FLAGS = re.VERBOSE | re.ASCII
except AttributeError:
RE_FLAGS = re.VERBOSE
class Version:
"""Abstract base class for version numbering classes. Just provides
constructor (__init__) and reproducer (__repr__), because those
seem to be the same for all version numbering classes; and route
rich comparisons to _cmp.
"""
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def __repr__(self):
return "%s ('%s')" % (self.__class__.__name__, str(self))
def __eq__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c == 0
def __lt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c < 0
def __le__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c <= 0
def __gt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c > 0
def __ge__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c >= 0
# Interface for version-number classes -- must be implemented
# by the following classes (the concrete ones -- Version should
# be treated as an abstract class).
# __init__ (string) - create and take same action as 'parse'
# (string parameter is optional)
# parse (string) - convert a string representation to whatever
# internal representation is appropriate for
# this style of version numbering
# __str__ (self) - convert back to a string; should be very similar
# (if not identical to) the string supplied to parse
# __repr__ (self) - generate Python code to recreate
# the instance
# _cmp (self, other) - compare two version numbers ('other' may
# be an unparsed version string, or another
# instance of your version class)
class StrictVersion(Version):
"""Version numbering for anal retentives and software idealists.
Implements the standard interface for version number classes as
described above. A version number consists of two or three
dot-separated numeric components, with an optional "pre-release" tag
on the end. The pre-release tag consists of the letter 'a' or 'b'
followed by a number. If the numeric components of two version
numbers are equal, then one with a pre-release tag will always
be deemed earlier (lesser) than one without.
The following are valid version numbers (shown in the order that
would be obtained by sorting according to the supplied cmp function):
0.4 0.4.0 (these two are equivalent)
0.4.1
0.5a1
0.5b3
0.5
0.9.6
1.0
1.0.4a3
1.0.4b1
1.0.4
The following are examples of invalid version numbers:
1
2.7.2.2
1.3.a4
1.3pl1
1.3c4
The rationale for this version numbering system will be explained
in the distutils documentation.
"""
version_re = re.compile(r"^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$", RE_FLAGS)
def parse(self, vstring):
match = self.version_re.match(vstring)
if not match:
raise ValueError("invalid version number '%s'" % vstring)
(major, minor, patch, prerelease, prerelease_num) = match.group(1, 2, 4, 5, 6)
if patch:
self.version = tuple(map(int, [major, minor, patch]))
else:
self.version = tuple(map(int, [major, minor])) + (0,)
if prerelease:
self.prerelease = (prerelease[0], int(prerelease_num))
else:
self.prerelease = None
def __str__(self):
if self.version[2] == 0:
vstring = ".".join(map(str, self.version[0:2]))
else:
vstring = ".".join(map(str, self.version))
if self.prerelease:
vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
return vstring
def _cmp(self, other):
if isinstance(other, str):
other = StrictVersion(other)
elif not isinstance(other, StrictVersion):
return NotImplemented
if self.version != other.version:
# numeric versions don't match
# prerelease stuff doesn't matter
if self.version < other.version:
return -1
else:
return 1
# have to compare prerelease
# case 1: neither has prerelease; they're equal
# case 2: self has prerelease, other doesn't; other is greater
# case 3: self doesn't have prerelease, other does: self is greater
# case 4: both have prerelease: must compare them!
if not self.prerelease and not other.prerelease:
return 0
elif self.prerelease and not other.prerelease:
return -1
elif not self.prerelease and other.prerelease:
return 1
elif self.prerelease and other.prerelease:
if self.prerelease == other.prerelease:
return 0
elif self.prerelease < other.prerelease:
return -1
else:
return 1
else:
raise AssertionError("never get here")
# end class StrictVersion
# The rules according to Greg Stein:
# 1) a version number has 1 or more numbers separated by a period or by
# sequences of letters. If only periods, then these are compared
# left-to-right to determine an ordering.
# 2) sequences of letters are part of the tuple for comparison and are
# compared lexicographically
# 3) recognize the numeric components may have leading zeroes
#
# The LooseVersion class below implements these rules: a version number
# string is split up into a tuple of integer and string components, and
# comparison is a simple tuple comparison. This means that version
# numbers behave in a predictable and obvious way, but a way that might
# not necessarily be how people *want* version numbers to behave. There
# wouldn't be a problem if people could stick to purely numeric version
# numbers: just split on period and compare the numbers as tuples.
# However, people insist on putting letters into their version numbers;
# the most common purpose seems to be:
# - indicating a "pre-release" version
# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
# - indicating a post-release patch ('p', 'pl', 'patch')
# but of course this can't cover all version number schemes, and there's
# no way to know what a programmer means without asking him.
#
# The problem is what to do with letters (and other non-numeric
# characters) in a version number. The current implementation does the
# obvious and predictable thing: keep them as strings and compare
# lexically within a tuple comparison. This has the desired effect if
# an appended letter sequence implies something "post-release":
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
#
# However, if letters in a version number imply a pre-release version,
# the "obvious" thing isn't correct. Eg. you would expect that
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
# implemented here, this just isn't so.
#
# Two possible solutions come to mind. The first is to tie the
# comparison algorithm to a particular set of semantic rules, as has
# been done in the StrictVersion class above. This works great as long
# as everyone can go along with bondage and discipline. Hopefully a
# (large) subset of Python module programmers will agree that the
# particular flavour of bondage and discipline provided by StrictVersion
# provides enough benefit to be worth using, and will submit their
# version numbering scheme to its domination. The free-thinking
# anarchists in the lot will never give in, though, and something needs
# to be done to accommodate them.
#
# Perhaps a "moderately strict" version class could be implemented that
# lets almost anything slide (syntactically), and makes some heuristic
# assumptions about non-digits in version number strings. This could
# sink into special-case-hell, though; if I was as talented and
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
# just as happy dealing with things like "2g6" and "1.13++". I don't
# think I'm smart enough to do it right though.
#
# In any case, I've coded the test suite for this module (see
# ../test/test_version.py) specifically to fail on things like comparing
# "1.2a2" and "1.2". That's not because the *code* is doing anything
# wrong, it's because the simple, obvious design doesn't match my
# complicated, hairy expectations for real-world version numbers. It
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
# the Right Thing" (ie. the code matches the conception). But I'd rather
# have a conception that matches common notions about version numbers.
class LooseVersion(Version):
"""Version numbering for anarchists and software realists.
Implements the standard interface for version number classes as
described above. A version number consists of a series of numbers,
separated by either periods or strings of letters. When comparing
version numbers, the numeric components will be compared
numerically, and the alphabetic components lexically. The following
are all valid version numbers, in no particular order:
1.5.1
1.5.2b2
161
3.10a
8.02
3.4j
1996.07.12
3.2.pl0
3.1.1.6
2g6
11g
0.960923
2.2beta29
1.13++
5.5.kw
2.0b1pl0
In fact, there is no such thing as an invalid version number under
this scheme; the rules for comparison are simple and predictable,
but may not always give the results you want (for some definition
of "want").
"""
component_re = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE)
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def parse(self, vstring):
# I've given up on thinking I can reconstruct the version string
# from the parsed tuple -- so I just store the string here for
# use by __str__
self.vstring = vstring
components = [x for x in self.component_re.split(vstring) if x and x != "."]
for i, obj in enumerate(components):
try:
components[i] = int(obj)
except ValueError:
pass
self.version = components
def __str__(self):
return self.vstring
def __repr__(self):
return "LooseVersion ('%s')" % str(self)
def _cmp(self, other):
if isinstance(other, str):
other = LooseVersion(other)
elif not isinstance(other, LooseVersion):
return NotImplemented
if self.version == other.version:
return 0
if self.version < other.version:
return -1
if self.version > other.version:
return 1
# end class LooseVersion

View File

@@ -1,4 +1,4 @@
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
@@ -14,9 +14,10 @@ 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:
from ansible.module_utils.basic import AnsibleModule # noqa: F401

View File

@@ -14,13 +14,22 @@
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.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
@@ -28,50 +37,52 @@ except ImportError:
pass
LAST_APPLIED_CONFIG_ANNOTATION = 'kubectl.kubernetes.io/last-applied-configuration'
LAST_APPLIED_CONFIG_ANNOTATION = "kubectl.kubernetes.io/last-applied-configuration"
POD_SPEC_SUFFIXES = {
'containers': 'name',
'initContainers': 'name',
'ephemeralContainers': 'name',
'volumes': 'name',
'imagePullSecrets': 'name',
'containers.volumeMounts': 'mountPath',
'containers.volumeDevices': 'devicePath',
'containers.env': 'name',
'containers.ports': 'containerPort',
'initContainers.volumeMounts': 'mountPath',
'initContainers.volumeDevices': 'devicePath',
'initContainers.env': 'name',
'initContainers.ports': 'containerPort',
'ephemeralContainers.volumeMounts': 'mountPath',
'ephemeralContainers.volumeDevices': 'devicePath',
'ephemeralContainers.env': 'name',
'ephemeralContainers.ports': 'containerPort',
"containers": "name",
"initContainers": "name",
"ephemeralContainers": "name",
"volumes": "name",
"imagePullSecrets": "name",
"containers.volumeMounts": "mountPath",
"containers.volumeDevices": "devicePath",
"containers.env": "name",
"containers.ports": "containerPort",
"initContainers.volumeMounts": "mountPath",
"initContainers.volumeDevices": "devicePath",
"initContainers.env": "name",
"initContainers.ports": "containerPort",
"ephemeralContainers.volumeMounts": "mountPath",
"ephemeralContainers.volumeDevices": "devicePath",
"ephemeralContainers.env": "name",
"ephemeralContainers.ports": "containerPort",
}
POD_SPEC_PREFIXES = [
'Pod.spec',
'Deployment.spec.template.spec',
'DaemonSet.spec.template.spec',
'StatefulSet.spec.template.spec',
'Job.spec.template.spec',
'Cronjob.spec.jobTemplate.spec.template.spec',
"Pod.spec",
"Deployment.spec.template.spec",
"DaemonSet.spec.template.spec",
"StatefulSet.spec.template.spec",
"Job.spec.template.spec",
"Cronjob.spec.jobTemplate.spec.template.spec",
]
# patch merge keys taken from generated.proto files under
# staging/src/k8s.io/api in kubernetes/kubernetes
STRATEGIC_MERGE_PATCH_KEYS = {
'Service.spec.ports': 'port',
'ServiceAccount.secrets': 'name',
'ValidatingWebhookConfiguration.webhooks': 'name',
'MutatingWebhookConfiguration.webhooks': 'name',
"Service.spec.ports": "port",
"ServiceAccount.secrets": "name",
"ValidatingWebhookConfiguration.webhooks": "name",
"MutatingWebhookConfiguration.webhooks": "name",
}
STRATEGIC_MERGE_PATCH_KEYS.update(
{"%s.%s" % (prefix, key): value
for prefix in POD_SPEC_PREFIXES
for key, value in POD_SPEC_SUFFIXES.items()}
{
"%s.%s" % (prefix, key): value
for prefix in POD_SPEC_PREFIXES
for key, value in POD_SPEC_SUFFIXES.items()
}
)
@@ -79,21 +90,28 @@ def annotate(desired):
return dict(
metadata=dict(
annotations={
LAST_APPLIED_CONFIG_ANNOTATION: json.dumps(desired, separators=(',', ':'), indent=None, sort_keys=True)
LAST_APPLIED_CONFIG_ANNOTATION: json.dumps(
desired, separators=(",", ":"), indent=None, sort_keys=True
)
}
)
)
def apply_patch(actual, desired):
last_applied = actual['metadata'].get('annotations', {}).get(LAST_APPLIED_CONFIG_ANNOTATION)
last_applied = (
actual["metadata"].get("annotations", {}).get(LAST_APPLIED_CONFIG_ANNOTATION)
)
if last_applied:
# ensure that last_applied doesn't come back as a dict of unicode key/value pairs
# json.loads can be used if we stop supporting python 2
last_applied = json.loads(last_applied)
patch = merge(dict_merge(last_applied, annotate(last_applied)),
dict_merge(desired, annotate(desired)), actual)
patch = merge(
dict_merge(last_applied, annotate(last_applied)),
dict_merge(desired, annotate(desired)),
actual,
)
if patch:
return actual, patch
else:
@@ -102,24 +120,52 @@ def apply_patch(actual, desired):
return actual, dict_merge(desired, annotate(desired))
def apply_object(resource, definition):
def apply_object(resource, definition, server_side=False):
try:
actual = resource.get(name=definition['metadata']['name'], namespace=definition['metadata'].get('namespace'))
actual = resource.get(
name=definition["metadata"]["name"],
namespace=definition["metadata"].get("namespace"),
)
if server_side:
return actual, None
except NotFoundError:
return None, dict_merge(definition, annotate(definition))
return apply_patch(actual.to_dict(), definition)
def k8s_apply(resource, definition):
def k8s_apply(resource, definition, **kwargs):
existing, desired = apply_object(resource, definition)
server_side = kwargs.get("server_side", False)
if server_side:
versions = gather_versions()
body = definition
if LooseVersion(versions["kubernetes"]) < LooseVersion("25.0.0"):
body = json.dumps(definition).encode()
# server_side_apply is forces content_type to 'application/apply-patch+yaml'
return resource.server_side_apply(
body=body,
name=definition["metadata"]["name"],
namespace=definition["metadata"].get("namespace"),
force_conflicts=kwargs.get("force_conflicts"),
field_manager=kwargs.get("field_manager"),
dry_run=kwargs.get("dry_run"),
)
if not existing:
return resource.create(body=desired, namespace=definition['metadata'].get('namespace'))
return resource.create(
body=desired, namespace=definition["metadata"].get("namespace"), **kwargs
)
if existing == desired:
return resource.get(name=definition['metadata']['name'], namespace=definition['metadata'].get('namespace'))
return resource.patch(body=desired,
name=definition['metadata']['name'],
namespace=definition['metadata'].get('namespace'),
content_type='application/merge-patch+json')
return resource.get(
name=definition["metadata"]["name"],
namespace=definition["metadata"].get("namespace"),
)
return resource.patch(
body=desired,
name=definition["metadata"]["name"],
namespace=definition["metadata"].get("namespace"),
content_type="application/merge-patch+json",
**kwargs
)
# The patch is the difference from actual to desired without deletions, plus deletions
@@ -128,7 +174,7 @@ def k8s_apply(resource, definition):
# deletions, and then apply delta to deletions as a patch, which should be strictly additive.
def merge(last_applied, desired, actual, position=None):
deletions = get_deletions(last_applied, desired)
delta = get_delta(last_applied, actual, desired, position or desired['kind'])
delta = get_delta(last_applied, actual, desired, position or desired["kind"])
return dict_merge(deletions, delta)
@@ -138,7 +184,9 @@ def list_to_dict(lst, key, position):
try:
result[item[key]] = item
except KeyError:
raise ApplyException("Expected key '%s' not found in position %s" % (key, position))
raise ApplyException(
"Expected key '%s' not found in position %s" % (key, position)
)
return result
@@ -157,7 +205,12 @@ def list_merge(last_applied, actual, desired, position):
if key not in actual_dict or key not in last_applied_dict:
result.append(desired_dict[key])
else:
patch = merge(last_applied_dict[key], desired_dict[key], actual_dict[key], position)
patch = merge(
last_applied_dict[key],
desired_dict[key],
actual_dict[key],
position,
)
result.append(dict_merge(actual_dict[key], patch))
for key in actual_dict:
if key not in desired_dict and key not in last_applied_dict:
@@ -197,11 +250,11 @@ def recursive_list_diff(list1, list2, position=None):
def recursive_diff(dict1, dict2, position=None):
if not position:
if 'kind' in dict1 and dict1.get('kind') == dict2.get('kind'):
position = dict1['kind']
if "kind" in dict1 and dict1.get("kind") == dict2.get("kind"):
position = dict1["kind"]
left = dict((k, v) for (k, v) in dict1.items() if k not in dict2)
right = dict((k, v) for (k, v) in dict2.items() if k not in dict1)
for k in (set(dict1.keys()) & set(dict2.keys())):
for k in set(dict1.keys()) & set(dict2.keys()):
if position:
this_position = "%s.%s" % (position, k)
if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
@@ -246,11 +299,15 @@ def get_delta(last_applied, actual, desired, position=None):
if actual_value is None:
patch[k] = desired_value
elif isinstance(desired_value, dict):
p = get_delta(last_applied.get(k, {}), actual_value, desired_value, this_position)
p = get_delta(
last_applied.get(k, {}), actual_value, desired_value, this_position
)
if p:
patch[k] = p
elif isinstance(desired_value, list):
p = list_merge(last_applied.get(k, []), actual_value, desired_value, this_position)
p = list_merge(
last_applied.get(k, []), actual_value, desired_value, this_position
)
if p:
patch[k] = [item for item in p if item is not None]
elif actual_value != desired_value:

View File

@@ -1,4 +1,4 @@
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
from ansible.module_utils.six import string_types
@@ -12,133 +12,87 @@ def list_dict_str(value):
AUTH_PROXY_HEADERS_SPEC = dict(
proxy_basic_auth=dict(type='str', no_log=True),
basic_auth=dict(type='str', no_log=True),
user_agent=dict(type='str')
proxy_basic_auth=dict(type="str", no_log=True),
basic_auth=dict(type="str", no_log=True),
user_agent=dict(type="str"),
)
AUTH_ARG_SPEC = {
'kubeconfig': {
'type': 'raw',
},
'context': {},
'host': {},
'api_key': {
'no_log': True,
},
'username': {},
'password': {
'no_log': True,
},
'validate_certs': {
'type': 'bool',
'aliases': ['verify_ssl'],
},
'ca_cert': {
'type': 'path',
'aliases': ['ssl_ca_cert'],
},
'client_cert': {
'type': 'path',
'aliases': ['cert_file'],
},
'client_key': {
'type': 'path',
'aliases': ['key_file'],
},
'proxy': {
'type': 'str',
},
'proxy_headers': {
'type': 'dict',
'options': AUTH_PROXY_HEADERS_SPEC
},
'persist_config': {
'type': 'bool',
},
"kubeconfig": {"type": "raw", "no_log": True},
"context": {},
"host": {},
"api_key": {"no_log": True},
"username": {},
"password": {"no_log": True},
"validate_certs": {"type": "bool", "aliases": ["verify_ssl"]},
"ca_cert": {"type": "path", "aliases": ["ssl_ca_cert"]},
"client_cert": {"type": "path", "aliases": ["cert_file"]},
"client_key": {"type": "path", "aliases": ["key_file"]},
"proxy": {"type": "str"},
"no_proxy": {"type": "str"},
"proxy_headers": {"type": "dict", "options": AUTH_PROXY_HEADERS_SPEC},
"persist_config": {"type": "bool"},
"impersonate_user": {},
"impersonate_groups": {"type": "list", "elements": "str"},
}
WAIT_ARG_SPEC = dict(
wait=dict(type='bool', default=False),
wait_sleep=dict(type='int', default=5),
wait_timeout=dict(type='int', default=120),
wait=dict(type="bool", default=False),
wait_sleep=dict(type="int", default=5),
wait_timeout=dict(type="int", default=120),
wait_condition=dict(
type='dict',
type="dict",
default=None,
options=dict(
type=dict(),
status=dict(default=True, choices=[True, False, "Unknown"]),
reason=dict()
)
)
reason=dict(),
),
),
)
# Map kubernetes-client parameters to ansible parameters
AUTH_ARG_MAP = {
'kubeconfig': 'kubeconfig',
'context': 'context',
'host': 'host',
'api_key': 'api_key',
'username': 'username',
'password': 'password',
'verify_ssl': 'validate_certs',
'ssl_ca_cert': 'ca_cert',
'cert_file': 'client_cert',
'key_file': 'client_key',
'proxy': 'proxy',
'proxy_headers': 'proxy_headers',
'persist_config': 'persist_config',
"kubeconfig": "kubeconfig",
"context": "context",
"host": "host",
"api_key": "api_key",
"username": "username",
"password": "password",
"verify_ssl": "validate_certs",
"ssl_ca_cert": "ca_cert",
"cert_file": "client_cert",
"key_file": "client_key",
"proxy": "proxy",
"no_proxy": "no_proxy",
"proxy_headers": "proxy_headers",
"persist_config": "persist_config",
}
NAME_ARG_SPEC = {
'kind': {},
'name': {},
'namespace': {},
'api_version': {
'default': 'v1',
'aliases': ['api', 'version'],
},
"kind": {},
"name": {},
"namespace": {},
"api_version": {"default": "v1", "aliases": ["api", "version"]},
}
COMMON_ARG_SPEC = {
'state': {
'default': 'present',
'choices': ['present', 'absent'],
},
'force': {
'type': 'bool',
'default': False,
},
"state": {"default": "present", "choices": ["present", "absent"]},
"force": {"type": "bool", "default": False},
}
RESOURCE_ARG_SPEC = {
'resource_definition': {
'type': list_dict_str,
'aliases': ['definition', 'inline']
},
'src': {
'type': 'path',
},
"resource_definition": {"type": list_dict_str, "aliases": ["definition", "inline"]},
"src": {"type": "path"},
}
ARG_ATTRIBUTES_BLACKLIST = ('property_path',)
ARG_ATTRIBUTES_BLACKLIST = ("property_path",)
DELETE_OPTS_ARG_SPEC = {
'propagationPolicy': {
'choices': ['Foreground', 'Background', 'Orphan'],
"propagationPolicy": {"choices": ["Foreground", "Background", "Orphan"]},
"gracePeriodSeconds": {"type": "int"},
"preconditions": {
"type": "dict",
"options": {"resourceVersion": {"type": "str"}, "uid": {"type": "str"}},
},
'gracePeriodSeconds': {
'type': 'int',
},
'preconditions': {
'type': 'dict',
'options': {
'resourceVersion': {
'type': 'str',
},
'uid': {
'type': 'str',
}
}
}
}

View File

@@ -13,27 +13,35 @@
# 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, ResourceNotUniqueError,
ServiceUnavailableError)
from ansible_collections.kubernetes.core.plugins.module_utils.client.resource import ResourceList
from kubernetes.dynamic.exceptions import (
ResourceNotFoundError,
ResourceNotUniqueError,
ServiceUnavailableError,
)
class Discoverer(kubernetes.dynamic.discovery.Discoverer):
def __init__(self, client, cache_file):
self.client = client
default_cache_file_name = 'k8srcp-{0}.json'.format(hashlib.sha256(self.__get_default_cache_id()).hexdigest())
self.__cache_file = cache_file or os.path.join(tempfile.gettempdir(), default_cache_file_name)
default_cache_file_name = "k8srcp-{0}.json".format(
hashlib.sha256(self.__get_default_cache_id()).hexdigest()
)
self.__cache_file = cache_file or os.path.join(
tempfile.gettempdir(), default_cache_file_name
)
self.__init_cache()
def __get_default_cache_id(self):
@@ -42,21 +50,21 @@ class Discoverer(kubernetes.dynamic.discovery.Discoverer):
cache_id = "{0}-{1}".format(self.client.configuration.host, user)
else:
cache_id = self.client.configuration.host
return cache_id.encode('utf-8')
return cache_id.encode("utf-8")
def __get_user(self):
# This is intended to provide a portable method for getting a username.
# It could, and maybe should, be replaced by getpass.getuser() but, due
# to a lack of portability testing the original code is being left in
# place.
if hasattr(os, 'getlogin'):
if hasattr(os, "getlogin"):
try:
user = os.getlogin()
if user:
return str(user)
except OSError:
pass
if hasattr(os, 'getuid'):
if hasattr(os, "getuid"):
try:
user = os.getuid()
if user:
@@ -70,13 +78,13 @@ class Discoverer(kubernetes.dynamic.discovery.Discoverer):
def __init_cache(self, refresh=False):
if refresh or not os.path.exists(self.__cache_file):
self._cache = {'library_version': __version__}
self._cache = {"library_version": __version__}
refresh = True
else:
try:
with open(self.__cache_file, 'r') as f:
with open(self.__cache_file, "r") as f:
self._cache = json.load(f, cls=partial(CacheDecoder, self.client))
if self._cache.get('library_version') != __version__:
if self._cache.get("library_version") != __version__:
# Version mismatch, need to refresh cache
self.invalidate_cache()
except Exception:
@@ -87,26 +95,30 @@ class Discoverer(kubernetes.dynamic.discovery.Discoverer):
self._write_cache()
def get_resources_for_api_version(self, prefix, group, version, preferred):
""" returns a dictionary of resources associated with provided (prefix, group, version)"""
"""returns a dictionary of resources associated with provided (prefix, group, version)"""
resources = defaultdict(list)
subresources = defaultdict(dict)
path = '/'.join(filter(None, [prefix, group, version]))
path = "/".join(filter(None, [prefix, group, version]))
try:
resources_response = self.client.request('GET', path).resources or []
resources_response = self.client.request("GET", path).resources or []
except ServiceUnavailableError:
resources_response = []
resources_raw = list(filter(lambda resource: '/' not in resource['name'], resources_response))
subresources_raw = list(filter(lambda resource: '/' in resource['name'], resources_response))
resources_raw = list(
filter(lambda resource: "/" not in resource["name"], resources_response)
)
subresources_raw = list(
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:
# Prevent duplicate keys
for key in ('prefix', 'group', 'api_version', 'client', 'preferred'):
for key in ("prefix", "group", "api_version", "client", "preferred"):
resource.pop(key, None)
resourceobj = kubernetes.dynamic.Resource(
@@ -115,19 +127,25 @@ class Discoverer(kubernetes.dynamic.discovery.Discoverer):
api_version=version,
client=self.client,
preferred=preferred,
subresources=subresources.get(resource['name']),
subresources=subresources.get(resource["name"]),
**resource
)
resources[resource['kind']].append(resourceobj)
resources[resource["kind"]].append(resourceobj)
resource_lookup = {
'prefix': prefix,
'group': group,
'api_version': version,
'kind': resourceobj.kind,
'name': resourceobj.name
"prefix": prefix,
"group": group,
"api_version": version,
"kind": resourceobj.kind,
"name": resourceobj.name,
}
resource_list = ResourceList(self.client, group=group, api_version=version, base_kind=resource['kind'], base_resource_lookup=resource_lookup)
resource_list = ResourceList(
self.client,
group=group,
api_version=version,
base_kind=resource["kind"],
base_resource_lookup=resource_lookup,
)
resources[resource_list.kind].append(resource_list)
return resources
@@ -139,23 +157,32 @@ class Discoverer(kubernetes.dynamic.discovery.Discoverer):
"""
results = self.search(**kwargs)
# If there are multiple matches, prefer exact matches on api_version
if len(results) > 1 and kwargs.get('api_version'):
if len(results) > 1 and kwargs.get("api_version"):
results = [
result for result in results if result.group_version == kwargs['api_version']
result
for result in results
if result.group_version == kwargs["api_version"]
]
# If there are multiple matches, prefer non-List kinds
if len(results) > 1 and not all(isinstance(x, ResourceList) for x in results):
results = [result for result in results if not isinstance(result, ResourceList)]
results = [
result for result in results if not isinstance(result, ResourceList)
]
# if multiple resources are found that share a GVK, prefer the one with the most supported verbs
if len(results) > 1 and len(set((x.group_version, x.kind) for x in results)) == 1:
if (
len(results) > 1
and len(set((x.group_version, x.kind) for x in results)) == 1
):
if len(set(len(x.verbs) for x in results)) != 1:
results = [max(results, key=lambda x: len(x.verbs))]
if len(results) == 1:
return results[0]
elif not results:
raise ResourceNotFoundError('No matches found for {0}'.format(kwargs))
raise ResourceNotFoundError("No matches found for {0}".format(kwargs))
else:
raise ResourceNotUniqueError('Multiple matches found for {0}: {1}'.format(kwargs, results))
raise ResourceNotUniqueError(
"Multiple matches found for {0}: {1}".format(kwargs, results)
)
class LazyDiscoverer(Discoverer, kubernetes.dynamic.LazyDiscoverer):
@@ -174,13 +201,15 @@ class CacheDecoder(json.JSONDecoder):
json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, obj):
if '_type' not in obj:
if "_type" not in obj:
return obj
_type = obj.pop('_type')
if _type == 'Resource':
_type = obj.pop("_type")
if _type == "Resource":
return kubernetes.dynamic.Resource(client=self.client, **obj)
elif _type == 'ResourceList':
elif _type == "ResourceList":
return ResourceList(self.client, **obj)
elif _type == 'ResourceGroup':
return kubernetes.dynamic.discovery.ResourceGroup(obj['preferred'], resources=self.object_hook(obj['resources']))
elif _type == "ResourceGroup":
return kubernetes.dynamic.discovery.ResourceGroup(
obj["preferred"], resources=self.object_hook(obj["resources"])
)
return obj

View File

@@ -14,6 +14,7 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
@@ -21,11 +22,19 @@ import kubernetes.dynamic
class ResourceList(kubernetes.dynamic.resource.ResourceList):
def __init__(self, client, group='', api_version='v1', base_kind='', kind=None, base_resource_lookup=None):
def __init__(
self,
client,
group="",
api_version="v1",
base_kind="",
kind=None,
base_resource_lookup=None,
):
self.client = client
self.group = group
self.api_version = api_version
self.kind = kind or '{0}List'.format(base_kind)
self.kind = kind or "{0}List".format(base_kind)
self.base_kind = base_kind
self.base_resource_lookup = base_resource_lookup
self.__base_resource = None
@@ -34,16 +43,18 @@ class ResourceList(kubernetes.dynamic.resource.ResourceList):
if self.__base_resource:
return self.__base_resource
elif self.base_resource_lookup:
self.__base_resource = self.client.resources.get(**self.base_resource_lookup)
self.__base_resource = self.client.resources.get(
**self.base_resource_lookup
)
return self.__base_resource
return None
def to_dict(self):
return {
'_type': 'ResourceList',
'group': self.group,
'api_version': self.api_version,
'kind': self.kind,
'base_kind': self.base_kind,
'base_resource_lookup': self.base_resource_lookup
"_type": "ResourceList",
"group": self.group,
"api_version": self.api_version,
"kind": self.kind,
"base_kind": self.base_kind,
"base_resource_lookup": self.base_resource_lookup,
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,445 @@
# Copyright [2021] [Red Hat, Inc.]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os
import tarfile
from abc import ABCMeta, abstractmethod
from select import select
from tempfile import NamedTemporaryFile, TemporaryFile
from ansible.module_utils._text import to_native
# from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import AnsibleModule
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
try:
from kubernetes.client.api import core_v1_api
from kubernetes.stream import stream
from kubernetes.stream.ws_client import (
ABNF,
ERROR_CHANNEL,
STDERR_CHANNEL,
STDOUT_CHANNEL,
)
except ImportError:
pass
try:
import yaml
except ImportError:
# ImportError are managed by the common module already.
pass
class K8SCopy(metaclass=ABCMeta):
def __init__(self, module, client):
self.client = client
self.module = module
self.api_instance = core_v1_api.CoreV1Api(client.client)
self.local_path = module.params.get("local_path")
self.name = module.params.get("pod")
self.namespace = module.params.get("namespace")
self.remote_path = module.params.get("remote_path")
self.content = module.params.get("content")
self.no_preserve = module.params.get("no_preserve")
self.container_arg = {}
if module.params.get("container"):
self.container_arg["container"] = module.params.get("container")
self.check_mode = self.module.check_mode
def _run_from_pod(self, cmd):
try:
resp = stream(
self.api_instance.connect_get_namespaced_pod_exec,
self.name,
self.namespace,
command=cmd,
async_req=False,
stderr=True,
stdin=False,
stdout=True,
tty=False,
_preload_content=False,
**self.container_arg,
)
stderr, stdout = [], []
while resp.is_open():
resp.update(timeout=1)
if resp.peek_stdout():
stdout.extend(resp.read_stdout().rstrip("\n").split("\n"))
if resp.peek_stderr():
stderr.extend(resp.read_stderr().rstrip("\n").split("\n"))
error = resp.read_channel(ERROR_CHANNEL)
resp.close()
error = yaml.safe_load(error)
return error, stdout, stderr
except Exception as e:
self.module.fail_json(
msg="Error while running/parsing from pod {1}/{2} command='{0}' : {3}".format(
self.namespace, self.name, cmd, to_native(e)
)
)
def is_directory_path_from_pod(self, file_path, failed_if_not_exists=True):
# check if file exists
error, out, err = self._run_from_pod(cmd=["test", "-e", file_path])
if error.get("status") != "Success":
if failed_if_not_exists:
return None, "%s does not exist in remote pod filesystem" % file_path
return False, None
error, out, err = self._run_from_pod(cmd=["test", "-d", file_path])
return error.get("status") == "Success", None
@abstractmethod
def run(self):
pass
class K8SCopyFromPod(K8SCopy):
"""
Copy files/directory from Pod into local filesystem
"""
def __init__(self, module, client):
super(K8SCopyFromPod, self).__init__(module, client)
self.is_remote_path_dir = None
self.files_to_copy = []
self._shellname = None
@property
def pod_shell(self):
if self._shellname is None:
for s in ("/bin/sh", "/bin/bash"):
error, out, err = self._run_from_pod(s)
if error.get("status") == "Success":
self._shellname = s
break
return self._shellname
def listfiles_with_find(self, path):
find_cmd = ["find", path, "-type", "f"]
error, files, err = self._run_from_pod(cmd=find_cmd)
if error.get("status") != "Success":
self.module.fail_json(msg=error.get("message"))
return files
def listfile_with_echo(self, path):
echo_cmd = [
self.pod_shell,
"-c",
"echo {path}/* {path}/.*".format(
path=path.translate(str.maketrans({" ": r"\ "}))
),
]
error, out, err = self._run_from_pod(cmd=echo_cmd)
if error.get("status") != "Success":
self.module.fail_json(msg=error.get("message"))
files = []
if out:
output = out[0] + " "
files = [
os.path.join(path, p[:-1])
for p in output.split(f"{path}/")
if p and p[:-1] not in (".", "..")
]
result = []
for f in files:
is_dir, err = self.is_directory_path_from_pod(f)
if err:
continue
if not is_dir:
result.append(f)
continue
result += self.listfile_with_echo(f)
return result
def list_remote_files(self):
"""
This method will check if the remote path is a dir or file
if it is a directory the file list will be updated accordingly
"""
# check is remote path exists and is a file or directory
is_dir, error = self.is_directory_path_from_pod(self.remote_path)
if error:
self.module.fail_json(msg=error)
if not is_dir:
return [self.remote_path]
else:
# find executable to list dir with
executables = dict(
find=self.listfiles_with_find,
echo=self.listfile_with_echo,
)
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
self.stderr = None
if self.response.is_open():
if not self.response.sock.connected:
self.response._connected = False
else:
ret, out, err = select((self.response.sock.sock,), (), (), 0)
if ret:
code, frame = self.response.sock.recv_data_frame(True)
if code == ABNF.OPCODE_CLOSE:
self.response._connected = False
elif (
code in (ABNF.OPCODE_BINARY, ABNF.OPCODE_TEXT)
and len(frame.data) > 1
):
channel = frame.data[0]
content = frame.data[1:]
if content:
if channel == STDOUT_CHANNEL:
self.stdout = content
elif channel == STDERR_CHANNEL:
self.stderr = content.decode("utf-8", "replace")
def copy(self):
is_remote_path_dir = (
len(self.files_to_copy) > 1 or self.files_to_copy[0] != self.remote_path
)
relpath_start = self.remote_path
if is_remote_path_dir and os.path.isdir(self.local_path):
relpath_start = os.path.dirname(self.remote_path)
if not self.check_mode:
for remote_file in self.files_to_copy:
dest_file = self.local_path
if is_remote_path_dir:
dest_file = os.path.join(
self.local_path,
os.path.relpath(remote_file, start=relpath_start),
)
# create directory to copy file in
os.makedirs(os.path.dirname(dest_file), exist_ok=True)
pod_command = ["cat", remote_file]
self.response = stream(
self.api_instance.connect_get_namespaced_pod_exec,
self.name,
self.namespace,
command=pod_command,
stderr=True,
stdin=True,
stdout=True,
tty=False,
_preload_content=False,
**self.container_arg,
)
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(
self.remote_path, self.local_path
),
)
def run(self):
self.files_to_copy = self.list_remote_files()
if self.files_to_copy == []:
self.module.exit_json(
changed=False,
warning="No file found from directory '{0}' into remote Pod.".format(
self.remote_path
),
)
self.copy()
class K8SCopyToPod(K8SCopy):
"""
Copy files/directory from local filesystem into remote Pod
"""
def __init__(self, module, client):
super(K8SCopyToPod, self).__init__(module, client)
self.files_to_copy = list()
def close_temp_file(self):
if self.named_temp_file:
self.named_temp_file.close()
def run(self):
# remove trailing slash from destination path
dest_file = self.remote_path.rstrip("/")
src_file = self.local_path
self.named_temp_file = None
if self.content:
self.named_temp_file = NamedTemporaryFile(mode="w")
self.named_temp_file.write(self.content)
self.named_temp_file.flush()
src_file = self.named_temp_file.name
else:
if not os.path.exists(self.local_path):
self.module.fail_json(
msg="{0} does not exist in local filesystem".format(self.local_path)
)
if not os.access(self.local_path, os.R_OK):
self.module.fail_json(msg="{0} not readable".format(self.local_path))
is_dir, err = self.is_directory_path_from_pod(
self.remote_path, failed_if_not_exists=False
)
if err:
self.module.fail_json(msg=err)
if is_dir:
if self.content:
self.module.fail_json(
msg="When content is specified, remote path should not be an existing directory"
)
else:
dest_file = os.path.join(dest_file, os.path.basename(src_file))
if not self.check_mode:
if self.no_preserve:
tar_command = [
"tar",
"--no-same-permissions",
"--no-same-owner",
"-xmf",
"-",
]
else:
tar_command = ["tar", "-xmf", "-"]
if 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)
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,
result="Content successfully copied into {0} on remote Pod".format(
self.remote_path
),
)
self.module.exit_json(
changed=True,
result="{0} successfully copied into remote Pod into {1}".format(
self.local_path, self.remote_path
),
)
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"):
msg = (
"Namespace={0} Kind=Pod Name={1}: Failed requested object: {2}".format(
namespace, name, exc.body
)
)
else:
msg = to_native(exc)
for attr in ["status", "reason"]:
if hasattr(exc, attr):
arg[attr] = getattr(exc, attr)
module.fail_json(msg=msg, **arg)
try:
result = svc.client.get(resource, name=name, namespace=namespace)
containers = [
c["name"] for c in result.to_dict()["status"]["containerStatuses"]
]
if container and container not in containers:
module.fail_json(msg="Pod has no container {0}".format(container))
return containers
except Exception as exc:
_fail(exc)

View File

@@ -14,8 +14,9 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ApplyException(Exception):
""" Could not apply patch """
"""Could not apply patch"""

View File

@@ -15,14 +15,16 @@
# Implement ConfigMapHash and SecretHash equivalents
# Based on https://github.com/kubernetes/kubernetes/pull/49961
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import hashlib
import json
try:
import string
maketrans = string.maketrans
except AttributeError:
maketrans = str.maketrans
@@ -35,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
@@ -44,14 +46,21 @@ def sorted_dict(unsorted_dict):
def generate_hash(resource):
# Get name from metadata
resource['name'] = resource.get('metadata', {}).get('name', '')
if resource['kind'] == 'ConfigMap':
marshalled = marshal(sorted_dict(resource), ['data', 'kind', 'name'])
del(resource['name'])
metada = resource.get("metadata", {})
key = "name"
resource["name"] = metada.get("name", "")
generate_name = metada.get("generateName", "")
if resource["name"] == "" and generate_name:
del resource["name"]
key = "generateName"
resource["generateName"] = generate_name
if resource["kind"] == "ConfigMap":
marshalled = marshal(sorted_dict(resource), ["data", "kind", key])
del resource[key]
return encode(marshalled)
if resource['kind'] == 'Secret':
marshalled = marshal(sorted_dict(resource), ['data', 'kind', 'name', 'type'])
del(resource['name'])
if resource["kind"] == "Secret":
marshalled = marshal(sorted_dict(resource), ["data", "kind", key, "type"])
del resource[key]
return encode(marshalled)
raise NotImplementedError
@@ -60,8 +69,10 @@ def marshal(data, keys):
ordered = OrderedDict()
for key in keys:
ordered[key] = data.get(key, "")
return json.dumps(ordered, separators=(',', ':')).encode('utf-8')
return json.dumps(ordered, separators=(",", ":")).encode("utf-8")
def encode(resource):
return hashlib.sha256(resource).hexdigest()[:10].translate(maketrans("013ae", "ghkmt"))
return (
hashlib.sha256(resource).hexdigest()[:10].translate(maketrans("013ae", "ghkmt"))
)

View File

@@ -7,138 +7,30 @@ 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
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.six import string_types
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
LooseVersion,
)
try:
import yaml
HAS_YAML = True
YAML_IMP_ERR = None
except ImportError:
YAML_IMP_ERR = traceback.format_exc()
HAS_YAML = False
@contextmanager
def prepare_helm_environ_update(module):
environ_update = {}
file_to_cleam_up = None
kubeconfig_path = module.params.get('kubeconfig')
if module.params.get('context') is not None:
environ_update["HELM_KUBECONTEXT"] = module.params.get('context')
if module.params.get('release_namespace'):
environ_update["HELM_NAMESPACE"] = module.params.get('release_namespace')
if module.params.get("api_key"):
environ_update["HELM_KUBETOKEN"] = module.params["api_key"]
if module.params.get("host"):
environ_update["HELM_KUBEAPISERVER"] = module.params["host"]
if module.params.get("validate_certs") is False or module.params.get("ca_cert"):
kubeconfig_path = write_temp_kubeconfig(
module.params["host"],
validate_certs=module.params["validate_certs"],
ca_cert=module.params["ca_cert"])
file_to_cleam_up = kubeconfig_path
if kubeconfig_path is not None:
environ_update["KUBECONFIG"] = kubeconfig_path
try:
yield environ_update
finally:
if file_to_cleam_up:
os.remove(file_to_cleam_up)
def run_helm(module, command, fails_on_error=True):
if not HAS_YAML:
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
with prepare_helm_environ_update(module) as environ_update:
rc, out, err = module.run_command(command, environ_update=environ_update)
if fails_on_error and rc != 0:
module.fail_json(
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
stdout=out,
stderr=err,
command=command,
)
return rc, out, err
def get_values(module, command, release_name):
"""
Get Values from deployed release
"""
if not HAS_YAML:
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
get_command = command + " get values --output=yaml " + release_name
rc, out, err = run_helm(module, get_command)
# Helm 3 return "null" string when no values are set
if out.rstrip("\n") == "null":
return {}
return yaml.safe_load(out)
def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None):
# Workaround until https://github.com/helm/helm/pull/8622 is merged
content = {
"apiVersion": "v1",
"kind": "Config",
"clusters": [
{
"cluster": {
"server": server,
},
"name": "generated-cluster"
}
],
"contexts": [
{
"context": {
"cluster": "generated-cluster"
},
"name": "generated-context"
}
],
"current-context": "generated-context"
}
if not validate_certs:
content["clusters"][0]["cluster"]["insecure-skip-tls-verify"] = True
if ca_cert:
content["clusters"][0]["cluster"]["certificate-authority"] = ca_cert
_fd, file_name = tempfile.mkstemp()
with os.fdopen(_fd, 'w') as fp:
yaml.dump(content, fp)
return file_name
def get_helm_plugin_list(module, helm_bin=None):
"""
Return `helm plugin list`
"""
if not helm_bin:
return []
helm_plugin_list = helm_bin + " list"
rc, out, err = run_helm(module, helm_plugin_list)
if rc != 0 or (out == '' and err == ''):
module.fail_json(
msg="Failed to get Helm plugin info",
command=helm_plugin_list,
stdout=out,
stderr=err,
rc=rc,
)
return (rc, out, err)
def parse_helm_plugin_list(module, output=None):
def parse_helm_plugin_list(output=None):
"""
Parse `helm plugin list`, return list of plugins
"""
@@ -149,12 +41,256 @@ def parse_helm_plugin_list(module, output=None):
for line in output:
if line.startswith("NAME"):
continue
name, version, description = line.split('\t', 3)
name, version, description = line.split("\t", 3)
name = name.strip()
version = version.strip()
description = description.strip()
if name == '':
if name == "":
continue
ret.append((name, version, description))
return ret
def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None, kubeconfig=None):
# Workaround until https://github.com/helm/helm/pull/8622 is merged
content = {
"apiVersion": "v1",
"kind": "Config",
"clusters": [{"cluster": {"server": server}, "name": "generated-cluster"}],
"contexts": [
{"context": {"cluster": "generated-cluster"}, "name": "generated-context"}
],
"current-context": "generated-context",
}
if kubeconfig:
content = copy.deepcopy(kubeconfig)
for cluster in content["clusters"]:
if server:
cluster["cluster"]["server"] = server
if not validate_certs:
cluster["cluster"]["insecure-skip-tls-verify"] = True
if ca_cert:
cluster["cluster"]["certificate-authority"] = ca_cert
return content
class AnsibleHelmModule(object):
"""
An Ansible module class for Kubernetes.core helm modules
"""
def __init__(self, **kwargs):
self._module = None
if "module" in kwargs:
self._module = kwargs.get("module")
else:
self._module = AnsibleModule(**kwargs)
self.helm_env = None
def __getattr__(self, name):
return getattr(self._module, name)
@property
def params(self):
return self._module.params
def _prepare_helm_environment(self):
param_to_env_mapping = [
("context", "HELM_KUBECONTEXT"),
("release_namespace", "HELM_NAMESPACE"),
("api_key", "HELM_KUBETOKEN"),
("host", "HELM_KUBEAPISERVER"),
]
env_update = {}
for p, env in param_to_env_mapping:
if self.params.get(p):
env_update[env] = self.params.get(p)
kubeconfig_content = None
kubeconfig = self.params.get("kubeconfig")
if kubeconfig:
if isinstance(kubeconfig, string_types):
with open(os.path.expanduser(kubeconfig)) as fd:
kubeconfig_content = yaml.safe_load(fd)
elif isinstance(kubeconfig, dict):
kubeconfig_content = kubeconfig
if self.params.get("ca_cert"):
ca_cert = self.params.get("ca_cert")
if LooseVersion(self.get_helm_version()) < LooseVersion("3.5.0"):
# update certs from kubeconfig
kubeconfig_content = write_temp_kubeconfig(
server=self.params.get("host"),
ca_cert=ca_cert,
kubeconfig=kubeconfig_content,
)
else:
env_update["HELM_KUBECAFILE"] = ca_cert
if self.params.get("validate_certs") is False:
validate_certs = self.params.get("validate_certs")
if LooseVersion(self.get_helm_version()) < LooseVersion("3.10.0"):
# update certs from kubeconfig
kubeconfig_content = write_temp_kubeconfig(
server=self.params.get("host"),
validate_certs=validate_certs,
kubeconfig=kubeconfig_content,
)
else:
env_update["HELM_KUBEINSECURE_SKIP_TLS_VERIFY"] = "true"
if kubeconfig_content:
fd, kubeconfig_path = tempfile.mkstemp()
with os.fdopen(fd, "w") as fp:
json.dump(kubeconfig_content, fp)
env_update["KUBECONFIG"] = kubeconfig_path
self.add_cleanup_file(kubeconfig_path)
return env_update
@property
def env_update(self):
if self.helm_env is None:
self.helm_env = self._prepare_helm_environment()
return self.helm_env
def run_helm_command(self, command, fails_on_error=True):
if not HAS_YAML:
self.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
rc, out, err = self.run_command(command, environ_update=self.env_update)
if fails_on_error and rc != 0:
self.fail_json(
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(
rc, out, err
),
stdout=out,
stderr=err,
command=command,
)
return rc, out, err
def get_helm_binary(self):
return self.params.get("binary_path") or self.get_bin_path(
"helm", required=True
)
def get_helm_version(self):
command = self.get_helm_binary() + " version"
rc, out, err = self.run_command(command)
m = re.match(r'version.BuildInfo{Version:"v(.*?)",', out)
if m:
return m.group(1)
m = re.match(r'Client: &version.Version{SemVer:"v(.*?)", ', out)
if m:
return m.group(1)
return None
def get_values(self, release_name, get_all=False):
"""
Get Values from deployed release
"""
if not HAS_YAML:
self.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
get_command = (
self.get_helm_binary() + " get values --output=yaml " + release_name
)
if get_all:
get_command += " -a"
rc, out, err = self.run_helm_command(get_command)
# Helm 3 return "null" string when no values are set
if out.rstrip("\n") == "null":
return {}
return yaml.safe_load(out)
def parse_yaml_content(self, content):
if not HAS_YAML:
self.fail_json(msg=missing_required_lib("yaml"), exception=HAS_YAML)
try:
return list(yaml.safe_load_all(content))
except (IOError, yaml.YAMLError) as exc:
self.fail_json(
msg="Error parsing YAML content: {0}".format(exc), raw_data=content
)
def get_manifest(self, release_name):
command = [
self.get_helm_binary(),
"get",
"manifest",
release_name,
]
rc, out, err = self.run_helm_command(" ".join(command))
if rc != 0:
self.fail_json(msg=err)
return self.parse_yaml_content(out)
def get_notes(self, release_name):
command = [
self.get_helm_binary(),
"get",
"notes",
release_name,
]
rc, out, err = self.run_helm_command(" ".join(command))
if rc != 0:
self.fail_json(msg=err)
return out
def get_hooks(self, release_name):
command = [
self.get_helm_binary(),
"get",
"hooks",
release_name,
]
rc, out, err = self.run_helm_command(" ".join(command))
if rc != 0:
self.fail_json(msg=err)
return self.parse_yaml_content(out)
def get_helm_plugin_list(self):
"""
Return `helm plugin list`
"""
helm_plugin_list = self.get_helm_binary() + " plugin list"
rc, out, err = self.run_helm_command(helm_plugin_list)
if rc != 0 or (out == "" and err == ""):
self.fail_json(
msg="Failed to get Helm plugin info",
command=helm_plugin_list,
stdout=out,
stderr=err,
rc=rc,
)
return (rc, out, err, helm_plugin_list)
def get_helm_set_values_args(self, set_values):
if any(v.get("value_type") == "json" for v in set_values):
if LooseVersion(self.get_helm_version()) < LooseVersion("3.10.0"):
self.fail_json(
msg="This module requires helm >= 3.10.0, to use set_values parameter with value type set to 'json'. current version is {0}".format(
self.get_helm_version()
)
)
options = []
for opt in set_values:
value_type = opt.get("value_type", "raw")
value = opt.get("value")
if value_type == "raw":
options.append("--set " + value)
else:
options.append("--set-{0} '{1}'".format(value_type, value))
return " ".join(options)

View File

@@ -0,0 +1,43 @@
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"]),
no_log=True,
),
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"),
]

View File

@@ -0,0 +1,367 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import hashlib
import os
from typing import Any, Dict, List, Optional
from ansible.module_utils.six import iteritems, string_types
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_MAP,
AUTH_ARG_SPEC,
AUTH_PROXY_HEADERS_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
requires as _requires,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
try:
from ansible_collections.kubernetes.core.plugins.module_utils import (
k8sdynamicclient,
)
from ansible_collections.kubernetes.core.plugins.module_utils.client.discovery import (
LazyDiscoverer,
)
except ImportError:
# Handled in module setup
pass
try:
import kubernetes
from kubernetes.dynamic.exceptions import (
ResourceNotFoundError,
ResourceNotUniqueError,
)
from kubernetes.dynamic.resource import Resource
except ImportError:
# kubernetes import error is handled in module setup
# This is defined only for the sake of Ansible's checked import requirement
Resource = Any # type: ignore
try:
import urllib3
urllib3.disable_warnings()
except ImportError:
# Handled in module setup
pass
_pool = {}
class unique_string(str):
_low = None
def __hash__(self):
return id(self)
def __eq__(self, other):
return self is other
def lower(self):
if self._low is None:
lower = str.lower(self)
if str.__eq__(lower, self):
self._low = self
else:
self._low = unique_string(lower)
return self._low
def _create_auth_spec(module=None, **kwargs) -> Dict:
auth: Dict = {}
# If authorization variables aren't defined, look for them in environment variables
for true_name, arg_name in AUTH_ARG_MAP.items():
if module and module.params.get(arg_name) is not None:
auth[true_name] = module.params.get(arg_name)
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
auth[true_name] = kwargs.get(arg_name)
elif true_name in kwargs and kwargs.get(true_name) is not None:
# Aliases in kwargs
auth[true_name] = kwargs.get(true_name)
elif arg_name == "proxy_headers":
# specific case for 'proxy_headers' which is a dictionary
proxy_headers = {}
for key in AUTH_PROXY_HEADERS_SPEC.keys():
env_value = os.getenv(
"K8S_AUTH_PROXY_HEADERS_{0}".format(key.upper()), None
)
if env_value is not None:
if AUTH_PROXY_HEADERS_SPEC[key].get("type") == "bool":
env_value = env_value.lower() not in ["0", "false", "no"]
proxy_headers[key] = env_value
if proxy_headers is not {}:
auth[true_name] = proxy_headers
else:
env_value = os.getenv(
"K8S_AUTH_{0}".format(arg_name.upper()), None
) or os.getenv("K8S_AUTH_{0}".format(true_name.upper()), None)
if env_value is not None:
if AUTH_ARG_SPEC[arg_name].get("type") == "bool":
env_value = env_value.lower() not in ["0", "false", "no"]
auth[true_name] = env_value
return auth
def _load_config(auth: Dict) -> None:
kubeconfig = auth.get("kubeconfig")
optional_arg = {
"context": auth.get("context"),
"persist_config": auth.get("persist_config"),
}
if kubeconfig:
if isinstance(kubeconfig, string_types):
kubernetes.config.load_kube_config(config_file=kubeconfig, **optional_arg)
elif isinstance(kubeconfig, dict):
kubernetes.config.load_kube_config_from_dict(
config_dict=kubeconfig, **optional_arg
)
else:
kubernetes.config.load_kube_config(config_file=None, **optional_arg)
def _create_configuration(auth: Dict):
def auth_set(*names: list) -> bool:
return all(auth.get(name) for name in names)
if auth_set("host"):
# Removing trailing slashes if any from hostname
auth["host"] = auth.get("host").rstrip("/")
if (
auth_set("username", "password", "host")
or auth_set("api_key", "host")
or auth_set("cert_file", "key_file", "host")
):
# We have enough in the parameters to authenticate, no need to load incluster or kubeconfig
pass
elif auth_set("kubeconfig") or auth_set("context"):
try:
_load_config(auth)
except Exception as err:
raise err
else:
# First try to do incluster config, then kubeconfig
try:
kubernetes.config.load_incluster_config()
except kubernetes.config.ConfigException:
try:
_load_config(auth)
except Exception as err:
raise err
# Override any values in the default configuration with Ansible parameters
# As of kubernetes-client v12.0.0, get_default_copy() is required here
try:
configuration = kubernetes.client.Configuration().get_default_copy()
except AttributeError:
configuration = kubernetes.client.Configuration()
for key, value in iteritems(auth):
if key in AUTH_ARG_MAP.keys() and value is not None:
if key == "api_key":
setattr(
configuration, key, {"authorization": "Bearer {0}".format(value)}
)
elif key == "proxy_headers":
headers = urllib3.util.make_headers(**value)
setattr(configuration, key, headers)
else:
setattr(configuration, key, value)
return configuration
def _create_headers(module=None, **kwargs):
header_map = {
"impersonate_user": "Impersonate-User",
"impersonate_groups": "Impersonate-Group",
}
headers = {}
for arg_name, header_name in header_map.items():
value = None
if module and module.params.get(arg_name) is not None:
value = module.params.get(arg_name)
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
value = kwargs.get(arg_name)
else:
value = os.getenv("K8S_AUTH_{0}".format(arg_name.upper()), None)
if value is not None:
if AUTH_ARG_SPEC[arg_name].get("type") == "list":
value = [x for x in value.split(",") if x != ""]
if value:
headers[header_name] = value
return headers
def _configuration_digest(configuration, **kwargs) -> str:
m = hashlib.sha256()
for k in AUTH_ARG_MAP:
if not hasattr(configuration, k):
v = None
else:
v = getattr(configuration, k)
if v and k in ["ssl_ca_cert", "cert_file", "key_file"]:
with open(str(v), "r") as fd:
content = fd.read()
m.update(content.encode())
else:
m.update(str(v).encode())
for k, v in kwargs.items():
content = "{0}: {1}".format(k, v)
m.update(content.encode())
digest = m.hexdigest()
return digest
def _set_header(client, header, value):
if isinstance(value, list):
for v in value:
client.set_default_header(header_name=unique_string(header), header_value=v)
else:
client.set_default_header(header_name=header, header_value=value)
def cache(func):
def wrapper(*args, **kwargs):
client = None
hashable_kwargs = {}
for k, v in kwargs.items():
if isinstance(v, list):
hashable_kwargs[k] = ",".join(sorted(v))
else:
hashable_kwargs[k] = v
digest = _configuration_digest(*args, **hashable_kwargs)
if digest in _pool:
client = _pool[digest]
else:
client = func(*args, **kwargs)
_pool[digest] = client
return client
return wrapper
@cache
def create_api_client(configuration, **headers):
client = kubernetes.client.ApiClient(configuration)
for header, value in headers.items():
_set_header(client, header, value)
return k8sdynamicclient.K8SDynamicClient(client, discoverer=LazyDiscoverer)
class K8SClient:
"""A Client class for K8S modules.
This class has the primary purpose to proxy the kubernetes client and resource objects.
If there is a need for other methods or attributes to be proxied, they can be added here.
"""
K8S_SERVER_DRY_RUN = "All"
def __init__(self, configuration, client, dry_run: bool = False) -> None:
self.configuration = configuration
self.client = client
self.dry_run = dry_run
@property
def resources(self) -> List[Any]:
return self.client.resources
def _find_resource_with_prefix(
self, prefix: str, kind: str, api_version: str
) -> Resource:
for attribute in ["kind", "name", "singular_name"]:
try:
return self.client.resources.get(
**{"prefix": prefix, "api_version": api_version, attribute: kind}
)
except (ResourceNotFoundError, ResourceNotUniqueError):
pass
return self.client.resources.get(
prefix=prefix, api_version=api_version, short_names=[kind]
)
def resource(self, kind: str, api_version: str) -> Resource:
"""Fetch a kubernetes client resource.
This will attempt to find a kubernetes resource trying, in order, kind,
name, singular_name and short_names.
"""
try:
if api_version == "v1":
return self._find_resource_with_prefix("api", kind, api_version)
except ResourceNotFoundError:
pass
return self._find_resource_with_prefix(None, kind, api_version)
def _ensure_dry_run(self, params: Dict) -> Dict:
if self.dry_run:
params["dry_run"] = self.K8S_SERVER_DRY_RUN
return params
def validate(
self, resource, version: Optional[str] = None, strict: Optional[bool] = False
):
return self.client.validate(resource, version, strict)
def get(self, resource, **params):
return resource.get(**params)
def delete(self, resource, **params):
return resource.delete(**self._ensure_dry_run(params))
def apply(self, resource, definition, namespace, **params):
return resource.apply(
definition, namespace=namespace, **self._ensure_dry_run(params)
)
def create(self, resource, definition, **params):
return resource.create(definition, **self._ensure_dry_run(params))
def replace(self, resource, definition, **params):
return resource.replace(definition, **self._ensure_dry_run(params))
def patch(self, resource, definition, **params):
return resource.patch(definition, **self._ensure_dry_run(params))
def get_api_client(module=None, **kwargs: Optional[Any]) -> K8SClient:
auth_spec = _create_auth_spec(module, **kwargs)
if module:
requires = module.requires
else:
requires = _requires
if isinstance(auth_spec.get("kubeconfig"), dict):
requires("kubernetes", "17.17.0", "to use in-memory config")
if auth_spec.get("no_proxy"):
requires("kubernetes", "19.15.0", "to use the no_proxy feature")
try:
configuration = _create_configuration(auth_spec)
headers = _create_headers(module, **kwargs)
client = create_api_client(configuration, **headers)
except kubernetes.config.ConfigException as e:
msg = "Could not create API client: {0}".format(e)
raise CoreException(msg) from e
dry_run = False
if module and module.server_side_dry_run:
dry_run = True
k8s_client = K8SClient(
configuration=configuration,
client=client,
dry_run=dry_run,
)
return k8s_client

View File

@@ -0,0 +1,172 @@
import traceback
from typing import Optional
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_text
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
LooseVersion,
)
class AnsibleK8SModule:
"""A base module class for K8S modules.
This class should be used instead of directly using AnsibleModule. If there
is a need for other methods or attributes to be proxied, they can be added
here.
"""
default_settings = {
"check_k8s": True,
"check_pyyaml": True,
"module_class": AnsibleModule,
}
def __init__(self, **kwargs) -> None:
local_settings = {}
for key in AnsibleK8SModule.default_settings:
try:
local_settings[key] = kwargs.pop(key)
except KeyError:
local_settings[key] = AnsibleK8SModule.default_settings[key]
self.settings = local_settings
self._module = self.settings["module_class"](**kwargs)
if self.settings["check_k8s"]:
self.requires("kubernetes")
self.has_at_least("kubernetes", "24.2.0", warn=True)
if self.settings["check_pyyaml"]:
self.requires("pyyaml")
@property
def check_mode(self):
return self._module.check_mode
@property
def server_side_dry_run(self):
return self.check_mode and self.has_at_least("kubernetes", "18.20.0")
@property
def _diff(self):
return self._module._diff
@property
def _name(self):
return self._module._name
@property
def params(self):
return self._module.params
def warn(self, *args, **kwargs):
return self._module.warn(*args, **kwargs)
def deprecate(self, *args, **kwargs):
return self._module.deprecate(*args, **kwargs)
def debug(self, *args, **kwargs):
return self._module.debug(*args, **kwargs)
def exit_json(self, *args, **kwargs):
return self._module.exit_json(*args, **kwargs)
def fail_json(self, *args, **kwargs):
return self._module.fail_json(*args, **kwargs)
def fail_from_exception(self, exception):
msg = to_text(exception)
tb = "".join(
traceback.format_exception(None, exception, exception.__traceback__)
)
return self.fail_json(msg=msg, exception=tb)
def has_at_least(
self, dependency: str, minimum: Optional[str] = None, warn: bool = False
) -> bool:
supported = has_at_least(dependency, minimum)
if not supported and warn:
self.warn(
"{0}<{1} is not supported or tested. Some features may not work.".format(
dependency, minimum
)
)
return supported
def requires(
self,
dependency: str,
minimum: Optional[str] = None,
reason: Optional[str] = None,
) -> None:
try:
requires(dependency, minimum, reason=reason)
except Exception as e:
self.fail_json(msg=to_text(e))
def gather_versions() -> dict:
versions = {}
try:
import jsonpatch
versions["jsonpatch"] = jsonpatch.__version__
except ImportError:
pass
try:
import kubernetes
versions["kubernetes"] = kubernetes.__version__
except ImportError:
pass
try:
import kubernetes_validate
versions["kubernetes-validate"] = kubernetes_validate.__version__
except ImportError:
pass
try:
import yaml
versions["pyyaml"] = yaml.__version__
except ImportError:
pass
return versions
def has_at_least(dependency: str, minimum: Optional[str] = None) -> bool:
"""Check if a specific dependency is present at a minimum version.
If a minimum version is not specified it will check only that the
dependency is present.
"""
dependencies = gather_versions()
current = dependencies.get(dependency)
if current is not None:
if minimum is None:
return True
supported = LooseVersion(current) >= LooseVersion(minimum)
return supported
return False
def requires(
dependency: str, minimum: Optional[str] = None, reason: Optional[str] = None
) -> None:
"""Fail if a specific dependency is not present at a minimum version.
If a minimum version is not specified it will require only that the
dependency is present. This function raises an exception when the
dependency is not found at the required version.
"""
if not has_at_least(dependency, minimum):
if minimum is not None:
lib = "{0}>={1}".format(dependency, minimum)
else:
lib = dependency
raise Exception(missing_required_lib(lib, reason=reason))

View 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)

View File

@@ -0,0 +1,134 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import os
from typing import Dict, Iterable, List, Optional, Union, cast
from ansible.module_utils.six import string_types
from ansible.module_utils.urls import Request
try:
import yaml
except ImportError:
# Handled in module setup
pass
class ResourceDefinition(dict):
"""Representation of a resource definition.
This is a thin wrapper around a dictionary representation of a resource
definition, with a few properties defined for conveniently accessing the
commonly used fields.
"""
@property
def kind(self) -> Optional[str]:
return self.get("kind")
@property
def api_version(self) -> Optional[str]:
return self.get("apiVersion")
@property
def namespace(self) -> Optional[str]:
metadata = self.get("metadata", {})
return metadata.get("namespace")
@property
def name(self) -> Optional[str]:
metadata = self.get("metadata", {})
return metadata.get("name")
def create_definitions(params: Dict) -> List[ResourceDefinition]:
"""Create a list of ResourceDefinitions from module inputs.
This will take the module's inputs and return a list of ResourceDefintion
objects. The resource definitions returned by this function should be as
complete a definition as we can create based on the input. Any *List kinds
will be removed and replaced by the resources contained in it.
"""
if params.get("resource_definition"):
d = cast(Union[str, List, Dict], params.get("resource_definition"))
definitions = from_yaml(d)
elif params.get("src"):
d = cast(str, params.get("src"))
if hasattr(d, "startswith") and d.startswith(("https://", "http://", "ftp://")):
data = Request().open("GET", d).read().decode("utf8")
definitions = from_yaml(data)
else:
definitions = from_file(d)
else:
# We'll create an empty definition and let merge_params set values
# from the module parameters.
definitions = [{}]
resource_definitions: List[Dict] = []
for definition in definitions:
merge_params(definition, params)
kind = cast(Optional[str], definition.get("kind"))
if kind and kind.endswith("List"):
resource_definitions += flatten_list_kind(definition, params)
else:
resource_definitions.append(definition)
return list(map(ResourceDefinition, resource_definitions))
def from_yaml(definition: Union[str, List, Dict]) -> Iterable[Dict]:
"""Load resource definitions from a yaml definition."""
definitions: List[Dict] = []
if isinstance(definition, string_types):
definitions += yaml.safe_load_all(definition)
elif isinstance(definition, list):
for item in definition:
if isinstance(item, string_types):
definitions += yaml.safe_load_all(item)
else:
definitions.append(item)
else:
definition = cast(Dict, definition)
definitions.append(definition)
return filter(None, definitions)
def from_file(filepath: str) -> Iterable[Dict]:
"""Load resource definitions from a path to a yaml file."""
path = os.path.normpath(filepath)
with open(path, "rb") as f:
definitions = list(yaml.safe_load_all(f))
return filter(None, definitions)
def merge_params(definition: Dict, params: Dict) -> Dict:
"""Merge module parameters with the resource definition.
Fields in the resource definition take precedence over module parameters.
"""
definition.setdefault("kind", params.get("kind"))
definition.setdefault("apiVersion", params.get("api_version"))
metadata = definition.setdefault("metadata", {})
# The following should only be set if we have values for them
if params.get("namespace"):
metadata.setdefault("namespace", params.get("namespace"))
if params.get("name"):
metadata.setdefault("name", params.get("name"))
if params.get("generate_name"):
metadata.setdefault("generateName", params.get("generate_name"))
return definition
def flatten_list_kind(definition: Dict, params: Dict) -> List[Dict]:
"""Replace *List kind with the items it contains.
This will take a definition for a *List resource and return a list of
definitions for the items contained within the List.
"""
items = []
kind = cast(str, definition.get("kind"))[:-4]
api_version = definition.get("apiVersion")
for item in definition.get("items", []):
item.setdefault("kind", kind)
item.setdefault("apiVersion", api_version)
items.append(merge_params(item, params))
return items

View File

@@ -0,0 +1,233 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from typing import Dict
from ansible.module_utils._text import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
ResourceTimeout,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
create_definitions,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
diff_objects,
hide_fields,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import exists
from ansible_collections.kubernetes.core.plugins.module_utils.selector import (
LabelSelectorFilter,
)
def validate(client, module, resource):
def _prepend_resource_info(resource, msg):
return "%s %s: %s" % (resource["kind"], resource["metadata"]["name"], msg)
module.requires("kubernetes-validate")
warnings, errors = client.validate(
resource,
module.params["validate"].get("version"),
module.params["validate"].get("strict"),
)
if errors and module.params["validate"]["fail_on_error"]:
module.fail_json(
msg="\n".join([_prepend_resource_info(resource, error) for error in errors])
)
return [_prepend_resource_info(resource, msg) for msg in warnings + errors]
def get_definitions(svc, params):
try:
definitions = create_definitions(params)
except Exception as e:
msg = "Failed to load resource definition: {0}".format(e)
raise CoreException(msg) from e
delete_all = params.get("delete_all")
src = params.get("src")
resource_definition = params.get("resource_definition")
name = params.get("name")
state = params.get("state")
if (
delete_all
and state == "absent"
and name is None
and resource_definition is None
and src is None
):
# Delete all resources in the namespace for the specified resource type
if params.get("kind") is None:
raise CoreException(
"'kind' option is required to specify the resource type."
)
resource = svc.find_resource(
params.get("kind"), params.get("api_version"), fail=True
)
definitions = svc.retrieve_all(
resource,
params.get("namespace"),
params.get("label_selectors"),
)
return definitions
def run_module(module) -> None:
results = []
changed = False
client = get_api_client(module)
svc = K8sService(client, module)
definitions = get_definitions(svc, module.params)
for definition in definitions:
result = {"changed": False, "result": {}}
warnings = []
if module.params.get("validate") is not None:
warnings = validate(client, module, definition)
try:
result = perform_action(svc, definition, module.params)
except Exception as e:
try:
error = e.result
except AttributeError:
error = {}
try:
error["reason"] = e.__cause__.reason
except AttributeError:
pass
error["msg"] = to_native(e)
if warnings:
error.setdefault("warnings", []).extend(warnings)
if module.params.get("continue_on_error"):
result["error"] = error
else:
module.fail_json(**error)
if warnings:
result.setdefault("warnings", []).extend(warnings)
changed |= result["changed"]
results.append(result)
if len(results) == 1:
module.exit_json(**results[0])
module.exit_json(**{"changed": changed, "result": {"results": results}})
def perform_action(svc, definition: Dict, params: Dict) -> Dict:
origin_name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
label_selectors = params.get("label_selectors")
state = params.get("state", None)
kind = definition.get("kind")
api_version = definition.get("apiVersion")
hidden_fields = params.get("hidden_fields")
result = {"changed": False, "result": {}}
instance = {}
resource = svc.find_resource(kind, api_version, fail=True)
definition["kind"] = resource.kind
definition["apiVersion"] = resource.group_version
existing = svc.retrieve(resource, definition)
if state == "absent":
if exists(existing) and existing.kind.endswith("List"):
instance = []
for item in existing.items:
r = svc.delete(resource, item, existing)
instance.append(r)
else:
instance = svc.delete(resource, definition, existing)
result["method"] = "delete"
if exists(existing):
result["changed"] = True
else:
if label_selectors:
filter_selector = LabelSelectorFilter(label_selectors)
if not filter_selector.isMatching(definition):
result["changed"] = False
result["msg"] = (
"resource 'kind={kind},name={name},namespace={namespace}' "
"filtered by label_selectors.".format(
kind=kind,
name=origin_name,
namespace=namespace,
)
)
return result
if params.get("apply"):
instance = svc.apply(resource, definition, existing)
result["method"] = "apply"
elif not existing:
if state == "patched":
result.setdefault("warnings", []).append(
"resource 'kind={kind},name={name}' was not found but will not be "
"created as 'state' parameter has been set to '{state}'".format(
kind=kind, name=definition["metadata"].get("name"), state=state
)
)
return result
instance = svc.create(resource, definition)
result["method"] = "create"
result["changed"] = True
elif params.get("force", False):
instance = svc.replace(resource, definition, existing)
result["method"] = "replace"
else:
instance = svc.update(resource, definition, existing)
result["method"] = "update"
# If needed, wait and/or create diff
success = True
if result["method"] == "delete":
# wait logic is a bit different for delete as `instance` may be a status object
if params.get("wait") and not svc.module.check_mode:
success, waited, duration = svc.wait(resource, definition)
result["duration"] = duration
else:
if params.get("wait") and not svc.module.check_mode:
success, instance, duration = svc.wait(resource, instance)
result["duration"] = duration
if result["method"] not in ("create", "delete"):
if existing:
existing = existing.to_dict()
else:
existing = {}
match, diffs = diff_objects(existing, instance, hidden_fields)
if match and diffs:
result.setdefault("warnings", []).append(
"No meaningful diff was generated, but the API may not be idempotent "
"(only metadata.generation or metadata.resourceVersion were changed)"
)
result["changed"] = not match
if svc.module._diff:
result["diff"] = diffs
result["result"] = hide_fields(instance, hidden_fields)
if not success:
raise ResourceTimeout(
'"{0}" "{1}": Timed out waiting on resource'.format(
definition["kind"], origin_name
),
result,
)
return result

View File

@@ -0,0 +1,545 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import copy
from typing import Any, Dict, List, Optional, Tuple
from ansible.module_utils.common.dict_transformations import dict_merge
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
generate_hash,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import requires
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
Waiter,
exists,
get_waiter,
resource_absent,
)
try:
from kubernetes.dynamic.exceptions import (
BadRequestError,
ConflictError,
ForbiddenError,
MethodNotAllowedError,
NotFoundError,
ResourceNotFoundError,
ResourceNotUniqueError,
)
except ImportError:
# Handled in module setup
pass
try:
from kubernetes.dynamic.resource import Resource, ResourceInstance
except ImportError:
# These are defined only for the sake of Ansible's checked import requirement
Resource = Any # type: ignore
ResourceInstance = Any # type: ignore
try:
from ansible_collections.kubernetes.core.plugins.module_utils.apply import (
apply_object,
)
except ImportError:
# Handled in module setup
pass
try:
from ansible_collections.kubernetes.core.plugins.module_utils.apply import (
recursive_diff,
)
except ImportError:
from ansible.module_utils.common.dict_transformations import recursive_diff
try:
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
_encode_stringdata,
)
except ImportError:
# Handled in module setup
pass
class K8sService:
"""A Service class for K8S modules.
This class has the primary purpose is to perform work on the cluster (e.g., create, apply, replace, update, delete).
"""
def __init__(self, client, module) -> None:
self.client = client
self.module = module
@property
def _client_side_dry_run(self):
return self.module.check_mode and not self.client.dry_run
def find_resource(
self, kind: str, api_version: str, fail: bool = False
) -> Optional[Resource]:
try:
return self.client.resource(kind, api_version)
except (ResourceNotFoundError, ResourceNotUniqueError):
if fail:
raise CoreException(
"Failed to find exact match for %s.%s by [kind, name, singularName, shortNames]"
% (api_version, kind)
)
def wait(
self, resource: Resource, instance: Dict
) -> Tuple[bool, Optional[Dict], int]:
wait_sleep = self.module.params.get("wait_sleep")
wait_timeout = self.module.params.get("wait_timeout")
wait_condition = None
if self.module.params.get("wait_condition") and self.module.params[
"wait_condition"
].get("type"):
wait_condition = self.module.params["wait_condition"]
state = "present"
if self.module.params.get("state") == "absent":
state = "absent"
label_selectors = self.module.params.get("label_selectors")
waiter = get_waiter(
self.client, resource, condition=wait_condition, state=state
)
return waiter.wait(
timeout=wait_timeout,
sleep=wait_sleep,
name=instance["metadata"].get("name"),
namespace=instance["metadata"].get("namespace"),
label_selectors=label_selectors,
)
def create_project_request(self, definition: Dict) -> Dict:
definition["kind"] = "ProjectRequest"
results = {"changed": False, "result": {}}
resource = self.find_resource(
"ProjectRequest", definition["apiVersion"], fail=True
)
if not self.module.check_mode:
try:
k8s_obj = self.client.create(resource, definition)
results["result"] = k8s_obj.to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to create object: {0}".format(reason)
raise CoreException(msg) from e
results["changed"] = True
return results
def patch_resource(
self,
resource: Resource,
definition: Dict,
name: str,
namespace: str,
merge_type: str = None,
) -> Dict:
if merge_type == "json":
self.module.deprecate(
msg="json as a merge_type value is deprecated. Please use the k8s_json_patch module instead.",
version="4.0.0",
collection_name="kubernetes.core",
)
try:
params = dict(name=name, namespace=namespace)
if merge_type:
params["content_type"] = "application/{0}-patch+json".format(merge_type)
return self.client.patch(resource, definition, **params).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to patch object: {0}".format(reason)
raise CoreException(msg) from e
def retrieve(self, resource: Resource, definition: Dict) -> ResourceInstance:
state = self.module.params.get("state", None)
append_hash = self.module.params.get("append_hash", False)
name = definition["metadata"].get("name")
generate_name = definition["metadata"].get("generateName")
namespace = definition["metadata"].get("namespace")
label_selectors = self.module.params.get("label_selectors")
existing: ResourceInstance = None
try:
# ignore append_hash for resources other than ConfigMap and Secret
if append_hash and definition["kind"] in ["ConfigMap", "Secret"]:
if name:
name = "%s-%s" % (name, generate_hash(definition))
definition["metadata"]["name"] = name
elif generate_name:
definition["metadata"]["generateName"] = "%s-%s" % (
generate_name,
generate_hash(definition),
)
params = {}
if name:
params["name"] = name
if namespace:
params["namespace"] = namespace
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
if "name" in params or "label_selector" in params:
existing = self.client.get(resource, **params)
except (NotFoundError, MethodNotAllowedError):
pass
except ForbiddenError as e:
if (
definition["kind"] in ["Project", "ProjectRequest"]
and state != "absent"
):
return self.create_project_request(definition)
reason = e.body if hasattr(e, "body") else e
msg = "Failed to retrieve requested object: {0}".format(reason)
raise CoreException(msg) from e
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to retrieve requested object: {0}".format(reason)
raise CoreException(msg) from e
return existing
def retrieve_all(
self, resource: Resource, namespace: str, label_selectors: List[str] = None
) -> List[Dict]:
definitions: List[ResourceInstance] = []
try:
params = dict(namespace=namespace)
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
resource_list = self.client.get(resource, **params)
for item in resource_list.items:
existing = self.client.get(
resource, name=item.metadata.name, namespace=namespace
)
definitions.append(existing.to_dict())
except (NotFoundError, MethodNotAllowedError):
pass
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to retrieve requested object: {0}".format(reason)
raise CoreException(msg) from e
return definitions
def find(
self,
kind: str,
api_version: str,
name: str = None,
namespace: Optional[str] = None,
label_selectors: Optional[List[str]] = None,
field_selectors: Optional[List[str]] = None,
wait: Optional[bool] = False,
wait_sleep: Optional[int] = 5,
wait_timeout: Optional[int] = 120,
state: Optional[str] = "present",
condition: Optional[Dict] = None,
hidden_fields: Optional[List] = None,
) -> Dict:
resource = self.find_resource(kind, api_version)
api_found = bool(resource)
if not api_found:
return dict(
resources=[],
msg='Failed to find API for resource with apiVersion "{0}" and kind "{1}"'.format(
api_version, kind
),
api_found=False,
)
if not label_selectors:
label_selectors = []
if not field_selectors:
field_selectors = []
result = {"resources": [], "api_found": True}
# With a timeout of 0 the waiter will do a single check and return, effectively not waiting.
if not wait:
wait_timeout = 0
if state == "present":
predicate = exists
else:
predicate = resource_absent
waiter = Waiter(self.client, resource, predicate)
# This is an initial check to get the resource or resources that we then need to wait on individually.
try:
success, resources, duration = waiter.wait(
timeout=wait_timeout,
sleep=wait_sleep,
name=name,
namespace=namespace,
label_selectors=label_selectors,
field_selectors=field_selectors,
)
except BadRequestError:
return result
except CoreException as e:
raise e
except Exception as e:
raise CoreException(
"Exception '{0}' raised while trying to get resource using (name={1}, namespace={2}, label_selectors={3}, field_selectors={4})".format(
e, name, namespace, label_selectors, field_selectors
)
)
# There is either no result or there is a List resource with no items
if (
not resources
or resources["kind"].endswith("List")
and not resources.get("items")
):
return result
instances = resources.get("items") or [resources]
if not wait:
result["resources"] = [
hide_fields(instance, hidden_fields) for instance in instances
]
return result
# Now wait for the specified state of any resource instances we have found.
waiter = get_waiter(self.client, resource, state=state, condition=condition)
for instance in instances:
name = instance["metadata"].get("name")
namespace = instance["metadata"].get("namespace")
success, res, duration = waiter.wait(
timeout=wait_timeout,
sleep=wait_sleep,
name=name,
namespace=namespace,
)
if not success:
raise CoreException(
"Failed to gather information about %s(s) even"
" after waiting for %s seconds" % (res.get("kind"), duration)
)
result["resources"].append(hide_fields(res, hidden_fields))
return result
def create(self, resource: Resource, definition: Dict) -> Dict:
namespace = definition["metadata"].get("namespace")
name = definition["metadata"].get("name")
if self._client_side_dry_run:
k8s_obj = _encode_stringdata(definition)
else:
try:
k8s_obj = self.client.create(
resource, definition, namespace=namespace
).to_dict()
except ConflictError:
# Some resources, like ProjectRequests, can't be created multiple times,
# because the resources that they create don't match their kind
# In this case we'll mark it as unchanged and warn the user
self.module.warn(
"{0} was not found, but creating it returned a 409 Conflict error. This can happen \
if the resource you are creating does not directly create a resource of the same kind.".format(
name
)
)
return dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to create object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def apply(
self,
resource: Resource,
definition: Dict,
existing: Optional[ResourceInstance] = None,
) -> Dict:
namespace = definition["metadata"].get("namespace")
server_side_apply = self.module.params.get("server_side_apply")
if server_side_apply:
requires("kubernetes", "19.15.0", reason="to use server side apply")
if self._client_side_dry_run:
ignored, patch = apply_object(resource, _encode_stringdata(definition))
if existing:
k8s_obj = dict_merge(existing.to_dict(), patch)
else:
k8s_obj = patch
else:
try:
params = {}
if server_side_apply:
params["server_side"] = True
params.update(server_side_apply)
k8s_obj = self.client.apply(
resource, definition, namespace=namespace, **params
).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to apply object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def replace(
self,
resource: Resource,
definition: Dict,
existing: ResourceInstance,
) -> Dict:
append_hash = self.module.params.get("append_hash", False)
name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
if self._client_side_dry_run:
k8s_obj = _encode_stringdata(definition)
else:
try:
k8s_obj = self.client.replace(
resource,
definition,
name=name,
namespace=namespace,
append_hash=append_hash,
).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to replace object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def update(
self, resource: Resource, definition: Dict, existing: ResourceInstance
) -> Dict:
name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
if self._client_side_dry_run:
k8s_obj = dict_merge(existing.to_dict(), _encode_stringdata(definition))
else:
exception = None
for merge_type in self.module.params.get("merge_type") or [
"strategic-merge",
"merge",
]:
try:
k8s_obj = self.patch_resource(
resource,
definition,
name,
namespace,
merge_type=merge_type,
)
exception = None
except CoreException as e:
exception = e
continue
break
if exception:
raise exception
return k8s_obj
def delete(
self,
resource: Resource,
definition: Dict,
existing: Optional[ResourceInstance] = None,
) -> Dict:
delete_options = self.module.params.get("delete_options")
label_selectors = self.module.params.get("label_selectors")
name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
params = {}
if not exists(existing):
return {}
# Delete the object
if self._client_side_dry_run:
return {}
if name:
params["name"] = name
if namespace:
params["namespace"] = namespace
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
if delete_options:
body = {
"apiVersion": "v1",
"kind": "DeleteOptions",
}
body.update(delete_options)
params["body"] = body
try:
k8s_obj = self.client.delete(resource, **params).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to delete object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def diff_objects(
existing: Dict, new: Dict, hidden_fields: Optional[list] = None
) -> Tuple[bool, Dict]:
result = {}
diff = recursive_diff(existing, new)
if not diff:
return True, result
result["before"] = diff[0]
result["after"] = diff[1]
if list(result["after"].keys()) != ["metadata"] or list(
result["before"].keys()
) != ["metadata"]:
return False, result
# If only metadata.generation and metadata.resourceVersion changed, ignore it
ignored_keys = set(["generation", "resourceVersion"])
if not set(result["after"]["metadata"].keys()).issubset(ignored_keys):
return False, result
if not set(result["before"]["metadata"].keys()).issubset(ignored_keys):
return False, result
result["before"] = hide_fields(result["before"], hidden_fields)
result["after"] = hide_fields(result["after"], hidden_fields)
return True, result
def hide_fields(definition: dict, hidden_fields: Optional[list]) -> dict:
if not hidden_fields:
return definition
result = copy.deepcopy(definition)
for hidden_field in hidden_fields:
result = hide_field(result, hidden_field)
return result
# hide_field is not hugely sophisticated and designed to cope
# with e.g. status or metadata.managedFields rather than e.g.
# spec.template.spec.containers[0].env[3].value
def hide_field(definition: dict, hidden_field: str) -> dict:
split = hidden_field.split(".", 1)
if split[0] in definition:
if len(split) == 2:
definition[split[0]] = hide_field(definition[split[0]], split[1])
else:
del definition[split[0]]
return definition

View File

@@ -0,0 +1,244 @@
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)
RESOURCE_PREDICATES = {
"DaemonSet": daemonset_ready,
"Deployment": deployment_ready,
"Pod": pod_ready,
"StatefulSet": statefulset_ready,
}
def empty_list(resource: ResourceInstance) -> bool:
return resource["kind"].endswith("List") and not resource.get("items")
def clock(total: int, interval: int) -> Iterator[int]:
start = time.monotonic()
yield 0
while (time.monotonic() - start) < total:
time.sleep(interval)
yield int(time.monotonic() - start)
class Waiter:
def __init__(
self, client, resource: Resource, predicate: Callable[[ResourceInstance], bool]
):
self.client = client
self.resource = resource
self.predicate = predicate
def wait(
self,
timeout: int,
sleep: int,
name: Optional[str] = None,
namespace: Optional[str] = None,
label_selectors: Optional[List[str]] = None,
field_selectors: Optional[List[str]] = None,
) -> Tuple[bool, Dict, int]:
params = {}
if name:
params["name"] = name
if namespace:
params["namespace"] = namespace
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
if field_selectors:
params["field_selector"] = ",".join(field_selectors)
instance = {}
response = None
elapsed = 0
for i in clock(timeout, sleep):
exception = None
elapsed = i
try:
response = self.client.get(self.resource, **params)
except NotFoundError:
response = None
# Retry connection errors as it may be intermittent network issues
except HTTPError as e:
exception = e
if self.predicate(response):
break
if exception:
msg = (
"Exception '{0}' raised while trying to get resource using {1}".format(
exception, params
)
)
raise CoreException(msg) from exception
if response:
instance = response.to_dict()
return self.predicate(response), instance, elapsed
class DummyWaiter:
"""A no-op waiter that simply returns the item being waited on.
No API call will be made with this waiter; the function returns
immediately. This waiter is useful for waiting on resource instances in
check mode, for example.
"""
def wait(
self,
definition: Dict,
timeout: int,
sleep: int,
label_selectors: Optional[List[str]] = None,
) -> Tuple[bool, Optional[Dict], int]:
return True, definition, 0
# The better solution would be typing.Protocol, but this is only in 3.8+
SupportsWait = Union[Waiter, DummyWaiter]
def get_waiter(
client,
resource: Resource,
state: str = "present",
condition: Optional[Dict] = None,
check_mode: Optional[bool] = False,
) -> SupportsWait:
"""Create a Waiter object based on the specified resource.
This is a convenience method for creating a waiter from a resource.
Based on the arguments and the kind of resource, an appropriate waiter
will be returned. A waiter can also be created directly, of course.
"""
if check_mode:
return DummyWaiter()
if state == "present":
if condition:
predicate: Callable[[ResourceInstance], bool] = partial(
custom_condition, condition
)
else:
predicate = RESOURCE_PREDICATES.get(resource.kind, exists)
else:
predicate = resource_absent
return Waiter(client, resource, predicate)

View File

@@ -14,26 +14,36 @@
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 ansible_collections.kubernetes.core.plugins.module_utils.exceptions import (
ApplyException,
)
from kubernetes.dynamic import DynamicClient
class K8SDynamicClient(DynamicClient):
def apply(self, resource, body=None, name=None, namespace=None):
def apply(self, resource, body=None, name=None, namespace=None, **kwargs):
body = super().serialize_body(body)
body['metadata'] = body.get('metadata', dict())
name = name or body['metadata'].get('name')
body["metadata"] = body.get("metadata", dict())
name = name or body["metadata"].get("name")
if not name:
raise ValueError("name is required to apply {0}.{1}".format(resource.group_version, resource.kind))
raise ValueError(
"name is required to apply {0}.{1}".format(
resource.group_version, resource.kind
)
)
if resource.namespaced:
body['metadata']['namespace'] = super().ensure_namespace(resource, namespace, body)
body["metadata"]["namespace"] = super().ensure_namespace(
resource, namespace, body
)
try:
return k8s_apply(resource, body)
return k8s_apply(resource, body, **kwargs)
except ApplyException as e:
raise ValueError("Could not apply strategic merge to %s/%s: %s" %
(body['kind'], body['metadata']['name'], e))
raise ValueError(
"Could not apply strategic merge to %s/%s: %s"
% (body["kind"], body["metadata"]["name"], e)
)

View File

@@ -16,8 +16,7 @@ import re
class Selector(object):
equality_based_operators = ('==', '!=', '=')
equality_based_operators = ("==", "!=", "=")
def __init__(self, data):
self._operator = None
@@ -27,18 +26,23 @@ class Selector(object):
for op in self.equality_based_operators:
idx = no_whitespace_data.find(op)
if idx != -1:
self._operator = "in" if op == '==' or op == '=' else "notin"
self._operator = "in" if op == "==" or op == "=" else "notin"
self._key = no_whitespace_data[0:idx]
# fmt: off
self._data = [no_whitespace_data[idx + len(op):]]
# fmt: on
break
def parse_set_based_requirement(self, data):
m = re.match(r'( *)([a-z0-9A-Z][a-z0-9A-Z\._-]*[a-z0-9A-Z])( +)(notin|in)( +)\((.*)\)( *)', data)
m = re.match(
r"( *)([a-z0-9A-Z][a-z0-9A-Z\._-]*[a-z0-9A-Z])( +)(notin|in)( +)\((.*)\)( *)",
data,
)
if m:
self._set_based_requirement = True
self._key = m.group(2)
self._operator = m.group(4)
self._data = [x.replace(' ', '') for x in m.group(6).split(',') if x != '']
self._data = [x.replace(" ", "") for x in m.group(6).split(",") if x != ""]
return True
elif all(x not in data for x in self.equality_based_operators):
self._key = data.rstrip(" ").lstrip(" ")
@@ -54,18 +58,21 @@ class Selector(object):
elif self._operator == "notin":
return self._key not in labels or labels.get(self._key) not in self._data
else:
return self._key not in labels if self._operator == "!" else self._key in labels
return (
self._key not in labels
if self._operator == "!"
else self._key in labels
)
class LabelSelectorFilter(object):
def __init__(self, label_selectors):
self.selectors = [Selector(data) for data in label_selectors]
def isMatching(self, definition):
if "metadata" not in definition or "labels" not in definition['metadata']:
if "metadata" not in definition or "labels" not in definition["metadata"]:
return False
labels = definition['metadata']['labels']
labels = definition["metadata"]["labels"]
if not isinstance(labels, dict):
return None
return all(sel.isMatch(labels) for sel in self.selectors)

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""Provide version object to compare version numbers."""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
# Once we drop support for Ansible 2.9, ansible-base 2.10, and ansible-core 2.11, we can
# remove the _version.py file, and replace the following import by
#
# from ansible.module_utils.compat.version import LooseVersion
from ._version import LooseVersion # noqa: F401

View File

@@ -4,16 +4,17 @@
# Copyright: 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'''
DOCUMENTATION = r"""
---
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)
@@ -26,6 +27,10 @@ requirements:
description:
- Install, upgrade, delete packages with the Helm package manager.
notes:
- The default idempotency check can fail to report changes when C(release_state) is set to C(present)
and C(chart_repo_url) is defined. Install helm diff >= 3.4.1 for better results.
options:
chart_ref:
description:
@@ -46,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.
@@ -83,12 +99,52 @@ 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.
- 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
#Helm options
disable_hook:
@@ -108,13 +164,24 @@ options:
type: bool
wait:
description:
- Wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful.
- When I(release_state) is set to C(present), wait until all Pods, PVCs, Services,
and minimum number of Pods of a Deployment are in a ready state before marking the release as successful.
- When I(release_state) is set to C(absent), will wait until all the resources are deleted before returning.
It will wait for as long as I(wait_timeout). This feature requires helm>=3.7.0. Added in version 2.3.0.
default: False
type: bool
wait_timeout:
description:
- Timeout when wait option is enabled (helm2 is a number of seconds, helm3 is a duration).
- The use of I(wait_timeout) to wait for kubernetes commands to complete has been deprecated and will be removed after 2022-12-01.
type: str
timeout:
description:
- A Go duration (described here I(https://pkg.go.dev/time#ParseDuration)) value to wait for Kubernetes commands to complete. This defaults to 5m0s.
- 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
atomic:
description:
- If set, the installation process deletes the installation on failure.
@@ -125,7 +192,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.
@@ -133,24 +205,24 @@ 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
extends_documentation_fragment:
- kubernetes.core.helm_common_options
'''
"""
EXAMPLES = r'''
EXAMPLES = r"""
- name: Deploy latest version of Prometheus chart inside monitoring namespace (and create it)
kubernetes.core.helm:
name: test
@@ -193,6 +265,22 @@ EXAMPLES = r'''
state: absent
wait: true
- name: Separately update the repository cache
kubernetes.core.helm:
name: dummy
namespace: kube-system
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:
@@ -237,7 +325,18 @@ 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"""
status:
@@ -294,22 +393,31 @@ command:
sample: helm upgrade ...
"""
import copy
import re
import tempfile
import traceback
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
LooseVersion,
)
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,
parse_helm_plugin_list
AnsibleHelmModule,
parse_helm_plugin_list,
)
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
HELM_AUTH_ARG_SPEC,
)
@@ -320,63 +428,103 @@ def get_release(state, release_name):
if state is not None:
for release in state:
if release['name'] == release_name:
if release["name"] == release_name:
return release
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
list_command = [
module.get_helm_binary(),
"list",
"--output=yaml",
"--filter",
release_name,
]
if all_status:
list_command.append("--all")
rc, out, err = run_helm(module, list_command)
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["values"] = module.get_values(release_name)
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 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):
"""
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)
rc, out, err = module.run_helm_command(inspect_command)
return yaml.safe_load(out)
def deploy(command, release_name, release_values, chart_name, wait,
wait_timeout, disable_hook, force, values_files, history_max, atomic=False,
create_namespace=False, replace=False, skip_crds=False):
def deploy(
module,
command,
release_name,
release_values,
chart_name,
wait,
wait_timeout,
disable_hook,
force,
values_files,
history_max,
atomic=False,
create_namespace=False,
replace=False,
post_renderer=None,
skip_crds=False,
timeout=None,
dependency_update=None,
set_value_args=None,
reuse_values=None,
reset_values=True,
):
"""
Install/upgrade/rollback release chart
"""
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"
# 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 wait:
deploy_command += " --wait"
@@ -386,6 +534,9 @@ def deploy(command, release_name, release_values, chart_name, wait,
if atomic:
deploy_command += " --atomic"
if timeout:
deploy_command += " --timeout " + timeout
if force:
deploy_command += " --force"
@@ -403,10 +554,14 @@ def deploy(command, release_name, release_values, chart_name, wait,
deploy_command += " --values=" + value_file
if release_values != {}:
fd, path = tempfile.mkstemp(suffix='.yml')
with open(path, 'w') as yaml_file:
fd, path = tempfile.mkstemp(suffix=".yml")
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"
@@ -414,11 +569,14 @@ def deploy(command, release_name, release_values, chart_name, wait,
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
deploy_command += " " + release_name + f" '{chart_name}'"
return deploy_command
def delete(command, release_name, purge, disable_hook):
def delete(command, release_name, purge, disable_hook, wait, wait_timeout):
"""
Delete release chart
"""
@@ -431,6 +589,12 @@ def delete(command, release_name, purge, disable_hook):
if disable_hook:
delete_command += " --no-hooks"
if wait:
delete_command += " --wait"
if wait_timeout is not None:
delete_command += " --timeout " + wait_timeout
delete_command += " " + release_name
return delete_command
@@ -439,7 +603,7 @@ def delete(command, release_name, purge, disable_hook):
def load_values_files(values_files):
values = {}
for values_file in values_files or []:
with open(values_file, 'r') as fd:
with open(values_file, "r") as fd:
content = yaml.safe_load(fd)
if not isinstance(content, dict):
continue
@@ -448,50 +612,72 @@ def load_values_files(values_files):
return values
def has_plugin(command, plugin):
def get_plugin_version(plugin):
"""
Check if helm plugin is installed.
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 False
return None
for line in out:
if line[0] == plugin:
return True
return False
return line[1]
return None
def helmdiff_check(module, helm_cmd, release_name, chart_ref, release_values,
values_files=None, chart_version=None, replace=False):
def helmdiff_check(
module,
release_name,
chart_ref,
release_values,
values_files=None,
chart_version=None,
replace=False,
chart_repo_url=None,
post_renderer=False,
set_value_args=None,
reuse_values=None,
reset_values=True,
):
"""
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
if chart_repo_url is not None:
cmd += " " + "--repo=" + chart_repo_url
if chart_version is not None:
cmd += " " + "--version=" + chart_version
if not replace:
cmd += " " + "--reset-values"
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
cmd += " " + "--reset-values=" + str(reset_values)
if post_renderer:
cmd += " --post-renderer=" + post_renderer
if values_files:
for values_file in values_files:
cmd += " -f=" + values_file
for value_file in values_files:
cmd += " --values=" + value_file
rc, out, err = run_helm(module, cmd)
return len(out.strip()) > 0
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 set_value_args:
cmd += " " + set_value_args
if reuse_values:
cmd += " --reuse-values"
rc, out, err = module.run_helm_command(cmd)
return (len(out.strip()) > 0, out.strip())
def default_check(release_status, chart_info, values=None, values_files=None):
@@ -499,64 +685,73 @@ def default_check(release_status, chart_info, values=None, values_files=None):
Use default check to determine if release would change by upgrading a chart.
"""
# the 'appVersion' specification is optional in a chart
chart_app_version = chart_info.get('appVersion', None)
released_app_version = release_status.get('app_version', None)
chart_app_version = chart_info.get("appVersion", None)
released_app_version = release_status.get("app_version", None)
# when deployed without an 'appVersion' chart value the 'helm list' command will return the entry `app_version: ""`
appversion_is_same = (chart_app_version == released_app_version) or (chart_app_version is None and released_app_version == "")
appversion_is_same = (chart_app_version == released_app_version) or (
chart_app_version is None and released_app_version == ""
)
if values_files:
values_match = release_status['values'] == load_values_files(values_files)
values_match = release_status["values"] == load_values_files(values_files)
else:
values_match = release_status['values'] == values
return not values_match \
or (chart_info['name'] + '-' + chart_info['version']) != release_status["chart"] \
values_match = release_status["values"] == values
return (
not values_match
or (chart_info["name"] + "-" + chart_info["version"]) != release_status["chart"]
or not appversion_is_same
)
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(
default="present", choices=["present", "absent"], aliases=["state"]
),
release_values=dict(type="dict", default={}, aliases=["values"]),
values_files=dict(type="list", default=[], elements="str"),
update_repo_cache=dict(type="bool", default=False),
disable_hook=dict(type="bool", default=False),
force=dict(type="bool", default=False),
purge=dict(type="bool", default=True),
wait=dict(type="bool", default=False),
wait_timeout=dict(type="str"),
timeout=dict(type="str"),
atomic=dict(type="bool", default=False),
create_namespace=dict(type="bool", default=False),
post_renderer=dict(type="str"),
replace=dict(type="bool", default=False),
skip_crds=dict(type="bool", default=False),
history_max=dict(type="int"),
set_values=dict(type="list", elements="dict"),
reuse_values=dict(type="bool"),
reset_values=dict(type="bool", default=True),
)
)
return arg_spec
def main():
global module
module = AnsibleModule(
argument_spec=dict(
binary_path=dict(type='path'),
chart_ref=dict(type='path'),
chart_repo_url=dict(type='str'),
chart_version=dict(type='str'),
release_name=dict(type='str', required=True, aliases=['name']),
release_namespace=dict(type='str', required=True, aliases=['namespace']),
release_state=dict(default='present', choices=['present', 'absent'], aliases=['state']),
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'),
atomic=dict(type='bool', default=False),
create_namespace=dict(type='bool', default=False),
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'])),
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']))
),
module = AnsibleHelmModule(
argument_spec=argument_spec(),
required_if=[
('release_state', 'present', ['release_name', 'chart_ref']),
('release_state', 'absent', ['release_name'])
("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"),
],
supports_check_mode=True,
)
@@ -566,49 +761,62 @@ def main():
changed = False
bin_path = module.params.get('binary_path')
chart_ref = module.params.get('chart_ref')
chart_repo_url = module.params.get('chart_repo_url')
chart_version = module.params.get('chart_version')
release_name = module.params.get('release_name')
release_state = module.params.get('release_state')
release_values = module.params.get('release_values')
values_files = module.params.get('values_files')
update_repo_cache = module.params.get('update_repo_cache')
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")
values_files = module.params.get("values_files")
update_repo_cache = module.params.get("update_repo_cache")
# Helm options
disable_hook = module.params.get('disable_hook')
force = module.params.get('force')
purge = module.params.get('purge')
wait = module.params.get('wait')
wait_timeout = module.params.get('wait_timeout')
atomic = module.params.get('atomic')
create_namespace = module.params.get('create_namespace')
replace = module.params.get('replace')
skip_crds = module.params.get('skip_crds')
history_max = module.params.get('history_max')
if bin_path is not None:
helm_cmd_common = bin_path
else:
helm_cmd_common = module.get_bin_path('helm', required=True)
disable_hook = module.params.get("disable_hook")
force = module.params.get("force")
purge = module.params.get("purge")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
atomic = module.params.get("atomic")
create_namespace = module.params.get("create_namespace")
post_renderer = module.params.get("post_renderer")
replace = module.params.get("replace")
skip_crds = module.params.get("skip_crds")
history_max = module.params.get("history_max")
timeout = module.params.get("timeout")
set_values = module.params.get("set_values")
reuse_values = module.params.get("reuse_values")
reset_values = module.params.get("reset_values")
if 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)
# keep helm_cmd_common for get_release_status in module_exit_json
helm_cmd = helm_cmd_common
helm_cmd = module.get_helm_binary()
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")
helm_cmd = delete(helm_cmd, release_name, purge, disable_hook)
changed = True
if wait:
helm_version = module.get_helm_version()
if LooseVersion(helm_version) < LooseVersion("3.7.0"):
opt_result["warnings"] = []
opt_result["warnings"].append(
"helm uninstall support option --wait for helm release >= 3.7.0"
)
wait = False
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
@@ -618,67 +826,161 @@ def main():
# Fetch chart info to have real version and real name for chart_ref from archive, folder or url
chart_info = fetch_chart_info(module, helm_cmd, chart_ref)
if dependency_update:
if chart_info.get("dependencies"):
# Can't use '--dependency-update' with 'helm upgrade' that is the
# default chart install method, so if chart_repo_url is defined
# we can't use the dependency update command. But, in the near future
# we can get rid of this method and use only '--dependency-update'
# option. Please see https://github.com/helm/helm/pull/8810
if not chart_repo_url and not re.fullmatch(
r"^http[s]*://[\w.:/?&=-]+$", chart_ref
):
run_dep_update(module, chart_ref)
# To not add --dependency-update option in the deploy function
dependency_update = False
else:
module.warn(
"This is a not stable feature with 'chart_repo_url'. Please consider to use dependency update with on-disk charts"
)
if not replace:
msg_fail = (
"'--dependency-update' hasn't been supported yet with 'helm upgrade'. "
"Please use 'helm install' instead by adding 'replace' option"
)
module.fail_json(msg=msg_fail)
else:
module.warn(
"There is no dependencies block defined in Chart.yaml. Dependency update will not be performed. "
"Please consider add dependencies block or disable dependency_update to remove this warning."
)
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(helm_cmd, release_name, release_values, chart_ref, wait, wait_timeout,
disable_hook, False, values_files=values_files, atomic=atomic,
create_namespace=create_namespace, replace=replace,
skip_crds=skip_crds, history_max=history_max)
helm_cmd = deploy(
module,
helm_cmd,
release_name,
release_values,
chart_ref,
wait,
wait_timeout,
disable_hook,
False,
values_files=values_files,
atomic=atomic,
create_namespace=create_namespace,
post_renderer=post_renderer,
replace=replace,
dependency_update=dependency_update,
skip_crds=skip_crds,
history_max=history_max,
timeout=timeout,
set_value_args=set_value_args,
reuse_values=reuse_values,
reset_values=reset_values,
)
changed = True
else:
if has_plugin(helm_cmd_common, "diff") and not chart_repo_url:
would_change = helmdiff_check(module, helm_cmd_common, release_name, chart_ref,
release_values, values_files, chart_version, replace)
helm_diff_version = get_plugin_version("diff")
if helm_diff_version and (
not chart_repo_url
or (
chart_repo_url
and LooseVersion(helm_diff_version) >= LooseVersion("3.4.1")
)
):
(would_change, prepared) = helmdiff_check(
module,
release_name,
chart_ref,
release_values,
values_files,
chart_version,
replace,
chart_repo_url,
post_renderer,
set_value_args,
reuse_values=reuse_values,
reset_values=reset_values,
)
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 for better results.")
would_change = default_check(release_status, chart_info, release_values, values_files)
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(helm_cmd, release_name, release_values, chart_ref, wait, wait_timeout,
disable_hook, force, values_files=values_files, atomic=atomic,
create_namespace=create_namespace, replace=replace,
skip_crds=skip_crds, history_max=history_max)
helm_cmd = deploy(
module,
helm_cmd,
release_name,
release_values,
chart_ref,
wait,
wait_timeout,
disable_hook,
force,
values_files=values_files,
atomic=atomic,
create_namespace=create_namespace,
post_renderer=post_renderer,
replace=replace,
skip_crds=skip_crds,
history_max=history_max,
timeout=timeout,
dependency_update=dependency_update,
set_value_args=set_value_args,
reuse_values=reuse_values,
reset_values=reset_values,
)
changed = True
if module.check_mode:
check_status = {
'values': {
"current": {},
"declared": {},
}
}
check_status = {"values": {"current": {}, "declared": {}}}
if release_status:
check_status['values']['current'] = release_status['values']
check_status['values']['declared'] = release_status
check_status["values"]["current"] = release_status["values"]
check_status["values"]["declared"] = release_status
module.exit_json(
changed=changed,
command=helm_cmd,
status=check_status,
stdout='',
stderr='',
stdout="",
stderr="",
**opt_result,
)
elif not changed:
module.exit_json(
changed=False,
status=release_status,
stdout='',
stderr='',
stdout="",
stderr="",
command=helm_cmd,
**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,
)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@@ -4,16 +4,17 @@
# Copyright: (c) 2020, 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'''
DOCUMENTATION = r"""
---
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)
@@ -38,18 +39,48 @@ options:
required: true
type: str
aliases: [ namespace ]
release_state:
description:
- Show releases as per their states.
- Default value is C(deployed) and C(failed).
- If set to C(all), show all releases without any filter applied.
- If set to C(deployed), show deployed releases.
- If set to C(failed), show failed releases.
- If set to C(pending), show pending releases.
- If set to C(superseded), show superseded releases.
- If set to C(uninstalled), show uninstalled releases, if C(helm uninstall --keep-history) was used.
- If set to C(uninstalling), show releases that are currently being uninstalled.
type: list
elements: str
default: []
version_added: 2.3.0
get_all_values:
description:
- Set to C(True) if you want to get all (computed) values of the release.
- When C(False) (default), only user supplied values are returned.
required: false
default: false
type: bool
version_added: 2.4.0
extends_documentation_fragment:
- kubernetes.core.helm_common_options
'''
"""
EXAMPLES = r'''
- name: Deploy latest version of Grafana chart inside monitoring namespace
EXAMPLES = r"""
- name: Gather information of Grafana chart inside monitoring namespace
kubernetes.core.helm_info:
name: test
release_namespace: monitoring
'''
RETURN = r'''
- name: Gather information about test-chart with pending state
kubernetes.core.helm_info:
name: test-chart
release_namespace: testenv
release_state:
- pending
"""
RETURN = r"""
status:
type: complex
description: A dictionary of status output
@@ -87,40 +118,83 @@ status:
type: str
returned: always
description: Dict of Values used to deploy
'''
hooks:
type: list
elements: dict
description: Hooks of the release
returned: always
version_added: 2.4.0
notes:
type: str
description: Notes of the release
returned: always
version_added: 2.4.0
manifest:
type: list
elements: dict
description: Manifest of the release
returned: always
version_added: 2.4.0
"""
import copy
import traceback
try:
import yaml
IMP_YAML = True
IMP_YAML_ERR = None
except ImportError:
IMP_YAML_ERR = traceback.format_exc()
IMP_YAML = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm, get_values
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 Release from all deployed releases
def get_release(state, release_name):
if state is not None:
for release in state:
if release['name'] == release_name:
if release["name"] == release_name:
return release
return None
# Get Release state from deployed release
def get_release_status(module, command, release_name):
list_command = command + " list --output=yaml --filter " + release_name
def get_release_status(module, release_name, release_state, get_all_values=False):
list_command = module.get_helm_binary() + " list --output=yaml"
rc, out, err = run_helm(module, list_command)
valid_release_states = [
"all",
"deployed",
"failed",
"pending",
"superseded",
"uninstalled",
"uninstalling",
]
for local_release_state in release_state:
if local_release_state in valid_release_states:
list_command += " --%s" % local_release_state
list_command += " --filter " + release_name
rc, out, err = module.run_helm_command(list_command)
if rc != 0:
module.fail_json(
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
command=list_command
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(
rc, out, err
),
command=list_command,
)
release = get_release(yaml.safe_load(out), release_name)
@@ -128,51 +202,46 @@ def get_release_status(module, command, release_name):
if release is None: # not install
return None
release['values'] = get_values(module, command, release_name)
release["values"] = module.get_values(release_name, get_all_values)
release["manifest"] = module.get_manifest(release_name)
release["notes"] = module.get_notes(release_name)
release["hooks"] = module.get_hooks(release_name)
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']))
),
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')
release_name = module.params.get('release_name')
release_name = module.params.get("release_name")
release_state = module.params.get("release_state")
get_all_values = module.params.get("get_all_values")
if bin_path is not None:
helm_cmd_common = bin_path
else:
helm_cmd_common = module.get_bin_path('helm', required=True)
release_status = get_release_status(module, helm_cmd_common, release_name)
release_status = get_release_status(
module, release_name, release_state, get_all_values
)
if release_status is not None:
module.exit_json(changed=False, status=release_status)
@@ -180,5 +249,5 @@ def main():
module.exit_json(changed=False)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@@ -8,11 +8,11 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
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:
@@ -24,14 +24,15 @@ options:
state:
description:
- If C(state=present) the Helm plugin will be installed.
- If C(state=latest) the Helm plugin will be updated. Added in version 2.3.0.
- If C(state=absent) the Helm plugin will be removed.
choices: [ absent, present ]
choices: [ absent, present, latest ]
default: present
type: str
plugin_name:
description:
- Name of Helm plugin.
- Required only if C(state=absent).
- Required only if C(state=absent) or C(state=latest).
type: str
plugin_path:
description:
@@ -40,11 +41,18 @@ options:
machine and not on Ansible controller.
- Required only if C(state=present).
type: str
plugin_version:
description:
- Plugin version to install. If this is not specified, the latest version is installed.
- Ignored when C(state=absent) or C(state=latest).
required: false
type: str
version_added: 2.3.0
extends_documentation_fragment:
- kubernetes.core.helm_common_options
'''
"""
EXAMPLES = r'''
EXAMPLES = r"""
- name: Install Helm env plugin
kubernetes.core.helm_plugin:
plugin_path: https://github.com/adamreese/helm-env
@@ -59,9 +67,20 @@ EXAMPLES = r'''
kubernetes.core.helm_plugin:
plugin_name: env
state: absent
'''
RETURN = r'''
- name: Install Helm plugin with a specific version
kubernetes.core.helm_plugin:
plugin_version: 2.0.1
plugin_path: https://domain/path/to/plugin.tar.gz
state: present
- name: Update Helm plugin
kubernetes.core.helm_plugin:
plugin_name: secrets
state: latest
"""
RETURN = r"""
stdout:
type: str
description: Full `helm` command stdout, in case you want to display it or examine the event log
@@ -87,67 +106,78 @@ rc:
description: Helm plugin command return code
returned: always
sample: 1
'''
"""
import copy
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
run_helm,
get_helm_plugin_list,
parse_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'),
state=dict(type='str', default='present', choices=['present', 'absent']),
plugin_path=dict(type='str',),
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'])),
def argument_spec():
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
arg_spec.update(
dict(
plugin_path=dict(
type="str",
),
plugin_name=dict(
type="str",
),
plugin_version=dict(
type="str",
),
state=dict(
type="str",
default="present",
choices=["present", "absent", "latest"],
),
)
)
return arg_spec
# Generic auth key
host=dict(type='str', fallback=(env_fallback, ['K8S_AUTH_HOST'])),
ca_cert=dict(type='path', aliases=['ssl_ca_cert'], fallback=(env_fallback, ['K8S_AUTH_SSL_CA_CERT'])),
validate_certs=dict(type='bool', default=True, aliases=['verify_ssl'], fallback=(env_fallback, ['K8S_AUTH_VERIFY_SSL'])),
api_key=dict(type='str', no_log=True, fallback=(env_fallback, ['K8S_AUTH_API_KEY']))
),
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')
state = module.params.get('state')
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_helm_binary() + " plugin"
helm_cmd_common = module.get_bin_path(helm_cmd_common, required=True)
helm_cmd_common += " plugin"
if state == 'present':
helm_cmd_common += " install %s" % module.params.get('plugin_path')
if state == "present":
helm_cmd_common += " install %s" % module.params.get("plugin_path")
plugin_version = module.params.get("plugin_version")
if plugin_version is not None:
helm_cmd_common += " --version=%s" % plugin_version
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, '', '')
rc, out, err = (0, "", "")
if rc == 1 and 'plugin already exists' in err:
if rc == 1 and "plugin already exists" in err:
module.exit_json(
failed=False,
changed=False,
@@ -155,7 +185,7 @@ def main():
command=helm_cmd_common,
stdout=out,
stderr=err,
rc=rc
rc=rc,
)
elif rc == 0:
module.exit_json(
@@ -175,20 +205,20 @@ def main():
stderr=err,
rc=rc,
)
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())
elif state == "absent":
plugin_name = module.params.get("plugin_name")
rc, output, err, command = module.get_helm_plugin_list()
out = parse_helm_plugin_list(output=output.splitlines())
if not out:
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
rc=rc,
)
found = False
@@ -201,17 +231,19 @@ def main():
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
rc=rc,
)
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, '', '')
rc, out, err = (0, "", "")
if rc == 0:
module.exit_json(
@@ -220,7 +252,7 @@ def main():
command=helm_uninstall_cmd,
stdout=out,
stderr=err,
rc=rc
rc=rc,
)
module.fail_json(
msg="Failed to get Helm plugin uninstall",
@@ -229,7 +261,63 @@ def main():
stderr=err,
rc=rc,
)
elif state == "latest":
plugin_name = module.params.get("plugin_name")
rc, output, err, command = module.get_helm_plugin_list()
out = parse_helm_plugin_list(output=output.splitlines())
if not out:
module.exit_json(
failed=False,
changed=False,
msg="Plugin not found",
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:
module.exit_json(
failed=False,
changed=False,
msg="Plugin not found",
command=command,
stdout=output,
stderr=err,
rc=rc,
)
helm_update_cmd = "%s update %s" % (helm_cmd_common, plugin_name)
if not module.check_mode:
rc, out, err = module.run_helm_command(
helm_update_cmd, fails_on_error=False
)
else:
rc, out, err = (0, "", "")
if rc == 0:
module.exit_json(
changed=True,
msg="Plugin updated successfully",
command=helm_update_cmd,
stdout=out,
stderr=err,
rc=rc,
)
module.fail_json(
msg="Failed to get Helm plugin update",
command=helm_update_cmd,
stdout=out,
stderr=err,
rc=rc,
)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@@ -8,11 +8,11 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
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:
@@ -27,18 +27,18 @@ options:
type: str
extends_documentation_fragment:
- kubernetes.core.helm_common_options
'''
"""
EXAMPLES = r'''
EXAMPLES = r"""
- name: Gather Helm plugin info
kubernetes.core.helm_plugin_info:
- name: Gather Helm env plugin info
kubernetes.core.helm_plugin_info:
plugin_name: env
'''
"""
RETURN = r'''
RETURN = r"""
stdout:
type: str
description: Full `helm` command stdout, in case you want to display it or examine the event log
@@ -68,82 +68,60 @@ rc:
description: Helm plugin command return code
returned: always
sample: 1
'''
"""
import copy
from ansible.module_utils.basic import AnsibleModule, env_fallback
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'),
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'])),
argument_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
argument_spec.update(
dict(
plugin_name=dict(
type="str",
),
)
)
# 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"
plugin_name = module.params.get('plugin_name')
plugin_name = module.params.get("plugin_name")
plugin_list = []
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
rc, output, err, command = module.get_helm_plugin_list()
out = parse_helm_plugin_list(module, output=output.splitlines())
out = parse_helm_plugin_list(output=output.splitlines())
for line in out:
if plugin_name is None:
plugin_list.append(
{
"name": line[0],
"version": line[1],
"description": line[2],
}
{"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],
}
{"name": line[0], "version": line[1], "description": line[2]}
)
break
module.exit_json(
changed=True,
command=helm_cmd_common + " list",
command=command,
stdout=output,
stderr=err,
rc=rc,
@@ -151,5 +129,5 @@ def main():
)
if __name__ == '__main__':
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More