This commit is contained in:
Waylon S. Walker 2025-04-08 21:09:10 -05:00
parent 90491d17bf
commit a60562b7fc
18 changed files with 1683 additions and 1 deletions

1
.gitignore vendored
View file

@ -962,4 +962,3 @@ FodyWeavers.xsd
# Additional files built by Visual Studio # Additional files built by Visual Studio
# End of https://www.toptal.com/developers/gitignore/api/vim,node,data,emacs,python,pycharm,executable,sublimetext,visualstudio,visualstudiocode # End of https://www.toptal.com/developers/gitignore/api/vim,node,data,emacs,python,pycharm,executable,sublimetext,visualstudio,visualstudiocode
krayt

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-present Waylon S. Walker <waylon@waylonwalker.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
krayt/__about__.py Normal file
View file

@ -0,0 +1 @@
__version__ = "0.0.0"

5
krayt/__init__.py Normal file
View file

@ -0,0 +1,5 @@
from krayt.__about__ import __version__
__all__ = [
"__version__",
]

18
krayt/cli/__init__.py Normal file
View file

@ -0,0 +1,18 @@
from krayt import __version__
from krayt.cli.create import app as create_app
from krayt.cli.templates import app as templates_app
from typer import Typer
app = Typer()
app.add_typer(templates_app, name="templates")
app.add_typer(create_app, name="create")
@app.command()
def version():
print(__version__)
def main():
app()

763
krayt/cli/create.py Normal file
View file

