k8s_log - fix issue when required name is not provided, add all_containers support (#528)

k8s_log - fix issue when required name is not provided, add all_containers support

SUMMARY

Fixes issue when the required name is not provided, closes #514
all support for all_containers option

ISSUE TYPE


Bugfix Pull Request
Feature Pull Request

COMPONENT NAME

k8s_log
ADDITIONAL INFORMATION

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: Jill R <None>
This commit is contained in:
Bikouo Aubin
2022-10-25 01:43:26 +02:00
committed by GitHub
parent 1b66dbbd8b
commit b967b55a16
3 changed files with 142 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
---
bugfixes:
- k8s_log - fix exception raised when the name is not provided for resources requiring. (https://github.com/ansible-collections/kubernetes.core/issues/514)
minor_changes:
- k8s_log - add the ``all_containers`` for retrieving all containers' logs in the pod(s).

View File

@@ -51,7 +51,8 @@ options:
description:
- Use to specify the container within a pod to grab the log from.
- If there is only one container, this will default to that container.
- If there is more than one container, this option is required.
- If there is more than one container, this option is required or set I(all_containers) to C(true).
- mutually exclusive with C(all_containers).
required: no
type: str
since_seconds:
@@ -73,6 +74,12 @@ options:
required: no
type: int
version_added: '2.4.0'
all_containers:
description:
- If set to C(true), retrieve all containers' logs in the pod(s).
- mutually exclusive with C(container).
type: bool
version_added: '2.4.0'
requirements:
- "python >= 3.6"
@@ -114,6 +121,13 @@ EXAMPLES = r"""
name: example
tail_lines: 100
register: log
# This will get the logs from all containers in Pod
- name: Get the logs from all containers in pod
kubernetes.core.k8s_log:
namespace: testing
name: some-pod
all_containers: true
"""
RETURN = r"""
@@ -131,6 +145,7 @@ log_lines:
import copy
import json
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
@@ -170,11 +185,35 @@ def argspec():
label_selectors=dict(type="list", elements="str", default=[]),
previous=dict(type="bool", default=False),
tail_lines=dict(type="int"),
all_containers=dict(type="bool"),
)
)
return args
def get_exception_message(exc):
try:
d = json.loads(exc.body.decode("utf8"))
return d["message"]
except Exception:
return exc
def list_containers_in_pod(svc, resource, namespace, name):
try:
result = svc.client.get(resource, name=name, namespace=namespace)
containers = [
c["name"] for c in result.to_dict()["status"]["containerStatuses"]
]
return containers
except Exception as exc:
raise CoreException(
"Unable to retrieve log from Pod due to: {0}".format(
get_exception_message(exc)
)
)
def execute_module(svc, params):
name = params.get("name")
namespace = params.get("namespace")
@@ -206,6 +245,11 @@ def execute_module(svc, params):
name = instances.items[0].metadata.name
resource = v1_pods
if "base" not in resource.log.urls and not name:
raise CoreException(
"name must be provided for resources that do not support namespaced base url"
)
kwargs = {}
if params.get("container"):
kwargs["query_params"] = {"container": params["container"]}
@@ -223,19 +267,28 @@ def execute_module(svc, params):
{"tailLines": params["tail_lines"]}
)
pod_containers = [None]
if params.get("all_containers"):
pod_containers = list_containers_in_pod(svc, resource, namespace, name)
log = ""
try:
response = resource.log.get(
name=name, namespace=namespace, serialize=False, **kwargs
)
for container in pod_containers:
if container is not None:
kwargs.setdefault("query_params", {}).update({"container": container})
response = resource.log.get(
name=name, namespace=namespace, serialize=False, **kwargs
)
log += response.data.decode("utf8")
except ApiException as exc:
if exc.reason == "Not Found":
raise CoreException("Pod {0}/{1} not found.".format(namespace, name))
raise CoreException(
"Unable to retrieve log from Pod due to: {0}".format(exc.reason)
"Unable to retrieve log from Pod due to: {0}".format(
get_exception_message(exc)
)
)
log = response.data.decode("utf8")
return {"changed": False, "log": log, "log_lines": log.split("\n")}
@@ -290,7 +343,10 @@ def extract_selectors(instance):
def main():
module = AnsibleK8SModule(
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
module_class=AnsibleModule,
argument_spec=argspec(),
supports_check_mode=True,
mutually_exclusive=[("container", "all_containers")],
)
try:

View File

@@ -165,6 +165,79 @@
assert:
that: tailed_log.log_lines | length == 5 + 1
# Trying to call module without name and label_selectors
- name: Retrieve without neither name nor label_selectors provided
k8s_log:
namespace: "{{ test_namespace }}"
register: noname_log
ignore_errors: true
- name: Ensure task failed
assert:
that:
- noname_log is failed
- 'noname_log.msg == "name must be provided for resources that do not support namespaced base url"'
# Test retrieve all containers logs
- name: Create deployments
k8s:
namespace: "{{ test_namespace }}"
wait: yes
wait_timeout: "{{ k8s_wait_timeout | default(omit) }}"
wait_condition:
type: Complete
status: 'True'
definition:
apiVersion: batch/v1
kind: Job
metadata:
name: multicontainer-log
spec:
template:
spec:
containers:
- name: p01
image: busybox
command: ['sh']
args: ['-c', 'for i in $(seq 0 9); do echo $i; done']
- name: p02
image: busybox
command: ['sh']
args: ['-c', 'for i in $(seq 10 19); do echo $i; done']
restartPolicy: Never
- name: Retrieve logs from all containers
k8s_log:
api_version: batch/v1
kind: Job
namespace: "{{ test_namespace }}"
name: multicontainer-log
all_containers: true
register: all_logs
- name: Retrieve logs from first job
k8s_log:
api_version: batch/v1
kind: Job
namespace: "{{ test_namespace }}"
name: multicontainer-log
container: p01
register: log_1
- name: Retrieve logs from second job
k8s_log:
api_version: batch/v1
kind: Job
namespace: "{{ test_namespace }}"
name: multicontainer-log
container: p02
register: log_2
- name: Validate that log using all_containers=true is the sum of all logs
assert:
that:
- all_logs.log == (log_1.log + log_2.log)
always:
- name: ensure that namespace is removed
k8s: