@joshualiamzn/open-stack 0.0.1 → 0.0.2
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/.kiro/memory-session-id +1 -0
- package/package.json +3 -1
- package/src/aws.mjs +680 -61
- package/src/cli.mjs +57 -7
- package/src/commands/create.mjs +200 -18
- package/src/commands/demo.mjs +193 -0
- package/src/commands/describe.mjs +53 -43
- package/src/commands/help.mjs +5 -6
- package/src/commands/index.mjs +14 -17
- package/src/commands/list.mjs +14 -13
- package/src/config.mjs +11 -1
- package/src/eks.mjs +356 -0
- package/src/interactive.mjs +290 -198
- package/src/main.mjs +111 -16
- package/src/render.mjs +1 -2
- package/src/repl.mjs +19 -43
- package/src/ui.mjs +123 -12
- package/src/commands/update.mjs +0 -309
package/src/commands/update.mjs
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
import { select, input, confirm } from '@inquirer/prompts';
|
|
2
|
-
import { getPipeline, updatePipeline } from '../aws.mjs';
|
|
3
|
-
import { printInfo, printPanel, createSpinner, theme, ARROW } from '../ui.mjs';
|
|
4
|
-
import { loadPipelines } from './index.mjs';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Extract known configurable values from pipeline YAML by regex.
|
|
8
|
-
*/
|
|
9
|
-
function parseConfigValues(yaml) {
|
|
10
|
-
if (!yaml) return {};
|
|
11
|
-
|
|
12
|
-
const vals = {};
|
|
13
|
-
|
|
14
|
-
// OpenSearch hosts (first occurrence)
|
|
15
|
-
const hostsMatch = yaml.match(/hosts:\s*\n\s*-\s*'([^']+)'/);
|
|
16
|
-
if (hostsMatch) vals.opensearchEndpoint = hostsMatch[1];
|
|
17
|
-
|
|
18
|
-
// IAM role ARN (sts_role_arn)
|
|
19
|
-
const roleMatch = yaml.match(/sts_role_arn:\s*"([^"]+)"/);
|
|
20
|
-
if (roleMatch) vals.iamRoleArn = roleMatch[1];
|
|
21
|
-
|
|
22
|
-
// Prometheus URL
|
|
23
|
-
const promMatch = yaml.match(/url:\s*'([^']*prometheus[^']*)'/i)
|
|
24
|
-
|| yaml.match(/url:\s*'([^']*aps-workspaces[^']*)'/i);
|
|
25
|
-
if (promMatch) vals.prometheusUrl = promMatch[1];
|
|
26
|
-
|
|
27
|
-
// Service map window duration
|
|
28
|
-
const windowMatch = yaml.match(/window_duration:\s*(\S+)/);
|
|
29
|
-
if (windowMatch) vals.serviceMapWindow = windowMatch[1];
|
|
30
|
-
|
|
31
|
-
// Serverless flag
|
|
32
|
-
vals.serverless = /serverless:\s*true/i.test(yaml);
|
|
33
|
-
|
|
34
|
-
return vals;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Replace known config values in pipeline YAML.
|
|
39
|
-
*/
|
|
40
|
-
function patchConfigYaml(yaml, updates) {
|
|
41
|
-
let patched = yaml;
|
|
42
|
-
|
|
43
|
-
if (updates.opensearchEndpoint) {
|
|
44
|
-
patched = patched.replace(
|
|
45
|
-
/(hosts:\s*\n\s*-\s*')([^']+)(')/g,
|
|
46
|
-
`$1${updates.opensearchEndpoint}$3`,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (updates.iamRoleArn) {
|
|
51
|
-
patched = patched.replace(
|
|
52
|
-
/(sts_role_arn:\s*")([^"]+)(")/g,
|
|
53
|
-
`$1${updates.iamRoleArn}$3`,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (updates.prometheusUrl) {
|
|
58
|
-
patched = patched.replace(
|
|
59
|
-
/(url:\s*')([^']*(?:prometheus|aps-workspaces)[^']*)(')/gi,
|
|
60
|
-
`$1${updates.prometheusUrl}$3`,
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (updates.serviceMapWindow) {
|
|
65
|
-
patched = patched.replace(
|
|
66
|
-
/(window_duration:\s*)\S+/,
|
|
67
|
-
`$1${updates.serviceMapWindow}`,
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return patched;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export async function runUpdate(session) {
|
|
75
|
-
console.error();
|
|
76
|
-
|
|
77
|
-
const pipelines = await loadPipelines(session.region);
|
|
78
|
-
|
|
79
|
-
// Filter to updatable pipelines
|
|
80
|
-
const updatable = pipelines.filter((p) => p.status === 'ACTIVE');
|
|
81
|
-
if (updatable.length === 0) {
|
|
82
|
-
printInfo('No active pipelines available for update.');
|
|
83
|
-
console.error();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Select pipeline
|
|
88
|
-
const choices = updatable.map((p) => ({
|
|
89
|
-
name: `${p.name} ${theme.muted(`(OCUs: ${p.minUnits}\u2013${p.maxUnits})`)}`,
|
|
90
|
-
value: p.name,
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
const pipelineName = await select({
|
|
94
|
-
message: 'Select pipeline to update',
|
|
95
|
-
choices,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Get current details
|
|
99
|
-
const detailSpinner = createSpinner(`Loading ${pipelineName}...`);
|
|
100
|
-
detailSpinner.start();
|
|
101
|
-
|
|
102
|
-
let pipeline;
|
|
103
|
-
try {
|
|
104
|
-
pipeline = await getPipeline(session.region, pipelineName);
|
|
105
|
-
detailSpinner.succeed(`Loaded ${pipelineName}`);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
detailSpinner.fail('Failed to get pipeline details');
|
|
108
|
-
throw err;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Parse current config values from YAML
|
|
112
|
-
const current = parseConfigValues(pipeline.pipelineConfigurationBody);
|
|
113
|
-
|
|
114
|
-
// Show current config summary in a panel
|
|
115
|
-
console.error();
|
|
116
|
-
const configEntries = [
|
|
117
|
-
['Min OCUs', String(pipeline.minUnits)],
|
|
118
|
-
['Max OCUs', String(pipeline.maxUnits)],
|
|
119
|
-
];
|
|
120
|
-
if (current.opensearchEndpoint) {
|
|
121
|
-
configEntries.push(['OpenSearch endpoint', current.opensearchEndpoint]);
|
|
122
|
-
}
|
|
123
|
-
if (current.iamRoleArn) {
|
|
124
|
-
configEntries.push(['IAM role ARN', current.iamRoleArn]);
|
|
125
|
-
}
|
|
126
|
-
if (current.prometheusUrl) {
|
|
127
|
-
configEntries.push(['Prometheus URL', current.prometheusUrl]);
|
|
128
|
-
}
|
|
129
|
-
if (current.serviceMapWindow) {
|
|
130
|
-
configEntries.push(['Service map window', current.serviceMapWindow]);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const cwLogGroup = pipeline.logPublishingOptions?.CloudWatchLogDestination?.LogGroup;
|
|
134
|
-
if (cwLogGroup) {
|
|
135
|
-
configEntries.push(['CloudWatch log group', cwLogGroup]);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const persistentBuffering = pipeline.bufferOptions?.PersistentBufferEnabled;
|
|
139
|
-
configEntries.push(['Persistent buffering', persistentBuffering ? 'enabled' : 'disabled']);
|
|
140
|
-
|
|
141
|
-
printPanel('Current Configuration', configEntries);
|
|
142
|
-
console.error();
|
|
143
|
-
|
|
144
|
-
// ── Prompt for new values ──────────────────────────────────────────────
|
|
145
|
-
|
|
146
|
-
const newMinOcu = Number(await input({
|
|
147
|
-
message: 'Minimum OCUs',
|
|
148
|
-
default: String(pipeline.minUnits),
|
|
149
|
-
validate: (v) => /^\d+$/.test(v.trim()) && Number(v) >= 1 || 'Must be a positive integer',
|
|
150
|
-
}));
|
|
151
|
-
|
|
152
|
-
const newMaxOcu = Number(await input({
|
|
153
|
-
message: 'Maximum OCUs',
|
|
154
|
-
default: String(pipeline.maxUnits),
|
|
155
|
-
validate: (v) => /^\d+$/.test(v.trim()) && Number(v) >= newMinOcu || `Must be >= min OCUs (${newMinOcu})`,
|
|
156
|
-
}));
|
|
157
|
-
|
|
158
|
-
// Pipeline config values
|
|
159
|
-
const yamlUpdates = {};
|
|
160
|
-
let configChanged = false;
|
|
161
|
-
|
|
162
|
-
if (current.opensearchEndpoint) {
|
|
163
|
-
const newEndpoint = await input({
|
|
164
|
-
message: 'OpenSearch endpoint',
|
|
165
|
-
default: current.opensearchEndpoint,
|
|
166
|
-
validate: (v) => /^https?:\/\//.test(v.trim()) || 'Must start with http:// or https://',
|
|
167
|
-
});
|
|
168
|
-
if (newEndpoint !== current.opensearchEndpoint) {
|
|
169
|
-
yamlUpdates.opensearchEndpoint = newEndpoint;
|
|
170
|
-
configChanged = true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (current.iamRoleArn) {
|
|
175
|
-
const newRole = await input({
|
|
176
|
-
message: 'IAM role ARN (sts_role_arn)',
|
|
177
|
-
default: current.iamRoleArn,
|
|
178
|
-
validate: (v) => v.trim().startsWith('arn:aws:iam:') || 'Must start with arn:aws:iam:',
|
|
179
|
-
});
|
|
180
|
-
if (newRole !== current.iamRoleArn) {
|
|
181
|
-
yamlUpdates.iamRoleArn = newRole;
|
|
182
|
-
configChanged = true;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (current.prometheusUrl) {
|
|
187
|
-
const newProm = await input({
|
|
188
|
-
message: 'Prometheus remote-write URL',
|
|
189
|
-
default: current.prometheusUrl,
|
|
190
|
-
validate: (v) => /^https?:\/\//.test(v.trim()) || 'Must start with http:// or https://',
|
|
191
|
-
});
|
|
192
|
-
if (newProm !== current.prometheusUrl) {
|
|
193
|
-
yamlUpdates.prometheusUrl = newProm;
|
|
194
|
-
configChanged = true;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (current.serviceMapWindow) {
|
|
199
|
-
const newWindow = await input({
|
|
200
|
-
message: 'Service map window duration',
|
|
201
|
-
default: current.serviceMapWindow,
|
|
202
|
-
validate: (v) => /^\d+[smh]$/.test(v.trim()) || 'Expected format: 10s, 5m, 1h',
|
|
203
|
-
});
|
|
204
|
-
if (newWindow !== current.serviceMapWindow) {
|
|
205
|
-
yamlUpdates.serviceMapWindow = newWindow;
|
|
206
|
-
configChanged = true;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Log publishing options
|
|
211
|
-
const newLogGroup = await input({
|
|
212
|
-
message: 'CloudWatch log group (leave empty to disable)',
|
|
213
|
-
default: cwLogGroup || '',
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
const logPublishingChanged = (newLogGroup || '') !== (cwLogGroup || '');
|
|
217
|
-
|
|
218
|
-
// Persistent buffering
|
|
219
|
-
const newPersistentBuffer = await confirm({
|
|
220
|
-
message: 'Enable persistent buffering?',
|
|
221
|
-
default: !!persistentBuffering,
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const bufferChanged = newPersistentBuffer !== !!persistentBuffering;
|
|
225
|
-
|
|
226
|
-
// ── Check for changes ──────────────────────────────────────────────────
|
|
227
|
-
|
|
228
|
-
const ocuChanged = newMinOcu !== pipeline.minUnits || newMaxOcu !== pipeline.maxUnits;
|
|
229
|
-
|
|
230
|
-
if (!ocuChanged && !configChanged && !logPublishingChanged && !bufferChanged) {
|
|
231
|
-
printInfo('No changes \u2014 all values are the same as current config.');
|
|
232
|
-
console.error();
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// ── Summary of changes ─────────────────────────────────────────────────
|
|
237
|
-
|
|
238
|
-
console.error();
|
|
239
|
-
const changeLines = [];
|
|
240
|
-
if (ocuChanged) {
|
|
241
|
-
changeLines.push(`OCUs: ${pipeline.minUnits}\u2013${pipeline.maxUnits} ${ARROW} ${newMinOcu}\u2013${newMaxOcu}`);
|
|
242
|
-
}
|
|
243
|
-
if (yamlUpdates.opensearchEndpoint) {
|
|
244
|
-
changeLines.push(`OpenSearch: ${theme.muted(current.opensearchEndpoint)} ${ARROW} ${yamlUpdates.opensearchEndpoint}`);
|
|
245
|
-
}
|
|
246
|
-
if (yamlUpdates.iamRoleArn) {
|
|
247
|
-
changeLines.push(`IAM role: ${theme.muted(current.iamRoleArn)} ${ARROW} ${yamlUpdates.iamRoleArn}`);
|
|
248
|
-
}
|
|
249
|
-
if (yamlUpdates.prometheusUrl) {
|
|
250
|
-
changeLines.push(`Prometheus: ${theme.muted(current.prometheusUrl)} ${ARROW} ${yamlUpdates.prometheusUrl}`);
|
|
251
|
-
}
|
|
252
|
-
if (yamlUpdates.serviceMapWindow) {
|
|
253
|
-
changeLines.push(`Service map window: ${current.serviceMapWindow} ${ARROW} ${yamlUpdates.serviceMapWindow}`);
|
|
254
|
-
}
|
|
255
|
-
if (logPublishingChanged) {
|
|
256
|
-
const from = cwLogGroup || '(disabled)';
|
|
257
|
-
const to = newLogGroup || '(disabled)';
|
|
258
|
-
changeLines.push(`CloudWatch logging: ${from} ${ARROW} ${to}`);
|
|
259
|
-
}
|
|
260
|
-
if (bufferChanged) {
|
|
261
|
-
changeLines.push(`Persistent buffering: ${persistentBuffering ? 'enabled' : 'disabled'} ${ARROW} ${newPersistentBuffer ? 'enabled' : 'disabled'}`);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
printPanel('Pending Changes', changeLines.map((l) => ['', theme.warn('\u2022') + ' ' + l]));
|
|
265
|
-
console.error();
|
|
266
|
-
|
|
267
|
-
// Confirm
|
|
268
|
-
const proceed = await confirm({
|
|
269
|
-
message: `Apply these changes to ${pipelineName}?`,
|
|
270
|
-
default: true,
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
if (!proceed) {
|
|
274
|
-
printInfo('Update cancelled.');
|
|
275
|
-
console.error();
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// ── Build update params ────────────────────────────────────────────────
|
|
280
|
-
|
|
281
|
-
const params = {};
|
|
282
|
-
|
|
283
|
-
if (ocuChanged) {
|
|
284
|
-
params.minUnits = newMinOcu;
|
|
285
|
-
params.maxUnits = newMaxOcu;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (configChanged) {
|
|
289
|
-
params.pipelineConfigurationBody = patchConfigYaml(
|
|
290
|
-
pipeline.pipelineConfigurationBody,
|
|
291
|
-
yamlUpdates,
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (logPublishingChanged) {
|
|
296
|
-
params.logPublishingOptions = newLogGroup
|
|
297
|
-
? { IsLoggingEnabled: true, CloudWatchLogDestination: { LogGroup: newLogGroup } }
|
|
298
|
-
: { IsLoggingEnabled: false };
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (bufferChanged) {
|
|
302
|
-
params.bufferOptions = { PersistentBufferEnabled: newPersistentBuffer };
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Execute update
|
|
306
|
-
console.error();
|
|
307
|
-
await updatePipeline(session.region, pipelineName, params);
|
|
308
|
-
console.error();
|
|
309
|
-
}
|