@ -0,0 +1,763 @@
from iterfzf import iterfzf
from krayt.templates import env
from kubernetes import client, config
import logging
import os
from pathlib import Path
import time
import typer
from typing import Any, Optional
import yaml
KRAYT_VERSION = "NIGHTLY"
logging.basicConfig(level=logging.WARNING)
app = typer.Typer()
def clean_dict(d: dict[str, Any]) -> dict[str, Any]:
"""Remove None values and empty dicts from a dictionary recursively."""
if not isinstance(d, dict):
return d
return {
k: clean_dict(v)
for k, v in d.items()
if v is not None and v != {} and not (isinstance(v, dict) and not clean_dict(v))
}
def format_volume_mount(vm: client.V1VolumeMount) -> dict[str, Any]:
"""Format volume mount with only relevant fields."""
# Skip Kubernetes service account mounts
if vm.mount_path.startswith("/var/run/secrets/kubernetes.io/"):
return None
return clean_dict(
{
"name": vm.name,
"mountPath": vm.mount_path,
"readOnly": vm.read_only if vm.read_only else None,
}
)
def format_volume(v: client.V1Volume) -> dict[str, Any]:
"""Format volume into a dictionary, return None if it should be skipped"""
# Skip Kubernetes service account volumes
if v.name.startswith("kube-api-access-"):
return None
volume_source = None
if v.persistent_volume_claim:
volume_source = {
"persistentVolumeClaim": {"claimName": v.persistent_volume_claim.claim_name}
}
elif v.config_map:
volume_source = {"configMap": {"name": v.config_map.name}}
elif v.secret:
volume_source = {"secret": {"secretName": v.secret.secret_name}}
elif v.host_path: # Add support for hostPath volumes (used for device mounts)
volume_source = {
"hostPath": {
"path": v.host_path.path,
"type": v.host_path.type if v.host_path.type else None,
}
}
elif v.empty_dir: # Add support for emptyDir volumes (used for /dev/shm)
volume_source = {
"emptyDir": {
"medium": v.empty_dir.medium if v.empty_dir.medium else None,
"sizeLimit": v.empty_dir.size_limit if v.empty_dir.size_limit else None,
}
}
if not volume_source:
return None
return clean_dict({"name": v.name, **volume_source})
def fuzzy_select(items):
"""Use fzf to select from a list of (name, namespace) tuples"""
if not items:
return None, None
# If there's only one item, return it without prompting
if len(items) == 1:
return items[0]
# Format items for display
formatted_items = [f"{name} ({namespace})" for name, namespace in items]
# Use fzf for selection
try:
selected = iterfzf(formatted_items)
if not selected:
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:
typer.echo(f"Error during selection: {e}")
return None, None
def get_pods(
namespace=None,
label_selector: str = "app=krayt",
):
"""Get list of pods in the specified namespace or all namespaces"""
try:
config.load_kube_config()
api = client.CoreV1Api()
if namespace:
pods = api.list_namespaced_pod(
namespace=namespace,
label_selector=label_selector,
)
else:
pods = api.list_pod_for_all_namespaces(
label_selector=label_selector,
)
# Convert to list of (name, namespace) tuples
pod_list = []
for pod in pods.items:
if pod.metadata.namespace not in PROTECTED_NAMESPACES:
pod_list.append((pod.metadata.name, pod.metadata.namespace))
return pod_list
except client.rest.ApiException as e:
typer.echo(f"Error listing pods: {e}")
raise typer.Exit(1)
def get_pod_spec(pod_name, namespace):
config.load_kube_config()
v1 = client.CoreV1Api()
return v1.read_namespaced_pod(pod_name, namespace)
def get_pod_volumes_and_mounts(pod_spec):
"""Extract all volumes and mounts from a pod spec"""
volume_mounts = []
for container in pod_spec.spec.containers:
if container.volume_mounts:
volume_mounts.extend(container.volume_mounts)
# Filter out None values from volume mounts
volume_mounts = [vm for vm in volume_mounts if format_volume_mount(vm)]
# Get all volumes, including device mounts
volumes = []
if pod_spec.spec.volumes:
for v in pod_spec.spec.volumes:
# Handle device mounts
if v.name in ["cache-volume"]:
volumes.append(
client.V1Volume(
name=v.name,
empty_dir=client.V1EmptyDirVolumeSource(medium="Memory"),
)
)
elif v.name in ["coral-device"]:
volumes.append(
client.V1Volume(
name=v.name,
host_path=client.V1HostPathVolumeSource(
path="/dev/apex_0", type="CharDevice"
),
)
)
elif v.name in ["qsv-device"]:
volumes.append(
client.V1Volume(
name=v.name,
host_path=client.V1HostPathVolumeSource(
path="/dev/dri", type="Directory"
),
)
)
else:
volumes.append(v)
# Filter out None values from volumes
volumes = [v for v in volumes if format_volume(v)]
return volume_mounts, volumes
def get_env_vars_and_secret_volumes(api, namespace: str):
"""Get environment variables and secret volumes for the inspector pod"""
env_vars = []
volumes = []
# Add proxy environment variables if they exist in the host environment
proxy_vars = [
"HTTP_PROXY",
"HTTPS_PROXY",
"NO_PROXY",
"http_proxy",
"https_proxy",
"no_proxy",
]
for var in proxy_vars:
if var in os.environ:
env_vars.append({"name": var, "value": os.environ[var]})
# Look for secret volumes in the namespace
try:
secrets = api.list_namespaced_secret(namespace)
for secret in secrets.items:
# Skip service account tokens and other system secrets
if secret.type != "Opaque" or secret.metadata.name.startswith(
"default-token-"
):
continue
# Mount each secret as a volume
volume_name = f"secret-{secret.metadata.name}"
volumes.append(
client.V1Volume(
name=volume_name,
secret=client.V1SecretVolumeSource(
secret_name=secret.metadata.name
),
)
)
except client.exceptions.ApiException as e:
if e.status != 404: # Ignore if no secrets found
logging.warning(f"Failed to list secrets in namespace {namespace}: {e}")
return env_vars, volumes
def get_init_scripts():
"""Get the contents of init scripts to be run in the pod"""
init_dir = Path.home() / ".config" / "krayt" / "init.d"
if not init_dir.exists():
logging.debug("No init.d directory found at %s", init_dir)
return ""
scripts = sorted(init_dir.glob("*.sh"))
if not scripts:
logging.debug("No init scripts found in %s", init_dir)
return ""
# Create a combined script that will run all init scripts
init_script = "#!/bin/bash\n\n"
init_script += "exec 2>&1 # Redirect stderr to stdout for proper logging\n"
init_script += "set -e # Exit on error\n\n"
init_script += "echo 'Running initialization scripts...' | tee /tmp/init.log\n\n"
init_script += "mkdir -p /tmp/init.d\n\n" # Create directory once at the start
for script in scripts:
try:
with open(script, "r") as f:
script_content = f.read()
if not script_content.strip():
logging.debug("Skipping empty script %s", script)
continue
# Use a unique heredoc delimiter for each script to avoid nesting issues
delimiter = f"EOF_SCRIPT_{script.stem.upper()}"
init_script += (
f"echo '=== Running {script.name} ===' | tee -a /tmp/init.log\n"
)
init_script += f"cat > /tmp/init.d/{script.name} << '{delimiter}'\n"
init_script += script_content
if not script_content.endswith("\n"):
init_script += "\n"
init_script += f"{delimiter}\n"
init_script += f"chmod +x /tmp/init.d/{script.name}\n"
init_script += f'cd /tmp/init.d && ./{script.name} 2>&1 | tee -a /tmp/init.log || {{ echo "Failed to run {script.name}"; exit 1; }}\n'
init_script += (
f"echo '=== Finished {script.name} ===' | tee -a /tmp/init.log\n\n"
)
except Exception as e:
logging.error(f"Failed to load init script {script}: {e}")
init_script += "echo 'Initialization scripts complete.' | tee -a /tmp/init.log\n"
return init_script
def get_motd_script(mount_info, pvc_info):
"""Generate the MOTD script with proper escaping"""
return f"""
# Create MOTD
cat << EOF > /etc/motd
====================================
Krayt Dragon's Lair
A safe haven for volume inspection
====================================
"Inside every volume lies a pearl of wisdom waiting to be discovered."
Mounted Volumes:
$(echo "{",".join(mount_info)}" | tr ',' '\\n' | sed 's/^/- /')
Persistent Volume Claims:
$(echo "{",".join(pvc_info)}" | tr ',' '\\n' | sed 's/^/- /')
Mounted Secrets:
$(for d in /mnt/secrets/*; do if [ -d "$d" ]; then echo "- $(basename $d)"; fi; done)
Init Script Status:
$(if [ -f /tmp/init.log ]; then echo "View initialization log at /tmp/init.log"; fi)
EOF
"""
def create_inspector_job(
api,
namespace: str,
pod_name: str,
volume_mounts: list,
volumes: list,
image: str = "alpine:latest",
imagepullsecret: Optional[str] = None,
):
"""Create a Krayt inspector job with the given mounts"""
timestamp = int(time.time())
job_name = f"{pod_name}-krayt-{timestamp}"
# Get environment variables and secret volumes from the target pod
env_vars, secret_volumes = get_env_vars_and_secret_volumes(api, namespace)
# Add secret volumes to our volumes list
volumes.extend(secret_volumes)
# Create corresponding volume mounts for secrets
secret_mounts = []
for vol in secret_volumes:
secret_mounts.append(
{
"name": vol.name,
"mountPath": f"/mnt/secrets/{vol.secret.secret_name}",
"readOnly": True,
}
)
# Convert volume mounts to dictionaries
formatted_mounts = [format_volume_mount(vm) for vm in volume_mounts]
formatted_mounts.extend(secret_mounts)
# Format mount and PVC info for MOTD
mount_info = []
for vm in formatted_mounts:
if vm:
mount_info.append(f"{vm['name']}:{vm['mountPath']}")
pvc_info = []
for v in volumes:
if hasattr(v, "persistent_volume_claim") and v.persistent_volume_claim:
pvc_info.append(f"{v.name}:{v.persistent_volume_claim.claim_name}")
template_name = "base.sh"
template = env.get_template(template_name)
additional_packages = [
"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",
"starship",
]
pvcs = None
pre_init_scripts = None
post_init_scripts = None
pre_init_hooks = None
post_init_hooks = None
command = template.render(
volumes=volumes,
pvcs=pvcs,
additional_packages=additional_packages,
pre_init_scripts=pre_init_scripts,
post_init_scripts=post_init_scripts,
pre_init_hooks=pre_init_hooks,
post_init_hooks=post_init_hooks,
)
inspector_job = {
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": {
"name": job_name,
"namespace": namespace,
"labels": {"app": "krayt"},
"annotations": {"pvcs": ",".join(pvc_info) if pvc_info else "none"},
},
"spec": {
"template": {
"metadata": {"labels": {"app": "krayt"}},
"spec": {
"containers": [
{
"name": "inspector",
"image": image,
"command": ["sh", "-c", command],
"env": env_vars,
"volumeMounts": formatted_mounts,
}
],
"volumes": [format_volume(v) for v in volumes if format_volume(v)],
"imagePullSecrets": [{"name": imagepullsecret}]
if imagepullsecret
else None,
"restartPolicy": "Never",
},
},
},
}
return inspector_job
PROTECTED_NAMESPACES = {
"kube-system",
"kube-public",
"kube-node-lease",
"argo-events",
"argo-rollouts",
"argo-workflows",
"argocd",
"cert-manager",
"ingress-nginx",
"monitoring",
"prometheus",
"istio-system",
"linkerd",
}
def load_init_scripts():
"""Load and execute initialization scripts from ~/.config/krayt/scripts/"""
init_dir = Path.home() / ".config" / "krayt" / "scripts"
if not init_dir.exists():
return
# Sort scripts to ensure consistent execution order
scripts = sorted(init_dir.glob("*.py"))
for script in scripts:
try:
with open(script, "r") as f:
exec(f.read(), globals())
logging.debug(f"Loaded init script: {script}")
except Exception as e:
logging.error(f"Failed to load init script {script}: {e}")
def setup_environment():
"""Set up the environment with proxy settings and other configurations"""
# Load environment variables for proxies
proxy_vars = [
"HTTP_PROXY",
"HTTPS_PROXY",
"NO_PROXY",
"http_proxy",
"https_proxy",
"no_proxy",
]
for var in proxy_vars:
if var in os.environ:
# Make both upper and lower case versions available
os.environ[var.upper()] = os.environ[var]
os.environ[var.lower()] = os.environ[var]
def version_callback(value: bool):
if value:
typer.echo(f"Version: {KRAYT_VERSION}")
raise typer.Exit()
@app.command()
def exec(
namespace: Optional[str] = typer.Option(
None,
help="Kubernetes namespace. If not specified, will search for inspectors across all namespaces.",
),
):
"""
Enter the Krayt dragon's lair! Connect to a running inspector pod.
If multiple inspectors are found, you'll get to choose which one to explore.
"""
config.load_kube_config()
batch_api = client.BatchV1Api()
try:
if namespace:
logging.debug(f"Listing jobs in namespace {namespace}")
jobs = batch_api.list_namespaced_job(
namespace=namespace, label_selector="app=krayt"
)
else:
logging.debug("Listing jobs in all namespaces")
jobs = batch_api.list_job_for_all_namespaces(label_selector="app=krayt")
running_inspectors = []
for job in jobs.items:
# Get the pod for this job
v1 = client.CoreV1Api()
pods = v1.list_namespaced_pod(
namespace=job.metadata.namespace,
label_selector=f"job-name={job.metadata.name}",
)
for pod in pods.items:
if pod.status.phase == "Running":
running_inspectors.append(
(pod.metadata.name, pod.metadata.namespace)
)
if not running_inspectors:
typer.echo("No running inspector pods found.")
raise typer.Exit(1)
if len(running_inspectors) == 1:
pod_name, pod_namespace = running_inspectors[0]
else:
pod_name, pod_namespace = fuzzy_select(running_inspectors)
if not pod_name:
typer.echo("No inspector selected.")
raise typer.Exit(1)
exec_command = [
"kubectl",
"exec",
"-it",
"-n",
pod_namespace,
pod_name,
"--",
"/bin/bash",
"-l",
]
os.execvp("kubectl", exec_command)
except client.exceptions.ApiException as e:
logging.error(f"Failed to list jobs: {e}")
typer.echo(f"Failed to list jobs: {e}", err=True)
raise typer.Exit(1)
@app.command()
def clean(
namespace: Optional[str] = typer.Option(
None,
help="Kubernetes namespace. If not specified, will cleanup in all namespaces.",
),
yes: bool = typer.Option(
False,
"--yes",
"-y",
help="Skip confirmation prompt.",
),
):
"""
Clean up after your hunt! Remove all Krayt inspector jobs.
Use --yes/-y to skip confirmation and clean up immediately.
"""
config.load_kube_config()
batch_api = client.BatchV1Api()
try:
if namespace:
if namespace in PROTECTED_NAMESPACES:
typer.echo(f"Error: Cannot cleanup in protected namespace {namespace}")
raise typer.Exit(1)
logging.debug(f"Listing jobs in namespace {namespace}")
jobs = batch_api.list_namespaced_job(
namespace=namespace, label_selector="app=krayt"
)
else:
logging.debug("Listing jobs in all namespaces")
jobs = batch_api.list_job_for_all_namespaces(label_selector="app=krayt")
# Filter out jobs in protected namespaces
jobs.items = [
job
for job in jobs.items
if job.metadata.namespace not in PROTECTED_NAMESPACES
]
if not jobs.items:
typer.echo("No Krayt inspector jobs found.")
return
# Show confirmation prompt
if not yes:
job_list = "\n".join(
f" {job.metadata.namespace}/{job.metadata.name}" for job in jobs.items
)
typer.echo(f"The following inspector jobs will be deleted:\n{job_list}")
if not typer.confirm("Are you sure you want to continue?"):
typer.echo("Operation cancelled.")
return
# Delete each job
for job in jobs.items:
try:
logging.debug(
f"Deleting job {job.metadata.namespace}/{job.metadata.name}"
)
batch_api.delete_namespaced_job(
name=job.metadata.name,
namespace=job.metadata.namespace,
body=client.V1DeleteOptions(propagation_policy="Background"),
)
typer.echo(f"Deleted job: {job.metadata.namespace}/{job.metadata.name}")
except client.exceptions.ApiException as e:
logging.error(
f"Failed to delete job {job.metadata.namespace}/{job.metadata.name}: {e}"
)
typer.echo(
f"Failed to delete job {job.metadata.namespace}/{job.metadata.name}: {e}",
err=True,
)
except client.exceptions.ApiException as e:
logging.error(f"Failed to list jobs: {e}")
typer.echo(f"Failed to list jobs: {e}", err=True)
raise typer.Exit(1)
@app.command()
def create(
namespace: Optional[str] = typer.Option(
None,
help="Kubernetes namespace. If not specified, will search for pods across all namespaces.",
),
image: str = typer.Option(
"alpine:latest",
"--image",
"-i",
help="Container image to use for the inspector pod",
),
imagepullsecret: Optional[str] = typer.Option(
None,
"--imagepullsecret",
help="Name of the image pull secret to use for pulling private images",
),
):
"""
Krack open a Krayt dragon! Create an inspector pod to explore what's inside your volumes.
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.
"""
# For create, we want to list all pods, not just Krayt pods
pods = get_pods(namespace, label_selector=None)
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)
pod_spec = get_pod_spec(selected_pod, selected_namespace)
volume_mounts, volumes = get_pod_volumes_and_mounts(pod_spec)
inspector_job = create_inspector_job(
client.CoreV1Api(),
selected_namespace,
selected_pod,
volume_mounts,
volumes,
image=image,
imagepullsecret=imagepullsecret,
)
# Output the job manifest
typer.echo(yaml.dump(clean_dict(inspector_job), sort_keys=False))
@app.command()
def version():
"""Show the version of Krayt."""
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():
setup_environment()
load_init_scripts()
app()
if __name__ == "__main__":
main()

