fix create/delete when there exists broken stacks
This commit is contained in:
@@ -1,18 +1,11 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import { exit } from 'process';
|
import { exit } from 'process';
|
||||||
|
import { getTestStacksInfo } from '../../../../common/testservers';
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const { AutoComplete, Input, Confirm } = require('enquirer');
|
const { AutoComplete, Input, Confirm } = require('enquirer');
|
||||||
import { defaultMenuSettings, getSettings } from '../../../../globals';
|
import { defaultMenuSettings, getSettings } from '../../../../globals';
|
||||||
import { getRepoBranches } from '../../../../services/github_rest';
|
import { getRepoBranches } from '../../../../services/github_rest';
|
||||||
|
|
||||||
type Stack = {
|
|
||||||
name: string;
|
|
||||||
status: string;
|
|
||||||
drift_status: string;
|
|
||||||
branch: string;
|
|
||||||
updated: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getConfirmation = async (branchName: string, stackName: string) => {
|
const getConfirmation = async (branchName: string, stackName: string) => {
|
||||||
const url = stackName.replace('photowall-', '');
|
const url = stackName.replace('photowall-', '');
|
||||||
const confirm = new Confirm({
|
const confirm = new Confirm({
|
||||||
@@ -36,7 +29,7 @@ module.exports = {
|
|||||||
description: 'Create new testserver on aws (c)',
|
description: 'Create new testserver on aws (c)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
const { system, strings, print, filesystem } = toolbox;
|
const { system, strings, print } = toolbox;
|
||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
const config = await getSettings(toolbox);
|
const config = await getSettings(toolbox);
|
||||||
@@ -67,47 +60,9 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
const branchName = await promptBranch.run();
|
const branchName = await promptBranch.run();
|
||||||
|
|
||||||
const stacks = JSON.parse(
|
const testStacks = await getTestStacksInfo(toolbox);
|
||||||
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
|
||||||
);
|
|
||||||
const teststacks: Stack[] = await Promise.all(
|
|
||||||
stacks.StackSummaries.map(async item => {
|
|
||||||
if (
|
|
||||||
item.StackName.indexOf('photowall-test-') > -1 &&
|
|
||||||
[
|
|
||||||
'CREATE_COMPLETE',
|
|
||||||
'UPDATE_COMPLETE',
|
|
||||||
'UPDATE_IN_PROGRESS',
|
|
||||||
'UPDATE_FAILED',
|
|
||||||
'CREATE_FAILED',
|
|
||||||
'CREATE_IN_PROGRESS'
|
|
||||||
].includes(item.StackStatus)
|
|
||||||
) {
|
|
||||||
const pipeline = JSON.parse(
|
|
||||||
strings.trim(
|
|
||||||
await system.run(
|
|
||||||
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const branch = pipeline.pipeline.stages.filter(stage => {
|
|
||||||
return stage.name === 'Source';
|
|
||||||
})[0].actions[0].configuration.Branch;
|
|
||||||
const lastUpdated = pipeline.metadata.updated;
|
|
||||||
return {
|
|
||||||
name: item.StackName,
|
|
||||||
status: item.StackStatus,
|
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
|
||||||
branch: branch,
|
|
||||||
updated: lastUpdated
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
const options = testStacks.map(item => {
|
||||||
const options = curratedStacks.map(item => {
|
|
||||||
return item.name;
|
return item.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -121,11 +76,11 @@ module.exports = {
|
|||||||
|
|
||||||
if (options.includes(stackName)) {
|
if (options.includes(stackName)) {
|
||||||
print.error(`
|
print.error(`
|
||||||
|
|
||||||
The stack ${chalk.yellow(
|
The stack ${chalk.yellow(
|
||||||
stackName
|
stackName
|
||||||
)} is already taken, try the update command instead.
|
)} is already taken, try the update command instead.
|
||||||
|
|
||||||
`);
|
`);
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
await toolbox.menu.showMenu('deployment testservers');
|
await toolbox.menu.showMenu('deployment testservers');
|
||||||
|
|||||||
@@ -1,17 +1,10 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import { GluegunSystem } from 'gluegun';
|
import { GluegunSystem } from 'gluegun';
|
||||||
import { defaultMenuSettings } from '../../../../globals';
|
import { defaultMenuSettings } from '../../../../globals';
|
||||||
|
import { getTestStacksInfo } from '../../../../common/testservers';
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const { AutoComplete, Input, Confirm } = require('enquirer');
|
const { AutoComplete, Input, Confirm } = require('enquirer');
|
||||||
|
|
||||||
type Stack = {
|
|
||||||
name: string;
|
|
||||||
status: string;
|
|
||||||
drift_status: string;
|
|
||||||
branch: string;
|
|
||||||
updated: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getConfirmation = async (stackName: string) => {
|
const getConfirmation = async (stackName: string) => {
|
||||||
const confirm = new Confirm({
|
const confirm = new Confirm({
|
||||||
name: 'confirm',
|
name: 'confirm',
|
||||||
@@ -57,49 +50,11 @@ module.exports = {
|
|||||||
description: 'Choose a testserver to delete (d)',
|
description: 'Choose a testserver to delete (d)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
const { system, strings, print } = toolbox;
|
const { system, strings } = toolbox;
|
||||||
|
|
||||||
const stacks = JSON.parse(
|
const testStacks = await getTestStacksInfo(toolbox);
|
||||||
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
|
||||||
);
|
|
||||||
const teststacks: Stack[] = await Promise.all(
|
|
||||||
stacks.StackSummaries.map(async item => {
|
|
||||||
if (
|
|
||||||
item.StackName.indexOf('photowall-test-') > -1 &&
|
|
||||||
[
|
|
||||||
'CREATE_COMPLETE',
|
|
||||||
'UPDATE_COMPLETE',
|
|
||||||
'UPDATE_IN_PROGRESS',
|
|
||||||
'UPDATE_FAILED',
|
|
||||||
'CREATE_FAILED',
|
|
||||||
'CREATE_IN_PROGRESS'
|
|
||||||
].includes(item.StackStatus)
|
|
||||||
) {
|
|
||||||
const pipeline = JSON.parse(
|
|
||||||
strings.trim(
|
|
||||||
await system.run(
|
|
||||||
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const branch = pipeline.pipeline.stages.filter(stage => {
|
|
||||||
return stage.name === 'Source';
|
|
||||||
})[0].actions[0].configuration.Branch;
|
|
||||||
const lastUpdated = pipeline.metadata.updated;
|
|
||||||
return {
|
|
||||||
name: item.StackName,
|
|
||||||
status: item.StackStatus,
|
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
|
||||||
branch: branch,
|
|
||||||
updated: lastUpdated
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
const options = testStacks.map(item => {
|
||||||
const options = curratedStacks.map(item => {
|
|
||||||
return item.name;
|
return item.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -116,6 +71,9 @@ module.exports = {
|
|||||||
const shouldRun = await getConfirmation(stackName);
|
const shouldRun = await getConfirmation(stackName);
|
||||||
|
|
||||||
if (shouldRun) {
|
if (shouldRun) {
|
||||||
|
// Order here matters. Try to delete things in the order
|
||||||
|
// they are created on stack creation so we can delete everything that created
|
||||||
|
// during a failed create stack operation.
|
||||||
await runCommand(
|
await runCommand(
|
||||||
system,
|
system,
|
||||||
await confirmCommand(
|
await confirmCommand(
|
||||||
@@ -128,18 +86,6 @@ module.exports = {
|
|||||||
`aws ecr delete-repository --force --repository-name ${stackName}`
|
`aws ecr delete-repository --force --repository-name ${stackName}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
await runCommand(
|
|
||||||
system,
|
|
||||||
await confirmCommand(
|
|
||||||
`aws logs delete-log-group --log-group-name /aws/codebuild/${stackName}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
await runCommand(
|
|
||||||
system,
|
|
||||||
await confirmCommand(
|
|
||||||
`aws logs delete-log-group --log-group-name ${stackName}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const artifacts = strings
|
const artifacts = strings
|
||||||
.trim(
|
.trim(
|
||||||
await system.run(
|
await system.run(
|
||||||
@@ -153,6 +99,18 @@ module.exports = {
|
|||||||
await confirmCommand(`aws s3 rb --force s3://${artifact}`)
|
await confirmCommand(`aws s3 rb --force s3://${artifact}`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await runCommand(
|
||||||
|
system,
|
||||||
|
await confirmCommand(
|
||||||
|
`aws logs delete-log-group --log-group-name /aws/codebuild/${stackName}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
await runCommand(
|
||||||
|
system,
|
||||||
|
await confirmCommand(
|
||||||
|
`aws logs delete-log-group --log-group-name ${stackName}`
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
|
|||||||
@@ -1,82 +1,16 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import chalk = require('chalk');
|
import chalk = require('chalk');
|
||||||
import { defaultMenuSettings } from '../../../../globals';
|
import { defaultMenuSettings } from '../../../../globals';
|
||||||
|
import { getTestStacksInfo } from '../../../../common/testservers';
|
||||||
const tto = require('terminal-table-output').create();
|
const tto = require('terminal-table-output').create();
|
||||||
|
|
||||||
type Stack = {
|
|
||||||
name: string;
|
|
||||||
status: string;
|
|
||||||
drift_status: string;
|
|
||||||
branch: string;
|
|
||||||
updated: string;
|
|
||||||
created: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'status',
|
name: 'status',
|
||||||
alias: ['s'],
|
alias: ['s'],
|
||||||
description: 'Show status for available testservers (s)',
|
description: 'Show status for available testservers (s)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
const { system, strings } = toolbox;
|
const testStacks = await getTestStacksInfo(toolbox);
|
||||||
|
|
||||||
const stacks = JSON.parse(
|
|
||||||
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
|
||||||
);
|
|
||||||
const teststacks: Stack[] = await Promise.all(
|
|
||||||
stacks.StackSummaries.map(async item => {
|
|
||||||
if (
|
|
||||||
item.StackName.indexOf('photowall-test-') > -1 &&
|
|
||||||
[
|
|
||||||
'CREATE_COMPLETE',
|
|
||||||
'UPDATE_COMPLETE',
|
|
||||||
'UPDATE_IN_PROGRESS',
|
|
||||||
'UPDATE_FAILED',
|
|
||||||
'CREATE_FAILED',
|
|
||||||
'CREATE_IN_PROGRESS',
|
|
||||||
'ROLLBACK_COMPLETE',
|
|
||||||
].includes(item.StackStatus)
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const pipeline = JSON.parse(
|
|
||||||
strings.trim(
|
|
||||||
await system.run(
|
|
||||||
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const sourceStage = pipeline.pipeline.stages.filter(stage => {
|
|
||||||
return stage.name === 'Source';
|
|
||||||
});
|
|
||||||
const branch =
|
|
||||||
sourceStage[0].actions[0].configuration.Branch ??
|
|
||||||
sourceStage[0].actions[0].configuration.BranchName;
|
|
||||||
const lastUpdated = pipeline.metadata.updated;
|
|
||||||
return {
|
|
||||||
name: item.StackName,
|
|
||||||
status: item.StackStatus,
|
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
|
||||||
branch: branch,
|
|
||||||
updated: lastUpdated,
|
|
||||||
created: item.CreationTime,
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
name: item.StackName,
|
|
||||||
status: item.StackStatus,
|
|
||||||
drift_status: '',
|
|
||||||
branch: '',
|
|
||||||
updated: '',
|
|
||||||
created: item.CreationTime,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
|
||||||
|
|
||||||
const status = (status: string) => {
|
const status = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'UPDATE_FAILED':
|
case 'UPDATE_FAILED':
|
||||||
@@ -88,7 +22,7 @@ module.exports = {
|
|||||||
return `(${chalk.yellow('IN PROGRESS')})`;
|
return `(${chalk.yellow('IN PROGRESS')})`;
|
||||||
case 'UPDATE_COMPLETE':
|
case 'UPDATE_COMPLETE':
|
||||||
case 'CREATE_COMPLETE':
|
case 'CREATE_COMPLETE':
|
||||||
return `(${chalk.greenBright('OK')})`;
|
return `(${chalk.green('OK')})`;
|
||||||
default:
|
default:
|
||||||
return `(${chalk.red.bold('?')})`;
|
return `(${chalk.red.bold('?')})`;
|
||||||
}
|
}
|
||||||
@@ -142,18 +76,20 @@ module.exports = {
|
|||||||
.pushrow(['| status', 'stack', 'branch', 'time since update', 'url'])
|
.pushrow(['| status', 'stack', 'branch', 'time since update', 'url'])
|
||||||
.line('-');
|
.line('-');
|
||||||
// Sort on stackname
|
// Sort on stackname
|
||||||
curratedStacks.sort((a, b) => {
|
testStacks.sort((a, b) => {
|
||||||
if (a.name < b.name) return -1;
|
if (a.name < b.name) return -1;
|
||||||
if (a.name > b.name) return 1;
|
if (a.name > b.name) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
for (const stack of curratedStacks) {
|
for (const stack of testStacks) {
|
||||||
const t = new Date(stack.updated);
|
const t = new Date(stack.updated);
|
||||||
tto.pushrow([
|
tto.pushrow([
|
||||||
status(stack.status),
|
status(stack.status),
|
||||||
stack.name,
|
stack.name,
|
||||||
ellipsis(stack.branch, 24),
|
ellipsis(stack.branch, 24),
|
||||||
!isNaN(t.getTime()) ? timeSince(t.getTime()) : timeSince((new Date(stack.created)).getTime()),
|
!isNaN(t.getTime())
|
||||||
|
? timeSince(t.getTime())
|
||||||
|
: timeSince(new Date(stack.created).getTime()),
|
||||||
stackUrl(stack.name)
|
stackUrl(stack.name)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const chalk = require('chalk');
|
|||||||
const { AutoComplete, Input, Confirm } = require('enquirer');
|
const { AutoComplete, Input, Confirm } = require('enquirer');
|
||||||
import { Octokit } from '@octokit/core';
|
import { Octokit } from '@octokit/core';
|
||||||
import { exit } from 'process';
|
import { exit } from 'process';
|
||||||
|
import { getTestStacksInfo } from '../../../../common/testservers';
|
||||||
import {
|
import {
|
||||||
defaultMenuSettings,
|
defaultMenuSettings,
|
||||||
getCurrentBranch,
|
getCurrentBranch,
|
||||||
@@ -99,47 +100,8 @@ module.exports = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stacks = JSON.parse(
|
const testStacks = await getTestStacksInfo(toolbox);
|
||||||
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
const options = testStacks.map(item => {
|
||||||
);
|
|
||||||
const teststacks: Stack[] = await Promise.all(
|
|
||||||
stacks.StackSummaries.map(async item => {
|
|
||||||
if (
|
|
||||||
item.StackName.indexOf('photowall-test-') > -1 &&
|
|
||||||
[
|
|
||||||
'CREATE_COMPLETE',
|
|
||||||
'UPDATE_COMPLETE',
|
|
||||||
'UPDATE_IN_PROGRESS',
|
|
||||||
'UPDATE_FAILED',
|
|
||||||
'CREATE_FAILED',
|
|
||||||
'CREATE_IN_PROGRESS'
|
|
||||||
].includes(item.StackStatus)
|
|
||||||
) {
|
|
||||||
const pipeline = JSON.parse(
|
|
||||||
strings.trim(
|
|
||||||
await system.run(
|
|
||||||
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const branch = pipeline.pipeline.stages.filter(stage => {
|
|
||||||
return stage.name === 'Source';
|
|
||||||
})[0].actions[0].configuration.Branch;
|
|
||||||
const lastUpdated = pipeline.metadata.updated;
|
|
||||||
return {
|
|
||||||
name: item.StackName,
|
|
||||||
status: item.StackStatus,
|
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
|
||||||
branch: branch,
|
|
||||||
updated: lastUpdated
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
|
||||||
const options = curratedStacks.map(item => {
|
|
||||||
return item.name;
|
return item.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
|
|
||||||
|
export type Stack = {
|
||||||
|
name: string;
|
||||||
|
status: string;
|
||||||
|
drift_status: string;
|
||||||
|
branch: string;
|
||||||
|
updated: string;
|
||||||
|
created: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTestStacksInfo = async (
|
||||||
|
toolbox: GluegunMenuToolbox
|
||||||
|
): Promise<Stack[]> => {
|
||||||
|
const { strings, system } = toolbox;
|
||||||
|
|
||||||
|
const stacks = JSON.parse(
|
||||||
|
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
||||||
|
);
|
||||||
|
|
||||||
|
const allStacks = await Promise.all(
|
||||||
|
stacks.StackSummaries.map(async item => {
|
||||||
|
if (
|
||||||
|
item.StackName.indexOf('photowall-test-') > -1 &&
|
||||||
|
[
|
||||||
|
'CREATE_COMPLETE',
|
||||||
|
'UPDATE_COMPLETE',
|
||||||
|
'UPDATE_IN_PROGRESS',
|
||||||
|
'UPDATE_FAILED',
|
||||||
|
'CREATE_FAILED',
|
||||||
|
'CREATE_IN_PROGRESS',
|
||||||
|
'ROLLBACK_COMPLETE'
|
||||||
|
].includes(item.StackStatus)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const pipeline = JSON.parse(
|
||||||
|
strings.trim(
|
||||||
|
await system.run(
|
||||||
|
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const sourceStage = pipeline.pipeline.stages.filter(stage => {
|
||||||
|
return stage.name === 'Source';
|
||||||
|
});
|
||||||
|
const branch =
|
||||||
|
sourceStage[0].actions[0].configuration.Branch ??
|
||||||
|
sourceStage[0].actions[0].configuration.BranchName;
|
||||||
|
const lastUpdated = pipeline.metadata.updated;
|
||||||
|
return {
|
||||||
|
name: item.StackName,
|
||||||
|
status: item.StackStatus,
|
||||||
|
drift_status: item.DriftInformation.StackDriftStatus,
|
||||||
|
branch: branch,
|
||||||
|
updated: lastUpdated,
|
||||||
|
created: item.CreationTime
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
name: item.StackName,
|
||||||
|
status: item.StackStatus,
|
||||||
|
drift_status: '',
|
||||||
|
branch: '',
|
||||||
|
updated: '',
|
||||||
|
created: item.CreationTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve(null);
|
||||||
|
})
|
||||||
|
) as Stack[] | null;
|
||||||
|
return allStacks.filter(Boolean);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user