titan-iac/scripts/wallet_sui_setup.fish

276 lines
9.1 KiB
Fish

### --------- helpers ----------
function _need
for c in $argv
if not type -q $c
echo (set_color red)"Error:"(set_color normal)" missing command: $c" >&2
return 1
end
end
end
function _banner -a MSG
set_color cyan; echo; echo "==> $MSG"; set_color normal
end
function _k8s_name -a S
set s (string lower -- $S)
set s (string replace -ar -- '[^a-z0-9-]' '-' $s)
set s (string replace -r -- '^-+|(-)+$' '$1' $s)
echo $s
end
function _require -a WHAT VAL
if test -z "$VAL"
echo (set_color red)"Error:"(set_color normal)" $WHAT cannot be empty." >&2
return 1
end
end
function _sc_list
kubectl get storageclass -o json 2>/dev/null | jq -r '
.items[] | [
.metadata.name,
.provisioner,
(.reclaimPolicy // "Delete"),
( .metadata.annotations["storageclass.kubernetes.io/is-default-class"] == "true"
or .metadata.annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true")
] | @tsv'
end
function _choose_sc
set lines (_sc_list)
if test (count $lines) -eq 0
echo (set_color red)"Error:"(set_color normal)" No StorageClasses found." >&2
return 1
end
echo "" >&2
echo "==> Available StorageClasses" >&2
echo " # | name | provisioner | reclaim | default" >&2
echo "----+----------------------+------------------------+---------+--------" >&2
set i 1
for l in $lines
set name (echo $l | awk -F'\t' '{print $1}')
set prov (echo $l | awk -F'\t' '{print $2}')
set rec (echo $l | awk -F'\t' '{print $3}')
set def (echo $l | awk -F'\t' '{print $4}')
printf " %2d | %-20s | %-22s | %-7s | %s\n" $i $name $prov $rec $def >&2
set i (math $i + 1)
end
set prefer (printf "%s\n" $lines | awk -F'\t' '$4=="true"{print $1}' | head -n1)
if test -z "$prefer"
set prefer (printf "%s\n" $lines | awk -F'\t' '{print $1}' | head -n1)
end
read -P "Pick StorageClass by number or name (blank = $prefer): " choice
if test -z "$choice"
echo $prefer
return 0
end
if string match -qr '^[0-9]+$' -- $choice
set idx (math $choice)
if test $idx -lt 1 -o $idx -gt (count $lines)
echo (set_color red)"Error:"(set_color normal)" Choice out of range." >&2
return 1
end
printf "%s\n" $lines[$idx] | awk -F'\t' '{print $1}'
else
printf "%s\n" $lines | awk -F'\t' -v want="$choice" '$1==want{print $1}'
end
end
### --------- main workflow ----------
function suiwallet_bootstrap --description "Create a Sui wallet (PVC+Deployment) and generate a new address"
_need kubectl jq awk; or return 1
_banner "Sui wallet bootstrap"
read -P "Namespace [crypto]: " ns
if test -z "$ns"; set ns crypto; end
set ns (_k8s_name $ns)
echo "Ensuring namespace '$ns' exists…"
if not kubectl get ns $ns >/dev/null 2>&1
kubectl create ns $ns; or return 1
end
kubectl get storageclass 2>/dev/null
echo "TIP: Prefer a Retain StorageClass so deleting the pod won't delete keys."
set sc (_choose_sc); or return 1
read -P "Wallet name (e.g. 'brad' → wallet-sui-brad): " wallet_raw
_require "Wallet name" $wallet_raw; or return 1
set base (_k8s_name $wallet_raw)
set app "wallet-sui-$base"
set pvc $app
read -P "Network [mainnet|testnet|devnet] [mainnet]: " net
if test -z "$net"; set net mainnet; end
switch $net
case mainnet
set rpc_url https://fullnode.mainnet.sui.io:443
case testnet
set rpc_url https://fullnode.testnet.sui.io:443
case devnet
set rpc_url https://fullnode.devnet.sui.io:443
case '*'
echo (set_color red)"Error:"(set_color normal)" network must be mainnet|testnet|devnet"; return 1
end
read -P "Container image [mysten/sui-tools:latest]: " image
if test -z "$image"; set image mysten/sui-tools:latest; end
read -P "Address alias to create [main]: " alias
if test -z "$alias"; set alias main; end
read -P "Mnemonic word length [24]: " wl
if test -z "$wl"; set wl 24; end
read -P "Store mnemonic as a Kubernetes Secret? (NOT recommended) (y/N): " store_mn
set store_mn (string lower -- $store_mn)
_banner "Summary"
echo " Namespace: $ns"
echo " App name: $app"
echo " StorageClass: $sc"
echo " PVC: $pvc"
echo " Image: $image"
echo " Network: $net"
echo " RPC URL: $rpc_url"
echo " New alias: $alias (word length: $wl)"
read -P "Proceed? [y/N]: " ok
if not string match -qi 'y*' -- $ok
echo "Aborted."; return 1
end
_banner "Applying PVC"
kubectl -n $ns get pvc $pvc >/dev/null 2>&1; or begin
printf "%s\n" \
"apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: $pvc
namespace: $ns
spec:
accessModes: [\"ReadWriteOnce\"]
storageClassName: $sc
resources:
requests:
storage: 1Gi" | kubectl apply -f -; or return 1
end
_banner "Applying Deployment"
printf "%s\n" \
"apiVersion: apps/v1
kind: Deployment
metadata:
name: $app
namespace: $ns
labels: { app: $app }
spec:
replicas: 1
selector: { matchLabels: { app: $app } }
template:
metadata:
labels: { app: $app }
spec:
containers:
- name: sui-tools
image: $image
imagePullPolicy: IfNotPresent
command: [\"bash\",\"-lc\",\"sleep infinity\"]
volumeMounts:
- name: data
mountPath: /root/.sui
resources:
requests: { cpu: 50m, memory: 128Mi }
limits: { cpu: 1, memory: 512Mi }
volumes:
- name: data
persistentVolumeClaim: { claimName: $pvc }" | kubectl apply -f -; or return 1
_banner "Waiting for rollout"
kubectl -n $ns rollout status deploy/$app --timeout=120s; or return 1
_banner "Initializing Sui client env ($net) and creating a new address"
# new-env: writes client.yaml; new-address: writes to sui.keystore and prints the phrase
set OUT (mktemp)
kubectl -n $ns exec deploy/$app -- bash -lc \
(printf 'mkdir -p /root/.sui/sui_config ;
sui client new-env --alias %s --rpc %s >/dev/null 2>&1 || true ;
sui client new-address ed25519 --word-length %s --alias %s' \
$net $rpc_url $wl $alias) | tee $OUT
set ADDR (kubectl -n $ns exec deploy/$app -- bash -lc \
(printf 'sui client addresses | awk "/%s/{print \$NF}" | tail -n1' $alias))
echo
echo (set_color yellow)"IMPORTANT — Secret Recovery Phrase (write down & store offline):"(set_color normal)
grep -E 'Secret Recovery Phrase|Mnemon' $OUT -A2 | sed -e 's/^\s\+//'
echo
echo "Address alias '$alias' appears to be: $ADDR"
rm -f $OUT
if test "$store_mn" = "y" -o "$store_mn" = "yes"
set MN (kubectl -n $ns exec deploy/$app -- bash -lc \
(printf 'sui client addresses >/dev/null 2>&1 ; sui client new-address ed25519 --word-length %s --alias %s --json 2>/dev/null | jq -r .secretRecoveryPhrase || true' $wl "${alias}-dup"))
if test -n "$MN" -a "$MN" != "null"
kubectl -n $ns create secret generic {$app}-mnemonic \
--from-literal=mnemonic="$MN" --dry-run=client -o yaml | kubectl -n $ns apply -f -
echo (set_color yellow)"Stored mnemonic in Secret $ns/{$app}-mnemonic. Treat your cluster as sensitive."(set_color normal)
# clean the duplicate alias we created to harvest JSON
kubectl -n $ns exec deploy/$app -- bash -lc "sui client remove-address --alias ${alias}-dup >/dev/null 2>&1 || true" >/dev/null 2>&1
else
echo (set_color red)"Warning:"(set_color normal)" could not capture mnemonic via --json, not storing."
end
end
echo
echo "Done. Files inside the pod:"
kubectl -n $ns exec deploy/$app -- bash -lc 'ls -l /root/.sui/sui_config || true'
echo
echo "Useful commands:"
echo " suiwallet_addresses $ns $app"
echo " suiwallet_export_keystore $ns $app ./sui.keystore.$base"
echo " kubectl -n $ns exec -it deploy/$app -- bash # interactive CLI"
end
### --------- utilities ----------
function suiwallet_addresses -a NS APP
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-sui-main; end
kubectl -n $NS exec deploy/$APP -- bash -lc 'sui client addresses || true'
end
function suiwallet_export_keystore -a NS APP DEST
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-sui-main; end
if test -z "$DEST"; set DEST ./sui.keystore; end
echo "Copying keystore to $DEST (keep it safe!)"
kubectl -n $NS cp deploy/$APP:/root/.sui/sui_config/sui.keystore $DEST
end
function suiwallet_status -a NS APP
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-sui-main; end
kubectl -n $NS get deploy,po,pvc -l app=$APP -o wide
end
function suiwallet_stop -a NS APP
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-sui-main; end
kubectl -n $NS delete deploy $APP --ignore-not-found
echo "Stopped $NS/$APP (PVC kept)."
end
function suiwallet_purge -a NS APP PVC
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-sui-main; end
if test -z "$PVC"; set PVC $APP; end
echo "Type to confirm: PURGE $NS $PVC"
read CONFIRM
if test "$CONFIRM" != "PURGE $NS $PVC"
echo "Aborted."; return 1
end
kubectl -n $NS delete deploy $APP --ignore-not-found
kubectl -n $NS delete pvc $PVC --ignore-not-found --wait=true
end