90
krayt/cli/templates.py Normal file
View file

@ -0,0 +1,90 @@
from krayt.templates import env
import typer
from typing import List, Optional
app = typer.Typer()
@app.command()
def base(
volumes: Optional[List[str]] = typer.Option(
None,
"--volume",
),
pvcs: Optional[List[str]] = typer.Option(
None,
"--pvc",
),
additional_packages: Optional[List[str]] = typer.Option(
None, "--additional-packages", "-ap"
),
pre_init_scripts: Optional[List[str]] = typer.Option(
None,
"--pre-init-scripts",
help="additional scripts to execute at the end of container initialization",
),
post_init_scripts: Optional[List[str]] = typer.Option(
None,
"--post-init-scripts",
"--init-scripts",
help="additional scripts to execute at the start of container initialization",
),
pre_init_hooks: Optional[List[str]] = typer.Option(
None,
"--pre-init-hooks",
help="additional hooks to execute at the end of container initialization",
),
post_init_hooks: Optional[List[str]] = typer.Option(
None,
"--post-init-hooks",
"--init-hooks",
help="additional hooks to execute at the start of container initialization",
),
):
template_name = "base.sh"
template = env.get_template(template_name)
rendered = template.render(
volumes=volumes,
pvcs=pvcs,
additional_packages=additional_packages,
pre_init_scripts=pre_init_scripts,
post_init_scripts=post_init_scripts,
pre_init_hooks=pre_init_hooks,
post_init_hooks=post_init_hooks,
)
print(rendered)
@app.command()
def install(
additional_packages: Optional[List[str]] = typer.Option(
..., "--additional-packages", "-ap"
),
):
template_name = "install.sh"
breakpoint()
template = env.get_template(template_name)
rendered = template.render(packages=packages)
print(rendered)
@app.command()
def motd(
volumes: Optional[List[str]] = typer.Option(
None,
"--volume",
),
pvcs: Optional[List[str]] = typer.Option(
None,
"--pvc",
),
additional_packages: Optional[List[str]] = typer.Option(
..., "--additional-packages", "-ap"
),
):
template_name = "motd.sh"
template = env.get_template(template_name)
rendered = template.render(
volumes=volumes, pvcs=pvcs, additional_packages=additional_packages
)
print(rendered)

