@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
|
@@ -3,8 +3,7 @@ import { useState } from 'react';
|
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import { useWizard } from '../WizardContext.js';
|
|
5
5
|
import { BorderBox, useTheme } from '../../common/index.js';
|
|
6
|
-
|
|
7
|
-
// Five features: AI, SSO, Monitoring, External Logging, Custom Emails
|
|
6
|
+
// Features: AI, SSO, External Logging, Custom Emails
|
|
8
7
|
// External DNS is handled in Domain step, not here
|
|
9
8
|
const FEATURES = [
|
|
10
9
|
{
|
|
@@ -20,17 +19,16 @@ const FEATURES = [
|
|
|
20
19
|
requiresConfig: true
|
|
21
20
|
},
|
|
22
21
|
{
|
|
23
|
-
id: '
|
|
24
|
-
label: '
|
|
25
|
-
description: '
|
|
26
|
-
requiresConfig:
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: 'logging',
|
|
30
|
-
label: 'External Logging',
|
|
31
|
-
description: 'Forward logs to cloud storage or logging platforms (Datadog, Splunk, etc.)',
|
|
32
|
-
requiresConfig: true
|
|
22
|
+
id: 'valkeyObservability',
|
|
23
|
+
label: 'Valkey Admin + Cache Metrics',
|
|
24
|
+
description: 'Deploy the official Apache-2.0 Valkey Admin console (internal by default, optionally public via Traefik BasicAuth) and export Valkey/Kafka lag metrics to Prometheus.',
|
|
25
|
+
requiresConfig: false
|
|
33
26
|
},
|
|
27
|
+
// NOTE: Forwarding a copy of decision logs to a third-party platform
|
|
28
|
+
// (Datadog, Splunk, Elasticsearch, Loki, New Relic, Axiom) is no longer
|
|
29
|
+
// offered in the wizard - it was confusing alongside the always-on
|
|
30
|
+
// decision-log archive to object storage. The capability still exists for
|
|
31
|
+
// config-file/redeploy users (features.logging.sink + generateVectorSinks).
|
|
34
32
|
{
|
|
35
33
|
id: 'customEmails',
|
|
36
34
|
label: 'Custom Email Templates',
|
|
@@ -45,8 +43,7 @@ export function FeaturesStep({ onComplete, onBack }) {
|
|
|
45
43
|
const enabledFeatures = {
|
|
46
44
|
ai: state.aiEnabled,
|
|
47
45
|
sso: state.ssoEnabled,
|
|
48
|
-
|
|
49
|
-
logging: state.loggingSink !== 'console', // External logging is "enabled" if not console-only (includes 'pending')
|
|
46
|
+
valkeyObservability: state.valkeyAdminEnabled,
|
|
50
47
|
customEmails: state.customEmailsEnabled
|
|
51
48
|
};
|
|
52
49
|
useInput((input, key) => {
|
|
@@ -86,34 +83,33 @@ export function FeaturesStep({ onComplete, onBack }) {
|
|
|
86
83
|
case 'sso':
|
|
87
84
|
dispatch({ type: 'SET_SSO_ENABLED', enabled: !state.ssoEnabled });
|
|
88
85
|
break;
|
|
89
|
-
case '
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
86
|
+
case 'valkeyObservability': {
|
|
87
|
+
const enabled = !state.valkeyAdminEnabled;
|
|
88
|
+
dispatch({
|
|
89
|
+
type: 'SET_EXTERNAL_SERVICES',
|
|
90
|
+
config: {
|
|
91
|
+
valkeyAdminEnabled: enabled,
|
|
92
|
+
redisExporterEnabled: enabled,
|
|
93
|
+
kafkaExporterEnabled: enabled,
|
|
94
|
+
valkeyAdminExposure: enabled ? 'ingress' : 'internal',
|
|
95
|
+
valkeyAdminHostname: '',
|
|
96
|
+
...(enabled ? {} : { valkeyAdminBasicAuthUsers: [], valkeyAdminAllowedIPs: [] })
|
|
97
|
+
}
|
|
98
|
+
});
|
|
101
99
|
break;
|
|
100
|
+
}
|
|
102
101
|
case 'customEmails':
|
|
103
102
|
dispatch({ type: 'SET_CUSTOM_EMAILS_ENABLED', enabled: !state.customEmailsEnabled });
|
|
104
103
|
break;
|
|
105
104
|
}
|
|
106
105
|
};
|
|
107
|
-
// Get current logging sink description
|
|
108
|
-
const getLoggingStatusText = () => {
|
|
109
|
-
if (state.loggingSink === 'console') {
|
|
110
|
-
return 'Console only (default)';
|
|
111
|
-
}
|
|
112
|
-
return LOGGING_SINK_INFO[state.loggingSink]?.name || state.loggingSink;
|
|
113
|
-
};
|
|
114
106
|
return (_jsxs(BorderBox, { title: "Optional Features", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select features to enable:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Use arrows to navigate, space/enter to toggle" })] }), _jsxs(Box, { flexDirection: "column", marginY: 1, children: [FEATURES.map((feature, index) => {
|
|
115
107
|
const isSelected = index === currentIndex;
|
|
116
108
|
const isEnabled = enabledFeatures[feature.id];
|
|
117
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: isSelected ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? colors.accent : undefined, children: isSelected ? '❯ ' : ' ' }), _jsx(Text, { color: isEnabled ? colors.success : colors.muted, children: isEnabled ? '[✓]' : '[ ]' }), _jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [' ', feature.label] })
|
|
118
|
-
}), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: currentIndex === FEATURES.length ? colors.accent : colors.muted, children: currentIndex === FEATURES.length ? '❯ ' : ' ' }), _jsx(Text, { color: currentIndex === FEATURES.length ? colors.success : colors.muted, bold: currentIndex === FEATURES.length, children: "[Continue \u2192]" })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", dimColor: true, children: "Space/Enter to toggle \u2022 \u2191/\u2193 to navigate \u2022 Esc to go back" }), (state.aiEnabled ||
|
|
109
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: isSelected ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? colors.accent : undefined, children: isSelected ? '❯ ' : ' ' }), _jsx(Text, { color: isEnabled ? colors.success : colors.muted, children: isEnabled ? '[✓]' : '[ ]' }), _jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [' ', feature.label] })] }), isSelected && (_jsx(Box, { marginLeft: 6, children: _jsx(Text, { color: "gray", dimColor: true, children: feature.description }) }))] }, feature.id));
|
|
110
|
+
}), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: currentIndex === FEATURES.length ? colors.accent : colors.muted, children: currentIndex === FEATURES.length ? '❯ ' : ' ' }), _jsx(Text, { color: currentIndex === FEATURES.length ? colors.success : colors.muted, bold: currentIndex === FEATURES.length, children: "[Continue \u2192]" })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", dimColor: true, children: "Space/Enter to toggle \u2022 \u2191/\u2193 to navigate \u2022 Esc to go back" }), (state.aiEnabled ||
|
|
111
|
+
state.ssoEnabled ||
|
|
112
|
+
state.valkeyAdminEnabled ||
|
|
113
|
+
state.loggingSink !== 'console' ||
|
|
114
|
+
state.customEmailsEnabled) && (_jsx(Text, { color: "yellow", dimColor: true, children: "Note: Enabled features will be configured in the next step" }))] })] }));
|
|
119
115
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Box, Text, useInput } from "ink";
|
|
4
|
+
import SelectInput from "ink-select-input";
|
|
5
|
+
import TextInput from "ink-text-input";
|
|
6
|
+
import { useWizard } from "../WizardContext.js";
|
|
7
|
+
import { BorderBox, useTheme } from "../../common/index.js";
|
|
8
|
+
const MODE_OPTIONS = [
|
|
9
|
+
{
|
|
10
|
+
label: "Use Rulebricks built-in observability (ClickStack + HyperDX)",
|
|
11
|
+
value: "built-in",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: "Export to my own observability systems",
|
|
15
|
+
value: "byo",
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
const SIGNALS = [
|
|
19
|
+
{
|
|
20
|
+
id: "metrics",
|
|
21
|
+
label: "Metrics export",
|
|
22
|
+
description: "Prometheus remote_write to your managed metrics backend.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "traces",
|
|
26
|
+
label: "Distributed tracing",
|
|
27
|
+
description: "OTLP traces to Elastic, Azure Monitor, or another backend.",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "logs",
|
|
31
|
+
label: "Application log shipping",
|
|
32
|
+
description: "Pod/app logs to Elasticsearch, Loki, or generic HTTP.",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
function parsePositiveInt(value, fallback) {
|
|
36
|
+
const parsed = Number.parseInt(value.trim(), 10);
|
|
37
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
38
|
+
}
|
|
39
|
+
function normalizeSize(value, fallback) {
|
|
40
|
+
const trimmed = value.trim();
|
|
41
|
+
return trimmed || fallback;
|
|
42
|
+
}
|
|
43
|
+
export function ObservabilityStep({ onComplete, onBack, }) {
|
|
44
|
+
const { state, dispatch } = useWizard();
|
|
45
|
+
const { colors } = useTheme();
|
|
46
|
+
const [subStep, setSubStep] = useState("mode");
|
|
47
|
+
const [telemetryRetention, setTelemetryRetention] = useState(String(state.clickStackTelemetryRetentionDays || 7));
|
|
48
|
+
const [clickHouseStorage, setClickHouseStorage] = useState(state.clickHouseStorageSize || "100Gi");
|
|
49
|
+
const [signalIndex, setSignalIndex] = useState(0);
|
|
50
|
+
const requestedStorageGi = Number.parseInt(clickHouseStorage, 10) + 10;
|
|
51
|
+
const reportedStorageGi = state.totalPersistentStorageGi || 0;
|
|
52
|
+
const storageWarning = reportedStorageGi > 0 && requestedStorageGi > reportedStorageGi * 0.75;
|
|
53
|
+
useInput((input, key) => {
|
|
54
|
+
if (key.escape) {
|
|
55
|
+
if (subStep === "mode") {
|
|
56
|
+
onBack();
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
setSubStep("mode");
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (subStep !== "byo-signals")
|
|
64
|
+
return;
|
|
65
|
+
if (key.upArrow) {
|
|
66
|
+
setSignalIndex((idx) => Math.max(0, idx - 1));
|
|
67
|
+
}
|
|
68
|
+
else if (key.downArrow) {
|
|
69
|
+
setSignalIndex((idx) => Math.min(SIGNALS.length, idx + 1));
|
|
70
|
+
}
|
|
71
|
+
else if (input === " " || input === "x" || key.return) {
|
|
72
|
+
if (signalIndex === SIGNALS.length) {
|
|
73
|
+
onComplete();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const signal = SIGNALS[signalIndex];
|
|
77
|
+
if (signal.id === "metrics") {
|
|
78
|
+
dispatch({
|
|
79
|
+
type: "SET_METRICS_EXPORT",
|
|
80
|
+
enabled: !state.metricsExportEnabled,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else if (signal.id === "traces") {
|
|
84
|
+
dispatch({
|
|
85
|
+
type: "SET_TRACING_ENABLED",
|
|
86
|
+
enabled: !state.tracingEnabled,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
dispatch({
|
|
91
|
+
type: "SET_APP_LOGS_ENABLED",
|
|
92
|
+
enabled: !state.appLogsEnabled,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const chooseMode = (item) => {
|
|
98
|
+
if (item.value === "built-in") {
|
|
99
|
+
dispatch({ type: "SET_CLICKSTACK_ENABLED", enabled: true });
|
|
100
|
+
setSubStep("builtin-telemetry-retention");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
dispatch({ type: "SET_CLICKSTACK_ENABLED", enabled: false });
|
|
104
|
+
setSubStep("byo-signals");
|
|
105
|
+
};
|
|
106
|
+
const saveBuiltInSettings = () => {
|
|
107
|
+
dispatch({
|
|
108
|
+
type: "SET_CLICKSTACK_CONFIG",
|
|
109
|
+
config: {
|
|
110
|
+
clickStackTelemetryRetentionDays: parsePositiveInt(telemetryRetention, 7),
|
|
111
|
+
clickHouseStorageSize: normalizeSize(clickHouseStorage, "100Gi"),
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
onComplete();
|
|
115
|
+
};
|
|
116
|
+
const renderCapacitySummary = () => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["Storage class: ", state.storageClass || "not detected"] }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Reported persistent storage:", " ", reportedStorageGi > 0
|
|
117
|
+
? `${Math.ceil(reportedStorageGi)} Gi`
|
|
118
|
+
: "unknown / dynamic provisioning"] }), _jsxs(Text, { color: storageWarning ? colors.warning : "gray", dimColor: true, children: ["Requested ClickStack PVCs: ", Number.isFinite(requestedStorageGi) ? requestedStorageGi : 0, " Gi", " ", "(", clickHouseStorage || "100Gi", " ClickHouse + 10Gi HyperDX metadata)", storageWarning ? " (high relative to reported capacity)" : ""] })] }));
|
|
119
|
+
if (subStep === "mode") {
|
|
120
|
+
return (_jsxs(BorderBox, { title: "Observability", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "How should Rulebricks observability be set up?" }), _jsx(Text, { color: "gray", dimColor: true, children: "Built-in ClickStack gives you logs, traces, mirrored metrics, and operational dashboards. Decision logs stay in object storage and are queried directly when needed." })] }), _jsx(SelectInput, { items: MODE_OPTIONS, onSelect: chooseMode }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Esc to go back" }) })] }));
|
|
121
|
+
}
|
|
122
|
+
if (subStep === "builtin-telemetry-retention") {
|
|
123
|
+
return (_jsx(BorderBox, { title: "Telemetry Retention", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "How many days of ClickStack logs/traces/metrics should be retained?" }), _jsx(Text, { color: "gray", dimColor: true, children: "This controls operational telemetry TTL. Decision logs are archived only to object storage." }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Days: " }), _jsx(TextInput, { value: telemetryRetention, onChange: setTelemetryRetention, onSubmit: () => setSubStep("builtin-clickhouse-storage"), placeholder: "7" })] })] }) }));
|
|
124
|
+
}
|
|
125
|
+
if (subStep === "builtin-clickhouse-storage") {
|
|
126
|
+
return (_jsx(BorderBox, { title: "ClickHouse Storage", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "How large should the ClickHouse PVC be?" }), _jsx(Text, { color: "gray", dimColor: true, children: "Stores ClickStack operational telemetry. Example: 100Gi" }), renderCapacitySummary(), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Size: " }), _jsx(TextInput, { value: clickHouseStorage, onChange: setClickHouseStorage, onSubmit: saveBuiltInSettings, placeholder: "100Gi" })] })] }) }));
|
|
127
|
+
}
|
|
128
|
+
return (_jsxs(BorderBox, { title: "BYO Observability Signals", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select the signals you want to export to your own systems:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Space/Enter to toggle, then Continue. Connection details come later in Feature Settings." })] }), _jsxs(Box, { flexDirection: "column", marginY: 1, children: [SIGNALS.map((signal, index) => {
|
|
129
|
+
const enabled = signal.id === "metrics"
|
|
130
|
+
? state.metricsExportEnabled
|
|
131
|
+
: signal.id === "traces"
|
|
132
|
+
? state.tracingEnabled
|
|
133
|
+
: state.appLogsEnabled;
|
|
134
|
+
const selected = signalIndex === index;
|
|
135
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: selected ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: selected ? colors.accent : undefined, children: selected ? "❯ " : " " }), _jsx(Text, { color: enabled ? colors.success : colors.muted, children: enabled ? "[✓]" : "[ ]" }), _jsxs(Text, { color: selected ? colors.accent : undefined, children: [" ", signal.label] })] }), selected && (_jsx(Box, { marginLeft: 6, children: _jsx(Text, { color: "gray", dimColor: true, children: signal.description }) }))] }, signal.id));
|
|
136
|
+
}), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: signalIndex === SIGNALS.length ? colors.accent : colors.muted, children: signalIndex === SIGNALS.length ? "❯ " : " " }), _jsx(Text, { color: signalIndex === SIGNALS.length ? colors.success : colors.muted, bold: signalIndex === SIGNALS.length, children: "[Continue \u2192]" })] })] })] }));
|
|
137
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
interface ReviewStepProps {
|
|
2
2
|
onComplete: () => void;
|
|
3
3
|
onBack: () => void;
|
|
4
|
+
allowEditName?: boolean;
|
|
4
5
|
}
|
|
5
|
-
export declare function ReviewStep({ onComplete, onBack }: ReviewStepProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function ReviewStep({ onComplete, onBack, allowEditName, }: ReviewStepProps): import("react/jsx-runtime").JSX.Element;
|
|
6
7
|
export {};
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import TextInput from 'ink-text-input';
|
|
5
5
|
import { useWizard } from '../WizardContext.js';
|
|
6
6
|
import { BorderBox, useTheme } from '../../common/index.js';
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import { DNS_PROVIDER_NAMES, CLOUD_PROVIDER_NAMES, LOGGING_SINK_INFO, isSupportedDnsProvider } from '../../../types/index.js';
|
|
8
|
+
function kafkaPresetLabel(preset) {
|
|
9
|
+
switch (preset) {
|
|
10
|
+
case 'aws-msk-iam':
|
|
11
|
+
return 'AWS MSK IAM';
|
|
12
|
+
case 'azure-event-hubs':
|
|
13
|
+
return 'Azure Event Hubs';
|
|
14
|
+
case 'gcp-managed':
|
|
15
|
+
return 'GCP Managed Kafka';
|
|
16
|
+
case 'custom':
|
|
17
|
+
return 'custom';
|
|
18
|
+
default:
|
|
19
|
+
return 'not configured';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function ReviewStep({ onComplete, onBack, allowEditName = true, }) {
|
|
9
23
|
const { state, dispatch } = useWizard();
|
|
10
24
|
const { colors } = useTheme();
|
|
11
|
-
const [editingName, setEditingName] = useState(!state.name);
|
|
25
|
+
const [editingName, setEditingName] = useState(allowEditName && !state.name);
|
|
12
26
|
const [name, setName] = useState(state.name || '');
|
|
13
27
|
const [error, setError] = useState(null);
|
|
14
28
|
useInput((input, key) => {
|
|
@@ -22,7 +36,7 @@ export function ReviewStep({ onComplete, onBack }) {
|
|
|
22
36
|
onComplete();
|
|
23
37
|
}
|
|
24
38
|
}
|
|
25
|
-
else if (input === 'e') {
|
|
39
|
+
else if (allowEditName && input === 'e') {
|
|
26
40
|
setEditingName(true);
|
|
27
41
|
}
|
|
28
42
|
});
|
|
@@ -43,13 +57,30 @@ export function ReviewStep({ onComplete, onBack }) {
|
|
|
43
57
|
dispatch({ type: 'SET_NAME', name });
|
|
44
58
|
setEditingName(false);
|
|
45
59
|
};
|
|
46
|
-
const tierConfig = state.infrastructureMode !== 'existing' && state.tier ? TIER_CONFIGS[state.tier] : null;
|
|
47
|
-
const tierLabel = state.infrastructureMode === 'existing'
|
|
48
|
-
? 'Inferred from cluster'
|
|
49
|
-
: state.tier
|
|
50
|
-
? `${state.tier.charAt(0).toUpperCase()}${state.tier.slice(1)}`
|
|
51
|
-
: 'Not selected';
|
|
52
60
|
const externalDnsEnabled = state.dnsAutoManage && isSupportedDnsProvider(state.dnsProvider);
|
|
61
|
+
const clusterCpuCores = state.eligibleCpuCores || Math.ceil(state.totalCpuCores);
|
|
62
|
+
const clusterMemoryGi = state.eligibleMemoryGi || Math.ceil(state.totalMemoryGi);
|
|
63
|
+
const storageValue = state.storageClass
|
|
64
|
+
? state.totalPersistentStorageGi > 0
|
|
65
|
+
? `${state.storageClass} (${Math.ceil(state.totalPersistentStorageGi)} Gi reported available)`
|
|
66
|
+
: `${state.storageClass} (dynamic PVC provisioning)`
|
|
67
|
+
: '';
|
|
68
|
+
const monitoringDestination = state.clickStackEnabled
|
|
69
|
+
? 'Prometheus + in-cluster ClickStack metrics mirror'
|
|
70
|
+
: state.metricsExportEnabled && state.prometheusRemoteWriteDestination
|
|
71
|
+
? `Remote write: ${state.prometheusRemoteWriteDestination}`
|
|
72
|
+
: 'In-cluster Prometheus (no remote write)';
|
|
73
|
+
const storageAuthValue = state.storageProvider === 's3'
|
|
74
|
+
? state.storageAwsIamRoleArn || 'not configured'
|
|
75
|
+
: state.storageProvider === 'gcs'
|
|
76
|
+
? state.storageGcpServiceAccountEmail || 'not configured'
|
|
77
|
+
: state.storageCloudAuthMode === 'secret'
|
|
78
|
+
? `secret: ${state.storageAzureBlobConnectionStringSecretRef || 'not configured'}`
|
|
79
|
+
: state.storageAzureBlobClientId
|
|
80
|
+
? `workload identity (${state.storageAzureBlobClientId})`
|
|
81
|
+
: 'workload identity';
|
|
82
|
+
const clickStackStorageGi = Number.parseInt(state.clickHouseStorageSize || "0", 10) + 10;
|
|
83
|
+
const usesInClusterPostgres = state.databaseType === 'self-hosted' && state.postgresMode !== 'external';
|
|
53
84
|
if (editingName) {
|
|
54
85
|
return (_jsx(BorderBox, { title: "Deployment Name", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Enter a name for this deployment:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Lowercase letters, numbers, and hyphens only" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: name, onChange: setName, onSubmit: handleNameSubmit, placeholder: "my-deployment" }) }), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) }))] }) }));
|
|
55
86
|
}
|
|
@@ -57,5 +88,18 @@ export function ReviewStep({ onComplete, onBack }) {
|
|
|
57
88
|
const ConfigRow = ({ label, value, valueColor }) => (_jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsx(Text, { color: colors.muted, children: label }) }), _jsx(Text, { color: valueColor || colors.accent, children: value })] }));
|
|
58
89
|
// Helper to render a section header
|
|
59
90
|
const SectionHeader = ({ title }) => (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { bold: true, color: colors.accent, children: ["\u2500\u2500 ", title, " \u2500\u2500"] }) }));
|
|
60
|
-
return (_jsxs(BorderBox, { title: "Review Configuration", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { title: "Deployment" }), _jsx(ConfigRow, { label: "Name", value: state.name }), state.
|
|
91
|
+
return (_jsxs(BorderBox, { title: "Review Configuration", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { title: "Deployment" }), _jsx(ConfigRow, { label: "Name", value: state.name }), state.version && (_jsx(ConfigRow, { label: "Version", value: state.version })), _jsx(SectionHeader, { title: "Infrastructure" }), state.provider && (_jsx(ConfigRow, { label: "Provider", value: CLOUD_PROVIDER_NAMES[state.provider] })), state.clusterName && (_jsx(ConfigRow, { label: "Cluster", value: state.clusterName })), state.region && (_jsx(ConfigRow, { label: "Region", value: state.region })), state.azureResourceGroup && (_jsx(ConfigRow, { label: "Resource Group", value: state.azureResourceGroup })), state.gcpProjectId && (_jsx(ConfigRow, { label: "GCP Project", value: state.gcpProjectId })), _jsx(SectionHeader, { title: "Domain & DNS" }), _jsx(ConfigRow, { label: "Domain", value: state.domain }), _jsx(ConfigRow, { label: "Admin Email", value: state.adminEmail }), state.tlsEmail && state.tlsEmail !== state.adminEmail && (_jsx(ConfigRow, { label: "TLS Email", value: state.tlsEmail })), _jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsx(Text, { color: colors.muted, children: "DNS" }) }), _jsx(Text, { color: colors.accent, children: DNS_PROVIDER_NAMES[state.dnsProvider] }), externalDnsEnabled && _jsx(Text, { color: colors.success, children: " (auto)" })] }), _jsx(SectionHeader, { title: "SMTP" }), _jsx(ConfigRow, { label: "Host", value: `${state.smtpHost}:${state.smtpPort}` }), _jsx(ConfigRow, { label: "From", value: `${state.smtpFromName} <${state.smtpFrom}>` }), _jsx(SectionHeader, { title: "Database" }), _jsx(ConfigRow, { label: "Type", value: state.databaseType === 'supabase-cloud' ? 'Supabase Cloud' : 'Self-hosted' }), _jsx(SectionHeader, { title: "Cluster" }), state.totalCpuCores > 0 && (_jsx(ConfigRow, { label: "Capacity", value: `${state.schedulableNodeCount} nodes, ${clusterCpuCores} vCPU, ${clusterMemoryGi} Gi` })), state.storageClass && (_jsx(ConfigRow, { label: "Storage", value: storageValue })), state.storageProvider && (_jsxs(_Fragment, { children: [_jsx(SectionHeader, { title: "Object Storage" }), _jsx(ConfigRow, { label: "Bucket", value: `${state.storageProvider} / ${state.storageBucket || 'not configured'}` }), state.storageRegion && (_jsx(ConfigRow, { label: "Region", value: state.storageRegion })), state.storageProvider === 'azure-blob' &&
|
|
92
|
+
state.storageAzureBlobContainer && (_jsx(ConfigRow, { label: "Container", value: state.storageAzureBlobContainer })), _jsx(ConfigRow, { label: "Auth", value: storageAuthValue }), usesInClusterPostgres && state.backupEnabled && (_jsx(ConfigRow, { label: "DB backups", value: `${state.storageBucket || 'not configured'} / db-backups/` }))] })), usesInClusterPostgres && (_jsxs(_Fragment, { children: [_jsx(SectionHeader, { title: "Backups" }), _jsx(ConfigRow, { label: "Database", value: state.backupEnabled
|
|
93
|
+
? `${state.backupSchedule} / ${state.backupRetentionDays} days`
|
|
94
|
+
: 'Disabled', valueColor: state.backupEnabled ? colors.success : colors.muted })] })), (state.redisMode === 'external' || state.kafkaMode === 'external') && (_jsxs(_Fragment, { children: [_jsx(SectionHeader, { title: "External Services" }), _jsx(ConfigRow, { label: "Redis", value: state.redisMode === 'external'
|
|
95
|
+
? `external (${state.redisHost || 'not configured'}${state.redisTls ? ', TLS' : ''})`
|
|
96
|
+
: 'in-cluster', valueColor: state.redisMode === 'external' ? colors.success : colors.muted }), _jsx(ConfigRow, { label: "Kafka", value: state.kafkaMode === 'external'
|
|
97
|
+
? `external (${kafkaPresetLabel(state.kafkaPreset)})`
|
|
98
|
+
: 'in-cluster', valueColor: state.kafkaMode === 'external' ? colors.success : colors.muted }), state.kafkaMode === 'external' && (_jsx(ConfigRow, { label: "Topic prefix", value: state.kafkaTopicPrefix || '(none)' })), state.kafkaMode === 'external' &&
|
|
99
|
+
state.kafkaSaslMechanism === 'aws-iam' && (_jsx(ConfigRow, { label: "Vector", value: "kafka-proxy bridge sidecar (MSK IAM)", valueColor: colors.muted })), state.kafkaMode === 'external' &&
|
|
100
|
+
state.kafkaSaslMechanism === 'aws-iam' && (_jsx(ConfigRow, { label: "Topics", value: state.kafkaProvisionTopics
|
|
101
|
+
? 'auto-provisioned by the chart'
|
|
102
|
+
: 'self-managed (no CreateTopic)', valueColor: colors.muted }))] })), _jsx(SectionHeader, { title: "Features" }), _jsxs(Box, { children: [_jsxs(Text, { color: state.aiEnabled ? colors.success : colors.muted, children: [state.aiEnabled ? '✓' : '○', " AI"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.ssoEnabled ? colors.success : colors.muted, children: [state.ssoEnabled ? '✓' : '○', " SSO"] }), _jsx(Text, { children: " " }), _jsx(Text, { color: colors.success, children: "\u2713 Monitoring" }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.clickStackEnabled ? colors.success : colors.muted, children: [state.clickStackEnabled ? '✓' : '○', " ClickStack"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: !state.clickStackEnabled && state.tracingEnabled ? colors.success : colors.muted, children: [!state.clickStackEnabled && state.tracingEnabled ? '✓' : '○', " Tracing"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: !state.clickStackEnabled && state.appLogsEnabled ? colors.success : colors.muted, children: [!state.clickStackEnabled && state.appLogsEnabled ? '✓' : '○', " App Logs"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.loggingSink !== 'console' ? colors.success : colors.muted, children: [state.loggingSink !== 'console' ? '✓' : '○', " Logging"] })] }), _jsx(ConfigRow, { label: "Observability", value: state.clickStackEnabled
|
|
103
|
+
? 'Built-in ClickStack + HyperDX'
|
|
104
|
+
: 'BYO/export mode', valueColor: state.clickStackEnabled ? colors.success : colors.muted }), state.clickStackEnabled && (_jsxs(_Fragment, { children: [_jsx(ConfigRow, { label: "Retention", value: `telemetry ${state.clickStackTelemetryRetentionDays}d` }), _jsx(ConfigRow, { label: "Storage", value: `ClickHouse ${state.clickHouseStorageSize}, HyperDX metadata 10Gi (${clickStackStorageGi} Gi requested)` })] })), _jsx(ConfigRow, { label: "Monitoring", value: monitoringDestination }), !state.clickStackEnabled && state.tracingEnabled && (_jsx(ConfigRow, { label: "Tracing", value: state.tracingElasticEndpoint || 'enabled' })), !state.clickStackEnabled && state.appLogsEnabled && (_jsx(ConfigRow, { label: "App logs", value: state.appLogsElasticEndpoint || 'enabled' })), state.loggingSink !== 'console' && (_jsx(ConfigRow, { label: "Logging", value: LOGGING_SINK_INFO[state.loggingSink]?.name || state.loggingSink })), _jsx(SectionHeader, { title: "License" }), _jsx(ConfigRow, { label: "Key", value: `${state.licenseKey?.substring(0, 12)}...` })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.success, bold: true, children: "Press Enter to save this configuration" }), _jsxs(Text, { color: colors.muted, dimColor: true, children: [allowEditName ? 'e to edit name • ' : '', "Esc to go back"] })] })] }));
|
|
61
105
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ObjectStorageProvider } from "../../../types/index.js";
|
|
2
|
+
interface StorageStepProps {
|
|
3
|
+
onComplete: () => void;
|
|
4
|
+
onBack: () => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function storageProviderForCloud(provider: string | null): ObjectStorageProvider;
|
|
7
|
+
export declare function storageRegionForCloud(cloudProvider: string | null, clusterRegion: string, savedStorageProvider?: ObjectStorageProvider | null, savedStorageRegion?: string): string;
|
|
8
|
+
export declare function StorageStep({ onComplete, onBack }: StorageStepProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|