@rulebricks/cli 2.1.6 → 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 +75 -14
- package/cluster-setup/aws/README.md +123 -0
- package/cluster-setup/aws/check-aws-access.sh +242 -0
- package/cluster-setup/aws/parameters.json +13 -0
- package/cluster-setup/aws/rulebricks-cluster.cfn.yaml +355 -0
- package/cluster-setup/azure/README.md +141 -0
- package/cluster-setup/azure/check-aks-prereqs.sh +276 -0
- package/cluster-setup/azure/parameters.json +30 -0
- package/cluster-setup/azure/rulebricks-cluster.bicep +546 -0
- package/cluster-setup/gcp/README.md +189 -0
- package/cluster-setup/gcp/check-gke-prereqs.sh +260 -0
- 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 -47
- 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 +174 -29
- package/dist/components/Wizard/WizardContext.js +896 -91
- package/dist/components/Wizard/steps/CloudProviderStep.js +192 -102
- 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 +959 -248
- 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 -7
- 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 +1937 -259
- 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 +126 -13
- package/dist/lib/kubernetes.js +624 -134
- 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 +2152 -95
- package/dist/types/index.js +554 -286
- package/package.json +10 -4
- package/schema/values.schema.json +1934 -0
- 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,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useReducer } from "react";
|
|
3
|
-
import { DEFAULT_EMAIL_SUBJECTS, } from "../../types/index.js";
|
|
3
|
+
import { DEFAULT_EMAIL_SUBJECTS, validateRemoteWriteConfig, } from "../../types/index.js";
|
|
4
|
+
import { generateSecureSecret } from "../../lib/validation.js";
|
|
4
5
|
/**
|
|
5
6
|
* Creates the initial wizard state, optionally pre-populated from a user profile.
|
|
6
7
|
* Profile values are used as defaults that the user can still modify.
|
|
@@ -10,7 +11,6 @@ function getInitialState(profile) {
|
|
|
10
11
|
step: 0,
|
|
11
12
|
name: "",
|
|
12
13
|
// Infrastructure - pre-populate from profile
|
|
13
|
-
infrastructureMode: profile?.infrastructureMode ?? null,
|
|
14
14
|
provider: profile?.provider ?? null,
|
|
15
15
|
region: profile?.region ?? "",
|
|
16
16
|
clusterName: profile?.clusterName ?? "",
|
|
@@ -19,11 +19,13 @@ function getInitialState(profile) {
|
|
|
19
19
|
// Domain & Email - pre-populate from profile
|
|
20
20
|
domain: "", // Domain is intentionally left empty - user should enter unique domain per deployment
|
|
21
21
|
adminEmail: profile?.adminEmail ?? "",
|
|
22
|
-
|
|
22
|
+
// The TLS email is no longer asked in the wizard; it defaults to the admin
|
|
23
|
+
// email in toConfig. Only an existing config (redeploy) can carry a custom
|
|
24
|
+
// value, so it is intentionally not pre-populated from the profile.
|
|
25
|
+
tlsEmail: "",
|
|
23
26
|
// DNS Configuration - pre-populate from profile
|
|
24
27
|
dnsProvider: profile?.dnsProvider ?? "other",
|
|
25
28
|
dnsAutoManage: false,
|
|
26
|
-
existingExternalDns: false,
|
|
27
29
|
// SMTP - pre-populate from profile
|
|
28
30
|
smtpHost: profile?.smtpHost ?? "",
|
|
29
31
|
smtpPort: profile?.smtpPort ?? 587,
|
|
@@ -38,12 +40,34 @@ function getInitialState(profile) {
|
|
|
38
40
|
supabaseServiceKey: "",
|
|
39
41
|
supabaseAccessToken: "",
|
|
40
42
|
supabaseProjectRef: "",
|
|
41
|
-
supabaseJwtSecret:
|
|
43
|
+
supabaseJwtSecret: generateSecureSecret(64),
|
|
42
44
|
supabaseDbPassword: "",
|
|
43
45
|
supabaseDashboardUser: "supabase",
|
|
44
46
|
supabaseDashboardPass: "",
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
+
// Cluster capabilities (populated by the cluster scan)
|
|
48
|
+
nodeArchitecture: null,
|
|
49
|
+
arm64TolerationRequired: false,
|
|
50
|
+
storageClass: "",
|
|
51
|
+
storageProvisioner: "",
|
|
52
|
+
schedulableNodeCount: 0,
|
|
53
|
+
totalCpuCores: 0,
|
|
54
|
+
totalMemoryGi: 0,
|
|
55
|
+
eligibleCpuCores: 0,
|
|
56
|
+
eligibleMemoryGi: 0,
|
|
57
|
+
totalPersistentStorageGi: 0,
|
|
58
|
+
// Shared object storage
|
|
59
|
+
storageProvider: profile?.storage?.provider ?? null,
|
|
60
|
+
storageBucket: profile?.storage?.bucket ?? "",
|
|
61
|
+
storageRegion: profile?.storage?.region ?? "",
|
|
62
|
+
storageCloudAuthMode: profile?.storage?.cloudAuthMode ?? "workload-identity",
|
|
63
|
+
storageAwsIamRoleArn: profile?.storage?.awsIamRoleArn ?? "",
|
|
64
|
+
storageAzureBlobContainer: profile?.storage?.azureBlobContainer ?? "rulebricks",
|
|
65
|
+
storageAzureBlobClientId: profile?.storage?.azureBlobClientId ?? "",
|
|
66
|
+
storageAzureBlobTenantId: profile?.storage?.azureBlobTenantId ?? "",
|
|
67
|
+
storageAzureBlobConnectionStringSecretRef: profile?.storage?.azureBlobConnectionStringSecretRef
|
|
68
|
+
? `${profile.storage.azureBlobConnectionStringSecretRef.name}:${profile.storage.azureBlobConnectionStringSecretRef.key}`
|
|
69
|
+
: "",
|
|
70
|
+
storageGcpServiceAccountEmail: profile?.storage?.gcpServiceAccountEmail ?? "",
|
|
47
71
|
// Features - AI - pre-populate from profile
|
|
48
72
|
aiEnabled: !!profile?.openaiApiKey,
|
|
49
73
|
openaiApiKey: profile?.openaiApiKey ?? "",
|
|
@@ -53,13 +77,49 @@ function getInitialState(profile) {
|
|
|
53
77
|
ssoUrl: profile?.ssoUrl ?? "",
|
|
54
78
|
ssoClientId: profile?.ssoClientId ?? "",
|
|
55
79
|
ssoClientSecret: profile?.ssoClientSecret ?? "",
|
|
56
|
-
// Features - Monitoring
|
|
57
|
-
|
|
80
|
+
// Features - Monitoring (metrics export is opt-in; in-cluster Prometheus
|
|
81
|
+
// is always installed)
|
|
82
|
+
clickStackEnabled: true,
|
|
83
|
+
clickStackTelemetryRetentionDays: 7,
|
|
84
|
+
clickHouseStorageSize: "100Gi",
|
|
85
|
+
metricsExportEnabled: false,
|
|
86
|
+
prometheusMonitoringDestination: null,
|
|
58
87
|
prometheusRemoteWriteUrl: "",
|
|
88
|
+
prometheusRemoteWriteDestination: null,
|
|
89
|
+
prometheusRemoteWriteAuthType: null,
|
|
90
|
+
prometheusRemoteWriteAwsRegion: "",
|
|
91
|
+
prometheusRemoteWriteAwsRoleArn: "",
|
|
92
|
+
prometheusRemoteWriteAzureCloud: "AzurePublic",
|
|
93
|
+
prometheusRemoteWriteClientId: "",
|
|
94
|
+
prometheusRemoteWriteTenantId: "",
|
|
95
|
+
prometheusRemoteWriteSecretRef: "",
|
|
96
|
+
prometheusRemoteWriteUsernameSecretRef: "",
|
|
97
|
+
prometheusRemoteWritePasswordSecretRef: "",
|
|
98
|
+
prometheusRemoteWriteBearerTokenSecretRef: "",
|
|
59
99
|
// Features - Logging
|
|
60
100
|
loggingSink: "console", // Default to console only
|
|
61
|
-
|
|
62
|
-
|
|
101
|
+
loggingPlatformCredential: "",
|
|
102
|
+
loggingPlatformDetail: "",
|
|
103
|
+
// Features - Distributed Tracing
|
|
104
|
+
tracingEnabled: false,
|
|
105
|
+
tracingDestination: "elastic",
|
|
106
|
+
tracingElasticEndpoint: "",
|
|
107
|
+
tracingElasticAuthMode: "secret-token",
|
|
108
|
+
tracingElasticSecretToken: "",
|
|
109
|
+
tracingElasticApiKey: "",
|
|
110
|
+
tracingOtlpEndpoint: "",
|
|
111
|
+
tracingOtlpAuthMode: "none",
|
|
112
|
+
tracingOtlpHeaderName: "Authorization",
|
|
113
|
+
tracingOtlpToken: "",
|
|
114
|
+
tracingAzureConnectionString: "",
|
|
115
|
+
// Features - Application log shipping
|
|
116
|
+
appLogsEnabled: false,
|
|
117
|
+
appLogsElasticEndpoint: "",
|
|
118
|
+
appLogsElasticIndex: "rulebricks-app-logs",
|
|
119
|
+
appLogsElasticAuthMode: "basic",
|
|
120
|
+
appLogsElasticUsername: "",
|
|
121
|
+
appLogsElasticPassword: "",
|
|
122
|
+
appLogsElasticApiKey: "",
|
|
63
123
|
// Features - Custom Email Templates
|
|
64
124
|
customEmailsEnabled: false,
|
|
65
125
|
emailSubjects: { ...DEFAULT_EMAIL_SUBJECTS },
|
|
@@ -69,26 +129,579 @@ function getInitialState(profile) {
|
|
|
69
129
|
recovery: "",
|
|
70
130
|
emailChange: "",
|
|
71
131
|
},
|
|
132
|
+
// Database backups
|
|
133
|
+
backupEnabled: false,
|
|
134
|
+
backupSchedule: "0 2 * * *",
|
|
135
|
+
backupRetentionDays: 7,
|
|
136
|
+
// External services - Redis (default to in-cluster)
|
|
137
|
+
redisMode: "embedded",
|
|
138
|
+
redisHost: "",
|
|
139
|
+
redisPort: 6379,
|
|
140
|
+
redisPassword: "",
|
|
141
|
+
redisExistingSecret: "",
|
|
142
|
+
redisExistingSecretKey: "redis-password",
|
|
143
|
+
redisTls: false,
|
|
144
|
+
redisHttpApiEnabled: false,
|
|
145
|
+
redisHttpApiUrl: "",
|
|
146
|
+
redisHttpApiToken: "",
|
|
147
|
+
valkeyAdminEnabled: false,
|
|
148
|
+
valkeyAdminExposure: "internal",
|
|
149
|
+
valkeyAdminHostname: "",
|
|
150
|
+
valkeyAdminBasicAuthUsers: [],
|
|
151
|
+
valkeyAdminAllowedIPs: [],
|
|
152
|
+
redisExporterEnabled: false,
|
|
153
|
+
kafkaExporterEnabled: false,
|
|
154
|
+
// External services - Kafka (default to in-cluster)
|
|
155
|
+
kafkaMode: "embedded",
|
|
156
|
+
kafkaPreset: null,
|
|
157
|
+
kafkaBrokers: "",
|
|
158
|
+
kafkaTopic: "logs",
|
|
159
|
+
kafkaTopicPrefix: "com.rulebricks.",
|
|
160
|
+
kafkaProvisionTopics: true,
|
|
161
|
+
kafkaSsl: false,
|
|
162
|
+
kafkaSaslMechanism: "",
|
|
163
|
+
kafkaSaslRegion: "",
|
|
164
|
+
kafkaSaslUsername: "",
|
|
165
|
+
kafkaSaslPassword: "",
|
|
166
|
+
kafkaSaslExistingSecret: "",
|
|
167
|
+
kafkaIdentityAwsRoleArn: "",
|
|
168
|
+
kafkaIdentityGcpServiceAccountEmail: "",
|
|
169
|
+
kafkaIdentityAzureClientId: "",
|
|
170
|
+
// External services - Postgres
|
|
171
|
+
postgresMode: "embedded",
|
|
172
|
+
postgresHost: "",
|
|
173
|
+
postgresPort: 5432,
|
|
174
|
+
postgresDatabase: "postgres",
|
|
175
|
+
postgresMasterUsername: "postgres",
|
|
176
|
+
postgresMasterPassword: "",
|
|
72
177
|
// Credentials - pre-populate from profile
|
|
73
178
|
licenseKey: profile?.licenseKey ?? "",
|
|
74
179
|
// Version
|
|
75
|
-
|
|
76
|
-
hpsVersion: "",
|
|
180
|
+
version: "",
|
|
77
181
|
chartVersion: "",
|
|
78
182
|
};
|
|
79
183
|
}
|
|
80
184
|
// Default initial state (for backwards compatibility)
|
|
81
185
|
const initialState = getInitialState();
|
|
186
|
+
/**
|
|
187
|
+
* Derives the Supabase project ref from a managed-Supabase project URL
|
|
188
|
+
* (https://<ref>.supabase.co), so the wizard never has to ask for it.
|
|
189
|
+
* Returns undefined for custom domains, where the ref can't be inferred.
|
|
190
|
+
*/
|
|
191
|
+
function deriveSupabaseProjectRef(url) {
|
|
192
|
+
const match = url
|
|
193
|
+
.trim()
|
|
194
|
+
.match(/^https?:\/\/([a-z0-9-]+)\.supabase\.(?:co|com|in)(?:[/:]|$)/i);
|
|
195
|
+
return match ? match[1].toLowerCase() : undefined;
|
|
196
|
+
}
|
|
197
|
+
function parseSecretKeyRef(value) {
|
|
198
|
+
const [name, key] = value.split(":").map((part) => part.trim());
|
|
199
|
+
if (!name || !key)
|
|
200
|
+
return undefined;
|
|
201
|
+
return { name, key };
|
|
202
|
+
}
|
|
203
|
+
function formatSecretKeyRef(ref) {
|
|
204
|
+
return ref ? `${ref.name}:${ref.key}` : "";
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Builds the externalServices config from wizard state. Returns undefined when
|
|
208
|
+
* both Redis and Kafka use the in-cluster defaults so configs stay clean.
|
|
209
|
+
*/
|
|
210
|
+
function buildExternalServices(state) {
|
|
211
|
+
const redisExternal = state.redisMode === "external";
|
|
212
|
+
const kafkaExternal = state.kafkaMode === "external";
|
|
213
|
+
const postgresExternal = state.postgresMode === "external";
|
|
214
|
+
if (!redisExternal && !kafkaExternal && !postgresExternal) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
redis: {
|
|
219
|
+
mode: state.redisMode,
|
|
220
|
+
external: redisExternal
|
|
221
|
+
? {
|
|
222
|
+
host: state.redisHost.trim() || undefined,
|
|
223
|
+
port: state.redisPort || undefined,
|
|
224
|
+
password: state.redisPassword || undefined,
|
|
225
|
+
existingSecret: state.redisExistingSecret.trim() || undefined,
|
|
226
|
+
existingSecretKey: state.redisExistingSecret.trim() && state.redisExistingSecretKey
|
|
227
|
+
? state.redisExistingSecretKey
|
|
228
|
+
: undefined,
|
|
229
|
+
tls: state.redisTls,
|
|
230
|
+
httpApi: state.redisHttpApiEnabled
|
|
231
|
+
? {
|
|
232
|
+
enabled: true,
|
|
233
|
+
url: state.redisHttpApiUrl.trim() || undefined,
|
|
234
|
+
token: state.redisHttpApiToken || undefined,
|
|
235
|
+
}
|
|
236
|
+
: undefined,
|
|
237
|
+
}
|
|
238
|
+
: undefined,
|
|
239
|
+
},
|
|
240
|
+
kafka: {
|
|
241
|
+
mode: state.kafkaMode,
|
|
242
|
+
external: kafkaExternal
|
|
243
|
+
? {
|
|
244
|
+
preset: state.kafkaPreset ?? "custom",
|
|
245
|
+
brokers: state.kafkaBrokers.trim() || undefined,
|
|
246
|
+
topic: state.kafkaTopic.trim() || undefined,
|
|
247
|
+
// Always emit the prefix (incl. "") so the choice round-trips and the
|
|
248
|
+
// chart doesn't silently fall back to its default.
|
|
249
|
+
topicPrefix: state.kafkaTopicPrefix,
|
|
250
|
+
provisionTopics: state.kafkaProvisionTopics,
|
|
251
|
+
ssl: state.kafkaSsl,
|
|
252
|
+
sasl: state.kafkaSaslMechanism
|
|
253
|
+
? {
|
|
254
|
+
mechanism: state.kafkaSaslMechanism,
|
|
255
|
+
region: state.kafkaSaslRegion.trim() || undefined,
|
|
256
|
+
username: state.kafkaSaslUsername || undefined,
|
|
257
|
+
password: state.kafkaSaslPassword || undefined,
|
|
258
|
+
existingSecret: state.kafkaSaslExistingSecret.trim() || undefined,
|
|
259
|
+
}
|
|
260
|
+
: undefined,
|
|
261
|
+
identity: buildKafkaIdentity(state),
|
|
262
|
+
}
|
|
263
|
+
: undefined,
|
|
264
|
+
},
|
|
265
|
+
postgres: {
|
|
266
|
+
mode: state.postgresMode,
|
|
267
|
+
external: postgresExternal
|
|
268
|
+
? {
|
|
269
|
+
provider: state.provider === "aws" || state.provider === "azure"
|
|
270
|
+
? state.provider
|
|
271
|
+
: undefined,
|
|
272
|
+
host: state.postgresHost.trim() || undefined,
|
|
273
|
+
port: state.postgresPort || undefined,
|
|
274
|
+
database: state.postgresDatabase.trim() || undefined,
|
|
275
|
+
// One-time creds for the chart's bootstrap hook. The shared
|
|
276
|
+
// service-role password is sourced separately from the Supabase DB
|
|
277
|
+
// password (secret.db) so it matches what the services present.
|
|
278
|
+
bootstrap: {
|
|
279
|
+
enabled: true,
|
|
280
|
+
masterUsername: state.postgresMasterUsername.trim() || undefined,
|
|
281
|
+
masterPassword: state.postgresMasterPassword || undefined,
|
|
282
|
+
appRole: "postgres",
|
|
283
|
+
},
|
|
284
|
+
}
|
|
285
|
+
: undefined,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Collects the cloud workload identity for token-based Kafka auth. Returns
|
|
291
|
+
* undefined when no identity is provided (e.g. Azure Event Hubs PLAIN).
|
|
292
|
+
*/
|
|
293
|
+
function buildKafkaIdentity(state) {
|
|
294
|
+
const awsRoleArn = state.kafkaIdentityAwsRoleArn.trim();
|
|
295
|
+
const gcpServiceAccountEmail = state.kafkaIdentityGcpServiceAccountEmail.trim();
|
|
296
|
+
const azureClientId = state.kafkaIdentityAzureClientId.trim();
|
|
297
|
+
if (!awsRoleArn && !gcpServiceAccountEmail && !azureClientId) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
awsRoleArn: awsRoleArn || undefined,
|
|
302
|
+
gcpServiceAccountEmail: gcpServiceAccountEmail || undefined,
|
|
303
|
+
azureClientId: azureClientId || undefined,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Builds the Prometheus remote_write config from wizard state. Metrics reuse the
|
|
308
|
+
* single Rulebricks identity chosen in the Storage step, so Azure/AWS principals
|
|
309
|
+
* fall back to the storage identity when not set explicitly.
|
|
310
|
+
*/
|
|
311
|
+
function buildRemoteWriteFromState(state) {
|
|
312
|
+
if (state.prometheusMonitoringDestination === "local-grafana" ||
|
|
313
|
+
!state.prometheusRemoteWriteDestination ||
|
|
314
|
+
!state.prometheusRemoteWriteUrl) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
destination: state.prometheusRemoteWriteDestination,
|
|
319
|
+
url: state.prometheusRemoteWriteUrl,
|
|
320
|
+
authType: state.prometheusRemoteWriteAuthType ||
|
|
321
|
+
(state.prometheusRemoteWriteDestination === "azure-monitor"
|
|
322
|
+
? "managed-identity"
|
|
323
|
+
: undefined),
|
|
324
|
+
awsRegion: state.prometheusRemoteWriteDestination === "aws-amp"
|
|
325
|
+
? state.prometheusRemoteWriteAwsRegion || state.region || undefined
|
|
326
|
+
: undefined,
|
|
327
|
+
awsRoleArn: state.prometheusRemoteWriteDestination === "aws-amp"
|
|
328
|
+
? state.prometheusRemoteWriteAwsRoleArn ||
|
|
329
|
+
state.storageAwsIamRoleArn ||
|
|
330
|
+
undefined
|
|
331
|
+
: undefined,
|
|
332
|
+
azureCloud: state.prometheusRemoteWriteAzureCloud,
|
|
333
|
+
clientId: state.prometheusRemoteWriteDestination === "azure-monitor"
|
|
334
|
+
? state.prometheusRemoteWriteClientId ||
|
|
335
|
+
state.storageAzureBlobClientId ||
|
|
336
|
+
undefined
|
|
337
|
+
: state.prometheusRemoteWriteClientId || undefined,
|
|
338
|
+
tenantId: state.prometheusRemoteWriteDestination === "azure-monitor"
|
|
339
|
+
? state.prometheusRemoteWriteTenantId ||
|
|
340
|
+
state.storageAzureBlobTenantId ||
|
|
341
|
+
undefined
|
|
342
|
+
: state.prometheusRemoteWriteTenantId || undefined,
|
|
343
|
+
clientSecretRef: parseSecretKeyRef(state.prometheusRemoteWriteSecretRef),
|
|
344
|
+
usernameSecretRef: parseSecretKeyRef(state.prometheusRemoteWriteUsernameSecretRef),
|
|
345
|
+
passwordSecretRef: parseSecretKeyRef(state.prometheusRemoteWritePasswordSecretRef),
|
|
346
|
+
bearerTokenSecretRef: parseSecretKeyRef(state.prometheusRemoteWriteBearerTokenSecretRef),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Returns a list of human-readable reasons the wizard state can't be saved as a
|
|
351
|
+
* valid DeploymentConfig. Empty array means it's good to save. Shared by toConfig
|
|
352
|
+
* (which returns null when non-empty) and the review screen (which shows them),
|
|
353
|
+
* so users get a specific reason instead of a generic "invalid configuration".
|
|
354
|
+
*/
|
|
355
|
+
export function collectConfigIssues(state) {
|
|
356
|
+
const issues = [];
|
|
357
|
+
if (!state.name)
|
|
358
|
+
issues.push("Deployment name is required.");
|
|
359
|
+
if (!state.domain)
|
|
360
|
+
issues.push("Domain is required.");
|
|
361
|
+
if (!state.adminEmail)
|
|
362
|
+
issues.push("Admin email is required.");
|
|
363
|
+
if (!state.licenseKey)
|
|
364
|
+
issues.push("License key is required.");
|
|
365
|
+
if (!state.smtpHost || !state.smtpUser || !state.smtpPass || !state.smtpFrom) {
|
|
366
|
+
issues.push("SMTP host, user, password, and from address are required.");
|
|
367
|
+
}
|
|
368
|
+
if (state.databaseType === "supabase-cloud") {
|
|
369
|
+
if (!state.supabaseUrl ||
|
|
370
|
+
!state.supabaseAnonKey ||
|
|
371
|
+
!state.supabaseServiceKey ||
|
|
372
|
+
!state.supabaseAccessToken) {
|
|
373
|
+
issues.push("Managed Supabase requires a project URL, anon key, service key, and access token.");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else if (state.databaseType === "self-hosted") {
|
|
377
|
+
if (!state.supabaseJwtSecret) {
|
|
378
|
+
issues.push("Self-hosted Supabase requires a JWT secret.");
|
|
379
|
+
}
|
|
380
|
+
if (!state.supabaseDbPassword) {
|
|
381
|
+
issues.push("Self-hosted Supabase requires a database password.");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (state.aiEnabled && !state.openaiApiKey) {
|
|
385
|
+
issues.push("AI is enabled but the OpenAI API key is missing.");
|
|
386
|
+
}
|
|
387
|
+
if (state.ssoEnabled &&
|
|
388
|
+
(!state.ssoProvider || !state.ssoClientId || !state.ssoClientSecret)) {
|
|
389
|
+
issues.push("SSO is enabled but the provider, client ID, or client secret is missing.");
|
|
390
|
+
}
|
|
391
|
+
if (state.loggingSink !== "console" &&
|
|
392
|
+
state.loggingSink !== "pending" &&
|
|
393
|
+
!state.loggingPlatformCredential) {
|
|
394
|
+
issues.push("The selected logging platform is missing its credentials/endpoint.");
|
|
395
|
+
}
|
|
396
|
+
if (!state.clickStackEnabled && state.tracingEnabled) {
|
|
397
|
+
if (state.tracingDestination === "elastic") {
|
|
398
|
+
if (!state.tracingElasticEndpoint) {
|
|
399
|
+
issues.push("Distributed tracing is enabled but the Elastic APM endpoint is missing.");
|
|
400
|
+
}
|
|
401
|
+
if (state.tracingElasticAuthMode === "secret-token" &&
|
|
402
|
+
!state.tracingElasticSecretToken) {
|
|
403
|
+
issues.push("Distributed tracing (secret-token auth) is missing the Elastic APM secret token.");
|
|
404
|
+
}
|
|
405
|
+
if (state.tracingElasticAuthMode === "api-key" &&
|
|
406
|
+
!state.tracingElasticApiKey) {
|
|
407
|
+
issues.push("Distributed tracing (API key auth) is missing the Elastic APM API key.");
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
else if (state.tracingDestination === "otlp") {
|
|
411
|
+
if (!state.tracingOtlpEndpoint) {
|
|
412
|
+
issues.push("Distributed tracing is enabled but the OTLP endpoint is missing.");
|
|
413
|
+
}
|
|
414
|
+
if (state.tracingOtlpAuthMode !== "none" && !state.tracingOtlpToken) {
|
|
415
|
+
issues.push("Distributed tracing (OTLP) is missing its authentication credential.");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else if (state.tracingDestination === "azure-monitor") {
|
|
419
|
+
if (!state.tracingAzureConnectionString) {
|
|
420
|
+
issues.push("Distributed tracing is enabled but the Azure Monitor connection string is missing.");
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (!state.clickStackEnabled && state.appLogsEnabled) {
|
|
425
|
+
if (!state.appLogsElasticEndpoint) {
|
|
426
|
+
issues.push("Application log shipping is enabled but the Elasticsearch endpoint is missing.");
|
|
427
|
+
}
|
|
428
|
+
if (state.appLogsElasticAuthMode === "basic" &&
|
|
429
|
+
(!state.appLogsElasticUsername || !state.appLogsElasticPassword)) {
|
|
430
|
+
issues.push("Application log shipping (basic auth) is missing the Elasticsearch username or password.");
|
|
431
|
+
}
|
|
432
|
+
if (state.appLogsElasticAuthMode === "api-key" &&
|
|
433
|
+
!state.appLogsElasticApiKey) {
|
|
434
|
+
issues.push("Application log shipping (API key auth) is missing the Elasticsearch API key.");
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (!state.storageProvider || !state.storageBucket || !state.storageRegion) {
|
|
438
|
+
issues.push("Object storage provider, bucket/account, and region are required.");
|
|
439
|
+
}
|
|
440
|
+
if (state.storageProvider === "s3" && !state.storageAwsIamRoleArn) {
|
|
441
|
+
issues.push("S3 storage requires an IAM role (IRSA).");
|
|
442
|
+
}
|
|
443
|
+
if (state.storageProvider === "azure-blob") {
|
|
444
|
+
if (!state.storageAzureBlobContainer) {
|
|
445
|
+
issues.push("Azure Blob storage requires a container name.");
|
|
446
|
+
}
|
|
447
|
+
if (state.storageCloudAuthMode === "workload-identity" &&
|
|
448
|
+
(!state.storageAzureBlobClientId || !state.storageAzureBlobTenantId)) {
|
|
449
|
+
issues.push("Azure Blob workload identity requires a client ID and tenant ID.");
|
|
450
|
+
}
|
|
451
|
+
if (state.storageCloudAuthMode === "secret" &&
|
|
452
|
+
!parseSecretKeyRef(state.storageAzureBlobConnectionStringSecretRef)) {
|
|
453
|
+
issues.push("Azure Blob connection-string auth requires a secret reference (name:key).");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (state.storageProvider === "gcs" && !state.storageGcpServiceAccountEmail) {
|
|
457
|
+
issues.push("GCS storage requires a Google service account email.");
|
|
458
|
+
}
|
|
459
|
+
if (state.redisMode === "external" && !state.redisHost.trim()) {
|
|
460
|
+
issues.push("External Redis requires a host.");
|
|
461
|
+
}
|
|
462
|
+
if (state.kafkaMode === "external" && !state.kafkaBrokers.trim()) {
|
|
463
|
+
issues.push("External Kafka requires brokers.");
|
|
464
|
+
}
|
|
465
|
+
if (state.postgresMode === "external") {
|
|
466
|
+
if (!state.postgresHost.trim()) {
|
|
467
|
+
issues.push("External Postgres requires a host.");
|
|
468
|
+
}
|
|
469
|
+
if (!state.postgresMasterPassword) {
|
|
470
|
+
issues.push("External Postgres requires master credentials to initialize the database.");
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (!state.clickStackEnabled && state.metricsExportEnabled) {
|
|
474
|
+
const remoteWrite = buildRemoteWriteFromState(state);
|
|
475
|
+
if (!remoteWrite) {
|
|
476
|
+
issues.push("Metrics export is enabled but the Prometheus remote_write destination is not configured.");
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
for (const message of validateRemoteWriteConfig(remoteWrite)) {
|
|
480
|
+
issues.push(message);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (state.valkeyAdminEnabled &&
|
|
485
|
+
state.valkeyAdminExposure === "ingress" &&
|
|
486
|
+
state.valkeyAdminBasicAuthUsers.length === 0) {
|
|
487
|
+
issues.push("Valkey Admin ingress exposure requires at least one htpasswd BasicAuth user.");
|
|
488
|
+
}
|
|
489
|
+
return issues;
|
|
490
|
+
}
|
|
491
|
+
export function configToWizardState(config, profile) {
|
|
492
|
+
const base = getInitialState(profile);
|
|
493
|
+
const remoteWrite = config.features.monitoring.remoteWrite;
|
|
494
|
+
const storage = config.storage;
|
|
495
|
+
const customEmails = config.features.customEmails;
|
|
496
|
+
const externalRedis = config.externalServices?.redis;
|
|
497
|
+
const externalKafka = config.externalServices?.kafka;
|
|
498
|
+
const externalPostgres = config.externalServices?.postgres;
|
|
499
|
+
return {
|
|
500
|
+
...base,
|
|
501
|
+
name: config.name,
|
|
502
|
+
provider: config.infrastructure.provider ?? base.provider,
|
|
503
|
+
region: config.infrastructure.region ?? base.region,
|
|
504
|
+
clusterName: config.infrastructure.clusterName ?? base.clusterName,
|
|
505
|
+
gcpProjectId: config.infrastructure.gcpProjectId ?? "",
|
|
506
|
+
azureResourceGroup: config.infrastructure.azureResourceGroup ?? "",
|
|
507
|
+
domain: config.domain,
|
|
508
|
+
adminEmail: config.adminEmail,
|
|
509
|
+
tlsEmail: config.tlsEmail,
|
|
510
|
+
dnsProvider: config.dns.provider,
|
|
511
|
+
dnsAutoManage: config.dns.autoManage,
|
|
512
|
+
smtpHost: config.smtp.host,
|
|
513
|
+
smtpPort: config.smtp.port,
|
|
514
|
+
smtpUser: config.smtp.user,
|
|
515
|
+
smtpPass: config.smtp.pass,
|
|
516
|
+
smtpFrom: config.smtp.from,
|
|
517
|
+
smtpFromName: config.smtp.fromName,
|
|
518
|
+
databaseType: config.database.type,
|
|
519
|
+
supabaseUrl: config.database.supabaseUrl ?? "",
|
|
520
|
+
supabaseAnonKey: config.database.supabaseAnonKey ?? "",
|
|
521
|
+
supabaseServiceKey: config.database.supabaseServiceKey ?? "",
|
|
522
|
+
supabaseAccessToken: config.database.supabaseAccessToken ?? "",
|
|
523
|
+
supabaseProjectRef: config.database.supabaseProjectRef ?? "",
|
|
524
|
+
supabaseJwtSecret: config.database.type === "self-hosted"
|
|
525
|
+
? config.database.supabaseJwtSecret || base.supabaseJwtSecret
|
|
526
|
+
: config.database.supabaseJwtSecret ?? "",
|
|
527
|
+
supabaseDbPassword: config.database.supabaseDbPassword ?? "",
|
|
528
|
+
supabaseDashboardUser: config.database.supabaseDashboardUser ?? base.supabaseDashboardUser,
|
|
529
|
+
supabaseDashboardPass: config.database.supabaseDashboardPass ?? "",
|
|
530
|
+
nodeArchitecture: config.infrastructure.nodeArchitecture ?? null,
|
|
531
|
+
arm64TolerationRequired: config.infrastructure.arm64TolerationRequired ?? false,
|
|
532
|
+
storageClass: config.infrastructure.storageClass ?? "",
|
|
533
|
+
storageProvisioner: config.infrastructure.storageProvisioner ?? "",
|
|
534
|
+
schedulableNodeCount: config.infrastructure.schedulableNodeCount ?? 0,
|
|
535
|
+
totalCpuCores: config.infrastructure.totalCpuCores ?? 0,
|
|
536
|
+
totalMemoryGi: config.infrastructure.totalMemoryGi ?? 0,
|
|
537
|
+
eligibleCpuCores: config.infrastructure.eligibleCpuCores ?? 0,
|
|
538
|
+
eligibleMemoryGi: config.infrastructure.eligibleMemoryGi ?? 0,
|
|
539
|
+
totalPersistentStorageGi: config.infrastructure.totalPersistentStorageGi ?? 0,
|
|
540
|
+
storageProvider: storage?.provider ?? base.storageProvider,
|
|
541
|
+
storageBucket: storage?.bucket ?? "",
|
|
542
|
+
storageRegion: storage?.region ?? "",
|
|
543
|
+
storageCloudAuthMode: storage?.cloudAuthMode ?? base.storageCloudAuthMode,
|
|
544
|
+
storageAwsIamRoleArn: storage?.awsIamRoleArn ?? "",
|
|
545
|
+
storageAzureBlobContainer: storage?.azureBlobContainer ?? base.storageAzureBlobContainer,
|
|
546
|
+
storageAzureBlobClientId: storage?.azureBlobClientId ?? "",
|
|
547
|
+
storageAzureBlobTenantId: storage?.azureBlobTenantId ?? "",
|
|
548
|
+
storageAzureBlobConnectionStringSecretRef: formatSecretKeyRef(storage?.azureBlobConnectionStringSecretRef),
|
|
549
|
+
storageGcpServiceAccountEmail: storage?.gcpServiceAccountEmail ?? "",
|
|
550
|
+
aiEnabled: config.features.ai.enabled,
|
|
551
|
+
openaiApiKey: config.features.ai.openaiApiKey ?? "",
|
|
552
|
+
ssoEnabled: config.features.sso.enabled,
|
|
553
|
+
ssoProvider: config.features.sso.provider ?? null,
|
|
554
|
+
ssoUrl: config.features.sso.url ?? "",
|
|
555
|
+
ssoClientId: config.features.sso.clientId ?? "",
|
|
556
|
+
ssoClientSecret: config.features.sso.clientSecret ?? "",
|
|
557
|
+
clickStackEnabled: config.features.observability?.clickstack?.enabled ?? true,
|
|
558
|
+
clickStackTelemetryRetentionDays: config.features.observability?.clickstack?.telemetryRetentionDays ??
|
|
559
|
+
base.clickStackTelemetryRetentionDays,
|
|
560
|
+
clickHouseStorageSize: config.features.observability?.clickstack?.clickHouseStorageSize ??
|
|
561
|
+
base.clickHouseStorageSize,
|
|
562
|
+
// The toggle reflects whether remote_write is actually configured, so a
|
|
563
|
+
// redeploy resumes with the metrics-export sub-flow only when in use.
|
|
564
|
+
metricsExportEnabled: !!(remoteWrite || config.features.monitoring.remoteWriteUrl),
|
|
565
|
+
prometheusMonitoringDestination: config.features.monitoring.destination ?? null,
|
|
566
|
+
prometheusRemoteWriteUrl: remoteWrite?.url ?? config.features.monitoring.remoteWriteUrl ?? "",
|
|
567
|
+
prometheusRemoteWriteDestination: remoteWrite?.destination ?? null,
|
|
568
|
+
prometheusRemoteWriteAuthType: remoteWrite?.authType ?? null,
|
|
569
|
+
prometheusRemoteWriteAwsRegion: remoteWrite?.awsRegion ?? "",
|
|
570
|
+
prometheusRemoteWriteAwsRoleArn: remoteWrite?.awsRoleArn ?? "",
|
|
571
|
+
prometheusRemoteWriteAzureCloud: remoteWrite?.azureCloud ?? base.prometheusRemoteWriteAzureCloud,
|
|
572
|
+
prometheusRemoteWriteClientId: remoteWrite?.clientId ?? "",
|
|
573
|
+
prometheusRemoteWriteTenantId: remoteWrite?.tenantId ?? "",
|
|
574
|
+
prometheusRemoteWriteSecretRef: formatSecretKeyRef(remoteWrite?.clientSecretRef),
|
|
575
|
+
prometheusRemoteWriteUsernameSecretRef: formatSecretKeyRef(remoteWrite?.usernameSecretRef),
|
|
576
|
+
prometheusRemoteWritePasswordSecretRef: formatSecretKeyRef(remoteWrite?.passwordSecretRef),
|
|
577
|
+
prometheusRemoteWriteBearerTokenSecretRef: formatSecretKeyRef(remoteWrite?.bearerTokenSecretRef),
|
|
578
|
+
loggingSink: config.features.logging.sink,
|
|
579
|
+
loggingPlatformCredential: config.features.logging.bucket ?? "",
|
|
580
|
+
loggingPlatformDetail: config.features.logging.region ?? "",
|
|
581
|
+
// Distributed tracing (Elastic APM / generic OTLP / Azure Monitor)
|
|
582
|
+
tracingEnabled: config.features.tracing?.enabled ?? false,
|
|
583
|
+
tracingDestination: config.features.tracing?.destination ?? "elastic",
|
|
584
|
+
tracingElasticEndpoint: config.features.tracing?.elastic?.endpoint ?? "",
|
|
585
|
+
tracingElasticAuthMode: config.features.tracing?.elastic?.authMode === "api-key"
|
|
586
|
+
? "api-key"
|
|
587
|
+
: "secret-token",
|
|
588
|
+
tracingElasticSecretToken: config.features.tracing?.elastic?.secretToken ?? "",
|
|
589
|
+
tracingElasticApiKey: config.features.tracing?.elastic?.apiKey ?? "",
|
|
590
|
+
tracingOtlpEndpoint: config.features.tracing?.otlp?.endpoint ?? "",
|
|
591
|
+
tracingOtlpAuthMode: config.features.tracing?.otlp?.authMode ?? "none",
|
|
592
|
+
tracingOtlpHeaderName: config.features.tracing?.otlp?.headerName ?? "Authorization",
|
|
593
|
+
tracingOtlpToken: config.features.tracing?.otlp?.token ??
|
|
594
|
+
config.features.tracing?.otlp?.apiKey ??
|
|
595
|
+
config.features.tracing?.otlp?.headerValue ??
|
|
596
|
+
"",
|
|
597
|
+
tracingAzureConnectionString: config.features.tracing?.azureMonitor?.connectionString ?? "",
|
|
598
|
+
// Application log shipping (Elasticsearch via Vector agent)
|
|
599
|
+
appLogsEnabled: config.features.logging.appLogs?.enabled ?? false,
|
|
600
|
+
appLogsElasticEndpoint: config.features.logging.appLogs?.elasticsearch?.endpoint ?? "",
|
|
601
|
+
appLogsElasticIndex: config.features.logging.appLogs?.elasticsearch?.index ??
|
|
602
|
+
base.appLogsElasticIndex,
|
|
603
|
+
appLogsElasticAuthMode: config.features.logging.appLogs?.elasticsearch?.authMode === "api-key"
|
|
604
|
+
? "api-key"
|
|
605
|
+
: "basic",
|
|
606
|
+
appLogsElasticUsername: config.features.logging.appLogs?.elasticsearch?.username ?? "",
|
|
607
|
+
appLogsElasticPassword: config.features.logging.appLogs?.elasticsearch?.password ?? "",
|
|
608
|
+
appLogsElasticApiKey: config.features.logging.appLogs?.elasticsearch?.apiKey ?? "",
|
|
609
|
+
customEmailsEnabled: customEmails?.enabled ?? false,
|
|
610
|
+
emailSubjects: customEmails?.subjects ?? base.emailSubjects,
|
|
611
|
+
emailTemplates: customEmails?.templates ?? base.emailTemplates,
|
|
612
|
+
backupEnabled: config.backup?.enabled ?? false,
|
|
613
|
+
backupSchedule: config.backup?.schedule ?? base.backupSchedule,
|
|
614
|
+
backupRetentionDays: config.backup?.retentionDays ?? base.backupRetentionDays,
|
|
615
|
+
// External services - Redis
|
|
616
|
+
redisMode: externalRedis?.mode ?? base.redisMode,
|
|
617
|
+
redisHost: externalRedis?.external?.host ?? base.redisHost,
|
|
618
|
+
redisPort: externalRedis?.external?.port ?? base.redisPort,
|
|
619
|
+
redisPassword: externalRedis?.external?.password ?? base.redisPassword,
|
|
620
|
+
redisExistingSecret: externalRedis?.external?.existingSecret ?? base.redisExistingSecret,
|
|
621
|
+
redisExistingSecretKey: externalRedis?.external?.existingSecretKey ?? base.redisExistingSecretKey,
|
|
622
|
+
redisTls: externalRedis?.external?.tls ?? base.redisTls,
|
|
623
|
+
redisHttpApiEnabled: externalRedis?.external?.httpApi?.enabled ?? base.redisHttpApiEnabled,
|
|
624
|
+
redisHttpApiUrl: externalRedis?.external?.httpApi?.url ?? base.redisHttpApiUrl,
|
|
625
|
+
redisHttpApiToken: externalRedis?.external?.httpApi?.token ?? base.redisHttpApiToken,
|
|
626
|
+
valkeyAdminEnabled: config.features.cache?.valkeyAdmin?.enabled ?? base.valkeyAdminEnabled,
|
|
627
|
+
valkeyAdminExposure: config.features.cache?.valkeyAdmin?.exposure ?? base.valkeyAdminExposure,
|
|
628
|
+
valkeyAdminHostname: config.features.cache?.valkeyAdmin?.hostname ?? base.valkeyAdminHostname,
|
|
629
|
+
valkeyAdminBasicAuthUsers: config.features.cache?.valkeyAdmin?.basicAuthUsers ??
|
|
630
|
+
base.valkeyAdminBasicAuthUsers,
|
|
631
|
+
valkeyAdminAllowedIPs: config.features.cache?.valkeyAdmin?.allowedIPs ??
|
|
632
|
+
base.valkeyAdminAllowedIPs,
|
|
633
|
+
redisExporterEnabled: config.features.cache?.redisExporter?.enabled ??
|
|
634
|
+
base.redisExporterEnabled,
|
|
635
|
+
kafkaExporterEnabled: config.features.cache?.kafkaExporter?.enabled ??
|
|
636
|
+
base.kafkaExporterEnabled,
|
|
637
|
+
// External services - Kafka
|
|
638
|
+
kafkaMode: externalKafka?.mode ?? base.kafkaMode,
|
|
639
|
+
kafkaPreset: externalKafka?.external?.preset ?? base.kafkaPreset,
|
|
640
|
+
kafkaBrokers: externalKafka?.external?.brokers ?? base.kafkaBrokers,
|
|
641
|
+
kafkaTopic: externalKafka?.external?.topic ?? base.kafkaTopic,
|
|
642
|
+
kafkaTopicPrefix: externalKafka?.external?.topicPrefix ?? base.kafkaTopicPrefix,
|
|
643
|
+
kafkaProvisionTopics: externalKafka?.external?.provisionTopics ?? base.kafkaProvisionTopics,
|
|
644
|
+
kafkaSsl: externalKafka?.external?.ssl ?? base.kafkaSsl,
|
|
645
|
+
kafkaSaslMechanism: externalKafka?.external?.sasl?.mechanism ?? base.kafkaSaslMechanism,
|
|
646
|
+
kafkaSaslRegion: externalKafka?.external?.sasl?.region ?? base.kafkaSaslRegion,
|
|
647
|
+
kafkaSaslUsername: externalKafka?.external?.sasl?.username ?? base.kafkaSaslUsername,
|
|
648
|
+
kafkaSaslPassword: externalKafka?.external?.sasl?.password ?? base.kafkaSaslPassword,
|
|
649
|
+
kafkaSaslExistingSecret: externalKafka?.external?.sasl?.existingSecret ??
|
|
650
|
+
base.kafkaSaslExistingSecret,
|
|
651
|
+
kafkaIdentityAwsRoleArn: externalKafka?.external?.identity?.awsRoleArn ??
|
|
652
|
+
base.kafkaIdentityAwsRoleArn,
|
|
653
|
+
kafkaIdentityGcpServiceAccountEmail: externalKafka?.external?.identity?.gcpServiceAccountEmail ??
|
|
654
|
+
base.kafkaIdentityGcpServiceAccountEmail,
|
|
655
|
+
kafkaIdentityAzureClientId: externalKafka?.external?.identity?.azureClientId ??
|
|
656
|
+
base.kafkaIdentityAzureClientId,
|
|
657
|
+
// External services - Postgres
|
|
658
|
+
postgresMode: externalPostgres?.mode ?? base.postgresMode,
|
|
659
|
+
postgresHost: externalPostgres?.external?.host ?? base.postgresHost,
|
|
660
|
+
postgresPort: externalPostgres?.external?.port ?? base.postgresPort,
|
|
661
|
+
postgresDatabase: externalPostgres?.external?.database ?? base.postgresDatabase,
|
|
662
|
+
postgresMasterUsername: externalPostgres?.external?.bootstrap?.masterUsername ??
|
|
663
|
+
base.postgresMasterUsername,
|
|
664
|
+
postgresMasterPassword: externalPostgres?.external?.bootstrap?.masterPassword ??
|
|
665
|
+
base.postgresMasterPassword,
|
|
666
|
+
licenseKey: config.licenseKey,
|
|
667
|
+
version: config.version,
|
|
668
|
+
chartVersion: config.chartVersion ?? "",
|
|
669
|
+
};
|
|
670
|
+
}
|
|
82
671
|
function wizardReducer(state, action) {
|
|
83
672
|
switch (action.type) {
|
|
84
673
|
case "SET_STEP":
|
|
85
674
|
return { ...state, step: action.step };
|
|
86
675
|
case "SET_NAME":
|
|
87
676
|
return { ...state, name: action.name };
|
|
88
|
-
case "SET_INFRA_MODE":
|
|
89
|
-
return { ...state, infrastructureMode: action.mode };
|
|
90
677
|
case "SET_PROVIDER":
|
|
91
|
-
|
|
678
|
+
if (action.provider === state.provider) {
|
|
679
|
+
return {
|
|
680
|
+
...state,
|
|
681
|
+
provider: action.provider,
|
|
682
|
+
region: "",
|
|
683
|
+
clusterName: "",
|
|
684
|
+
gcpProjectId: "",
|
|
685
|
+
azureResourceGroup: "",
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
...state,
|
|
690
|
+
provider: action.provider,
|
|
691
|
+
region: "",
|
|
692
|
+
clusterName: "",
|
|
693
|
+
gcpProjectId: "",
|
|
694
|
+
azureResourceGroup: "",
|
|
695
|
+
storageProvider: null,
|
|
696
|
+
storageBucket: "",
|
|
697
|
+
storageRegion: "",
|
|
698
|
+
storageAwsIamRoleArn: "",
|
|
699
|
+
storageAzureBlobContainer: "",
|
|
700
|
+
storageAzureBlobClientId: "",
|
|
701
|
+
storageAzureBlobTenantId: "",
|
|
702
|
+
storageAzureBlobConnectionStringSecretRef: "",
|
|
703
|
+
storageGcpServiceAccountEmail: "",
|
|
704
|
+
};
|
|
92
705
|
case "SET_REGION":
|
|
93
706
|
return { ...state, region: action.region };
|
|
94
707
|
case "SET_CLUSTER_NAME":
|
|
@@ -101,8 +714,6 @@ function wizardReducer(state, action) {
|
|
|
101
714
|
return { ...state, domain: action.domain };
|
|
102
715
|
case "SET_ADMIN_EMAIL":
|
|
103
716
|
return { ...state, adminEmail: action.email };
|
|
104
|
-
case "SET_TLS_EMAIL":
|
|
105
|
-
return { ...state, tlsEmail: action.email };
|
|
106
717
|
case "SET_DNS_PROVIDER":
|
|
107
718
|
// Reset auto-manage if switching to unsupported provider
|
|
108
719
|
return {
|
|
@@ -112,8 +723,6 @@ function wizardReducer(state, action) {
|
|
|
112
723
|
};
|
|
113
724
|
case "SET_DNS_AUTO_MANAGE":
|
|
114
725
|
return { ...state, dnsAutoManage: action.autoManage };
|
|
115
|
-
case "SET_EXISTING_EXTERNAL_DNS":
|
|
116
|
-
return { ...state, existingExternalDns: action.exists };
|
|
117
726
|
case "SET_SMTP":
|
|
118
727
|
return { ...state, ...action.config };
|
|
119
728
|
case "SET_DATABASE_TYPE":
|
|
@@ -122,8 +731,20 @@ function wizardReducer(state, action) {
|
|
|
122
731
|
return { ...state, ...action.config };
|
|
123
732
|
case "SET_SUPABASE_SELF_HOSTED":
|
|
124
733
|
return { ...state, ...action.config };
|
|
125
|
-
case "
|
|
126
|
-
return {
|
|
734
|
+
case "SET_CLUSTER_CAPABILITIES":
|
|
735
|
+
return {
|
|
736
|
+
...state,
|
|
737
|
+
nodeArchitecture: action.nodeArchitecture,
|
|
738
|
+
arm64TolerationRequired: action.arm64TolerationRequired,
|
|
739
|
+
storageClass: action.storageClass ?? state.storageClass,
|
|
740
|
+
storageProvisioner: action.storageProvisioner ?? state.storageProvisioner,
|
|
741
|
+
schedulableNodeCount: action.schedulableNodeCount ?? state.schedulableNodeCount,
|
|
742
|
+
totalCpuCores: action.totalCpuCores ?? state.totalCpuCores,
|
|
743
|
+
totalMemoryGi: action.totalMemoryGi ?? state.totalMemoryGi,
|
|
744
|
+
eligibleCpuCores: action.eligibleCpuCores ?? state.eligibleCpuCores,
|
|
745
|
+
eligibleMemoryGi: action.eligibleMemoryGi ?? state.eligibleMemoryGi,
|
|
746
|
+
totalPersistentStorageGi: action.totalPersistentStorageGi ?? state.totalPersistentStorageGi,
|
|
747
|
+
};
|
|
127
748
|
case "SET_AI_ENABLED":
|
|
128
749
|
return { ...state, aiEnabled: action.enabled };
|
|
129
750
|
case "SET_OPENAI_KEY":
|
|
@@ -132,20 +753,68 @@ function wizardReducer(state, action) {
|
|
|
132
753
|
return { ...state, ssoEnabled: action.enabled };
|
|
133
754
|
case "SET_SSO_CONFIG":
|
|
134
755
|
return { ...state, ...action.config };
|
|
135
|
-
case "
|
|
136
|
-
return {
|
|
756
|
+
case "SET_METRICS_EXPORT":
|
|
757
|
+
return {
|
|
758
|
+
...state,
|
|
759
|
+
clickStackEnabled: action.enabled ? false : state.clickStackEnabled,
|
|
760
|
+
metricsExportEnabled: action.enabled,
|
|
761
|
+
};
|
|
137
762
|
case "SET_PROMETHEUS_REMOTE_WRITE":
|
|
138
763
|
return { ...state, prometheusRemoteWriteUrl: action.url };
|
|
764
|
+
case "SET_PROMETHEUS_REMOTE_WRITE_CONFIG":
|
|
765
|
+
return { ...state, ...action.config };
|
|
139
766
|
case "SET_LOGGING_SINK":
|
|
140
|
-
// Reset
|
|
767
|
+
// Reset the platform credential/detail if switching back to console.
|
|
141
768
|
return {
|
|
142
769
|
...state,
|
|
143
770
|
loggingSink: action.sink,
|
|
144
|
-
|
|
145
|
-
|
|
771
|
+
loggingPlatformCredential: action.sink === "console" ? "" : state.loggingPlatformCredential,
|
|
772
|
+
loggingPlatformDetail: action.sink === "console" ? "" : state.loggingPlatformDetail,
|
|
146
773
|
};
|
|
774
|
+
case "SET_STORAGE_CONFIG":
|
|
775
|
+
return { ...state, ...action.config };
|
|
147
776
|
case "SET_LOGGING_CONFIG":
|
|
148
777
|
return { ...state, ...action.config };
|
|
778
|
+
case "SET_CLICKSTACK_ENABLED":
|
|
779
|
+
return {
|
|
780
|
+
...state,
|
|
781
|
+
clickStackEnabled: action.enabled,
|
|
782
|
+
metricsExportEnabled: action.enabled ? false : state.metricsExportEnabled,
|
|
783
|
+
tracingEnabled: action.enabled ? false : state.tracingEnabled,
|
|
784
|
+
appLogsEnabled: action.enabled ? false : state.appLogsEnabled,
|
|
785
|
+
};
|
|
786
|
+
case "SET_CLICKSTACK_CONFIG":
|
|
787
|
+
return { ...state, ...action.config };
|
|
788
|
+
case "SET_TRACING_ENABLED":
|
|
789
|
+
return {
|
|
790
|
+
...state,
|
|
791
|
+
clickStackEnabled: action.enabled ? false : state.clickStackEnabled,
|
|
792
|
+
tracingEnabled: action.enabled,
|
|
793
|
+
};
|
|
794
|
+
case "SET_TRACING_CONFIG":
|
|
795
|
+
return { ...state, ...action.config };
|
|
796
|
+
case "SET_APP_LOGS_ENABLED":
|
|
797
|
+
return {
|
|
798
|
+
...state,
|
|
799
|
+
clickStackEnabled: action.enabled ? false : state.clickStackEnabled,
|
|
800
|
+
appLogsEnabled: action.enabled,
|
|
801
|
+
};
|
|
802
|
+
case "SET_APP_LOGS_CONFIG":
|
|
803
|
+
return { ...state, ...action.config };
|
|
804
|
+
case "SET_BACKUP_ENABLED":
|
|
805
|
+
return { ...state, backupEnabled: action.enabled };
|
|
806
|
+
case "SET_BACKUP_SCHEDULE":
|
|
807
|
+
return { ...state, backupSchedule: action.schedule };
|
|
808
|
+
case "SET_BACKUP_RETENTION_DAYS":
|
|
809
|
+
return { ...state, backupRetentionDays: action.retentionDays };
|
|
810
|
+
case "SET_EXTERNAL_SERVICES":
|
|
811
|
+
return {
|
|
812
|
+
...state,
|
|
813
|
+
...action.config,
|
|
814
|
+
...(action.config.postgresMode === "external"
|
|
815
|
+
? { backupEnabled: false }
|
|
816
|
+
: {}),
|
|
817
|
+
};
|
|
149
818
|
case "SET_CUSTOM_EMAILS_ENABLED":
|
|
150
819
|
return { ...state, customEmailsEnabled: action.enabled };
|
|
151
820
|
case "SET_EMAIL_SUBJECTS":
|
|
@@ -160,11 +829,10 @@ function wizardReducer(state, action) {
|
|
|
160
829
|
};
|
|
161
830
|
case "SET_LICENSE_KEY":
|
|
162
831
|
return { ...state, licenseKey: action.key };
|
|
163
|
-
case "
|
|
832
|
+
case "SET_VERSION":
|
|
164
833
|
return {
|
|
165
834
|
...state,
|
|
166
|
-
|
|
167
|
-
hpsVersion: action.hpsVersion,
|
|
835
|
+
version: action.version,
|
|
168
836
|
};
|
|
169
837
|
case "SET_CHART_VERSION":
|
|
170
838
|
return { ...state, chartVersion: action.version };
|
|
@@ -177,62 +845,58 @@ function wizardReducer(state, action) {
|
|
|
177
845
|
}
|
|
178
846
|
}
|
|
179
847
|
const WizardContext = createContext(null);
|
|
180
|
-
export function WizardProvider({ children, initialName, profile, }) {
|
|
848
|
+
export function WizardProvider({ children, initialName, initialState, profile, }) {
|
|
181
849
|
// Initialize state with profile values for pre-population
|
|
182
850
|
const [state, dispatch] = useReducer(wizardReducer, {
|
|
183
851
|
...getInitialState(profile),
|
|
184
|
-
|
|
852
|
+
...initialState,
|
|
853
|
+
name: initialState?.name || initialName || "",
|
|
185
854
|
});
|
|
186
|
-
const toConfig = () => {
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
!state.tlsEmail ||
|
|
192
|
-
!state.licenseKey) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
// Validate SMTP
|
|
196
|
-
if (!state.smtpHost ||
|
|
197
|
-
!state.smtpUser ||
|
|
198
|
-
!state.smtpPass ||
|
|
199
|
-
!state.smtpFrom) {
|
|
855
|
+
const toConfig = (options = {}) => {
|
|
856
|
+
// All field/credential gates (including the remote_write checks) live in one
|
|
857
|
+
// place so the review screen can show the specific reason a config is
|
|
858
|
+
// invalid instead of a generic message.
|
|
859
|
+
if (collectConfigIssues(state).length > 0) {
|
|
200
860
|
return null;
|
|
201
861
|
}
|
|
202
|
-
//
|
|
203
|
-
if (state.
|
|
204
|
-
if (!state.supabaseUrl ||
|
|
205
|
-
!state.supabaseAnonKey ||
|
|
206
|
-
!state.supabaseServiceKey) {
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
else if (state.databaseType === "self-hosted") {
|
|
211
|
-
if (!state.supabaseDbPassword) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// Validate logging sink config
|
|
216
|
-
if (state.loggingSink !== "console" && !state.loggingBucket) {
|
|
862
|
+
// collectConfigIssues guarantees these, but narrow them for the type checker.
|
|
863
|
+
if (!state.storageProvider) {
|
|
217
864
|
return null;
|
|
218
865
|
}
|
|
866
|
+
const externalServices = buildExternalServices(state);
|
|
867
|
+
const remoteWrite = !state.clickStackEnabled && state.metricsExportEnabled
|
|
868
|
+
? buildRemoteWriteFromState(state)
|
|
869
|
+
: undefined;
|
|
219
870
|
return {
|
|
220
871
|
name: state.name,
|
|
221
872
|
infrastructure: {
|
|
222
|
-
mode:
|
|
873
|
+
mode: "existing",
|
|
223
874
|
provider: state.provider || undefined,
|
|
224
875
|
region: state.region || undefined,
|
|
225
876
|
clusterName: state.clusterName || undefined,
|
|
226
877
|
gcpProjectId: state.gcpProjectId || undefined,
|
|
227
878
|
azureResourceGroup: state.azureResourceGroup || undefined,
|
|
879
|
+
nodeArchitecture: options.nodeArchitecture || state.nodeArchitecture || undefined,
|
|
880
|
+
arm64TolerationRequired: options.arm64TolerationRequired ?? state.arm64TolerationRequired,
|
|
881
|
+
storageClass: options.storageClass || state.storageClass || undefined,
|
|
882
|
+
storageProvisioner: options.storageProvisioner || state.storageProvisioner || undefined,
|
|
883
|
+
schedulableNodeCount: options.schedulableNodeCount || state.schedulableNodeCount || undefined,
|
|
884
|
+
totalCpuCores: options.totalCpuCores || state.totalCpuCores || undefined,
|
|
885
|
+
totalMemoryGi: options.totalMemoryGi || state.totalMemoryGi || undefined,
|
|
886
|
+
eligibleCpuCores: options.eligibleCpuCores || state.eligibleCpuCores || undefined,
|
|
887
|
+
eligibleMemoryGi: options.eligibleMemoryGi || state.eligibleMemoryGi || undefined,
|
|
888
|
+
totalPersistentStorageGi: options.totalPersistentStorageGi ||
|
|
889
|
+
state.totalPersistentStorageGi ||
|
|
890
|
+
undefined,
|
|
228
891
|
},
|
|
229
892
|
domain: state.domain,
|
|
230
893
|
adminEmail: state.adminEmail,
|
|
231
|
-
|
|
894
|
+
// Not asked in the wizard; defaults to the admin email. A custom value
|
|
895
|
+
// survives redeploys via configToWizardState / config.yaml edits.
|
|
896
|
+
tlsEmail: state.tlsEmail || state.adminEmail,
|
|
232
897
|
dns: {
|
|
233
898
|
provider: state.dnsProvider,
|
|
234
899
|
autoManage: state.dnsAutoManage,
|
|
235
|
-
existingExternalDns: state.existingExternalDns || undefined,
|
|
236
900
|
},
|
|
237
901
|
smtp: {
|
|
238
902
|
host: state.smtpHost,
|
|
@@ -248,13 +912,52 @@ export function WizardProvider({ children, initialName, profile, }) {
|
|
|
248
912
|
supabaseAnonKey: state.supabaseAnonKey || undefined,
|
|
249
913
|
supabaseServiceKey: state.supabaseServiceKey || undefined,
|
|
250
914
|
supabaseAccessToken: state.supabaseAccessToken || undefined,
|
|
251
|
-
|
|
915
|
+
// Derived from the project URL when not set explicitly (config.yaml
|
|
916
|
+
// edits and redeploy merges still win).
|
|
917
|
+
supabaseProjectRef: state.supabaseProjectRef ||
|
|
918
|
+
(state.databaseType === "supabase-cloud" && state.supabaseUrl
|
|
919
|
+
? deriveSupabaseProjectRef(state.supabaseUrl)
|
|
920
|
+
: undefined) ||
|
|
921
|
+
undefined,
|
|
252
922
|
supabaseJwtSecret: state.supabaseJwtSecret || undefined,
|
|
253
923
|
supabaseDbPassword: state.supabaseDbPassword || undefined,
|
|
254
924
|
supabaseDashboardUser: state.supabaseDashboardUser || undefined,
|
|
255
925
|
supabaseDashboardPass: state.supabaseDashboardPass || undefined,
|
|
256
926
|
},
|
|
257
|
-
|
|
927
|
+
storage: {
|
|
928
|
+
provider: state.storageProvider,
|
|
929
|
+
cloudAuthMode: state.storageCloudAuthMode,
|
|
930
|
+
bucket: state.storageBucket,
|
|
931
|
+
region: state.storageRegion,
|
|
932
|
+
awsIamRoleArn: state.storageProvider === "s3"
|
|
933
|
+
? state.storageAwsIamRoleArn || undefined
|
|
934
|
+
: undefined,
|
|
935
|
+
azureBlobClientId: state.storageProvider === "azure-blob"
|
|
936
|
+
? state.storageAzureBlobClientId || undefined
|
|
937
|
+
: undefined,
|
|
938
|
+
azureBlobTenantId: state.storageProvider === "azure-blob"
|
|
939
|
+
? state.storageAzureBlobTenantId || undefined
|
|
940
|
+
: undefined,
|
|
941
|
+
azureBlobConnectionStringSecretRef: state.storageProvider === "azure-blob"
|
|
942
|
+
? parseSecretKeyRef(state.storageAzureBlobConnectionStringSecretRef)
|
|
943
|
+
: undefined,
|
|
944
|
+
azureBlobContainer: state.storageProvider === "azure-blob"
|
|
945
|
+
? state.storageAzureBlobContainer || undefined
|
|
946
|
+
: undefined,
|
|
947
|
+
gcpServiceAccountEmail: state.storageProvider === "gcs"
|
|
948
|
+
? state.storageGcpServiceAccountEmail || undefined
|
|
949
|
+
: undefined,
|
|
950
|
+
paths: {
|
|
951
|
+
decisionLogs: "decision-logs",
|
|
952
|
+
dbBackups: "db-backups",
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
backup: {
|
|
956
|
+
enabled: state.databaseType === "self-hosted" ? state.backupEnabled : false,
|
|
957
|
+
schedule: state.backupSchedule || "0 2 * * *",
|
|
958
|
+
retentionDays: state.backupRetentionDays || 7,
|
|
959
|
+
},
|
|
960
|
+
externalServices,
|
|
258
961
|
features: {
|
|
259
962
|
ai: {
|
|
260
963
|
enabled: state.aiEnabled,
|
|
@@ -268,13 +971,130 @@ export function WizardProvider({ children, initialName, profile, }) {
|
|
|
268
971
|
clientSecret: state.ssoClientSecret || undefined,
|
|
269
972
|
},
|
|
270
973
|
monitoring: {
|
|
271
|
-
|
|
272
|
-
|
|
974
|
+
// In-cluster Prometheus is always installed.
|
|
975
|
+
enabled: true,
|
|
976
|
+
destination: !state.clickStackEnabled && state.metricsExportEnabled
|
|
977
|
+
? state.prometheusMonitoringDestination ||
|
|
978
|
+
remoteWrite?.destination ||
|
|
979
|
+
undefined
|
|
980
|
+
: // "local-grafana" is a config-file-only option (in-cluster
|
|
981
|
+
// Grafana, no remote write) and must survive redeploys.
|
|
982
|
+
state.prometheusMonitoringDestination === "local-grafana"
|
|
983
|
+
? "local-grafana"
|
|
984
|
+
: undefined,
|
|
985
|
+
remoteWriteUrl: !state.clickStackEnabled && state.metricsExportEnabled
|
|
986
|
+
? state.prometheusRemoteWriteUrl || undefined
|
|
987
|
+
: undefined,
|
|
988
|
+
remoteWrite,
|
|
989
|
+
},
|
|
990
|
+
observability: {
|
|
991
|
+
clickstack: {
|
|
992
|
+
enabled: state.clickStackEnabled,
|
|
993
|
+
telemetryRetentionDays: state.clickStackTelemetryRetentionDays,
|
|
994
|
+
clickHouseStorageSize: state.clickHouseStorageSize,
|
|
995
|
+
},
|
|
273
996
|
},
|
|
997
|
+
// Distributed tracing (self-hosted only). Omitted when disabled. The
|
|
998
|
+
// destination selects which backend sub-block is emitted.
|
|
999
|
+
tracing: !state.clickStackEnabled && state.tracingEnabled
|
|
1000
|
+
? state.tracingDestination === "otlp"
|
|
1001
|
+
? {
|
|
1002
|
+
enabled: true,
|
|
1003
|
+
destination: "otlp",
|
|
1004
|
+
otlp: {
|
|
1005
|
+
endpoint: state.tracingOtlpEndpoint || undefined,
|
|
1006
|
+
authMode: state.tracingOtlpAuthMode,
|
|
1007
|
+
headerName: state.tracingOtlpAuthMode === "header"
|
|
1008
|
+
? state.tracingOtlpHeaderName || undefined
|
|
1009
|
+
: undefined,
|
|
1010
|
+
token: state.tracingOtlpAuthMode === "bearer"
|
|
1011
|
+
? state.tracingOtlpToken || undefined
|
|
1012
|
+
: undefined,
|
|
1013
|
+
apiKey: state.tracingOtlpAuthMode === "api-key"
|
|
1014
|
+
? state.tracingOtlpToken || undefined
|
|
1015
|
+
: undefined,
|
|
1016
|
+
headerValue: state.tracingOtlpAuthMode === "header"
|
|
1017
|
+
? state.tracingOtlpToken || undefined
|
|
1018
|
+
: undefined,
|
|
1019
|
+
},
|
|
1020
|
+
}
|
|
1021
|
+
: state.tracingDestination === "azure-monitor"
|
|
1022
|
+
? {
|
|
1023
|
+
enabled: true,
|
|
1024
|
+
destination: "azure-monitor",
|
|
1025
|
+
azureMonitor: {
|
|
1026
|
+
connectionString: state.tracingAzureConnectionString || undefined,
|
|
1027
|
+
},
|
|
1028
|
+
}
|
|
1029
|
+
: {
|
|
1030
|
+
enabled: true,
|
|
1031
|
+
destination: "elastic",
|
|
1032
|
+
elastic: {
|
|
1033
|
+
endpoint: state.tracingElasticEndpoint || undefined,
|
|
1034
|
+
authMode: state.tracingElasticAuthMode,
|
|
1035
|
+
secretToken: state.tracingElasticAuthMode === "secret-token"
|
|
1036
|
+
? state.tracingElasticSecretToken || undefined
|
|
1037
|
+
: undefined,
|
|
1038
|
+
apiKey: state.tracingElasticAuthMode === "api-key"
|
|
1039
|
+
? state.tracingElasticApiKey || undefined
|
|
1040
|
+
: undefined,
|
|
1041
|
+
},
|
|
1042
|
+
}
|
|
1043
|
+
: undefined,
|
|
1044
|
+
cache: state.valkeyAdminEnabled ||
|
|
1045
|
+
state.redisExporterEnabled ||
|
|
1046
|
+
state.kafkaExporterEnabled
|
|
1047
|
+
? {
|
|
1048
|
+
valkeyAdmin: state.valkeyAdminEnabled
|
|
1049
|
+
? {
|
|
1050
|
+
enabled: true,
|
|
1051
|
+
exposure: state.valkeyAdminExposure,
|
|
1052
|
+
hostname: state.valkeyAdminHostname || undefined,
|
|
1053
|
+
basicAuthUsers: state.valkeyAdminBasicAuthUsers.length > 0
|
|
1054
|
+
? state.valkeyAdminBasicAuthUsers
|
|
1055
|
+
: undefined,
|
|
1056
|
+
allowedIPs: state.valkeyAdminAllowedIPs.length > 0
|
|
1057
|
+
? state.valkeyAdminAllowedIPs
|
|
1058
|
+
: undefined,
|
|
1059
|
+
}
|
|
1060
|
+
: undefined,
|
|
1061
|
+
redisExporter: state.redisExporterEnabled
|
|
1062
|
+
? { enabled: true }
|
|
1063
|
+
: undefined,
|
|
1064
|
+
kafkaExporter: state.kafkaExporterEnabled
|
|
1065
|
+
? { enabled: true }
|
|
1066
|
+
: undefined,
|
|
1067
|
+
}
|
|
1068
|
+
: undefined,
|
|
274
1069
|
logging: {
|
|
1070
|
+
// External logging is now a platform-only sink (Datadog, Splunk,
|
|
1071
|
+
// etc.). The persisted bucket/region keys carry the platform
|
|
1072
|
+
// credential and endpoint/detail (not an object-storage bucket).
|
|
1073
|
+
// Cloud object storage for decision logs is configured separately
|
|
1074
|
+
// under `storage` above.
|
|
275
1075
|
sink: state.loggingSink,
|
|
276
|
-
bucket: state.
|
|
277
|
-
region: state.
|
|
1076
|
+
bucket: state.loggingPlatformCredential || undefined,
|
|
1077
|
+
region: state.loggingPlatformDetail || undefined,
|
|
1078
|
+
// Application/container log shipping to Elasticsearch (Vector agent).
|
|
1079
|
+
appLogs: !state.clickStackEnabled && state.appLogsEnabled
|
|
1080
|
+
? {
|
|
1081
|
+
enabled: true,
|
|
1082
|
+
elasticsearch: {
|
|
1083
|
+
endpoint: state.appLogsElasticEndpoint || undefined,
|
|
1084
|
+
index: state.appLogsElasticIndex || undefined,
|
|
1085
|
+
authMode: state.appLogsElasticAuthMode,
|
|
1086
|
+
username: state.appLogsElasticAuthMode === "basic"
|
|
1087
|
+
? state.appLogsElasticUsername || undefined
|
|
1088
|
+
: undefined,
|
|
1089
|
+
password: state.appLogsElasticAuthMode === "basic"
|
|
1090
|
+
? state.appLogsElasticPassword || undefined
|
|
1091
|
+
: undefined,
|
|
1092
|
+
apiKey: state.appLogsElasticAuthMode === "api-key"
|
|
1093
|
+
? state.appLogsElasticApiKey || undefined
|
|
1094
|
+
: undefined,
|
|
1095
|
+
},
|
|
1096
|
+
}
|
|
1097
|
+
: undefined,
|
|
278
1098
|
},
|
|
279
1099
|
customEmails: state.customEmailsEnabled
|
|
280
1100
|
? {
|
|
@@ -290,51 +1110,36 @@ export function WizardProvider({ children, initialName, profile, }) {
|
|
|
290
1110
|
: undefined,
|
|
291
1111
|
},
|
|
292
1112
|
licenseKey: state.licenseKey,
|
|
293
|
-
|
|
294
|
-
hpsVersion: state.hpsVersion || undefined,
|
|
1113
|
+
version: state.version,
|
|
295
1114
|
chartVersion: state.chartVersion || undefined,
|
|
296
1115
|
};
|
|
297
1116
|
};
|
|
298
1117
|
const skipToStep = (stepId) => {
|
|
299
|
-
// For conditional step skipping
|
|
300
1118
|
const stepIndex = [
|
|
301
|
-
"mode",
|
|
302
1119
|
"cloud",
|
|
303
1120
|
"domain",
|
|
304
1121
|
"smtp",
|
|
305
1122
|
"database",
|
|
306
1123
|
"database-creds",
|
|
307
|
-
"
|
|
1124
|
+
"external-services",
|
|
1125
|
+
"storage",
|
|
1126
|
+
"observability",
|
|
308
1127
|
"features",
|
|
309
1128
|
"feature-config",
|
|
310
|
-
"
|
|
1129
|
+
"version",
|
|
311
1130
|
"review",
|
|
312
1131
|
].indexOf(stepId);
|
|
313
1132
|
if (stepIndex >= 0) {
|
|
314
1133
|
dispatch({ type: "SET_STEP", step: stepIndex });
|
|
315
1134
|
}
|
|
316
1135
|
};
|
|
317
|
-
/**
|
|
318
|
-
* Suggests a domain based on the profile's domain suffix and a deployment name.
|
|
319
|
-
* e.g., if profile has domainSuffix ".example.com" and name is "staging",
|
|
320
|
-
* suggests "staging.example.com"
|
|
321
|
-
*/
|
|
322
|
-
const suggestDomain = (name) => {
|
|
323
|
-
if (!profile?.domainSuffix || !name)
|
|
324
|
-
return "";
|
|
325
|
-
// Remove leading dot if present and combine with name
|
|
326
|
-
const suffix = profile.domainSuffix.startsWith(".")
|
|
327
|
-
? profile.domainSuffix.slice(1)
|
|
328
|
-
: profile.domainSuffix;
|
|
329
|
-
return `${name}.${suffix}`;
|
|
330
|
-
};
|
|
331
1136
|
return (_jsx(WizardContext.Provider, { value: {
|
|
332
1137
|
state,
|
|
333
1138
|
dispatch,
|
|
334
1139
|
toConfig,
|
|
1140
|
+
configIssues: () => collectConfigIssues(state),
|
|
335
1141
|
skipToStep,
|
|
336
1142
|
profile: profile ?? null,
|
|
337
|
-
suggestDomain,
|
|
338
1143
|
}, children: children }));
|
|
339
1144
|
}
|
|
340
1145
|
export function useWizard() {
|