11
krayt/templates.py Normal file
View file

@ -0,0 +1,11 @@
from jinja2 import Environment, FileSystemLoader
from pathlib import Path
# Get the two template directories
template_dirs = [
Path(__file__).resolve().parents[0] / "templates",
Path.home() / ".config" / "krayt" / "templates",
]
# Create the Jinja environment
env = Environment(loader=FileSystemLoader([str(path) for path in template_dirs]))

3
krayt/templates/.kraytrc Normal file
View file

@ -0,0 +1,3 @@
if [ -t 1 ] && [ -f /etc/motd ]; then
cat /etc/motd
fi

25
krayt/templates/base.sh Normal file
View file

@ -0,0 +1,25 @@
mkdir -p /etc/krayt
cat <<'KRAYT_INIT_SH_EOF' >/etc/krayt/init.sh
{%- if pre_init_hooks %}
{% for hook in pre_init_hooks %}{{ hook }}{% endfor %}
{% endif -%}
{%- if pre_init_scripts %}
{% for script in pre_init_scripts %}{{ script }}{% endfor %}
{% endif -%}
{% include 'install.sh' %}
{% include 'motd.sh' %}
{% include 'kraytrc.sh' %}
{%- if post_init_scripts %}
{% for script in post_init_scripts %}{{ script }}{% endfor %}
{% endif %}
{%- if post_init_hooks %}
{% for hook in post_init_hooks %}{{ hook }}{% endfor %}
{% endif %}
echo "Krayt environment ready. Sleeping forever..."
trap "echo 'Received SIGTERM. Exiting...'; exit 0" TERM
tail -f /dev/null &
wait
KRAYT_INIT_SH_EOF
chmod +x /etc/krayt/init.sh
/etc/krayt/init.sh

View file

