@rulebricks/cli 2.0.5 → 2.1.0
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/dist/commands/deploy.js +50 -2
- package/dist/commands/destroy.js +1 -1
- package/dist/components/Wizard/steps/CloudProviderStep.js +18 -5
- package/dist/lib/cloudCli.d.ts +47 -3
- package/dist/lib/cloudCli.js +271 -53
- package/dist/lib/helmValues.js +50 -6
- package/dist/lib/terraform.d.ts +10 -3
- package/dist/lib/terraform.js +132 -5
- package/dist/types/index.js +87 -18
- package/package.json +1 -1
- package/terraform/aws/main.tf +5 -5
- package/terraform/azure/main.tf +4 -4
- package/terraform/gcp/main.tf +10 -6
package/dist/commands/deploy.js
CHANGED
|
@@ -4,8 +4,9 @@ import { Box, Text, useApp, useInput } from "ink";
|
|
|
4
4
|
import { platform } from "os";
|
|
5
5
|
import { BorderBox, Spinner, StatusLine, ThemeProvider, useTheme, Logo, } from "../components/common/index.js";
|
|
6
6
|
import { DNSWaitScreen } from "../components/DNSWaitScreen.js";
|
|
7
|
-
import { loadDeploymentConfig, loadDeploymentState, saveDeploymentState, updateDeploymentStatus, } from "../lib/config.js";
|
|
8
|
-
import { setupTerraformWorkspace, terraformInit, terraformPlan, terraformApply, terraformDestroy, updateKubeconfig, hasTerraformState, isTerraformInstalled, } from "../lib/terraform.js";
|
|
7
|
+
import { loadDeploymentConfig, loadDeploymentState, saveDeploymentState, updateDeploymentStatus, saveTerraformVars, } from "../lib/config.js";
|
|
8
|
+
import { setupTerraformWorkspace, terraformInit, terraformPlan, terraformApply, terraformDestroy, updateKubeconfig, hasTerraformState, isTerraformInstalled, generateTerraformVars, } from "../lib/terraform.js";
|
|
9
|
+
import { checkGcpApplicationDefaultCredentials, checkAzureResourceProviders, checkAzureVmQuota, AZURE_TIER_CORES, } from "../lib/cloudCli.js";
|
|
9
10
|
import { installOrUpgradeChart, upgradeChart, isHelmInstalled, } from "../lib/helm.js";
|
|
10
11
|
import { isKubectlInstalled, checkClusterAccessible, } from "../lib/kubernetes.js";
|
|
11
12
|
import { generateHelmValues, updateHelmValuesForTLS, } from "../lib/helmValues.js";
|
|
@@ -158,6 +159,10 @@ function DeployCommandInner({ name, skipInfra, skipDns, version, }) {
|
|
|
158
159
|
setStep("infra-setup");
|
|
159
160
|
await setupTerraformWorkspace(name, cfg.infrastructure.provider);
|
|
160
161
|
}
|
|
162
|
+
// Generate and save terraform variables (always do this before plan,
|
|
163
|
+
// even if state exists, in case config changed)
|
|
164
|
+
const terraformVars = generateTerraformVars(cfg);
|
|
165
|
+
await saveTerraformVars(name, terraformVars);
|
|
161
166
|
setStep("infra-init");
|
|
162
167
|
await terraformInit(name);
|
|
163
168
|
setStep("infra-plan");
|
|
@@ -287,6 +292,49 @@ function DeployCommandInner({ name, skipInfra, skipDns, version, }) {
|
|
|
287
292
|
if (cfg.infrastructure.mode === "provision" && !terraform) {
|
|
288
293
|
throw new Error("Terraform is not installed. Required for infrastructure provisioning.");
|
|
289
294
|
}
|
|
295
|
+
// Check GCP Application Default Credentials if provisioning GCP infrastructure
|
|
296
|
+
if (cfg.infrastructure.mode === "provision" &&
|
|
297
|
+
cfg.infrastructure.provider === "gcp") {
|
|
298
|
+
const adcCheck = await checkGcpApplicationDefaultCredentials();
|
|
299
|
+
if (!adcCheck.configured) {
|
|
300
|
+
throw new Error("GCP Application Default Credentials (ADC) not configured.\n\n" +
|
|
301
|
+
"Terraform requires ADC to authenticate with Google Cloud.\n\n" +
|
|
302
|
+
"To fix this:\n" +
|
|
303
|
+
" • Run: gcloud auth login\n" +
|
|
304
|
+
" • Run: gcloud auth application-default login\n" +
|
|
305
|
+
" • Verify: gcloud auth application-default print-access-token\n\n" +
|
|
306
|
+
"For more information: https://cloud.google.com/docs/authentication/application-default-credentials");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Check Azure prerequisites if provisioning Azure infrastructure
|
|
310
|
+
if (cfg.infrastructure.mode === "provision" &&
|
|
311
|
+
cfg.infrastructure.provider === "azure") {
|
|
312
|
+
// 1. Verify resource providers are registered
|
|
313
|
+
const providerCheck = await checkAzureResourceProviders();
|
|
314
|
+
if (!providerCheck.allRegistered) {
|
|
315
|
+
throw new Error(`Azure resource providers not registered: ${providerCheck.missing.join(", ")}\n\n` +
|
|
316
|
+
"To register:\n" +
|
|
317
|
+
providerCheck.missing
|
|
318
|
+
.map((p) => ` • az provider register --namespace ${p}`)
|
|
319
|
+
.join("\n") +
|
|
320
|
+
"\n\nNote: Registration may take a few minutes to complete.");
|
|
321
|
+
}
|
|
322
|
+
// 2. Check VM quota for the selected tier
|
|
323
|
+
const tier = cfg.tier || "small";
|
|
324
|
+
const region = cfg.infrastructure.region;
|
|
325
|
+
if (!region) {
|
|
326
|
+
throw new Error("Azure region is required for infrastructure provisioning");
|
|
327
|
+
}
|
|
328
|
+
const requiredCores = AZURE_TIER_CORES[tier] || AZURE_TIER_CORES.small;
|
|
329
|
+
const quotaCheck = await checkAzureVmQuota(region, requiredCores);
|
|
330
|
+
if (!quotaCheck.sufficient) {
|
|
331
|
+
throw new Error(`Insufficient Azure vCPU quota in ${region}.\n` +
|
|
332
|
+
`Required: ${requiredCores} cores (${tier} tier), Available: ${quotaCheck.available}/${quotaCheck.limit}\n\n` +
|
|
333
|
+
"Request a quota increase in the Azure portal:\n" +
|
|
334
|
+
" • Go to: Subscriptions > Usage + quotas\n" +
|
|
335
|
+
" • Request increase for 'Total Regional vCPUs' in your region");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
290
338
|
// Check cluster access if using existing infrastructure
|
|
291
339
|
if (cfg.infrastructure.mode === "existing") {
|
|
292
340
|
let clusterError = await checkClusterAccessible();
|
package/dist/commands/destroy.js
CHANGED
|
@@ -243,7 +243,7 @@ function DestroyCommandInner({ name, cluster, config, force, }) {
|
|
|
243
243
|
// Only cleaning local files (with --config)
|
|
244
244
|
_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.warning, bold: true, children: "\u2139 Local Cleanup" }), _jsxs(Box, { marginY: 1, flexDirection: "column", children: [_jsx(Text, { children: "No cluster resources found to clean up." }), _jsx(Text, { children: "This will delete local configuration files." })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.warning, children: "Press Enter to confirm, Esc to cancel" }) })] })) : (
|
|
245
245
|
// Full destruction
|
|
246
|
-
_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.accent, bold: true, children: "\u26A0 WARNING" }), _jsxs(Box, { marginY: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, children: "This will permanently delete:" }), (scope?.hasHelmRelease || scope?.hasNamespace) && (_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.muted, children: " \u2022 Rulebricks application" }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 All databases and stored data"] }), _jsx(Text, { color: colors.muted, children: " \u2022 All persistent volumes" }), _jsx(Text, { color: colors.muted, children: " \u2022 Monitoring stack" }), _jsx(Text, { color: colors.muted, children: " \u2022 Kubernetes namespace" })] })), needsInfraConfirm && (_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.accent, children: " \u2022 Kubernetes cluster" }), _jsx(Text, { color: colors.accent, children: " \u2022 All cloud infrastructure" })] })), willDeleteConfig && (_jsx(Text, { color: colors.muted, children: " \u2022 Local configuration files" })), !cluster && scope?.hasInfrastructure && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "Cloud infrastructure will be preserved. Use --cluster to remove it." }) })), cluster && !scope?.hasInfrastructure && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "No CLI managed infrastructure found for this deployment." }) })), !willDeleteConfig && (_jsx(Box, { marginTop: !needsInfraConfirm && !cluster ? 0 : 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "Local config files will be preserved. Use --config to remove them." }) })), !scope?.clusterAccessible && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.warning, dimColor: true, children: "\u26A0 Cluster is not accessible.
|
|
246
|
+
_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.accent, bold: true, children: "\u26A0 WARNING" }), _jsxs(Box, { marginY: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, children: "This will permanently delete:" }), (scope?.hasHelmRelease || scope?.hasNamespace) && (_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.muted, children: " \u2022 Rulebricks application" }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 All databases and stored data"] }), _jsx(Text, { color: colors.muted, children: " \u2022 All persistent volumes" }), _jsx(Text, { color: colors.muted, children: " \u2022 Monitoring stack" }), _jsx(Text, { color: colors.muted, children: " \u2022 Kubernetes namespace" })] })), needsInfraConfirm && (_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.accent, children: " \u2022 Kubernetes cluster" }), _jsx(Text, { color: colors.accent, children: " \u2022 All cloud infrastructure" })] })), willDeleteConfig && (_jsx(Text, { color: colors.muted, children: " \u2022 Local configuration files" })), !cluster && scope?.hasInfrastructure && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "Cloud infrastructure will be preserved. Use --cluster to remove it." }) })), cluster && !scope?.hasInfrastructure && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "No CLI managed infrastructure found for this deployment." }) })), !willDeleteConfig && (_jsx(Box, { marginTop: !needsInfraConfirm && !cluster ? 0 : 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "Local config files will be preserved. Use --config to remove them." }) })), !scope?.clusterAccessible && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.warning, dimColor: true, children: "\u26A0 Cluster is not accessible. Some cluster resources may need manual cleanup." }) }))] }), needsInfraConfirm ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Type", " ", _jsx(Text, { color: colors.accent, bold: true, children: "destroy-all" }), " ", "to confirm:"] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.accent, children: "\u276F " }), _jsx(Text, { children: confirmText }), _jsx(Text, { color: colors.muted, children: "\u2588" })] })] })) : (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.warning, children: "Press Enter to confirm, Esc to cancel" }) }))] })) }) }));
|
|
247
247
|
}
|
|
248
248
|
export function DestroyCommand(props) {
|
|
249
249
|
return (_jsxs(ThemeProvider, { theme: "destroy", children: [_jsx(Logo, {}), _jsx(DestroyCommandInner, { ...props })] }));
|
|
@@ -89,12 +89,17 @@ export function CloudProviderStep({ onComplete, onBack, }) {
|
|
|
89
89
|
const provider = item.value;
|
|
90
90
|
dispatch({ type: "SET_PROVIDER", provider });
|
|
91
91
|
if (provider === "gcp") {
|
|
92
|
-
//
|
|
92
|
+
// GCP requires project to be pre-configured, so use the detected project
|
|
93
93
|
const detectedProject = await getGcpProjectId();
|
|
94
94
|
if (detectedProject) {
|
|
95
|
-
|
|
95
|
+
dispatch({ type: "SET_GCP_PROJECT", projectId: detectedProject });
|
|
96
|
+
// Skip project input and go directly to regions
|
|
97
|
+
loadRegions(provider);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Fallback to project input if somehow not detected (shouldn't happen with new auth check)
|
|
101
|
+
setSubStep("gcp-project");
|
|
96
102
|
}
|
|
97
|
-
setSubStep("gcp-project");
|
|
98
103
|
}
|
|
99
104
|
else if (provider === "azure") {
|
|
100
105
|
setSubStep("azure-rg");
|
|
@@ -166,8 +171,9 @@ export function CloudProviderStep({ onComplete, onBack, }) {
|
|
|
166
171
|
dispatch({ type: "SET_CLUSTER_NAME", clusterName });
|
|
167
172
|
onComplete();
|
|
168
173
|
};
|
|
169
|
-
const handleGcpProjectSubmit = () => {
|
|
174
|
+
const handleGcpProjectSubmit = async () => {
|
|
170
175
|
dispatch({ type: "SET_GCP_PROJECT", projectId: gcpProject });
|
|
176
|
+
// ADC is now checked upfront in checkGcloudCli(), so proceed directly to regions
|
|
171
177
|
loadRegions("gcp");
|
|
172
178
|
};
|
|
173
179
|
const handleAzureRgSubmit = () => {
|
|
@@ -181,11 +187,18 @@ export function CloudProviderStep({ onComplete, onBack, }) {
|
|
|
181
187
|
return _jsx(Text, { color: "gray", children: " (not installed)" });
|
|
182
188
|
}
|
|
183
189
|
if (!status.authenticated) {
|
|
190
|
+
// Check for specific error types to show more helpful messages
|
|
191
|
+
if (status.error?.toLowerCase().includes("quota")) {
|
|
192
|
+
return _jsx(Text, { color: "yellow", children: " (insufficient quota)" });
|
|
193
|
+
}
|
|
194
|
+
if (status.error?.toLowerCase().includes("resource provider")) {
|
|
195
|
+
return _jsx(Text, { color: "yellow", children: " (providers not registered)" });
|
|
196
|
+
}
|
|
184
197
|
return _jsx(Text, { color: "yellow", children: " (log in required)" });
|
|
185
198
|
}
|
|
186
199
|
return _jsx(Text, { color: "green", children: " \u2713" });
|
|
187
200
|
};
|
|
188
|
-
return (_jsxs(BorderBox, { title: "Cloud Provider", children: [subStep === "checking" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Spinner, { label: "Checking cloud CLI tools..." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Detecting AWS, GCP, and Azure CLIs..." }) })] })), subStep === "no-cli" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: "red", bold: true, children: "No cloud CLI tools detected" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { children: "To provision infrastructure, you need to install and authenticate with at least one cloud CLI:" }) }), _jsx(Box, { marginTop: 1, flexDirection: "column", marginLeft: 2, children: Object.entries(CLI_INSTALL_URLS).map(([provider, info]) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, children: [info.name, ":"] }), _jsxs(Text, { color: "gray", children: [" ", info.installCmd] }), _jsxs(Text, { color: "gray", children: [" ", info.url] })] }, provider))) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "After installing, authenticate:" }) }), _jsx(Box, { marginLeft: 2, flexDirection: "column", children: Object.entries(CLI_LOGIN_COMMANDS).map(([provider, cmd]) => (_jsxs(Text, { color: "gray", children: [" ", provider, ": ", cmd] }, provider))) })] })), subStep === "provider" && cliStatus && (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select your cloud provider:" }), !cliStatus.anyAvailable && cliStatus.anyInstalled && (_jsx(Text, { color: "yellow", dimColor: true, children: "\u26A0 Some CLIs are installed but not authenticated" })), needsTerraform &&
|
|
201
|
+
return (_jsxs(BorderBox, { title: "Cloud Provider", children: [subStep === "checking" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Spinner, { label: "Checking cloud CLI tools..." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Detecting AWS, GCP, and Azure CLIs..." }) })] })), subStep === "no-cli" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: "red", bold: true, children: "No cloud CLI tools detected" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { children: "To provision infrastructure, you need to install and authenticate with at least one cloud CLI:" }) }), _jsx(Box, { marginTop: 1, flexDirection: "column", marginLeft: 2, children: Object.entries(CLI_INSTALL_URLS).map(([provider, info]) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, children: [info.name, ":"] }), _jsxs(Text, { color: "gray", children: [" ", info.installCmd] }), _jsxs(Text, { color: "gray", children: [" ", info.url] })] }, provider))) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "After installing, authenticate:" }) }), _jsx(Box, { marginLeft: 2, flexDirection: "column", children: Object.entries(CLI_LOGIN_COMMANDS).map(([provider, cmd]) => (_jsx(Box, { flexDirection: "column", children: Array.isArray(cmd) ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", children: [" ", provider, ":"] }), cmd.map((c, i) => (_jsxs(Text, { color: "gray", children: [" ", c] }, i)))] })) : (_jsxs(Text, { color: "gray", children: [" ", provider, ": ", cmd] })) }, provider))) })] })), subStep === "provider" && cliStatus && (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select your cloud provider:" }), !cliStatus.anyAvailable && cliStatus.anyInstalled && (_jsx(Text, { color: "yellow", dimColor: true, children: "\u26A0 Some CLIs are installed but not authenticated" })), needsTerraform &&
|
|
189
202
|
terraformStatus &&
|
|
190
203
|
!terraformStatus.installed && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", bold: true, children: "\u26A0 Terraform not installed" }), _jsx(Text, { color: "gray", children: "You'll need Terraform to provision infrastructure." }), _jsxs(Text, { color: "gray", children: ["Install: ", TERRAFORM_INSTALL_INFO.installCmd] }), _jsx(Text, { color: "gray", dimColor: true, children: TERRAFORM_INSTALL_INFO.url })] })), needsTerraform && terraformStatus?.installed && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" ", "Terraform", " ", terraformStatus.version
|
|
191
204
|
? `v${terraformStatus.version}`
|
package/dist/lib/cloudCli.d.ts
CHANGED
|
@@ -43,13 +43,26 @@ export declare function listS3Buckets(): Promise<string[]>;
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function listEksClusters(region: string): Promise<string[]>;
|
|
45
45
|
/**
|
|
46
|
-
* Check if gcloud CLI is installed and authenticated
|
|
46
|
+
* Check if gcloud CLI is installed and fully authenticated
|
|
47
|
+
*
|
|
48
|
+
* For GCP to be considered "authenticated", the user must have:
|
|
49
|
+
* 1. Logged in with `gcloud auth login`
|
|
50
|
+
* 2. Set a default project with `gcloud config set project PROJECT_ID`
|
|
51
|
+
* 3. Configured Application Default Credentials with `gcloud auth application-default login`
|
|
47
52
|
*/
|
|
48
53
|
export declare function checkGcloudCli(): Promise<CloudCliStatus>;
|
|
49
54
|
/**
|
|
50
55
|
* Get the active GCP project ID
|
|
51
56
|
*/
|
|
52
57
|
export declare function getGcpProjectId(): Promise<string | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Check if GCP Application Default Credentials (ADC) are configured
|
|
60
|
+
* ADC is required for Terraform to authenticate with Google Cloud
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkGcpApplicationDefaultCredentials(): Promise<{
|
|
63
|
+
configured: boolean;
|
|
64
|
+
error?: string;
|
|
65
|
+
}>;
|
|
53
66
|
/**
|
|
54
67
|
* List available GCP regions
|
|
55
68
|
*/
|
|
@@ -64,7 +77,13 @@ export declare function listGcsBuckets(): Promise<string[]>;
|
|
|
64
77
|
*/
|
|
65
78
|
export declare function listGkeClusters(region: string): Promise<string[]>;
|
|
66
79
|
/**
|
|
67
|
-
* Check if Azure CLI is installed and authenticated
|
|
80
|
+
* Check if Azure CLI is installed and fully authenticated
|
|
81
|
+
*
|
|
82
|
+
* For Azure to be considered "authenticated", the user must have:
|
|
83
|
+
* 1. Logged in with `az login`
|
|
84
|
+
* 2. An active subscription in "Enabled" state
|
|
85
|
+
* 3. Required resource providers registered (Microsoft.ContainerService, etc.)
|
|
86
|
+
* 4. Sufficient vCPU quota for at least the small tier (8 cores)
|
|
68
87
|
*/
|
|
69
88
|
export declare function checkAzureCli(): Promise<CloudCliStatus>;
|
|
70
89
|
/**
|
|
@@ -87,6 +106,31 @@ export declare function listAzureBlobContainers(storageAccount: string): Promise
|
|
|
87
106
|
* List AKS clusters, optionally filtered by resource group
|
|
88
107
|
*/
|
|
89
108
|
export declare function listAksClusters(resourceGroup?: string): Promise<string[]>;
|
|
109
|
+
/**
|
|
110
|
+
* Azure tier to vCPU core requirements mapping
|
|
111
|
+
*/
|
|
112
|
+
export declare const AZURE_TIER_CORES: Record<string, number>;
|
|
113
|
+
/**
|
|
114
|
+
* Check if required Azure resource providers are registered
|
|
115
|
+
*/
|
|
116
|
+
export declare function checkAzureResourceProviders(): Promise<{
|
|
117
|
+
allRegistered: boolean;
|
|
118
|
+
missing: string[];
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Check Azure VM quota for a specific region
|
|
122
|
+
*
|
|
123
|
+
* @param region - Azure region to check quota for
|
|
124
|
+
* @param requiredCores - Number of vCPUs required
|
|
125
|
+
* @returns Quota check result with availability info
|
|
126
|
+
*/
|
|
127
|
+
export declare function checkAzureVmQuota(region: string, requiredCores: number): Promise<{
|
|
128
|
+
sufficient: boolean;
|
|
129
|
+
available: number;
|
|
130
|
+
limit: number;
|
|
131
|
+
used: number;
|
|
132
|
+
error?: string;
|
|
133
|
+
}>;
|
|
90
134
|
/**
|
|
91
135
|
* Check all cloud CLIs in parallel
|
|
92
136
|
*/
|
|
@@ -116,7 +160,7 @@ export declare const CLI_INSTALL_URLS: Record<CloudProvider, {
|
|
|
116
160
|
/**
|
|
117
161
|
* Get login commands for cloud CLIs
|
|
118
162
|
*/
|
|
119
|
-
export declare const CLI_LOGIN_COMMANDS: Record<CloudProvider, string>;
|
|
163
|
+
export declare const CLI_LOGIN_COMMANDS: Record<CloudProvider, string | string[]>;
|
|
120
164
|
/**
|
|
121
165
|
* Terraform installation status
|
|
122
166
|
*/
|