mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
* feat: make direct deployments the default * docs: update docs to specify that direct deployments are the default
199 lines
6.5 KiB
JavaScript
199 lines
6.5 KiB
JavaScript
import getS3EndpointForRegion from '../utils/get-s3-endpoint-for-region.js'
|
|
import utils from '@serverlessinc/sf-core/src/utils.js'
|
|
import isChangeSetWithoutChanges from '../utils/is-change-set-without-changes.js'
|
|
|
|
const { log, progress } = utils
|
|
const NO_UPDATE_MESSAGE = 'No updates are to be performed.'
|
|
|
|
export default {
|
|
async createFallback() {
|
|
this.createLater = false
|
|
progress
|
|
.get('main')
|
|
.notice('Creating AWS CloudFormation stack', { isMainEvent: true })
|
|
|
|
const stackName = this.provider.naming.getStackName()
|
|
const compiledTemplateFileName =
|
|
this.provider.naming.getCompiledTemplateS3Suffix()
|
|
const s3Endpoint = getS3EndpointForRegion(this.provider.getRegion())
|
|
const templateUrl = `https://${s3Endpoint}/${this.bucketName}/${this.serverless.service.package.artifactDirectoryName}/${compiledTemplateFileName}`
|
|
|
|
let monitorCfData
|
|
if (
|
|
!this.serverless.service.provider.deploymentMethod ||
|
|
this.serverless.service.provider.deploymentMethod === 'direct'
|
|
) {
|
|
const params = this.getCreateStackParams({
|
|
templateUrl,
|
|
})
|
|
|
|
monitorCfData = await this.provider.request(
|
|
'CloudFormation',
|
|
'createStack',
|
|
params,
|
|
)
|
|
} else {
|
|
// Change-set based deployment
|
|
const changeSetName = this.provider.naming.getStackChangeSetName()
|
|
|
|
const createChangeSetParams = this.getCreateChangeSetParams({
|
|
changeSetType: 'CREATE',
|
|
templateUrl,
|
|
})
|
|
|
|
const executeChangeSetParams = this.getExecuteChangeSetParams()
|
|
|
|
// Create new change set
|
|
this.provider.didCreateService = true
|
|
log.info('Creating new change set')
|
|
await this.provider.request(
|
|
'CloudFormation',
|
|
'createChangeSet',
|
|
createChangeSetParams,
|
|
)
|
|
|
|
// Wait for changeset to be created
|
|
log.info('Waiting for new change set to be created')
|
|
const changeSetDescription = await this.waitForChangeSetCreation(
|
|
changeSetName,
|
|
stackName,
|
|
)
|
|
|
|
// Check if stack has changes
|
|
if (isChangeSetWithoutChanges(changeSetDescription)) {
|
|
// Cleanup changeset when it does not include any changes
|
|
log.info('Created change set does not include any changes, removing it')
|
|
await this.provider.request('CloudFormation', 'deleteChangeSet', {
|
|
StackName: stackName,
|
|
ChangeSetName: changeSetName,
|
|
})
|
|
this.serverless.service.provider.deploymentWithEmptyChangeSet = true
|
|
return false
|
|
}
|
|
|
|
log.info('Executing created change set')
|
|
await this.provider.request(
|
|
'CloudFormation',
|
|
'executeChangeSet',
|
|
executeChangeSetParams,
|
|
)
|
|
monitorCfData = changeSetDescription
|
|
}
|
|
await this.monitorStack('create', monitorCfData)
|
|
return true
|
|
},
|
|
|
|
async update() {
|
|
const compiledTemplateFileName =
|
|
this.provider.naming.getCompiledTemplateS3Suffix()
|
|
const s3Endpoint = getS3EndpointForRegion(this.provider.getRegion())
|
|
const templateUrl = `https://${s3Endpoint}/${this.bucketName}/${this.serverless.service.package.artifactDirectoryName}/${compiledTemplateFileName}`
|
|
|
|
const stackName = this.provider.naming.getStackName()
|
|
|
|
let monitorCfData
|
|
if (
|
|
!this.serverless.service.provider.deploymentMethod ||
|
|
this.serverless.service.provider.deploymentMethod === 'direct'
|
|
) {
|
|
const params = this.getUpdateStackParams({ templateUrl })
|
|
|
|
try {
|
|
monitorCfData = await this.provider.request(
|
|
'CloudFormation',
|
|
'updateStack',
|
|
params,
|
|
)
|
|
} catch (e) {
|
|
if (e.message.includes(NO_UPDATE_MESSAGE)) {
|
|
return false
|
|
}
|
|
throw e
|
|
}
|
|
} else {
|
|
// Change-set based deployment
|
|
const changeSetName = this.provider.naming.getStackChangeSetName()
|
|
|
|
const createChangeSetParams = this.getCreateChangeSetParams({
|
|
changeSetType: 'UPDATE',
|
|
templateUrl,
|
|
})
|
|
|
|
const executeChangeSetParams = this.getExecuteChangeSetParams()
|
|
|
|
// Ensure that previous change set has been removed
|
|
await this.provider.request('CloudFormation', 'deleteChangeSet', {
|
|
StackName: stackName,
|
|
ChangeSetName: changeSetName,
|
|
})
|
|
|
|
// Create new change set
|
|
log.info('Creating new change set')
|
|
await this.provider.request(
|
|
'CloudFormation',
|
|
'createChangeSet',
|
|
createChangeSetParams,
|
|
)
|
|
|
|
// Wait for changeset to be created
|
|
log.info('Waiting for new change set to be created')
|
|
const changeSetDescription = await this.waitForChangeSetCreation(
|
|
changeSetName,
|
|
stackName,
|
|
)
|
|
|
|
// Check if stack has changes
|
|
if (isChangeSetWithoutChanges(changeSetDescription)) {
|
|
// Cleanup changeset when it does not include any changes
|
|
log.info('Created change set does not include any changes, removing it')
|
|
await this.provider.request('CloudFormation', 'deleteChangeSet', {
|
|
StackName: stackName,
|
|
ChangeSetName: changeSetName,
|
|
})
|
|
this.serverless.service.provider.deploymentWithEmptyChangeSet = true
|
|
return false
|
|
}
|
|
|
|
log.info('Executing created change set')
|
|
await this.provider.request(
|
|
'CloudFormation',
|
|
'executeChangeSet',
|
|
executeChangeSetParams,
|
|
)
|
|
monitorCfData = changeSetDescription
|
|
}
|
|
|
|
await this.monitorStack('update', monitorCfData)
|
|
|
|
// Policy must have at least one statement, otherwise no updates would be possible at all
|
|
// Stack policy must be set after change set has been executed,
|
|
// as it might reference resources newly added in that change set.
|
|
// Applied only for ChangeSet deployments which is a default method
|
|
if (
|
|
this.serverless.service.provider.deploymentMethod === 'changesets' &&
|
|
this.serverless.service.provider.stackPolicy &&
|
|
Object.keys(this.serverless.service.provider.stackPolicy).length
|
|
) {
|
|
log.info('Setting stack policy')
|
|
const stackPolicyBody = JSON.stringify({
|
|
Statement: this.serverless.service.provider.stackPolicy,
|
|
})
|
|
await this.provider.request('CloudFormation', 'setStackPolicy', {
|
|
StackName: stackName,
|
|
StackPolicyBody: stackPolicyBody,
|
|
})
|
|
}
|
|
|
|
return true
|
|
},
|
|
|
|
async updateStack() {
|
|
return Promise.resolve(this).then(() => {
|
|
if (this.createLater) {
|
|
return Promise.resolve(this).then(() => this.createFallback())
|
|
}
|
|
return Promise.resolve(this).then(() => this.update())
|
|
})
|
|
},
|
|
}
|