logs
This commit is contained in:
parent
5238ef5f6b
commit
256e62377e
1 changed files with 268 additions and 144 deletions
288
krayt.py
288
krayt.py
|
|
@ -16,12 +16,11 @@ Hunt down storage issues and explore your persistent data like a true Tatooine d
|
||||||
May the Force be with your volumes!
|
May the Force be with your volumes!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
from pathlib import Path
|
|
||||||
from iterfzf import iterfzf
|
from iterfzf import iterfzf
|
||||||
from kubernetes import client, config
|
from kubernetes import client, config
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
import time
|
import time
|
||||||
import typer
|
import typer
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
@ -101,47 +100,57 @@ def fuzzy_select(items):
|
||||||
if not items:
|
if not items:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
# Format items as "namespace/name" for display
|
# If there's only one item, return it without prompting
|
||||||
formatted_items = [f"{ns}/{name}" for name, ns in items]
|
if len(items) == 1:
|
||||||
logging.debug(f"Found {len(formatted_items)} pods")
|
return items[0]
|
||||||
|
|
||||||
|
# Format items for display
|
||||||
|
formatted_items = [f"{name} ({namespace})" for name, namespace in items]
|
||||||
|
|
||||||
|
# Use fzf for selection
|
||||||
try:
|
try:
|
||||||
# Use iterfzf for selection
|
|
||||||
selected = iterfzf(formatted_items)
|
selected = iterfzf(formatted_items)
|
||||||
|
if not selected:
|
||||||
if selected:
|
|
||||||
namespace, name = selected.split("/")
|
|
||||||
logging.debug(f"Selected pod {name} in namespace {namespace}")
|
|
||||||
return name, namespace
|
|
||||||
else:
|
|
||||||
logging.debug("No selection made")
|
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
# Parse selection back into name and namespace
|
||||||
|
# Example: "pod-name (namespace)" -> ("pod-name", "namespace")
|
||||||
|
name = selected.split(" (")[0]
|
||||||
|
namespace = selected.split(" (")[1][:-1]
|
||||||
|
return name, namespace
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error during selection: {e}", exc_info=True)
|
typer.echo(f"Error during selection: {e}")
|
||||||
typer.echo(f"Error during selection: {e}", err=True)
|
return None, None
|
||||||
raise typer.Exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def get_pods(namespace=None):
|
def get_pods(
|
||||||
|
namespace=None,
|
||||||
|
label_selector: str = "app=krayt",
|
||||||
|
):
|
||||||
"""Get list of pods in the specified namespace or all namespaces"""
|
"""Get list of pods in the specified namespace or all namespaces"""
|
||||||
config.load_kube_config()
|
|
||||||
v1 = client.CoreV1Api()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
config.load_kube_config()
|
||||||
|
api = client.CoreV1Api()
|
||||||
if namespace:
|
if namespace:
|
||||||
logging.debug(f"Listing pods in namespace {namespace}")
|
pods = api.list_namespaced_pod(
|
||||||
pod_list = v1.list_namespaced_pod(namespace=namespace)
|
namespace=namespace,
|
||||||
|
label_selector=label_selector,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logging.debug("Listing pods in all namespaces")
|
pods = api.list_pod_for_all_namespaces(
|
||||||
pod_list = v1.list_pod_for_all_namespaces()
|
label_selector=label_selector,
|
||||||
|
)
|
||||||
|
|
||||||
pods = [(pod.metadata.name, pod.metadata.namespace) for pod in pod_list.items]
|
# Convert to list of (name, namespace) tuples
|
||||||
logging.debug(f"Found {len(pods)} pods")
|
pod_list = []
|
||||||
return pods
|
for pod in pods.items:
|
||||||
except client.exceptions.ApiException as e:
|
if pod.metadata.namespace not in PROTECTED_NAMESPACES:
|
||||||
logging.error(f"Error listing pods: {e}")
|
pod_list.append((pod.metadata.name, pod.metadata.namespace))
|
||||||
typer.echo(f"Error listing pods: {e}", err=True)
|
return pod_list
|
||||||
|
|
||||||
|
except client.rest.ApiException as e:
|
||||||
|
typer.echo(f"Error listing pods: {e}")
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -206,8 +215,14 @@ def get_env_vars_and_secret_volumes(api, namespace: str):
|
||||||
volumes = []
|
volumes = []
|
||||||
|
|
||||||
# Add proxy environment variables if they exist in the host environment
|
# Add proxy environment variables if they exist in the host environment
|
||||||
proxy_vars = ["HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
|
proxy_vars = [
|
||||||
"http_proxy", "https_proxy", "no_proxy"]
|
"HTTP_PROXY",
|
||||||
|
"HTTPS_PROXY",
|
||||||
|
"NO_PROXY",
|
||||||
|
"http_proxy",
|
||||||
|
"https_proxy",
|
||||||
|
"no_proxy",
|
||||||
|
]
|
||||||
|
|
||||||
for var in proxy_vars:
|
for var in proxy_vars:
|
||||||
if var in os.environ:
|
if var in os.environ:
|
||||||
|
|
@ -218,9 +233,8 @@ def get_env_vars_and_secret_volumes(api, namespace: str):
|
||||||
secrets = api.list_namespaced_secret(namespace)
|
secrets = api.list_namespaced_secret(namespace)
|
||||||
for secret in secrets.items:
|
for secret in secrets.items:
|
||||||
# Skip service account tokens and other system secrets
|
# Skip service account tokens and other system secrets
|
||||||
if (
|
if secret.type != "Opaque" or secret.metadata.name.startswith(
|
||||||
secret.type != "Opaque"
|
"default-token-"
|
||||||
or secret.metadata.name.startswith("default-token-")
|
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -259,15 +273,15 @@ def get_init_scripts():
|
||||||
|
|
||||||
for script in scripts:
|
for script in scripts:
|
||||||
try:
|
try:
|
||||||
with open(script, 'r') as f:
|
with open(script, "r") as f:
|
||||||
script_content = f.read()
|
script_content = f.read()
|
||||||
if script_content:
|
if script_content:
|
||||||
init_script += f"echo '=== Running {script.name} ==='\n"
|
init_script += f"echo '=== Running {script.name} ==='\n"
|
||||||
# Write each script to a separate file
|
# Write each script to a separate file
|
||||||
init_script += f"cat > /tmp/{script.name} << 'EOFSCRIPT'\n"
|
init_script += f"cat > /tmp/{script.name} << 'EOFSCRIPT'\n"
|
||||||
init_script += script_content
|
init_script += script_content
|
||||||
if not script_content.endswith('\n'):
|
if not script_content.endswith("\n"):
|
||||||
init_script += '\n'
|
init_script += "\n"
|
||||||
init_script += "EOFSCRIPT\n\n"
|
init_script += "EOFSCRIPT\n\n"
|
||||||
# Make it executable and run it
|
# Make it executable and run it
|
||||||
init_script += f"chmod +x /tmp/{script.name}\n"
|
init_script += f"chmod +x /tmp/{script.name}\n"
|
||||||
|
|
@ -284,7 +298,7 @@ def get_motd_script(mount_info, pvc_info):
|
||||||
"""Generate the MOTD script with proper escaping"""
|
"""Generate the MOTD script with proper escaping"""
|
||||||
return f"""
|
return f"""
|
||||||
# Create MOTD
|
# Create MOTD
|
||||||
cat << 'EOF' > /etc/motd
|
cat << EOF > /etc/motd
|
||||||
====================================
|
====================================
|
||||||
Krayt Dragon's Lair
|
Krayt Dragon's Lair
|
||||||
A safe haven for volume inspection
|
A safe haven for volume inspection
|
||||||
|
|
@ -293,21 +307,27 @@ A safe haven for volume inspection
|
||||||
"Inside every volume lies a pearl of wisdom waiting to be discovered."
|
"Inside every volume lies a pearl of wisdom waiting to be discovered."
|
||||||
|
|
||||||
Mounted Volumes:
|
Mounted Volumes:
|
||||||
$(echo "{','.join(mount_info)}" | tr ',' '\\n' | sed 's/^/- /')
|
$(echo "{",".join(mount_info)}" | tr ',' '\\n' | sed 's/^/- /')
|
||||||
|
|
||||||
Persistent Volume Claims:
|
Persistent Volume Claims:
|
||||||
$(echo "{','.join(pvc_info)}" | tr ',' '\\n' | sed 's/^/- /')
|
$(echo "{",".join(pvc_info)}" | tr ',' '\\n' | sed 's/^/- /')
|
||||||
|
|
||||||
Mounted Secrets:
|
Mounted Secrets:
|
||||||
$(for d in /mnt/secrets/*; do if [ -d "$d" ]; then echo "- $(basename $d)"; fi; done)
|
$(for d in /mnt/secrets/*; do if [ -d "$d" ]; then echo "- $(basename $d)"; fi; done)
|
||||||
|
|
||||||
Init Script Status:
|
Init Script Status:
|
||||||
$(if [ -f /tmp/init.log ]; then echo "View initialization log at /tmp/init.log"; fi)
|
$(if [ -f /tmp/init.log ]; then echo "View initialization log at /tmp/init.log"; fi)
|
||||||
EOF"""
|
EOF
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def create_inspector_job(
|
def create_inspector_job(
|
||||||
api, namespace: str, pod_name: str, volume_mounts: list, volumes: list, image: str = "alpine:latest"
|
api,
|
||||||
|
namespace: str,
|
||||||
|
pod_name: str,
|
||||||
|
volume_mounts: list,
|
||||||
|
volumes: list,
|
||||||
|
image: str = "alpine:latest",
|
||||||
):
|
):
|
||||||
"""Create a Krayt inspector job with the given mounts"""
|
"""Create a Krayt inspector job with the given mounts"""
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
|
|
@ -351,7 +371,8 @@ def create_inspector_job(
|
||||||
command_parts = []
|
command_parts = []
|
||||||
|
|
||||||
# Configure apk proxy settings BEFORE any package installation
|
# Configure apk proxy settings BEFORE any package installation
|
||||||
command_parts.extend([
|
command_parts.extend(
|
||||||
|
[
|
||||||
"# Configure apk proxy settings",
|
"# Configure apk proxy settings",
|
||||||
"mkdir -p /etc/apk",
|
"mkdir -p /etc/apk",
|
||||||
"cat > /etc/apk/repositories << 'EOF'",
|
"cat > /etc/apk/repositories << 'EOF'",
|
||||||
|
|
@ -359,8 +380,8 @@ def create_inspector_job(
|
||||||
"https://dl-cdn.alpinelinux.org/alpine/latest-stable/community",
|
"https://dl-cdn.alpinelinux.org/alpine/latest-stable/community",
|
||||||
"EOF",
|
"EOF",
|
||||||
"",
|
"",
|
||||||
"if [ ! -z \"$HTTP_PROXY\" ]; then",
|
'if [ ! -z "$HTTP_PROXY" ]; then',
|
||||||
" echo \"Setting up apk proxy configuration...\"",
|
' echo "Setting up apk proxy configuration..."',
|
||||||
" mkdir -p /etc/apk/",
|
" mkdir -p /etc/apk/",
|
||||||
" cat > /etc/apk/repositories << EOF",
|
" cat > /etc/apk/repositories << EOF",
|
||||||
"#/media/cdrom/apks",
|
"#/media/cdrom/apks",
|
||||||
|
|
@ -371,12 +392,59 @@ def create_inspector_job(
|
||||||
"proxy=$HTTP_PROXY",
|
"proxy=$HTTP_PROXY",
|
||||||
"EOF",
|
"EOF",
|
||||||
"fi",
|
"fi",
|
||||||
""
|
"",
|
||||||
])
|
"# Install basic tools first",
|
||||||
|
"apk update",
|
||||||
|
"apk add curl",
|
||||||
|
"",
|
||||||
|
"# Install uv CLI",
|
||||||
|
"echo 'Installing uv CLI...'",
|
||||||
|
"curl -LsSf https://astral.sh/uv/install.sh | sh",
|
||||||
|
"echo 'uv version:'",
|
||||||
|
"uv --version",
|
||||||
|
"",
|
||||||
|
"echo 'Installing starship...'",
|
||||||
|
"curl -sS https://starship.rs/install.sh | sh -s -- -y",
|
||||||
|
"echo 'starship version:'",
|
||||||
|
"starship --version",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"# Install additional tools",
|
||||||
|
"apk add "
|
||||||
|
+ " ".join(
|
||||||
|
[
|
||||||
|
"ripgrep",
|
||||||
|
"exa",
|
||||||
|
"ncdu",
|
||||||
|
"dust",
|
||||||
|
"file",
|
||||||
|
"hexyl",
|
||||||
|
"jq",
|
||||||
|
"yq",
|
||||||
|
"bat",
|
||||||
|
"fd",
|
||||||
|
"fzf",
|
||||||
|
"htop",
|
||||||
|
"bottom",
|
||||||
|
"difftastic",
|
||||||
|
"mtr",
|
||||||
|
"bind-tools",
|
||||||
|
"aws-cli",
|
||||||
|
"sqlite",
|
||||||
|
"sqlite-dev",
|
||||||
|
"sqlite-libs",
|
||||||
|
"bash",
|
||||||
|
"neovim",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# Add init scripts if present
|
# Add init scripts if present
|
||||||
if init_scripts:
|
if init_scripts:
|
||||||
command_parts.extend([
|
command_parts.extend(
|
||||||
|
[
|
||||||
"# Write and run init scripts",
|
"# Write and run init scripts",
|
||||||
"mkdir -p /tmp/init.d",
|
"mkdir -p /tmp/init.d",
|
||||||
"cat > /tmp/init.sh << 'EOFSCRIPT'",
|
"cat > /tmp/init.sh << 'EOFSCRIPT'",
|
||||||
|
|
@ -387,27 +455,30 @@ def create_inspector_job(
|
||||||
"chmod +x /tmp/init.sh",
|
"chmod +x /tmp/init.sh",
|
||||||
"/tmp/init.sh 2>&1 | tee /tmp/init.log",
|
"/tmp/init.sh 2>&1 | tee /tmp/init.log",
|
||||||
"echo 'Init script log available at /tmp/init.log'",
|
"echo 'Init script log available at /tmp/init.log'",
|
||||||
""
|
"",
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# Add base installation commands AFTER proxy configuration
|
# Add shell setup and MOTD
|
||||||
command_parts.extend([
|
command_parts.extend(
|
||||||
"# Install basic tools first",
|
[
|
||||||
"apk update",
|
|
||||||
"apk add curl",
|
|
||||||
"",
|
|
||||||
"# Install additional tools",
|
|
||||||
"apk add ripgrep exa ncdu dust file hexyl jq yq bat fd fzf htop bottom difftastic mtr bind-tools aws-cli sqlite sqlite-dev sqlite-libs",
|
|
||||||
"",
|
|
||||||
"# Create .ashrc with MOTD",
|
"# Create .ashrc with MOTD",
|
||||||
"cat > /root/.ashrc << 'EOF'",
|
"cat > /root/.ashrc << 'EOF'",
|
||||||
"# Display MOTD on login",
|
"# Display MOTD on login",
|
||||||
"[ -f /etc/motd ] && cat /etc/motd",
|
"[ -f /etc/motd ] && cat /etc/motd",
|
||||||
"EOF",
|
|
||||||
"",
|
|
||||||
"# Set up shell environment",
|
"# Set up shell environment",
|
||||||
"export EDITOR=vi",
|
"export EDITOR=vi",
|
||||||
"export PAGER=less",
|
"export PAGER=less",
|
||||||
|
"# Set up aliases",
|
||||||
|
"alias ll='ls -la'",
|
||||||
|
"alias l='ls -la'",
|
||||||
|
"alias la='ls -la'",
|
||||||
|
"alias vi='vim'",
|
||||||
|
"# Set up PATH",
|
||||||
|
"export PATH=/root/.local/bin:$PATH",
|
||||||
|
'eval "$(starship init bash)"',
|
||||||
|
"EOF",
|
||||||
|
"",
|
||||||
"",
|
"",
|
||||||
"# Set up environment to always source our RC file",
|
"# Set up environment to always source our RC file",
|
||||||
"echo 'export ENV=/root/.ashrc' > /etc/profile",
|
"echo 'export ENV=/root/.ashrc' > /etc/profile",
|
||||||
|
|
@ -425,8 +496,9 @@ def create_inspector_job(
|
||||||
get_motd_script(mount_info, pvc_info),
|
get_motd_script(mount_info, pvc_info),
|
||||||
"",
|
"",
|
||||||
"# Keep container running",
|
"# Keep container running",
|
||||||
"tail -f /dev/null"
|
"tail -f /dev/null",
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
inspector_job = {
|
inspector_job = {
|
||||||
"apiVersion": "batch/v1",
|
"apiVersion": "batch/v1",
|
||||||
|
|
@ -435,25 +507,17 @@ def create_inspector_job(
|
||||||
"name": job_name,
|
"name": job_name,
|
||||||
"namespace": namespace,
|
"namespace": namespace,
|
||||||
"labels": {"app": "krayt"},
|
"labels": {"app": "krayt"},
|
||||||
"annotations": {
|
"annotations": {"pvcs": ",".join(pvc_info) if pvc_info else "none"},
|
||||||
"pvcs": ",".join(pvc_info) if pvc_info else "none"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"template": {
|
"template": {
|
||||||
"metadata": {
|
"metadata": {"labels": {"app": "krayt"}},
|
||||||
"labels": {"app": "krayt"}
|
|
||||||
},
|
|
||||||
"spec": {
|
"spec": {
|
||||||
"containers": [
|
"containers": [
|
||||||
{
|
{
|
||||||
"name": "inspector",
|
"name": "inspector",
|
||||||
"image": image,
|
"image": image,
|
||||||
"command": [
|
"command": ["sh", "-c", "\n".join(command_parts)],
|
||||||
"sh",
|
|
||||||
"-c",
|
|
||||||
"\n".join(command_parts)
|
|
||||||
],
|
|
||||||
"env": env_vars,
|
"env": env_vars,
|
||||||
"volumeMounts": formatted_mounts,
|
"volumeMounts": formatted_mounts,
|
||||||
}
|
}
|
||||||
|
|
@ -495,7 +559,7 @@ def load_init_scripts():
|
||||||
|
|
||||||
for script in scripts:
|
for script in scripts:
|
||||||
try:
|
try:
|
||||||
with open(script, 'r') as f:
|
with open(script, "r") as f:
|
||||||
exec(f.read(), globals())
|
exec(f.read(), globals())
|
||||||
logging.debug(f"Loaded init script: {script}")
|
logging.debug(f"Loaded init script: {script}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -505,8 +569,14 @@ def load_init_scripts():
|
||||||
def setup_environment():
|
def setup_environment():
|
||||||
"""Set up the environment with proxy settings and other configurations"""
|
"""Set up the environment with proxy settings and other configurations"""
|
||||||
# Load environment variables for proxies
|
# Load environment variables for proxies
|
||||||
proxy_vars = ["HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
|
proxy_vars = [
|
||||||
"http_proxy", "https_proxy", "no_proxy"]
|
"HTTP_PROXY",
|
||||||
|
"HTTPS_PROXY",
|
||||||
|
"NO_PROXY",
|
||||||
|
"http_proxy",
|
||||||
|
"https_proxy",
|
||||||
|
"no_proxy",
|
||||||
|
]
|
||||||
|
|
||||||
for var in proxy_vars:
|
for var in proxy_vars:
|
||||||
if var in os.environ:
|
if var in os.environ:
|
||||||
|
|
@ -596,7 +666,7 @@ def exec(
|
||||||
"--",
|
"--",
|
||||||
"/bin/sh",
|
"/bin/sh",
|
||||||
"-c",
|
"-c",
|
||||||
"cat /etc/motd; exec /bin/ash -l"
|
"cat /etc/motd; exec /bin/ash -l",
|
||||||
]
|
]
|
||||||
|
|
||||||
os.execvp("kubectl", exec_command)
|
os.execvp("kubectl", exec_command)
|
||||||
|
|
@ -706,7 +776,8 @@ def create(
|
||||||
If namespace is not specified, will search for pods across all namespaces.
|
If namespace is not specified, will search for pods across all namespaces.
|
||||||
The inspector will be created in the same namespace as the selected pod.
|
The inspector will be created in the same namespace as the selected pod.
|
||||||
"""
|
"""
|
||||||
pods = get_pods(namespace)
|
# For create, we want to list all pods, not just Krayt pods
|
||||||
|
pods = get_pods(namespace, label_selector=None)
|
||||||
if not pods:
|
if not pods:
|
||||||
typer.echo("No pods found.")
|
typer.echo("No pods found.")
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
@ -720,7 +791,12 @@ def create(
|
||||||
volume_mounts, volumes = get_pod_volumes_and_mounts(pod_spec)
|
volume_mounts, volumes = get_pod_volumes_and_mounts(pod_spec)
|
||||||
|
|
||||||
inspector_job = create_inspector_job(
|
inspector_job = create_inspector_job(
|
||||||
client.CoreV1Api(), selected_namespace, selected_pod, volume_mounts, volumes, image=image
|
client.CoreV1Api(),
|
||||||
|
selected_namespace,
|
||||||
|
selected_pod,
|
||||||
|
volume_mounts,
|
||||||
|
volumes,
|
||||||
|
image=image,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Output the job manifest
|
# Output the job manifest
|
||||||
|
|
@ -733,6 +809,54 @@ def version():
|
||||||
typer.echo(f"Version: {KRAYT_VERSION}")
|
typer.echo(f"Version: {KRAYT_VERSION}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def logs(
|
||||||
|
namespace: Optional[str] = typer.Option(
|
||||||
|
None,
|
||||||
|
help="Kubernetes namespace. If not specified, will search for inspectors across all namespaces.",
|
||||||
|
),
|
||||||
|
follow: bool = typer.Option(
|
||||||
|
False,
|
||||||
|
"--follow",
|
||||||
|
"-f",
|
||||||
|
help="Follow the logs in real-time",
|
||||||
|
),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
View logs from a Krayt inspector pod.
|
||||||
|
If multiple inspectors are found, you'll get to choose which one to explore.
|
||||||
|
"""
|
||||||
|
pods = get_pods(namespace)
|
||||||
|
if not pods:
|
||||||
|
typer.echo("No pods found.")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
selected_pod, selected_namespace = fuzzy_select(pods)
|
||||||
|
if not selected_pod:
|
||||||
|
typer.echo("No pod selected.")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
config.load_kube_config()
|
||||||
|
api = client.CoreV1Api()
|
||||||
|
logs = api.read_namespaced_pod_log(
|
||||||
|
name=selected_pod,
|
||||||
|
namespace=selected_namespace,
|
||||||
|
follow=follow,
|
||||||
|
_preload_content=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if follow:
|
||||||
|
for line in logs:
|
||||||
|
typer.echo(line.decode("utf-8").strip())
|
||||||
|
else:
|
||||||
|
typer.echo(logs.read().decode("utf-8"))
|
||||||
|
|
||||||
|
except client.rest.ApiException as e:
|
||||||
|
typer.echo(f"Error reading logs: {e}")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
setup_environment()
|
setup_environment()
|
||||||
load_init_scripts()
|
load_init_scripts()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue