forked from aws-actions/amazon-ecs-deploy-task-definition
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial action code Co-Authored-By: Steve Winton <[email protected]>
- Loading branch information
1 parent
bffbc85
commit ffa97ba
Showing
8 changed files
with
5,851 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"env": { | ||
"commonjs": true, | ||
"es6": true, | ||
"node": true, | ||
"jest": true | ||
}, | ||
"extends": "eslint:recommended", | ||
"globals": { | ||
"Atomics": "readonly", | ||
"SharedArrayBuffer": "readonly" | ||
}, | ||
"parserOptions": { | ||
"ecmaVersion": 2018 | ||
}, | ||
"rules": { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
node_modules/ | ||
|
||
# Editors | ||
.vscode | ||
|
||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# Runtime data | ||
pids | ||
*.pid | ||
*.seed | ||
*.pid.lock | ||
|
||
# Directory for instrumented libs generated by jscoverage/JSCover | ||
lib-cov | ||
|
||
# Coverage directory used by tools like istanbul | ||
coverage | ||
|
||
# nyc test coverage | ||
.nyc_output | ||
|
||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | ||
.grunt | ||
|
||
# Bower dependency directory (https://bower.io/) | ||
bower_components | ||
|
||
# node-waf configuration | ||
.lock-wscript | ||
|
||
# Compiled binary addons (https://nodejs.org/api/addons.html) | ||
build/Release | ||
|
||
# Other Dependency directories | ||
jspm_packages/ | ||
|
||
# TypeScript v1 declaration files | ||
typings/ | ||
|
||
# Optional npm cache directory | ||
.npm | ||
|
||
# Optional eslint cache | ||
.eslintcache | ||
|
||
# Optional REPL history | ||
.node_repl_history | ||
|
||
# Output of 'npm pack' | ||
*.tgz | ||
|
||
# Yarn Integrity file | ||
.yarn-integrity | ||
|
||
# dotenv environment variables file | ||
.env | ||
|
||
# next.js build output | ||
.next |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
name: 'Amazon ECS "Deploy Task Definition" Action for GitHub Actions' | ||
description: 'Registers an Amazon ECS task definition, and deploys it to an ECS service' | ||
inputs: | ||
task-definition: | ||
description: 'The path to the ECS task definition file to register' | ||
required: true | ||
service: | ||
description: 'The name of the ECS service to deploy to. The action will only register the task definition if no service is given.' | ||
required: false | ||
cluster: | ||
description: "The name of the ECS service's cluster. Will default to the 'default' cluster" | ||
required: false | ||
wait-for-service-stability: | ||
description: 'Whether to wait for the ECS service to reach stable state after deploying the new task definition. Valid value is "true". Will default to not waiting.' | ||
required: false | ||
outputs: | ||
task-definition-arn: | ||
description: 'The ARN of the registered ECS task definition' | ||
runs: | ||
using: 'node12' | ||
main: 'dist/index.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
const path = require('path'); | ||
const core = require('@actions/core'); | ||
const aws = require('aws-sdk'); | ||
|
||
async function run() { | ||
try { | ||
const ecs = new aws.ECS(); | ||
|
||
// Get inputs | ||
const taskDefinitionFile = core.getInput('task-definition', { required: true }); | ||
const service = core.getInput('service', { required: false }); | ||
const cluster = core.getInput('cluster', { required: false }); | ||
const waitForService = core.getInput('wait-for-service-stability', { required: false }); | ||
|
||
// Register the task definition | ||
core.debug('Registering the task definition'); | ||
const taskDefPath = path.isAbsolute(taskDefinitionFile) ? | ||
taskDefinitionFile : | ||
path.join(process.env.GITHUB_WORKSPACE, taskDefinitionFile); | ||
const taskDefContents = require(taskDefPath); | ||
const registerResponse = await ecs.registerTaskDefinition(taskDefContents).promise(); | ||
const taskDefArn = registerResponse.taskDefinition.taskDefinitionArn; | ||
core.setOutput('task-definition-arn', taskDefArn); | ||
|
||
// Update the service with the new task definition | ||
if (service) { | ||
core.debug('Updating the service'); | ||
const clusterName = cluster ? cluster : 'default'; | ||
await ecs.updateService({ | ||
cluster: clusterName, | ||
service: service, | ||
taskDefinition: taskDefArn | ||
}).promise(); | ||
|
||
// Wait for service stability | ||
if (waitForService && waitForService.toLowerCase() === 'true') { | ||
core.debug('Waiting for the service to become stable'); | ||
await ecs.waitFor('servicesStable', { | ||
services: [service], | ||
cluster: clusterName | ||
}).promise(); | ||
} else { | ||
core.debug('Not waiting for the service to become stable'); | ||
} | ||
} else { | ||
core.debug('Service was not specified, no service updated'); | ||
} | ||
} | ||
catch (error) { | ||
core.setFailed(error.message); | ||
} | ||
} | ||
|
||
module.exports = run; | ||
|
||
/* istanbul ignore next */ | ||
if (require.main === module) { | ||
run(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
const run = require('.'); | ||
const core = require('@actions/core'); | ||
|
||
jest.mock('@actions/core'); | ||
|
||
const mockEcsRegisterTaskDef = jest.fn(); | ||
const mockEcsUpdateService = jest.fn(); | ||
const mockEcsWaiter = jest.fn(); | ||
jest.mock('aws-sdk', () => { | ||
return { | ||
ECS: jest.fn(() => ({ | ||
registerTaskDefinition: mockEcsRegisterTaskDef, | ||
updateService: mockEcsUpdateService, | ||
waitFor: mockEcsWaiter | ||
})) | ||
}; | ||
}); | ||
|
||
describe('Deploy to ECS', () => { | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
|
||
core.getInput = jest | ||
.fn() | ||
.mockReturnValueOnce('task-definition.json') // task-definition | ||
.mockReturnValueOnce('service-456') // service | ||
.mockReturnValueOnce('cluster-789'); // cluster | ||
|
||
process.env = Object.assign(process.env, { GITHUB_WORKSPACE: __dirname }); | ||
|
||
jest.mock('./task-definition.json', () => ({ family: 'task-def-family' }), { virtual: true }); | ||
|
||
mockEcsRegisterTaskDef.mockImplementation((params) => { | ||
return { | ||
promise() { | ||
return Promise.resolve({ taskDefinition: { taskDefinitionArn: 'task:def:arn' } }); | ||
} | ||
}; | ||
}); | ||
|
||
mockEcsUpdateService.mockImplementation((params) => { | ||
return { | ||
promise() { | ||
return Promise.resolve({}); | ||
} | ||
}; | ||
}); | ||
|
||
mockEcsWaiter.mockImplementation((params) => { | ||
return { | ||
promise() { | ||
return Promise.resolve({}); | ||
} | ||
}; | ||
}); | ||
}); | ||
|
||
test('registers the task definition contents and updates the service', async () => { | ||
await run(); | ||
expect(mockEcsRegisterTaskDef).toHaveBeenNthCalledWith(1, { family: 'task-def-family'}); | ||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn'); | ||
expect(mockEcsUpdateService).toHaveBeenNthCalledWith(1, { | ||
cluster: 'cluster-789', | ||
service: 'service-456', | ||
taskDefinition: 'task:def:arn' | ||
}); | ||
expect(mockEcsWaiter).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
test('registers the task definition contents at an absolute path', async () => { | ||
core.getInput = jest.fn().mockReturnValueOnce('/hello/task-definition.json'); | ||
jest.mock('/hello/task-definition.json', () => ({ family: 'task-def-family-absolute-path' }), { virtual: true }); | ||
|
||
await run(); | ||
|
||
expect(mockEcsRegisterTaskDef).toHaveBeenNthCalledWith(1, { family: 'task-def-family-absolute-path'}); | ||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn'); | ||
}); | ||
|
||
test('waits for the service to be stable', async () => { | ||
core.getInput = jest | ||
.fn() | ||
.mockReturnValueOnce('task-definition.json') // task-definition | ||
.mockReturnValueOnce('service-456') // service | ||
.mockReturnValueOnce('cluster-789') // cluster | ||
.mockReturnValueOnce('TRUE'); // wait-for-service-stability | ||
|
||
await run(); | ||
|
||
expect(mockEcsRegisterTaskDef).toHaveBeenNthCalledWith(1, { family: 'task-def-family'}); | ||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn'); | ||
expect(mockEcsUpdateService).toHaveBeenNthCalledWith(1, { | ||
cluster: 'cluster-789', | ||
service: 'service-456', | ||
taskDefinition: 'task:def:arn' | ||
}); | ||
expect(mockEcsWaiter).toHaveBeenNthCalledWith(1, 'servicesStable', { | ||
services: ['service-456'], | ||
cluster: 'cluster-789' | ||
}); | ||
}); | ||
|
||
test('defaults to the default cluster', async () => { | ||
core.getInput = jest | ||
.fn() | ||
.mockReturnValueOnce('task-definition.json') // task-definition | ||
.mockReturnValueOnce('service-456'); // service | ||
|
||
await run(); | ||
|
||
expect(mockEcsRegisterTaskDef).toHaveBeenNthCalledWith(1, { family: 'task-def-family'}); | ||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn'); | ||
expect(mockEcsUpdateService).toHaveBeenNthCalledWith(1, { | ||
cluster: 'default', | ||
service: 'service-456', | ||
taskDefinition: 'task:def:arn' | ||
}); | ||
}); | ||
|
||
test('does not update service if none specified', async () => { | ||
core.getInput = jest | ||
.fn() | ||
.mockReturnValueOnce('task-definition.json'); // task-definition | ||
|
||
await run(); | ||
|
||
expect(mockEcsRegisterTaskDef).toHaveBeenNthCalledWith(1, { family: 'task-def-family'}); | ||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn'); | ||
expect(mockEcsUpdateService).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
test('error is caught by core.setFailed', async () => { | ||
mockEcsRegisterTaskDef.mockImplementation(() => { | ||
throw new Error(); | ||
}); | ||
|
||
await run(); | ||
|
||
expect(core.setFailed).toBeCalled(); | ||
}); | ||
}); |
Oops, something went wrong.