2025-08-14 20:37:30 -05:00
|
|
|
### --------- helpers ----------
|
2025-08-19 01:06:45 -05:00
|
|
|
function _need --description "ensure a command exists"
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
function _banner --description "pretty section header" -a MSG
|
2025-08-14 20:37:30 -05:00
|
|
|
set_color cyan; echo; echo "==> $MSG"; set_color normal
|
|
|
|
|
end
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# 32-char alphanumeric generator (fast, no shell-specials)
|
|
|
|
|
function _rand_alnum32
|
|
|
|
|
if type -q openssl
|
|
|
|
|
openssl rand -base64 48 2>/dev/null | tr -dc 'A-Za-z0-9' | head -c 32
|
|
|
|
|
else
|
|
|
|
|
dd if=/dev/urandom bs=48 count=1 2>/dev/null | base64 | tr -dc 'A-Za-z0-9' | head -c 32
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Default image chooser (you should override with your own multi-arch image)
|
|
|
|
|
function _sui_default_image -a NET
|
|
|
|
|
echo registry.bstein.dev/infra/sui-tools:1.53.2
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Convert any string to a k8s-safe name (RFC-1123 label-ish)
|
|
|
|
|
function _k8s_name --description "sanitize to RFC-1123 label" -a S
|
2025-08-14 20:37:30 -05:00
|
|
|
set s (string lower -- $S)
|
|
|
|
|
set s (string replace -ar -- '[^a-z0-9-]' '-' $s)
|
2025-08-19 01:06:45 -05:00
|
|
|
set s (string replace -r -- '^-+' '' $s)
|
|
|
|
|
set s (string replace -r -- '-+$' '' $s)
|
2025-08-14 20:37:30 -05:00
|
|
|
echo $s
|
|
|
|
|
end
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# Simple non-empty check
|
|
|
|
|
function _require --description "require a non-empty value" -a WHAT VAL
|
2025-08-14 20:37:30 -05:00
|
|
|
if test -z "$VAL"
|
2025-08-19 01:06:45 -05:00
|
|
|
echo (set_color red)"Error:"(set_color normal)" $WHAT cannot be empty. Aborting." >&2
|
2025-08-14 20:37:30 -05:00
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
function _sc_list --description "list storageclasses as menu"
|
|
|
|
|
kubectl get storageclass -o json | jq -r '
|
|
|
|
|
.items[]
|
|
|
|
|
| {name: .metadata.name,
|
|
|
|
|
provisioner: .provisioner,
|
|
|
|
|
reclaim: (.reclaimPolicy // "Delete"),
|
|
|
|
|
default: ( .metadata.annotations["storageclass.kubernetes.io/is-default-class"] == "true"
|
|
|
|
|
or .metadata.annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true") }
|
|
|
|
|
| "\(.name)\t\(.provisioner)\t\(.reclaim)\t\((.default|tostring))"
|
|
|
|
|
' 2>/dev/null
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# Choose StorageClass by index or name
|
|
|
|
|
function _choose_sc --description "interactive StorageClass picker"
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
2025-08-19 01:06:45 -05:00
|
|
|
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
2025-08-19 01:06:45 -05:00
|
|
|
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
2025-08-19 01:06:45 -05:00
|
|
|
|
2025-08-14 20:37:30 -05:00
|
|
|
read -P "Pick StorageClass by number or name (blank = $prefer): " choice
|
2025-08-19 01:06:45 -05:00
|
|
|
|
2025-08-14 20:37:30 -05:00
|
|
|
if test -z "$choice"
|
2025-08-19 01:06:45 -05:00
|
|
|
echo "Using StorageClass: $prefer" >&2
|
2025-08-14 20:37:30 -05:00
|
|
|
echo $prefer
|
|
|
|
|
return 0
|
|
|
|
|
end
|
2025-08-19 01:06:45 -05:00
|
|
|
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
2025-08-19 01:06:45 -05:00
|
|
|
set sel (printf "%s\n" $lines[$idx] | awk -F'\t' '{print $1}')
|
|
|
|
|
echo "Using StorageClass: $sel" >&2
|
|
|
|
|
echo $sel
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
set found (printf "%s\n" $lines | awk -F'\t' -v want="$choice" '$1==want{print $1}')
|
|
|
|
|
if test -z "$found"
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" No StorageClass named '$choice'." >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
echo "Using StorageClass: $found" >&2
|
|
|
|
|
echo $found
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# ---- probe Sui JSON-RPC endpoint
|
|
|
|
|
function _probe_sui_rpc --description "probe Sui JSON-RPC endpoint" -a URL
|
|
|
|
|
if test -z "$URL"
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" _probe_sui_rpc needs an URL" >&2
|
|
|
|
|
return 1
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
2025-08-19 01:06:45 -05:00
|
|
|
set payload '{"jsonrpc":"2.0","id":1,"method":"sui_getLatestCheckpointSequenceNumber","params":[]}'
|
|
|
|
|
set tmp (mktemp)
|
|
|
|
|
set code (curl -sS -o $tmp -w "%{http_code}" -H "Content-Type: application/json" -d "$payload" "$URL")
|
|
|
|
|
set rc $status
|
|
|
|
|
set body (cat $tmp); rm -f $tmp
|
|
|
|
|
if test $rc -ne 0
|
|
|
|
|
echo (set_color red)"Transport error probing $URL (curl rc=$rc)."(set_color normal) >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
if test "$code" != "200"
|
|
|
|
|
echo (set_color red)"HTTP $code from $URL"(set_color normal) >&2
|
|
|
|
|
echo $body >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
set seq (printf "%s" $body | jq -r '.result // empty' 2>/dev/null)
|
|
|
|
|
if test -z "$seq"
|
|
|
|
|
echo (set_color red)"No result from RPC probe at $URL."(set_color normal) >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
echo "Sui RPC OK: latest checkpoint seq=$seq"
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
### --------- main workflow ----------
|
2025-08-19 01:06:45 -05:00
|
|
|
function suiwallet_bootstrap --description "Interactive setup of a Sui wallet pod with PVC; creates address and prints recovery phrase"
|
|
|
|
|
_need kubectl jq curl awk; or return 1
|
2025-08-14 20:37:30 -05:00
|
|
|
|
|
|
|
|
_banner "Sui wallet bootstrap"
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# --- Namespace
|
|
|
|
|
read -P "Namespace [crypto]: " ns_raw
|
|
|
|
|
if test -z "$ns_raw"; set ns_raw crypto; end
|
|
|
|
|
set ns (_k8s_name $ns_raw)
|
|
|
|
|
_require "Namespace" $ns; or return 1
|
2025-08-14 20:37:30 -05:00
|
|
|
|
|
|
|
|
echo "Ensuring namespace '$ns' exists…"
|
2025-08-19 01:06:45 -05:00
|
|
|
if kubectl get ns $ns >/dev/null 2>&1
|
|
|
|
|
echo "Namespace $ns already exists."
|
|
|
|
|
else
|
2025-08-14 20:37:30 -05:00
|
|
|
kubectl create ns $ns; or return 1
|
2025-08-19 01:06:45 -05:00
|
|
|
echo "Created namespace $ns."
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# --- StorageClass
|
|
|
|
|
kubectl get storageclass
|
|
|
|
|
echo "TIP: Prefer a class with reclaimPolicy=Retain so a PVC delete won't delete the PV."
|
2025-08-14 20:37:30 -05:00
|
|
|
set sc (_choose_sc); or return 1
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# --- Unified name (prefix wallet-sui-)
|
|
|
|
|
read -P "Wallet name (no spaces, e.g. 'brad', base will be wallet-sui-brad): " wallet_raw
|
2025-08-14 20:37:30 -05:00
|
|
|
_require "Wallet name" $wallet_raw; or return 1
|
2025-08-19 01:06:45 -05:00
|
|
|
if string match -rq '\s' -- $wallet_raw
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" Wallet name cannot contain spaces." >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
2025-08-14 20:37:30 -05:00
|
|
|
set base (_k8s_name $wallet_raw)
|
2025-08-19 01:06:45 -05:00
|
|
|
set base (string replace -r '^wallet-sui-' '' -- $base)
|
|
|
|
|
set name "wallet-sui-$base"
|
|
|
|
|
|
|
|
|
|
# Bind all k8s object names to the unified base
|
|
|
|
|
set pvc_name $name
|
|
|
|
|
set app_name $name
|
|
|
|
|
|
|
|
|
|
# --- Detect existing resources (idempotence)
|
|
|
|
|
set exists_any 0
|
|
|
|
|
set found_list
|
|
|
|
|
for r in "deploy/$app_name" (printf "netpol/%s-deny-ingress" $app_name)
|
|
|
|
|
if kubectl -n $ns get $r >/dev/null 2>&1
|
|
|
|
|
set -a found_list $r
|
|
|
|
|
set exists_any 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
|
|
|
|
|
set -a found_list "pvc/$pvc_name"
|
|
|
|
|
set exists_any 1
|
|
|
|
|
end
|
|
|
|
|
if kubectl -n $ns get secret (printf "%s-mnemonic" $app_name) >/dev/null 2>&1
|
|
|
|
|
set -a found_list (printf "secret/%s-mnemonic" $app_name)
|
|
|
|
|
set exists_any 1
|
|
|
|
|
end
|
|
|
|
|
if test $exists_any -eq 1
|
|
|
|
|
_banner "Existing resources detected for $name"
|
|
|
|
|
for r in $found_list
|
|
|
|
|
echo " - $ns/$r"
|
|
|
|
|
end
|
|
|
|
|
read -P "Remove EVERYTHING (deploy/netpol/PVC/secret) and recreate? (y/N): " wipe
|
|
|
|
|
if string match -qi 'y*' -- $wipe
|
|
|
|
|
kubectl -n $ns delete deploy $app_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete netpol (printf "%s-deny-ingress" $app_name) --ignore-not-found --wait=true >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete pvc $pvc_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete secret (printf "%s-mnemonic" $app_name) --ignore-not-found >/dev/null 2>&1; or true
|
|
|
|
|
echo "Cleaned up previous resources."
|
|
|
|
|
else
|
|
|
|
|
echo "Reusing existing resources; they will be applied in-place."
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- Node scheduling (worker-only by default)
|
|
|
|
|
read -P "Schedule only on worker nodes? (y/N) [y]: " pin_workers
|
|
|
|
|
if test -z "$pin_workers"; set pin_workers y; end
|
|
|
|
|
set use_pin 0
|
|
|
|
|
if string match -qi 'y*' -- $pin_workers
|
|
|
|
|
set use_pin 1
|
|
|
|
|
read -P "Worker label key [node-role.kubernetes.io/worker]: " node_label_key
|
|
|
|
|
if test -z "$node_label_key"; set node_label_key node-role.kubernetes.io/worker; end
|
|
|
|
|
read -P "Worker label value [true]: " node_label_val
|
|
|
|
|
if test -z "$node_label_val"; set node_label_val true; end
|
|
|
|
|
end
|
2025-08-14 20:37:30 -05:00
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# --- Network and RPC
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
_banner "Probing Sui RPC endpoint"
|
|
|
|
|
if not _probe_sui_rpc $rpc_url
|
|
|
|
|
echo (set_color red)"Aborting due to RPC probe failure."(set_color normal)
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- Image and address alias
|
|
|
|
|
set image_default (_sui_default_image $net)
|
|
|
|
|
echo "NOTE: Prefer a multi-arch image you built (see instructions below)."
|
|
|
|
|
read -P "Container image [$image_default]: " image
|
|
|
|
|
if test -z "$image"; set image $image_default; end
|
2025-08-14 20:37:30 -05:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# --- Optional NetworkPolicy
|
|
|
|
|
read -P "Add NetworkPolicy (deny all ingress; allow egress) ? (y/N): " want_np
|
|
|
|
|
set want_np (string lower -- $want_np)
|
|
|
|
|
|
2025-08-14 20:37:30 -05:00
|
|
|
_banner "Summary"
|
|
|
|
|
echo " Namespace: $ns"
|
|
|
|
|
echo " StorageClass: $sc"
|
2025-08-19 01:06:45 -05:00
|
|
|
echo " Base name: $name"
|
|
|
|
|
echo " PVC: $pvc_name"
|
|
|
|
|
echo " App/Deployment:$app_name"
|
2025-08-14 20:37:30 -05:00
|
|
|
echo " Image: $image"
|
|
|
|
|
echo " Network: $net"
|
|
|
|
|
echo " RPC URL: $rpc_url"
|
|
|
|
|
echo " New alias: $alias (word length: $wl)"
|
2025-08-19 01:06:45 -05:00
|
|
|
if test $use_pin -eq 1
|
|
|
|
|
echo " NodeSelector: $node_label_key=$node_label_val"
|
|
|
|
|
end
|
|
|
|
|
if string match -qi 'y*' -- $want_np
|
|
|
|
|
echo " NetworkPolicy: deny-all ingress (egress allowed)"
|
|
|
|
|
end
|
|
|
|
|
read -P "Proceed? [y/N]: " proceed
|
|
|
|
|
if not string match -qi 'y*' -- $proceed
|
|
|
|
|
echo (set_color yellow)"Aborted by user at confirmation step."(set_color normal)
|
|
|
|
|
return 1
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
_banner "Applying PVC"
|
2025-08-19 01:06:45 -05:00
|
|
|
if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
|
|
|
|
|
echo "PVC $ns/$pvc_name exists; keeping as-is."
|
|
|
|
|
else
|
|
|
|
|
set pvcfile (mktemp -t $pvc_name-pvc.XXXX.yaml)
|
|
|
|
|
begin
|
|
|
|
|
echo "apiVersion: v1"
|
|
|
|
|
echo "kind: PersistentVolumeClaim"
|
|
|
|
|
echo "metadata:"
|
|
|
|
|
echo " name: $pvc_name"
|
|
|
|
|
echo " namespace: $ns"
|
|
|
|
|
echo "spec:"
|
|
|
|
|
echo " accessModes: [\"ReadWriteOnce\"]"
|
|
|
|
|
echo " storageClassName: $sc"
|
|
|
|
|
echo " resources:"
|
|
|
|
|
echo " requests:"
|
|
|
|
|
echo " storage: 1Gi"
|
|
|
|
|
end > $pvcfile
|
|
|
|
|
echo "--- PVC manifest ($pvcfile) ---"
|
|
|
|
|
cat $pvcfile
|
|
|
|
|
kubectl apply -f $pvcfile; or return 1
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
_banner "Applying Deployment"
|
2025-08-19 01:06:45 -05:00
|
|
|
set dply (mktemp -t $app_name-deploy.XXXX.yaml)
|
|
|
|
|
begin
|
|
|
|
|
echo "apiVersion: apps/v1"
|
|
|
|
|
echo "kind: Deployment"
|
|
|
|
|
echo "metadata:"
|
|
|
|
|
echo " name: $app_name"
|
|
|
|
|
echo " namespace: $ns"
|
|
|
|
|
echo " labels: { app: $app_name }"
|
|
|
|
|
echo "spec:"
|
|
|
|
|
echo " replicas: 1"
|
|
|
|
|
echo " strategy: { type: Recreate }"
|
|
|
|
|
echo " selector: { matchLabels: { app: $app_name } }"
|
|
|
|
|
echo " template:"
|
|
|
|
|
echo " metadata:"
|
|
|
|
|
echo " labels: { app: $app_name }"
|
|
|
|
|
echo " spec:"
|
|
|
|
|
if test $use_pin -eq 1
|
|
|
|
|
echo " nodeSelector:"
|
|
|
|
|
echo " $node_label_key: \"$node_label_val\""
|
|
|
|
|
end
|
|
|
|
|
echo " securityContext:"
|
|
|
|
|
echo " fsGroup: 1000"
|
|
|
|
|
echo " fsGroupChangePolicy: OnRootMismatch"
|
|
|
|
|
echo " initContainers:"
|
|
|
|
|
echo " - name: volume-permissions"
|
|
|
|
|
echo " image: busybox:1.36"
|
|
|
|
|
echo " command: [\"/bin/sh\",\"-lc\",\"mkdir -p /home/sui/.sui/sui_config && chown -R 1000:1000 /home/sui/.sui && chmod -R 0770 /home/sui/.sui\"]"
|
|
|
|
|
echo " securityContext: { runAsUser: 0 }"
|
|
|
|
|
echo " volumeMounts: [ { name: data, mountPath: /home/sui/.sui } ]"
|
|
|
|
|
echo " containers:"
|
|
|
|
|
echo " - name: sui-tools"
|
|
|
|
|
echo " image: $image"
|
|
|
|
|
echo " imagePullPolicy: IfNotPresent"
|
|
|
|
|
echo " command: [\"/bin/sh\",\"-lc\"]"
|
|
|
|
|
echo " args:"
|
|
|
|
|
echo " - |"
|
|
|
|
|
echo " # keep container ready for execs"
|
|
|
|
|
echo " sleep infinity"
|
|
|
|
|
echo " volumeMounts:"
|
|
|
|
|
echo " - name: data"
|
|
|
|
|
echo " mountPath: /home/sui/.sui"
|
|
|
|
|
echo " resources:"
|
|
|
|
|
echo " requests: { cpu: \"50m\", memory: \"128Mi\" }"
|
|
|
|
|
echo " limits: { cpu: \"1\", memory: \"512Mi\" }"
|
|
|
|
|
echo " volumes:"
|
|
|
|
|
echo " - name: data"
|
|
|
|
|
echo " persistentVolumeClaim: { claimName: $pvc_name }"
|
|
|
|
|
end > $dply
|
|
|
|
|
echo "--- Deployment manifest ($dply) ---"
|
|
|
|
|
head -n 70 $dply
|
|
|
|
|
echo "..."
|
|
|
|
|
kubectl apply -f $dply; or return 1
|
|
|
|
|
|
|
|
|
|
if string match -qi 'y*' -- $want_np
|
|
|
|
|
_banner "Applying NetworkPolicy (deny-all ingress; allow all egress)"
|
|
|
|
|
set np (mktemp -t $app_name-netpol.XXXX.yaml)
|
|
|
|
|
begin
|
|
|
|
|
echo "apiVersion: networking.k8s.io/v1"
|
|
|
|
|
echo "kind: NetworkPolicy"
|
|
|
|
|
echo "metadata:"
|
|
|
|
|
printf " name: %s-deny-ingress\n" $app_name
|
|
|
|
|
echo " namespace: $ns"
|
|
|
|
|
echo "spec:"
|
|
|
|
|
echo " podSelector: { matchLabels: { app: $app_name } }"
|
|
|
|
|
echo " policyTypes: [\"Ingress\",\"Egress\"]"
|
|
|
|
|
echo " ingress: []"
|
|
|
|
|
echo " egress:"
|
|
|
|
|
echo " - {}"
|
|
|
|
|
end > $np
|
|
|
|
|
echo "--- NetworkPolicy manifest ($np) ---"
|
|
|
|
|
cat $np
|
|
|
|
|
kubectl apply -f $np; or return 1
|
|
|
|
|
end
|
2025-08-14 20:37:30 -05:00
|
|
|
|
|
|
|
|
_banner "Waiting for rollout"
|
2025-08-19 01:06:45 -05:00
|
|
|
if not kubectl -n $ns rollout status deploy/$app_name --timeout=300s
|
|
|
|
|
echo (set_color red)"Rollout did not finish in time. Recent events:"(set_color normal) >&2
|
|
|
|
|
kubectl -n $ns get events --sort-by=.lastTimestamp | tail -n 40
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
_banner "Validating Sui CLI inside the pod"
|
|
|
|
|
set ARCH (kubectl -n $ns exec deploy/$app_name -- sh -lc 'uname -m' 2>/dev/null)
|
|
|
|
|
set GLIBC (kubectl -n $ns exec deploy/$app_name -- sh -lc 'ldd --version 2>&1 | head -n1 || true' 2>/dev/null)
|
|
|
|
|
set SUIV (kubectl -n $ns exec deploy/$app_name -- sh -lc 'sui --version 2>&1 || true' 2>/dev/null)
|
|
|
|
|
echo "Node arch: $ARCH"
|
|
|
|
|
echo "GLIBC: $GLIBC"
|
|
|
|
|
echo "sui: $SUIV"
|
|
|
|
|
|
|
|
|
|
if test -z "$SUIV"; or string match -q "*not found*" -- $SUIV
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" 'sui' did not run inside the pod."
|
|
|
|
|
echo "If you see GLIBC < 2.38 here, rebuild the image on Ubuntu 24.04 as shown in section A."
|
|
|
|
|
return 1
|
|
|
|
|
end
|
2025-08-14 20:37:30 -05:00
|
|
|
|
|
|
|
|
_banner "Initializing Sui client env ($net) and creating a new address"
|
2025-08-19 01:06:45 -05:00
|
|
|
# Create/switch env, then create a new address with alias.
|
|
|
|
|
# Try JSON first; fall back to parsing text.
|
|
|
|
|
set TMP (mktemp)
|
|
|
|
|
|
|
|
|
|
# Ensure config dir exists (inside the pod) and env points to your chosen RPC.
|
|
|
|
|
kubectl -n $ns exec deploy/$app_name -- sh -lc \
|
|
|
|
|
(printf 'mkdir -p /home/sui/.sui/sui_config ;
|
2025-08-14 20:37:30 -05:00
|
|
|
sui client new-env --alias %s --rpc %s >/dev/null 2>&1 || true ;
|
2025-08-19 01:06:45 -05:00
|
|
|
sui client switch --env %s >/dev/null 2>&1 || true' $net $rpc_url $net)
|
2025-08-14 20:37:30 -05:00
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# Try JSON (preferred)
|
|
|
|
|
set JSON (kubectl -n $ns exec deploy/$app_name -- sh -lc \
|
|
|
|
|
(printf 'sui client new-address ed25519 --word-length %s --alias %s --json 2>/dev/null || true' $wl $alias))
|
|
|
|
|
|
|
|
|
|
set MN (printf "%s" $JSON | jq -r '.secretRecoveryPhrase // .mnemonic // .mnemonicPhrase // empty' 2>/dev/null)
|
|
|
|
|
set ADDR (printf "%s" $JSON | jq -r '.address // empty' 2>/dev/null)
|
|
|
|
|
|
|
|
|
|
if test -n "$MN"
|
|
|
|
|
kubectl -n $ns logs deploy/$app_name --since=10s >/dev/null 2>&1 || true
|
|
|
|
|
kubectl -n $ns exec deploy/$app_name -- sh -lc \
|
|
|
|
|
(printf 'echo "SECRET RECOVERY PHRASE: %s" >> /home/sui/README_MNEMONIC && chmod 0600 /home/sui/README_MNEMONIC' "$MN")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if test -z "$MN" -o -z "$ADDR"
|
|
|
|
|
# Fall back to plain text (robust across CLI changes)
|
|
|
|
|
kubectl -n $ns exec deploy/$app_name -- sh -lc \
|
|
|
|
|
(printf 'sui client new-address ed25519 --word-length %s --alias %s 2>&1 || true' $wl $alias) | tee $TMP >/dev/null
|
|
|
|
|
|
|
|
|
|
if test -z "$MN"
|
|
|
|
|
# Usually phrase is printed on the line right after the "Secret Recovery Phrase" header
|
|
|
|
|
set MN (awk '/Secret Recovery Phrase/ {getline; print; exit}' $TMP)
|
|
|
|
|
end
|
|
|
|
|
if test -z "$ADDR"
|
|
|
|
|
# Typical line: "Address: 0xabc..."; extract the RHS
|
|
|
|
|
set ADDR (awk -F': ' '/^Address:/ {print $2; exit}' $TMP)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Make the generated address active (address wins; alias as backup)
|
|
|
|
|
if test -n "$ADDR"
|
|
|
|
|
kubectl -n $ns exec deploy/$app_name -- sh -lc (printf 'sui client switch --address %s >/dev/null 2>&1 || true' $ADDR)
|
|
|
|
|
else
|
|
|
|
|
kubectl -n $ns exec deploy/$app_name -- sh -lc (printf 'sui client switch --alias %s >/dev/null 2>&1 || true' $alias)
|
|
|
|
|
end
|
2025-08-14 20:37:30 -05:00
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo (set_color yellow)"IMPORTANT — Secret Recovery Phrase (write down & store offline):"(set_color normal)
|
2025-08-19 01:06:45 -05:00
|
|
|
if test -n "$MN"
|
|
|
|
|
echo $MN
|
|
|
|
|
else
|
|
|
|
|
echo "(mnemonic not detected — check 'kubectl -n $ns logs deploy/$app_name' output)"
|
|
|
|
|
end
|
2025-08-14 20:37:30 -05:00
|
|
|
echo
|
|
|
|
|
echo "Address alias '$alias' appears to be: $ADDR"
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# Optional: store mnemonic in a Secret (not recommended)
|
2025-08-14 20:37:30 -05:00
|
|
|
if test "$store_mn" = "y" -o "$store_mn" = "yes"
|
2025-08-19 01:06:45 -05:00
|
|
|
if test -n "$MN"
|
|
|
|
|
kubectl -n $ns create secret generic "$app_name-mnemonic" \
|
2025-08-14 20:37:30 -05:00
|
|
|
--from-literal=mnemonic="$MN" --dry-run=client -o yaml | kubectl -n $ns apply -f -
|
2025-08-19 01:06:45 -05:00
|
|
|
echo (set_color yellow)"Stored mnemonic in Secret $ns/{$app_name}-mnemonic. Treat your cluster as sensitive."(set_color normal)
|
2025-08-14 20:37:30 -05:00
|
|
|
else
|
2025-08-19 01:06:45 -05:00
|
|
|
echo (set_color red)"Warning:"(set_color normal)" mnemonic empty; not storing."
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "Done. Files inside the pod:"
|
2025-08-19 01:06:45 -05:00
|
|
|
kubectl -n $ns exec deploy/$app_name -- sh -lc 'ls -l /home/sui/.sui/sui_config || true'
|
2025-08-14 20:37:30 -05:00
|
|
|
echo
|
|
|
|
|
echo "Useful commands:"
|
2025-08-19 01:06:45 -05:00
|
|
|
echo " suiwallet_addresses $ns $app_name"
|
|
|
|
|
echo " suiwallet_gas $ns $app_name"
|
|
|
|
|
echo " suiwallet_export_keystore $ns $app_name ./sui.keystore.$base"
|
|
|
|
|
echo " suiwallet_metrics $ns $app_name $base"
|
|
|
|
|
echo " kubectl -n $ns exec -it deploy/$app_name -- sh # interactive CLI"
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
### --------- utilities ----------
|
2025-08-19 01:06:45 -05:00
|
|
|
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,netpol -l app=$APP -o wide
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function suiwallet_logs -a NS APP LINES
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
if test -z "$LINES"; set LINES 200; end
|
|
|
|
|
kubectl -n $NS logs deploy/$APP --tail=$LINES
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function suiwallet_wallet_ls -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
echo "Listing /home/sui/.sui/sui_config in $NS/$APP…" >&2
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc 'ls -la /home/sui/.sui/sui_config || true'
|
|
|
|
|
end
|
|
|
|
|
|
2025-08-14 20:37:30 -05:00
|
|
|
function suiwallet_addresses -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
2025-08-19 01:06:45 -05:00
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc 'sui client addresses || true'
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
2025-08-19 01:06:45 -05:00
|
|
|
set POD (kubectl -n $NS get pod -l app=$APP -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
|
|
|
|
|
if test -z "$POD"
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" no pod found for app=$APP in ns=$NS" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
echo "Copying keystore from $POD to $DEST (keep it safe!)"
|
|
|
|
|
kubectl -n $NS cp $POD:/home/sui/.sui/sui_config/sui.keystore $DEST
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
|
2025-08-19 01:06:45 -05:00
|
|
|
# Summarize gas coins and total balance; tolerant to schema differences
|
|
|
|
|
function suiwallet_gas -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
set J (kubectl -n $NS exec deploy/$APP -- sh -lc 'sui client gas --json' 2>/dev/null)
|
|
|
|
|
if test -z "$J"
|
|
|
|
|
echo "No JSON from 'sui client gas' (is the address set and env working?)" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
set NUM (printf "%s" $J | jq -r '[.. | .gasObjects? // empty | length] | add // 0')
|
|
|
|
|
if test -z "$NUM" -o "$NUM" = "null"
|
|
|
|
|
set NUM (printf "%s" $J | jq -r '[.. | .coins? // empty | length] | add // 0')
|
|
|
|
|
end
|
|
|
|
|
set SUM (printf "%s" $J | jq -r '
|
|
|
|
|
(.. | .totalBalance? // empty) as $t
|
|
|
|
|
| if $t != null then ($t|tonumber)
|
|
|
|
|
else [.. | .gasObjects? // empty | .[]? | .balance? | tonumber] | add
|
|
|
|
|
end // 0
|
|
|
|
|
')
|
|
|
|
|
# Convert Mist -> SUI using python -c
|
|
|
|
|
set SUM_SUI (python3 -c 'from decimal import Decimal; import sys; q=Decimal(sys.argv[1]) if sys.argv[1] else Decimal(0); print((q/Decimal("1e9")).quantize(Decimal("0.000000001")))' -- $SUM)
|
|
|
|
|
echo "Gas coins: $NUM Total: $SUM ($SUM_SUI SUI)"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Wait until balance >= threshold (SUI), timeout seconds
|
|
|
|
|
function suiwallet_wait_balance -a NS APP MIN_SUI TIMEOUT
|
|
|
|
|
_need python3; or return 1
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
if test -z "$MIN_SUI"; set MIN_SUI 0; end
|
|
|
|
|
if test -z "$TIMEOUT"; set TIMEOUT 300; end
|
|
|
|
|
set start (date +%s)
|
|
|
|
|
while true
|
|
|
|
|
set J (kubectl -n $NS exec deploy/$APP -- sh -lc 'sui client gas --json' 2>/dev/null)
|
|
|
|
|
set SUM (printf "%s" $J | jq -r '
|
|
|
|
|
(.. | .totalBalance? // empty) as $t
|
|
|
|
|
| if $t != null then ($t|tonumber)
|
|
|
|
|
else [.. | .gasObjects? // empty | .[]? | .balance? | tonumber] | add
|
|
|
|
|
end // 0
|
|
|
|
|
')
|
|
|
|
|
set SUM_SUI (python3 -c 'from decimal import Decimal; import sys; q=Decimal(sys.argv[1]) if sys.argv[1] else Decimal(0); print(q/Decimal("1e9"))' -- $SUM)
|
|
|
|
|
set ge (python3 -c 'from decimal import Decimal; import sys; print(1 if Decimal(sys.argv[1]) >= Decimal(sys.argv[2]) else 0)' -- $SUM_SUI $MIN_SUI)
|
|
|
|
|
if test "$ge" = "1"
|
|
|
|
|
echo "Balance ready: $SUM_SUI SUI (>= $MIN_SUI)"
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
if test (math (date +%s) - $start) -ge $TIMEOUT
|
|
|
|
|
echo "Timed out waiting for balance >= $MIN_SUI SUI (current $SUM_SUI SUI)."
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
sleep 6
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Send SUI using `transfer-sui` picking the largest coin
|
|
|
|
|
# Usage: suiwallet_transfer_sui [ns] [app] <to_addr> <amount_sui> [gas_budget_mist=10000000]
|
|
|
|
|
function suiwallet_transfer_sui -a NS APP TO AMT_SUI GAS_BUDGET
|
|
|
|
|
_need jq python3; or return 1
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
if test -z "$TO" -o -z "$AMT_SUI"
|
|
|
|
|
echo "Usage: suiwallet_transfer_sui [ns] [app] <to_address> <amount_sui> [gas_budget_mist]" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
if test -z "$GAS_BUDGET"; set GAS_BUDGET 10000000; end
|
|
|
|
|
|
|
|
|
|
set AMT_MIST (python3 -c 'from decimal import Decimal; import sys; print(int((Decimal(sys.argv[1])*Decimal("1e9")).to_integral_value()))' -- $AMT_SUI)
|
|
|
|
|
set J (kubectl -n $NS exec deploy/$APP -- sh -lc 'sui client gas --json' 2>/dev/null)
|
|
|
|
|
set COIN (printf "%s" $J | jq -r '
|
|
|
|
|
([.. | .gasObjects? // empty | .[]?] // [])
|
|
|
|
|
| sort_by(.balance|tonumber) | last | .coinObjectId // empty
|
|
|
|
|
')
|
|
|
|
|
if test -z "$COIN"
|
|
|
|
|
echo (set_color red)"No gas coins found to send from."(set_color normal) >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
echo "Using coin: $COIN"
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc \
|
|
|
|
|
(printf 'sui client transfer-sui --to %s --sui-coin-object-id %s --amount %s --gas-budget %s' $TO $COIN $AMT_MIST $GAS_BUDGET)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Merge all gas coins into the largest one
|
|
|
|
|
function suiwallet_merge_gas -a NS APP
|
|
|
|
|
_need jq; or return 1
|
2025-08-14 20:37:30 -05:00
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
2025-08-19 01:06:45 -05:00
|
|
|
set J (kubectl -n $NS exec deploy/$APP -- sh -lc 'sui client gas --json' 2>/dev/null)
|
|
|
|
|
set IDS (printf "%s" $J | jq -r '([.. | .gasObjects? // empty | .[]?] // []) | sort_by(.balance|tonumber) | reverse | .[].coinObjectId')
|
|
|
|
|
set coins $IDS
|
|
|
|
|
if test (count $coins) -lt 2
|
|
|
|
|
echo "Nothing to merge (need >=2 gas coins)."
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
set primary $coins[1]
|
|
|
|
|
for i in (seq 2 (count $coins))
|
|
|
|
|
set c $coins[$i]
|
|
|
|
|
echo "Merging $c -> $primary"
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc (printf 'sui client merge-coin --primary-coin %s --coin-to-merge %s --gas-budget 10000000' $primary $c)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Restore an address from a Secret Recovery Phrase into the running pod
|
|
|
|
|
# Usage: suiwallet_restore [ns] [app] [alias] [network=mainnet]
|
|
|
|
|
function suiwallet_restore -a NS APP ALIAS NET
|
|
|
|
|
_need kubectl jq; or return 1
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
if test -z "$ALIAS"; set ALIAS restored; end
|
|
|
|
|
if test -z "$NET"; set NET mainnet; end
|
|
|
|
|
|
|
|
|
|
switch $NET
|
|
|
|
|
case mainnet
|
|
|
|
|
set RPC https://fullnode.mainnet.sui.io:443
|
|
|
|
|
case testnet
|
|
|
|
|
set RPC https://fullnode.testnet.sui.io:443
|
|
|
|
|
case devnet
|
|
|
|
|
set RPC https://fullnode.devnet.sui.io:443
|
|
|
|
|
case '*'
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" NET must be mainnet|testnet|devnet"; return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
echo (set_color yellow)"Paste your 12/24-word Sui Secret Recovery Phrase (input hidden):"(set_color normal)
|
|
|
|
|
read -s MNEMONIC
|
|
|
|
|
echo
|
|
|
|
|
if test -z "$MNEMONIC"
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" empty mnemonic."; return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Ensure env is configured and active
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc \
|
|
|
|
|
(printf 'mkdir -p /home/sui/.sui/sui_config ;
|
|
|
|
|
sui client new-env --alias %s --rpc %s >/dev/null 2>&1 || true ;
|
|
|
|
|
sui client switch --env %s >/dev/null 2>&1 || true' $NET $RPC $NET); or return 1
|
|
|
|
|
|
|
|
|
|
# Import mnemonic into keystore (non-interactive; enter scheme then phrase)
|
|
|
|
|
# keytool is interactive; we feed "ed25519" then the phrase via stdin.
|
|
|
|
|
set TMP (mktemp)
|
|
|
|
|
printf "ed25519\n%s\n" "$MNEMONIC" \
|
|
|
|
|
| kubectl -n $NS exec -i deploy/$APP -- sh -lc 'sui keytool import 2>&1' \
|
|
|
|
|
| tee $TMP >/dev/null
|
|
|
|
|
|
|
|
|
|
# Create a named alias pointing to one of the imported keys by re-using new-address --recover
|
|
|
|
|
# (new-address --recover will prompt for phrase; we feed it again to bind an alias)
|
|
|
|
|
set OUT (mktemp)
|
|
|
|
|
printf "%s\n" "$MNEMONIC" \
|
|
|
|
|
| kubectl -n $NS exec -i deploy/$APP -- sh -lc \
|
|
|
|
|
(printf 'sui client new-address ed25519 --alias %s --recover 2>&1' $ALIAS) \
|
|
|
|
|
| tee $OUT >/dev/null
|
|
|
|
|
|
|
|
|
|
# Try to extract the resulting address
|
|
|
|
|
set ADDR (awk -F': ' '/^Address:/ {print $2; exit}' $OUT)
|
|
|
|
|
if test -z "$ADDR"
|
|
|
|
|
# fallback: pick the address for the alias from the address table
|
|
|
|
|
set ADDR (kubectl -n $NS exec deploy/$APP -- sh -lc (printf 'sui client addresses | awk "/ %s\\s*$/ {print \\$NF}" | tail -n1' $ALIAS) 2>/dev/null)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "Restored alias: $ALIAS"
|
|
|
|
|
echo "Address: $ADDR"
|
|
|
|
|
echo (set_color yellow)"IMPORTANT:"(set_color normal)" store your recovery phrase offline. Do NOT keep it in the cluster."
|
|
|
|
|
|
|
|
|
|
# Make it active for the client
|
|
|
|
|
if test -n "$ADDR"
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc (printf 'sui client switch --address %s >/dev/null 2>&1 || true' $ADDR)
|
|
|
|
|
end
|
2025-08-14 20:37:30 -05:00
|
|
|
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
|
2025-08-19 01:06:45 -05:00
|
|
|
echo "Stopped $NS/$APP. PVC retained."
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Print the primary (first) address by alias search
|
|
|
|
|
function suiwallet_primary_address -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 -- sh -lc \
|
|
|
|
|
"sui client addresses | awk 'NF{print \$NF}' | head -n1" 2>/dev/null
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Quick overview
|
|
|
|
|
function suiwallet_overview -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
echo "== Sui wallet overview (ns=$NS app=$APP)"
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc 'echo "-- Addresses:"; sui client addresses || true'
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc 'echo "-- Gas objects:"; sui client gas 2>/dev/null || true'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Sui keystore size (for Grafana/Prometheus)
|
|
|
|
|
function suiwallet_keystore_size_bytes -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
set SZ (kubectl -n $NS exec deploy/$APP -- sh -lc \
|
|
|
|
|
'test -e /home/sui/.sui/sui_config/sui.keystore && stat -c %s /home/sui/.sui/sui_config/sui.keystore || echo 0' 2>/dev/null)
|
|
|
|
|
echo $SZ
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Prometheus-friendly one-liner metric
|
|
|
|
|
# Example usage: suiwallet_metrics crypto wallet-sui-test mywallet
|
|
|
|
|
function suiwallet_metrics -a NS APP WALLET_NAME
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-sui-main; end
|
|
|
|
|
if test -z "$WALLET_NAME"; set WALLET_NAME main; end
|
|
|
|
|
set BYTES (suiwallet_keystore_size_bytes $NS $APP)
|
|
|
|
|
printf "wallet_size_bytes{chain=\"sui\",namespace=\"%s\",app=\"%s\",wallet=\"%s\"} %s\n" $NS $APP $WALLET_NAME $BYTES
|
2025-08-14 20:37:30 -05:00
|
|
|
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"
|
2025-08-19 01:06:45 -05:00
|
|
|
echo "Aborted."
|
|
|
|
|
return 1
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|
|
|
|
|
kubectl -n $NS delete deploy $APP --ignore-not-found
|
|
|
|
|
kubectl -n $NS delete pvc $PVC --ignore-not-found --wait=true
|
2025-08-19 01:06:45 -05:00
|
|
|
kubectl -n $NS delete netpol (printf "%s-deny-ingress" $APP) --ignore-not-found >/dev/null 2>&1
|
|
|
|
|
echo "Purged. Ensure you have the mnemonic backed up if you ever need to restore."
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function suiwallet_help
|
|
|
|
|
echo "suiwallet_bootstrap # interactive deploy + PVC + optional NP; creates env+address, prints mnemonic"
|
|
|
|
|
echo "suiwallet_status [ns] [app] # show deploy/pods/pvc/netpol"
|
|
|
|
|
echo "suiwallet_logs [ns] [app] [N] # tail N (default 200) lines of pod logs"
|
|
|
|
|
echo "suiwallet_wallet_ls [ns] [app] # list ~/.sui/sui_config inside pod"
|
|
|
|
|
echo "suiwallet_addresses [ns] [app] # list addresses"
|
|
|
|
|
echo "suiwallet_gas [ns] [app] # summarize gas coins and total (Mist & SUI)"
|
|
|
|
|
echo "suiwallet_wait_balance [ns] [app] <min_sui> [timeout_s=300]"
|
|
|
|
|
echo "suiwallet_transfer_sui [ns] [app] <to> <amount_sui> [gas_budget_mist]"
|
|
|
|
|
echo "suiwallet_merge_gas [ns] [app] # merge many gas coins into one"
|
|
|
|
|
echo "suiwallet_export_keystore [ns] [app] [dest=./sui.keystore]"
|
|
|
|
|
echo "suiwallet_metrics [ns] [app] [walletname] # Prometheus line: wallet_size_bytes{chain=\"sui\",...}"
|
|
|
|
|
echo "suiwallet_stop [ns] [app] # delete deployment (keep PVC)"
|
|
|
|
|
echo "suiwallet_purge [ns] [app] [pvc] # delete deployment + PVC (danger!)"
|
2025-08-14 20:37:30 -05:00
|
|
|
end
|