commit a07ad6018168a5bfc46e52b1ca605a49b5337d81
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-07-07T09:24:46Z |
| subject | Added kcreate to create debug pods |
commit a07ad6018168a5bfc46e52b1ca605a49b5337d81
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-07-07T09:24:46Z
Added kcreate to create debug pods
---
bash/dist/rbashrc | 2 +-
bash/dist/rbashrc.asc | 10 +-
bash/dist/sources.list | 2 +-
bash/sources/19_k8s_funcs | 282 +++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 288 insertions(+), 8 deletions(-)
diff --git a/bash/dist/rbashrc b/bash/dist/rbashrc
index 7bedf5f..43ad6ef 100755
--- a/bash/dist/rbashrc
+++ b/bash/dist/rbashrc
@@ -18,7 +18,7 @@ RBASH_CONFIG="$RBASH_HOME/config"
RBASH_ENV="$RBASH_HOME/env"
RBASH_MERGED="$RBASH_CACHE_DIR/merged"
RBASH_BIN="$RBASH_HOME/bin"
-RBASH_SOURCES_SUM="e221c016049e096ecef46d03b52864fe99ec84f6dba560707ca4f1a44368e6a9"
+RBASH_SOURCES_SUM="53c4c6eaceb4526d0940873d22ff9978f8eb49d23b6431a5cf814893e086bb23"
# ---- rbash: bash/core/00_env.bash ----
diff --git a/bash/dist/rbashrc.asc b/bash/dist/rbashrc.asc
index 06df515..774203b 100644
--- a/bash/dist/rbashrc.asc
+++ b/bash/dist/rbashrc.asc
@@ -1,8 +1,8 @@
-----BEGIN PGP SIGNATURE-----
-iJEEABYKADkWIQR8HpSNhBJUxWnt+97Vz1+xz60nwwUCakCGbRsUgAAAAAAEAA5t
-YW51MiwyLjUrMS4xMiwwLDMACgkQ1c9fsc+tJ8OCjQD/bHWhIGbbuB2RS4oNu7Ur
-LSIgPDSUqHH1hM3TDT6PijEBANUZYsLndu92wjLa6rtQnv9A6ZTYsBR+P6D24teq
-FbcA
-=6ZYt
+iJEEABYKADkWIQR8HpSNhBJUxWnt+97Vz1+xz60nwwUCakzH+RsUgAAAAAAEAA5t
+YW51MiwyLjUrMS4xMiwwLDMACgkQ1c9fsc+tJ8OgegD/cmVsnne+vp67XFOigKRB
+jPWPdbROVndkYmG4DyqLKVIA/jocAoCry9fe6hY8Iv8FvWjFViyF1d6oCQ+SY4I/
+qMYB
+=zzsS
-----END PGP SIGNATURE-----
diff --git a/bash/dist/sources.list b/bash/dist/sources.list
index 2a2cbc9..29e7d06 100644
--- a/bash/dist/sources.list
+++ b/bash/dist/sources.list
@@ -6,7 +6,7 @@ a0d62f10f39fa988d8ff8ce13a1751488e6bad1662c3b97ab8ef3fa09c702666 sources/15_gpg
4b3c7bdc57fbc94e072e74399cd800c3693ff635fa9064f9e5d05c1ac92e255c sources/16_keystore
570a67928ad4eb2ae343ebc3eee39413989087388fe35e0cd675573add6730b0 sources/17_kcontext
f45e3d068475a40b838336f675948d07b544967d03fbf491abd70e131db38fb0 sources/18_kotp
-9d26839015cf799a0cb2661c49be3f1197fbbc5faf954302eeac93696c85a221 sources/19_k8s_funcs
+7ce7ab97af4aa3f6681981d0d2f687b3eb9b7e7beda5e3b74b651210f7c6fac9 sources/19_k8s_funcs
60c468b70dbcfa2d198362b90278e4a5e86fb1784c85e846402527df7406774d sources/21_win-greps
e007fe16e8ed65c5dca0effcd7227ed52008ca42fe3b6f1b7344a685c17ac02f sources/22_win-setups
b5a488efc0558e2b795a1558e2c91a8aeecc3fd441d437aff3a4749d5eb0bdfc sources/23_win-binaries
diff --git a/bash/sources/19_k8s_funcs b/bash/sources/19_k8s_funcs
index 9ac90f3..5012cca 100644
--- a/bash/sources/19_k8s_funcs
+++ b/bash/sources/19_k8s_funcs
@@ -1,6 +1,5 @@
#!/bin/bash
-# Written with the help of chatgpt o3-mini
function klabels {
local header=
local padding=
@@ -65,3 +64,284 @@ function klabels {
done
done
}
+
+function __kcreate_help {
+ cat <<'EOH'
+usage:
+ kcreate [-n NAMESPACE] [options] TARGET_POD NEW_POD [-exec [--] CMD...]
+
+examples:
+ kcreate -n www app-pod debug-pod
+ kcreate -n www app-pod debug-pod -exec -- bash -l
+ kcreate -n www -c app --rm app-pod debug-pod -exec -- bash -l
+
+options:
+ -n, --namespace NS Namespace. Defaults to current kubectl namespace, then default.
+ -c, --container NAME Container to wait for / exec into.
+ -e, -exec, --exec After creating the Pod, wait for container(s) to run, then kubectl exec -it.
+ --rm Delete the new Pod after the exec command exits.
+ --sleep, --sleep-infinity
+ Start copied containers with: sleep infinity.
+ --force Delete NEW_POD first if it already exists.
+ --dry-run Print the generated Pod manifest instead of creating it.
+ --copy-labels Copy labels from the target Pod. Dangerous: Services may route traffic to it.
+ --copy-annotations Copy annotations from the target Pod. Usually not needed.
+ --same-node Keep spec.nodeName, forcing the new Pod onto the same node.
+ --no-probes Remove liveness/readiness/startup probes from copied containers.
+ --timeout SECONDS Exec wait timeout. Default: 90.
+EOH
+}
+
+function __kcreate_current_namespace {
+ local ns
+
+ ns=$(kubectl config view --minify -o 'jsonpath={..namespace}' 2>/dev/null)
+ if [ -z "$ns" ]; then
+ ns=default
+ fi
+
+ printf '%s\n' "$ns"
+}
+
+function __kcreate_wait_container_running {
+ local ns="$1"
+ local pod="$2"
+ local container="$3"
+ local timeout="$4"
+ local end
+ local ok
+
+ end=$((SECONDS + timeout))
+
+ while [ "$SECONDS" -le "$end" ]; do
+ if [ -n "$container" ]; then
+ ok=$(
+ kubectl -n "$ns" get pod "$pod" -o json 2>/dev/null | jq -r --arg c "$container" '
+ [.status.containerStatuses[]? | select(.name == $c and (.state.running != null))] | length > 0
+ '
+ ) || ok=false
+ else
+ ok=$(
+ kubectl -n "$ns" get pod "$pod" -o json 2>/dev/null | jq -r '
+ [.status.containerStatuses[]?] as $s
+ | ($s | length > 0) and ([ $s[] | select(.state.running != null) ] | length == ($s | length))
+ '
+ ) || ok=false
+ fi
+
+ if [ "$ok" = true ]; then
+ return 0
+ fi
+
+ sleep 1
+ done
+
+ echo "kcreate: timed out waiting for pod/$pod container(s) to be running" >&2
+ kubectl -n "$ns" get pod "$pod" >&2
+ return 1
+}
+
+function kcreate {
+ local ns=
+ local target_pod=
+ local new_pod=
+ local container=
+ local sleep_forever=false
+ local exec_after=false
+ local rm_after=false
+ local force=false
+ local dry_run=false
+ local copy_labels=false
+ local copy_annotations=false
+ local same_node=false
+ local no_probes=false
+ local timeout=90
+ local manifest
+ local rc
+ local exec_cmd=()
+ local exec_args=()
+
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ __kcreate_help
+ return 0
+ ;;
+ -n|--namespace)
+ if [ -z "$2" ]; then echo "kcreate: $1 needs a namespace" >&2; return 2; fi
+ ns="$2"
+ shift 2
+ ;;
+ -c|--container)
+ if [ -z "$2" ]; then echo "kcreate: $1 needs a container name" >&2; return 2; fi
+ container="$2"
+ shift 2
+ ;;
+ --sleep|--sleep-infinity)
+ sleep_forever=true
+ shift
+ ;;
+ -e|-exec|--exec)
+ exec_after=true
+ shift
+ if [ "${1:-}" = "--" ]; then shift; fi
+ exec_cmd=("$@")
+ break
+ ;;
+ --rm)
+ rm_after=true
+ shift
+ ;;
+ --force)
+ force=true
+ shift
+ ;;
+ --dry-run)
+ dry_run=true
+ shift
+ ;;
+ --copy-labels)
+ copy_labels=true
+ shift
+ ;;
+ --copy-annotations)
+ copy_annotations=true
+ shift
+ ;;
+ --same-node)
+ same_node=true
+ shift
+ ;;
+ --no-probes)
+ no_probes=true
+ shift
+ ;;
+ --timeout)
+ if [ -z "$2" ]; then echo "kcreate: $1 needs seconds" >&2; return 2; fi
+ timeout="$2"
+ shift 2
+ ;;
+ -*)
+ echo "kcreate: unknown option: $1" >&2
+ return 2
+ ;;
+ *)
+ if [ -z "$target_pod" ]; then
+ target_pod="$1"
+ elif [ -z "$new_pod" ]; then
+ new_pod="$1"
+ else
+ echo "kcreate: unexpected argument: $1" >&2
+ return 2
+ fi
+ shift
+ ;;
+ esac
+ done
+
+ if [ -z "$ns" ]; then
+ ns=$(__kcreate_current_namespace)
+ fi
+
+ if [ -z "$target_pod" ] || [ -z "$new_pod" ]; then
+ __kcreate_help >&2
+ return 2
+ fi
+
+ if ! command -v kubectl >/dev/null 2>&1; then
+ echo "kcreate: kubectl is required" >&2
+ return 127
+ fi
+
+ if ! command -v jq >/dev/null 2>&1; then
+ echo "kcreate: jq is required" >&2
+ return 127
+ fi
+
+ if ! [[ "$timeout" =~ ^[0-9]+$ ]]; then
+ echo "kcreate: --timeout must be plain seconds, e.g. 90" >&2
+ return 2
+ fi
+
+ manifest=$(
+ kubectl -n "$ns" get pod "$target_pod" -o json | jq \
+ --arg name "$new_pod" \
+ --arg src "$target_pod" \
+ --arg srcns "$ns" \
+ --argjson sleep_forever "$sleep_forever" \
+ --argjson copy_labels "$copy_labels" \
+ --argjson copy_annotations "$copy_annotations" \
+ --argjson same_node "$same_node" \
+ --argjson no_probes "$no_probes" '
+ {
+ apiVersion: "v1",
+ kind: "Pod",
+ metadata: {
+ name: $name,
+ labels: (if $copy_labels then (.metadata.labels // {}) else {"app.kubernetes.io/managed-by": "kcreate"} end),
+ annotations: (if $copy_annotations then (.metadata.annotations // {}) else {} end)
+ },
+ spec: .spec
+ }
+ | .metadata.annotations["kcreate/source-pod"] = $src
+ | .metadata.annotations["kcreate/source-namespace"] = $srcns
+ | del(.metadata.labels["pod-template-hash"]?)
+ | del(.spec.priority?)
+ | del(.spec.serviceAccount?)
+ | if $same_node then . else del(.spec.nodeName?) end
+ | if $no_probes then
+ (.spec.containers |= map(del(.livenessProbe?, .readinessProbe?, .startupProbe?)))
+ else
+ .
+ end
+ | if $sleep_forever then
+ (.spec.containers |= map(
+ .command = ["sleep"]
+ | .args = ["infinity"]
+ | del(.livenessProbe?, .readinessProbe?, .startupProbe?)
+ ))
+ else
+ .
+ end
+ '
+ ) || return $?
+
+ if [ "$dry_run" = true ]; then
+ printf '%s\n' "$manifest"
+ return 0
+ fi
+
+ if [ "$copy_labels" = true ]; then
+ echo "kcreate: warning: --copy-labels may let Services route traffic to pod/$new_pod" >&2
+ fi
+
+ if [ "$force" = true ]; then
+ kubectl -n "$ns" delete pod "$new_pod" --ignore-not-found --wait=true || return $?
+ fi
+
+ printf '%s\n' "$manifest" | kubectl -n "$ns" create -f - || return $?
+
+ if [ "$exec_after" != true ]; then
+ kubectl -n "$ns" get pod "$new_pod"
+ return $?
+ fi
+
+ if [ "${#exec_cmd[@]}" -eq 0 ]; then
+ exec_cmd=(sh)
+ fi
+
+ __kcreate_wait_container_running "$ns" "$new_pod" "$container" "$timeout" || return $?
+
+ if [ -n "$container" ]; then
+ exec_args=(-c "$container")
+ fi
+
+ kubectl -n "$ns" exec -it "$new_pod" "${exec_args[@]}" -- "${exec_cmd[@]}"
+ rc=$?
+
+ if [ "$rm_after" = true ]; then
+ kubectl -n "$ns" delete pod "$new_pod" --ignore-not-found >/dev/null
+ fi
+
+ return "$rc"
+}