garage-cluster/tools
James Andariese 441856198f add generate_external_config
generates a config based on the configmap and secrets in the cluster.
this config is intended to be used to add an external node to the
cluster.
2025-04-11 14:42:08 -05:00

226 lines
6.4 KiB
Bash
Executable File

#!/bin/bash
set -e -o pipefail
shq() {(
while [ $# -gt 0 ];do
echo -n "'$(echo -n "$1" | sed -e "s/'/'\"'\"'/g")'"
shift
done
)}
json() {( # json encode the argument
jq -n --arg v "$@" '$v'
)}
GARAGE_NAMESPACE="${GARAGE_NAMESPACE-garage}"
ARGS="$(shq "$@")"
ZERO="$0"
ONE="$1"
ONE_UNDER="$(echo -n "$ONE" | tr - _)"
NL="$(echo)"
enumerate_pods() { # enumerate kubernetes-based garage pods
kubectl get pod -n "$GARAGE_NAMESPACE" -l app=garage -o json | yq -ot '.items[] | [
.metadata.name,
.status.podIP,
.spec.containers[].ports[]|select(.name == "rpc")|.hostPort,
.spec.nodeName
]' | while read -r pod ip port node;do
gnid="$(grun "$pod" node id -q)"
printf "%${#pod}s %25s %s\n" $pod $node "$gnid@$ip:$port"
done
}
get_ids() { # get the public id of all discoverable servers
enumerate_pods | while read -r pod node gnid;do
echo "$gnid"
done
}
iterstats() { # get stats from running garage nodes via iteration over discovered ids
get_ids | while read -r id;do
garage -h "$id" stats
done
}
grun() {
TARGET="$1"
shift
kubectl exec -n "$GARAGE_NAMESPACE" -c garage "$TARGET" -- garage "$@"
}
garage() { # run an arbitrary garage command via a random garage pod
grun ds/garage "$@"
}
generate_secrets() { # generates secrets required to run garage
kubectl create secret -n "$GARAGE_NAMESPACE" generic garage-secrets \
--from-literal=admin-token="$( openssl rand -base64 32 )" \
--from-literal=metrics-token="$( openssl rand -base64 32 )" \
--from-literal=rpc-secret="$( openssl rand -hex 32 )"
}
generate_layout() {( # generates a sample layout, (args are included verbatim, e.g. -t k8s)
enumerate_pods | while read -r pod node gnid;do
kubectl get node/$node -o json | jq -r --arg zero "$ZERO" --arg gnid "${gnid%%@*}" '
[
$zero,
"garage", "layout", "assign", $gnid,
"-t", .metadata.name,
"-c", "\((.metadata.labels["strudelline.net/garage-data-free-bytes"]//"10737418240")|tonumber/1024/1024/1024|floor)G"
]+$ARGS.positional
| @sh' --args -- "$@" | while read -r cmd;do
eval "set $cmd"
printf " %q" "$@" | cut -c 2-
done
done
)}
generate_external_config() {( # generate a config for an external node
jq -rn \
--argjson secret "$(kubectl -n "$GARAGE_NAMESPACE" get secret garage-secrets -o json)" \
--argjson cm "$(kubectl -n "$GARAGE_NAMESPACE" get configmap garage-config -o json)" \
--argjson peers "$(get_ids | jq -R | jq -s)" \
'
# FUNCTIONS
def prettyjson:
if type != "array" then [tojson] else
if length < 2 then [tojson] else
[ (.[0] | "[\n \(tojson),\n")
, (.[1:-1][] | " \(tojson),\n")
, (.[-1] | " \(tojson)\n")
, "]"
]
end
end | add;
# gencfg: generates a toml line which will use a raw json value if possible or a string if not.
# to guarantee a string, double encode it.
def gencfg:
( (.value | fromjson?) // .value) as $v
| (if .key == "bootstrap_peers" then ($v + $peers) else ($v) end) as $v
| "\(.key) = \($v | prettyjson)";
# secval: get the value of the $secret, base64 decoded.
# will emit 0 items (not an empty list!) if no matching key is found.
# try jq -n "[[][],1,[][]]" (and figure it out) for a more clear understanding.
def secval($k): $secret.data | to_entries[] | select(.key == $k) | .value | @base64d;
# SETUP VARIABLES
[ $cm | .data | to_entries[]
, {"key": "rpc_secret", "value": secval("rpc-secret")}
, {"key": "admin.admin_token", "value": secval("admin-token")}
, {"key": "admin.metrics_token", "value": secval("metrics-token")}
] | group_by(.key | test("[.]"))
as $asec
# data format now: [[{k,v},...],[{s.k,v},...]]
| $asec[0]
as $nsec
| [$asec[1][] | (.key | split(".")) as $k | {"section": $k[0], "key": $k[1], "value": .value}]
as $ysec
| [$ysec[] | .section] | unique
as $sections
| [][] # EMIT DATA -- [][] is to allow all outputs to start with ,
# emit unsectioned data
,(
$nsec[] |
gencfg
)
# emit sectioned data
,(
$sections[] as $section | (
"\n[\($section)]"
, ($ysec[] | select(.section == $section) | gencfg)
)
)
'
)}
make_bucket() { # make a bucket along with metadata in a namespace
TARGETBUCKET="$1"
TARGETKEYNAME="${2-$TARGETBUCKET}-app-key"
TARGETNS="${3-$TARGETBUCKET}"
TARGETSECRET="${3-$TARGETKEYNAME}"
kubectl get namespace "$TARGETNS" > /dev/null
garage bucket create "$TARGETBUCKET"
eval "$(
(garage key info "$TARGETKEYNAME" 2> /dev/null || garage key create "$TARGETKEYNAME") | awk '
/^key id: /i {printf("KID=%s\n", $3);}
/^secret key: /i {printf("SK=%s\n", $3);}
')"
garage bucket allow --read --write --owner "$TARGETBUCKET" --key "$TARGETKEYNAME"
kubectl create secret generic \
-n "$TARGETNS" "$TARGETSECRET" \
--type kubernetes.io/basic-auth \
--from-literal=username="$KID" \
--from-literal=password="$SK" \
--from-literal=AWS_ACCESS_KEY_ID="$KID" \
--from-literal=AWS_SECRET_ACCESS_KEY="$SK" \
--from-literal=bucket="$TARGETBUCKET"
kubectl label -n "$TARGETNS" secret/$TARGETSECRET bucket="$TARGETBUCKET" key="$TARGETKEYNAME"
}
connect() { # attempt to connect all nodes
allids="$(get_ids)"
primary="$(echo "$allids" | head -1)"
echo "$allids" | sed 1d | while read -r id;do
garage -h $primary node connect "$id"
done
}
env() { # generate alias to use cluster garage cli.
case "$1" in
bash | sh | zsh | '' )
printf "\nalias garage=%q\n" "$(printf "%q garage" "$PWD/tools")"
;;
*)
1>&2 echo "unknown shell"
exit 1
;;
esac
}
help() {( # this help
exec 1>&2
echo "usage: $0 <subcmd> [options]"
echo
echo GARAGE SUBCOMMANDS:
( 2>&1 garage --help || true ) | awk 'p==1&&/^ / {print lc,$0;next} p==1 {lc=$1;print;} /^SUBCOMMANDS:$/ {p=1}' | while read -r cmd help;do
printf " %-25s %s\n" "garage $cmd" "$help"
done
echo
echo "GARAGE CLUSTER TOOLS:"
grep -E "^[a-z_]+[(][)] {[(]? # .*" "$ZERO" | sed -Ee 's@[(][)] [{][(]? # @: @' |
while read cmd help;do
printf " %-25s %s\n" "$(echo -n $cmd | tr _ -)" "$help"
done
echo
)}
if [ "x$1" = x ];then
help
exit 1
fi
# we translate - to _ to allow enumerate-pods or enumerate_pods.
# it also changes -h to _h.
if [ "x$ONE" = "x-h" ];then
help
exit 0
fi
shift 1
"$ONE_UNDER" "$@"