@ -0,0 +1,52 @@
{% if additional_packages %}
detect_package_manager_and_install_command() {
if [ $# -eq 0 ]; then
echo "Usage: detect_package_manager_and_install_command <package1> [package2] [...]"
return 1
fi
if command -v apt >/dev/null 2>&1; then
PKG_MANAGER="apt"
UPDATE_CMD="apt update &&"
INSTALL_CMD="apt install -y"
elif command -v dnf >/dev/null 2>&1; then
PKG_MANAGER="dnf"
UPDATE_CMD=""
INSTALL_CMD="dnf install -y"
elif command -v yum >/dev/null 2>&1; then
PKG_MANAGER="yum"
UPDATE_CMD=""
INSTALL_CMD="yum install -y"
elif command -v pacman >/dev/null 2>&1; then
PKG_MANAGER="pacman"
UPDATE_CMD=""
INSTALL_CMD="pacman -Sy --noconfirm"
elif command -v zypper >/dev/null 2>&1; then
PKG_MANAGER="zypper"
UPDATE_CMD=""
INSTALL_CMD="zypper install -y"
elif command -v apk >/dev/null 2>&1; then
PKG_MANAGER="apk"
UPDATE_CMD=""
INSTALL_CMD="apk add"
else
echo "No supported package manager found."
return 2
fi
PACKAGES="$*"
if [ -n "$UPDATE_CMD" ]; then
echo "$UPDATE_CMD
echo $INSTALL_CMD $PACKAGES"
$UPDATE_CMD
$INSTALL_CMD $PACKAGES
else
echo "$INSTALL_CMD $PACKAGES"
$INSTALL_CMD $PACKAGES
fi
}
detect_package_manager_and_install_command {% for package in additional_packages %}{{ package | trim }}{% if not loop.last %} {% endif %}{% endfor %}
{% endif %}

116
krayt/templates/kraytrc.sh Normal file
View file

@ -0,0 +1,116 @@
KRAYT_MARKER_START="# >>> Added by krayt-inject <<<"
KRAYT_MARKER_END='# <<< End krayt-inject >>>'
KRAYT_BLOCK='
if [ -t 1 ] && [ -f /etc/motd ] && [ -z "$MOTD_SHOWN" ]; then
cat /etc/motd
export MOTD_SHOWN=1
fi
# fix $SHELL, not set in some distros like alpine
if [ -n "$BASH_VERSION" ]; then
export SHELL=/bin/bash
elif [ -n "$ZSH_VERSION" ]; then
export SHELL=/bin/zsh
else
export SHELL=/bin/sh
fi
# krayt ENVIRONMENT
{%- if pvcs %}
export KRAYT_PVCS="{{ pvcs | join(' ') }}"
{% endif -%}
{%- if volumes %}
export KRAYT_VOLUMES="{{ volumes | join(' ') }}"
{% endif -%}
{%- if secrets %}
export KRAYT_SECRETS="{{ secrets | join(' ') }}"
{% endif -%}
{%- if additional_packages %}
export KRAYT_ADDITIONAL_PACKAGES="{{ additional_packages | join(' ') }}"
{% endif -%}
# Universal shell initializers
# Prompt
if command -v starship >/dev/null 2>&1; then
eval "$(starship init "$(basename "$SHELL")")"
fi
# Smarter cd
if command -v zoxide >/dev/null 2>&1; then
eval "$(zoxide init "$(basename "$SHELL")")"
fi
# Smarter shell history
if command -v atuin >/dev/null 2>&1; then
eval "$(atuin init "$(basename "$SHELL")")"
fi
if command -v mcfly >/dev/null 2>&1; then
eval "$(mcfly init "$(basename "$SHELL")")"
fi
# Directory-based environment
if command -v direnv >/dev/null 2>&1; then
eval "$(direnv hook "$(basename "$SHELL")")"
fi
if command -v fzf >/dev/null 2>&1; then
case "$(basename "$SHELL")" in
bash|zsh|fish)
eval "$(fzf --$(basename "$SHELL"))"
;;
*)
# shell not supported for fzf init
;;
esac
fi
# "Did you mean...?" for mistyped commands
if command -v thefuck >/dev/null 2>&1; then
eval "$(thefuck --alias)"
fi
'
cat <<EOF >/etc/.kraytrc
$KRAYT_MARKER_START
$KRAYT_BLOCK
$KRAYT_MARKER_END
EOF
KRAYT_RC_SOURCE='
if [ -f /etc/.kraytrc ]; then
. /etc/.kraytrc
fi
'
# List of common rc/profile files to patch
RC_FILES="
/etc/profile
/etc/bash.bashrc
/etc/bash/bashrc
/etc/bashrc
/etc/ashrc
/etc/zsh/zshrc
/etc/zsh/zprofile
/etc/shinit
/etc/fish/config.fish
"
echo "Searching for rc files..."
for rc_file in $RC_FILES; do
if [ -f "$rc_file" ]; then
echo "* Found $rc_file"
# Check if already patched
if grep -q "$KRAYT_MARKER_START" "$rc_file"; then
echo "- $rc_file already has krayt block. Skipping."
else
echo "+ Patching $rc_file"
echo "" >>"$rc_file"
echo "$KRAYT_MARKER_START" >>"$rc_file"
echo "$KRAYT_RC_SOURCE" >>"$rc_file"
echo "$KRAYT_MARKER_END" >>"$rc_file"
fi
fi
done

40
krayt/templates/motd.sh Normal file
View file

@ -0,0 +1,40 @@
cat <<EOF >/etc/motd
┌───────────────────────────────────┐
│Krayt Dragon's Lair │
│A safe haven for volume inspection │
└───────────────────────────────────┘
"Inside every volume lies a pearl of wisdom waiting to be discovered."
{%- if volumes %}
Mounted Volumes:
{%- for volume in volumes %}
- {{ volume }}
{%- endfor %}
{%- endif %}
{%- if pvcs %}
Persistent Volume Claims:
{%- for pvc in pvcs %}
- {{ pvc }}
{%- endfor %}
{%- endif %}
{%- if secrets %}
Mounted Secrets:
{%- for secret in secrets %}
- {{ secret }}
{%- endfor %}
{%- endif %}
{%- if additional_packages %}
Additional Packages:
{%- for package in additional_packages %}
- {{ package }}
{%- endfor %}
{%- endif %}
EOF

View file

337
krayt2.py Executable file
View file

