Run integration tests using ansible-core 2.19 (#888) (#895)

* fix integration test ``k8s_full`` running with ansible-core 2.19

* Fix templating issues

* fix test on current ansible version

* fix tests cases

* Fix additional tests

* fix the templating mechanism

* consider using variable_[start/end]_string while parsing template

* Remove support for omit into template option

* Remove unnecessary unit tests

(cherry picked from commit 2cb5d6c316)

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
This commit is contained in:
patchback[bot]
2025-04-25 16:49:10 +02:00
committed by GitHub
parent 0eff03dd19
commit d2dcb9e55f
16 changed files with 103 additions and 249 deletions

View File

@@ -50,6 +50,7 @@ jobs:
source: "./source"
cloud_common: "./cloudcommon"
ansible_posix: "./ansible_posix"
community_general: "./community_general"
strategy:
fail-fast: false
matrix:
@@ -61,7 +62,7 @@ jobs:
- true
- false
workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }}
name: "integration-py${{ matrix.python-version }}-${{ matrix.ansible-version }}-${{ matrix.workflow-id }}"
name: "integration-py${{ matrix.python-version }}-${{ matrix.ansible-version }}-${{ matrix.workflow-id }}-enable_turbo=${{ matrix.enable-turbo-mode }}"
steps:
- name: Read target
id: read-targets
@@ -118,6 +119,13 @@ jobs:
path: ${{ env.ansible_posix }}
ref: main
- name: checkout ansible-collections/community.general
uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
with:
repository: ansible-collections/community.general
path: ${{ env.community_general }}
ref: main
- name: install cloud.common collection
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
with:
@@ -130,6 +138,12 @@ jobs:
install_python_dependencies: true
source_path: ${{ env.ansible_posix }}
- name: install community.general collection
uses: ansible-network/github_actions/.github/actions/build_install_collection@main
with:
install_python_dependencies: false
source_path: ${{ env.community_general }}
- name: create kubernetes cluster
uses: helm/kind-action@v1.8.0
with:

View File

@@ -0,0 +1,3 @@
---
minor_changes:
- action/k8s_info - update templating mechanism with changes from ``ansible-core 2.19`` (https://github.com/ansible-collections/kubernetes.core/pull/888).

View File

@@ -25,30 +25,18 @@ from ansible.module_utils.parsing.convert_bool import boolean
from ansible.module_utils.six import iteritems, string_types
from ansible.plugins.action import ActionBase
try:
from ansible.template import trust_as_template
except ImportError:
trust_as_template = None
class RemoveOmit(object):
def __init__(self, buffer, omit_value):
try:
import yaml
except ImportError:
raise AnsibleError("Failed to import the required Python library (PyYAML).")
self.data = yaml.safe_load_all(buffer)
self.omit = omit_value
def remove_omit(self, data):
if isinstance(data, dict):
result = dict()
for key, value in iteritems(data):
if value == self.omit:
continue
result[key] = self.remove_omit(value)
return result
if isinstance(data, list):
return [self.remove_omit(v) for v in data if v != self.omit]
return data
def output(self):
return [self.remove_omit(d) for d in self.data]
def _from_yaml_to_definition(buffer):
try:
import yaml
except ImportError:
raise AnsibleError("Failed to import the required Python library (PyYAML).")
return list(yaml.safe_load_all(buffer))
ENV_KUBECONFIG_PATH_SEPARATOR = ";" if platform.system() == "Windows" else ":"
@@ -207,7 +195,6 @@ 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))
@@ -230,17 +217,18 @@ class ActionModule(ActionBase):
old_vars = self._templar.available_variables
default_environment = {}
for key in (
"newline_sequence",
"variable_start_string",
"variable_end_string",
"block_start_string",
"block_end_string",
"trim_blocks",
"lstrip_blocks",
):
if hasattr(self._templar.environment, key):
default_environment[key] = getattr(self._templar.environment, key)
if trust_as_template is None:
for key in (
"newline_sequence",
"variable_start_string",
"variable_end_string",
"block_start_string",
"block_end_string",
"trim_blocks",
"lstrip_blocks",
):
if hasattr(self._templar.environment, key):
default_environment[key] = getattr(self._templar.environment, key)
for template_item in template_params:
# We need to convert unescaped sequences to proper escaped sequences for Jinja2
newline_sequence = template_item["newline_sequence"]
@@ -257,26 +245,35 @@ class ActionModule(ActionBase):
with self.get_template_data(template_item["path"]) as template_data:
# add ansible 'template' vars
temp_vars = copy.deepcopy(task_vars)
overrides = {}
for key, value in iteritems(template_item):
if hasattr(self._templar.environment, key):
if value is not None:
setattr(self._templar.environment, key, value)
else:
overrides[key] = value
if trust_as_template is None:
setattr(self._templar.environment, key, value)
elif trust_as_template is None:
setattr(
self._templar.environment,
key,
default_environment.get(key),
)
self._templar.available_variables = temp_vars
result = self._templar.do_template(
template_data,
preserve_trailing_newlines=True,
escape_backslashes=False,
)
if omit_value is not None:
result_template.extend(RemoveOmit(result, omit_value).output())
if trust_as_template:
template_data = trust_as_template(template_data)
result = self._templar.template(
template_data,
preserve_trailing_newlines=True,
escape_backslashes=False,
overrides=overrides,
)
else:
result_template.append(result)
result = self._templar.do_template(
template_data,
preserve_trailing_newlines=True,
escape_backslashes=False,
)
result_template.extend(_from_yaml_to_definition(result))
self._templar.available_variables = old_vars
resource_definition = self._task.args.get("definition", None)
if not resource_definition:

