Phase A status: BLOCKED on luxfi/operator v0.6.3 + v0.7.0 binaries (see luxfi/operator d2ecca5 — duplicate Network reconciler registration makes both crashloop at startup). Cluster rolled back to v0.3.12 on lux.network/v1alpha1 reconcile, all 3 chains 5/5 healthy. What this commit lays down for the next attempt (once a v0.6.4 or v0.7.1 image ships with the operator fix): - `scripts/migrate-luxnetwork-crds.sh <ctx>` — race-clean CR migration script. Walks lux.network/v1alpha1 LuxNetwork, LuxChain, LuxGateway, LuxIndexer, LuxExplorer, LuxMPC CRs, apiVersion-swaps each to lux.cloud/v1 (dropping the Lux prefix on every Kind except LuxNetwork which v0.6.x also calls LuxNetwork in the canonical group). Spec/status pass through opaque — both CRDs use x-kubernetes-preserve-unknown-fields: true. Strips server-side metadata. Idempotent. - `k8s/backups/pre-phase-a-20260602-020926/` — full live state snapshot for the 3 DOKS lux clusters, captured BEFORE any in-cluster mutation. Used to verify migration and as the rollback source if the next attempt needs to re-restore CRs. - `k8s/PHASE_A_ROLLBACK.md` — step-by-step rollback procedure. Inventory (lux-k8s only — test-k8s / dev-k8s have zero lux CRs): - 3 LuxNetworks (luxnetworks.lux.network) — mainnet/testnet/devnet luxd - 11 LuxChains (luxchains.lux.network) — mainnet a/b/d/g/i/k/m/o/q/r/z - 3 LuxGateways (luxgateways.lux.network) — */api-gateway - 1 LuxMPC (luxmpcs.lux.network) — mainnet/lux-mpc - 0 LuxIndexers, 0 LuxExplorers In-cluster prep that survived rollback: - lux.cloud/v1 CRDs installed alongside lux.network/v1alpha1 (idempotent apply of `~/work/lux/operator/spec/crd/bases/lux.cloud/`). - Migrated lux.cloud/v1 CRs already created (apply succeeded, no controller bound — inert until v0.6.4+ operator ships). Next attempt blocked on: 1. CI build of v0.6.4 or v0.7.1 operator image with luxfi/operator d2ecca5 fix (push triggers via `hanzo-build-linux-amd64` ARC pool). 2. Bump `k8s/lux-k8s/operator/deployment.yaml` image tag. 3. Re-run scale-down → image-bump → scale-up cycle. Forward-only, no AI slop, no API breakage, current chains stay alive during entire procedure.
135 lines
4.6 KiB
Bash
Executable File
135 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# migrate-luxnetwork-crds.sh — Phase A: lux.network/v1alpha1 → lux.cloud/v1
|
|
#
|
|
# Migrates all live CRs from the legacy `lux.network` API group at v1alpha1
|
|
# to the canonical `lux.cloud` API group at v1 (operator v0.6.3).
|
|
#
|
|
# Kind mapping:
|
|
# LuxNetwork → LuxNetwork (name unchanged)
|
|
# LuxChain → Chain (drop Lux prefix)
|
|
# LuxGateway → Gateway (drop Lux prefix)
|
|
# LuxIndexer → Indexer (drop Lux prefix)
|
|
# LuxExplorer → Explorer (drop Lux prefix)
|
|
# LuxMPC → MPC (drop Lux prefix)
|
|
#
|
|
# Spec/status pass through opaque (both CRDs use
|
|
# x-kubernetes-preserve-unknown-fields: true).
|
|
#
|
|
# Mode of operation:
|
|
# 1. List source kind in source group
|
|
# 2. For each item: read full yaml, swap apiVersion + kind,
|
|
# strip server-side metadata (resourceVersion, uid, generation,
|
|
# managedFields, creationTimestamp, status), apply under new group
|
|
# 3. Verify both CRs coexist (legacy + migrated) before any deletion
|
|
#
|
|
# Idempotent: re-applying produces same target CR (server-side merge by name).
|
|
#
|
|
# Usage:
|
|
# ./migrate-luxnetwork-crds.sh <kubectl-context> [--dry-run]
|
|
#
|
|
# Env:
|
|
# YQ — yq v4 binary path (default: yq). Required.
|
|
|
|
set -euo pipefail
|
|
|
|
CTX="${1:-}"
|
|
DRY_RUN=""
|
|
if [[ "${2:-}" == "--dry-run" ]]; then
|
|
DRY_RUN="--dry-run=client"
|
|
fi
|
|
|
|
if [[ -z "$CTX" ]]; then
|
|
echo "usage: $0 <kubectl-context> [--dry-run]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
YQ="${YQ:-yq}"
|
|
if ! command -v "$YQ" >/dev/null 2>&1; then
|
|
echo "FATAL: yq v4+ required on PATH (or set YQ=)" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# kind → (sourceGVR, targetGVR, targetKind)
|
|
# kubectl-friendly: lux<resource>.lux.network/v1alpha1 → <target>.lux.cloud/v1
|
|
declare -a KINDS=(
|
|
"luxnetwork:luxnetworks.lux.network:luxnetworks.lux.cloud:LuxNetwork:lux.cloud/v1"
|
|
"luxchain:luxchains.lux.network:chains.lux.cloud:Chain:lux.cloud/v1"
|
|
"luxgateway:luxgateways.lux.network:gateways.lux.cloud:Gateway:lux.cloud/v1"
|
|
"luxindexer:luxindexers.lux.network:indexers.lux.cloud:Indexer:lux.cloud/v1"
|
|
"luxexplorer:luxexplorers.lux.network:explorers.lux.cloud:Explorer:lux.cloud/v1"
|
|
"luxmpc:luxmpcs.lux.network:mpcs.lux.cloud:MPC:lux.cloud/v1"
|
|
)
|
|
|
|
TMP=$(mktemp -d -t lux-cr-migrate-XXXXXXXX)
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
echo "Working dir: $TMP"
|
|
echo "Context: $CTX"
|
|
echo
|
|
|
|
migrated_total=0
|
|
for entry in "${KINDS[@]}"; do
|
|
IFS=':' read -r tag src_gvr dst_gvr dst_kind dst_apiver <<<"$entry"
|
|
|
|
echo "=== $tag : $src_gvr → $dst_gvr ($dst_kind / $dst_apiver) ==="
|
|
|
|
# List items in source — succeeds even if 0 items
|
|
if ! list_yaml=$(kubectl --context="$CTX" get "$src_gvr" -A -o yaml 2>/dev/null); then
|
|
echo " (source CRD $src_gvr not present, skipping)"
|
|
continue
|
|
fi
|
|
|
|
# Count items
|
|
count=$(printf '%s' "$list_yaml" | "$YQ" '.items | length' -)
|
|
if [[ "$count" == "0" || -z "$count" ]]; then
|
|
echo " (no items)"
|
|
continue
|
|
fi
|
|
echo " found $count item(s)"
|
|
|
|
# Emit each item, swap apiVersion + kind, strip server fields, apply
|
|
for i in $(seq 0 $((count - 1))); do
|
|
item_file="$TMP/${tag}-${i}.yaml"
|
|
printf '%s' "$list_yaml" | "$YQ" ".items[$i]
|
|
| .apiVersion = \"$dst_apiver\"
|
|
| .kind = \"$dst_kind\"
|
|
| del(.metadata.resourceVersion)
|
|
| del(.metadata.uid)
|
|
| del(.metadata.generation)
|
|
| del(.metadata.managedFields)
|
|
| del(.metadata.creationTimestamp)
|
|
| del(.metadata.selfLink)
|
|
| del(.metadata.ownerReferences)
|
|
| del(.metadata.annotations[\"kubectl.kubernetes.io/last-applied-configuration\"])
|
|
| del(.status)" - > "$item_file"
|
|
|
|
ns=$("$YQ" '.metadata.namespace' "$item_file")
|
|
name=$("$YQ" '.metadata.name' "$item_file")
|
|
echo " → $ns/$name"
|
|
|
|
if [[ -n "$DRY_RUN" ]]; then
|
|
kubectl --context="$CTX" apply $DRY_RUN -f "$item_file" -o name | sed 's/^/ /'
|
|
else
|
|
kubectl --context="$CTX" apply -f "$item_file" -o name | sed 's/^/ /'
|
|
fi
|
|
migrated_total=$((migrated_total + 1))
|
|
done
|
|
done
|
|
|
|
echo
|
|
echo "=== TOTAL: $migrated_total CR(s) migrated (or planned in dry-run) ==="
|
|
echo
|
|
echo "=== POST-MIGRATION INVENTORY ==="
|
|
echo "--- legacy lux.network CRs (should still be present) ---"
|
|
for entry in "${KINDS[@]}"; do
|
|
IFS=':' read -r tag src_gvr _ _ _ <<<"$entry"
|
|
count=$(kubectl --context="$CTX" get "$src_gvr" -A --no-headers 2>/dev/null | wc -l | tr -d ' ')
|
|
printf " %-30s %d\n" "$src_gvr" "$count"
|
|
done
|
|
echo "--- new lux.cloud CRs (should match counts) ---"
|
|
for entry in "${KINDS[@]}"; do
|
|
IFS=':' read -r tag _ dst_gvr _ _ <<<"$entry"
|
|
count=$(kubectl --context="$CTX" get "$dst_gvr" -A --no-headers 2>/dev/null | wc -l | tr -d ' ')
|
|
printf " %-30s %d\n" "$dst_gvr" "$count"
|
|
done
|