Added support for copying files to init Containers. (#971)

SUMMARY
Was going trough the list with issues and found 958; which seemed a quick fix.
What I fixed with with this PR:

Added support for copying files to init containers.
Fixed the format message when an exec is failing for a pod (the order was wrong).
Added a check if the container that you try to run copy for is started.

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME
copy.py module
ADDITIONAL INFORMATION
Some testing.
Verify that the pod does not exist:
kubectl -n default get pod/yorick
Output:
Error from server (NotFound): pods "yorick" not found

Run the playbook to create the file, create the deployment, wait for the init container to be ready, copy the created file to the init container, cat the copied file (using kubernetes.core.k8s_exec) that is now in the init container and try to copy the created file to the (not started) container (which fails - to see the new error message for it):
cat << EOF | ansible-playbook /dev/stdin
- hosts: localhost
  gather_facts: False
  tasks:

  - ansible.builtin.copy:
      content: |
        Hi there
      dest: /tmp/yorick.txt

  - name: Deploy pod with initContainer with an unlimited while loop
    kubernetes.core.k8s:
      kubeconfig: "~/.kube/config"
      definition:
        apiVersion: v1
        kind: Pod
        metadata:
          name: "yorick"
          namespace: "default"
        spec:
          initContainers:
            - name: "yorick-init"
              image: busybox:latest
              command: ["/bin/sh"]
              args:
                - "-c"
                - |
                  echo "Init container started, waiting for file..."
                  # Wait for the file to be copied
                  while :;do
                    echo "Waiting for file"
                    sleep 5
                  done
                  echo "File received! Init container completing..."
          containers:
            - name: "yorick-container"
              image: busybox:latest
              command: ["/bin/sh"]
              args:
                - "-c"
                - |
                  # Keep container running for testing
                  sleep 300

  - kubernetes.core.k8s_info:
      kubeconfig: "~/.kube/config"
      api_version: v1
      kind: Pod
      name: "yorick"
      namespace: "default"
    register: pod_status
    until: >-
      pod_status.resources|length > 0
      and 'initContainerStatuses' in pod_status.resources.0.status
      and pod_status.resources.0.status.initContainerStatuses|length > 0
      and pod_status.resources.0.status.initContainerStatuses.0.started|bool

  - name: Copy /tmp/yorick.txt to the yorick-init init container
    kubernetes.core.k8s_cp:
      kubeconfig: "~/.kube/config"
      namespace: default
      pod: yorick
      remote_path: /tmp/yorick.txt
      local_path: /tmp/yorick.txt
      container: yorick-init

  - name: Execute a command
    kubernetes.core.k8s_exec:
      kubeconfig: "~/.kube/config"
      namespace: default
      pod: yorick
      container: yorick-init
      command: cat /tmp/yorick.txt
    register: exec_out

  - ansible.builtin.debug:
      var: exec_out.stdout

  - name: Try to copy /tmp/yorick.txt to the yorick-container container
    kubernetes.core.k8s_cp:
      kubeconfig: "~/.kube/config"
      namespace: default
      pod: yorick
      remote_path: /tmp/yorick.txt
      local_path: /tmp/yorick.txt
      container: yorick-container
EOF
Output:
PLAY [localhost] ********************************************************************************************************************************************************************

TASK [ansible.builtin.copy] *********************************************************************************************************************************************************
Thursday 31 July 2025  02:01:21 +0200 (0:00:00.016)       0:00:00.016 *********
ok: [localhost]

TASK [Deploy pod with initContainer with an unlimited while loop] *******************************************************************************************************************
Thursday 31 July 2025  02:01:21 +0200 (0:00:00.788)       0:00:00.804 *********
changed: [localhost]

TASK [kubernetes.core.k8s_info] *****************************************************************************************************************************************************
Thursday 31 July 2025  02:01:25 +0200 (0:00:03.963)       0:00:04.768 *********
FAILED - RETRYING: [localhost]: kubernetes.core.k8s_info (3 retries left).
ok: [localhost]

TASK [Copy /tmp/yorick.txt to the yorick-init init container] ***********************************************************************************************************************
Thursday 31 July 2025  02:01:32 +0200 (0:00:06.598)       0:00:11.366 *********
changed: [localhost]

TASK [Execute a command] ************************************************************************************************************************************************************
Thursday 31 July 2025  02:01:39 +0200 (0:00:07.017)       0:00:18.383 *********
changed: [localhost]

TASK [ansible.builtin.debug] ********************************************************************************************************************************************************
Thursday 31 July 2025  02:01:40 +0200 (0:00:00.644)       0:00:19.028 *********
ok: [localhost] => {
    "exec_out.stdout": "Hi there\n"
}

TASK [Try to copy /tmp/yorick.txt to the yorick-container container] ****************************************************************************************************************
Thursday 31 July 2025  02:01:40 +0200 (0:00:00.021)       0:00:19.050 *********
fatal: [localhost]: FAILED! => {
    "changed": false
}

MSG:

Pod container yorick-container is not started

PLAY RECAP **************************************************************************************************************************************************************************
localhost                  : ok=6    changed=3    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

Playbook run took 0 days, 0 hours, 0 minutes, 21 seconds

Reviewed-by: spatterlight
Reviewed-by: Yorick Gruijthuijzen <yorick-1989@hotmail.com>
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
Reviewed-by: Bikouo Aubin
This commit is contained in:
Yorick Gruijthuijzen
2025-09-24 22:36:56 +02:00
committed by GitHub
parent da93cce1fa
commit 027700c3f4
8 changed files with 107 additions and 6 deletions

View File

@@ -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).

View File

@@ -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)

View File

@@ -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"""

View File

@@ -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

View File

@@ -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:

View File

@@ -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"

View File

@@ -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

View File

@@ -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