View File

@@ -26,7 +26,8 @@
assert:
that:
- k8s_configmap is changed
- k8s_configmap.result.metadata.annotations|default(False)
- '"annotations" in k8s_configmap.result.metadata'
- k8s_configmap.result.metadata.annotations != {}
- name: Add same configmap again
k8s:
@@ -467,7 +468,7 @@
assert:
that:
- k8s_secret is changed
- k8s_secret.result.data.foo
- k8s_secret.result.data.foo != ""
- name: Add same secret
k8s:
@@ -748,7 +749,7 @@
assert:
that:
- _create is changed
- not _info.resources
- _info.resources | length == 0
# server side apply over kubernetes client releases
- name: Create temporary directory

View File

@@ -38,7 +38,7 @@
- name: Assert that there are pods
assert:
that:
- pods_create.resources
- pods_create.resources | length > 0
- name: Remove the daemonset
k8s:
@@ -74,7 +74,7 @@
- name: Assert that deleting the daemonset deleted the pods
assert:
that:
- not pods_delete.resources
- pods_delete.resources | length == 0
# test deletion using label selector
- name: Deploy load balancer

View File

@@ -244,7 +244,7 @@
kind: Pod
name: '{{ drain_pod_name }}'
register: _result
failed_when: _result.resources
failed_when: _result.resources | length > 0
- name: assert that emptyDir pod was deleted
k8s_info:
@@ -346,7 +346,7 @@
kind: Pod
name: '{{ drain_pod_name }}-01'
register: _result
failed_when: _result.resources
failed_when: _result.resources | length > 0
# test: drain using pod_selectors
- name: Uncordon node

View File

@@ -394,9 +394,11 @@
register: k8s_info_testing6
failed_when: not k8s_info_testing6.resources or k8s_info_testing6.resources[0].status.phase != "Active"
- name: Create large configmap data
command: dd if=/dev/urandom bs=500K count=1
register: cmap_data
- name: Create a file with specific size and attributes, to be used as swap space
community.general.filesize:
path: /tmp/configmap.bin
size: 500K
source: /dev/urandom
- name: Create configmap with large value
k8s:
@@ -407,7 +409,7 @@
name: testmap
namespace: testing
data:
testkey: "{{ cmap_data.stdout | b64encode }}"
testkey: "{{ lookup('file', '/tmp/configmap.bin') | b64encode }}"
wait: true
register: result
@@ -424,7 +426,7 @@
- assert:
that:
- result.resources[0].data.testkey == (cmap_data.stdout | b64encode)
- result.resources[0].data.testkey == (lookup('file', '/tmp/configmap.bin') | b64encode)
# test setting module defaults for kubernetes.core.k8s_info
- block:

View File

