'use strict'; const BbPromise = require('bluebird'); module.exports = { create() { // Note: using three dots instead of ellipsis to support non uni-code consoles. this.serverless.cli.log('Creating Stack...'); const stackName = this.provider.naming.getStackName(); let stackTags = { STAGE: this.provider.getStage() }; // Merge additional stack tags if (typeof this.serverless.service.provider.stackTags === 'object') { const customKeys = Object.keys(this.serverless.service.provider.stackTags); const collisions = Object.keys(stackTags).filter(defaultKey => customKeys.some(key => defaultKey.toLowerCase() === key.toLowerCase()) ); // Delete collisions upfront for (const key of collisions) { delete stackTags[key]; } stackTags = Object.assign(stackTags, this.serverless.service.provider.stackTags); } const params = { StackName: stackName, OnFailure: 'DELETE', Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'], Parameters: [], TemplateBody: JSON.stringify(this.serverless.service.provider.coreCloudFormationTemplate), Tags: Object.keys(stackTags).map(key => ({ Key: key, Value: stackTags[key] })), }; if ( this.serverless.service.provider.compiledCloudFormationTemplate && this.serverless.service.provider.compiledCloudFormationTemplate.Transform ) { params.Capabilities.push('CAPABILITY_AUTO_EXPAND'); } if (this.serverless.service.provider.cfnRole) { params.RoleARN = this.serverless.service.provider.cfnRole; } if (this.serverless.service.provider.notificationArns) { params.NotificationARNs = this.serverless.service.provider.notificationArns; } return this.provider .request('CloudFormation', 'createStack', params) .then(cfData => this.monitorStack('create', cfData)); }, createStack() { const stackName = this.provider.naming.getStackName(); if (/^[^a-zA-Z].+|.*[^a-zA-Z0-9-].*/.test(stackName) || stackName.length > 128) { const errorMessage = [ `The stack service name "${stackName}" is not valid. `, 'A service name should only contain alphanumeric', ' (case sensitive) and hyphens. It should start', " with an alphabetic character and shouldn't", ' exceed 128 characters.', ].join(''); throw new this.serverless.classes.Error(errorMessage); } return BbPromise.bind(this) .then(() => this.provider .request('CloudFormation', 'describeStacks', { StackName: stackName }) .then(data => { const shouldCheckStackOutput = // check stack output only if acceleration is requested this.provider.isS3TransferAccelerationEnabled() && // custom deployment bucket won't generate any output (no check) !this.serverless.service.provider.deploymentBucket; if (shouldCheckStackOutput) { const isAlreadyAccelerated = data.Stacks[0].Outputs.some( output => output.OutputKey === 'ServerlessDeploymentBucketAccelerated' ); if (!isAlreadyAccelerated) { this.serverless.cli.log('Not using S3 Transfer Acceleration (1st deploy)'); this.provider.disableTransferAccelerationForCurrentDeploy(); } } return BbPromise.resolve('alreadyCreated'); }) ) .catch(e => { if (e.message.indexOf('does not exist') > -1) { if (this.serverless.service.provider.deploymentBucket) { this.createLater = true; return BbPromise.resolve(); } return BbPromise.bind(this).then(this.create); } throw e; }); }, };