UNPKG

serverless-domain-manager

Version:
182 lines (181 loc) 8.53 kB
"use strict"; /** * Wrapper class for AWS CloudFormation provider */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; const client_cloudformation_1 = require("@aws-sdk/client-cloudformation"); const globals_1 = __importDefault(require("../globals")); const logging_1 = __importDefault(require("../logging")); const utils_1 = require("../utils"); class CloudFormationWrapper { constructor(credentials) { // for the CloudFormation stack we should use the `base` stage not the plugin custom stage const defaultStackName = globals_1.default.serverless.service.service + "-" + globals_1.default.getBaseStage(); this.stackName = globals_1.default.serverless.service.provider.stackName || defaultStackName; this.cloudFormation = new client_cloudformation_1.CloudFormationClient({ credentials, region: globals_1.default.getRegion(), retryStrategy: globals_1.default.getRetryStrategy(), requestHandler: globals_1.default.getRequestHandler(), endpoint: globals_1.default.getServiceEndpoint("cloudformation") }); } /** * Get an API id from the existing config or CloudFormation stack resources or outputs */ findApiId(apiType) { return __awaiter(this, void 0, void 0, function* () { const configApiId = yield this.getConfigId(apiType); if (configApiId) { return configApiId; } return yield this.getStackApiId(apiType); }); } /** * Get an API id from the existing config or CloudFormation stack based on provider.apiGateway params */ getConfigId(apiType) { return __awaiter(this, void 0, void 0, function* () { const apiGateway = globals_1.default.serverless.service.provider.apiGateway || {}; const apiIdKey = globals_1.default.gatewayAPIIdKeys[apiType]; const apiGatewayValue = apiGateway[apiIdKey]; if (apiGatewayValue) { if (typeof apiGatewayValue === "string") { return apiGatewayValue; } return yield this.getCloudformationId(apiGatewayValue, apiType); } return null; }); } getCloudformationId(apiGatewayValue, apiType) { return __awaiter(this, void 0, void 0, function* () { // in case object and Fn::ImportValue try to get API id from the CloudFormation outputs const importName = apiGatewayValue[globals_1.default.CFFuncNames.fnImport]; if (importName) { const importValues = yield this.getImportValues([importName]); const nameValue = importValues[importName]; if (!nameValue) { logging_1.default.logWarning(`CloudFormation ImportValue '${importName}' not found in the outputs`); } return nameValue; } const ref = apiGatewayValue[globals_1.default.CFFuncNames.ref]; if (ref) { try { return yield this.getStackApiId(apiType, ref); } catch (error) { logging_1.default.logWarning(`Unable to get ref ${ref} value.\n ${error.message}`); return null; } } // log warning not supported restApiId logging_1.default.logWarning(`Unsupported apiGateway.${apiType} object`); return null; }); } /** * Gets rest API id from CloudFormation stack or nested stack */ getStackApiId(apiType_1) { return __awaiter(this, arguments, void 0, function* (apiType, logicalResourceId = null) { if (!logicalResourceId) { logicalResourceId = globals_1.default.CFResourceIds[apiType]; } let response; try { // trying to get information for specified stack name response = yield this.getStack(logicalResourceId, this.stackName); } catch (_a) { // in case error trying to get information from some of nested stacks response = yield this.getNestedStack(logicalResourceId, this.stackName); } if (!response) { throw new Error(`Failed to find logicalResourceId '${logicalResourceId}' for the stack ${this.stackName}\n` + "Make sure the stack exists and the API gateway event is added"); } const apiId = response.StackResourceDetail.PhysicalResourceId; if (!apiId) { throw new Error(`No ApiId associated with CloudFormation stack ${this.stackName}`); } return apiId; }); } /** * Gets values by names from cloudformation exports */ getImportValues(names) { return __awaiter(this, void 0, void 0, function* () { const exports = yield (0, utils_1.getAWSPagedResults)(this.cloudFormation, "Exports", "NextToken", "NextToken", new client_cloudformation_1.ListExportsCommand({})); // filter Exports by names which we need const filteredExports = exports.filter((item) => names.indexOf(item.Name) !== -1); // converting a list of unique values to dict // [{Name: "export-name", Value: "export-value"}, ...] - > {"export-name": "export-value"} return filteredExports.reduce((prev, current) => (Object.assign(Object.assign({}, prev), { [current.Name]: current.Value })), {}); }); } /** * Returns a description of the specified resource in the specified stack. */ getStack(logicalResourceId, stackName) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.cloudFormation.send(new client_cloudformation_1.DescribeStackResourceCommand({ LogicalResourceId: logicalResourceId, StackName: stackName })); } catch (err) { throw new Error(`Failed to find CloudFormation resources with an error: ${err.message}\n`); } }); } /** * Returns a description of the specified resource in the specified nested stack. */ getNestedStack(logicalResourceId, stackName) { return __awaiter(this, void 0, void 0, function* () { // get all stacks from the CloudFormation const stacks = yield (0, utils_1.getAWSPagedResults)(this.cloudFormation, "Stacks", "NextToken", "NextToken", new client_cloudformation_1.DescribeStacksCommand({})); // filter stacks by given stackName and check by nested stack RootId const regex = new RegExp("/" + stackName + "/"); const filteredStackNames = stacks .reduce((acc, stack) => { if (!stack.RootId) { return acc; } const match = stack.RootId.match(regex); if (match) { acc.push(stack.StackName); } return acc; }, []); for (const name of filteredStackNames) { try { // stop the loop and return the stack details in case the first one found // in case of error continue the looping return yield this.getStack(logicalResourceId, name); } catch (err) { logging_1.default.logWarning(err.message); } } return null; }); } } module.exports = CloudFormationWrapper;