Skip to content

Instantly share code, notes, and snippets.

@wojas
Last active April 2, 2020 09:12
Show Gist options
  • Save wojas/9628e56045ca31cee1c6ce4467aede73 to your computer and use it in GitHub Desktop.
Save wojas/9628e56045ca31cee1c6ce4467aede73 to your computer and use it in GitHub Desktop.
Tool to quickly select and view Kubernetes container logs using fzf (screenshot below in comments)
#!/usr/bin/env python3
"""
kubelog is a tool that lists all pods and their contains using fzf
and prints the logs for the selected one.
Options to get fzf:
- https://github.com/junegunn/fzf
- brew install fzf
"""
import os
import sys
import json
import argparse
import shlex
import shutil
from subprocess import Popen, PIPE, STDOUT
parser = argparse.ArgumentParser(usage=__doc__)
parser.add_argument("--list", "-l", action="store_true", help="List and exit")
parser.add_argument("--namespace", "-n", help="Specific namespace")
parser.add_argument("--preview", help=argparse.SUPPRESS)
def run():
# Unknown args are passed to kubectl logs
opt, other = parser.parse_known_args()
width, height = shutil.get_terminal_size()
preview_height = height // 2
# Hidden preview option, called by fzf
if opt.preview:
namespace, name, container, *rest = opt.preview.split()
os.execlp("kubectl", "kubectl", "logs", "-n", namespace, name,
"-c", container, "--tail={}".format(preview_height))
return # never reached
# Get JSON with all pod info
# FIXME: use subprocess, UNSAFE
if opt.namespace:
qn = shlex.quote(opt.namespace)
get_cmd = "kubectl get -n {} pod -o json".format(qn)
else:
get_cmd = "kubectl get --all-namespaces pod -o json"
pods_data = os.popen(get_cmd, "r").read()
pods = json.loads(pods_data)
# Find all container info
containers = []
def add_container(namespace, name, container, st):
state = list(st.keys())[0]
started = st[state].get('startedAt')
containers.append("{:20s} {:60s} {:30s} ({} {})".format(
namespace, name, container, state, started))
for pod in pods['items']:
m = pod['metadata']
for ck in ('containerStatuses', 'initContainerStatuses'):
for c in pod.get('status', {}).get(ck, []):
add_container(m['namespace'], m['name'], c['name'], c['state'])
containers.sort()
# For --list, list containers and exit (same if output is not a tty)
if opt.list or not sys.stdout.isatty():
for line in containers:
print(line)
return
# Show fzf to select a container, calling self for --preview
# This keeps a '{}' placeholder for fzf
preview_cmd = "{} --preview={{}}".format(shlex.quote(sys.argv[0]))
fzf_cmd = [
'fzf',
"--preview=" + preview_cmd,
'--preview-window=up:{}'.format(preview_height),
]
p = Popen(fzf_cmd, stdin=PIPE, stdout=PIPE)
stdin_bytes = '\n'.join(containers).encode('ascii')
selection = p.communicate(input=stdin_bytes)[0].decode('ascii')
if not selection:
print("No container selected")
sys.exit(1)
# Show logs for selected container
namespace, name, container, *rest = selection.split()
cmd = ["kubectl", "logs", "-n", namespace, name, "-c", container, *other]
print("=" * width)
cmd_str = ' '.join( shlex.quote(x) for x in cmd )
print("=== {}".format(cmd_str))
print("=" * width)
os.execlp(cmd[0], *cmd)
if __name__ == '__main__':
run()
@wojas
Copy link
Author

wojas commented Apr 2, 2020

Don't forget to make it executable (chmod a+x kubelog), or the preview function won't work.

kubelog

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment