Skip to content

Commit 275ab30

Browse files
can now publish a new version of the lambda
1 parent 72a0c68 commit 275ab30

File tree

7 files changed

+200
-5
lines changed

7 files changed

+200
-5
lines changed

__mocks__/aws-sdk.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const promisifyMock = (mockFn) => {
2+
const promise = jest.fn()
3+
mockFn.mockImplementation(() => ({
4+
promise
5+
}))
6+
7+
return promise
8+
}
9+
10+
const mockCreateFunction = jest.fn()
11+
const mockCreateFunctionPromise = promisifyMock(mockCreateFunction)
12+
13+
const mockPublishVersion = jest.fn()
14+
const mockPublishVersionPromise = promisifyMock(mockPublishVersion)
15+
16+
const mockGetFunctionConfiguration = jest.fn()
17+
const mockGetFunctionConfigurationPromise = promisifyMock(mockGetFunctionConfiguration)
18+
19+
const mockUpdateFunctionCode = jest.fn()
20+
const mockUpdateFunctionCodePromise = promisifyMock(mockUpdateFunctionCode)
21+
22+
const mockUpdateFunctionConfiguration = jest.fn()
23+
const mockUpdateFunctionConfigurationPromise = promisifyMock(mockUpdateFunctionConfiguration)
24+
25+
module.exports = {
26+
mockCreateFunction,
27+
mockCreateFunctionPromise,
28+
mockPublishVersion,
29+
mockPublishVersionPromise,
30+
mockGetFunctionConfiguration,
31+
mockGetFunctionConfigurationPromise,
32+
mockUpdateFunctionCode,
33+
mockUpdateFunctionCodePromise,
34+
mockUpdateFunctionConfiguration,
35+
mockUpdateFunctionConfigurationPromise,
36+
37+
Lambda: jest.fn(() => ({
38+
createFunction: mockCreateFunction,
39+
publishVersion: mockPublishVersion,
40+
getFunctionConfiguration: mockGetFunctionConfiguration,
41+
updateFunctionCode: mockUpdateFunctionCode,
42+
updateFunctionConfiguration: mockUpdateFunctionConfiguration
43+
}))
44+
}

__tests__/publishVersion.test.js

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const { createComponent, createTmpDir } = require('../test-utils')
2+
3+
const {
4+
mockCreateFunctionPromise,
5+
mockPublishVersion,
6+
mockPublishVersionPromise,
7+
mockGetFunctionConfigurationPromise,
8+
mockUpdateFunctionCodePromise,
9+
mockUpdateFunctionConfigurationPromise
10+
} = require('aws-sdk')
11+
12+
const mockIamRole = jest.fn()
13+
jest.mock('@serverless/aws-iam-role', () =>
14+
jest.fn(() => {
15+
const iamRole = mockIamRole
16+
iamRole.init = () => {}
17+
iamRole.default = () => {}
18+
iamRole.context = {}
19+
return iamRole
20+
})
21+
)
22+
23+
describe('publishVersion', () => {
24+
let component
25+
26+
beforeEach(async () => {
27+
mockIamRole.mockResolvedValue({
28+
arn: 'arn:aws:iam::123456789012:role/xyz'
29+
})
30+
mockCreateFunctionPromise.mockResolvedValueOnce({
31+
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:my-func',
32+
CodeSha256: 'LQT0VA='
33+
})
34+
35+
component = await createComponent()
36+
})
37+
38+
it('publishes new version of lambda that was created', async () => {
39+
mockGetFunctionConfigurationPromise.mockRejectedValueOnce({ code: 'ResourceNotFoundException' })
40+
mockPublishVersionPromise.mockResolvedValueOnce({
41+
Version: 'v2'
42+
})
43+
const tmpFolder = await createTmpDir()
44+
45+
await component.default({
46+
code: tmpFolder
47+
})
48+
49+
const versionResult = await component.publishVersion()
50+
51+
expect(mockPublishVersion).toBeCalledWith({
52+
FunctionName: expect.any(String),
53+
CodeSha256: 'LQT0VA='
54+
})
55+
56+
expect(versionResult).toEqual({
57+
version: 'v2'
58+
})
59+
})
60+
61+
it('publishes new version of lambda that was updated', async () => {
62+
mockPublishVersionPromise.mockResolvedValue({
63+
Version: 'v2'
64+
})
65+
mockGetFunctionConfigurationPromise.mockRejectedValueOnce({ code: 'ResourceNotFoundException' })
66+
mockGetFunctionConfigurationPromise.mockResolvedValueOnce({
67+
FunctionName: 'my-func'
68+
})
69+
mockUpdateFunctionCodePromise.mockResolvedValueOnce({
70+
FunctionName: 'my-func'
71+
})
72+
mockCreateFunctionPromise.mockResolvedValueOnce({
73+
CodeSha256: 'LQT0VA='
74+
})
75+
mockUpdateFunctionConfigurationPromise.mockResolvedValueOnce({
76+
CodeSha256: 'XYZ0VA='
77+
})
78+
79+
const tmpFolder = await createTmpDir()
80+
81+
await component.default({
82+
code: tmpFolder
83+
})
84+
85+
await component.default({
86+
code: tmpFolder
87+
})
88+
89+
const versionResult = await component.publishVersion()
90+
91+
expect(mockPublishVersion).toBeCalledWith({
92+
FunctionName: expect.any(String),
93+
CodeSha256: 'XYZ0VA=' // compare against the hash received from the function update, *not* create
94+
})
95+
96+
expect(versionResult).toEqual({
97+
version: 'v2'
98+
})
99+
})
100+
})

