Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions lib/createChangeSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ const _ = require('lodash')
const createChangeSet = (plugin, stackName, changeSetName, changeSetType) => {
const compiledTemplateFileName = 'compiled-cloudformation-template.json'
const templateUrl = `https://s3.amazonaws.com/${plugin.bucketName}/${plugin.serverless.service.package.artifactDirectoryName}/${compiledTemplateFileName}`
const stage = plugin.serverless.service.provider.stage
const region = plugin.serverless.service.provider.region

let stackTags = {
STAGE: plugin.options.stage
STAGE: stage
}
// Merge additional stack tags
if (typeof plugin.serverless.service.provider.stackTags === 'object') {
Expand Down Expand Up @@ -39,8 +41,8 @@ const createChangeSet = (plugin, stackName, changeSetName, changeSetType) => {
'CloudFormation',
'createChangeSet',
params,
plugin.options.stage,
plugin.options.region
stage,
region
)
}

Expand Down
346 changes: 237 additions & 109 deletions lib/createChangeSet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,127 +11,255 @@ describe('updateStack', () => {
let serverless
let serverlessChangeSets

beforeEach(() => {
serverless = new Serverless()
serverless.config.servicePath = 'foo'
serverless.setProvider('aws', new AwsProvider(serverless))
const options = {
stage: 'dev',
region: 'us-east-1',
changeset: 'test'
}
serverlessChangeSets = new ServerlessCloudFormationChangeSets(serverless, options)
serverless.service.service = `service-${(new Date()).getTime().toString()}`
serverlessChangeSets.bucketName = 'deployment-bucket'
serverlessChangeSets.serverless.service.package.artifactDirectoryName = 'somedir'
serverlessChangeSets.serverless.cli = new serverless.classes.CLI()
})

describe('#createChangeSet()', () => {
let createChangeSetStub

describe('with options', () => {
beforeEach(() => {
createChangeSetStub = sinon
.stub(serverlessChangeSets.provider, 'request').resolves()
serverless = new Serverless()
serverless.config.servicePath = 'foo'
serverless.setProvider('aws', new AwsProvider(serverless))
const options = {
stage: 'dev',
region: 'us-east-1',
changeset: 'test'
}
serverlessChangeSets = new ServerlessCloudFormationChangeSets(serverless, options)
serverless.service.service = `service-${(new Date()).getTime().toString()}`
serverlessChangeSets.bucketName = 'deployment-bucket'
serverlessChangeSets.serverless.service.package.artifactDirectoryName = 'somedir'
serverlessChangeSets.serverless.cli = new serverless.classes.CLI()
})

afterEach(() => {
createChangeSetStub.restore()
})
describe('#createChangeSet()', () => {
let createChangeSetStub

it('should create the CF ChangeSet', () => createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledOnce(createChangeSetStub)
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: serverlessChangeSets.provider.naming.getStackName(),
ChangeSetName: 'test',
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'UPDATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
beforeEach(() => {
createChangeSetStub = sinon
.stub(serverlessChangeSets.provider, 'request').resolves()
})

afterEach(() => {
createChangeSetStub.restore()
})

it('should create the CF ChangeSet', () => createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledOnce(createChangeSetStub)
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: serverlessChangeSets.provider.naming.getStackName(),
ChangeSetName: 'test',
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'UPDATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
})
)

it('should generate ChangSet name if it\'s not provided', () => {
const fakeTimer = sinon.useFakeTimers(1510926650275)
const stackName = serverlessChangeSets.provider.naming.getStackName()
serverlessChangeSets.options.changeSetName = undefined

return createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: stackName,
ChangeSetName: `${stackName}-1510926650275`,
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'UPDATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
fakeTimer.restore()
})
})
)

it('should generate ChangSet name if it\'s not provided', () => {
const fakeTimer = sinon.useFakeTimers(1510926650275)
const stackName = serverlessChangeSets.provider.naming.getStackName()
serverlessChangeSets.options.changeSetName = undefined

return createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: stackName,
ChangeSetName: `${stackName}-1510926650275`,
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'UPDATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
fakeTimer.restore()

it('should include custom stack tags and CF service role', () => {
serverlessChangeSets.serverless.service.provider.stackTags = { STAGE: 'overridden', tag1: 'value1' }
serverlessChangeSets.serverless.service.provider.cfnRole = 'arn:aws:iam::123456789012:role/myrole'

return createChangeSet.bind(serverlessChangeSets)().then(() => {
expect(createChangeSetStub.args[0][2].Tags)
.to.deep.equal([
{ Key: 'STAGE', Value: 'overridden' },
{ Key: 'tag1', Value: 'value1' }
])
expect(createChangeSetStub.args[0][2].RoleARN).to.equal('arn:aws:iam::123456789012:role/myrole')
})
})
})

it('should create the CF empty stack if it does not exist', () => {
const stackName = serverlessChangeSets.provider.naming.getStackName()
createChangeSetStub.onCall(0).rejects(new Error(`Stack [${stackName}] does not exist`))

it('should include custom stack tags and CF service role', () => {
serverlessChangeSets.serverless.service.provider.stackTags = { STAGE: 'overridden', tag1: 'value1' }
serverlessChangeSets.serverless.service.provider.cfnRole = 'arn:aws:iam::123456789012:role/myrole'

return createChangeSet.bind(serverlessChangeSets)().then(() => {
expect(createChangeSetStub.args[0][2].Tags)
.to.deep.equal([
{ Key: 'STAGE', Value: 'overridden' },
{ Key: 'tag1', Value: 'value1' }
])
expect(createChangeSetStub.args[0][2].RoleARN).to.equal('arn:aws:iam::123456789012:role/myrole')
return createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledTwice(createChangeSetStub)
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: stackName,
ChangeSetName: 'test',
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'CREATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
})
})
})
})

describe('with ', () => {
beforeEach(() => {
serverless = new Serverless()
serverless.config.servicePath = 'foo'
serverless.setProvider('aws', new AwsProvider(serverless))
const options = {
changeset: 'test'
}
serverlessChangeSets = new ServerlessCloudFormationChangeSets(serverless, options)
serverless.service.service = `service-${(new Date()).getTime().toString()}`
serverlessChangeSets.bucketName = 'deployment-bucket'
serverlessChangeSets.serverless.service.package.artifactDirectoryName = 'somedir'
serverlessChangeSets.serverless.cli = new serverless.classes.CLI()
serverlessChangeSets.serverless.service.provider.stage = 'dev'
serverlessChangeSets.serverless.service.provider.region = 'us-east-1'
})

describe('#createChangeSet()', () => {
let createChangeSetStub

beforeEach(() => {
createChangeSetStub = sinon
.stub(serverlessChangeSets.provider, 'request').resolves()
})

afterEach(() => {
createChangeSetStub.restore()
})

it('should create the CF ChangeSet', () => createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledOnce(createChangeSetStub)
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: serverlessChangeSets.provider.naming.getStackName(),
ChangeSetName: 'test',
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'UPDATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
})
)

it('should create the CF empty stack if it does not exist', () => {
const stackName = serverlessChangeSets.provider.naming.getStackName()
createChangeSetStub.onCall(0).rejects(new Error(`Stack [${stackName}] does not exist`))

return createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledTwice(createChangeSetStub)
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: stackName,
ChangeSetName: 'test',
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'CREATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
it('should generate ChangSet name if it\'s not provided', () => {
const fakeTimer = sinon.useFakeTimers(1510926650275)
const stackName = serverlessChangeSets.provider.naming.getStackName()
serverlessChangeSets.options.changeSetName = undefined

return createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: stackName,
ChangeSetName: `${stackName}-1510926650275`,
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'UPDATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
fakeTimer.restore()
})
})

it('should include custom stack tags and CF service role', () => {
serverlessChangeSets.serverless.service.provider.stackTags = { STAGE: 'overridden', tag1: 'value1' }
serverlessChangeSets.serverless.service.provider.cfnRole = 'arn:aws:iam::123456789012:role/myrole'

return createChangeSet.bind(serverlessChangeSets)().then(() => {
expect(createChangeSetStub.args[0][2].Tags)
.to.deep.equal([
{ Key: 'STAGE', Value: 'overridden' },
{ Key: 'tag1', Value: 'value1' }
])
expect(createChangeSetStub.args[0][2].RoleARN).to.equal('arn:aws:iam::123456789012:role/myrole')
})
})

it('should create the CF empty stack if it does not exist', () => {
const stackName = serverlessChangeSets.provider.naming.getStackName()
createChangeSetStub.onCall(0).rejects(new Error(`Stack [${stackName}] does not exist`))

return createChangeSet.bind(serverlessChangeSets)()
.then(() => {
sinon.assert.calledTwice(createChangeSetStub)
sinon.assert.calledWithExactly(createChangeSetStub,
'CloudFormation',
'createChangeSet',
{
StackName: stackName,
ChangeSetName: 'test',
Capabilities: [
'CAPABILITY_IAM',
'CAPABILITY_NAMED_IAM'
],
ChangeSetType: 'CREATE',
Parameters: [],
TemplateURL: 'https://s3.amazonaws.com/deployment-bucket/somedir/compiled-cloudformation-template.json',
Tags: [{ Key: 'STAGE', Value: 'dev' }]
},
'dev',
'us-east-1'
)
})
})
})
})
})