Skip to content

Instantly share code, notes, and snippets.

@tarik02
Created February 11, 2025 21:29
Show Gist options
  • Save tarik02/855f47e5db4c2dbc8b8e4391d667f4da to your computer and use it in GitHub Desktop.
Save tarik02/855f47e5db4c2dbc8b8e4391d667f4da to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -Eeuo pipefail
# -c, --context string context in the kubeconfig file of the PVC
# -k, --kubeconfig string path of the kubeconfig file of the PVC
# -R, --mount-read-only mount the PVC in ReadOnly mode (default false)
# -n, --namespace string namespace of the PVC
# -d, --dir string dir to mount to (defaults to /mnt)
POSITIONAL_ARGS=()
readonly=no
dir=/mnt
empty_dirs=()
while [[ $# -gt 0 ]]; do
case $1 in
-c|--context)
context=$2
shift 2
;;
-k|--kubeconfig)
kubeconfig=$2
shift 2
;;
-R|--read-only)
readonly=yes
shift
;;
-n|--namespace)
namespace=$2
shift 2
;;
-i|--image)
image=$2
shift 2
;;
-c|--command)
command=$2
shift 2
;;
-d|--dir)
dir=$2
shift 2
;;
-t|--empty-dir)
empty_dirs+=( "$2" )
shift 2
;;
--*|-*)
echo "Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}"
[ -n "${image:-}" ] || image=docker.io/library/alpine:latest
kubectl_args=()
if [ -n "${context:-}" ]; then
kubectl_args+=("--context" "$context")
fi
if [ -n "${kubeconfig:-}" ]; then
kubectl_args+=("--kubeconfig" "$kubeconfig")
fi
if [ -n "${namespace:-}" ]; then
kubectl_args+=("--namespace" "$namespace")
fi
kubectl() {
command kubectl "${kubectl_args[@]}" "$@"
}
list_pvc() {
kubectl get pvc -o name --ignore-not-found | sed "s~^persistentvolumeclaim/~~"
}
pvcs=()
if (( $# == 0 )); then
claim_name="$(list_pvc | fzf)"
pvcs+=( "$claim_name" )
else
for i in "$@"; do
pvcs+=( "$i" )
done
fi
pod_name="pv-attach-$(uuidgen)"
node_names=()
mounts_data=""
mp_counter=0
for pvc in "${pvcs[@]}"; do
if [[ "$pvc" == *:* ]]; then
pvc_name="${pvc%:*}"
pvc_mp="${pvc#*:}"
else
pvc_name="$pvc"
if (( mp_counter == 0 )); then
pvc_mp="/mnt"
else
pvc_mp="/mnt${mp_counter}"
fi
fi
mp_counter=$(( mp_counter + 1))
volume_name=$(kubectl get persistentvolumeclaim "$pvc_name" -o json | jq .spec.volumeName -r)
node_name=$(kubectl -n apps get volumeattachments.storage.k8s.io -o json | jq --arg volumeName "$volume_name" '[.items | .[] | select(.spec.source.persistentVolumeName == $volumeName) | .spec.nodeName] | first // "-"' -r)
node_names+=( "$node_name" )
mount_data="$(jq -n --arg name "$pvc_name" --arg mp "$pvc_mp" '{"name": $name, "mp": $mp}')"
if [ -z "$mounts_data" ]; then
mounts_data="$mount_data"
else
mounts_data="$mounts_data,$mount_data"
fi
done
mounts_data="[$mounts_data]"
template=$(cat <<-'JSON'
{
"apiVersion": "v1",
"spec": ({
"volumes": (
[
$mountsData
| to_entries
| map({
"name": "mnt\(.key)",
"persistentVolumeClaim": {
"claimName": .value.name
}
})
| .[]
] + [
$emptyDirs / " "
| to_entries
| map({ "name": ("emptydir" + (.key | tostring)), "emptyDir": {} })
| .[]
]
),
"containers": [
{
"name": "main",
"workingDir": $mountsData[0].mp,
"image": $image,
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": (
[
$mountsData
| to_entries
| map({
"name": "mnt\(.key)",
"mountPath": .value.mp
})
| .[]
] + [
$emptyDirs / " "
| to_entries
| map({ "mountPath": .value, "name": "emptydir\(.key)" })
| .[]
])
} * (if $command == "" then {} else {
"command": [$command]
} end)
]
} * (if $nodeName == "-" then {} else {
"nodeSelector": {
"kubernetes.io/hostname": $nodeName
}
} end))
}
JSON
)
overrides="$(jq -n \
--arg name "$pod_name" \
--argjson mountsData "$mounts_data" \
--arg image "$image" \
--arg nodeName "$node_name" \
--arg command "${command:-}" \
--arg emptyDirs "${empty_dirs[*]}" \
"$template")"
exec kubectl "${kubectl_args[@]}" run \
--image "$image" \
--overrides "$overrides" \
--restart Never \
"$pod_name" \
--rm \
--stdin \
--tty
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment