diff --git a/changelogs/fragments/20250731-fix-k8s_cp-initcontainers.yaml b/changelogs/fragments/20250731-fix-k8s_cp-initcontainers.yaml new file mode 100644 index 00000000..b02d3153 --- /dev/null +++ b/changelogs/fragments/20250731-fix-k8s_cp-initcontainers.yaml @@ -0,0 +1,3 @@ +--- +bugfixes: + - Update the `k8s_cp` module to also work for init containers (https://github.com/ansible-collections/kubernetes.core/pull/971). diff --git a/plugins/module_utils/copy.py b/plugins/module_utils/copy.py index c70eed1a..0e19e17d 100644 --- a/plugins/module_utils/copy.py +++ b/plugins/module_utils/copy.py @@ -96,7 +96,7 @@ class K8SCopy(metaclass=ABCMeta): return error, stdout, stderr except Exception as e: self.module.fail_json( - msg="Error while running/parsing from pod {1}/{2} command='{0}' : {3}".format( + msg="Error while running/parsing from pod {0}/{1} command='{2}' : {3}".format( self.namespace, self.name, cmd, to_native(e) ) ) @@ -435,11 +435,21 @@ def check_pod(svc): try: result = svc.client.get(resource, name=name, namespace=namespace) - containers = [ - c["name"] for c in result.to_dict()["status"]["containerStatuses"] - ] - if container and container not in containers: + containers = dict( + { + c["name"]: c + for cl in ["initContainerStatuses", "containerStatuses"] + for c in result.to_dict()["status"].get(cl, []) + } + ) + if container and container not in containers.keys(): module.fail_json(msg="Pod has no container {0}".format(container)) - return containers + if ( + container + and container in containers + and not bool(containers[container].get("started", False)) + ): + module.fail_json(msg="Pod container {0} is not started".format(container)) + return containers.keys() except Exception as exc: _fail(exc) diff --git a/plugins/modules/k8s_cp.py b/plugins/modules/k8s_cp.py index 16f0f8cb..b18ca556 100644 --- a/plugins/modules/k8s_cp.py +++ b/plugins/modules/k8s_cp.py @@ -79,6 +79,7 @@ options: notes: - the tar binary is required on the container when copying from local filesystem to pod. + - the (init) container has to be started before you copy files or directories to it. """ EXAMPLES = r""" diff --git a/tests/integration/targets/k8s_copy/defaults/main.yml b/tests/integration/targets/k8s_copy/defaults/main.yml index aaf46330..7c82d25d 100644 --- a/tests/integration/targets/k8s_copy/defaults/main.yml +++ b/tests/integration/targets/k8s_copy/defaults/main.yml @@ -14,3 +14,9 @@ pod_with_two_container: pod_without_executable_find: name: openjdk-pod + +pod_with_initcontainer_and_container: + name: pod-copy-2 + container: + - container-20 + - container-21 diff --git a/tests/integration/targets/k8s_copy/tasks/main.yml b/tests/integration/targets/k8s_copy/tasks/main.yml index 519be8f6..62cced82 100644 --- a/tests/integration/targets/k8s_copy/tasks/main.yml +++ b/tests/integration/targets/k8s_copy/tasks/main.yml @@ -18,6 +18,23 @@ wait: yes template: pods_definition.j2 + - name: Create Init Pod + k8s: + namespace: '{{ copy_namespace }}' + template: pods_definition_init.j2 + + - kubernetes.core.k8s_info: + api_version: v1 + kind: Pod + name: '{{ pod_with_initcontainer_and_container.name }}' + namespace: '{{ copy_namespace }}' + register: init_pod_status + until: >- + init_pod_status.resources|length > 0 + and 'initContainerStatuses' in init_pod_status.resources.0.status + and init_pod_status.resources.0.status.initContainerStatuses|length > 0 + and init_pod_status.resources.0.status.initContainerStatuses.0.started|bool + - include_tasks: test_copy_errors.yml - include_tasks: test_check_mode.yml - include_tasks: test_copy_file.yml @@ -25,6 +42,7 @@ - include_tasks: test_copy_directory.yml - include_tasks: test_copy_large_file.yml - include_tasks: test_copy_item_with_space_in_its_name.yml + - include_tasks: test_init_container_pod.yml always: diff --git a/tests/integration/targets/k8s_copy/tasks/test_copy_errors.yml b/tests/integration/targets/k8s_copy/tasks/test_copy_errors.yml index b1e799bf..0064e8ac 100644 --- a/tests/integration/targets/k8s_copy/tasks/test_copy_errors.yml +++ b/tests/integration/targets/k8s_copy/tasks/test_copy_errors.yml @@ -67,3 +67,21 @@ that: - copy_fake_container is failed - copy_fake_container.msg == "Pod has no container this_is_a_fake_container" + +# copy file to not started container in pod should fail +- name: copy file to not started container in pod should fail + k8s_cp: + namespace: '{{ copy_namespace }}' + pod: '{{ pod_with_initcontainer_and_container.name }}' + remote_path: /tmp + local_path: files/simple_file.txt + state: to_pod + container: '{{ pod_with_initcontainer_and_container.container[1] }}' + ignore_errors: true + register: copy_not_started_container + +- name: check that error message is as expected + assert: + that: + - copy_not_started_container is failed + - copy_not_started_container.msg == "Pod container {{ pod_with_initcontainer_and_container.container[1] }} is not started" diff --git a/tests/integration/targets/k8s_copy/tasks/test_init_container_pod.yml b/tests/integration/targets/k8s_copy/tasks/test_init_container_pod.yml new file mode 100644 index 00000000..4b5ad66f --- /dev/null +++ b/tests/integration/targets/k8s_copy/tasks/test_init_container_pod.yml @@ -0,0 +1,25 @@ +--- +- set_fact: + random_content: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits,punctuation length=128') }}" + +- name: Copy content into init container + k8s_cp: + namespace: '{{ copy_namespace }}' + pod: '{{ pod_with_initcontainer_and_container.name }}' + remote_path: /file_from_localhost.txt + content: '{{ random_content }}' + container: '{{ pod_with_initcontainer_and_container.container[0] }}' + state: to_pod + +- name: Get the content from copied file + kubernetes.core.k8s_exec: + namespace: '{{ copy_namespace }}' + pod: '{{ pod_with_initcontainer_and_container.name }}' + container: '{{ pod_with_initcontainer_and_container.container[0] }}' + command: cat /file_from_localhost.txt + register: exec_out + +- name: check that content is found and the same as generated earlier + assert: + that: + - exec_out.stdout == random_content diff --git a/tests/integration/targets/k8s_copy/templates/pods_definition_init.j2 b/tests/integration/targets/k8s_copy/templates/pods_definition_init.j2 new file mode 100644 index 00000000..0b1524cf --- /dev/null +++ b/tests/integration/targets/k8s_copy/templates/pods_definition_init.j2 @@ -0,0 +1,20 @@ +--- +apiVersion: v1 +kind: Pod +metadata: + name: '{{ pod_with_initcontainer_and_container.name }}' +spec: + initContainers: + - name: '{{ pod_with_initcontainer_and_container.container[0] }}' + image: busybox + command: + - /bin/sh + - -c + - while true;do date;sleep 5; done + containers: + - name: '{{ pod_with_initcontainer_and_container.container[1] }}' + image: busybox + command: + - /bin/sh + - -c + - while true;do date;sleep 5; done