Compare commits
25 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f9e70267e | ||
|
|
f8c44999fe | ||
|
|
45e5dd74aa | ||
|
|
eb54e314ff | ||
|
|
d6056cead2 | ||
|
|
be6ee91098 | ||
|
|
1fee6e2c97 | ||
|
|
2fbc15ae84 | ||
|
|
b64e635b71 | ||
|
|
7511cace42 | ||
|
|
1138f695f4 | ||
|
|
0a6e19731d | ||
|
|
64077d3ba2 | ||
|
|
681194fc1f | ||
|
|
ed449034f7 | ||
|
|
17c088526b | ||
|
|
225edce32d | ||
|
|
db96853646 | ||
|
|
3ce69baf26 | ||
|
|
7daa9a3874 | ||
|
|
cc425cf812 | ||
|
|
ae918bf5f2 | ||
|
|
2f16036c8e | ||
|
|
82102c4adf | ||
|
|
1959e1a39b |
10 changed files with 396 additions and 201 deletions
25
.github/workflows/release-pypi.yaml
vendored
25
.github/workflows/release-pypi.yaml
vendored
|
|
@ -12,7 +12,7 @@ permissions:
|
|||
packages: none
|
||||
id-token: write
|
||||
jobs:
|
||||
release-krayt:
|
||||
pypi-release-krayt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
@ -22,3 +22,26 @@ jobs:
|
|||
env:
|
||||
# required for gh release
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
- run: sudo rm -rf dist
|
||||
- name: Install just
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
|
||||
shell: bash
|
||||
- name: Install uv
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/0.6.16/install.sh | sh
|
||||
shell: bash
|
||||
- name: Install hatch
|
||||
run: |
|
||||
uv tool install hatch
|
||||
shell: bash
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
shell: bash
|
||||
- name: GitHub Release (just release)
|
||||
run: just create-release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
shell: bash
|
||||
|
|
|
|||
26
CHANGELOG.md
26
CHANGELOG.md
|
|
@ -1,3 +1,29 @@
|
|||
## 0.4.3
|
||||
|
||||
- working out binary release process
|
||||
|
||||
## 0.4.2
|
||||
|
||||
- working out binary release process
|
||||
|
||||
## 0.4.1
|
||||
|
||||
- Automated release for both pypi and github
|
||||
|
||||
## 0.4.0
|
||||
|
||||
- create now has --apply to apply the generated manifest to the cluster
|
||||
- generic templates endpoint for cli
|
||||
- better motd for volume mounts
|
||||
|
||||
## 0.3.0
|
||||
|
||||
- created pypi release
|
||||
- updated releases to use pyapp
|
||||
- all new package
|
||||
- port forward support
|
||||
- additional_packages support
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Added
|
||||
|
|
|
|||
82
justfile
82
justfile
|
|
@ -3,7 +3,7 @@ delete-tag:
|
|||
set -euo pipefail
|
||||
|
||||
# Get the version
|
||||
VERSION=$(cat version)
|
||||
VERSION=$(hatch version)
|
||||
|
||||
# Delete the tag
|
||||
git tag -d "v$VERSION"
|
||||
|
|
@ -14,83 +14,65 @@ delete-release:
|
|||
set -euo pipefail
|
||||
|
||||
# Get the version
|
||||
VERSION=$(cat version)
|
||||
VERSION=$(hatch version)
|
||||
|
||||
# Delete the release
|
||||
gh release delete "v$VERSION"
|
||||
|
||||
create-tag:
|
||||
#!/usr/bin/env bash
|
||||
VERSION=$(cat version)
|
||||
VERSION=$(hatch version)
|
||||
git tag -a "v$VERSION" -m "Release v$VERSION"
|
||||
git push origin "v$VERSION"
|
||||
|
||||
create-archives:
|
||||
#!/usr/bin/env bash
|
||||
VERSION=$(cat version)
|
||||
VERSION=$(hatch version)
|
||||
rm -rf dist build
|
||||
mkdir -p dist
|
||||
hatch build -t binary
|
||||
|
||||
krayt_bin=dist/binary/krayt-${VERSION}
|
||||
|
||||
# Create the binary for each platform
|
||||
for platform in "x86_64-unknown-linux-gnu" "aarch64-unknown-linux-gnu"; do
|
||||
outdir="krayt-${VERSION}-${platform}"
|
||||
mkdir -p "dist/${outdir}"
|
||||
|
||||
outbin="krayt-${VERSION}-${platform}"
|
||||
# Copy the Python script and update version
|
||||
cp krayt.py "dist/${outdir}/krayt.py"
|
||||
sed -i "s/NIGHTLY/${VERSION}/" "dist/${outdir}/krayt.py"
|
||||
|
||||
cd dist
|
||||
tar czf "${outdir}.tar.gz" "${outdir}"
|
||||
sha256sum "${outdir}.tar.gz" > "${outdir}.tar.gz.sha256"
|
||||
cd ..
|
||||
cp ${krayt_bin} "dist/binary/${outbin}"
|
||||
done
|
||||
|
||||
# Generate install.sh
|
||||
./scripts/generate_install_script.py "$VERSION"
|
||||
chmod +x dist/install.sh
|
||||
# ./scripts/generate_install_script.py "$VERSION"
|
||||
# chmod +x dist/install.sh
|
||||
|
||||
create-release: create-tag create-archives
|
||||
#!/usr/bin/env bash
|
||||
VERSION=$(cat version)
|
||||
VERSION=$(hatch version)
|
||||
./scripts/get_release_notes.py "$VERSION" > release_notes.tmp
|
||||
gh release create "v$VERSION" \
|
||||
--title "v$VERSION" \
|
||||
--notes-file release_notes.tmp \
|
||||
dist/krayt-${VERSION}-x86_64-unknown-linux-gnu.tar.gz \
|
||||
dist/krayt-${VERSION}-x86_64-unknown-linux-gnu.tar.gz.sha256 \
|
||||
dist/krayt-${VERSION}-aarch64-unknown-linux-gnu.tar.gz \
|
||||
dist/krayt-${VERSION}-aarch64-unknown-linux-gnu.tar.gz.sha256 \
|
||||
dist/install.sh
|
||||
|
||||
# Check if release already exists
|
||||
if gh release view "v$VERSION" &>/dev/null; then
|
||||
echo "Release v$VERSION already exists. Uploading binaries..."
|
||||
# Upload binaries to existing release
|
||||
gh release upload "v$VERSION" \
|
||||
dist/binary/krayt-${VERSION} \
|
||||
dist/binary/krayt-${VERSION}-aarch64-unknown-linux-gnu \
|
||||
dist/binary/krayt-${VERSION}-x86_64-unknown-linux-gnu || true
|
||||
else
|
||||
echo "Creating new release v$VERSION"
|
||||
# Create new release with binaries
|
||||
gh release create "v$VERSION" \
|
||||
--title "v$VERSION" \
|
||||
--notes-file release_notes.tmp \
|
||||
dist/binary/krayt-${VERSION} \
|
||||
dist/binary/krayt-${VERSION}-aarch64-unknown-linux-gnu \
|
||||
dist/binary/krayt-${VERSION}-x86_64-unknown-linux-gnu
|
||||
fi
|
||||
rm release_notes.tmp
|
||||
|
||||
preview-release-notes:
|
||||
#!/usr/bin/env bash
|
||||
VERSION=$(cat version)
|
||||
VERSION=$(hatch version)
|
||||
./scripts/get_release_notes.py "$VERSION" | less -R
|
||||
|
||||
release: create-release
|
||||
|
||||
build-pyapp:
|
||||
export PYAPP_PROJECT_NAME=krayt
|
||||
export PYAPP_PROJECT_VERSION=`hatch version`
|
||||
export PYAPP_DISTRIBUTION_SOURCE=~/git/krayt/dist/krayt-${PYAPP_PROJECT_VERSION}.tar.gz
|
||||
export PYAPP_DISTRIBUTION_EMBED=true
|
||||
|
||||
|
||||
echo "linting"
|
||||
hatch run lint-format
|
||||
|
||||
echo "Building pyapp"
|
||||
hatch build
|
||||
|
||||
echo "Uploading pyapp"
|
||||
hatch publish
|
||||
|
||||
cd ~/git/pyapp
|
||||
cargo build --release --quiet
|
||||
|
||||
|
||||
echo "Done"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.3.0"
|
||||
__version__ = "0.4.3"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typer import Typer
|
|||
|
||||
app = Typer()
|
||||
|
||||
app.add_typer(templates_app, name="templates", no_args_is_help=True)
|
||||
app.add_typer(templates_app, name="template", no_args_is_help=True)
|
||||
app.add_typer(pod_app, name="pod", no_args_is_help=True)
|
||||
app.command(name="create")(create)
|
||||
app.command(name="c")(create)
|
||||
|
|
|
|||
354
krayt/cli/pod.py
354
krayt/cli/pod.py
|
|
@ -1,5 +1,6 @@
|
|||
import iterfzf
|
||||
from krayt.templates import env
|
||||
from kubernetes.stream import stream
|
||||
from kubernetes import client, config
|
||||
import logging
|
||||
import os
|
||||
|
|
@ -8,6 +9,12 @@ import typer
|
|||
from typing import Any, List, Optional
|
||||
import yaml
|
||||
from krayt.__about__ import __version__
|
||||
import sys
|
||||
import tty
|
||||
import termios
|
||||
import select
|
||||
import signal
|
||||
import json
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
|
@ -35,8 +42,8 @@ def format_volume_mount(vm: client.V1VolumeMount) -> dict[str, Any]:
|
|||
return clean_dict(
|
||||
{
|
||||
"name": vm.name,
|
||||
"mountPath": vm.mount_path,
|
||||
"readOnly": vm.read_only if vm.read_only else None,
|
||||
"mount_path": vm.mount_path,
|
||||
"read_only": vm.read_only if vm.read_only else None,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -144,6 +151,17 @@ def get_pods(
|
|||
raise typer.Exit(1)
|
||||
|
||||
|
||||
def get_namespaces(
|
||||
namespace=None,
|
||||
label_selector: str = "app=krayt",
|
||||
):
|
||||
config.load_kube_config()
|
||||
api = client.CoreV1Api()
|
||||
|
||||
all_namespaces = [n.metadata.name for n in api.list_namespace().items]
|
||||
return all_namespaces
|
||||
|
||||
|
||||
def get_pod_spec(pod_name, namespace):
|
||||
config.load_kube_config()
|
||||
v1 = client.CoreV1Api()
|
||||
|
|
@ -260,92 +278,81 @@ def create_inspector_job(
|
|||
pre_init_hooks: Optional[List[str]] = None,
|
||||
post_init_hooks: Optional[List[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,
|
||||
}
|
||||
secret_mounts = [
|
||||
client.V1VolumeMount(
|
||||
name=vol.name,
|
||||
mount_path=f"/mnt/secrets/{vol.secret.secret_name}",
|
||||
read_only=True,
|
||||
)
|
||||
for vol in secret_volumes
|
||||
]
|
||||
|
||||
# Convert volume mounts to dictionaries
|
||||
formatted_mounts = [format_volume_mount(vm) for vm in volume_mounts]
|
||||
formatted_mounts = [client.V1VolumeMount(**vm) for vm in formatted_mounts if vm]
|
||||
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 = [
|
||||
f"{v.name}:{v.persistent_volume_claim.claim_name}"
|
||||
for v in volumes
|
||||
if hasattr(v, "persistent_volume_claim") and v.persistent_volume_claim
|
||||
]
|
||||
|
||||
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)
|
||||
pvcs = None
|
||||
pre_init_scripts = None
|
||||
post_init_scripts = None
|
||||
pre_init_hooks = None
|
||||
post_init_hooks = None
|
||||
template = env.get_template("base.sh")
|
||||
command = template.render(
|
||||
volumes=volumes,
|
||||
pvcs=pvcs,
|
||||
pvcs=None,
|
||||
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,
|
||||
pre_init_scripts=None,
|
||||
post_init_scripts=None,
|
||||
pre_init_hooks=None,
|
||||
post_init_hooks=None,
|
||||
)
|
||||
|
||||
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": {
|
||||
"ttlSecondsAfterFinished": 600,
|
||||
"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
|
||||
container = client.V1Container(
|
||||
name="inspector",
|
||||
image=image,
|
||||
command=["sh", "-c", command],
|
||||
env=env_vars,
|
||||
volume_mounts=formatted_mounts,
|
||||
)
|
||||
|
||||
spec = client.V1PodSpec(
|
||||
containers=[container],
|
||||
volumes=[format_volume(v) for v in volumes if format_volume(v)],
|
||||
restart_policy="Never",
|
||||
image_pull_secrets=[client.V1LocalObjectReference(name=imagepullsecret)]
|
||||
if imagepullsecret
|
||||
else None,
|
||||
)
|
||||
|
||||
template = client.V1PodTemplateSpec(
|
||||
metadata=client.V1ObjectMeta(labels={"app": "krayt"}), spec=spec
|
||||
)
|
||||
|
||||
job_spec = client.V1JobSpec(
|
||||
template=template,
|
||||
ttl_seconds_after_finished=600,
|
||||
)
|
||||
|
||||
job = client.V1Job(
|
||||
api_version="batch/v1",
|
||||
kind="Job",
|
||||
metadata=client.V1ObjectMeta(
|
||||
name=job_name,
|
||||
namespace=namespace,
|
||||
labels={"app": "krayt"},
|
||||
annotations={"pvcs": ",".join(pvc_info) if pvc_info else "none"},
|
||||
),
|
||||
spec=job_spec,
|
||||
)
|
||||
|
||||
return job
|
||||
|
||||
|
||||
PROTECTED_NAMESPACES = {
|
||||
|
|
@ -438,32 +445,164 @@ def get_pod(namespace: Optional[str] = None):
|
|||
return pod_name, pod_namespace
|
||||
|
||||
|
||||
def interactive_exec(pod_name: str, namespace: str):
|
||||
# Load kubeconfig from local context (or use load_incluster_config if running inside the cluster)
|
||||
print(f"Connecting to pod {pod_name} in namespace {namespace}...")
|
||||
try:
|
||||
config.load_kube_config()
|
||||
except Exception as e:
|
||||
print(f"Error loading kubeconfig: {e}", file=sys.stderr)
|
||||
return
|
||||
|
||||
core_v1 = client.CoreV1Api()
|
||||
command = ["/bin/bash", "-l"]
|
||||
resp = None
|
||||
|
||||
# Save the current terminal settings
|
||||
oldtty = termios.tcgetattr(sys.stdin)
|
||||
|
||||
# Function to handle window resize events
|
||||
def handle_resize(signum, frame):
|
||||
if resp and resp.is_open():
|
||||
# Get the current terminal size
|
||||
cols, rows = os.get_terminal_size()
|
||||
# Send terminal resize command via websocket
|
||||
# Format matches kubectl's resize message format
|
||||
resize_msg = json.dumps({"Width": cols, "Height": rows})
|
||||
resp.write_channel(4, resize_msg)
|
||||
|
||||
# Function to handle exit signals
|
||||
def handle_exit(signum, frame):
|
||||
if resp and resp.is_open():
|
||||
# Send Ctrl+C to the remote process
|
||||
resp.write_stdin("\x03")
|
||||
|
||||
try:
|
||||
# Put terminal into raw mode but don't handle local echo ourselves
|
||||
# Let the remote terminal handle echoing and control characters
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
|
||||
# Set up signal handlers
|
||||
signal.signal(signal.SIGWINCH, handle_resize) # Window resize
|
||||
signal.signal(signal.SIGINT, handle_exit) # Ctrl+C
|
||||
|
||||
# Create a TTY-enabled exec connection to the pod
|
||||
try:
|
||||
resp = stream(
|
||||
core_v1.connect_get_namespaced_pod_exec,
|
||||
pod_name,
|
||||
namespace,
|
||||
command=command,
|
||||
stderr=True,
|
||||
stdin=True,
|
||||
stdout=True,
|
||||
tty=True,
|
||||
_preload_content=False,
|
||||
)
|
||||
print(f"Connected to {pod_name}")
|
||||
except Exception as e:
|
||||
print(f"\nError connecting to pod: {e}", file=sys.stderr)
|
||||
return
|
||||
|
||||
# Wait for the connection to be ready
|
||||
time.sleep(0.2)
|
||||
|
||||
# Send initial terminal size
|
||||
cols, rows = os.get_terminal_size()
|
||||
resize_msg = json.dumps({"Width": cols, "Height": rows})
|
||||
resp.write_channel(4, resize_msg)
|
||||
|
||||
# Make sure the size is set by sending a resize event
|
||||
handle_resize(None, None)
|
||||
|
||||
# Set up a simple select-based event loop to handle I/O
|
||||
try:
|
||||
while resp and resp.is_open():
|
||||
# Update the websocket connection
|
||||
resp.update(timeout=0.1)
|
||||
|
||||
# Handle output from the pod
|
||||
if resp.peek_stdout():
|
||||
sys.stdout.write(resp.read_stdout())
|
||||
sys.stdout.flush()
|
||||
if resp.peek_stderr():
|
||||
sys.stderr.write(resp.read_stderr())
|
||||
sys.stderr.flush()
|
||||
|
||||
# Check for input from the user
|
||||
rlist, _, _ = select.select([sys.stdin], [], [], 0.01)
|
||||
if sys.stdin in rlist:
|
||||
# Read input and forward it to the pod without local echo
|
||||
data = os.read(sys.stdin.fileno(), 1024)
|
||||
if not data: # EOF (e.g., user pressed Ctrl+D)
|
||||
break
|
||||
resp.write_stdin(data.decode())
|
||||
except Exception as e:
|
||||
print(f"\nConnection error: {e}", file=sys.stderr)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# Handle Ctrl+C gracefully
|
||||
print("\nSession terminated by user", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f"\nError in interactive session: {e}", file=sys.stderr)
|
||||
finally:
|
||||
# Reset signal handlers
|
||||
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# Close the connection if it's still open
|
||||
if resp and resp.is_open():
|
||||
try:
|
||||
resp.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Always restore terminal settings
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
|
||||
print("\nConnection closed", file=sys.stderr)
|
||||
|
||||
|
||||
@app.command()
|
||||
def exec(
|
||||
namespace: Optional[str] = typer.Option(
|
||||
None,
|
||||
help="Kubernetes namespace. If not specified, will search for inspectors across all namespaces.",
|
||||
),
|
||||
shell: Optional[str] = typer.Option(
|
||||
"/bin/bash",
|
||||
"--shell",
|
||||
"-s",
|
||||
help="Shell to use for the inspector pod",
|
||||
),
|
||||
):
|
||||
"""
|
||||
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() # or config.load_incluster_config() if running inside a pod
|
||||
client.CoreV1Api()
|
||||
|
||||
pod_name, pod_namespace = get_pod(namespace)
|
||||
exec_command = [
|
||||
"kubectl",
|
||||
"exec",
|
||||
"-it",
|
||||
"-n",
|
||||
pod_namespace,
|
||||
pod_name,
|
||||
"--",
|
||||
"/bin/bash",
|
||||
"-l",
|
||||
]
|
||||
|
||||
os.execvp("kubectl", exec_command)
|
||||
try:
|
||||
pod_name, pod_namespace = get_pod(namespace)
|
||||
exec_command = [
|
||||
"kubectl",
|
||||
"exec",
|
||||
"-it",
|
||||
"-n",
|
||||
pod_namespace,
|
||||
pod_name,
|
||||
"--",
|
||||
shell,
|
||||
"-l",
|
||||
]
|
||||
|
||||
os.execvp("kubectl", exec_command)
|
||||
except Exception as e:
|
||||
print(f"Error executing command with kubectl trying python api: {e}")
|
||||
|
||||
interactive_exec(pod_name, pod_namespace)
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
@ -640,6 +779,11 @@ def create(
|
|||
"--post-init-hooks",
|
||||
help="additional hooks to execute at the start of container initialization",
|
||||
),
|
||||
apply: bool = typer.Option(
|
||||
False,
|
||||
"--apply",
|
||||
help="Automatically apply the changes instead of just echoing them.",
|
||||
),
|
||||
):
|
||||
"""
|
||||
Krack open a Krayt dragon! Create an inspector pod to explore what's inside your volumes.
|
||||
|
|
@ -647,16 +791,15 @@ def create(
|
|||
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
|
||||
selected_namespace = None
|
||||
selected_pod = None
|
||||
selected_namespace = namespace
|
||||
selected_pod = clone
|
||||
|
||||
if namespace is None and clone is not None and "/" in clone:
|
||||
selected_namespace, selected_pod = clone.split("/", 1)
|
||||
elif namespace is not None and clone is not None:
|
||||
selected_namespace = namespace
|
||||
selected_pod = clone
|
||||
|
||||
pods = get_pods(namespace, label_selector=None)
|
||||
get_namespaces(namespace)
|
||||
pods = get_pods(namespace, label_selector="app!=krayt")
|
||||
|
||||
if not pods:
|
||||
typer.echo("No pods found.")
|
||||
raise typer.Exit(1)
|
||||
|
|
@ -672,9 +815,6 @@ def create(
|
|||
typer.echo("No pod selected.")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# typer.echo(f"Selected pod exists: {selected_pod in (p[0] for p in pods)}")
|
||||
# typer.echo(f"Selected pod: {selected_pod} ({selected_namespace})")
|
||||
|
||||
pod_spec = get_pod_spec(selected_pod, selected_namespace)
|
||||
volume_mounts, volumes = get_pod_volumes_and_mounts(pod_spec)
|
||||
|
||||
|
|
@ -694,7 +834,21 @@ def create(
|
|||
)
|
||||
|
||||
# Output the job manifest
|
||||
typer.echo(yaml.dump(clean_dict(inspector_job), sort_keys=False))
|
||||
api_client = client.ApiClient()
|
||||
job_dict = api_client.sanitize_for_serialization(inspector_job)
|
||||
job_yaml = yaml.dump(job_dict, sort_keys=False)
|
||||
|
||||
if apply:
|
||||
batch_api = client.BatchV1Api()
|
||||
job = batch_api.create_namespaced_job(
|
||||
namespace=selected_namespace,
|
||||
body=inspector_job,
|
||||
)
|
||||
print(f"Job {job.metadata.name} created.")
|
||||
return job
|
||||
else:
|
||||
# Just echo the YAML
|
||||
typer.echo(job_yaml)
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
@ -762,10 +916,10 @@ def list_pods():
|
|||
typer.echo(f"{pod} ({namespace})")
|
||||
|
||||
|
||||
def main():
|
||||
setup_environment()
|
||||
app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
# def main():
|
||||
# setup_environment()
|
||||
# app()
|
||||
#
|
||||
#
|
||||
# if __name__ == "__main__":
|
||||
# main()
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ def list():
|
|||
|
||||
|
||||
@app.command()
|
||||
def base(
|
||||
def render(
|
||||
template_name: Optional[str] = typer.Option("base.sh", "--template-name", "-t"),
|
||||
volumes: Optional[List[str]] = typer.Option(
|
||||
None,
|
||||
"--volume",
|
||||
|
|
@ -48,7 +49,6 @@ def base(
|
|||
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,
|
||||
|
|
@ -62,37 +62,37 @@ def base(
|
|||
print(rendered)
|
||||
|
||||
|
||||
@app.command()
|
||||
def install(
|
||||
additional_packages: Optional[List[str]] = typer.Option(
|
||||
..., "--additional-packages", "-ap"
|
||||
),
|
||||
):
|
||||
template_name = "install.sh"
|
||||
template = env.get_template(template_name)
|
||||
rendered = template.render(additional_packages=additional_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)
|
||||
# @app.command()
|
||||
# def install(
|
||||
# additional_packages: Optional[List[str]] = typer.Option(
|
||||
# ..., "--additional-packages", "-ap"
|
||||
# ),
|
||||
# ):
|
||||
# template_name = "install.sh"
|
||||
# template = env.get_template(template_name)
|
||||
# rendered = template.render(additional_packages=additional_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)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ cat <<EOF >/etc/motd
|
|||
└───────────────────────────────────┘
|
||||
|
||||
"Inside every volume lies a pearl of wisdom waiting to be discovered."
|
||||
{%- if volumes %}
|
||||
{%- if mounts %}
|
||||
|
||||
Mounted Volumes:
|
||||
{%- for volume in volumes %}
|
||||
- {{ volume }}
|
||||
{%- for mount in mounts %}
|
||||
- {{ mount.name }}:{{ mount.mount_path }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ def get_release_notes(version):
|
|||
|
||||
You can install krayt using one of these methods:
|
||||
|
||||
> !krayt requires [uv](https://docs.astral.sh/uv/getting-started/installation/) to be installed
|
||||
## pypi
|
||||
|
||||
``` bash
|
||||
pip install krayt
|
||||
```
|
||||
|
||||
### Using i.jpillora.com (recommended)
|
||||
|
||||
|
|
@ -37,8 +41,8 @@ curl -fsSL https://github.com/waylonwalker/krayt/releases/download/v{version}/in
|
|||
|
||||
### Manual download
|
||||
You can also manually download the archive for your platform from the releases page:
|
||||
- [x86_64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-x86_64-unknown-linux-gnu.tar.gz)
|
||||
- [aarch64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-aarch64-unknown-linux-gnu.tar.gz)"""
|
||||
- [x86_64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-x86_64-unknown-linux-gnu)
|
||||
- [aarch64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-aarch64-unknown-linux-gnu)"""
|
||||
|
||||
# Get help output for main command and all subcommands
|
||||
try:
|
||||
|
|
@ -46,17 +50,23 @@ You can also manually download the archive for your platform from the releases p
|
|||
|
||||
# Get main help output
|
||||
main_help = subprocess.check_output(
|
||||
["./krayt.py", "--help"],
|
||||
["krayt", "--help"],
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
)
|
||||
help_outputs.append(("Main Command", main_help))
|
||||
|
||||
# Get help for each subcommand
|
||||
subcommands = ["create", "exec", "clean", "version"]
|
||||
subcommands = [
|
||||
"create",
|
||||
"exec",
|
||||
"clean",
|
||||
"version",
|
||||
"pod",
|
||||
]
|
||||
for cmd in subcommands:
|
||||
cmd_help = subprocess.check_output(
|
||||
["./krayt.py", cmd, "--help"],
|
||||
["krayt", cmd, "--help"],
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -148,11 +148,11 @@ function install {
|
|||
FTYPE=""
|
||||
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"
|
||||
URL="https://github.com/WaylonWalker/krayt/releases/download/v${RELEASE}/krayt-${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"
|
||||
URL="https://github.com/WaylonWalker/krayt/releases/download/v${RELEASE}/krayt-${RELEASE}-aarch64-unknown-linux-gnu.tar.gz"
|
||||
FTYPE=".tar.gz"
|
||||
;;
|
||||
*) fail "No asset for platform ${OS}-${ARCH}" ;;
|
||||
|
|
@ -193,7 +193,7 @@ function install {
|
|||
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"
|
||||
bash -c "$GET $URL" >"krayt_${OS}_${ARCH}" || fail "download failed"
|
||||
else
|
||||
fail "unknown file type: $FTYPE"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue