@aws-amplify/cli-internal
Version:
Amplify CLI
177 lines • 7.56 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createUnifiedCategoryView = exports.cfnChangesetConsoleUrl = void 0;
const client_cloudformation_1 = require("@aws-sdk/client-cloudformation");
const chalk_1 = __importDefault(require("chalk"));
const amplify_cli_core_1 = require("@aws-amplify/amplify-cli-core");
const categories_1 = require("../../gen2-migration/_common/categories");
function colorResourceLine(symbol, line) {
if (symbol === '+')
return chalk_1.default.green(line);
if (symbol === '-')
return chalk_1.default.red(line);
if (symbol === '~')
return chalk_1.default.yellow(line);
return line;
}
function getActionSymbol(action) {
if (action === client_cloudformation_1.ChangeAction.Add)
return '+';
if (action === client_cloudformation_1.ChangeAction.Remove)
return '-';
return '~';
}
function getCFDriftSymbol(status) {
if (status === client_cloudformation_1.StackResourceDriftStatus.MODIFIED)
return '~';
if (status === client_cloudformation_1.StackResourceDriftStatus.DELETED)
return '-';
return '?';
}
function regionFromArn(arn) {
try {
return (0, amplify_cli_core_1.parseArn)(arn).region || undefined;
}
catch (_a) {
return undefined;
}
}
function cfnDriftConsoleUrl(stackArn) {
const region = regionFromArn(stackArn);
if (!region)
return undefined;
return `https://${region}.console.aws.amazon.com/cloudformation/home?region=${region}#/stacks/drifts?stackId=${encodeURIComponent(stackArn)}`;
}
function cfnChangesetConsoleUrl(changeSetArn, stackArn) {
const region = regionFromArn(changeSetArn);
if (!region)
return undefined;
const encodedStackId = encodeURIComponent(stackArn || '');
const encodedChangeSetId = encodeURIComponent(changeSetArn);
return `https://${region}.console.aws.amazon.com/cloudformation/home?region=${region}#/stacks/changesets/changes?stackId=${encodedStackId}&changeSetId=${encodedChangeSetId}`;
}
exports.cfnChangesetConsoleUrl = cfnChangesetConsoleUrl;
function flattenTree(node, result = []) {
result.push(node);
for (const child of node.children)
flattenTree(child, result);
return result;
}
function collectDriftBlocks(phase1, phase2, phase3) {
const blocks = [];
for (const node of flattenTree(phase1.root)) {
for (const drift of node.drifts) {
blocks.push({
categoryName: node.category,
logicalId: drift.LogicalResourceId || 'Unknown',
type: 'cf',
cfDrift: drift,
driftDetectionId: node.driftDetectionId,
});
}
}
if (!phase2.skipped && phase2.changes.length > 0) {
const flattenChanges = (changes, fallbackCategory, fallbackChangeSetId, parentStackArn) => {
for (const change of changes) {
if (change.ResourceType === 'AWS::CloudFormation::Stack' && change.nestedChanges && change.nestedChanges.length > 0) {
flattenChanges(change.nestedChanges, (0, categories_1.extractCategory)(change.LogicalResourceId), change.ChangeSetId || fallbackChangeSetId, change.PhysicalResourceId || parentStackArn);
}
else {
const resourceCategory = (0, categories_1.extractCategory)(change.LogicalResourceId);
blocks.push({
categoryName: resourceCategory !== 'Other' ? resourceCategory : fallbackCategory,
logicalId: change.LogicalResourceId || 'Unknown',
type: 'template',
templateChange: change,
changeSetId: change.ChangeSetId || fallbackChangeSetId,
stackArn: parentStackArn,
});
}
}
};
flattenChanges(phase2.changes, 'Other', phase2.changeSetId);
}
const localResources = [
...(phase3.resourcesToBeCreated || []),
...(phase3.resourcesToBeUpdated || []),
...(phase3.resourcesToBeDeleted || []),
];
if (!phase3.skipped && localResources.length > 0) {
const seenCategories = new Set();
for (const resource of localResources) {
const categoryName = (0, categories_1.extractCategory)(resource.category || resource.service || 'Other');
if (!seenCategories.has(categoryName)) {
seenCategories.add(categoryName);
blocks.push({ categoryName, type: 'local' });
}
}
}
return blocks;
}
function formatPropertyDiffs(differences) {
let output = '';
for (const propDiff of differences) {
output += ` Property: ${propDiff.PropertyPath}\n`;
if (propDiff.ActualValue != null) {
output += ` ${chalk_1.default.green(`Deployed: "${propDiff.ActualValue}"`)}\n`;
}
if (propDiff.ExpectedValue != null) {
output += ` ${chalk_1.default.red(`Expected: "${propDiff.ExpectedValue}"`)}\n`;
}
}
return output;
}
function formatBlock(block) {
const header = block.logicalId ? `${block.categoryName.toUpperCase()} ${block.logicalId}` : block.categoryName.toUpperCase();
let output = chalk_1.default.bold(header) + '\n';
if (block.type === 'cf') {
const drift = block.cfDrift;
const symbol = getCFDriftSymbol(drift.StackResourceDriftStatus);
const arn = drift.PhysicalResourceId || '';
output += ` CloudFormation Drift: Deployed resources do not match templates\n`;
if (block.driftDetectionId) {
const stackArn = drift.StackId || '';
const driftUrl = cfnDriftConsoleUrl(stackArn);
output += ` Drift Id: ${driftUrl || block.driftDetectionId}\n`;
}
if (arn) {
output += `\n ${arn}\n`;
}
output += ` ${colorResourceLine(symbol, `${symbol} ${drift.ResourceType || 'Unknown'}`)}\n`;
if (drift.StackResourceDriftStatus === client_cloudformation_1.StackResourceDriftStatus.MODIFIED &&
drift.PropertyDifferences &&
drift.PropertyDifferences.length > 0) {
output += formatPropertyDiffs(drift.PropertyDifferences);
}
}
else if (block.type === 'template') {
const change = block.templateChange;
const symbol = getActionSymbol(change.Action);
output += ` Template Drift: S3 and deployed templates differ\n`;
if (block.changeSetId) {
const changesetUrl = cfnChangesetConsoleUrl(block.changeSetId, block.stackArn);
output += ` Changeset Id: ${changesetUrl || block.changeSetId}\n`;
}
output += ` ${colorResourceLine(symbol, `${symbol} ${change.ResourceType || 'Unknown'}`)}\n`;
}
else {
output += ` Local Drift: Undeployed changes in this category\n`;
}
return output;
}
function createUnifiedCategoryView(phase1, phase2, phase3) {
const blocks = collectDriftBlocks(phase1, phase2, phase3);
if (blocks.length === 0) {
return undefined;
}
let output = '\n';
for (const block of blocks) {
output += formatBlock(block) + '\n';
}
return output;
}
exports.createUnifiedCategoryView = createUnifiedCategoryView;
//# sourceMappingURL=drift-formatter.js.map