Skip to content

Commit

Permalink
Merge pull request #1 from filipemacedo/fix/use-the-existing-task-def…
Browse files Browse the repository at this point in the history
…inition

Fix/use the existing task definition
  • Loading branch information
filipemacedo authored Sep 12, 2023
2 parents 37ec59d + 48637fd commit d359f41
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 17 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ If you do not wish to store your task definition as a file in your git repositor
aws ecs describe-task-definition --task-definition my-task-definition-family --query taskDefinition > task-definition.json
```

If you don't want to create new revisions of your task definition, you can define the task definition family and revision as inputs for the action. By default, the action will use the latest revision of the task definition family if the revision is not specified.

```yaml
- name: Deploy to Amazon ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: my-task-definition-family
service: my-service
cluster: my-cluster
wait-for-service-stability: true
```

### Task definition container image values

It is highly recommended that each time your GitHub Actions workflow runs and builds a new container image for deployment, a new container image ID is generated. For example, use the commit ID as the new image's tag, instead of updating the 'latest' tag with the new image. Using a unique container image ID for each deployment allows rolling back to a previous container image.
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ branding:
color: 'orange'
inputs:
task-definition:
description: 'The path to the ECS task definition file to register'
description: 'The path to the ECS task definition file to register or the name of the task definition family to use. If the task definition family is given, the action will use the latest ACTIVE revision of the task definition.'
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.'
Expand Down
57 changes: 42 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,23 +269,50 @@ async function run() {
const forceNewDeployInput = core.getInput('force-new-deployment', { required: false }) || 'false';
const forceNewDeployment = forceNewDeployInput.toLowerCase() === 'true';

// Register the task definition
core.debug('Registering the task definition');
const taskDefPath = path.isAbsolute(taskDefinitionFile) ?
taskDefinitionFile :
path.join(process.env.GITHUB_WORKSPACE, taskDefinitionFile);
const fileContents = fs.readFileSync(taskDefPath, 'utf8');
const taskDefContents = maintainValidObjects(removeIgnoredAttributes(cleanNullKeys(yaml.parse(fileContents))));
let registerResponse;
try {
registerResponse = await ecs.registerTaskDefinition(taskDefContents).promise();
} catch (error) {
core.setFailed("Failed to register task definition in ECS: " + error.message);
core.debug("Task definition contents:");
core.debug(JSON.stringify(taskDefContents, undefined, 4));
throw(error);
taskDefinitionFile :
path.join(process.env.GITHUB_WORKSPACE, taskDefinitionFile);

const isExistingTaskDef = fs.existsSync(taskDefPath);

let taskDefArn;

if (!isExistingTaskDef) {
core.debug(`Searching for task definition ${taskDefinitionFile} in ECS`);
try {
const describeResponse = await ecs.describeTaskDefinition({
taskDefinition: taskDefinitionFile
}).promise();
const taskDef = describeResponse.taskDefinition;

if (!taskDef) {
throw new Error(`Task definition ${taskDefinitionFile} not found in ECS`);
}

core.debug(`Found task definition ${taskDef.taskDefinitionArn}`);
taskDefArn = taskDef.taskDefinitionArn;
} catch (error) {
core.setFailed("Failed to describe task definition in ECS: " + error.message);
throw(error);
}
}
const taskDefArn = registerResponse.taskDefinition.taskDefinitionArn;

if (isExistingTaskDef) {
core.debug('Registering the task definition');
const fileContents = fs.readFileSync(taskDefPath, 'utf8');
const taskDefContents = maintainValidObjects(removeIgnoredAttributes(cleanNullKeys(yaml.parse(fileContents))));
let registerResponse;
try {
registerResponse = await ecs.registerTaskDefinition(taskDefContents).promise();
} catch (error) {
core.setFailed("Failed to register task definition in ECS: " + error.message);
core.debug("Task definition contents:");
core.debug(JSON.stringify(taskDefContents, undefined, 4));
throw(error);
}
taskDefArn = registerResponse.taskDefinition.taskDefinitionArn;
}

core.setOutput('task-definition-arn', taskDefArn);

// Update the service with the new task definition
Expand Down
72 changes: 71 additions & 1 deletion index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ jest.mock('@actions/core');
jest.mock('fs', () => ({
promises: { access: jest.fn() },
readFileSync: jest.fn(),
existsSync: jest.fn().mockReturnValue(true)
}));

const mockEcsRegisterTaskDef = jest.fn();
const mockEcsUpdateService = jest.fn();
const mockEcsDescribeServices = jest.fn();
const mockEcsWaiter = jest.fn();
const mockEcsDescribeTaskDef = jest.fn();
const mockCodeDeployCreateDeployment = jest.fn();
const mockCodeDeployGetDeploymentGroup = jest.fn();
const mockCodeDeployWaiter = jest.fn();
Expand All @@ -27,7 +29,8 @@ jest.mock('aws-sdk', () => {
registerTaskDefinition: mockEcsRegisterTaskDef,
updateService: mockEcsUpdateService,
describeServices: mockEcsDescribeServices,
waitFor: mockEcsWaiter
waitFor: mockEcsWaiter,
describeTaskDefinition: mockEcsDescribeTaskDef
})),
CodeDeploy: jest.fn(() => ({
createDeployment: mockCodeDeployCreateDeployment,
Expand Down Expand Up @@ -86,6 +89,14 @@ describe('Deploy to ECS', () => {
};
});

mockEcsDescribeTaskDef.mockImplementation(() => {
return {
promise() {
return Promise.resolve({ taskDefinition: { taskDefinitionArn: 'task:def:arn' } });
}
};
})

mockEcsUpdateService.mockImplementation(() => {
return {
promise() {
Expand Down Expand Up @@ -151,6 +162,65 @@ describe('Deploy to ECS', () => {
});
});

test("try to get existing task definition ARN when user provides family instead of file", async () => {
fs.existsSync.mockReturnValueOnce(false);

core.getInput = jest
.fn()
.mockReturnValueOnce('task-def-family')
.mockReturnValueOnce('service-456')
.mockReturnValueOnce('cluster-789');

await run();

expect(core.setFailed).toHaveBeenCalledTimes(0);
expect(mockEcsDescribeTaskDef).toHaveBeenNthCalledWith(1, {
taskDefinition: "task-def-family"
});
expect(mockEcsRegisterTaskDef).not.toHaveBeenCalled();
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn');
expect(mockEcsDescribeServices).toHaveBeenNthCalledWith(1, {
cluster: 'cluster-789',
services: ['service-456']
});
expect(mockEcsUpdateService).toHaveBeenNthCalledWith(1, {
cluster: 'cluster-789',
service: 'service-456',
taskDefinition: 'task:def:arn',
forceNewDeployment: false
});
expect(mockEcsWaiter).toHaveBeenCalledTimes(0);
expect(core.info).toBeCalledWith("Deployment started. Watch this deployment's progress in the Amazon ECS console: https://console.aws.amazon.com/ecs/home?region=fake-region#/clusters/cluster-789/services/service-456/events");
});

test("should be able to throw an error when the task definition family is not found", async () => {
fs.existsSync.mockReturnValueOnce(false);

core.getInput = jest
.fn()
.mockReturnValueOnce('task-def-family')
.mockReturnValueOnce('service-456')
.mockReturnValueOnce('cluster-789');

mockEcsDescribeTaskDef.mockImplementation(() => {
return {
promise() {
return Promise.resolve({ taskDefinition: null });
}
};
});

await run()

expect(mockEcsDescribeTaskDef).toHaveBeenNthCalledWith(1, {
taskDefinition: "task-def-family"
});
expect(core.setFailed).toHaveBeenCalledTimes(2);
expect(mockEcsRegisterTaskDef).not.toHaveBeenCalled();
expect(core.setOutput).not.toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn');
expect(core.info).not.toBeCalledWith("Deployment started. Watch this deployment's progress in the Amazon ECS console: https://console.aws.amazon.com/ecs/home?region=fake-region#/clusters/cluster-789/services/service-456/events");
});

test('registers the task definition contents and updates the service', async () => {
await run();
expect(core.setFailed).toHaveBeenCalledTimes(0);
Expand Down

0 comments on commit d359f41

Please sign in to comment.