mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-07-28 10:24:45 +00:00
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:
@@ -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).
|
||||||
@@ -51,7 +51,8 @@ options:
|
|||||||
description:
|
description:
|
||||||
- Use to specify the container within a pod to grab the log from.
|
- 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 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
|
required: no
|
||||||
type: str
|
type: str
|
||||||
since_seconds:
|
since_seconds:
|
||||||
@@ -73,6 +74,12 @@ options:
|
|||||||
required: no
|
required: no
|
||||||
type: int
|
type: int
|
||||||
version_added: '2.4.0'
|
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:
|
requirements:
|
||||||
- "python >= 3.6"
|
- "python >= 3.6"
|
||||||
@@ -114,6 +121,13 @@ EXAMPLES = r"""
|
|||||||
name: example
|
name: example
|
||||||
tail_lines: 100
|
tail_lines: 100
|
||||||
register: log
|
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"""
|
RETURN = r"""
|
||||||
@@ -131,6 +145,7 @@ log_lines:
|
|||||||
|
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import json
|
||||||
|
|
||||||
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
||||||
AnsibleModule,
|
AnsibleModule,
|
||||||
@@ -170,11 +185,35 @@ def argspec():
|
|||||||
label_selectors=dict(type="list", elements="str", default=[]),
|
label_selectors=dict(type="list", elements="str", default=[]),
|
||||||
previous=dict(type="bool", default=False),
|
previous=dict(type="bool", default=False),
|
||||||
tail_lines=dict(type="int"),
|
tail_lines=dict(type="int"),
|
||||||
|
all_containers=dict(type="bool"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return args
|
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):
|
def execute_module(svc, params):
|
||||||
name = params.get("name")
|
name = params.get("name")
|
||||||
namespace = params.get("namespace")
|
namespace = params.get("namespace")
|
||||||
@@ -206,6 +245,11 @@ def execute_module(svc, params):
|
|||||||
name = instances.items[0].metadata.name
|
name = instances.items[0].metadata.name
|
||||||
resource = v1_pods
|
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 = {}
|
kwargs = {}
|
||||||
if params.get("container"):
|
if params.get("container"):
|
||||||
kwargs["query_params"] = {"container": params["container"]}
|
kwargs["query_params"] = {"container": params["container"]}
|
||||||
@@ -223,19 +267,28 @@ def execute_module(svc, params):
|
|||||||
{"tailLines": params["tail_lines"]}
|
{"tailLines": params["tail_lines"]}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
pod_containers = [None]
|
||||||
|
if params.get("all_containers"):
|
||||||
|
pod_containers = list_containers_in_pod(svc, resource, namespace, name)
|
||||||
|
|
||||||
|
log = ""
|
||||||
try:
|
try:
|
||||||
response = resource.log.get(
|
for container in pod_containers:
|
||||||
name=name, namespace=namespace, serialize=False, **kwargs
|
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:
|
except ApiException as exc:
|
||||||
if exc.reason == "Not Found":
|
if exc.reason == "Not Found":
|
||||||
raise CoreException("Pod {0}/{1} not found.".format(namespace, name))
|
raise CoreException("Pod {0}/{1} not found.".format(namespace, name))
|
||||||
raise CoreException(
|
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")}
|
return {"changed": False, "log": log, "log_lines": log.split("\n")}
|
||||||
|
|
||||||
|
|
||||||
@@ -290,7 +343,10 @@ def extract_selectors(instance):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = AnsibleK8SModule(
|
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:
|
try:
|
||||||
|
|||||||
@@ -165,6 +165,79 @@
|
|||||||
assert:
|
assert:
|
||||||
that: tailed_log.log_lines | length == 5 + 1
|
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:
|
always:
|
||||||
- name: ensure that namespace is removed
|
- name: ensure that namespace is removed
|
||||||
k8s:
|
k8s:
|
||||||
|
|||||||
Reference in New Issue
Block a user