mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-11 20:12:18 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69304d1c3b | ||
|
|
fdddb6f78f | ||
|
|
e8cf1ef517 | ||
|
|
6dbc0d5b6d | ||
|
|
0a72c87d2c | ||
|
|
c149394556 | ||
|
|
e2dec91460 | ||
|
|
edf104d687 | ||
|
|
7b09c01d98 | ||
|
|
7409eaf993 | ||
|
|
321b6dcdd8 | ||
|
|
68d45af767 |
@@ -5,6 +5,27 @@ Kubernetes Collection Release Notes
|
|||||||
.. contents:: Topics
|
.. contents:: Topics
|
||||||
|
|
||||||
|
|
||||||
|
v2.3.2
|
||||||
|
======
|
||||||
|
|
||||||
|
Minor Changes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- helm_repository - mark `pass_credentials` as no_log=True to silence false warning (https://github.com/ansible-collections/kubernetes.core/issues/412).
|
||||||
|
- kubectl.py - replace distutils.spawn.find_executable with shutil.which in the kubectl connection plugin (https://github.com/ansible-collections/kubernetes.core/pull/456).
|
||||||
|
|
||||||
|
v2.3.1
|
||||||
|
======
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Catch exception raised when the process is waiting for resources (https://github.com/ansible-collections/kubernetes.core/issues/407).
|
||||||
|
- Remove `omit` placeholder when defining resource using template parameter (https://github.com/ansible-collections/kubernetes.core/issues/431).
|
||||||
|
- k8s - fix the issue when trying to delete resources using label_selectors options (https://github.com/ansible-collections/kubernetes.core/issues/433).
|
||||||
|
- k8s_cp - fix issue when using parameter local_path with file on managed node. (https://github.com/ansible-collections/kubernetes.core/issues/421).
|
||||||
|
- 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
|
v2.3.0
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
|||||||
# Also needs to be updated in galaxy.yml
|
# Also needs to be updated in galaxy.yml
|
||||||
VERSION = 2.3.0
|
VERSION = 2.3.2
|
||||||
|
|
||||||
TEST_ARGS ?= ""
|
TEST_ARGS ?= ""
|
||||||
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`
|
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`
|
||||||
|
|||||||
48
PSF-license.txt
Normal file
48
PSF-license.txt
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||||
|
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||||
|
otherwise using this software ("Python") in source or binary form and
|
||||||
|
its associated documentation.
|
||||||
|
|
||||||
|
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||||
|
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||||
|
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||||
|
distribute, and otherwise use Python alone or in any derivative version,
|
||||||
|
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||||
|
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||||
|
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
|
||||||
|
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||||
|
prepared by Licensee.
|
||||||
|
|
||||||
|
3. In the event Licensee prepares a derivative work that is based on
|
||||||
|
or incorporates Python or any part thereof, and wants to make
|
||||||
|
the derivative work available to others as provided herein, then
|
||||||
|
Licensee hereby agrees to include in any such work a brief summary of
|
||||||
|
the changes made to Python.
|
||||||
|
|
||||||
|
4. PSF is making Python available to Licensee on an "AS IS"
|
||||||
|
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||||
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||||
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||||
|
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
|
||||||
|
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||||
|
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||||
|
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||||
|
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
|
||||||
|
6. This License Agreement will automatically terminate upon a material
|
||||||
|
breach of its terms and conditions.
|
||||||
|
|
||||||
|
7. Nothing in this License Agreement shall be deemed to create any
|
||||||
|
relationship of agency, partnership, or joint venture between PSF and
|
||||||
|
Licensee. This License Agreement does not grant permission to use PSF
|
||||||
|
trademarks or trade name in a trademark sense to endorse or promote
|
||||||
|
products or services of Licensee, or any third party.
|
||||||
|
|
||||||
|
8. By copying, installing or otherwise using Python, Licensee
|
||||||
|
agrees to be bound by the terms and conditions of this License
|
||||||
|
Agreement.
|
||||||
@@ -11,6 +11,8 @@ The collection includes a variety of Ansible content to help automate the manage
|
|||||||
|
|
||||||
This collection has been tested against following Ansible versions: **>=2.9.17**.
|
This collection has been tested against following Ansible versions: **>=2.9.17**.
|
||||||
|
|
||||||
|
For collections that support Ansible 2.9, please ensure you update your `network_os` to use the
|
||||||
|
fully qualified collection name (for example, `cisco.ios.ios`).
|
||||||
Plugins and modules within a collection may be tested with only specific Ansible versions.
|
Plugins and modules within a collection may be tested with only specific Ansible versions.
|
||||||
A collection may contain metadata that identifies these versions.
|
A collection may contain metadata that identifies these versions.
|
||||||
PEP440 is the schema used to describe the versions of Ansible.
|
PEP440 is the schema used to describe the versions of Ansible.
|
||||||
@@ -90,7 +92,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
|
|||||||
---
|
---
|
||||||
collections:
|
collections:
|
||||||
- name: kubernetes.core
|
- name: kubernetes.core
|
||||||
version: 2.3.0
|
version: 2.3.2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Installing the Kubernetes Python Library
|
### Installing the Kubernetes Python Library
|
||||||
|
|||||||
@@ -568,3 +568,33 @@ releases:
|
|||||||
name: k8s_taint
|
name: k8s_taint
|
||||||
namespace: ''
|
namespace: ''
|
||||||
release_date: '2022-03-11'
|
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.3.2:
|
||||||
|
changes:
|
||||||
|
minor_changes:
|
||||||
|
- helm_repository - mark `pass_credentials` as no_log=True to silence false
|
||||||
|
warning (https://github.com/ansible-collections/kubernetes.core/issues/412).
|
||||||
|
- 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:
|
||||||
|
- 412_pass_creds.yml
|
||||||
|
- 456-replace-distutils.yml
|
||||||
|
release_date: '2022-06-09'
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ Parameters
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>Group(s) to impersonate for the operation.</div>
|
<div>Group(s) to impersonate for the operation.</div>
|
||||||
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'</div>
|
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ tags:
|
|||||||
- openshift
|
- openshift
|
||||||
- okd
|
- okd
|
||||||
- cluster
|
- cluster
|
||||||
version: 2.3.0
|
version: 2.3.2
|
||||||
build_ignore:
|
build_ignore:
|
||||||
- .DS_Store
|
- .DS_Store
|
||||||
- '*.tar.gz'
|
- '*.tar.gz'
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import traceback
|
|||||||
import os
|
import os
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
|
||||||
from ansible.config.manager import ensure_type
|
from ansible.config.manager import ensure_type
|
||||||
from ansible.errors import (
|
from ansible.errors import (
|
||||||
AnsibleError,
|
AnsibleError,
|
||||||
@@ -26,6 +25,31 @@ from ansible.module_utils._text import to_text, to_bytes, to_native
|
|||||||
from ansible.plugins.action import ActionBase
|
from ansible.plugins.action import 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]
|
||||||
|
|
||||||
|
|
||||||
class ActionModule(ActionBase):
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
TRANSFERS_FILES = True
|
TRANSFERS_FILES = True
|
||||||
@@ -180,6 +204,7 @@ class ActionModule(ActionBase):
|
|||||||
"'template' is only a supported parameter for the 'k8s' module."
|
"'template' is only a supported parameter for the 'k8s' module."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
omit_value = task_vars.get("omit")
|
||||||
template_params = []
|
template_params = []
|
||||||
if isinstance(template, string_types) or isinstance(template, dict):
|
if isinstance(template, string_types) or isinstance(template, dict):
|
||||||
template_params.append(self.get_template_args(template))
|
template_params.append(self.get_template_args(template))
|
||||||
@@ -245,6 +270,9 @@ class ActionModule(ActionBase):
|
|||||||
preserve_trailing_newlines=True,
|
preserve_trailing_newlines=True,
|
||||||
escape_backslashes=False,
|
escape_backslashes=False,
|
||||||
)
|
)
|
||||||
|
if omit_value is not None:
|
||||||
|
result_template.extend(RemoveOmit(result, omit_value).output())
|
||||||
|
else:
|
||||||
result_template.append(result)
|
result_template.append(result)
|
||||||
self._templar.available_variables = old_vars
|
self._templar.available_variables = old_vars
|
||||||
resource_definition = self._task.args.get("definition", None)
|
resource_definition = self._task.args.get("definition", None)
|
||||||
@@ -352,7 +380,7 @@ class ActionModule(ActionBase):
|
|||||||
|
|
||||||
local_path = self._task.args.get("local_path")
|
local_path = self._task.args.get("local_path")
|
||||||
state = self._task.args.get("state", None)
|
state = self._task.args.get("state", None)
|
||||||
if local_path and state == "to_pod":
|
if local_path and state == "to_pod" and not remote_transport:
|
||||||
new_module_args["local_path"] = self.get_file_realpath(local_path)
|
new_module_args["local_path"] = self.get_file_realpath(local_path)
|
||||||
|
|
||||||
# Execute the k8s_* module.
|
# Execute the k8s_* module.
|
||||||
|
|||||||
@@ -171,9 +171,9 @@ DOCUMENTATION = r"""
|
|||||||
aliases: [ kubectl_verify_ssl ]
|
aliases: [ kubectl_verify_ssl ]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import distutils.spawn
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||||
@@ -219,14 +219,9 @@ class Connection(ConnectionBase):
|
|||||||
# Note: kubectl runs commands as the user that started the container.
|
# Note: kubectl runs commands as the user that started the container.
|
||||||
# It is impossible to set the remote user for a kubectl connection.
|
# It is impossible to set the remote user for a kubectl connection.
|
||||||
cmd_arg = "{0}_command".format(self.transport)
|
cmd_arg = "{0}_command".format(self.transport)
|
||||||
if cmd_arg in kwargs:
|
self.transport_cmd = kwargs.get(cmd_arg, shutil.which(self.transport))
|
||||||
self.transport_cmd = kwargs[cmd_arg]
|
|
||||||
else:
|
|
||||||
self.transport_cmd = distutils.spawn.find_executable(self.transport)
|
|
||||||
if not self.transport_cmd:
|
if not self.transport_cmd:
|
||||||
raise AnsibleError(
|
raise AnsibleError("{0} command not found in PATH".format(self.transport))
|
||||||
"{0} command not found in PATH".format(self.transport)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _build_exec_cmd(self, cmd):
|
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"""
|
||||||
@@ -240,12 +235,12 @@ class Connection(ConnectionBase):
|
|||||||
# Translate verify_ssl to skip_verify_ssl, and output as string
|
# Translate verify_ssl to skip_verify_ssl, and output as string
|
||||||
skip_verify_ssl = not self.get_option(key)
|
skip_verify_ssl = not self.get_option(key)
|
||||||
local_cmd.append(
|
local_cmd.append(
|
||||||
u"{0}={1}".format(
|
"{0}={1}".format(
|
||||||
self.connection_options[key], str(skip_verify_ssl).lower()
|
self.connection_options[key], str(skip_verify_ssl).lower()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
censored_local_cmd.append(
|
censored_local_cmd.append(
|
||||||
u"{0}={1}".format(
|
"{0}={1}".format(
|
||||||
self.connection_options[key], str(skip_verify_ssl).lower()
|
self.connection_options[key], str(skip_verify_ssl).lower()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -262,12 +257,12 @@ class Connection(ConnectionBase):
|
|||||||
else:
|
else:
|
||||||
censored_local_cmd += [cmd_arg, self.get_option(key)]
|
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):
|
if self.get_option(extra_args_name):
|
||||||
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(" ")
|
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:
|
if not pod:
|
||||||
pod = self._play_context.remote_addr
|
pod = self._play_context.remote_addr
|
||||||
# -i is needed to keep stdin open which allows pipelining to work
|
# -i is needed to keep stdin open which allows pipelining to work
|
||||||
@@ -275,7 +270,7 @@ class Connection(ConnectionBase):
|
|||||||
censored_local_cmd += ["exec", "-i", pod]
|
censored_local_cmd += ["exec", "-i", pod]
|
||||||
|
|
||||||
# if the pod has more than one container, then container is required
|
# 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):
|
if self.get_option(container_arg_name):
|
||||||
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)]
|
censored_local_cmd += ["-c", self.get_option(container_arg_name)]
|
||||||
@@ -290,7 +285,7 @@ class Connection(ConnectionBase):
|
|||||||
super(Connection, self)._connect()
|
super(Connection, self)._connect()
|
||||||
if not self._connected:
|
if not self._connected:
|
||||||
display.vvv(
|
display.vvv(
|
||||||
u"ESTABLISH {0} CONNECTION".format(self.transport),
|
"ESTABLISH {0} CONNECTION".format(self.transport),
|
||||||
host=self._play_context.remote_addr,
|
host=self._play_context.remote_addr,
|
||||||
)
|
)
|
||||||
self._connected = True
|
self._connected = True
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ options:
|
|||||||
impersonate_groups:
|
impersonate_groups:
|
||||||
description:
|
description:
|
||||||
- Group(s) to impersonate for the operation.
|
- Group(s) to impersonate for the operation.
|
||||||
- "Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: 'Group1,Group2'"
|
- "Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2"
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
version_added: 2.3.0
|
version_added: 2.3.0
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Implements multiple version numbering conventions for the
|
# Implements multiple version numbering conventions for the
|
||||||
# Python Module Distribution Utilities.
|
# Python Module Distribution Utilities.
|
||||||
#
|
#
|
||||||
# PSF License (see licenses/PSF-license.txt or https://opensource.org/licenses/Python-2.0)
|
# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0)
|
||||||
#
|
#
|
||||||
|
|
||||||
"""Provides classes to represent module version numbers (one class for
|
"""Provides classes to represent module version numbers (one class for
|
||||||
|
|||||||
@@ -421,18 +421,25 @@ class K8sAnsibleMixin(object):
|
|||||||
field_selectors = []
|
field_selectors = []
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
try:
|
params = dict(
|
||||||
result = resource.get(
|
|
||||||
name=name,
|
name=name,
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
label_selector=",".join(label_selectors),
|
label_selector=",".join(label_selectors),
|
||||||
field_selector=",".join(field_selectors),
|
field_selector=",".join(field_selectors),
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
result = resource.get(**params)
|
||||||
except BadRequestError:
|
except BadRequestError:
|
||||||
return dict(resources=[], api_found=True)
|
return dict(resources=[], api_found=True)
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
if not wait or name is None:
|
if not wait or name is None:
|
||||||
return dict(resources=[], api_found=True)
|
return dict(resources=[], api_found=True)
|
||||||
|
except Exception as e:
|
||||||
|
if not wait or name is None:
|
||||||
|
err = "Exception '{0}' raised while trying to get resource using {1}".format(
|
||||||
|
e, params
|
||||||
|
)
|
||||||
|
return dict(resources=[], msg=err, api_found=True)
|
||||||
|
|
||||||
if not wait:
|
if not wait:
|
||||||
result = result.to_dict()
|
result = result.to_dict()
|
||||||
@@ -452,21 +459,27 @@ class K8sAnsibleMixin(object):
|
|||||||
and not result.get("items")
|
and not result.get("items")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
last_exception = None
|
||||||
while result_empty(result) and _elapsed() < wait_timeout:
|
while result_empty(result) and _elapsed() < wait_timeout:
|
||||||
try:
|
try:
|
||||||
result = resource.get(
|
result = resource.get(**params)
|
||||||
name=name,
|
|
||||||
namespace=namespace,
|
|
||||||
label_selector=",".join(label_selectors),
|
|
||||||
field_selector=",".join(field_selectors),
|
|
||||||
)
|
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
pass
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
last_exception = e
|
||||||
if not result_empty(result):
|
if not result_empty(result):
|
||||||
break
|
break
|
||||||
time.sleep(wait_sleep)
|
time.sleep(wait_sleep)
|
||||||
if result_empty(result):
|
if result_empty(result):
|
||||||
return dict(resources=[], api_found=True)
|
res = dict(resources=[], api_found=True)
|
||||||
|
if last_exception is not None:
|
||||||
|
res[
|
||||||
|
"msg"
|
||||||
|
] = "Exception '%s' raised while trying to get resource using %s" % (
|
||||||
|
last_exception,
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
if isinstance(result, ResourceInstance):
|
if isinstance(result, ResourceInstance):
|
||||||
satisfied_by = []
|
satisfied_by = []
|
||||||
@@ -583,6 +596,8 @@ class K8sAnsibleMixin(object):
|
|||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
if state == "absent":
|
if state == "absent":
|
||||||
return True, {}, _wait_for_elapsed()
|
return True, {}, _wait_for_elapsed()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
if response:
|
if response:
|
||||||
response = response.to_dict()
|
response = response.to_dict()
|
||||||
return False, response, _wait_for_elapsed()
|
return False, response, _wait_for_elapsed()
|
||||||
@@ -980,6 +995,7 @@ class K8sAnsibleMixin(object):
|
|||||||
if self.check_mode and not self.supports_dry_run:
|
if self.check_mode and not self.supports_dry_run:
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
|
params = {"namespace": namespace}
|
||||||
if delete_options:
|
if delete_options:
|
||||||
body = {
|
body = {
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
@@ -990,6 +1006,16 @@ class K8sAnsibleMixin(object):
|
|||||||
if self.check_mode:
|
if self.check_mode:
|
||||||
params["dry_run"] = "All"
|
params["dry_run"] = "All"
|
||||||
try:
|
try:
|
||||||
|
if existing.kind.endswith("List"):
|
||||||
|
result["result"] = []
|
||||||
|
for item in existing.items:
|
||||||
|
origin_name = item.metadata.name
|
||||||
|
params["name"] = origin_name
|
||||||
|
k8s_obj = resource.delete(**params)
|
||||||
|
result["result"].append(k8s_obj.to_dict())
|
||||||
|
else:
|
||||||
|
origin_name = existing.metadata.name
|
||||||
|
params["name"] = origin_name
|
||||||
k8s_obj = resource.delete(**params)
|
k8s_obj = resource.delete(**params)
|
||||||
result["result"] = k8s_obj.to_dict()
|
result["result"] = k8s_obj.to_dict()
|
||||||
except DynamicApiError as exc:
|
except DynamicApiError as exc:
|
||||||
|
|||||||
@@ -385,9 +385,11 @@ def check_pod(k8s_ansible_mixin, module):
|
|||||||
def _fail(exc):
|
def _fail(exc):
|
||||||
arg = {}
|
arg = {}
|
||||||
if hasattr(exc, "body"):
|
if hasattr(exc, "body"):
|
||||||
msg = "Namespace={0} Kind=Pod Name={1}: Failed requested object: {2}".format(
|
msg = (
|
||||||
|
"Namespace={0} Kind=Pod Name={1}: Failed requested object: {2}".format(
|
||||||
namespace, name, exc.body
|
namespace, name, exc.body
|
||||||
)
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
msg = to_native(exc)
|
msg = to_native(exc)
|
||||||
for attr in ["status", "reason"]:
|
for attr in ["status", "reason"]:
|
||||||
|
|||||||
@@ -123,9 +123,15 @@ def main():
|
|||||||
state=dict(
|
state=dict(
|
||||||
type="str", default="present", choices=["present", "absent", "latest"]
|
type="str", default="present", choices=["present", "absent", "latest"]
|
||||||
),
|
),
|
||||||
plugin_path=dict(type="str",),
|
plugin_path=dict(
|
||||||
plugin_name=dict(type="str",),
|
type="str",
|
||||||
plugin_version=dict(type="str",),
|
),
|
||||||
|
plugin_name=dict(
|
||||||
|
type="str",
|
||||||
|
),
|
||||||
|
plugin_version=dict(
|
||||||
|
type="str",
|
||||||
|
),
|
||||||
# Helm options
|
# Helm options
|
||||||
context=dict(
|
context=dict(
|
||||||
type="str",
|
type="str",
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ def main():
|
|||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
binary_path=dict(type="path"),
|
binary_path=dict(type="path"),
|
||||||
plugin_name=dict(type="str",),
|
plugin_name=dict(
|
||||||
|
type="str",
|
||||||
|
),
|
||||||
# Helm options
|
# Helm options
|
||||||
context=dict(
|
context=dict(
|
||||||
type="str",
|
type="str",
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ def main():
|
|||||||
repo_state=dict(
|
repo_state=dict(
|
||||||
default="present", choices=["present", "absent"], aliases=["state"]
|
default="present", choices=["present", "absent"], aliases=["state"]
|
||||||
),
|
),
|
||||||
pass_credentials=dict(type="bool", default=False),
|
pass_credentials=dict(type="bool", default=False, no_log=True),
|
||||||
# Generic auth key
|
# Generic auth key
|
||||||
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
|
||||||
ca_cert=dict(
|
ca_cert=dict(
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ from ansible.module_utils._text import to_native
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from kubernetes.client.api import core_v1_api
|
from kubernetes.client.api import core_v1_api
|
||||||
from kubernetes.client.models import V1DeleteOptions
|
from kubernetes.client.models import V1DeleteOptions, V1ObjectMeta
|
||||||
from kubernetes.client.exceptions import ApiException
|
from kubernetes.client.exceptions import ApiException
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# ImportError are managed by the common module already.
|
# ImportError are managed by the common module already.
|
||||||
@@ -273,15 +273,8 @@ class K8sDrainAnsible(object):
|
|||||||
self._drain_options = module.params.get("delete_options", {})
|
self._drain_options = module.params.get("delete_options", {})
|
||||||
self._delete_options = None
|
self._delete_options = None
|
||||||
if self._drain_options.get("terminate_grace_period"):
|
if self._drain_options.get("terminate_grace_period"):
|
||||||
self._delete_options = {}
|
self._delete_options = V1DeleteOptions(
|
||||||
self._delete_options.update({"apiVersion": "v1"})
|
grace_period_seconds=self._drain_options.get("terminate_grace_period")
|
||||||
self._delete_options.update({"kind": "DeleteOptions"})
|
|
||||||
self._delete_options.update(
|
|
||||||
{
|
|
||||||
"gracePeriodSeconds": self._drain_options.get(
|
|
||||||
"terminate_grace_period"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._changed = False
|
self._changed = False
|
||||||
@@ -318,17 +311,16 @@ class K8sDrainAnsible(object):
|
|||||||
|
|
||||||
def evict_pods(self, pods):
|
def evict_pods(self, pods):
|
||||||
for namespace, name in pods:
|
for namespace, name in pods:
|
||||||
definition = {"metadata": {"name": name, "namespace": namespace}}
|
|
||||||
if self._delete_options:
|
|
||||||
definition.update({"delete_options": self._delete_options})
|
|
||||||
try:
|
try:
|
||||||
if self._drain_options.get("disable_eviction"):
|
if self._drain_options.get("disable_eviction"):
|
||||||
body = V1DeleteOptions(**definition)
|
|
||||||
self._api_instance.delete_namespaced_pod(
|
self._api_instance.delete_namespaced_pod(
|
||||||
name=name, namespace=namespace, body=body
|
name=name, namespace=namespace, body=self._delete_options
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
body = v1_eviction(**definition)
|
body = v1_eviction(
|
||||||
|
delete_options=self._delete_options,
|
||||||
|
metadata=V1ObjectMeta(name=name, namespace=namespace),
|
||||||
|
)
|
||||||
self._api_instance.create_namespaced_pod_eviction(
|
self._api_instance.create_namespaced_pod_eviction(
|
||||||
name=name, namespace=namespace, body=body
|
name=name, namespace=namespace, body=body
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -227,7 +227,10 @@ def execute_module(module, k8s_ansible_mixin):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True,)
|
module = AnsibleModule(
|
||||||
|
argument_spec=argspec(),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||||
K8sAnsibleMixin,
|
K8sAnsibleMixin,
|
||||||
get_api_client,
|
get_api_client,
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ SCALE_ARG_SPEC = {
|
|||||||
|
|
||||||
|
|
||||||
def execute_module(
|
def execute_module(
|
||||||
module, k8s_ansible_mixin,
|
module,
|
||||||
|
k8s_ansible_mixin,
|
||||||
):
|
):
|
||||||
k8s_ansible_mixin.set_resource_definitions(module)
|
k8s_ansible_mixin.set_resource_definitions(module)
|
||||||
|
|
||||||
|
|||||||
@@ -301,7 +301,10 @@ class K8sTaintAnsible:
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True,)
|
module = AnsibleModule(
|
||||||
|
argument_spec=argspec(),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
k8s_taint = K8sTaintAnsible(module)
|
k8s_taint = K8sTaintAnsible(module)
|
||||||
k8s_taint.execute_module()
|
k8s_taint.execute_module()
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ from ansible.module_utils.basic import AnsibleModule
|
|||||||
def main():
|
def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
binary_path=dict(type="path"), version=dict(type="str", default="3.7.0"),
|
binary_path=dict(type="path"),
|
||||||
|
version=dict(type="str", default="3.7.0"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,51 @@
|
|||||||
that:
|
that:
|
||||||
- not pods_delete.resources
|
- not pods_delete.resources
|
||||||
|
|
||||||
|
# test deletion using label selector
|
||||||
|
- name: Deploy load balancer
|
||||||
|
k8s:
|
||||||
|
namespace: "{{ test_namespace }}"
|
||||||
|
definition:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: deletion
|
||||||
|
name: "deletion-svc-{{ item }}"
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 5000
|
||||||
|
targetPort: 5000
|
||||||
|
selector:
|
||||||
|
test: deletion
|
||||||
|
type: LoadBalancer
|
||||||
|
with_items:
|
||||||
|
- "01"
|
||||||
|
- "02"
|
||||||
|
- "03"
|
||||||
|
|
||||||
|
- name: Delete services using label selector
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
api_version: v1
|
||||||
|
namespace: "{{ test_namespace }}"
|
||||||
|
kind: Service
|
||||||
|
state: absent
|
||||||
|
label_selectors:
|
||||||
|
- test=deletion
|
||||||
|
|
||||||
|
- name: list services using label selector
|
||||||
|
k8s_info:
|
||||||
|
kind: Service
|
||||||
|
namespace: "{{ test_namespace }}"
|
||||||
|
label_selectors:
|
||||||
|
- test=deletion
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- name: Validate that all services were deleted
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _result.resources | length == 0
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Remove namespace
|
- name: Remove namespace
|
||||||
k8s:
|
k8s:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
k8s_drain
|
k8s_drain
|
||||||
k8s
|
k8s
|
||||||
k8s_info
|
k8s_info
|
||||||
time=78
|
time=121
|
||||||
|
|||||||
@@ -286,6 +286,72 @@
|
|||||||
state: uncordon
|
state: uncordon
|
||||||
name: '{{ node_to_drain }}'
|
name: '{{ node_to_drain }}'
|
||||||
|
|
||||||
|
- name: Create another Pod
|
||||||
|
k8s:
|
||||||
|
namespace: '{{ test_namespace }}'
|
||||||
|
wait: yes
|
||||||
|
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
|
||||||
|
definition:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: '{{ drain_pod_name }}-01'
|
||||||
|
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
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /emptydir
|
||||||
|
name: emptydir
|
||||||
|
volumes:
|
||||||
|
- name: emptydir
|
||||||
|
emptyDir: {}
|
||||||
|
|
||||||
|
- name: Drain node using disable_eviction set to yes
|
||||||
|
k8s_drain:
|
||||||
|
state: drain
|
||||||
|
name: '{{ node_to_drain }}'
|
||||||
|
delete_options:
|
||||||
|
force: true
|
||||||
|
disable_eviction: yes
|
||||||
|
terminate_grace_period: 0
|
||||||
|
ignore_daemonsets: yes
|
||||||
|
wait_timeout: 0
|
||||||
|
delete_emptydir_data: true
|
||||||
|
register: disable_evict
|
||||||
|
|
||||||
|
- name: assert that node has been drained
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- disable_evict is changed
|
||||||
|
- '"node {{ node_to_drain }} marked unschedulable." in disable_evict.result'
|
||||||
|
|
||||||
|
- name: assert that unmanaged pod were deleted
|
||||||
|
k8s_info:
|
||||||
|
namespace: '{{ test_namespace }}'
|
||||||
|
kind: Pod
|
||||||
|
name: '{{ drain_pod_name }}-01'
|
||||||
|
register: _result
|
||||||
|
failed_when: _result.resources
|
||||||
|
|
||||||
|
- name: Uncordon node
|
||||||
|
k8s_drain:
|
||||||
|
state: uncordon
|
||||||
|
name: '{{ node_to_drain }}'
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Uncordon node
|
- name: Uncordon node
|
||||||
k8s_drain:
|
k8s_drain:
|
||||||
|
|||||||
@@ -239,6 +239,63 @@
|
|||||||
- resource.result.results | selectattr('changed') | list | length == 1
|
- resource.result.results | selectattr('changed') | list | length == 1
|
||||||
- resource.result.results | selectattr('error', 'defined') | list | length == 1
|
- resource.result.results | selectattr('error', 'defined') | list | length == 1
|
||||||
|
|
||||||
|
# Test resource definition using template with 'omit'
|
||||||
|
- name: Deploy configmap using template
|
||||||
|
k8s:
|
||||||
|
namespace: "{{ template_namespace }}"
|
||||||
|
name: test-data
|
||||||
|
template: configmap.yml.j2
|
||||||
|
|
||||||
|
- name: Read configmap created
|
||||||
|
k8s_info:
|
||||||
|
kind: configmap
|
||||||
|
namespace: "{{ template_namespace }}"
|
||||||
|
name: test-data
|
||||||
|
register: _configmap
|
||||||
|
|
||||||
|
- name: Validate that the configmap does not contains annotations
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- '"annotations" not in _configmap.resources.0.metadata'
|
||||||
|
|
||||||
|
- name: Create resource once again
|
||||||
|
k8s:
|
||||||
|
namespace: "{{ template_namespace }}"
|
||||||
|
name: test-data
|
||||||
|
template: configmap.yml.j2
|
||||||
|
register: _configmap
|
||||||
|
|
||||||
|
- name: assert that nothing changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _configmap is not changed
|
||||||
|
|
||||||
|
- name: Create resource once again (using description)
|
||||||
|
k8s:
|
||||||
|
namespace: "{{ template_namespace }}"
|
||||||
|
name: test-data
|
||||||
|
template: configmap.yml.j2
|
||||||
|
register: _configmap
|
||||||
|
vars:
|
||||||
|
k8s_configmap_desc: "This is a simple configmap used to test ansible k8s collection"
|
||||||
|
|
||||||
|
- name: assert that configmap was changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _configmap is changed
|
||||||
|
|
||||||
|
- name: Read configmap created
|
||||||
|
k8s_info:
|
||||||
|
kind: configmap
|
||||||
|
namespace: "{{ template_namespace }}"
|
||||||
|
name: test-data
|
||||||
|
register: _configmap
|
||||||
|
|
||||||
|
- name: Validate that the configmap does not contains annotations
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _configmap.resources.0.metadata.annotations.description == "This is a simple configmap used to test ansible k8s collection"
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Remove namespace (Cleanup)
|
- name: Remove namespace (Cleanup)
|
||||||
kubernetes.core.k8s:
|
kubernetes.core.k8s:
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
description: "{{ k8s_configmap_desc | default(omit) }}"
|
||||||
|
data:
|
||||||
|
key: "testing-template"
|
||||||
104
tests/unit/action/test_remove_omit.py
Normal file
104
tests/unit/action/test_remove_omit.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright: (c) 2022, Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from ansible_collections.kubernetes.core.plugins.action.k8s_info import RemoveOmit
|
||||||
|
|
||||||
|
|
||||||
|
def get_omit_token():
|
||||||
|
return "__omit_place_holder__%s" % datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_omit_from_str():
|
||||||
|
omit_token = get_omit_token()
|
||||||
|
src = """
|
||||||
|
project: ansible
|
||||||
|
collection: {omit}
|
||||||
|
""".format(
|
||||||
|
omit=omit_token
|
||||||
|
)
|
||||||
|
result = RemoveOmit(src, omit_value=omit_token).output()
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0] == dict(project="ansible")
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_omit_from_list():
|
||||||
|
omit_token = get_omit_token()
|
||||||
|
src = """
|
||||||
|
items:
|
||||||
|
- {omit}
|
||||||
|
""".format(
|
||||||
|
omit=omit_token
|
||||||
|
)
|
||||||
|
result = RemoveOmit(src, omit_value=omit_token).output()
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0] == dict(items=[])
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_omit_from_list_of_dict():
|
||||||
|
omit_token = get_omit_token()
|
||||||
|
src = """
|
||||||
|
items:
|
||||||
|
- owner: ansible
|
||||||
|
team: {omit}
|
||||||
|
- simple_list_item
|
||||||
|
""".format(
|
||||||
|
omit=omit_token
|
||||||
|
)
|
||||||
|
result = RemoveOmit(src, omit_value=omit_token).output()
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0] == dict(items=[dict(owner="ansible"), "simple_list_item"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_omit_combined():
|
||||||
|
omit_token = get_omit_token()
|
||||||
|
src = """
|
||||||
|
items:
|
||||||
|
- {omit}
|
||||||
|
- list_item_a
|
||||||
|
- list_item_b
|
||||||
|
parent:
|
||||||
|
child:
|
||||||
|
subchilda: {omit}
|
||||||
|
subchildb:
|
||||||
|
name: {omit}
|
||||||
|
age: 3
|
||||||
|
""".format(
|
||||||
|
omit=omit_token
|
||||||
|
)
|
||||||
|
result = RemoveOmit(src, omit_value=omit_token).output()
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0] == dict(
|
||||||
|
items=["list_item_a", "list_item_b"],
|
||||||
|
parent=dict(child=dict(subchildb=dict(age=3))),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_omit_mutiple_documents():
|
||||||
|
omit_token = get_omit_token()
|
||||||
|
src = [
|
||||||
|
"""
|
||||||
|
project: ansible
|
||||||
|
collection: {omit}
|
||||||
|
""".format(
|
||||||
|
omit=omit_token
|
||||||
|
),
|
||||||
|
"---",
|
||||||
|
"""
|
||||||
|
project: kubernetes
|
||||||
|
environment: production
|
||||||
|
collection: {omit}""".format(
|
||||||
|
omit=omit_token
|
||||||
|
),
|
||||||
|
]
|
||||||
|
src = "\n".join(src)
|
||||||
|
print(src)
|
||||||
|
result = RemoveOmit(src, omit_value=omit_token).output()
|
||||||
|
assert len(result) == 2
|
||||||
|
assert result[0] == dict(project="ansible")
|
||||||
|
assert result[1] == dict(project="kubernetes", environment="production")
|
||||||
@@ -324,27 +324,27 @@ tests = [
|
|||||||
# str type and everything else was mostly unicode type (don't ask me how)
|
# str type and everything else was mostly unicode type (don't ask me how)
|
||||||
dict(
|
dict(
|
||||||
last_applied={
|
last_applied={
|
||||||
u"kind": u"ConfigMap",
|
"kind": "ConfigMap",
|
||||||
u"data": {u"one": "1", "three": "3", "two": "2"},
|
"data": {"one": "1", "three": "3", "two": "2"},
|
||||||
u"apiVersion": u"v1",
|
"apiVersion": "v1",
|
||||||
u"metadata": {u"namespace": u"apply", u"name": u"apply-configmap"},
|
"metadata": {"namespace": "apply", "name": "apply-configmap"},
|
||||||
},
|
},
|
||||||
actual={
|
actual={
|
||||||
u"kind": u"ConfigMap",
|
"kind": "ConfigMap",
|
||||||
u"data": {u"one": "1", "three": "3", "two": "2"},
|
"data": {"one": "1", "three": "3", "two": "2"},
|
||||||
u"apiVersion": u"v1",
|
"apiVersion": "v1",
|
||||||
u"metadata": {
|
"metadata": {
|
||||||
u"namespace": u"apply",
|
"namespace": "apply",
|
||||||
u"name": u"apply-configmap",
|
"name": "apply-configmap",
|
||||||
u"resourceVersion": "1714994",
|
"resourceVersion": "1714994",
|
||||||
u"creationTimestamp": u"2019-08-17T05:08:05Z",
|
"creationTimestamp": "2019-08-17T05:08:05Z",
|
||||||
u"annotations": {},
|
"annotations": {},
|
||||||
u"selfLink": u"/api/v1/namespaces/apply/configmaps/apply-configmap",
|
"selfLink": "/api/v1/namespaces/apply/configmaps/apply-configmap",
|
||||||
u"uid": u"fed45fb0-c0ac-11e9-9d95-025000000001",
|
"uid": "fed45fb0-c0ac-11e9-9d95-025000000001",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
desired={
|
desired={
|
||||||
"kind": u"ConfigMap",
|
"kind": "ConfigMap",
|
||||||
"data": {"one": "1", "three": "3", "two": "2"},
|
"data": {"one": "1", "three": "3", "two": "2"},
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
"metadata": {"namespace": "apply", "name": "apply-configmap"},
|
"metadata": {"namespace": "apply", "name": "apply-configmap"},
|
||||||
@@ -355,7 +355,7 @@ tests = [
|
|||||||
# then apply the Deployment again. Should un-scale the Deployment
|
# then apply the Deployment again. Should un-scale the Deployment
|
||||||
dict(
|
dict(
|
||||||
last_applied={
|
last_applied={
|
||||||
"kind": u"Deployment",
|
"kind": "Deployment",
|
||||||
"spec": {
|
"spec": {
|
||||||
"replicas": 1,
|
"replicas": 1,
|
||||||
"template": {
|
"template": {
|
||||||
@@ -372,10 +372,10 @@ tests = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"metadata": {"namespace": "apply", "name": u"apply-deployment"},
|
"metadata": {"namespace": "apply", "name": "apply-deployment"},
|
||||||
},
|
},
|
||||||
actual={
|
actual={
|
||||||
"kind": u"Deployment",
|
"kind": "Deployment",
|
||||||
"spec": {
|
"spec": {
|
||||||
"replicas": 0,
|
"replicas": 0,
|
||||||
"template": {
|
"template": {
|
||||||
@@ -392,10 +392,10 @@ tests = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"metadata": {"namespace": "apply", "name": u"apply-deployment"},
|
"metadata": {"namespace": "apply", "name": "apply-deployment"},
|
||||||
},
|
},
|
||||||
desired={
|
desired={
|
||||||
"kind": u"Deployment",
|
"kind": "Deployment",
|
||||||
"spec": {
|
"spec": {
|
||||||
"replicas": 1,
|
"replicas": 1,
|
||||||
"template": {
|
"template": {
|
||||||
@@ -409,7 +409,7 @@ tests = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"metadata": {"namespace": "apply", "name": u"apply-deployment"},
|
"metadata": {"namespace": "apply", "name": "apply-deployment"},
|
||||||
},
|
},
|
||||||
expected={
|
expected={
|
||||||
"spec": {
|
"spec": {
|
||||||
|
|||||||
@@ -26,25 +26,49 @@ from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
|
|||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
dict(
|
dict(
|
||||||
resource=dict(kind="ConfigMap", name="", data=dict(),),
|
resource=dict(
|
||||||
|
kind="ConfigMap",
|
||||||
|
name="",
|
||||||
|
data=dict(),
|
||||||
|
),
|
||||||
expected=b'{"data":{},"kind":"ConfigMap","name":""}',
|
expected=b'{"data":{},"kind":"ConfigMap","name":""}',
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
resource=dict(kind="ConfigMap", name="", data=dict(one=""),),
|
resource=dict(
|
||||||
|
kind="ConfigMap",
|
||||||
|
name="",
|
||||||
|
data=dict(one=""),
|
||||||
|
),
|
||||||
expected=b'{"data":{"one":""},"kind":"ConfigMap","name":""}',
|
expected=b'{"data":{"one":""},"kind":"ConfigMap","name":""}',
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
resource=dict(
|
resource=dict(
|
||||||
kind="ConfigMap", name="", data=dict(two="2", one="", three="3",),
|
kind="ConfigMap",
|
||||||
|
name="",
|
||||||
|
data=dict(
|
||||||
|
two="2",
|
||||||
|
one="",
|
||||||
|
three="3",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
expected=b'{"data":{"one":"","three":"3","two":"2"},"kind":"ConfigMap","name":""}',
|
expected=b'{"data":{"one":"","three":"3","two":"2"},"kind":"ConfigMap","name":""}',
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
resource=dict(kind="Secret", type="my-type", name="", data=dict(),),
|
resource=dict(
|
||||||
|
kind="Secret",
|
||||||
|
type="my-type",
|
||||||
|
name="",
|
||||||
|
data=dict(),
|
||||||
|
),
|
||||||
expected=b'{"data":{},"kind":"Secret","name":"","type":"my-type"}',
|
expected=b'{"data":{},"kind":"Secret","name":"","type":"my-type"}',
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
resource=dict(kind="Secret", type="my-type", name="", data=dict(one=""),),
|
resource=dict(
|
||||||
|
kind="Secret",
|
||||||
|
type="my-type",
|
||||||
|
name="",
|
||||||
|
data=dict(one=""),
|
||||||
|
),
|
||||||
expected=b'{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}',
|
expected=b'{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}',
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
@@ -52,7 +76,11 @@ tests = [
|
|||||||
kind="Secret",
|
kind="Secret",
|
||||||
type="my-type",
|
type="my-type",
|
||||||
name="",
|
name="",
|
||||||
data=dict(two="Mg==", one="", three="Mw==",),
|
data=dict(
|
||||||
|
two="Mg==",
|
||||||
|
one="",
|
||||||
|
three="Mw==",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
expected=b'{"data":{"one":"","three":"Mw==","two":"Mg=="},"kind":"Secret","name":"","type":"my-type"}',
|
expected=b'{"data":{"one":"","three":"Mw==","two":"Mg=="},"kind":"Secret","name":"","type":"my-type"}',
|
||||||
),
|
),
|
||||||
|
|||||||
4
tox.ini
4
tox.ini
@@ -21,7 +21,7 @@ commands = collection_prep_add_docs -p .
|
|||||||
|
|
||||||
[testenv:black]
|
[testenv:black]
|
||||||
deps =
|
deps =
|
||||||
black==19.10b0
|
black >= 22.0, < 23.0
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
black -v --check --diff {toxinidir}/plugins {toxinidir}/tests
|
black -v --check --diff {toxinidir}/plugins {toxinidir}/tests
|
||||||
@@ -30,7 +30,7 @@ commands =
|
|||||||
deps =
|
deps =
|
||||||
yamllint
|
yamllint
|
||||||
flake8
|
flake8
|
||||||
black==19.10b0
|
{[testenv:black]deps}
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
black -v --check --diff {toxinidir}/plugins {toxinidir}/tests
|
black -v --check --diff {toxinidir}/plugins {toxinidir}/tests
|
||||||
|
|||||||
Reference in New Issue
Block a user