Skip to content

Kubernetes metadata enrichment #4173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
26b00a8
scl/python-modules: move python modules related includes to a separat…
bazsi Dec 3, 2022
bfa80bc
kubernetes: add python based metadata parser
alltilla Aug 27, 2022
4410cce
kubernetes: make kubernetes a Python module
bazsi Oct 24, 2022
30959d9
kubernetes: improve error reporting on missing kubernetes libraries
bazsi Oct 25, 2022
8019589
kubernetes: handle missing in_pod option
bazsi Oct 15, 2022
6ad6c5d
packaging/debian: include kubernetes package into our mod-python package
bazsi Oct 26, 2022
e6345bc
tests/copyright: update files on kubernetes module
bazsi Oct 26, 2022
8876bdb
news: added news file
bazsi Oct 26, 2022
ebe643c
dev-requirements: add pytest-mock
bazsi Nov 1, 2022
b91b64d
python-modules/kubernetes: assume that kubernetes libraries are avail…
bazsi Nov 1, 2022
4d62da5
python-modules/syslogng/message: improve the LogMessage fake class
bazsi Nov 1, 2022
6889382
python-modules/kubernetes: use Python native logging module
bazsi Nov 1, 2022
875d867
python-modules/kubernetes: handle KeyError when checking whether a ke…
bazsi Nov 1, 2022
9f64846
python-modules/kubernetes: add a few simple testcases for characteriz…
bazsi Nov 1, 2022
36b7721
python-modules/kubernetes: add pod annotations as metadata
bazsi Nov 2, 2022
986c7b8
python-modules/kubernetes: drop explicit in_pod option
bazsi Dec 3, 2022
ce5a9a8
python-modules/kubernetes: avoid using format strings in add_prefix()
bazsi Dec 3, 2022
54ced6b
python-modules/kubernetes: add "cached_" prefix to variable names in …
bazsi Dec 3, 2022
c67a8e7
python-modules/kubernetes: add error handling for invalid responses f…
bazsi Dec 3, 2022
ab0e197
python-modules/kubernetes: remove pod_name and namespace_name from ca…
bazsi Dec 3, 2022
284e45a
tests/copyright: add scl/python to copyright exceptions
bazsi Dec 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ astroid==1.6.1
logilab-common<=0.63.0
psutil
pytest
pytest-mock
pre-commit
2 changes: 2 additions & 0 deletions modules/python-modules/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ EXTRA_DIST += \
\
modules/python-modules/syslogng/modules/example/scl/example.conf \
modules/python-modules/syslogng/modules/example/__init__.py \
modules/python-modules/syslogng/modules/kubernetes/scl/kubernetes.conf \
modules/python-modules/syslogng/modules/kubernetes/__init__.py \
modules/python-modules/setup.py \
modules/python-modules/README.md

Expand Down
4 changes: 4 additions & 0 deletions modules/python-modules/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@
"syslogng",
"syslogng.debuggercli",
"syslogng.modules.example",
"syslogng.modules.kubernetes",
],
install_requires=[
"kubernetes"
])
24 changes: 22 additions & 2 deletions modules/python-modules/syslogng/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@
warnings.warn("You have imported the syslogng package outside of syslog-ng, thus some of the functionality is not available. Defining fake classes for those exported by the underlying syslog-ng code")

class LogMessage(dict):

# syslog-ng internally stores everything as a sequence of bytes that
# is strongly "preferred" to be an utf8 string. It takes a bytes
# and stores it verbatim. Strings are converted to utf8 (not the
# local character set!)

def __init__(self, msg):
super().__init__()
self['MESSAGE'] = msg
super().__init__([('MESSAGE', msg)])

def __getitem__(self, key):
if isinstance(key, str):
key = key.encode('utf8')
return super().__getitem__(key)

def __setitem__(self, key, value):
if isinstance(key, str):
key = key.encode('utf8')
if isinstance(value, str):
value = value.encode('utf8')
super().__setitem__(key, value)

def __eq__(self):
return False
93 changes: 93 additions & 0 deletions modules/python-modules/syslogng/modules/kubernetes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#############################################################################
# Copyright (c) 2022 Attila Szakacs <szakacs.attila96@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# As an additional exemption you are allowed to compile & link against the
# OpenSSL libraries as published by the OpenSSL project. See the file
# COPYING for details.
#
#############################################################################

from syslogng import Logger, LogParser
import kubernetes
import logging


class KubernetesAPIEnrichment(LogParser):
logger = logging.getLogger(__name__)

def init(self, options):
self.__metadata = {}
self.__prefix = options["prefix"]

kubernetes.config.load_config()
self.__client_api = kubernetes.client.CoreV1Api()

return True

def add_prefix(self, string):
return self.__prefix + string

def __get_cached_pod_metadata(self, namespace_name, pod_name):
self.logger.debug("Trying to find cached metadata for pod {}".format(pod_name))
return self.__metadata[namespace_name][pod_name]

def __query_pod_metadata_from_kubernetes_api_server(self, namespace_name, pod_name):
self.logger.debug("Trying to query metadata for pod {}/{} from the Kubernetes API".format(namespace_name, pod_name))

cached_namespace = self.__metadata.setdefault(namespace_name, {})
cached_pod_metadata = cached_namespace.setdefault(pod_name, {})

pod = self.__client_api.list_namespaced_pod(namespace_name, field_selector="metadata.name=={}".format(pod_name)).items[0]
try:
cached_pod_metadata[self.add_prefix("pod_uuid")] = pod.metadata.uid
except (AttributeError, IndexError, TypeError):
self.logger.warning("Error querying pod.metadata.uid for pod {}/{} from the Kubernetes API".format(namespace_name, pod_name))

for name, value in (pod.metadata.labels or {}).items():
cached_pod_metadata[self.add_prefix("labels.{}".format(name))] = value

for name, value in (pod.metadata.annotations or {}).items():
cached_pod_metadata[self.add_prefix("annotations.{}".format(name))] = value

try:
container_status = pod.status.container_statuses[0]
cached_pod_metadata[self.add_prefix("container_name")] = container_status.name
cached_pod_metadata[self.add_prefix("container_image")] = container_status.image
cached_pod_metadata[self.add_prefix("container_hash")] = container_status.image_id.replace("docker-pullable://", "", 1)
cached_pod_metadata[self.add_prefix("docker_id")] = container_status.container_id.replace("docker://", "", 1)
except (AttributeError, IndexError, TypeError):
self.logger.warning("Error querying container_status for pod {}/{} from the Kubernetes API".format(namespace_name, pod_name))
return cached_pod_metadata

def get_pod_metadata(self, namespace_name, pod_name):
try:
return self.__get_cached_pod_metadata(namespace_name, pod_name)
except KeyError:
return self.__query_pod_metadata_from_kubernetes_api_server(namespace_name, pod_name)

def parse(self, msg):
namespace_name = msg[self.add_prefix("namespace_name")].decode()
pod_name = msg[self.add_prefix("pod_name")].decode()

pod_metadata = self.get_pod_metadata(namespace_name, pod_name)
for name, value in pod_metadata.items():
try:
if not msg[name]:
msg[name] = value
except KeyError:
msg[name] = value

return True
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ block parser kubernetes-metadata-parser (prefix() template("$FILE_NAME")) {
template(`template`)
prefix(`prefix`)
);
python(
class("syslogng.modules.kubernetes.KubernetesAPIEnrichment")
options(
"prefix" => "`prefix`"
)
);
};

# make the container-id of 12 characters long as usual in cli
Expand Down
Loading