@ -0,0 +1,337 @@
#!/usr/bin/env -S uv run --quiet --script
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "typer",
# "kubernetes",
# "InquirerPy",
# ]
# ///
from InquirerPy import inquirer
from kubernetes import client, config
import os
import typer
from typing import List, Optional
app = typer.Typer(name="krayt")
VERSION = "0.1.0"
# Default values
container_image_default = "ubuntu:22.04"
container_name_default = "krayt-container"
KNOWN_PACKAGE_MANAGERS = {
"apk": "apk add",
"dnf": "dnf install -y",
"yum": "yum install -y",
"apt-get": "apt-get update && apt-get install -y",
"apt": "apt update && apt install -y",
"zypper": "zypper install -y",
"pacman": "pacman -Sy --noconfirm",
}
def load_kube_config():
try:
config.load_kube_config()
except Exception as e:
typer.secho(f"Failed to load kubeconfig: {e}", fg=typer.colors.RED)
raise typer.Exit(1)
def detect_package_manager_command() -> str:
checks = [
f"which {pm} >/dev/null 2>&1 && echo {cmd}"
for pm, cmd in KNOWN_PACKAGE_MANAGERS.items()
]
return " || ".join(checks)
def get_proxy_env_vars() -> List[client.V1EnvVar]:
proxy_vars = [
"HTTP_PROXY",
"http_proxy",
"HTTPS_PROXY",
"https_proxy",
"NO_PROXY",
"no_proxy",
]
env_vars = []
for var in proxy_vars:
value = os.environ.get(var)
if value:
env_vars.append(client.V1EnvVar(name=var, value=value))
return env_vars
def fuzzy_pick_pod(namespace: Optional[str] = None) -> str:
load_kube_config()
core_v1 = client.CoreV1Api()
if namespace is None:
pods = core_v1.list_pod_for_all_namespaces()
else:
pods = core_v1.list_namespaced_pod(namespace=namespace)
pods = {pod.metadata.name: pod for pod in pods.items}
if not pods:
typer.secho("No pods found to clone.", fg=typer.colors.RED)
raise typer.Exit(1)
choice = inquirer.fuzzy(
message="Select a pod to clone:", choices=pods.keys()
).execute()
return pods[choice]
def clone_pod(core_v1, namespace: str, source_pod_name: str):
source_pod = core_v1.read_namespaced_pod(name=source_pod_name, namespace=namespace)
container = source_pod.spec.containers[0]
breakpoint()
return (
container.image,
container.volume_mounts,
source_pod.spec.volumes,
container.env,
source_pod.spec.image_pull_secrets,
)
@app.command()
def create(
image: str = typer.Option(
container_image_default, "--image", "-i", help="Image to use for the container"
),
name: str = typer.Option(
container_name_default, "--name", "-n", help="Name for the krayt container"
),
yes: bool = typer.Option(
False, "--yes", "-Y", help="Non-interactive, pull images without asking"
),
fuzzy_clone: bool = typer.Option(
False,
"--fuzzy-clone",
"-f",
help="Clone an existing pod",
),
clone: Optional[str] = typer.Option(
None, "--clone", "-c", help="Clone an existing krayt container"
),
volume: List[str] = typer.Option(
[],
"--volume",
help="Additional volumes to add to the container (pvc-name:/mount/path)",
),
additional_flags: List[str] = typer.Option(
[],
"--additional-flags",
"-a",
help="Additional flags to pass to the container manager command",
),
additional_packages: List[str] = typer.Option(
[],
"--additional-packages",
"-ap",
help="Additional packages to install during setup",
),
init_hooks: List[str] = typer.Option(
[], "--init-hooks", help="Commands to execute at the end of initialization"
),
pre_init_hooks: List[str] = typer.Option(
[],
"--pre-init-hooks",
help="Commands to execute at the start of initialization",
),
namespace: str = typer.Option(None, help="Kubernetes namespace"),
dry_run: bool = typer.Option(
False, "--dry-run", "-d", help="Only print the generated Kubernetes manifest"
),
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show more verbosity"),
image_pull_secret: Optional[str] = typer.Option(
None,
"--image-pull-secret",
help="Name of the Kubernetes secret for pulling the image",
),
):
"""Create a new Kubernetes pod inspired by distrobox."""
load_kube_config()
core_v1 = client.CoreV1Api()
if fuzzy_clone:
namespace, clone = fuzzy_pick_pod(namespace)
if clone is not None:
image, volume_mounts, volumes, env_vars, image_pull_secrets = clone_pod(
core_v1, namespace, clone
)
else:
volume_mounts = []
volumes = []
env_vars = get_proxy_env_vars()
for idx, pvc_entry in enumerate(volume):
try:
pvc_name, mount_path = pvc_entry.split(":", 1)
except ValueError:
typer.secho(
f"Invalid volume format: {pvc_entry}. Use pvc-name:/mount/path",
fg=typer.colors.RED,
)
raise typer.Exit(1)
volumes.append(
client.V1Volume(
name=f"volume-{idx}",
persistent_volume_claim=client.V1PersistentVolumeClaimVolumeSource(
claim_name=pvc_name
),
)
)
volume_mounts.append(
client.V1VolumeMount(
name=f"volume-{idx}",
mount_path=mount_path,
)
)
package_manager_detection = detect_package_manager_command()
package_manager_detection = """
detect_package_manager_and_install_command() {
if [ $# -eq 0 ]; then
echo "Usage: detect_package_manager_and_install_command <package1> [package2] [...]"
return 1
fi
if command -v apt >/dev/null 2>&1; then
PKG_MANAGER="apt"
UPDATE_CMD="apt update &&"
INSTALL_CMD="apt install -y"
elif command -v dnf >/dev/null 2>&1; then
PKG_MANAGER="dnf"
UPDATE_CMD=""
INSTALL_CMD="dnf install -y"
elif command -v yum >/dev/null 2>&1; then
PKG_MANAGER="yum"
UPDATE_CMD=""
INSTALL_CMD="yum install -y"
elif command -v pacman >/dev/null 2>&1; then
PKG_MANAGER="pacman"
UPDATE_CMD=""
INSTALL_CMD="pacman -Sy --noconfirm"
elif command -v zypper >/dev/null 2>&1; then
PKG_MANAGER="zypper"
UPDATE_CMD=""
INSTALL_CMD="zypper install -y"
elif command -v apk >/dev/null 2>&1; then
PKG_MANAGER="apk"
UPDATE_CMD=""
INSTALL_CMD="apk add"
else
echo "No supported package manager found."
return 2
fi
PACKAGES="$*"
if [ -n "$UPDATE_CMD" ]; then
echo "$UPDATE_CMD
echo $INSTALL_CMD $PACKAGES"
$UPDATE_CMD
$INSTALL_CMD $PACKAGES
else
echo "$INSTALL_CMD $PACKAGES"
$INSTALL_CMD $PACKAGES
fi
}
"""
pre_hooks_command = " && ".join(pre_init_hooks) if pre_init_hooks else ""
install_packages_command = ""
if additional_packages:
install_packages_command = f"{package_manager_detection}\n detect_package_manager_and_install_command {' '.join(additional_packages)}"
# install_packages_command = (
# f"$({{package_manager_detection}} {' '.join(additional_packages)})"
# )
post_hooks_command = " && ".join(init_hooks) if init_hooks else ""
combined_command_parts = [
cmd
for cmd in [pre_hooks_command, install_packages_command, post_hooks_command]
if cmd
]
command = None
if combined_command_parts:
combined_command = " && ".join(combined_command_parts)
command = ["/bin/sh", "-c", f"{combined_command} && tail -f /dev/null"]
pod_spec = client.V1PodSpec(
containers=[
client.V1Container(
name=name,
image=image,
command=command,
volume_mounts=volume_mounts if volume_mounts else None,
env=env_vars if env_vars else None,
)
],
volumes=volumes if volumes else None,
restart_policy="Never",
)
if image_pull_secret:
pod_spec.image_pull_secrets = [
client.V1LocalObjectReference(name=image_pull_secret)
]
elif clone and image_pull_secrets:
pod_spec.image_pull_secrets = image_pull_secrets
pod = client.V1Pod(
metadata=client.V1ObjectMeta(name=name, namespace=namespace), spec=pod_spec
)
if dry_run or verbose:
typer.secho(f"Dry-run/Verbose: Pod definition:\n{pod}", fg=typer.colors.BLUE)
if dry_run:
typer.secho("Dry run completed.", fg=typer.colors.GREEN)
raise typer.Exit()
typer.secho(
f"Creating pod '{name}' in namespace '{namespace}'...", fg=typer.colors.GREEN
)
core_v1.create_namespaced_pod(namespace=namespace, body=pod)
typer.secho("Pod created successfully.", fg=typer.colors.GREEN)
@app.command("fuzzy-pick-pod")
def cli_fuzzy_pick_pod(
namespace: str = typer.Option(None, help="Kubernetes namespace"),
):
load_kube_config()
pod = fuzzy_pick_pod(namespace)
if not pod:
typer.secho("No pod selected.", fg=typer.colors.RED)
raise typer.Exit(1)
typer.secho("Selected pod", fg=typer.colors.GREEN)
typer.secho(f"Name: {pod.metadata.name}", fg=typer.colors.GREEN)
typer.secho(f"Namespace: {pod.metadata.namespace}", fg=typer.colors.GREEN)
typer.secho(f"Image: {pod.spec.containers[0].image}", fg=typer.colors.GREEN)
typer.secho(f"Command: {pod.spec.containers[0].command}", fg=typer.colors.GREEN)
typer.secho(f"Volume mounts: {pod.spec.volumes}", fg=typer.colors.GREEN)
typer.secho(
f"Environment variables: {pod.spec.containers[0].env}", fg=typer.colors.GREEN
)
return pod
@app.command()
def version(show: bool = typer.Option(False, "--version", "-V", help="Show version")):
if show:
typer.echo(f"krayt version {VERSION}")
if __name__ == "__main__":
app()

