mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-11 20:12:18 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd0a376451 | ||
|
|
f22ffcab18 | ||
|
|
072a08091b | ||
|
|
cbadbe32f9 | ||
|
|
966fa7e906 | ||
|
|
485eae3b10 | ||
|
|
a4c1bd8541 | ||
|
|
8858b19121 | ||
|
|
6360763098 | ||
|
|
ac943e9890 | ||
|
|
0408aa9328 | ||
|
|
874fbfedd5 | ||
|
|
d8d9133912 | ||
|
|
86d9a3f45f | ||
|
|
fb25ff44f1 | ||
|
|
600c10dffb | ||
|
|
9f7c865c9c | ||
|
|
23e94b60c1 | ||
|
|
1955989278 | ||
|
|
7c4ec3b982 | ||
|
|
8d15489ec2 | ||
|
|
3dcdcbc85d | ||
|
|
fe9c12326d |
127
.github/workflows/integration-tests-kubevirt.yaml
vendored
Normal file
127
.github/workflows/integration-tests-kubevirt.yaml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
name: Integration tests Kubevirt
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
jobs:
|
||||
splitter:
|
||||
continue-on-error: true
|
||||
env:
|
||||
kubernetes: "./kubernetes"
|
||||
kubevirt: "./kubevirt"
|
||||
py_version: 3.9
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
test_targets: ${{ steps.splitter.outputs.kubevirt_targets }}
|
||||
steps:
|
||||
- name: Checkout kubernetes.core repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: ${{ env.kubernetes }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Checkout the kubevirt.core collection
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: kubevirt/kubevirt.core
|
||||
path: ${{ env.kubevirt }}
|
||||
|
||||
- name: "Set up Python ${{ env.py_version }}"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "${{ env.py_version }}"
|
||||
|
||||
- name: List targets from kubevirt.core collection
|
||||
id: splitter
|
||||
run: python ${{ env.kubernetes }}/tools/kubevirt_list_targets.py ${{ env.kubevirt }}
|
||||
shell: bash
|
||||
|
||||
integration:
|
||||
if: ${{ needs.splitter.outputs.test_targets != '' }}
|
||||
name: "integration-kubevirt-${{ matrix.test-target }}"
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
needs:
|
||||
- splitter
|
||||
env:
|
||||
kubernetes: "./kubernetes"
|
||||
kubevirt: "./kubevirt"
|
||||
ansible_version: milestone
|
||||
python_version: 3.12
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test-target: ${{ fromJson(needs.splitter.outputs.test_targets) }}
|
||||
steps:
|
||||
- name: Checkout kubernetes.core repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.kubernetes }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Checkout kubevirt.core repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: kubevirt/kubevirt.core
|
||||
path: ${{ env.kubevirt }}
|
||||
ref: main
|
||||
|
||||
# Install ansible
|
||||
- name: Install ansible-core (${{ env.ansible_version }})
|
||||
run: >-
|
||||
python3 -m pip install
|
||||
https://github.com/ansible/ansible/archive/${{ env.ansible_version }}.tar.gz
|
||||
--disable-pip-version-check
|
||||
shell: bash
|
||||
|
||||
- name: Build and install kubevirt.core collection
|
||||
id: install-kubevirt
|
||||
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
|
||||
with:
|
||||
install_python_dependencies: true
|
||||
source_path: ${{ env.kubevirt }}
|
||||
|
||||
- name: Build and install kubernetes.core collection
|
||||
id: install-kubernetes
|
||||
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
|
||||
with:
|
||||
install_python_dependencies: true
|
||||
source_path: ${{ env.kubernetes }}
|
||||
|
||||
- name: Install kind / kubectl
|
||||
uses: helm/kind-action@v1.9.0
|
||||
with:
|
||||
version: v0.22.0
|
||||
install_only: true
|
||||
|
||||
- name: Deploy kubevirt
|
||||
run: >-
|
||||
${{ env.kubevirt }}/hack/e2e-setup.sh \
|
||||
-v \
|
||||
--configure-inotify-limits \
|
||||
--configure-secondary-network \
|
||||
--deploy-kubevirt \
|
||||
--deploy-kubevirt-cdi \
|
||||
--deploy-kubevirt-common-instancetypes \
|
||||
--deploy-cnao \
|
||||
--create-cluster \
|
||||
--create-nad
|
||||
env:
|
||||
KIND: kind
|
||||
KUBECTL: kubectl
|
||||
|
||||
- name: Run integration tests
|
||||
uses: ansible-network/github_actions/.github/actions/ansible_test_integration@main
|
||||
with:
|
||||
collection_path: ${{ steps.install-kubevirt.outputs.collection_path }}
|
||||
python_version: ${{ env.python_version }}
|
||||
ansible_version: ${{ env.ansible_version }}
|
||||
ansible_test_targets: ${{ matrix.test-target }}
|
||||
env:
|
||||
ANSIBLE_COLLECTIONS_PATHS: /home/runner/collections
|
||||
@@ -4,6 +4,39 @@ Kubernetes Collection Release Notes
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
v4.0.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This major release brings several bug fixes. We have also removed support for ``ansible-core<2.15`` and deprecated functions and class from ``module_utils/common.py``.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
|
||||
- k8s - The module and K8sService were changed so warnings returned by the K8S API are now displayed to the user.
|
||||
|
||||
Removed Features (previously deprecated)
|
||||
----------------------------------------
|
||||
|
||||
- k8s - Support for ``merge_type=json`` has been removed in version 4.0.0. Please use ``kubernetes.core.k8s_json_patch`` instead (https://github.com/ansible-collections/kubernetes.core/pull/722).
|
||||
- k8s_exec - the previously deprecated ``result.return_code`` return value has been removed, consider using ``result.rc`` instead (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``K8sAnsibleMixin`` class has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``configuration_digest()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``get_api_client()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``unique_string()`` function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Resolve Collections util resource discovery fails when complex subresources present (https://github.com/ansible-collections/kubernetes.core/pull/676).
|
||||
- align `helmdiff_check()` function commandline rendering with the `deploy()` function (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
- avoid unsafe conditions in integration tests (https://github.com/ansible-collections/kubernetes.core/pull/665).
|
||||
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
|
||||
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
|
||||
v3.1.0
|
||||
======
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
# Also needs to be updated in galaxy.yml
|
||||
VERSION = 3.0.0
|
||||
VERSION = 4.0.0
|
||||
|
||||
TEST_ARGS ?= ""
|
||||
PYTHON_VERSION ?= `python -c 'import platform; print(".".join(platform.python_version_tuple()[0:2]))'`
|
||||
|
||||
@@ -93,7 +93,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
|
||||
---
|
||||
collections:
|
||||
- name: kubernetes.core
|
||||
version: 3.0.0
|
||||
version: 4.0.0
|
||||
```
|
||||
|
||||
### Installing the Kubernetes Python Library
|
||||
|
||||
@@ -854,3 +854,45 @@ releases:
|
||||
- 652-fix-json-patch-action.yml
|
||||
- 654-helm-expand-user.yml
|
||||
release_date: '2024-05-16'
|
||||
4.0.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Resolve Collections util resource discovery fails when complex subresources
|
||||
present (https://github.com/ansible-collections/kubernetes.core/pull/676).
|
||||
- align `helmdiff_check()` function commandline rendering with the `deploy()`
|
||||
function (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
- avoid unsafe conditions in integration tests (https://github.com/ansible-collections/kubernetes.core/pull/665).
|
||||
- helm - use ``reuse-values`` when running ``helm diff`` command (https://github.com/ansible-collections/kubernetes.core/issues/680).
|
||||
- integrations test helm_kubeconfig - set helm version to v3.10.3 to avoid incompatability
|
||||
with new bitnami charts (https://github.com/ansible-collections/kubernetes.core/pull/670).
|
||||
minor_changes:
|
||||
- inventory/k8s.py - Defer removal of k8s inventory plugin to version 5.0 (https://github.com/ansible-collections/kubernetes.core/pull/723).
|
||||
- k8s - The module and K8sService were changed so warnings returned by the K8S
|
||||
API are now displayed to the user.
|
||||
release_summary: This major release brings several bug fixes. We have also removed
|
||||
support for ``ansible-core<2.15`` and deprecated functions and class from
|
||||
``module_utils/common.py``.
|
||||
removed_features:
|
||||
- k8s - Support for ``merge_type=json`` has been removed in version 4.0.0. Please
|
||||
use ``kubernetes.core.k8s_json_patch`` instead (https://github.com/ansible-collections/kubernetes.core/pull/722).
|
||||
- k8s_exec - the previously deprecated ``result.return_code`` return value has
|
||||
been removed, consider using ``result.rc`` instead (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``K8sAnsibleMixin`` class
|
||||
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``configuration_digest()``
|
||||
function has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``get_api_client()`` function
|
||||
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
- module_utils/common.py - the previously deprecated ``unique_string()`` function
|
||||
has been removed (https://github.com/ansible-collections/kubernetes.core/pull/726).
|
||||
fragments:
|
||||
- 20231206-fix-unsafe-condition-in-integration.yml
|
||||
- 20240117-fix-helm-diff-cmd-line-rendering.yml
|
||||
- 20240222-Collections-util-resource-discovery-fails-when-complex-subresources-present.yml
|
||||
- 20240228-fix-helm-diff-with-reuse-values.yml
|
||||
- 20240423-k8s-display-warnings-to-users.yml
|
||||
- 4.0.0.yaml
|
||||
- inventory-update_removal_date.yml
|
||||
- k8s-merge_type-removed.yml
|
||||
- module_utils-common-remove-deprecated-functions-and-class.yaml
|
||||
release_date: '2024-05-24'
|
||||
|
||||
@@ -17,7 +17,7 @@ DEPRECATED
|
||||
----------
|
||||
:Removed in collection release after
|
||||
:Why: As discussed in https://github.com/ansible-collections/kubernetes.core/issues/31, we decided to
|
||||
remove the k8s inventory plugin in release 4.0.0.
|
||||
remove the k8s inventory plugin in release 5.0.0.
|
||||
|
||||
:Alternative: Use :ref:`kubernetes.core.k8s_info <kubernetes.core.k8s_info_module>` and :ref:`ansible.builtin.add_host <ansible.builtin.add_host_module>` instead.
|
||||
|
||||
@@ -357,7 +357,7 @@ Status
|
||||
------
|
||||
|
||||
|
||||
- This inventory will be removed in version 4.0.0. *[deprecated]*
|
||||
- This inventory will be removed in version 5.0.0. *[deprecated]*
|
||||
- For more information see `DEPRECATED`_.
|
||||
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ Parameters
|
||||
<td>
|
||||
<div><code>apply</code> compares the desired resource definition with the previously supplied resource definition, ignoring properties that are automatically generated</div>
|
||||
<div><code>apply</code> works better with Services than 'force=yes'</div>
|
||||
<div>mutually exclusive with <code>merge_type</code></div>
|
||||
<div>Mutually exclusive with <code>merge_type</code>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -513,18 +513,17 @@ Parameters
|
||||
</td>
|
||||
<td>
|
||||
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||
<li>json</li>
|
||||
<li>merge</li>
|
||||
<li>strategic-merge</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<div>Whether to override the default patch merge approach with a specific type. By default, the strategic merge will typically be used.</div>
|
||||
<div>For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may want to use <code>merge</code> if you see "strategic merge patch format is not supported"</div>
|
||||
<div>For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may want to use <code>merge</code> if you see "strategic merge patch format is not supported".</div>
|
||||
<div>See <a href='https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment'>https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment</a></div>
|
||||
<div>If more than one <code>merge_type</code> is given, the merge_types will be tried in order. This defaults to <code>['strategic-merge', 'merge']</code>, which is ideal for using the same parameters on resource kinds that combine Custom Resources and built-in resources.</div>
|
||||
<div>mutually exclusive with <code>apply</code></div>
|
||||
<div><em>merge_type=json</em> is deprecated and will be removed in version 4.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div>
|
||||
<div>Mutually exclusive with <code>apply</code>.</div>
|
||||
<div><em>merge_type=json</em> has been removed in version 4.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -25,7 +25,7 @@ tags:
|
||||
- openshift
|
||||
- okd
|
||||
- cluster
|
||||
version: 3.1.0
|
||||
version: 4.0.0
|
||||
build_ignore:
|
||||
- .DS_Store
|
||||
- "*.tar.gz"
|
||||
|
||||
@@ -22,10 +22,10 @@ plugin_routing:
|
||||
redirect: community.okd.openshift
|
||||
k8s:
|
||||
deprecation:
|
||||
removal_version: 4.0.0
|
||||
removal_version: 5.0.0
|
||||
warning_text: >-
|
||||
The k8s inventory plugin has been deprecated and
|
||||
will be removed in release 4.0.0.
|
||||
will be removed in release 5.0.0.
|
||||
modules:
|
||||
k8s_auth:
|
||||
redirect: community.okd.k8s_auth
|
||||
|
||||
@@ -20,10 +20,10 @@ DOCUMENTATION = """
|
||||
- Uses k8s.(yml|yaml) YAML configuration file to set parameter values.
|
||||
|
||||
deprecated:
|
||||
removed_in: 4.0.0
|
||||
removed_in: 5.0.0
|
||||
why: |
|
||||
As discussed in U(https://github.com/ansible-collections/kubernetes.core/issues/31), we decided to
|
||||
remove the k8s inventory plugin in release 4.0.0.
|
||||
remove the k8s inventory plugin in release 5.0.0.
|
||||
alternative: "Use M(kubernetes.core.k8s_info) and M(ansible.builtin.add_host) instead."
|
||||
|
||||
options:
|
||||
@@ -125,18 +125,18 @@ import json
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
HAS_K8S_MODULE_HELPER,
|
||||
k8s_import_exception,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
try:
|
||||
from kubernetes.dynamic.exceptions import DynamicApiError
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
HAS_K8S_MODULE_HELPER = True
|
||||
k8s_import_exception = None
|
||||
except ImportError as e:
|
||||
HAS_K8S_MODULE_HELPER = False
|
||||
k8s_import_exception = e
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
get_api_client,
|
||||
)
|
||||
|
||||
|
||||
def format_dynamic_api_exc(exc):
|
||||
@@ -164,8 +164,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
super(InventoryModule, self).parse(inventory, loader, path)
|
||||
|
||||
self.display.deprecated(
|
||||
"The 'k8s' inventory plugin has been deprecated and will be removed in release 4.0.0",
|
||||
version="4.0.0",
|
||||
"The 'k8s' inventory plugin has been deprecated and will be removed in release 5.0.0",
|
||||
version="5.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
cache_key = self._get_cache_prefix(path)
|
||||
|
||||
@@ -149,6 +149,7 @@ def k8s_apply(resource, definition, **kwargs):
|
||||
force_conflicts=kwargs.get("force_conflicts"),
|
||||
field_manager=kwargs.get("field_manager"),
|
||||
dry_run=kwargs.get("dry_run"),
|
||||
serialize=kwargs.get("serialize"),
|
||||
)
|
||||
if not existing:
|
||||
return resource.create(
|
||||
@@ -158,6 +159,7 @@ def k8s_apply(resource, definition, **kwargs):
|
||||
return resource.get(
|
||||
name=definition["metadata"]["name"],
|
||||
namespace=definition["metadata"].get("namespace"),
|
||||
**kwargs
|
||||
)
|
||||
return resource.patch(
|
||||
body=desired,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -139,6 +139,7 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
|
||||
|
||||
result = {"changed": False, "result": {}}
|
||||
instance = {}
|
||||
warnings = []
|
||||
|
||||
resource = svc.find_resource(kind, api_version, fail=True)
|
||||
definition["kind"] = resource.kind
|
||||
@@ -172,7 +173,7 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
|
||||
return result
|
||||
|
||||
if params.get("apply"):
|
||||
instance = svc.apply(resource, definition, existing)
|
||||
instance, warnings = svc.apply(resource, definition, existing)
|
||||
result["method"] = "apply"
|
||||
elif not existing:
|
||||
if state == "patched":
|
||||
@@ -183,16 +184,19 @@ def perform_action(svc, definition: Dict, params: Dict) -> Dict:
|
||||
)
|
||||
)
|
||||
return result
|
||||
instance = svc.create(resource, definition)
|
||||
instance, warnings = svc.create(resource, definition)
|
||||
result["method"] = "create"
|
||||
result["changed"] = True
|
||||
elif params.get("force", False):
|
||||
instance = svc.replace(resource, definition, existing)
|
||||
instance, warnings = svc.replace(resource, definition, existing)
|
||||
result["method"] = "replace"
|
||||
else:
|
||||
instance = svc.update(resource, definition, existing)
|
||||
instance, warnings = svc.update(resource, definition, existing)
|
||||
result["method"] = "update"
|
||||
|
||||
if warnings:
|
||||
result["warnings"] = warnings
|
||||
|
||||
# If needed, wait and/or create diff
|
||||
success = True
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
import copy
|
||||
from json import loads
|
||||
from re import compile
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
@@ -142,18 +144,12 @@ class K8sService:
|
||||
name: str,
|
||||
namespace: str,
|
||||
merge_type: str = None,
|
||||
) -> Dict:
|
||||
if merge_type == "json":
|
||||
self.module.deprecate(
|
||||
msg="json as a merge_type value is deprecated. Please use the k8s_json_patch module instead.",
|
||||
version="4.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
try:
|
||||
params = dict(name=name, namespace=namespace)
|
||||
params = dict(name=name, namespace=namespace, serialize=False)
|
||||
if merge_type:
|
||||
params["content_type"] = "application/{0}-patch+json".format(merge_type)
|
||||
return self.client.patch(resource, definition, **params).to_dict()
|
||||
return decode_response(self.client.patch(resource, definition, **params))
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to patch object: {0}".format(reason)
|
||||
@@ -330,123 +326,124 @@ class K8sService:
|
||||
result["resources"].append(hide_fields(res, hidden_fields))
|
||||
return result
|
||||
|
||||
def create(self, resource: Resource, definition: Dict) -> Dict:
|
||||
def create(self, resource: Resource, definition: Dict) -> Tuple[Dict, List[str]]:
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
name = definition["metadata"].get("name")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
k8s_obj = _encode_stringdata(definition)
|
||||
else:
|
||||
try:
|
||||
k8s_obj = self.client.create(
|
||||
resource, definition, namespace=namespace
|
||||
).to_dict()
|
||||
except ConflictError:
|
||||
# Some resources, like ProjectRequests, can't be created multiple times,
|
||||
# because the resources that they create don't match their kind
|
||||
# In this case we'll mark it as unchanged and warn the user
|
||||
self.module.warn(
|
||||
"{0} was not found, but creating it returned a 409 Conflict error. This can happen \
|
||||
if the resource you are creating does not directly create a resource of the same kind.".format(
|
||||
name
|
||||
)
|
||||
return _encode_stringdata(definition), []
|
||||
|
||||
try:
|
||||
return decode_response(
|
||||
self.client.create(
|
||||
resource, definition, namespace=namespace, serialize=False
|
||||
)
|
||||
return dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to create object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
return k8s_obj
|
||||
)
|
||||
except ConflictError:
|
||||
# Some resources, like ProjectRequests, can't be created multiple times,
|
||||
# because the resources that they create don't match their kind
|
||||
# In this case we'll mark it as unchanged and warn the user
|
||||
self.module.warn(
|
||||
"{0} was not found, but creating it returned a 409 Conflict error. This can happen \
|
||||
if the resource you are creating does not directly create a resource of the same kind.".format(
|
||||
name
|
||||
)
|
||||
)
|
||||
return dict(), []
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to create object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
def apply(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
existing: Optional[ResourceInstance] = None,
|
||||
) -> Dict:
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
|
||||
server_side_apply = self.module.params.get("server_side_apply")
|
||||
if server_side_apply:
|
||||
requires("kubernetes", "19.15.0", reason="to use server side apply")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
ignored, patch = apply_object(resource, _encode_stringdata(definition))
|
||||
if existing:
|
||||
k8s_obj = dict_merge(existing.to_dict(), patch)
|
||||
return dict_merge(existing.to_dict(), patch), []
|
||||
else:
|
||||
k8s_obj = patch
|
||||
else:
|
||||
try:
|
||||
params = {}
|
||||
if server_side_apply:
|
||||
params["server_side"] = True
|
||||
params.update(server_side_apply)
|
||||
k8s_obj = self.client.apply(
|
||||
resource, definition, namespace=namespace, **params
|
||||
).to_dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to apply object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
return k8s_obj
|
||||
return patch, []
|
||||
|
||||
try:
|
||||
params = {}
|
||||
if server_side_apply:
|
||||
params["server_side"] = True
|
||||
params.update(server_side_apply)
|
||||
return decode_response(
|
||||
self.client.apply(
|
||||
resource, definition, namespace=namespace, serialize=False, **params
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to apply object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
def replace(
|
||||
self,
|
||||
resource: Resource,
|
||||
definition: Dict,
|
||||
existing: ResourceInstance,
|
||||
) -> Dict:
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
append_hash = self.module.params.get("append_hash", False)
|
||||
name = definition["metadata"].get("name")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
k8s_obj = _encode_stringdata(definition)
|
||||
else:
|
||||
try:
|
||||
k8s_obj = self.client.replace(
|
||||
return _encode_stringdata(definition), []
|
||||
|
||||
try:
|
||||
return decode_response(
|
||||
self.client.replace(
|
||||
resource,
|
||||
definition,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
append_hash=append_hash,
|
||||
).to_dict()
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to replace object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
return k8s_obj
|
||||
serialize=False,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
reason = e.body if hasattr(e, "body") else e
|
||||
msg = "Failed to replace object: {0}".format(reason)
|
||||
raise CoreException(msg) from e
|
||||
|
||||
def update(
|
||||
self, resource: Resource, definition: Dict, existing: ResourceInstance
|
||||
) -> Dict:
|
||||
) -> Tuple[Dict, List[str]]:
|
||||
name = definition["metadata"].get("name")
|
||||
namespace = definition["metadata"].get("namespace")
|
||||
|
||||
if self._client_side_dry_run:
|
||||
k8s_obj = dict_merge(existing.to_dict(), _encode_stringdata(definition))
|
||||
else:
|
||||
exception = None
|
||||
for merge_type in self.module.params.get("merge_type") or [
|
||||
"strategic-merge",
|
||||
"merge",
|
||||
]:
|
||||
try:
|
||||
k8s_obj = self.patch_resource(
|
||||
resource,
|
||||
definition,
|
||||
name,
|
||||
namespace,
|
||||
merge_type=merge_type,
|
||||
)
|
||||
exception = None
|
||||
except CoreException as e:
|
||||
exception = e
|
||||
continue
|
||||
break
|
||||
if exception:
|
||||
raise exception
|
||||
return k8s_obj
|
||||
return dict_merge(existing.to_dict(), _encode_stringdata(definition)), []
|
||||
|
||||
exception = None
|
||||
for merge_type in self.module.params.get("merge_type") or [
|
||||
"strategic-merge",
|
||||
"merge",
|
||||
]:
|
||||
try:
|
||||
return self.patch_resource(
|
||||
resource,
|
||||
definition,
|
||||
name,
|
||||
namespace,
|
||||
merge_type=merge_type,
|
||||
)
|
||||
except CoreException as e:
|
||||
exception = e
|
||||
continue
|
||||
raise exception
|
||||
|
||||
def delete(
|
||||
self,
|
||||
@@ -543,3 +540,83 @@ def hide_field(definition: dict, hidden_field: str) -> dict:
|
||||
else:
|
||||
del definition[split[0]]
|
||||
return definition
|
||||
|
||||
|
||||
def decode_response(resp) -> Tuple[Dict, List[str]]:
|
||||
"""
|
||||
This function decodes unserialized responses from the Kubernetes python
|
||||
client and decodes the RFC2616 14.46 warnings found in the response
|
||||
headers.
|
||||
"""
|
||||
obj = ResourceInstance(None, loads(resp.data.decode("utf8"))).to_dict()
|
||||
warnings = []
|
||||
if (
|
||||
resp.headers is not None
|
||||
and "warning" in resp.headers
|
||||
and resp.headers["warning"] is not None
|
||||
):
|
||||
warnings = resp.headers["warning"].split(", ")
|
||||
return obj, decode_warnings(warnings)
|
||||
|
||||
|
||||
def decode_warnings(warnings: str) -> List[str]:
|
||||
"""
|
||||
This function decodes RFC2616 14.46 warnings in a simplified way, where
|
||||
only the warn-texts are returned in a list.
|
||||
"""
|
||||
p = compile('\\d{3} .+ (".+")')
|
||||
|
||||
decoded = []
|
||||
for warning in warnings:
|
||||
m = p.match(warning)
|
||||
if m:
|
||||
try:
|
||||
parsed, unused = parse_quoted_string(m.group(1))
|
||||
decoded.append(parsed)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
return decoded
|
||||
|
||||
|
||||
def parse_quoted_string(quoted_string: str) -> Tuple[str, str]:
|
||||
"""
|
||||
This function was adapted from:
|
||||
https://github.com/kubernetes/apimachinery/blob/bb8822152cabfb4f34dbc26270f874ce53db50de/pkg/util/net/http.go#L609
|
||||
"""
|
||||
if len(quoted_string) == 0:
|
||||
raise ValueError("invalid quoted string: 0-length")
|
||||
|
||||
if quoted_string[0] != '"':
|
||||
raise ValueError("invalid quoted string: missing initial quote")
|
||||
|
||||
quoted_string = quoted_string[1:]
|
||||
remainder = ""
|
||||
escaping = False
|
||||
closed_quote = False
|
||||
result = []
|
||||
|
||||
for i, b in enumerate(quoted_string):
|
||||
if b == '"':
|
||||
if escaping:
|
||||
result.append(b)
|
||||
escaping = False
|
||||
else:
|
||||
closed_quote = True
|
||||
remainder_start = i + 1
|
||||
remainder = quoted_string[remainder_start:].strip()
|
||||
break
|
||||
elif b == "\\":
|
||||
if escaping:
|
||||
result.append(b)
|
||||
escaping = False
|
||||
else:
|
||||
escaping = True
|
||||
else:
|
||||
result.append(b)
|
||||
escaping = False
|
||||
|
||||
if not closed_quote:
|
||||
raise ValueError("invalid quoted string: missing closing quote")
|
||||
|
||||
return "".join(result), remainder
|
||||
|
||||
@@ -57,15 +57,14 @@ options:
|
||||
- Whether to override the default patch merge approach with a specific type. By default, the strategic
|
||||
merge will typically be used.
|
||||
- For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may
|
||||
want to use C(merge) if you see "strategic merge patch format is not supported"
|
||||
want to use C(merge) if you see "strategic merge patch format is not supported".
|
||||
- See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)
|
||||
- If more than one C(merge_type) is given, the merge_types will be tried in order. This defaults to
|
||||
C(['strategic-merge', 'merge']), which is ideal for using the same parameters on resource kinds that
|
||||
combine Custom Resources and built-in resources.
|
||||
- mutually exclusive with C(apply)
|
||||
- I(merge_type=json) is deprecated and will be removed in version 4.0.0. Please use M(kubernetes.core.k8s_json_patch) instead.
|
||||
- Mutually exclusive with C(apply).
|
||||
- I(merge_type=json) has been removed in version 4.0.0. Please use M(kubernetes.core.k8s_json_patch) instead.
|
||||
choices:
|
||||
- json
|
||||
- merge
|
||||
- strategic-merge
|
||||
type: list
|
||||
@@ -101,7 +100,7 @@ options:
|
||||
- C(apply) compares the desired resource definition with the previously supplied resource definition,
|
||||
ignoring properties that are automatically generated
|
||||
- C(apply) works better with Services than 'force=yes'
|
||||
- mutually exclusive with C(merge_type)
|
||||
- Mutually exclusive with C(merge_type).
|
||||
default: False
|
||||
type: bool
|
||||
template:
|
||||
@@ -460,7 +459,7 @@ def argspec():
|
||||
argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC))
|
||||
argument_spec.update(copy.deepcopy(WAIT_ARG_SPEC))
|
||||
argument_spec["merge_type"] = dict(
|
||||
type="list", elements="str", choices=["json", "merge", "strategic-merge"]
|
||||
type="list", elements="str", choices=["merge", "strategic-merge"]
|
||||
)
|
||||
argument_spec["validate"] = dict(type="dict", default=None, options=validate_spec())
|
||||
argument_spec["append_hash"] = dict(type="bool", default=False)
|
||||
|
||||
@@ -135,7 +135,7 @@ from ansible.module_utils._text import to_native
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||
AnsibleModule,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
||||
AUTH_ARG_SPEC,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||
@@ -219,12 +219,6 @@ def execute_module(module, client):
|
||||
else:
|
||||
rc = int(err["details"]["causes"][0]["message"])
|
||||
|
||||
module.deprecate(
|
||||
"The 'return_code' return key is being renamed to 'rc'. "
|
||||
"Both keys are being returned for now to allow users to migrate their automation.",
|
||||
version="4.0.0",
|
||||
collection_name="kubernetes.core",
|
||||
)
|
||||
module.exit_json(
|
||||
# Some command might change environment, but ultimately failing at end
|
||||
changed=True,
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
assert:
|
||||
that:
|
||||
- install is changed
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- install.status.status | lower == 'deployed'
|
||||
|
||||
- name: Check helm_info content
|
||||
@@ -95,7 +95,7 @@
|
||||
- name: "Assert that {{ chart_test }} is installed from {{ source }} with helm_info"
|
||||
assert:
|
||||
that:
|
||||
- content_info.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- content_info.status.chart == chart_test+"-"+chart_test_version
|
||||
- content_info.status.status | lower == 'deployed'
|
||||
- release_state_content_info.status.status | lower == 'deployed'
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
assert:
|
||||
that:
|
||||
- install is not changed
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- install.status.status | lower == 'deployed'
|
||||
|
||||
- name: "Add vars to {{ chart_test }} from {{ source }}"
|
||||
@@ -130,7 +130,7 @@
|
||||
that:
|
||||
- install is changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- "install.status['values'].revisionHistoryLimit == 0"
|
||||
|
||||
- name: Check idempotency after adding vars
|
||||
@@ -148,7 +148,7 @@
|
||||
that:
|
||||
- install is not changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- "install.status['values'].revisionHistoryLimit == 0"
|
||||
|
||||
- name: "Remove Vars to {{ chart_test }} from {{ source }}"
|
||||
@@ -165,7 +165,7 @@
|
||||
that:
|
||||
- install is changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- install.status['values'] == {}
|
||||
|
||||
- name: Check idempotency after removing vars
|
||||
@@ -182,7 +182,7 @@
|
||||
that:
|
||||
- install is not changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- install.status['values'] == {}
|
||||
|
||||
- name: "Upgrade {{ chart_test }} from {{ source }}"
|
||||
@@ -199,7 +199,7 @@
|
||||
that:
|
||||
- install is changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version_upgrade }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version_upgrade
|
||||
|
||||
- name: Check idempotency after upgrade
|
||||
helm:
|
||||
@@ -215,7 +215,7 @@
|
||||
that:
|
||||
- install is not changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version_upgrade }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version_upgrade
|
||||
|
||||
- name: "Remove {{ chart_test }} from {{ source }}"
|
||||
helm:
|
||||
@@ -316,7 +316,7 @@
|
||||
that:
|
||||
- install is changed
|
||||
- install.status.status | lower == 'deployed'
|
||||
- install.status.chart == "{{ chart_test }}-{{ chart_test_version }}"
|
||||
- install.status.chart == chart_test+"-"+chart_test_version
|
||||
- "install.status['values'].revisionHistoryLimit == 0"
|
||||
|
||||
- name: "Install {{ chart_test }} from {{ source }} with values_files (again)"
|
||||
@@ -357,7 +357,7 @@
|
||||
- result is changed
|
||||
- result is not failed
|
||||
- result.rc == 0
|
||||
- result.command is match("{{ helm_binary }} template {{ chart_source }}")
|
||||
- result.command is match(helm_binary+" template "+chart_source)
|
||||
|
||||
- name: Check templates created
|
||||
stat:
|
||||
@@ -388,7 +388,7 @@
|
||||
- result is changed
|
||||
- result is not failed
|
||||
- result.rc == 0
|
||||
- result.command is match("{{ helm_binary }} template MyRelease {{ chart_source }}")
|
||||
- result.command is match(helm_binary+" template MyRelease "+chart_source)
|
||||
- result.stdout is search("ThisValue")
|
||||
when: chart_source is search("test-chart")
|
||||
# limit assertion of test result to controlled (local) chart_source
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
- name: assert that pods are running on cordoned node
|
||||
assert:
|
||||
that:
|
||||
- "{{ Pod.resources | selectattr('status.phase', 'equalto', 'Running') | selectattr('spec.nodeName', 'equalto', node_to_drain) | list | length > 0 }}"
|
||||
- Pod.resources | selectattr('status.phase', 'equalto', 'Running') | selectattr('spec.nodeName', 'equalto', node_to_drain) | list | length > 0
|
||||
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
@@ -236,7 +236,7 @@
|
||||
assert:
|
||||
that:
|
||||
- drain_result is changed
|
||||
- '"node {{ node_to_drain }} marked unschedulable." in drain_result.result'
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_result.result'
|
||||
|
||||
- name: assert that unmanaged pod were deleted
|
||||
k8s_info:
|
||||
@@ -338,7 +338,7 @@
|
||||
assert:
|
||||
that:
|
||||
- disable_evict is changed
|
||||
- '"node {{ node_to_drain }} marked unschedulable." in disable_evict.result'
|
||||
- '"node "+node_to_drain+" marked unschedulable." in disable_evict.result'
|
||||
|
||||
- name: assert that unmanaged pod were deleted
|
||||
k8s_info:
|
||||
@@ -401,7 +401,7 @@
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector is changed
|
||||
- '"node {{ node_to_drain }} marked unschedulable." in drain_pod_selector.result'
|
||||
- '"node "+node_to_drain+" marked unschedulable." in drain_pod_selector.result'
|
||||
|
||||
- name: assert that pod created before is still running
|
||||
k8s_info:
|
||||
@@ -429,8 +429,8 @@
|
||||
assert:
|
||||
that:
|
||||
- drain_pod_selector_equal is changed
|
||||
- '"node {{ node_to_drain }} already marked unschedulable." in drain_pod_selector_equal.result'
|
||||
- '"Deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: {{ test_namespace }}/ansible-drain-pod." in drain_pod_selector_equal.warnings'
|
||||
- '"node "+node_to_drain+" already marked unschedulable." in drain_pod_selector_equal.result'
|
||||
- '"Deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: "+test_namespace+"/ansible-drain-pod." in drain_pod_selector_equal.warnings'
|
||||
|
||||
- name: Uncordon node
|
||||
k8s_drain:
|
||||
|
||||
@@ -424,7 +424,7 @@
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.resources[0].data.testkey == "{{ cmap_data.stdout | b64encode }}"
|
||||
- result.resources[0].data.testkey == (cmap_data.stdout | b64encode)
|
||||
|
||||
# test setting module defaults for kubernetes.core.k8s_info
|
||||
- block:
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
- name: assert pod has been created
|
||||
assert:
|
||||
that:
|
||||
- "{{ pods.resources | length == 1 }}"
|
||||
- pods.resources | length == 1
|
||||
|
||||
- name: create pod using generate_name parameter should succeed
|
||||
k8s:
|
||||
@@ -86,7 +86,7 @@
|
||||
- name: assert pod has been created
|
||||
assert:
|
||||
that:
|
||||
- "{{ pods.resources | length == 2 }}"
|
||||
- pods.resources | length == 2
|
||||
|
||||
- name: create pod using metadata.generateName parameter should succeed
|
||||
k8s:
|
||||
@@ -102,7 +102,7 @@
|
||||
- name: assert pod has been created
|
||||
assert:
|
||||
that:
|
||||
- "{{ pods.resources | length == 3 }}"
|
||||
- pods.resources | length == 3
|
||||
|
||||
- name: create object using metadata.generateName should support wait option
|
||||
k8s:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
assert:
|
||||
that:
|
||||
- fake_pod is failed
|
||||
- 'fake_pod.msg == "Pod {{ test_namespace }}/this_pod_does_exist not found."'
|
||||
- fake_pod.msg == "Pod "+test_namespace+"/this_pod_does_exist not found."
|
||||
|
||||
- name: create hello-world deployment
|
||||
k8s:
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
assert:
|
||||
that:
|
||||
- _result.result.status.phase == 'Running'
|
||||
- _result.result.spec.nodeName == "{{ node_to_taint }}"
|
||||
- _result.result.spec.nodeName == node_to_taint
|
||||
|
||||
- name: Taint node (check_mode)
|
||||
kubernetes.core.k8s_taint:
|
||||
@@ -89,8 +89,8 @@
|
||||
assert:
|
||||
that:
|
||||
- _result.changed
|
||||
- "{{ item['effect'] == taint_patch_1[0]['effect'] }}"
|
||||
- "{{ item['key'] == taint_patch_1[0]['key'] }}"
|
||||
- item['effect'] == taint_patch_1[0]['effect']
|
||||
- item['key'] == taint_patch_1[0]['key']
|
||||
loop: "{{ _result.result.spec.taints }}"
|
||||
|
||||
- name: Taint node (idempotency) - (check_mode)
|
||||
|
||||
@@ -213,8 +213,8 @@
|
||||
- name: check that resources creation failed
|
||||
assert:
|
||||
that:
|
||||
- '{{ resource.results[0].resources | length == 0 }}'
|
||||
- '{{ resource.results[1].resources | length == 0 }}'
|
||||
- resource.results.0.resources | length == 0
|
||||
- resource.results.1.resources | length == 0
|
||||
|
||||
- name: create pod without namespace (continue_on_error = true)
|
||||
kubernetes.core.k8s:
|
||||
|
||||
@@ -130,9 +130,9 @@
|
||||
assert:
|
||||
that:
|
||||
- result_configmap.apiVersion == 'v1'
|
||||
- result_configmap.metadata.name == "{{ configmap_name }}"
|
||||
- result_configmap.metadata.namespace == "{{ test_namespace[2] }}"
|
||||
- result_configmap.data.value == "{{ configmap_data }}"
|
||||
- result_configmap.metadata.name == configmap_name
|
||||
- result_configmap.metadata.namespace == test_namespace[2]
|
||||
- result_configmap.data.value == configmap_data
|
||||
|
||||
# test lookup plugin using src parameter
|
||||
- block:
|
||||
@@ -159,9 +159,9 @@
|
||||
assert:
|
||||
that:
|
||||
- src_configmap.apiVersion == 'v1'
|
||||
- src_configmap.metadata.name == "{{ configmap_name }}"
|
||||
- src_configmap.metadata.namespace == "{{ test_namespace[2] }}"
|
||||
- src_configmap.data.value == "{{ configmap_data }}"
|
||||
- src_configmap.metadata.name == configmap_name
|
||||
- src_configmap.metadata.namespace == test_namespace[2]
|
||||
- src_configmap.data.value == configmap_data
|
||||
|
||||
always:
|
||||
- name: Delete temporary file created
|
||||
@@ -198,9 +198,9 @@
|
||||
assert:
|
||||
that:
|
||||
- configmap_no_ssl.apiVersion == 'v1'
|
||||
- configmap_no_ssl.metadata.name == "{{ configmap_name }}"
|
||||
- configmap_no_ssl.metadata.namespace == "{{ test_namespace[2] }}"
|
||||
- configmap_no_ssl.data.value == "{{ configmap_data }}"
|
||||
- configmap_no_ssl.metadata.name == configmap_name
|
||||
- configmap_no_ssl.metadata.namespace == test_namespace[2]
|
||||
- configmap_no_ssl.data.value == configmap_data
|
||||
|
||||
- name: Retrieve configmap using authentication aliases (validate_certs=true)
|
||||
set_fact:
|
||||
@@ -210,9 +210,9 @@
|
||||
assert:
|
||||
that:
|
||||
- configmap_with_ssl.apiVersion == 'v1'
|
||||
- configmap_with_ssl.metadata.name == "{{ configmap_name }}"
|
||||
- configmap_with_ssl.metadata.namespace == "{{ test_namespace[2] }}"
|
||||
- configmap_with_ssl.data.value == "{{ configmap_data }}"
|
||||
- configmap_with_ssl.metadata.name == configmap_name
|
||||
- configmap_with_ssl.metadata.namespace == test_namespace[2]
|
||||
- configmap_with_ssl.data.value == configmap_data
|
||||
|
||||
always:
|
||||
- name: Delete temporary directory
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
plugins/module_utils/client/discovery.py import-3.6!skip
|
||||
plugins/module_utils/client/discovery.py import-3.7!skip
|
||||
plugins/module_utils/client/discovery.py import-3.8!skip
|
||||
plugins/module_utils/client/discovery.py import-3.9!skip
|
||||
plugins/module_utils/client/discovery.py import-3.10!skip
|
||||
plugins/module_utils/client/discovery.py import-3.11!skip
|
||||
plugins/module_utils/client/resource.py import-3.6!skip
|
||||
plugins/module_utils/client/resource.py import-3.7!skip
|
||||
plugins/module_utils/client/resource.py import-3.8!skip
|
||||
plugins/module_utils/client/resource.py import-3.9!skip
|
||||
plugins/module_utils/client/resource.py import-3.10!skip
|
||||
plugins/module_utils/client/resource.py import-3.11!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.10!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.11!skip
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
plugins/module_utils/client/discovery.py import-3.6!skip
|
||||
plugins/module_utils/client/discovery.py import-3.7!skip
|
||||
plugins/module_utils/client/discovery.py import-3.8!skip
|
||||
plugins/module_utils/client/discovery.py import-3.9!skip
|
||||
plugins/module_utils/client/discovery.py import-3.10!skip
|
||||
plugins/module_utils/client/discovery.py import-3.11!skip
|
||||
plugins/module_utils/client/resource.py import-3.6!skip
|
||||
plugins/module_utils/client/resource.py import-3.7!skip
|
||||
plugins/module_utils/client/resource.py import-3.8!skip
|
||||
plugins/module_utils/client/resource.py import-3.9!skip
|
||||
plugins/module_utils/client/resource.py import-3.10!skip
|
||||
plugins/module_utils/client/resource.py import-3.11!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.10!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.11!skip
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
plugins/module_utils/client/discovery.py import-3.6!skip
|
||||
plugins/module_utils/client/discovery.py import-3.7!skip
|
||||
plugins/module_utils/client/discovery.py import-3.8!skip
|
||||
plugins/module_utils/client/discovery.py import-3.9!skip
|
||||
plugins/module_utils/client/discovery.py import-3.10!skip
|
||||
plugins/module_utils/client/discovery.py import-3.11!skip
|
||||
plugins/module_utils/client/discovery.py import-3.12!skip
|
||||
plugins/module_utils/client/resource.py import-3.6!skip
|
||||
plugins/module_utils/client/resource.py import-3.7!skip
|
||||
plugins/module_utils/client/resource.py import-3.8!skip
|
||||
plugins/module_utils/client/resource.py import-3.9!skip
|
||||
plugins/module_utils/client/resource.py import-3.10!skip
|
||||
plugins/module_utils/client/resource.py import-3.11!skip
|
||||
plugins/module_utils/client/resource.py import-3.12!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.10!skip
|
||||
plugins/module_utils/k8sdynamicclient.py import-3.11!skip
|
||||
|
||||
@@ -31,7 +31,7 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"action, params, existing, instance, expected",
|
||||
"action, params, existing, instance_warnings, expected",
|
||||
[
|
||||
(
|
||||
"delete",
|
||||
@@ -51,14 +51,26 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
|
||||
"apply",
|
||||
{"apply": "yes"},
|
||||
{},
|
||||
definition,
|
||||
(definition, []),
|
||||
{"changed": True, "method": "apply", "result": definition},
|
||||
),
|
||||
(
|
||||
"apply",
|
||||
{"apply": "yes"},
|
||||
{},
|
||||
(definition, ["test warning"]),
|
||||
{
|
||||
"changed": True,
|
||||
"method": "apply",
|
||||
"result": definition,
|
||||
"warnings": ["test warning"],
|
||||
},
|
||||
),
|
||||
(
|
||||
"create",
|
||||
{"state": "patched"},
|
||||
{},
|
||||
{},
|
||||
({}, []),
|
||||
{
|
||||
"changed": False,
|
||||
"result": {},
|
||||
@@ -71,42 +83,78 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
|
||||
"create",
|
||||
{},
|
||||
{},
|
||||
definition,
|
||||
(definition, []),
|
||||
{"changed": True, "method": "create", "result": definition},
|
||||
),
|
||||
(
|
||||
"create",
|
||||
{},
|
||||
{},
|
||||
(definition, ["test warning"]),
|
||||
{
|
||||
"changed": True,
|
||||
"method": "create",
|
||||
"result": definition,
|
||||
"warnings": ["test warning"],
|
||||
},
|
||||
),
|
||||
(
|
||||
"replace",
|
||||
{"force": "yes"},
|
||||
definition,
|
||||
definition,
|
||||
(definition, []),
|
||||
{"changed": False, "method": "replace", "result": definition},
|
||||
),
|
||||
(
|
||||
"replace",
|
||||
{"force": "yes"},
|
||||
definition,
|
||||
modified_def,
|
||||
(modified_def, []),
|
||||
{"changed": True, "method": "replace", "result": modified_def},
|
||||
),
|
||||
(
|
||||
"replace",
|
||||
{"force": "yes"},
|
||||
definition,
|
||||
(modified_def, ["test warning"]),
|
||||
{
|
||||
"changed": True,
|
||||
"method": "replace",
|
||||
"result": modified_def,
|
||||
"warnings": ["test warning"],
|
||||
},
|
||||
),
|
||||
(
|
||||
"update",
|
||||
{},
|
||||
definition,
|
||||
definition,
|
||||
(definition, []),
|
||||
{"changed": False, "method": "update", "result": definition},
|
||||
),
|
||||
(
|
||||
"update",
|
||||
{},
|
||||
definition,
|
||||
modified_def,
|
||||
(modified_def, []),
|
||||
{"changed": True, "method": "update", "result": modified_def},
|
||||
),
|
||||
(
|
||||
"update",
|
||||
{},
|
||||
definition,
|
||||
(modified_def, ["test warning"]),
|
||||
{
|
||||
"changed": True,
|
||||
"method": "update",
|
||||
"result": modified_def,
|
||||
"warnings": ["test warning"],
|
||||
},
|
||||
),
|
||||
(
|
||||
"create",
|
||||
{"label_selectors": ["app=foo"]},
|
||||
{},
|
||||
definition,
|
||||
(definition, []),
|
||||
{
|
||||
"changed": False,
|
||||
"msg": "resource 'kind=Pod,name=foo,namespace=foo' filtered by label_selectors.",
|
||||
@@ -116,18 +164,18 @@ modified_def["metadata"]["labels"]["environment"] = "testing"
|
||||
"create",
|
||||
{"label_selectors": ["app=nginx"]},
|
||||
{},
|
||||
definition,
|
||||
(definition, []),
|
||||
{"changed": True, "method": "create", "result": definition},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_perform_action(action, params, existing, instance, expected):
|
||||
def test_perform_action(action, params, existing, instance_warnings, expected):
|
||||
svc = Mock()
|
||||
svc.find_resource.return_value = Mock(
|
||||
kind=definition["kind"], group_version=definition["apiVersion"]
|
||||
)
|
||||
svc.retrieve.return_value = ResourceInstance(None, existing) if existing else None
|
||||
spec = {action + ".return_value": instance}
|
||||
spec = {action + ".return_value": instance_warnings}
|
||||
svc.configure_mock(**spec)
|
||||
|
||||
result = perform_action(svc, definition, params)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from json import dumps
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
||||
K8sService,
|
||||
diff_objects,
|
||||
parse_quoted_string,
|
||||
)
|
||||
from kubernetes.dynamic.exceptions import NotFoundError
|
||||
from kubernetes.dynamic.resource import Resource, ResourceInstance
|
||||
@@ -57,6 +59,22 @@ def mock_pod_updated_resource_instance():
|
||||
return ResourceInstance(None, pod_definition_updated)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def mock_pod_response():
|
||||
resp = Mock()
|
||||
resp.data.decode.return_value = dumps(pod_definition)
|
||||
resp.headers = {}
|
||||
return resp
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def mock_pod_warnings_response():
|
||||
resp = Mock()
|
||||
resp.data.decode.return_value = dumps(pod_definition)
|
||||
resp.headers = {"warning": '299 - "test warning 1", 299 - "test warning 2"'}
|
||||
return resp
|
||||
|
||||
|
||||
def test_diff_objects_no_diff():
|
||||
match, diff = diff_objects(pod_definition, pod_definition)
|
||||
|
||||
@@ -159,16 +177,33 @@ def test_service_delete_existing_resource_check_mode(mock_pod_resource_instance)
|
||||
client.delete.assert_not_called()
|
||||
|
||||
|
||||
def test_service_create_resource(mock_pod_resource_instance):
|
||||
spec = {"create.side_effect": [mock_pod_resource_instance]}
|
||||
def test_service_create_resource(mock_pod_response, mock_pod_resource_instance):
|
||||
spec = {"create.side_effect": [mock_pod_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result = svc.create(Mock(), pod_definition)
|
||||
result, warnings = svc.create(Mock(), pod_definition)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert not warnings
|
||||
|
||||
|
||||
def test_service_create_resource_warnings(
|
||||
mock_pod_warnings_response, mock_pod_resource_instance
|
||||
):
|
||||
spec = {"create.side_effect": [mock_pod_warnings_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result, warnings = svc.create(Mock(), pod_definition)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert warnings[0] == "test warning 1"
|
||||
assert warnings[1] == "test warning 2"
|
||||
|
||||
|
||||
def test_service_create_resource_check_mode():
|
||||
@@ -176,9 +211,10 @@ def test_service_create_resource_check_mode():
|
||||
client.create.return_value = mock_pod_resource_instance
|
||||
module = Mock(params={}, check_mode=True)
|
||||
svc = K8sService(client, module)
|
||||
result = svc.create(Mock(), pod_definition)
|
||||
result, warnings = svc.create(Mock(), pod_definition)
|
||||
|
||||
assert result == pod_definition
|
||||
assert not warnings
|
||||
client.create.assert_not_called()
|
||||
|
||||
|
||||
@@ -224,40 +260,99 @@ def test_create_project_request():
|
||||
assert results["result"] == project_definition
|
||||
|
||||
|
||||
def test_service_apply_existing_resource(mock_pod_resource_instance):
|
||||
spec = {"apply.side_effect": [mock_pod_resource_instance]}
|
||||
def test_service_apply_existing_resource(mock_pod_response, mock_pod_resource_instance):
|
||||
spec = {"apply.side_effect": [mock_pod_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {"apply": True}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result = svc.apply(Mock(), pod_definition_updated, mock_pod_resource_instance)
|
||||
result, warnings = svc.apply(
|
||||
Mock(), pod_definition_updated, mock_pod_resource_instance
|
||||
)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert not warnings
|
||||
|
||||
|
||||
def test_service_replace_existing_resource(mock_pod_resource_instance):
|
||||
spec = {"replace.side_effect": [mock_pod_resource_instance]}
|
||||
def test_service_apply_existing_resource_warnings(
|
||||
mock_pod_warnings_response, mock_pod_resource_instance
|
||||
):
|
||||
spec = {"apply.side_effect": [mock_pod_warnings_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {"apply": True}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result, warnings = svc.apply(
|
||||
Mock(), pod_definition_updated, mock_pod_resource_instance
|
||||
)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert warnings[0] == "test warning 1"
|
||||
assert warnings[1] == "test warning 2"
|
||||
|
||||
|
||||
def test_service_replace_existing_resource(
|
||||
mock_pod_response, mock_pod_resource_instance
|
||||
):
|
||||
spec = {"replace.side_effect": [mock_pod_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert not warnings
|
||||
|
||||
|
||||
def test_service_update_existing_resource(mock_pod_resource_instance):
|
||||
spec = {"replace.side_effect": [mock_pod_resource_instance]}
|
||||
def test_service_replace_existing_resource_warnings(
|
||||
mock_pod_warnings_response, mock_pod_resource_instance
|
||||
):
|
||||
spec = {"replace.side_effect": [mock_pod_warnings_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert warnings[0] == "test warning 1"
|
||||
assert warnings[1] == "test warning 2"
|
||||
|
||||
|
||||
def test_service_update_existing_resource(
|
||||
mock_pod_response, mock_pod_resource_instance
|
||||
):
|
||||
spec = {"replace.side_effect": [mock_pod_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert not warnings
|
||||
|
||||
|
||||
def test_service_update_existing_resource_warnings(
|
||||
mock_pod_warnings_response, mock_pod_resource_instance
|
||||
):
|
||||
spec = {"replace.side_effect": [mock_pod_warnings_response]}
|
||||
client = Mock(**spec)
|
||||
module = Mock()
|
||||
module.params = {}
|
||||
module.check_mode = False
|
||||
svc = K8sService(client, module)
|
||||
result, warnings = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
|
||||
|
||||
assert result == mock_pod_resource_instance.to_dict()
|
||||
assert warnings[0] == "test warning 1"
|
||||
assert warnings[1] == "test warning 2"
|
||||
|
||||
|
||||
def test_service_find(mock_pod_resource_instance):
|
||||
@@ -288,3 +383,24 @@ def test_service_find_error():
|
||||
assert isinstance(results, dict)
|
||||
assert results["api_found"] is True
|
||||
assert results["resources"] == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"quoted_string,expected_val,expected_remainder",
|
||||
[
|
||||
(
|
||||
'"Response is stale" Tue, 15 Nov 1994 12:45:26 GMT',
|
||||
"Response is stale",
|
||||
"Tue, 15 Nov 1994 12:45:26 GMT",
|
||||
),
|
||||
(
|
||||
'"unknown field \\"spec.template.spec.disk\\""',
|
||||
'unknown field "spec.template.spec.disk"',
|
||||
"",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_parse_quoted_string(quoted_string, expected_val, expected_remainder):
|
||||
val, remainder = parse_quoted_string(quoted_string)
|
||||
assert val == expected_val
|
||||
assert remainder == expected_remainder
|
||||
|
||||
22
tools/kubevirt_list_targets.py
Normal file
22
tools/kubevirt_list_targets.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import os
|
||||
import sys
|
||||
from pathlib import PosixPath
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
src = sys.argv[1]
|
||||
path = PosixPath(src) / PosixPath("tests/integration/targets/")
|
||||
|
||||
def _is_disable(path):
|
||||
flags = ("unsupported", "disabled", "unstable", "hidden")
|
||||
aliases_path = path / PosixPath("aliases")
|
||||
return (aliases_path.exists() and any((d.startswith(flags) for d in aliases_path.read_text().split("\n"))))
|
||||
|
||||
targets = [i.stem for i in path.glob("*") if i.is_dir() and not _is_disable(i)]
|
||||
with open(os.environ.get("GITHUB_OUTPUT"), "a", encoding="utf-8") as fw:
|
||||
fw.write(f"kubevirt_targets={targets}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user