jest.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
clearMocks: true
3+
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"access": "public"
77
},
88
"scripts": {
9-
"test": "echo \"Error: no test specified\" && exit 1",
9+
"test": "jest",
1010
"lint": "eslint . --fix --cache"
1111
},
1212
"author": "Serverless, Inc.",
@@ -28,6 +28,7 @@
2828
"eslint-config-prettier": "^3.6.0",
2929
"eslint-plugin-import": "^2.18.0",
3030
"eslint-plugin-prettier": "^3.1.0",
31+
"jest": "^24.9.0",
3132
"prettier": "^1.18.2"
3233
}
3334
}

serverless.js

+26-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515

1616
const outputsList = [
1717
'name',
18+
'hash',
1819
'description',
1920
'memory',
2021
'timeout',
@@ -132,9 +133,12 @@ class AwsLambda extends Component {
132133
this.context.status(`Creating`)
133134
this.context.debug(`Creating lambda ${config.name} in the ${config.region} region.`)
134135

135-
config.arn = await createLambda({ lambda, ...config })
136+
const createResult = await createLambda({ lambda, ...config })
137+
config.arn = createResult.arn
138+
config.hash = createResult.hash
136139
} else {
137140
config.arn = prevLambda.arn
141+
138142
if (configChanged(prevLambda, config)) {
139143
if (config.bucket && prevLambda.hash !== config.hash) {
140144
this.context.status(`Uploading code`)
@@ -147,10 +151,12 @@ class AwsLambda extends Component {
147151
this.context.debug(`Uploading ${config.name} lambda code.`)
148152
await updateLambdaCode({ lambda, ...config })
149153
}
154+
150155
this.context.status(`Updating`)
151156
this.context.debug(`Updating ${config.name} lambda config.`)
152157

153-
await updateLambdaConfig({ lambda, ...config })
158+
const updateResult = await updateLambdaConfig({ lambda, ...config })
159+
config.hash = updateResult.hash
154160
}
155161
}
156162

@@ -172,6 +178,24 @@ class AwsLambda extends Component {
172178
return outputs
173179
}
174180

181+
async publishVersion() {
182+
const { name, region, hash } = this.state
183+
184+
const lambda = new AwsSdkLambda({
185+
region,
186+
credentials: this.context.credentials.aws
187+
})
188+
189+
const { Version } = await lambda
190+
.publishVersion({
191+
FunctionName: name,
192+
CodeSha256: hash
193+
})
194+
.promise()
195+
196+
return { version: Version }
197+
}
198+
175199
async remove() {
176200
this.context.status(`Removing`)
177201

test-utils.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const fse = require('fs-extra')
2+
const path = require('path')
3+
const os = require('os')
4+
const LambdaComponent = require('./serverless')
5+
6+
const createTmpDir = () => {
7+
return fse.mkdtemp(path.join(os.tmpdir(), 'test-'))
8+
}
9+
10+
const createComponent = async () => {
11+
// create tmp folder to avoid state collisions between tests
12+
const tmpFolder = await createTmpDir()
13+
14+
const component = new LambdaComponent('TestLambda', {
15+
stateRoot: tmpFolder
16+
})
17+
18+
await component.init()
19+
20+
return component
21+
}
22+
23+
module.exports = { createComponent, createTmpDir }

utils.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const createLambda = async ({
106106

107107
const res = await lambda.createFunction(params).promise()
108108

109-
return res.FunctionArn
109+
return { arn: res.FunctionArn, hash: res.CodeSha256 }
110110
}
111111

112112
const updateLambdaConfig = async ({
@@ -140,7 +140,7 @@ const updateLambdaConfig = async ({
140140

141141
const res = await lambda.updateFunctionConfiguration(functionConfigParams).promise()
142142

143-
return res.FunctionArn
143+
return { arn: res.FunctionArn, hash: res.CodeSha256 }
144144
}
145145

146146
const updateLambdaCode = async ({ lambda, name, zipPath, bucket }) => {

0 commit comments

Comments
 (0)