still working
This commit is contained in:
parent
08c70cc18f
commit
eb90496cbc
8 changed files with 541 additions and 264 deletions
74
README.md
74
README.md
|
|
@ -65,6 +65,80 @@ Your inspector pod comes equipped with a full arsenal of tools:
|
|||
- **Network Tools**: `mtr`, `dig`
|
||||
- **Cloud & Database**: `aws-cli`, `sqlite3`
|
||||
|
||||
## Customization
|
||||
|
||||
### Init Scripts
|
||||
|
||||
Krayt supports initialization scripts that run in the inspector pod before any packages are installed. These scripts are useful for:
|
||||
- Setting up proxy configurations
|
||||
- Installing additional tools
|
||||
- Configuring custom package repositories
|
||||
- Setting environment variables
|
||||
|
||||
Place your scripts in `~/.config/krayt/init.d/` with a `.sh` extension. Scripts are executed in alphabetical order, so you can control the execution sequence using numerical prefixes.
|
||||
|
||||
Example init scripts:
|
||||
|
||||
1. Install additional tools (`~/.config/krayt/init.d/10_install_git.sh`):
|
||||
```bash
|
||||
#!/bin/sh
|
||||
echo "Installing additional tools..."
|
||||
|
||||
# Install git for source control
|
||||
apk add git
|
||||
|
||||
# Configure git
|
||||
git config --global init.defaultBranch main
|
||||
git config --global core.editor vi
|
||||
```
|
||||
|
||||
2. Set up custom repositories (`~/.config/krayt/init.d/20_custom_repos.sh`):
|
||||
```bash
|
||||
#!/bin/sh
|
||||
echo "Adding custom package repositories..."
|
||||
|
||||
# Add testing repository for newer packages
|
||||
echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||
|
||||
# Update package list
|
||||
apk update
|
||||
```
|
||||
|
||||
### Proxy Configuration
|
||||
|
||||
If your environment requires a proxy, you have two options:
|
||||
|
||||
1. **Environment Variables** (Recommended):
|
||||
```bash
|
||||
# Add to your shell's rc file (e.g., ~/.bashrc, ~/.zshrc)
|
||||
export HTTP_PROXY="http://proxy.example.com:8080"
|
||||
export HTTPS_PROXY="http://proxy.example.com:8080"
|
||||
export NO_PROXY="localhost,127.0.0.1,.internal.example.com"
|
||||
```
|
||||
|
||||
2. **Init Script** (`~/.config/krayt/init.d/00_proxy.sh`):
|
||||
```bash
|
||||
#!/bin/sh
|
||||
echo "Configuring proxy settings..."
|
||||
|
||||
# Set proxy for Alpine package manager
|
||||
mkdir -p /etc/apk
|
||||
cat > /etc/apk/repositories << EOF
|
||||
http://dl-cdn.alpinelinux.org/alpine/latest-stable/main
|
||||
http://dl-cdn.alpinelinux.org/alpine/latest-stable/community
|
||||
|
||||
# Configure proxy
|
||||
proxy=http://proxy.example.com:8080
|
||||
EOF
|
||||
|
||||
# Set proxy for other tools
|
||||
export HTTP_PROXY="http://proxy.example.com:8080"
|
||||
export HTTPS_PROXY="http://proxy.example.com:8080"
|
||||
export NO_PROXY="localhost,127.0.0.1,.internal.example.com"
|
||||
```
|
||||
|
||||
The proxy configuration will be applied before any packages are installed, ensuring that all package installations and network operations work correctly through your proxy.
|
||||
|
||||
## Quotes from the Field
|
||||
|
||||
> "Inside every volume lies a pearl of wisdom waiting to be discovered."
|
||||
|
|
|
|||
25
examples/init.d/00_proxy.sh
Normal file
25
examples/init.d/00_proxy.sh
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/sh
|
||||
echo "Configuring proxy settings..."
|
||||
|
||||
# Set proxy for Alpine package manager
|
||||
mkdir -p /etc/apk
|
||||
cat > /etc/apk/repositories << EOF
|
||||
http://dl-cdn.alpinelinux.org/alpine/latest-stable/main
|
||||
http://dl-cdn.alpinelinux.org/alpine/latest-stable/community
|
||||
|
||||
# Configure proxy
|
||||
proxy=http://proxy.example.com:8080
|
||||
EOF
|
||||
|
||||
# Set proxy for other tools
|
||||
export HTTP_PROXY="http://proxy.example.com:8080"
|
||||
export HTTPS_PROXY="http://proxy.example.com:8080"
|
||||
export NO_PROXY="localhost,127.0.0.1,.internal.example.com"
|
||||
|
||||
# Test proxy configuration
|
||||
echo "Testing proxy configuration..."
|
||||
if curl -s -m 5 https://www.google.com > /dev/null; then
|
||||
echo "Proxy configuration successful!"
|
||||
else
|
||||
echo "Warning: Proxy test failed. Check your proxy settings."
|
||||
fi
|
||||
20
examples/init.d/10_install_git.sh
Normal file
20
examples/init.d/10_install_git.sh
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
echo "Installing additional development tools..."
|
||||
|
||||
# Install git and related tools
|
||||
apk add git git-lfs
|
||||
|
||||
# Configure git defaults
|
||||
git config --global init.defaultBranch main
|
||||
git config --global core.editor vi
|
||||
git config --global pull.rebase false
|
||||
|
||||
# Add some helpful git aliases
|
||||
git config --global alias.st status
|
||||
git config --global alias.co checkout
|
||||
git config --global alias.br branch
|
||||
git config --global alias.ci commit
|
||||
git config --global alias.unstage 'reset HEAD --'
|
||||
git config --global alias.last 'log -1 HEAD'
|
||||
|
||||
echo "Git configuration complete."
|
||||
19
examples/init.d/20_custom_repos.sh
Normal file
19
examples/init.d/20_custom_repos.sh
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
echo "Setting up additional package repositories..."
|
||||
|
||||
# Add testing repository for newer packages
|
||||
echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||
|
||||
# Add community repository
|
||||
echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
|
||||
|
||||
# Update package list
|
||||
apk update
|
||||
|
||||
# Install some useful tools from testing/community
|
||||
apk add \
|
||||
@testing golang \
|
||||
@community rust \
|
||||
@community cargo
|
||||
|
||||
echo "Additional repositories configured and packages installed."
|
||||
BIN
krayt-squooshed.png
Normal file
BIN
krayt-squooshed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 511 KiB |
BIN
krayt.png
Normal file
BIN
krayt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
467
krayt.py
467
krayt.py
|
|
@ -13,20 +13,15 @@ Krayt - The Kubernetes Volume Inspector
|
|||
Like cracking open a Krayt dragon pearl, this tool helps you inspect what's inside your Kubernetes volumes.
|
||||
Hunt down storage issues and explore your persistent data like a true Tatooine dragon hunter.
|
||||
|
||||
Features:
|
||||
- Create inspector pods with all the tools you need
|
||||
- Access volumes and device mounts from any pod
|
||||
- Fuzzy search across all namespaces
|
||||
- Built-in tools for file exploration and analysis
|
||||
- Automatic cleanup of inspector pods
|
||||
|
||||
May the Force be with your volumes!
|
||||
"""
|
||||
|
||||
import os
|
||||
import glob
|
||||
from pathlib import Path
|
||||
from iterfzf import iterfzf
|
||||
from kubernetes import client, config
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import typer
|
||||
from typing import Any, Optional
|
||||
|
|
@ -205,46 +200,110 @@ def get_pod_volumes_and_mounts(pod_spec):
|
|||
return volume_mounts, volumes
|
||||
|
||||
|
||||
def get_pod_env_and_secrets(api, namespace, pod_name):
|
||||
pod = api.read_namespaced_pod(pod_name, namespace)
|
||||
|
||||
# Get environment variables from the pod
|
||||
def get_env_vars_and_secret_volumes(api, namespace: str):
|
||||
"""Get environment variables and secret volumes for the inspector pod"""
|
||||
env_vars = []
|
||||
for container in pod.spec.containers:
|
||||
if container.env:
|
||||
for env in container.env:
|
||||
env_dict = {"name": env.name}
|
||||
if env.value:
|
||||
env_dict["value"] = env.value
|
||||
elif env.value_from:
|
||||
if env.value_from.config_map_key_ref:
|
||||
env_dict["valueFrom"] = {
|
||||
"configMapKeyRef": {
|
||||
"name": env.value_from.config_map_key_ref.name,
|
||||
"key": env.value_from.config_map_key_ref.key,
|
||||
}
|
||||
}
|
||||
elif env.value_from.secret_key_ref:
|
||||
env_dict["valueFrom"] = {
|
||||
"secretKeyRef": {
|
||||
"name": env.value_from.secret_key_ref.name,
|
||||
"key": env.value_from.secret_key_ref.key,
|
||||
}
|
||||
}
|
||||
elif env.value_from.field_ref:
|
||||
env_dict["valueFrom"] = {
|
||||
"fieldRef": {
|
||||
"fieldPath": env.value_from.field_ref.field_path
|
||||
}
|
||||
}
|
||||
env_vars.append(env_dict)
|
||||
volumes = []
|
||||
|
||||
# Get all volume mounts that are secrets
|
||||
secret_volumes = []
|
||||
if pod.spec.volumes:
|
||||
secret_volumes = [v for v in pod.spec.volumes if v.secret]
|
||||
# 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]})
|
||||
|
||||
return env_vars, secret_volumes
|
||||
# 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():
|
||||
return ""
|
||||
|
||||
# Sort scripts to ensure consistent execution order
|
||||
scripts = sorted(init_dir.glob("*.sh"))
|
||||
if not scripts:
|
||||
return ""
|
||||
|
||||
# Create a combined script that will run all init scripts
|
||||
init_script = "#!/bin/sh\n\n"
|
||||
init_script += "echo 'Running initialization scripts...'\n\n"
|
||||
|
||||
for script in scripts:
|
||||
try:
|
||||
with open(script, 'r') as f:
|
||||
script_content = f.read()
|
||||
if script_content:
|
||||
init_script += f"echo '=== Running {script.name} ==='\n"
|
||||
# Write each script to a separate file
|
||||
init_script += f"cat > /tmp/{script.name} << 'EOFSCRIPT'\n"
|
||||
init_script += script_content
|
||||
if not script_content.endswith('\n'):
|
||||
init_script += '\n'
|
||||
init_script += "EOFSCRIPT\n\n"
|
||||
# Make it executable and run it
|
||||
init_script += f"chmod +x /tmp/{script.name}\n"
|
||||
init_script += f"/tmp/{script.name} 2>&1 | tee -a /tmp/init.log\n"
|
||||
init_script += f"echo '=== Finished {script.name} ===\n\n'"
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to load init script {script}: {e}")
|
||||
|
||||
init_script += "echo 'Initialization scripts complete.'\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(
|
||||
|
|
@ -254,8 +313,8 @@ def create_inspector_job(
|
|||
timestamp = int(time.time())
|
||||
job_name = f"{pod_name}-krayt-{timestamp}"
|
||||
|
||||
# Get environment variables and secrets from the target pod
|
||||
env_vars, secret_volumes = get_pod_env_and_secrets(api, namespace, pod_name)
|
||||
# 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)
|
||||
|
|
@ -286,6 +345,89 @@ def create_inspector_job(
|
|||
if hasattr(v, "persistent_volume_claim") and v.persistent_volume_claim:
|
||||
pvc_info.append(f"{v.name}:{v.persistent_volume_claim.claim_name}")
|
||||
|
||||
init_scripts = get_init_scripts()
|
||||
|
||||
# Build the command script
|
||||
command_parts = []
|
||||
|
||||
# Configure apk proxy settings BEFORE any package installation
|
||||
command_parts.extend([
|
||||
"# Configure apk proxy settings",
|
||||
"mkdir -p /etc/apk",
|
||||
"cat > /etc/apk/repositories << 'EOF'",
|
||||
"https://dl-cdn.alpinelinux.org/alpine/latest-stable/main",
|
||||
"https://dl-cdn.alpinelinux.org/alpine/latest-stable/community",
|
||||
"EOF",
|
||||
"",
|
||||
"if [ ! -z \"$HTTP_PROXY\" ]; then",
|
||||
" echo \"Setting up apk proxy configuration...\"",
|
||||
" mkdir -p /etc/apk/",
|
||||
" cat > /etc/apk/repositories << EOF",
|
||||
"#/media/cdrom/apks",
|
||||
"http://dl-cdn.alpinelinux.org/alpine/latest-stable/main",
|
||||
"http://dl-cdn.alpinelinux.org/alpine/latest-stable/community",
|
||||
"",
|
||||
"# Configure proxy",
|
||||
"proxy=$HTTP_PROXY",
|
||||
"EOF",
|
||||
"fi",
|
||||
""
|
||||
])
|
||||
|
||||
# Add init scripts if present
|
||||
if init_scripts:
|
||||
command_parts.extend([
|
||||
"# Write and run init scripts",
|
||||
"mkdir -p /tmp/init.d",
|
||||
"cat > /tmp/init.sh << 'EOFSCRIPT'",
|
||||
init_scripts,
|
||||
"EOFSCRIPT",
|
||||
"",
|
||||
"# Make init script executable and run it",
|
||||
"chmod +x /tmp/init.sh",
|
||||
"/tmp/init.sh 2>&1 | tee /tmp/init.log",
|
||||
"echo 'Init script log available at /tmp/init.log'",
|
||||
""
|
||||
])
|
||||
|
||||
# Add base installation commands AFTER proxy configuration
|
||||
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",
|
||||
"cat > /root/.ashrc << 'EOF'",
|
||||
"# Display MOTD on login",
|
||||
"[ -f /etc/motd ] && cat /etc/motd",
|
||||
"EOF",
|
||||
"",
|
||||
"# Set up shell environment",
|
||||
"export EDITOR=vi",
|
||||
"export PAGER=less",
|
||||
"",
|
||||
"# Set up environment to always source our RC file",
|
||||
"echo 'export ENV=/root/.ashrc' > /etc/profile",
|
||||
"echo 'export ENV=/root/.ashrc' > /etc/environment",
|
||||
"",
|
||||
"# Make RC file available to all shells",
|
||||
"mkdir -p /etc/profile.d",
|
||||
"cp /root/.ashrc /etc/profile.d/motd.sh",
|
||||
"ln -sf /root/.ashrc /root/.profile",
|
||||
"ln -sf /root/.ashrc /root/.bashrc",
|
||||
"ln -sf /root/.ashrc /root/.mkshrc",
|
||||
"ln -sf /root/.ashrc /etc/shinit",
|
||||
"",
|
||||
"# Update MOTD",
|
||||
get_motd_script(mount_info, pvc_info),
|
||||
"",
|
||||
"# Keep container running",
|
||||
"tail -f /dev/null"
|
||||
])
|
||||
|
||||
inspector_job = {
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
|
|
@ -293,11 +435,15 @@ def create_inspector_job(
|
|||
"name": job_name,
|
||||
"namespace": namespace,
|
||||
"labels": {"app": "krayt"},
|
||||
"annotations": {
|
||||
"pvcs": ",".join(pvc_info) if pvc_info else "none"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"ttlSecondsAfterFinished": 0, # Delete immediately after completion
|
||||
"template": {
|
||||
"metadata": {"labels": {"app": "krayt"}},
|
||||
"metadata": {
|
||||
"labels": {"app": "krayt"}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
|
|
@ -306,170 +452,9 @@ def create_inspector_job(
|
|||
"command": [
|
||||
"sh",
|
||||
"-c",
|
||||
"""
|
||||
# Install basic tools first
|
||||
apk update
|
||||
apk add curl
|
||||
|
||||
# Install lf (terminal file manager)
|
||||
curl -L https://github.com/gokcehan/lf/releases/download/r31/lf-linux-amd64.tar.gz | tar xzf - -C /usr/local/bin
|
||||
|
||||
# Install the rest of the 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
|
||||
|
||||
# Function to update MOTD
|
||||
update_motd() {
|
||||
cat << EOF > /etc/motd
|
||||
====================================
|
||||
Krayt Dragon's Lair
|
||||
====================================
|
||||
"Inside every volume lies a pearl of wisdom waiting to be discovered."
|
||||
|
||||
Mounted Volumes:
|
||||
$(echo "$MOUNTS" | tr ',' '\\n' | sed 's/^/- /')
|
||||
|
||||
Persistent Volume Claims:
|
||||
$(echo "$PVCS" | tr ',' '\\n' | sed 's/^/- /')
|
||||
|
||||
Mounted Secrets:
|
||||
$(for d in /mnt/secrets/*; do if [ -d "$d" ]; then echo "- $(basename $d)"; fi; done)
|
||||
|
||||
Environment Variables:
|
||||
$(env | sort | sed 's/^/- /')
|
||||
|
||||
Your Hunting Tools:
|
||||
File Navigation:
|
||||
- lf: Terminal file manager (run 'lf')
|
||||
- exa: Modern ls (run 'ls', 'll', or 'tree')
|
||||
- fd: Modern find (run 'fd pattern')
|
||||
|
||||
Search & Analysis:
|
||||
- rg (ripgrep): Fast search (run 'rg pattern')
|
||||
- bat: Better cat with syntax highlighting
|
||||
- hexyl: Hex viewer (run 'hexyl file')
|
||||
- file: File type detection
|
||||
|
||||
Disk Usage:
|
||||
- ncdu: Interactive disk usage analyzer
|
||||
- dust: Disk usage analyzer
|
||||
- du: Standard disk usage tool
|
||||
|
||||
File Comparison:
|
||||
- difft: Modern diff tool (alias 'diff')
|
||||
|
||||
System Monitoring:
|
||||
- btm: Modern system monitor (alias 'top')
|
||||
- htop: Interactive process viewer
|
||||
|
||||
JSON/YAML Tools:
|
||||
- jq: JSON processor
|
||||
- yq: YAML processor
|
||||
|
||||
Network Tools:
|
||||
- dig: DNS lookup
|
||||
- mtr: Network diagnostics
|
||||
|
||||
Cloud & Database:
|
||||
- aws: AWS CLI
|
||||
- sqlite3: SQLite database tool
|
||||
|
||||
Type 'tools-help' for detailed usage information
|
||||
====================================
|
||||
EOF
|
||||
}
|
||||
|
||||
# Create helpful aliases and functions
|
||||
cat << 'EOF' > /root/.ashrc
|
||||
if [ "$PS1" ]; then
|
||||
cat /etc/motd
|
||||
fi
|
||||
|
||||
# Aliases for better file navigation
|
||||
alias ls='exa'
|
||||
alias ll='exa -l'
|
||||
alias la='exa -la'
|
||||
alias tree='exa --tree'
|
||||
alias find='fd'
|
||||
alias top='btm'
|
||||
alias diff='difft'
|
||||
alias cat='bat --paging=never'
|
||||
|
||||
# Function to show detailed tool help
|
||||
tools-help() {
|
||||
echo "Krayt Dragon Hunter's Guide:"
|
||||
echo
|
||||
echo "File Navigation:"
|
||||
echo " lf : Navigate with arrow keys, q to quit, h for help"
|
||||
echo " ls, ll, la : List files (exa with different options)"
|
||||
echo " tree : Show directory structure"
|
||||
echo " fd pattern : Find files matching pattern"
|
||||
echo
|
||||
echo "Search & Analysis:"
|
||||
echo " rg pattern : Search file contents"
|
||||
echo " bat file : View file with syntax highlighting"
|
||||
echo " hexyl file : View file in hex format"
|
||||
echo " file path : Determine file type"
|
||||
echo
|
||||
echo "Disk Usage:"
|
||||
echo " ncdu : Interactive disk usage analyzer (navigate with arrows)"
|
||||
echo " dust path : Tree-based disk usage"
|
||||
echo " du -sh * : Summarize disk usage"
|
||||
echo
|
||||
echo "File Comparison:"
|
||||
echo " diff file1 file2 : Compare files with syntax highlighting"
|
||||
echo
|
||||
echo "System Monitoring:"
|
||||
echo " top (btm) : Modern system monitor"
|
||||
echo " htop : Process viewer"
|
||||
echo
|
||||
echo "JSON/YAML Tools:"
|
||||
echo " jq . file.json : Format and query JSON"
|
||||
echo " yq . file.yaml : Format and query YAML"
|
||||
echo
|
||||
echo "Network Tools:"
|
||||
echo " dig domain : DNS lookup"
|
||||
echo " mtr host : Network diagnostics"
|
||||
echo
|
||||
echo "Cloud & Database:"
|
||||
echo " aws : AWS CLI tool"
|
||||
echo " sqlite3 : SQLite database tool"
|
||||
echo
|
||||
echo "Secrets:"
|
||||
echo " ls /mnt/secrets : List mounted secrets"
|
||||
}
|
||||
|
||||
# Set some helpful environment variables
|
||||
export EDITOR=vi
|
||||
export PAGER=less
|
||||
EOF
|
||||
|
||||
# Set up environment to always source our RC file
|
||||
echo "export ENV=/root/.ashrc" > /etc/profile
|
||||
echo "export ENV=/root/.ashrc" > /etc/environment
|
||||
|
||||
# Make RC file available to all shells
|
||||
cp /root/.ashrc /etc/profile.d/motd.sh
|
||||
ln -sf /root/.ashrc /root/.profile
|
||||
ln -sf /root/.ashrc /root/.bashrc
|
||||
ln -sf /root/.ashrc /root/.mkshrc
|
||||
ln -sf /root/.ashrc /etc/shinit
|
||||
|
||||
# Create initial MOTD
|
||||
update_motd
|
||||
|
||||
sleep 3600
|
||||
""",
|
||||
],
|
||||
"env": env_vars
|
||||
+ [
|
||||
{"name": "MOUNTS", "value": ",".join(mount_info)},
|
||||
{"name": "PVCS", "value": ",".join(pvc_info)},
|
||||
{"name": "ENV", "value": "/root/.ashrc"},
|
||||
"\n".join(command_parts)
|
||||
],
|
||||
"env": env_vars,
|
||||
"volumeMounts": formatted_mounts,
|
||||
}
|
||||
],
|
||||
|
|
@ -499,6 +484,37 @@ PROTECTED_NAMESPACES = {
|
|||
}
|
||||
|
||||
|
||||
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}")
|
||||
|
|
@ -569,12 +585,21 @@ def exec(
|
|||
typer.echo("No inspector selected.")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Execute the shell
|
||||
typer.echo(f"Connecting to inspector {pod_namespace}/{pod_name}...")
|
||||
os.execvp(
|
||||
# Execute into the pod with a login shell to source .ashrc and show MOTD
|
||||
exec_command = [
|
||||
"kubectl",
|
||||
["kubectl", "exec", "-it", "-n", pod_namespace, pod_name, "--", "sh", "-l"],
|
||||
)
|
||||
"-n",
|
||||
pod_namespace,
|
||||
"exec",
|
||||
"-it",
|
||||
pod_name,
|
||||
"--",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"cat /etc/motd; exec /bin/ash -l"
|
||||
]
|
||||
|
||||
os.execvp("kubectl", exec_command)
|
||||
|
||||
except client.exceptions.ApiException as e:
|
||||
logging.error(f"Failed to list jobs: {e}")
|
||||
|
|
@ -702,5 +727,11 @@ def version():
|
|||
typer.echo(f"Version: {KRAYT_VERSION}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main():
|
||||
setup_environment()
|
||||
load_init_scripts()
|
||||
app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function fail {
|
|||
echo "Error: $msg" 1>&2
|
||||
exit 1
|
||||
}
|
||||
function check_deps {
|
||||
function check_uv {
|
||||
if ! command -v uv &>/dev/null; then
|
||||
echo " Error: uv is not installed"
|
||||
echo "krayt requires uv to run. You can install it with:"
|
||||
|
|
@ -24,15 +24,67 @@ function check_deps {
|
|||
fail "uv not found"
|
||||
fi
|
||||
}
|
||||
|
||||
function setup_config_dir {
|
||||
# Create config directory
|
||||
CONFIG_DIR="${HOME}/.config/krayt"
|
||||
mkdir -p "${CONFIG_DIR}/init.d"
|
||||
|
||||
# Create example init script if it doesn't exist
|
||||
EXAMPLE_SCRIPT="${CONFIG_DIR}/init.d/00_proxy.sh.example"
|
||||
if [ ! -f "$EXAMPLE_SCRIPT" ]; then
|
||||
cat > "$EXAMPLE_SCRIPT" << 'EOF'
|
||||
#!/bin/sh
|
||||
# Example initialization script for Krayt inspector pods
|
||||
# This script runs before any packages are installed
|
||||
# To use this script, rename it to remove the .example extension
|
||||
|
||||
# Example: Set up proxy configuration
|
||||
setup_proxy() {
|
||||
# Uncomment and modify these lines to set up your proxy
|
||||
# export HTTP_PROXY="http://proxy.example.com:8080"
|
||||
# export HTTPS_PROXY="http://proxy.example.com:8080"
|
||||
# export NO_PROXY="localhost,127.0.0.1,.example.com"
|
||||
|
||||
# Set up proxy for apk if needed
|
||||
if [ ! -z "$HTTP_PROXY" ]; then
|
||||
echo "proxy = $HTTP_PROXY" >> /etc/apk/repositories
|
||||
fi
|
||||
}
|
||||
|
||||
# Example: Add custom Alpine repositories
|
||||
setup_repos() {
|
||||
# Uncomment to add custom repos
|
||||
# echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||
:
|
||||
}
|
||||
|
||||
# Run the setup functions
|
||||
setup_proxy
|
||||
setup_repos
|
||||
|
||||
# Log the configuration
|
||||
echo "Krayt inspector pod initialization complete"
|
||||
echo "Proxy settings:"
|
||||
echo "HTTP_PROXY=$HTTP_PROXY"
|
||||
echo "HTTPS_PROXY=$HTTPS_PROXY"
|
||||
echo "NO_PROXY=$NO_PROXY"
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "Created config directory at ${CONFIG_DIR}"
|
||||
echo "Example init script created at ${EXAMPLE_SCRIPT}"
|
||||
}
|
||||
|
||||
function install {
|
||||
#settings
|
||||
USER="waylonwalker"
|
||||
PROG="krayt"
|
||||
ASPROG="krayt"
|
||||
MOVE="true"
|
||||
MOVE="false"
|
||||
RELEASE="{{VERSION}}"
|
||||
INSECURE="false"
|
||||
OUT_DIR="/usr/local/bin"
|
||||
OUT_DIR="$(pwd)"
|
||||
GH="https://github.com"
|
||||
#bash check
|
||||
[ ! "$BASH_VERSION" ] && fail "Please use bash instead"
|
||||
|
|
@ -57,70 +109,126 @@ function install {
|
|||
else
|
||||
fail "neither wget/curl are installed"
|
||||
fi
|
||||
#find OS
|
||||
#debug HTTP
|
||||
if [ "$DEBUG" == "1" ]; then
|
||||
GET="$GET -v"
|
||||
fi
|
||||
#optional auth to install from private repos
|
||||
#NOTE: this also needs to be set on your instance of installer
|
||||
AUTH="${GITHUB_TOKEN}"
|
||||
if [ ! -z "$AUTH" ]; then
|
||||
GET="$GET -H 'Authorization: $AUTH'"
|
||||
fi
|
||||
#find OS #TODO BSDs and other posixs
|
||||
case $(uname -s) in
|
||||
Darwin) OS="darwin" ;;
|
||||
Linux) OS="linux" ;;
|
||||
*) fail "unknown os: $(uname -s)" ;;
|
||||
esac
|
||||
#find ARCH
|
||||
if uname -m | grep -E '(arm|aarch)64' >/dev/null; then
|
||||
ARCH="aarch64"
|
||||
if uname -m | grep -E '(arm|arch)64' >/dev/null; then
|
||||
ARCH="arm64"
|
||||
|
||||
# no m1 assets. if on mac arm64, rosetta allows fallback to amd64
|
||||
if [[ $OS = "darwin" ]]; then
|
||||
ARCH="amd64"
|
||||
fi
|
||||
|
||||
elif uname -m | grep 64 >/dev/null; then
|
||||
ARCH="x86_64"
|
||||
ARCH="amd64"
|
||||
elif uname -m | grep arm >/dev/null; then
|
||||
ARCH="arm" #TODO armv6/v7
|
||||
elif uname -m | grep 386 >/dev/null; then
|
||||
ARCH="386"
|
||||
else
|
||||
fail "unknown arch: $(uname -m)"
|
||||
fi
|
||||
#choose from asset list
|
||||
URL=""
|
||||
FTYPE=""
|
||||
VERSION=${RELEASE#v}
|
||||
if [[ $VERSION == "" ]]; then
|
||||
VERSION=$(curl -s https://api.github.com/repos/$USER/$PROG/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)
|
||||
case "${OS}_${ARCH}" in
|
||||
"linux_amd64")
|
||||
URL="https://github.com/WaylonWalker/nvim-manager/releases/download/v${RELEASE}/nvim-manager-${RELEASE}-x86_64-unknown-linux-gnu.tar.gz"
|
||||
FTYPE=".tar.gz"
|
||||
;;
|
||||
"linux_arm64")
|
||||
URL="https://github.com/WaylonWalker/nvim-manager/releases/download/v${RELEASE}/nvim-manager-${RELEASE}-aarch64-unknown-linux-gnu.tar.gz"
|
||||
FTYPE=".tar.gz"
|
||||
;;
|
||||
*) fail "No asset for platform ${OS}-${ARCH}" ;;
|
||||
esac
|
||||
#got URL! download it...
|
||||
echo -n "Downloading"
|
||||
echo -n " $USER/$PROG"
|
||||
if [ ! -z "$RELEASE" ]; then
|
||||
echo -n " $RELEASE"
|
||||
fi
|
||||
if [[ $VERSION == "" ]]; then
|
||||
fail "cannot find latest version"
|
||||
if [ ! -z "$ASPROG" ]; then
|
||||
echo -n " as $ASPROG"
|
||||
fi
|
||||
VERSION=${VERSION#v}
|
||||
ASSET_URL="$GH/$USER/$PROG/releases/download/v$VERSION/${PROG}-${VERSION}-${ARCH}-unknown-${OS}-gnu.tar.gz"
|
||||
echo "Installing $PROG v$VERSION..."
|
||||
echo "Downloading binary from $ASSET_URL"
|
||||
echo -n " (${OS}/${ARCH})"
|
||||
|
||||
echo "....."
|
||||
|
||||
#enter tempdir
|
||||
mkdir -p $TMP_DIR
|
||||
cd $TMP_DIR
|
||||
#download and unpack
|
||||
if [[ $ASSET_URL =~ \.gz$ ]]; then
|
||||
which tar >/dev/null || fail "tar not installed"
|
||||
if [[ $GET =~ ^curl ]]; then
|
||||
curl -s ${ASSET_URL} | tar zx || fail "download failed"
|
||||
else
|
||||
wget -qO- ${ASSET_URL} | tar zx || fail "download failed"
|
||||
fi
|
||||
if [[ $FTYPE = ".gz" ]]; then
|
||||
which gzip >/dev/null || fail "gzip is not installed"
|
||||
bash -c "$GET $URL" | gzip -d - >$PROG || fail "download failed"
|
||||
elif [[ $FTYPE = ".bz2" ]]; then
|
||||
which bzip2 >/dev/null || fail "bzip2 is not installed"
|
||||
bash -c "$GET $URL" | bzip2 -d - >$PROG || fail "download failed"
|
||||
elif [[ $FTYPE = ".tar.bz" ]] || [[ $FTYPE = ".tar.bz2" ]]; then
|
||||
which tar >/dev/null || fail "tar is not installed"
|
||||
which bzip2 >/dev/null || fail "bzip2 is not installed"
|
||||
bash -c "$GET $URL" | tar jxf - || fail "download failed"
|
||||
elif [[ $FTYPE = ".tar.gz" ]] || [[ $FTYPE = ".tgz" ]]; then
|
||||
which tar >/dev/null || fail "tar is not installed"
|
||||
which gzip >/dev/null || fail "gzip is not installed"
|
||||
bash -c "$GET $URL" | tar zxf - || fail "download failed"
|
||||
elif [[ $FTYPE = ".zip" ]]; then
|
||||
which unzip >/dev/null || fail "unzip is not installed"
|
||||
bash -c "$GET $URL" >tmp.zip || fail "download failed"
|
||||
unzip -o -qq tmp.zip || fail "unzip failed"
|
||||
rm tmp.zip || fail "cleanup failed"
|
||||
elif [[ $FTYPE = ".bin" ]]; then
|
||||
bash -c "$GET $URL" >"nvim-manager_${OS}_${ARCH}" || fail "download failed"
|
||||
else
|
||||
fail "unknown file type: $ASSET_URL"
|
||||
fail "unknown file type: $FTYPE"
|
||||
fi
|
||||
#check for error
|
||||
cd ${PROG}-${VERSION}-${ARCH}-unknown-${OS}-gnu
|
||||
#move binary
|
||||
if [[ -f "${PROG}.py" ]]; then
|
||||
chmod +x "${PROG}.py"
|
||||
if [[ $MOVE == "true" ]]; then
|
||||
echo "Moving binary to $OUT_DIR/$ASPROG"
|
||||
# Create a wrapper script to ensure uv is used
|
||||
cat > "$OUT_DIR/$ASPROG" << EOF
|
||||
#!/bin/bash
|
||||
exec uv run --quiet --script "$OUT_DIR/${ASPROG}.py" "\$@"
|
||||
EOF
|
||||
chmod +x "$OUT_DIR/$ASPROG"
|
||||
mv "${PROG}.py" "$OUT_DIR/${ASPROG}.py" || fail "Cannot move binary to $OUT_DIR"
|
||||
else
|
||||
echo "Moving binary to $OUT_DIR/${ASPROG}.py"
|
||||
mv "${PROG}.py" "$OUT_DIR/${ASPROG}.py" || fail "Cannot move binary to $OUT_DIR"
|
||||
fi
|
||||
#search subtree largest file (bin)
|
||||
TMP_BIN=$(find . -type f | xargs du | sort -n | tail -n 1 | cut -f 2)
|
||||
if [ ! -f "$TMP_BIN" ]; then
|
||||
fail "could not find find binary (largest file)"
|
||||
fi
|
||||
#ensure its larger than 1MB
|
||||
#TODO linux=elf/darwin=macho file detection?
|
||||
if [[ $(du -m $TMP_BIN | cut -f1) -lt 1 ]]; then
|
||||
fail "no binary found ($TMP_BIN is not larger than 1MB)"
|
||||
fi
|
||||
#move into PATH or cwd
|
||||
chmod +x $TMP_BIN || fail "chmod +x failed"
|
||||
DEST="$OUT_DIR/$PROG"
|
||||
if [ ! -z "$ASPROG" ]; then
|
||||
DEST="$OUT_DIR/$ASPROG"
|
||||
fi
|
||||
#move without sudo
|
||||
OUT=$(mv $TMP_BIN $DEST 2>&1)
|
||||
STATUS=$?
|
||||
# failed and string contains "Permission denied"
|
||||
if [ $STATUS -ne 0 ]; then
|
||||
if [[ $OUT =~ "Permission denied" ]]; then
|
||||
echo "mv with sudo..."
|
||||
sudo mv $TMP_BIN $DEST || fail "sudo mv failed"
|
||||
else
|
||||
fail "cannot find binary"
|
||||
fail "mv failed ($OUT)"
|
||||
fi
|
||||
fi
|
||||
echo "Installation complete!"
|
||||
echo "Downloaded to $DEST"
|
||||
#done
|
||||
cleanup
|
||||
check_uv
|
||||
}
|
||||
check_deps
|
||||
install
|
||||
setup_config_dir
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue