Compare commits

12 Commits
2.3.0 ... 2.3.2

Author SHA1 Message Date
Mike Graves
69304d1c3b Release version 2.3.2 (#475)
Release version 2.3.2

SUMMARY

Release version 2.3.2

ISSUE TYPE

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
2022-06-10 12:29:55 +00:00
Mike Graves
fdddb6f78f helm_repository: Silence false no_log warning (#423) (#473)
[backport/2.3] helm_repository: Silence false no_log warning (#423)

Depends-on: ansible/ansible-zuul-jobs#1563
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
(cherry picked from commit d311ac7)
SUMMARY


ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION
2022-06-08 17:20:12 +00:00
Mike Graves
e8cf1ef517 Add missing PSF license (#463) (#465)
[backport/2.3] 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 
Reviewed-by: Felix Fontein felix@fontein.de
(cherry picked from commit 3729b8b)
2022-05-16 23:34:34 +00:00
Mike Graves
6dbc0d5b6d Remove distutils from connection plugin (#456) (#464)
[backport/2.3] Remove distutils from connection plugin (#456)

Remove distutils from connection plugin
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 
(cherry picked from commit 531a9fe)
2022-05-16 16:19:36 +00:00
Mike Graves
0a72c87d2c Release 2.3.1 (#448)
Release 2.3.1

SUMMARY


ISSUE TYPE


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

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Joseph Torcasso <None>
Reviewed-by: Jill R <None>
Reviewed-by: Gonéri Le Bouder <goneri@lebouder.net>
2022-05-02 20:13:12 +00:00
Mike Graves
c149394556 k8s_cp - fix issue when using local_path (#422) (#442)
[backport/2.3] k8s_cp - fix issue when using local_path (#422)

Depends-On: #446
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 
Reviewed-by: Mike Graves mgraves@redhat.com
(cherry picked from commit 1d05cf5)
2022-05-02 17:41:21 +00:00
Mike Graves
e2dec91460 k8s - fix issue when try to delete resources using label_selectors option (#434) (#444)
[backport/2.3] k8s - fix issue when try to delete resources using label_selectors op…

Depends-On: #446
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 
(cherry picked from commit f2f4b66)
2022-05-02 15:43:19 +00:00
Mike Graves
edf104d687 [backport/2.3] continue waiting when an exception is raised (#408) (#441)
[backport/2.3] continue waiting when an exception is raised (#408)

Depends-On: #446
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 
(cherry picked from commit f418353)
2022-05-02 13:37:13 +00:00
Mike Graves
7b09c01d98 Change line in doc fragment yaml (#439) (#445)
[backport/2.3] Change line in doc fragment yaml (#439)

Depends-On: #446
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 
(cherry picked from commit b5cfc85)
2022-05-02 13:03:32 +00:00
Mike Graves
7409eaf993 Remove omit from template resource (#432) (#443)
[backport/2.3] Remove omit from template resource (#432)

Depends-On: #446
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 
(cherry picked from commit 882e672)
2022-05-02 12:59:08 +00:00
Mike Graves
321b6dcdd8 fix issue when using k8s_drain with disable_eviction set to yes (#418) (#440)
[backport/2.3] fix issue when using k8s_drain with disable_eviction set to yes (#418)

Depends-On: #446
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 
(cherry picked from commit 074f0a6)
2022-04-29 20:45:47 +00:00
Mike Graves
68d45af767 Upgrade black version (#424) (#446)
[backport/2.3] Upgrade black version (#424)

Depends-On: ansible/ansible-zuul-jobs#1515
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 
(cherry picked from commit 7c71436)
2022-04-29 17:47:08 +00:00
44 changed files with 588 additions and 121 deletions

View File

@@ -5,6 +5,27 @@ Kubernetes Collection Release Notes
.. 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
======

View File

@@ -1,5 +1,5 @@
# Also needs to be updated in galaxy.yml
VERSION = 2.3.0
VERSION = 2.3.2
TEST_ARGS ?= ""
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`

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

@@ -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**.
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.
@@ -90,7 +92,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
---
collections:
- name: kubernetes.core
version: 2.3.0
version: 2.3.2
```
### Installing the Kubernetes Python Library

View File

@@ -568,3 +568,33 @@ releases:
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.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'

View File

@@ -151,7 +151,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -182,7 +182,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -302,7 +302,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -182,7 +182,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -188,7 +188,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -170,7 +170,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -189,7 +189,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -386,7 +386,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -187,7 +187,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -204,7 +204,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -187,7 +187,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -149,7 +149,7 @@ Parameters
</td>
<td>
<div>Group(s) to impersonate for the operation.</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: &#x27;Group1,Group2&#x27;</div>
<div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
</td>
</tr>
<tr>

View File

@@ -25,7 +25,7 @@ tags:
- openshift
- okd
- cluster
version: 2.3.0
version: 2.3.2
build_ignore:
- .DS_Store
- '*.tar.gz'

View File

@@ -12,7 +12,6 @@ import traceback
import os
from contextlib import contextmanager
from ansible.config.manager import ensure_type
from ansible.errors import (
AnsibleError,
@@ -26,6 +25,31 @@ from ansible.module_utils._text import to_text, to_bytes, to_native
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):
TRANSFERS_FILES = True
@@ -180,6 +204,7 @@ class ActionModule(ActionBase):
"'template' is only a supported parameter for the 'k8s' module."
)
omit_value = task_vars.get("omit")
template_params = []
if isinstance(template, string_types) or isinstance(template, dict):
template_params.append(self.get_template_args(template))
@@ -245,7 +270,10 @@ class ActionModule(ActionBase):
preserve_trailing_newlines=True,
escape_backslashes=False,
)
result_template.append(result)
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)
if not resource_definition:
@@ -295,7 +323,7 @@ class ActionModule(ActionBase):
)
def run(self, tmp=None, task_vars=None):
""" handler for k8s options """
"""handler for k8s options"""
if task_vars is None:
task_vars = dict()
@@ -352,7 +380,7 @@ class ActionModule(ActionBase):
local_path = self._task.args.get("local_path")
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)
# Execute the k8s_* module.

View File

@@ -171,9 +171,9 @@ DOCUMENTATION = r"""
aliases: [ kubectl_verify_ssl ]
"""
import distutils.spawn
import os
import os.path
import shutil
import subprocess
from ansible.parsing.yaml.loader import AnsibleLoader
@@ -205,7 +205,7 @@ CONNECTION_OPTIONS = {
class Connection(ConnectionBase):
""" Local kubectl based connections """
"""Local kubectl based connections"""
transport = CONNECTION_TRANSPORT
connection_options = CONNECTION_OPTIONS
@@ -219,14 +219,9 @@ class Connection(ConnectionBase):
# Note: kubectl runs commands as the user that started the container.
# It is impossible to set the remote user for a kubectl connection.
cmd_arg = "{0}_command".format(self.transport)
if cmd_arg in kwargs:
self.transport_cmd = kwargs[cmd_arg]
else:
self.transport_cmd = distutils.spawn.find_executable(self.transport)
if not self.transport_cmd:
raise AnsibleError(
"{0} command not found in PATH".format(self.transport)
)
self.transport_cmd = kwargs.get(cmd_arg, shutil.which(self.transport))
if not self.transport_cmd:
raise AnsibleError("{0} command not found in PATH".format(self.transport))
def _build_exec_cmd(self, cmd):
"""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
skip_verify_ssl = not self.get_option(key)
local_cmd.append(
u"{0}={1}".format(
"{0}={1}".format(
self.connection_options[key], str(skip_verify_ssl).lower()
)
)
censored_local_cmd.append(
u"{0}={1}".format(
"{0}={1}".format(
self.connection_options[key], str(skip_verify_ssl).lower()
)
)
@@ -262,12 +257,12 @@ class Connection(ConnectionBase):
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(" ")
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
@@ -275,7 +270,7 @@ class Connection(ConnectionBase):
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)]
@@ -286,17 +281,17 @@ class Connection(ConnectionBase):
return local_cmd, censored_local_cmd
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),
"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(
@@ -333,7 +328,7 @@ class Connection(ConnectionBase):
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
@@ -376,7 +371,7 @@ class Connection(ConnectionBase):
)
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
@@ -420,6 +415,6 @@ class Connection(ConnectionBase):
)
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

@@ -128,7 +128,7 @@ options:
impersonate_groups:
description:
- 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
elements: str
version_added: 2.3.0

View File

@@ -3,7 +3,7 @@
# Implements multiple version numbering conventions for the
# Python Module Distribution Utilities.
#
# PSF License (see licenses/PSF-license.txt or https://opensource.org/licenses/Python-2.0)
# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0)
#
"""Provides classes to represent module version numbers (one class for

View File

@@ -96,7 +96,7 @@ 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)

View File

@@ -421,18 +421,25 @@ class K8sAnsibleMixin(object):
field_selectors = []
result = None
params = dict(
name=name,
namespace=namespace,
label_selector=",".join(label_selectors),
field_selector=",".join(field_selectors),
)
try:
result = resource.get(
name=name,
namespace=namespace,
label_selector=",".join(label_selectors),
field_selector=",".join(field_selectors),
)
result = resource.get(**params)
except BadRequestError:
return dict(resources=[], api_found=True)
except NotFoundError:
if not wait or name is None:
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:
result = result.to_dict()
@@ -452,21 +459,27 @@ class K8sAnsibleMixin(object):
and not result.get("items")
)
last_exception = None
while result_empty(result) and _elapsed() < wait_timeout:
try:
result = resource.get(
name=name,
namespace=namespace,
label_selector=",".join(label_selectors),
field_selector=",".join(field_selectors),
)
result = resource.get(**params)
except NotFoundError:
pass
except Exception as e:
last_exception = e
if not result_empty(result):
break
time.sleep(wait_sleep)
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):
satisfied_by = []
@@ -583,6 +596,8 @@ class K8sAnsibleMixin(object):
except NotFoundError:
if state == "absent":
return True, {}, _wait_for_elapsed()
except Exception:
pass
if response:
response = response.to_dict()
return False, response, _wait_for_elapsed()
@@ -980,6 +995,7 @@ class K8sAnsibleMixin(object):
if self.check_mode and not self.supports_dry_run:
return result
else:
params = {"namespace": namespace}
if delete_options:
body = {
"apiVersion": "v1",
@@ -990,8 +1006,18 @@ class K8sAnsibleMixin(object):
if self.check_mode:
params["dry_run"] = "All"
try:
k8s_obj = resource.delete(**params)
result["result"] = k8s_obj.to_dict()
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)
result["result"] = k8s_obj.to_dict()
except DynamicApiError as exc:
msg = "Failed to delete object: {0}".format(exc.body)
if continue_on_error:

View File

@@ -385,8 +385,10 @@ def check_pod(k8s_ansible_mixin, module):
def _fail(exc):
arg = {}
if hasattr(exc, "body"):
msg = "Namespace={0} Kind=Pod Name={1}: Failed requested object: {2}".format(
namespace, name, exc.body
msg = (
"Namespace={0} Kind=Pod Name={1}: Failed requested object: {2}".format(
namespace, name, exc.body
)
)
else:
msg = to_native(exc)

View File

@@ -19,4 +19,4 @@ __metaclass__ = type
class ApplyException(Exception):
""" Could not apply patch """
"""Could not apply patch"""

View File

@@ -123,9 +123,15 @@ def main():
state=dict(
type="str", default="present", choices=["present", "absent", "latest"]
),
plugin_path=dict(type="str",),
plugin_name=dict(type="str",),
plugin_version=dict(type="str",),
plugin_path=dict(
type="str",
),
plugin_name=dict(
type="str",
),
plugin_version=dict(
type="str",
),
# Helm options
context=dict(
type="str",

View File

@@ -81,7 +81,9 @@ def main():
module = AnsibleModule(
argument_spec=dict(
binary_path=dict(type="path"),
plugin_name=dict(type="str",),
plugin_name=dict(
type="str",
),
# Helm options
context=dict(
type="str",

View File

@@ -228,7 +228,7 @@ def main():
repo_state=dict(
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
host=dict(type="str", fallback=(env_fallback, ["K8S_AUTH_HOST"])),
ca_cert=dict(

View File

@@ -140,7 +140,7 @@ from ansible.module_utils._text import to_native
try:
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
except ImportError:
# ImportError are managed by the common module already.
@@ -273,15 +273,8 @@ class K8sDrainAnsible(object):
self._drain_options = module.params.get("delete_options", {})
self._delete_options = None
if self._drain_options.get("terminate_grace_period"):
self._delete_options = {}
self._delete_options.update({"apiVersion": "v1"})
self._delete_options.update({"kind": "DeleteOptions"})
self._delete_options.update(
{
"gracePeriodSeconds": self._drain_options.get(
"terminate_grace_period"
)
}
self._delete_options = V1DeleteOptions(
grace_period_seconds=self._drain_options.get("terminate_grace_period")
)
self._changed = False
@@ -318,17 +311,16 @@ class K8sDrainAnsible(object):
def evict_pods(self, pods):
for namespace, name in pods:
definition = {"metadata": {"name": name, "namespace": namespace}}
if self._delete_options:
definition.update({"delete_options": self._delete_options})
try:
if self._drain_options.get("disable_eviction"):
body = V1DeleteOptions(**definition)
self._api_instance.delete_namespaced_pod(
name=name, namespace=namespace, body=body
name=name, namespace=namespace, body=self._delete_options
)
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(
name=name, namespace=namespace, body=body
)

View File

@@ -227,7 +227,10 @@ def execute_module(module, k8s_ansible_mixin):
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 (
K8sAnsibleMixin,
get_api_client,

View File

@@ -164,7 +164,8 @@ SCALE_ARG_SPEC = {
def execute_module(
module, k8s_ansible_mixin,
module,
k8s_ansible_mixin,
):
k8s_ansible_mixin.set_resource_definitions(module)

View File

@@ -187,7 +187,7 @@ def merge_dicts(x, y):
def argspec():
""" argspec property builder """
"""argspec property builder"""
argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
argument_spec.update(COMMON_ARG_SPEC)
argument_spec.update(RESOURCE_ARG_SPEC)
@@ -196,7 +196,7 @@ def argspec():
def execute_module(module, k8s_ansible_mixin):
""" Module execution """
"""Module execution"""
k8s_ansible_mixin.set_resource_definitions(module)
api_version = "v1"

View File

@@ -301,7 +301,10 @@ class K8sTaintAnsible:
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.execute_module()

View File

@@ -62,7 +62,8 @@ from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
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"),
),
)

View File

@@ -76,6 +76,51 @@
that:
- 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:
- name: Remove namespace
k8s:

View File

@@ -1,4 +1,4 @@
k8s_drain
k8s
k8s_info
time=78
time=121

View File

@@ -286,6 +286,72 @@
state: uncordon
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:
- name: Uncordon node
k8s_drain:

View File

@@ -239,6 +239,63 @@
- resource.result.results | selectattr('changed') | 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:
- name: Remove namespace (Cleanup)
kubernetes.core.k8s:

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
description: "{{ k8s_configmap_desc | default(omit) }}"
data:
key: "testing-template"

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

View File

@@ -324,27 +324,27 @@ tests = [
# str type and everything else was mostly unicode type (don't ask me how)
dict(
last_applied={
u"kind": u"ConfigMap",
u"data": {u"one": "1", "three": "3", "two": "2"},
u"apiVersion": u"v1",
u"metadata": {u"namespace": u"apply", u"name": u"apply-configmap"},
"kind": "ConfigMap",
"data": {"one": "1", "three": "3", "two": "2"},
"apiVersion": "v1",
"metadata": {"namespace": "apply", "name": "apply-configmap"},
},
actual={
u"kind": u"ConfigMap",
u"data": {u"one": "1", "three": "3", "two": "2"},
u"apiVersion": u"v1",
u"metadata": {
u"namespace": u"apply",
u"name": u"apply-configmap",
u"resourceVersion": "1714994",
u"creationTimestamp": u"2019-08-17T05:08:05Z",
u"annotations": {},
u"selfLink": u"/api/v1/namespaces/apply/configmaps/apply-configmap",
u"uid": u"fed45fb0-c0ac-11e9-9d95-025000000001",
"kind": "ConfigMap",
"data": {"one": "1", "three": "3", "two": "2"},
"apiVersion": "v1",
"metadata": {
"namespace": "apply",
"name": "apply-configmap",
"resourceVersion": "1714994",
"creationTimestamp": "2019-08-17T05:08:05Z",
"annotations": {},
"selfLink": "/api/v1/namespaces/apply/configmaps/apply-configmap",
"uid": "fed45fb0-c0ac-11e9-9d95-025000000001",
},
},
desired={
"kind": u"ConfigMap",
"kind": "ConfigMap",
"data": {"one": "1", "three": "3", "two": "2"},
"apiVersion": "v1",
"metadata": {"namespace": "apply", "name": "apply-configmap"},
@@ -355,7 +355,7 @@ tests = [
# then apply the Deployment again. Should un-scale the Deployment
dict(
last_applied={
"kind": u"Deployment",
"kind": "Deployment",
"spec": {
"replicas": 1,
"template": {
@@ -372,10 +372,10 @@ tests = [
}
},
},
"metadata": {"namespace": "apply", "name": u"apply-deployment"},
"metadata": {"namespace": "apply", "name": "apply-deployment"},
},
actual={
"kind": u"Deployment",
"kind": "Deployment",
"spec": {
"replicas": 0,
"template": {
@@ -392,10 +392,10 @@ tests = [
}
},
},
"metadata": {"namespace": "apply", "name": u"apply-deployment"},
"metadata": {"namespace": "apply", "name": "apply-deployment"},
},
desired={
"kind": u"Deployment",
"kind": "Deployment",
"spec": {
"replicas": 1,
"template": {
@@ -409,7 +409,7 @@ tests = [
}
},
},
"metadata": {"namespace": "apply", "name": u"apply-deployment"},
"metadata": {"namespace": "apply", "name": "apply-deployment"},
},
expected={
"spec": {

View File

@@ -26,25 +26,49 @@ from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
tests = [
dict(
resource=dict(kind="ConfigMap", name="", data=dict(),),
resource=dict(
kind="ConfigMap",
name="",
data=dict(),
),
expected=b'{"data":{},"kind":"ConfigMap","name":""}',
),
dict(
resource=dict(kind="ConfigMap", name="", data=dict(one=""),),
resource=dict(
kind="ConfigMap",
name="",
data=dict(one=""),
),
expected=b'{"data":{"one":""},"kind":"ConfigMap","name":""}',
),
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":""}',
),
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"}',
),
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"}',
),
dict(
@@ -52,7 +76,11 @@ tests = [
kind="Secret",
type="my-type",
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"}',
),

View File

@@ -21,16 +21,16 @@ commands = collection_prep_add_docs -p .
[testenv:black]
deps =
black==19.10b0
black >= 22.0, < 23.0
commands =
black -v --check --diff {toxinidir}/plugins {toxinidir}/tests
[testenv:linters]
deps =
yamllint
flake8
black==19.10b0
yamllint
flake8
{[testenv:black]deps}
commands =
black -v --check --diff {toxinidir}/plugins {toxinidir}/tests