54
pyproject.toml Normal file
View file

@ -0,0 +1,54 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
[tool.hatch.build.targets.sdist]
exclude = ["/.github"]
[project]
name = "krayt"
dynamic = ["version"]
description = 'kubernetes volume explorer'
readme = "README.md"
requires-python = ">=3.8"
keywords = []
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = [
"typer",
"kubernetes",
"InquirerPy",
"jinja2",
"iterfzf",
]
[[project.authors]]
name = "Waylon Walker"
email = "waylon@waylonwalker.com"
[project.license]
file = "LICENSE"
[project.urls]
Homepage = "https://github.com/waylonwalker/krayt#readme"
Documentation = "https://github.com/waylonwalker/krayt#readme"
Changelog = "https://github.com/waylonwalker/krayt#changelog"
Issues = "https://github.com/waylonwalker/krayt/issues"
Source = "https://github.com/waylonwalker/krayt"
[tool.hatch.version]
path = "krayt/__about__.py"
[project.scripts]
krayt = "krayt.cli:app"

51
test.sh Normal file
View file

@ -0,0 +1,51 @@
detect_package_manager_and_install_command() {
# Accept packages as arguments
PACKAGES=("$@")
if [[ ${#PACKAGES[@]} -eq 0 ]]; then
echo "Usage: detect_package_manager_and_install_command <package1> [package2] [...]"
return 1
fi
if command -v apt &>/dev/null; then
PKG_MANAGER="apt"
UPDATE_CMD="sudo apt update"
INSTALL_CMD="sudo apt install -y"
elif command -v dnf &>/dev/null; then
PKG_MANAGER="dnf"
UPDATE_CMD=""
INSTALL_CMD="sudo dnf install -y"
elif command -v yum &>/dev/null; then
PKG_MANAGER="yum"
UPDATE_CMD=""
INSTALL_CMD="sudo yum install -y"
elif command -v pacman &>/dev/null; then
PKG_MANAGER="pacman"
UPDATE_CMD=""
INSTALL_CMD="sudo pacman -Sy --noconfirm"
elif command -v zypper &>/dev/null; then
PKG_MANAGER="zypper"
UPDATE_CMD=""
INSTALL_CMD="sudo zypper install -y"
elif command -v apk &>/dev/null; then
PKG_MANAGER="apk"
UPDATE_CMD=""
INSTALL_CMD="sudo apk add"
else
echo "No supported package manager found."
return 2
fi
# Build the full install command
if [[ -n "$UPDATE_CMD" ]]; then
# echo $UPDATE_CMD
# $UPDATE_CMD
echo $INSTALL_CMD ${PACKAGES[*]}
$INSTALL_CMD ${PACKAGES[*]}
else
echo $INSTALL_CMD ${PACKAGES[*]}
$INSTALL_CMD ${PACKAGES[*]}
fi
}
detect_package_manager_and_install_command git htop

96
test.yaml Normal file
View file

@ -0,0 +1,96 @@
apiVersion: batch/v1
kind: Job
metadata:
name: htmx-patterns-66bfd987d7-98sw7-krayt-1744164311
namespace: htmx-patterns
labels:
app: krayt
annotations:
pvcs: none
spec:
template:
metadata:
labels:
app: krayt
spec:
containers:
- name: inspector
image: alpine:latest
command:
- sh
- -c
- "mkdir -p /etc/krayt\ncat <<'KRAYT_INIT_SH_EOF' >/etc/krayt/init.sh\ndetect_package_manager_and_install_command()\
\ {\n\tif [ $# -eq 0 ]; then\n\t\techo \"Usage: detect_package_manager_and_install_command\
\ <package1> [package2] [...]\"\n\t\treturn 1\n\tfi\n\n\tif command -v apt\
\ >/dev/null 2>&1; then\n\t\tPKG_MANAGER=\"apt\"\n\t\tUPDATE_CMD=\"apt update\
\ &&\"\n\t\tINSTALL_CMD=\"apt install -y\"\n\telif command -v dnf >/dev/null\
\ 2>&1; then\n\t\tPKG_MANAGER=\"dnf\"\n\t\tUPDATE_CMD=\"\"\n\t\tINSTALL_CMD=\"\
dnf install -y\"\n\telif command -v yum >/dev/null 2>&1; then\n\t\tPKG_MANAGER=\"\
yum\"\n\t\tUPDATE_CMD=\"\"\n\t\tINSTALL_CMD=\"yum install -y\"\n\telif command\
\ -v pacman >/dev/null 2>&1; then\n\t\tPKG_MANAGER=\"pacman\"\n\t\tUPDATE_CMD=\"\
\"\n\t\tINSTALL_CMD=\"pacman -Sy --noconfirm\"\n\telif command -v zypper\
\ >/dev/null 2>&1; then\n\t\tPKG_MANAGER=\"zypper\"\n\t\tUPDATE_CMD=\"\"\
\n\t\tINSTALL_CMD=\"zypper install -y\"\n\telif command -v apk >/dev/null\
\ 2>&1; then\n\t\tPKG_MANAGER=\"apk\"\n\t\tUPDATE_CMD=\"\"\n\t\tINSTALL_CMD=\"\
apk add\"\n\telse\n\t\techo \"No supported package manager found.\"\n\t\t\
return 2\n\tfi\n\n\tPACKAGES=\"$*\"\n\n\tif [ -n \"$UPDATE_CMD\" ]; then\n\
\t\techo \"$UPDATE_CMD\n echo $INSTALL_CMD $PACKAGES\"\n\t\t$UPDATE_CMD\n\
\t\t$INSTALL_CMD $PACKAGES\n\n\telse\n\t\techo \"$INSTALL_CMD $PACKAGES\"\
\n\t\t$INSTALL_CMD $PACKAGES\n\tfi\n}\n\ndetect_package_manager_and_install_command\
\ 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 starship\n\
\ncat <<EOF >/etc/motd\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
\u2500\u2500\u2500\u2500\u2510\n\u2502Krayt Dragon's Lair \
\ \u2502\n\u2502A safe haven for volume inspection \u2502\n\u2514\u2500\
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\"\
Inside every volume lies a pearl of wisdom waiting to be discovered.\"\n\
\nAdditional Packages:\n- ripgrep\n- exa\n- ncdu\n- dust\n- file\n- hexyl\n\
- jq\n- yq\n- bat\n- fd\n- fzf\n- htop\n- bottom\n- difftastic\n- mtr\n\
- bind-tools\n- aws-cli\n- sqlite\n- sqlite-dev\n- sqlite-libs\n- bash\n\
- neovim\n- starship\n\nEOF\nKRAYT_MARKER_START=\"# >>> Added by krayt-inject\
\ <<<\"\nKRAYT_MARKER_END='# <<< End krayt-inject >>>'\nKRAYT_BLOCK='\n\
if [ -t 1 ] && [ -f /etc/motd ] && [ -z \"$MOTD_SHOWN\" ]; then\n cat\
\ /etc/motd\n export MOTD_SHOWN=1\nfi\n\n# fix $SHELL, not set in some\
\ distros like alpine\nif [ -n \"$BASH_VERSION\" ]; then\n export SHELL=/bin/bash\n\
elif [ -n \"$ZSH_VERSION\" ]; then\n export SHELL=/bin/zsh\nelse\n \
\ export SHELL=/bin/sh\nfi\n\n# krayt ENVIRONMENT\nexport KRAYT_ADDITIONAL_PACKAGES=\"\
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 starship\"\
\n# Universal shell initializers\n\n# Prompt\nif command -v starship >/dev/null\
\ 2>&1; then\n\teval \"$(starship init \"$(basename \"$SHELL\")\")\"\nfi\n\
\n# Smarter cd\nif command -v zoxide >/dev/null 2>&1; then\n\teval \"$(zoxide\
\ init \"$(basename \"$SHELL\")\")\"\nfi\n\n# Smarter shell history\nif\
\ command -v atuin >/dev/null 2>&1; then\n\teval \"$(atuin init \"$(basename\
\ \"$SHELL\")\")\"\nfi\n\nif command -v mcfly >/dev/null 2>&1; then\n\t\
eval \"$(mcfly init \"$(basename \"$SHELL\")\")\"\nfi\n\n# Directory-based\
\ environment\nif command -v direnv >/dev/null 2>&1; then\n\teval \"$(direnv\
\ hook \"$(basename \"$SHELL\")\")\"\nfi\n\nif command -v fzf >/dev/null\
\ 2>&1; then\n case \"$(basename \"$SHELL\")\" in\n bash|zsh|fish)\n\
\ eval \"$(fzf --$(basename \"$SHELL\"))\"\n ;;\n\
\ *)\n # shell not supported for fzf init\n \
\ ;;\n esac\nfi\n# \"Did you mean...?\" for mistyped commands\nif command\
\ -v thefuck >/dev/null 2>&1; then\n\teval \"$(thefuck --alias)\"\nfi\n\
'\ncat <<EOF >/etc/.kraytrc\n$KRAYT_MARKER_START\n$KRAYT_BLOCK\n$KRAYT_MARKER_END\n\
EOF\n\nKRAYT_RC_SOURCE='\nif [ -f /etc/.kraytrc ]; then\n . /etc/.kraytrc\n\
fi\n'\n\n# List of common rc/profile files to patch\nRC_FILES=\"\n/etc/profile\n\
/etc/bash.bashrc\n/etc/bash/bashrc\n/etc/bashrc\n/etc/ashrc\n/etc/zsh/zshrc\n\
/etc/zsh/zprofile\n/etc/shinit\n/etc/fish/config.fish\n\"\n\necho \"Searching\
\ for rc files...\"\n\nfor rc_file in $RC_FILES; do\n\tif [ -f \"$rc_file\"\
\ ]; then\n\t\techo \"* Found $rc_file\"\n\n\t\t# Check if already patched\n\
\t\tif grep -q \"$KRAYT_MARKER_START\" \"$rc_file\"; then\n\t\t\techo \"\
- $rc_file already has krayt block. Skipping.\"\n\t\telse\n\t\t\techo \"\
+ Patching $rc_file\"\n\t\t\techo \"\" >>\"$rc_file\"\n\t\t\techo \"$KRAYT_MARKER_START\"\
\ >>\"$rc_file\"\n\t\t\techo \"$KRAYT_RC_SOURCE\" >>\"$rc_file\"\n\t\t\t\
echo \"$KRAYT_MARKER_END\" >>\"$rc_file\"\n\t\tfi\n\tfi\ndone\necho \"Krayt\
\ environment ready. Sleeping forever...\"\ntrap \"echo 'Received SIGTERM.\
\ Exiting...'; exit 0\" TERM\ntail -f /dev/null &\nwait\nKRAYT_INIT_SH_EOF\n\
\nchmod +x /etc/krayt/init.sh\n/etc/krayt/init.sh"
env: []
volumeMounts: []
volumes: []
restartPolicy: Never