@@ -36,7 +36,7 @@
label_selectors:
- "job=gc"
register: wait_job
until: wait_job.resources
until: wait_job.resources | length > 0
retries: 5
delay: 10
@@ -87,7 +87,7 @@
label_selectors:
- "job=gc"
register: wait_job
until: wait_job.resources
until: wait_job.resources | length > 0
retries: 5
delay: 10
@@ -139,7 +139,7 @@
label_selectors:
- "job=gc"
register: wait_job
until: wait_job.resources
until: wait_job.resources | length > 0
retries: 5
delay: 10
@@ -225,7 +225,7 @@
- name: Assert job is deleted
assert:
that: not job.resources
that: job.resources | length == 0
always:
- name: Delete namespace

View File

@@ -47,7 +47,7 @@
- result.changed
- result.result.metadata.labels.label2 == "bar"
- result.result.spec.containers[0].image == "busybox:glibc"
- result.diff
- result.diff != {}
- name: Describe pod
kubernetes.core.k8s_info:

View File

@@ -129,7 +129,7 @@
that:
- scale_down is changed
- '"duration" in scale_down'
- scale_down.diff
- scale_down.diff != {}
- name: Scale the deployment once again (idempotency)
k8s_scale:
@@ -274,7 +274,7 @@
assert:
that:
- scale_down_no_wait is changed
- scale_down_no_wait.diff
- scale_down_no_wait.diff != {}
- scale_down_no_wait_pods.resources | length == 1
# scale multiple resource using label selectors

View File

@@ -421,7 +421,7 @@
- name: Assert that taints have been removed
assert:
that:
- _result.resources | selectattr('spec.taints', 'undefined')
- _result.resources | selectattr('spec.taints', 'undefined') | list | length > 0
always:

View File

@@ -7,7 +7,7 @@
kubernetes.core.k8s_service:
template: "pod_one.j2"
state: present
ignore_errors: yes
ignore_errors: true
register: r
- name: Check for expected failures in last tasks
@@ -35,7 +35,7 @@
k8s_pod_name_one: pod
k8s_pod_namespace: "{{ template_namespace }}"
register: r
ignore_errors: yes
ignore_errors: true
- name: Check if definition and template are mutually exclusive
assert:
@@ -52,7 +52,7 @@
k8s_pod_name_one: pod
k8s_pod_namespace: "{{ template_namespace }}"
register: r
ignore_errors: yes
ignore_errors: true
- name: Check if src and template are mutually exclusive
assert:
@@ -63,7 +63,7 @@
- name: Create pod using template (direct specification)
kubernetes.core.k8s:
template: "pod_one.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-1
@@ -79,7 +79,7 @@
kubernetes.core.k8s:
template:
- default
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-2
@@ -96,7 +96,7 @@
kubernetes.core.k8s:
template:
path: "pod_one.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-3
@@ -114,12 +114,11 @@
path: "pod_two.j2"
variable_start_string: '[['
variable_end_string: ']]'
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_two: pod-4
k8s_pod_namespace: "[[ template_namespace ]]"
ansible_python_interpreter: "[[ ansible_playbook_python ]]"
k8s_pod_namespace: "template-test"
register: r
- name: Assert that pod creation succeeded using template
@@ -131,7 +130,7 @@
kubernetes.core.k8s:
template:
path: "pod_three.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_three_one: pod-5
@@ -152,7 +151,7 @@
variable_start_string: '[['
variable_end_string: ']]'
- path: "pod_three.j2"
wait: yes
wait: true
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
vars:
k8s_pod_name_one: pod-7
@@ -239,63 +238,6 @@
- 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

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

View File

@@ -56,8 +56,8 @@
# Without wantlist=True lookup should return mapping
- test5 is mapping
- test6 is mapping
# errors='ignore'
- test7 is string
# errors='ignore' (return null with ansible-core 2.19)
- test7 is string or not test7
- test8 is not defined
- name: Create another namespace with label

View File

@@ -4,10 +4,17 @@
namespace_to_create: "{{ item.name | default(item) }}"
namespace_labels: "{{ item.labels | default(omit) }}"
with_items: "{{ test_namespace }}"
when: test_namespace | type_debug == "list"
when:
- test_namespace is not string
- test_namespace is not mapping
- test_namespace is iterable
- include_tasks: tasks/create.yml
vars:
namespace_to_create: "{{ test_namespace }}"
namespace_labels: "{{ test_namespace_labels | default(omit) }}"
when: test_namespace | type_debug == "AnsibleUnicode"
when:
- test_namespace is string
- test_namespace is iterable
- test_namespace is sequence
- test_namespace is not mapping

View File

@@ -1,105 +0,0 @@
# -*- 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")