@rulebricks/cli 2.1.7 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -16
- package/cluster-setup/aws/README.md +96 -47
- package/cluster-setup/aws/check-aws-access.sh +216 -52
- package/cluster-setup/aws/parameters.json +13 -0
- package/cluster-setup/aws/rulebricks-cluster.cfn.yaml +355 -0
- package/cluster-setup/azure/README.md +103 -55
- package/cluster-setup/azure/check-aks-prereqs.sh +236 -56
- package/cluster-setup/azure/parameters.json +30 -0
- package/cluster-setup/azure/rulebricks-cluster.bicep +546 -0
- package/cluster-setup/gcp/README.md +51 -34
- package/cluster-setup/gcp/check-gke-prereqs.sh +222 -60
- package/dist/commands/backup.d.ts +5 -0
- package/dist/commands/backup.js +104 -0
- package/dist/commands/deploy.d.ts +3 -1
- package/dist/commands/deploy.js +226 -326
- package/dist/commands/destroy.d.ts +1 -1
- package/dist/commands/destroy.js +73 -123
- package/dist/commands/init.d.ts +5 -1
- package/dist/commands/init.js +78 -54
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +74 -0
- package/dist/commands/open.d.ts +1 -1
- package/dist/commands/open.js +4 -12
- package/dist/commands/redeploy.d.ts +6 -0
- package/dist/commands/redeploy.js +310 -0
- package/dist/commands/restore.d.ts +5 -0
- package/dist/commands/restore.js +338 -0
- package/dist/commands/status.js +62 -49
- package/dist/commands/upgrade.js +74 -51
- package/dist/components/DNSWaitScreen.d.ts +5 -1
- package/dist/components/DNSWaitScreen.js +47 -41
- package/dist/components/Wizard/WizardContext.d.ts +157 -36
- package/dist/components/Wizard/WizardContext.js +872 -160
- package/dist/components/Wizard/steps/CloudProviderStep.js +192 -107
- package/dist/components/Wizard/steps/DomainStep.js +5 -24
- package/dist/components/Wizard/steps/ExternalServicesStep.d.ts +6 -0
- package/dist/components/Wizard/steps/ExternalServicesStep.js +645 -0
- package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +2 -1
- package/dist/components/Wizard/steps/FeatureConfigStep.js +739 -425
- package/dist/components/Wizard/steps/FeaturesStep.js +31 -35
- package/dist/components/Wizard/steps/ObservabilityStep.d.ts +6 -0
- package/dist/components/Wizard/steps/ObservabilityStep.js +137 -0
- package/dist/components/Wizard/steps/ReviewStep.d.ts +2 -1
- package/dist/components/Wizard/steps/ReviewStep.js +56 -12
- package/dist/components/Wizard/steps/StorageStep.d.ts +9 -0
- package/dist/components/Wizard/steps/StorageStep.js +592 -0
- package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +20 -21
- package/dist/components/Wizard/steps/VersionStep.js +45 -23
- package/dist/components/Wizard/steps/index.d.ts +3 -3
- package/dist/components/Wizard/steps/index.js +3 -3
- package/dist/components/common/CommandApproval.d.ts +12 -0
- package/dist/components/common/CommandApproval.js +91 -0
- package/dist/components/common/DeploymentPicker.d.ts +14 -0
- package/dist/components/common/DeploymentPicker.js +16 -0
- package/dist/components/common/index.d.ts +2 -0
- package/dist/components/common/index.js +2 -0
- package/dist/index.js +94 -62
- package/dist/lib/cloudCli.d.ts +134 -63
- package/dist/lib/cloudCli.js +512 -220
- package/dist/lib/clusterSetupDefaults.d.ts +30 -0
- package/dist/lib/clusterSetupDefaults.js +64 -0
- package/dist/lib/commandApproval.d.ts +26 -0
- package/dist/lib/commandApproval.js +114 -0
- package/dist/lib/config.d.ts +12 -10
- package/dist/lib/config.js +91 -33
- package/dist/lib/configFixtures.d.ts +5 -0
- package/dist/lib/configFixtures.js +513 -0
- package/dist/lib/deploymentHealth.d.ts +32 -0
- package/dist/lib/deploymentHealth.js +157 -0
- package/dist/lib/dns.d.ts +1 -1
- package/dist/lib/dns.js +19 -1
- package/dist/lib/dns.test.d.ts +1 -0
- package/dist/lib/dns.test.js +27 -0
- package/dist/lib/dockerHub.d.ts +12 -1
- package/dist/lib/dockerHub.js +18 -8
- package/dist/lib/helm.d.ts +4 -0
- package/dist/lib/helm.js +16 -0
- package/dist/lib/helmValues.d.ts +25 -0
- package/dist/lib/helmValues.js +1762 -289
- package/dist/lib/helmValues.test.d.ts +1 -0
- package/dist/lib/helmValues.test.js +966 -0
- package/dist/lib/htpasswd.d.ts +1 -0
- package/dist/lib/htpasswd.js +15 -0
- package/dist/lib/kubernetes.d.ts +124 -17
- package/dist/lib/kubernetes.js +576 -145
- package/dist/lib/secrets.d.ts +23 -0
- package/dist/lib/secrets.js +158 -0
- package/dist/lib/validateValues.d.ts +31 -0
- package/dist/lib/validateValues.js +253 -0
- package/dist/lib/versions.d.ts +82 -11
- package/dist/lib/versions.js +131 -31
- package/dist/lib/versions.test.d.ts +1 -0
- package/dist/lib/versions.test.js +81 -0
- package/dist/lib/wizardSteps.d.ts +14 -0
- package/dist/lib/wizardSteps.js +23 -0
- package/dist/lib/workloadIdentity.d.ts +26 -0
- package/dist/lib/workloadIdentity.js +323 -0
- package/dist/lib/workloadIdentity.test.d.ts +1 -0
- package/dist/lib/workloadIdentity.test.js +57 -0
- package/dist/types/index.d.ts +1860 -164
- package/dist/types/index.js +518 -295
- package/package.json +9 -4
- package/schema/values.schema.json +1934 -0
- package/cluster-setup/aws/cluster.yaml +0 -33
- package/cluster-setup/azure/main.bicep +0 -282
- package/cluster-setup/azure/main.parameters.json +0 -21
- package/dist/components/Wizard/steps/CredentialsStep.d.ts +0 -6
- package/dist/components/Wizard/steps/CredentialsStep.js +0 -22
- package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +0 -5
- package/dist/components/Wizard/steps/DeploymentModeStep.js +0 -26
- package/dist/components/Wizard/steps/TierStep.d.ts +0 -6
- package/dist/components/Wizard/steps/TierStep.js +0 -29
- package/dist/lib/terraform.d.ts +0 -66
- package/dist/lib/terraform.js +0 -754
- package/terraform/aws/main.tf +0 -355
- package/terraform/azure/main.tf +0 -371
- package/terraform/gcp/main.tf +0 -407
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GCP Cluster Setup
|
|
2
2
|
|
|
3
|
-
Use these commands to create a
|
|
3
|
+
Use these commands to create a compact GKE cluster that can run Rulebricks before installing with the Rulebricks CLI. GCP does not have an `eksctl`-style cluster YAML or a concise Bicep equivalent; the most familiar native interface is `gcloud`.
|
|
4
4
|
|
|
5
5
|
## Files
|
|
6
6
|
|
|
@@ -11,10 +11,11 @@ Use these commands to create a minimum GKE cluster that can run Rulebricks witho
|
|
|
11
11
|
- Cluster name: `rulebricks-cluster` (`Core cluster parameters` block -> `CLUSTER_NAME`)
|
|
12
12
|
- Region / zone: `us-central1` / `us-central1-a` (`Core cluster parameters` block -> `REGION` / `ZONE`)
|
|
13
13
|
- Kubernetes version: `1.34` (`Core cluster parameters` block -> `KUBERNETES_VERSION`)
|
|
14
|
-
-
|
|
15
|
-
-
|
|
14
|
+
- Initial node count: `2` (`Core cluster parameters` block -> `NODE_COUNT`)
|
|
15
|
+
- Autoscaling range: `2-4` nodes (`Core cluster parameters` block -> `NODE_COUNT` / `MAX_NODE_COUNT`)
|
|
16
|
+
- Machine type: `n2-standard-4` (`Core cluster parameters` block -> `MACHINE_TYPE`)
|
|
16
17
|
- Disk size (GB): `20` (`Core cluster parameters` block -> `DISK_SIZE`)
|
|
17
|
-
- Disk type: `
|
|
18
|
+
- Disk type: `pd-balanced` (`Core cluster parameters` block -> `DISK_TYPE`)
|
|
18
19
|
|
|
19
20
|
## Check Access
|
|
20
21
|
|
|
@@ -29,7 +30,7 @@ If API warnings appear, run the suggested `gcloud services enable` commands and
|
|
|
29
30
|
|
|
30
31
|
## Create The Cluster
|
|
31
32
|
|
|
32
|
-
Set the core cluster parameters.
|
|
33
|
+
Set the core cluster parameters.
|
|
33
34
|
|
|
34
35
|
```bash
|
|
35
36
|
PROJECT_ID="$(gcloud config get-value project)"
|
|
@@ -37,10 +38,11 @@ CLUSTER_NAME=rulebricks-cluster
|
|
|
37
38
|
REGION=us-central1
|
|
38
39
|
ZONE=us-central1-a
|
|
39
40
|
KUBERNETES_VERSION="1.34"
|
|
40
|
-
NODE_COUNT=
|
|
41
|
-
|
|
41
|
+
NODE_COUNT=2
|
|
42
|
+
MAX_NODE_COUNT=4
|
|
43
|
+
MACHINE_TYPE=n2-standard-4
|
|
42
44
|
DISK_SIZE=20
|
|
43
|
-
DISK_TYPE=
|
|
45
|
+
DISK_TYPE=pd-balanced
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
Enable required APIs:
|
|
@@ -54,7 +56,7 @@ gcloud services enable \
|
|
|
54
56
|
--project "$PROJECT_ID"
|
|
55
57
|
```
|
|
56
58
|
|
|
57
|
-
Create the VPC, subnet, NAT, and
|
|
59
|
+
Create the VPC, subnet, NAT, and firewall rules:
|
|
58
60
|
|
|
59
61
|
```bash
|
|
60
62
|
gcloud compute networks create "${CLUSTER_NAME}-vpc" \
|
|
@@ -87,6 +89,13 @@ gcloud compute firewall-rules create "${CLUSTER_NAME}-allow-internal" \
|
|
|
87
89
|
--allow tcp:0-65535,udp:0-65535,icmp \
|
|
88
90
|
--source-ranges 10.0.0.0/16,10.1.0.0/16,10.2.0.0/16 \
|
|
89
91
|
--target-tags "gke-${CLUSTER_NAME}"
|
|
92
|
+
|
|
93
|
+
gcloud compute firewall-rules create "${CLUSTER_NAME}-allow-web" \
|
|
94
|
+
--project "$PROJECT_ID" \
|
|
95
|
+
--network "${CLUSTER_NAME}-vpc" \
|
|
96
|
+
--allow tcp:80,tcp:443 \
|
|
97
|
+
--source-ranges 0.0.0.0/0 \
|
|
98
|
+
--target-tags "gke-${CLUSTER_NAME}"
|
|
90
99
|
```
|
|
91
100
|
|
|
92
101
|
Create the GKE cluster:
|
|
@@ -113,13 +122,15 @@ gcloud container clusters create "$CLUSTER_NAME" \
|
|
|
113
122
|
--node-pool rulebricks-nodes \
|
|
114
123
|
--machine-type "$MACHINE_TYPE" \
|
|
115
124
|
--num-nodes "$NODE_COUNT" \
|
|
125
|
+
--enable-autoscaling \
|
|
126
|
+
--min-nodes "$NODE_COUNT" \
|
|
127
|
+
--max-nodes "$MAX_NODE_COUNT" \
|
|
116
128
|
--disk-type "$DISK_TYPE" \
|
|
117
129
|
--disk-size "$DISK_SIZE" \
|
|
118
130
|
--scopes cloud-platform \
|
|
119
131
|
--workload-metadata GKE_METADATA \
|
|
120
132
|
--enable-autorepair \
|
|
121
133
|
--enable-autoupgrade \
|
|
122
|
-
--node-labels environment=rulebricks \
|
|
123
134
|
--tags "gke-${CLUSTER_NAME}"
|
|
124
135
|
```
|
|
125
136
|
|
|
@@ -131,42 +142,48 @@ gcloud container clusters get-credentials "$CLUSTER_NAME" \
|
|
|
131
142
|
--project "$PROJECT_ID"
|
|
132
143
|
```
|
|
133
144
|
|
|
134
|
-
Use `rulebricks init`
|
|
145
|
+
Use `rulebricks init` after kubeconfig works, then select this cluster from the GCP cluster list.
|
|
135
146
|
|
|
136
|
-
##
|
|
147
|
+
## Multi-Node Scheduling
|
|
137
148
|
|
|
138
|
-
|
|
149
|
+
The default configuration starts with two 4-vCPU nodes for a simple standalone deployment and can scale out to four nodes. Splitting the baseline across two nodes provides more Kubernetes pod slots than a single large node while keeping the initial 8-vCPU footprint. Rulebricks worker pods use soft scheduling preferences so Kubernetes can place them away from the rest of the deployment when extra nodes are available. No node labels or taints are required.
|
|
150
|
+
|
|
151
|
+
## Identity Setup (one service account, one bucket)
|
|
152
|
+
|
|
153
|
+
All Rulebricks data lives in a single GCS bucket; decision logs and database
|
|
154
|
+
backups are key prefixes (`decision-logs/`, `db-backups/`) within it. Create one
|
|
155
|
+
Google service account and the bucket — this is deployment-independent:
|
|
139
156
|
|
|
140
157
|
```bash
|
|
141
|
-
NAMESPACE=rulebricks-demo
|
|
142
158
|
PROJECT_ID="$(gcloud config get-value project)"
|
|
143
|
-
|
|
159
|
+
CLUSTER_NAME=rulebricks-cluster
|
|
160
|
+
GSA=rulebricks@"$PROJECT_ID".iam.gserviceaccount.com
|
|
161
|
+
BUCKET="$CLUSTER_NAME-data"
|
|
144
162
|
|
|
145
|
-
gcloud iam service-accounts create rulebricks
|
|
146
|
-
--project "$PROJECT_ID"
|
|
163
|
+
gcloud iam service-accounts create rulebricks --project "$PROJECT_ID"
|
|
147
164
|
|
|
148
|
-
|
|
165
|
+
# Create the single data bucket and grant read/write/delete (delete is needed so
|
|
166
|
+
# the backup job can prune backups older than the retention window).
|
|
167
|
+
gcloud storage buckets create "gs://$BUCKET" --project "$PROJECT_ID" --location "$REGION"
|
|
168
|
+
gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
|
|
149
169
|
--member "serviceAccount:$GSA" \
|
|
150
|
-
--role roles/storage.
|
|
151
|
-
|
|
152
|
-
gcloud iam service-accounts add-iam-policy-binding "$GSA" \
|
|
153
|
-
--project "$PROJECT_ID" \
|
|
154
|
-
--role roles/iam.workloadIdentityUser \
|
|
155
|
-
--member "serviceAccount:$PROJECT_ID.svc.id.goog[$NAMESPACE/vector]"
|
|
156
|
-
```
|
|
170
|
+
--role roles/storage.objectAdmin
|
|
157
171
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
--namespace "$NAMESPACE" \
|
|
163
|
-
iam.gke.io/gcp-service-account="$GSA"
|
|
172
|
+
# Prometheus remote write to Google Managed Prometheus (skip if unused).
|
|
173
|
+
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
|
|
174
|
+
--member "serviceAccount:$GSA" \
|
|
175
|
+
--role roles/monitoring.metricWriter
|
|
164
176
|
```
|
|
165
177
|
|
|
166
|
-
|
|
178
|
+
The per-namespace `roles/iam.workloadIdentityUser` bindings (for `vector`,
|
|
179
|
+
`<release>-backup`, and `prometheus`) are **created by the Rulebricks CLI at
|
|
180
|
+
`rulebricks deploy` time**, since they're namespace-scoped — so this setup stays
|
|
181
|
+
generic and one cluster can host many deployments. Enter the Google service
|
|
182
|
+
account email (`$GSA`) and the `$BUCKET` name when prompted by the CLI.
|
|
167
183
|
|
|
168
184
|
## Notes
|
|
169
185
|
|
|
170
|
-
- The example creates
|
|
171
|
-
-
|
|
186
|
+
- The example creates two `n2-standard-4` nodes initially and enables autoscaling up to four nodes. The initial nodes provide 8 vCPU total for the compact Rulebricks cluster shape while avoiding single-node pod density limits.
|
|
187
|
+
- If you change `REGION`, choose a `ZONE` where the selected machine type is available.
|
|
172
188
|
- Regional GKE clusters can multiply node counts across node locations. This example pins one node location to keep the minimum cluster shape predictable.
|
|
189
|
+
- The public web firewall rule allows HTTP and HTTPS to the node pool so Kubernetes LoadBalancer services and cert-manager HTTP-01 validation can receive internet traffic.
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# Rulebricks GKE prerequisite check.
|
|
3
|
+
#
|
|
4
|
+
# Prints a short pass/fail report and a final READY / NOT READY verdict
|
|
5
|
+
# with the exact actions you need to take before running the GKE deploy.
|
|
6
|
+
#
|
|
7
|
+
# Env vars:
|
|
8
|
+
# GOOGLE_CLOUD_PROJECT GCP project id (defaults to gcloud config)
|
|
9
|
+
# GCP_REGION Region to check (default: us-central1)
|
|
10
|
+
# VERBOSE=1 Print raw gcloud error messages inline
|
|
11
|
+
|
|
2
12
|
set -euo pipefail
|
|
3
13
|
|
|
14
|
+
if [[ -z "${BASH_VERSION:-}" ]]; then
|
|
15
|
+
exec bash "$0" "$@"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Quiet gcloud's interactive nags / survey output.
|
|
19
|
+
export CLOUDSDK_CORE_DISABLE_PROMPTS=1
|
|
20
|
+
|
|
4
21
|
PROJECT_ID="${GOOGLE_CLOUD_PROJECT:-$(gcloud config get-value project 2>/dev/null || true)}"
|
|
5
22
|
REGION="${GCP_REGION:-us-central1}"
|
|
6
23
|
REQUIRED_VCPU=8
|
|
24
|
+
VERBOSE="${VERBOSE:-0}"
|
|
25
|
+
|
|
7
26
|
REQUIRED_APIS=(
|
|
8
27
|
compute.googleapis.com
|
|
9
28
|
container.googleapis.com
|
|
@@ -11,88 +30,231 @@ REQUIRED_APIS=(
|
|
|
11
30
|
cloudresourcemanager.googleapis.com
|
|
12
31
|
)
|
|
13
32
|
|
|
33
|
+
ACTIONS=()
|
|
34
|
+
BLOCKERS=0
|
|
35
|
+
|
|
36
|
+
# ---------- helpers ----------
|
|
37
|
+
|
|
14
38
|
require_cmd() {
|
|
15
39
|
command -v "$1" >/dev/null 2>&1 || {
|
|
16
|
-
|
|
40
|
+
printf "ERROR: required command not found: %s\n" "$1" >&2
|
|
17
41
|
exit 1
|
|
18
42
|
}
|
|
19
43
|
}
|
|
20
44
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
# Run a gcloud command. Sets GC_STDOUT / GC_STDERR / GC_RC. Never aborts.
|
|
46
|
+
gc_run() {
|
|
47
|
+
GC_STDOUT=""; GC_STDERR=""; GC_RC=0
|
|
48
|
+
local _err
|
|
49
|
+
_err="$(mktemp)"
|
|
50
|
+
GC_STDOUT="$(gcloud "$@" 2>"$_err")" || GC_RC=$?
|
|
51
|
+
GC_STDERR="$(cat "$_err")"
|
|
52
|
+
rm -f "$_err"
|
|
53
|
+
if [[ "$VERBOSE" == "1" && -n "$GC_STDERR" ]]; then
|
|
54
|
+
printf " debug: %s\n" "${GC_STDERR%%$'\n'*}" >&2
|
|
31
55
|
fi
|
|
56
|
+
return "$GC_RC"
|
|
32
57
|
}
|
|
33
58
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
local limit="${quota_line%,*}"
|
|
48
|
-
local usage="${quota_line#*,}"
|
|
49
|
-
local available
|
|
50
|
-
available="$(awk -v limit="$limit" -v usage="$usage" 'BEGIN { printf "%d", limit - usage }')"
|
|
59
|
+
is_auth_error() {
|
|
60
|
+
[[ "$GC_STDERR" == *"reauthentication"* ]] && return 0
|
|
61
|
+
[[ "$GC_STDERR" == *"credentials"*"expired"* ]] && return 0
|
|
62
|
+
[[ "$GC_STDERR" == *"invalid_grant"* ]] && return 0
|
|
63
|
+
[[ "$GC_STDERR" == *"gcloud auth login"* ]] && return 0
|
|
64
|
+
[[ "$GC_STDERR" == *"gcloud auth application-default login"* ]] && return 0
|
|
65
|
+
[[ "$GC_STDERR" == *"There was a problem refreshing"* ]] && return 0
|
|
66
|
+
[[ "$GC_STDERR" == *"do not have active credentials"* ]] && return 0
|
|
67
|
+
return 1
|
|
68
|
+
}
|
|
51
69
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
else
|
|
55
|
-
echo "OK: ${available}/${limit} CPUs available."
|
|
56
|
-
fi
|
|
70
|
+
row() {
|
|
71
|
+
printf " %-50s %s\n" "$1" "$2"
|
|
57
72
|
}
|
|
58
73
|
|
|
74
|
+
mark_blocker() { BLOCKERS=$((BLOCKERS + 1)); }
|
|
75
|
+
add_action() { ACTIONS+=("$1"); }
|
|
76
|
+
|
|
77
|
+
# ---------- pre-flight ----------
|
|
78
|
+
|
|
59
79
|
require_cmd gcloud
|
|
60
80
|
require_cmd kubectl
|
|
61
81
|
require_cmd helm
|
|
62
82
|
require_cmd awk
|
|
63
83
|
|
|
84
|
+
printf "Rulebricks GKE prerequisite check\n"
|
|
85
|
+
printf " Region: %s\n" "$REGION"
|
|
86
|
+
printf " Project: %s\n" "${PROJECT_ID:-<unset>}"
|
|
87
|
+
printf "\n"
|
|
88
|
+
|
|
64
89
|
if [[ -z "$PROJECT_ID" ]]; then
|
|
65
|
-
|
|
90
|
+
row "GCP project configured" "FAIL - no project set"
|
|
91
|
+
add_action "Set a project: gcloud config set project <PROJECT_ID>"
|
|
92
|
+
printf "\n========================================\n"
|
|
93
|
+
printf "RESULT: NOT READY\n"
|
|
94
|
+
printf "========================================\n"
|
|
95
|
+
printf "Required actions:\n 1. %s\n" "${ACTIONS[0]}"
|
|
66
96
|
exit 1
|
|
67
97
|
fi
|
|
68
98
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
99
|
+
# ---------- 1. Authentication ----------
|
|
100
|
+
AUTH_OK=0
|
|
101
|
+
ACTIVE_ACCOUNT=""
|
|
102
|
+
|
|
103
|
+
if gc_run auth list --filter=status:ACTIVE --format="value(account)"; then
|
|
104
|
+
ACTIVE_ACCOUNT="$(printf '%s' "$GC_STDOUT" | head -n 1)"
|
|
105
|
+
if [[ -z "$ACTIVE_ACCOUNT" ]]; then
|
|
106
|
+
row "gcloud user signed in" "FAIL - no active account"
|
|
107
|
+
add_action "Run: gcloud auth login"
|
|
108
|
+
mark_blocker
|
|
109
|
+
else
|
|
110
|
+
row "gcloud user signed in" "OK ($ACTIVE_ACCOUNT)"
|
|
111
|
+
|
|
112
|
+
# Now verify ADC actually works against Google APIs - this is what
|
|
113
|
+
# the deploy tooling uses, and the most common breakage.
|
|
114
|
+
if gc_run auth application-default print-access-token; then
|
|
115
|
+
row "Application Default Credentials" "OK"
|
|
116
|
+
AUTH_OK=1
|
|
117
|
+
else
|
|
118
|
+
if is_auth_error; then
|
|
119
|
+
row "Application Default Credentials" "FAIL - ADC missing or expired"
|
|
120
|
+
else
|
|
121
|
+
row "Application Default Credentials" "FAIL - ${GC_STDERR%%$'\n'*}"
|
|
122
|
+
fi
|
|
123
|
+
add_action "Run: gcloud auth application-default login"
|
|
124
|
+
mark_blocker
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
else
|
|
128
|
+
row "gcloud user signed in" "FAIL - ${GC_STDERR%%$'\n'*}"
|
|
129
|
+
add_action "Run: gcloud auth login && gcloud auth application-default login"
|
|
130
|
+
mark_blocker
|
|
131
|
+
fi
|
|
73
132
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
133
|
+
if [[ $AUTH_OK -eq 0 ]]; then
|
|
134
|
+
printf "\nRemaining checks skipped - fix authentication first.\n"
|
|
135
|
+
printf "\n========================================\n"
|
|
136
|
+
printf "RESULT: NOT READY\n"
|
|
137
|
+
printf "========================================\n"
|
|
138
|
+
printf "Required actions:\n"
|
|
139
|
+
i=1
|
|
140
|
+
for a in "${ACTIONS[@]}"; do
|
|
141
|
+
printf " %d. %s\n" "$i" "$a"
|
|
142
|
+
i=$((i + 1))
|
|
143
|
+
done
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
79
146
|
|
|
80
|
-
|
|
81
|
-
|
|
147
|
+
# ---------- 2. Project access ----------
|
|
148
|
+
if gc_run projects describe "$PROJECT_ID" --format="value(projectId)"; then
|
|
149
|
+
row "Project '$PROJECT_ID' accessible" "OK"
|
|
150
|
+
else
|
|
151
|
+
row "Project '$PROJECT_ID' accessible" "FAIL - ${GC_STDERR%%$'\n'*}"
|
|
152
|
+
add_action "Ensure '$ACTIVE_ACCOUNT' has at least roles/viewer on project '$PROJECT_ID', or fix the project id."
|
|
153
|
+
mark_blocker
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# ---------- 3. Required APIs ----------
|
|
157
|
+
missing_apis=()
|
|
158
|
+
unknown_apis=()
|
|
159
|
+
if gc_run services list --project "$PROJECT_ID" --enabled --format="value(config.name)"; then
|
|
160
|
+
enabled="$GC_STDOUT"
|
|
161
|
+
for api in "${REQUIRED_APIS[@]}"; do
|
|
162
|
+
if ! printf '%s\n' "$enabled" | grep -qx "$api"; then
|
|
163
|
+
missing_apis+=("$api")
|
|
164
|
+
fi
|
|
165
|
+
done
|
|
166
|
+
else
|
|
167
|
+
unknown_apis=("${REQUIRED_APIS[@]}")
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
total=${#REQUIRED_APIS[@]}
|
|
171
|
+
|
|
172
|
+
if [[ ${#unknown_apis[@]} -gt 0 ]]; then
|
|
173
|
+
row "Required APIs enabled" "WARN - could not list enabled services"
|
|
174
|
+
add_action "Verify 'serviceusage.services.list' permission, then re-run."
|
|
175
|
+
elif [[ ${#missing_apis[@]} -eq 0 ]]; then
|
|
176
|
+
row "Required APIs enabled" "OK ($total/$total)"
|
|
177
|
+
else
|
|
178
|
+
enabled_count=$((total - ${#missing_apis[@]}))
|
|
179
|
+
row "Required APIs enabled" "WARN ($enabled_count/$total)"
|
|
180
|
+
enable_cmd="gcloud services enable ${missing_apis[*]} --project $PROJECT_ID"
|
|
181
|
+
add_action "Enable missing APIs (takes ~1 min):"
|
|
182
|
+
add_action " $enable_cmd"
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# ---------- 4. Region + GKE access ----------
|
|
186
|
+
REGION_OK=1
|
|
187
|
+
if ! gc_run compute regions describe "$REGION" --project "$PROJECT_ID" --format="value(name)"; then
|
|
188
|
+
REGION_OK=0
|
|
189
|
+
row "Region '$REGION' accessible" "FAIL - ${GC_STDERR%%$'\n'*}"
|
|
190
|
+
add_action "Verify region name '$REGION' and that the Compute Engine API is enabled."
|
|
191
|
+
mark_blocker
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
if [[ $REGION_OK -eq 1 ]]; then
|
|
195
|
+
if gc_run container clusters list --region "$REGION" --project "$PROJECT_ID" --format="value(name)"; then
|
|
196
|
+
row "Region + GKE list access" "OK"
|
|
197
|
+
else
|
|
198
|
+
row "Region + GKE list access" "WARN - ${GC_STDERR%%$'\n'*}"
|
|
199
|
+
add_action "Ensure '$ACTIVE_ACCOUNT' has roles/container.viewer (or higher) on '$PROJECT_ID'."
|
|
200
|
+
fi
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# ---------- 5. Regional CPU quota ----------
|
|
204
|
+
quota_label="Regional CPU quota in $REGION (need ${REQUIRED_VCPU}+)"
|
|
205
|
+
quota_line=""
|
|
206
|
+
if gc_run compute regions describe "$REGION" --project "$PROJECT_ID" \
|
|
207
|
+
--format="csv[no-heading](quotas.metric,quotas.limit,quotas.usage)"; then
|
|
208
|
+
quota_line="$(printf '%s\n' "$GC_STDOUT" | awk -F, '$1=="CPUS"{print $2 "," $3; exit}')"
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
if [[ -z "$quota_line" ]]; then
|
|
212
|
+
row "$quota_label" "WARN - could not read quota"
|
|
213
|
+
add_action "Manually check CPU quota in Console → IAM & Admin → Quotas (region: $REGION)."
|
|
214
|
+
else
|
|
215
|
+
limit="${quota_line%,*}"
|
|
216
|
+
usage="${quota_line#*,}"
|
|
217
|
+
available="$(awk -v l="$limit" -v u="$usage" 'BEGIN { printf "%d", l - u }')"
|
|
218
|
+
if (( available < REQUIRED_VCPU )); then
|
|
219
|
+
row "$quota_label" "WARN ($available/$limit free)"
|
|
220
|
+
add_action "Request CPU quota increase: Console → IAM & Admin → Quotas → 'Compute Engine API CPUs' in $REGION."
|
|
221
|
+
else
|
|
222
|
+
row "$quota_label" "OK ($available/$limit free)"
|
|
223
|
+
fi
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
# ---------- 6. Local tools ----------
|
|
227
|
+
if kubectl version --client=true >/dev/null 2>&1 && helm version >/dev/null 2>&1; then
|
|
228
|
+
row "Local tools (kubectl, helm)" "OK"
|
|
229
|
+
else
|
|
230
|
+
row "Local tools (kubectl, helm)" "FAIL"
|
|
231
|
+
add_action "Install/repair kubectl and helm."
|
|
232
|
+
mark_blocker
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
# ---------- summary ----------
|
|
236
|
+
printf "\n========================================\n"
|
|
237
|
+
if [[ $BLOCKERS -eq 0 && ${#ACTIONS[@]} -eq 0 ]]; then
|
|
238
|
+
printf "RESULT: READY - you can run the GKE deploy.\n"
|
|
239
|
+
printf "========================================\n"
|
|
240
|
+
exit 0
|
|
241
|
+
elif [[ $BLOCKERS -eq 0 ]]; then
|
|
242
|
+
printf "RESULT: READY WITH WARNINGS\n"
|
|
243
|
+
printf "========================================\n"
|
|
244
|
+
printf "The deploy should work, but address these first if possible:\n"
|
|
245
|
+
else
|
|
246
|
+
printf "RESULT: NOT READY\n"
|
|
247
|
+
printf "========================================\n"
|
|
248
|
+
printf "Required actions:\n"
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
i=1
|
|
252
|
+
for a in "${ACTIONS[@]}"; do
|
|
253
|
+
printf " %d. %s\n" "$i" "$a"
|
|
254
|
+
i=$((i + 1))
|
|
82
255
|
done
|
|
83
256
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
echo "OK: GKE cluster list access works."
|
|
89
|
-
check_quota
|
|
90
|
-
|
|
91
|
-
echo
|
|
92
|
-
echo "Checking local Kubernetes tools..."
|
|
93
|
-
kubectl version --client=true >/dev/null
|
|
94
|
-
helm version >/dev/null
|
|
95
|
-
echo "OK: kubectl and Helm are installed."
|
|
96
|
-
|
|
97
|
-
echo
|
|
98
|
-
echo "GKE prerequisite checks completed. Warnings may require GCP project-admin review before cluster creation."
|
|
257
|
+
printf "\nRe-run this script after completing the actions above.\n"
|
|
258
|
+
printf "(Set VERBOSE=1 to see raw gcloud error messages.)\n"
|
|
259
|
+
|
|
260
|
+
[[ $BLOCKERS -gt 0 ]] && exit 1 || exit 0
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Box, Text, useApp } from "ink";
|
|
4
|
+
import { BorderBox, Logo, Spinner, StatusLine, ThemeProvider, useTheme, CommandApprovalProvider, } from "../components/common/index.js";
|
|
5
|
+
import { loadDeploymentConfig } from "../lib/config.js";
|
|
6
|
+
import { updateKubeconfig } from "../lib/cloudCli.js";
|
|
7
|
+
import { CommandDeniedError } from "../lib/commandApproval.js";
|
|
8
|
+
import { checkClusterAccessible, createJobFromCronJob, isKubectlInstalled, waitForJobComplete, } from "../lib/kubernetes.js";
|
|
9
|
+
import { getNamespace, getReleaseName } from "../types/index.js";
|
|
10
|
+
function k8sName(value) {
|
|
11
|
+
return value.toLowerCase().replace(/[^a-z0-9-]/g, "-").slice(0, 63).replace(/-+$/, "");
|
|
12
|
+
}
|
|
13
|
+
function BackupCommandInner({ name }) {
|
|
14
|
+
const { exit } = useApp();
|
|
15
|
+
const { colors } = useTheme();
|
|
16
|
+
const [step, setStep] = useState("loading");
|
|
17
|
+
const [error, setError] = useState(null);
|
|
18
|
+
const [logs, setLogs] = useState("");
|
|
19
|
+
const [status, setStatus] = useState({
|
|
20
|
+
preflight: "pending",
|
|
21
|
+
job: "pending",
|
|
22
|
+
});
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
runBackup();
|
|
25
|
+
}, []);
|
|
26
|
+
async function runBackup() {
|
|
27
|
+
try {
|
|
28
|
+
const config = await loadDeploymentConfig(name);
|
|
29
|
+
validateConfig(config);
|
|
30
|
+
setStep("preflight");
|
|
31
|
+
setStatus((current) => ({ ...current, preflight: "running" }));
|
|
32
|
+
await runPreflight(config);
|
|
33
|
+
setStatus((current) => ({ ...current, preflight: "success" }));
|
|
34
|
+
const namespace = getNamespace(config.name);
|
|
35
|
+
const releaseName = getReleaseName(config.name);
|
|
36
|
+
const cronJobName = `${releaseName}-db-backup`;
|
|
37
|
+
const jobName = k8sName(`${cronJobName}-manual-${Date.now()}`);
|
|
38
|
+
setStep("running");
|
|
39
|
+
setStatus((current) => ({ ...current, job: "running" }));
|
|
40
|
+
await createJobFromCronJob(namespace, cronJobName, jobName);
|
|
41
|
+
const jobLogs = await waitForJobComplete(namespace, jobName);
|
|
42
|
+
setLogs(jobLogs);
|
|
43
|
+
setStatus((current) => ({ ...current, job: "success" }));
|
|
44
|
+
setStep("complete");
|
|
45
|
+
setTimeout(() => exit(), 5000);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
setError(err instanceof Error ? err.message : "Backup failed");
|
|
49
|
+
setStatus((current) => ({
|
|
50
|
+
...current,
|
|
51
|
+
preflight: step === "preflight" ? "error" : current.preflight,
|
|
52
|
+
job: step === "running" ? "error" : current.job,
|
|
53
|
+
}));
|
|
54
|
+
setStep("error");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function validateConfig(config) {
|
|
58
|
+
if (config.database.type !== "self-hosted") {
|
|
59
|
+
throw new Error("Backups are only available for self-hosted Supabase.");
|
|
60
|
+
}
|
|
61
|
+
if (!config.storage) {
|
|
62
|
+
throw new Error("Shared object storage is required for database backups.");
|
|
63
|
+
}
|
|
64
|
+
if (!config.backup?.enabled) {
|
|
65
|
+
throw new Error("Database backups are disabled for this deployment. Re-run `rulebricks init` to enable them.");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function runPreflight(config) {
|
|
69
|
+
if (!(await isKubectlInstalled())) {
|
|
70
|
+
throw new Error("kubectl is not installed. Please install kubectl first.");
|
|
71
|
+
}
|
|
72
|
+
let clusterError = await checkClusterAccessible();
|
|
73
|
+
if (clusterError &&
|
|
74
|
+
config.infrastructure.provider &&
|
|
75
|
+
config.infrastructure.region &&
|
|
76
|
+
config.infrastructure.clusterName) {
|
|
77
|
+
try {
|
|
78
|
+
await updateKubeconfig(config.infrastructure.provider, config.infrastructure.clusterName, config.infrastructure.region, {
|
|
79
|
+
gcpProjectId: config.infrastructure.gcpProjectId,
|
|
80
|
+
azureResourceGroup: config.infrastructure.azureResourceGroup,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
if (!(err instanceof CommandDeniedError)) {
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
clusterError = await checkClusterAccessible();
|
|
89
|
+
}
|
|
90
|
+
if (clusterError) {
|
|
91
|
+
throw new Error(`Cannot access Kubernetes cluster:\n${clusterError}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (step === "error") {
|
|
95
|
+
return (_jsx(BorderBox, { title: "Backup Failed", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.error, bold: true, children: "\u2717 Error" }), _jsx(Text, { color: colors.error, children: error })] }) }));
|
|
96
|
+
}
|
|
97
|
+
if (step === "complete") {
|
|
98
|
+
return (_jsx(BorderBox, { title: "Backup Complete", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.success, bold: true, children: "\u2713 Database backup completed" }), logs && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, children: "Job output:" }), logs.split("\n").slice(-8).map((line, index) => (_jsx(Text, { color: colors.muted, children: line }, index)))] }))] }) }));
|
|
99
|
+
}
|
|
100
|
+
return (_jsx(BorderBox, { title: `Backing Up ${name}`, children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(StatusLine, { status: status.preflight, label: "Preflight checks" }), _jsx(StatusLine, { status: status.job, label: "Database backup job" }), _jsx(Box, { marginTop: 1, children: _jsx(Spinner, { label: step === "running" ? "Running backup job..." : "Preparing backup..." }) })] }) }));
|
|
101
|
+
}
|
|
102
|
+
export function BackupCommand(props) {
|
|
103
|
+
return (_jsxs(ThemeProvider, { theme: "status", children: [_jsx(Logo, {}), _jsx(CommandApprovalProvider, { children: _jsx(BackupCommandInner, { ...props }) })] }));
|
|
104
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
interface DeployCommandProps {
|
|
2
2
|
name: string;
|
|
3
|
-
skipInfra?: boolean;
|
|
4
3
|
skipDns?: boolean;
|
|
5
4
|
version?: string;
|
|
5
|
+
regenerateValues?: boolean;
|
|
6
|
+
assumeDnsConfigured?: boolean;
|
|
7
|
+
inlineSecrets?: boolean;
|
|
6
8
|
}
|
|
7
9
|
export declare function DeployCommand(props: DeployCommandProps): import("react/jsx-runtime").JSX.Element;
|
|
8
10
|
export {};
|