@@ -26,15 +26,123 @@ const IGNORED_TASK_DEFINITION_ATTRIBUTES = [
26
26
'registeredBy'
27
27
];
28
28
29
+ async function runInitTask(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, initTaskCommand) {
30
+ core.info(`Starting init task "${initTaskCommand}"`)
31
+
32
+ const agent = 'amazon-ecs-run-task-for-github-actions'
33
+ const startedBy = core.getInput('started-by', { required: false }) || agent;
34
+ const networkConfiguration = JSON.parse(core.getInput('init-task-network-configuration', { required : false }));
35
+ const containerName = core.getInput('init-task-name', { required : false })
36
+
37
+ const runTaskResponse = await ecs.runTask({
38
+ startedBy: startedBy,
39
+ cluster: clusterName,
40
+ taskDefinition: taskDefArn,
41
+ enableExecuteCommand: true,
42
+ overrides: {
43
+ containerOverrides: [
44
+ {
45
+ name: containerName,
46
+ command: initTaskCommand.split(' ')
47
+ }
48
+ ]
49
+ },
50
+ launchType: 'FARGATE',
51
+ networkConfiguration: networkConfiguration
52
+ }).promise();
53
+
54
+ core.debug(`Run task response ${JSON.stringify(runTaskResponse)}`)
55
+
56
+ const taskArns = runTaskResponse.tasks.map(task => task.taskArn);
57
+ core.setOutput('init-task-arn', taskArns);
58
+
59
+ taskArns.map(taskArn =>{
60
+ let taskId = taskArn.split('/').pop();
61
+
62
+ core.info(`Init task started. Watch the task logs in https://${aws.config.region}.console.aws.amazon.com/cloudwatch/home?region=${aws.config.region}#logsV2:log-groups/log-group/ecs$252F${service}/log-events/ecs$252Fapp$252F${taskId}`)
63
+ });
64
+
65
+ if (runTaskResponse.failures && runTaskResponse.failures.length > 0) {
66
+ const failure = runTaskResponse.failures[0];
67
+ throw new Error(`${failure.arn} is ${failure.reason}`);
68
+ }
69
+
70
+ // Wait for task to end
71
+ if (waitForService && waitForService.toLowerCase() === 'true') {
72
+ core.debug(`Waiting for the service to become stable. Will wait for ${waitForMinutes} minutes`);
73
+ await waitForTasksStopped(ecs, clusterName, taskArns, waitForMinutes)
74
+ await tasksExitCode(ecs, clusterName, taskArns)
75
+ } else {
76
+ core.debug('Not waiting for the service to become stable');
77
+ }
78
+ }
79
+
80
+ async function waitForTasksStopped(ecs, clusterName, taskArns, waitForMinutes) {
81
+ if (waitForMinutes > MAX_WAIT_MINUTES) {
82
+ waitForMinutes = MAX_WAIT_MINUTES;
83
+ }
84
+
85
+ const maxAttempts = (waitForMinutes * 60) / WAIT_DEFAULT_DELAY_SEC;
86
+
87
+ core.info('Waiting for tasks to stop');
88
+
89
+ const waitTaskResponse = await ecs.waitFor('tasksStopped', {
90
+ cluster: clusterName,
91
+ tasks: taskArns,
92
+ $waiter: {
93
+ delay: WAIT_DEFAULT_DELAY_SEC,
94
+ maxAttempts: maxAttempts
95
+ }
96
+ }).promise();
97
+
98
+ core.debug(`Run task response ${JSON.stringify(waitTaskResponse)}`)
99
+
100
+ core.info(`All tasks have stopped. Watch progress in the Amazon ECS console: https://console.aws.amazon.com/ecs/home?region=${aws.config.region}#/clusters/${clusterName}/tasks`);
101
+ }
102
+
103
+ async function tasksExitCode(ecs, clusterName, taskArns) {
104
+ const describeResponse = await ecs.describeTasks({
105
+ cluster: clusterName,
106
+ tasks: taskArns
107
+ }).promise();
108
+
109
+ const containers = [].concat(...describeResponse.tasks.map(task => task.containers))
110
+ const exitCodes = containers.map(container => container.exitCode)
111
+ const reasons = containers.map(container => container.reason)
112
+
113
+ const failuresIdx = [];
114
+
115
+ exitCodes.filter((exitCode, index) => {
116
+ if (exitCode !== 0) {
117
+ failuresIdx.push(index)
118
+ }
119
+ })
120
+
121
+ const failures = reasons.filter((_, index) => failuresIdx.indexOf(index) !== -1)
122
+
123
+ if (failures.length > 0) {
124
+ console.log(`failed to with exit code${failures}`)
125
+ core.setFailed(failures.join("\n"));
126
+ throw new Error(`Run task failed: ${JSON.stringify(failures)}`);
127
+ } else {
128
+ core.info(`All tasks have exited successfully.`);
129
+ }
130
+ }
131
+
29
132
// Deploy to a service that uses the 'ECS' deployment controller
30
- async function updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment) {
133
+ async function updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount ) {
31
134
core.debug('Updating the service');
32
- await ecs.updateService( {
135
+ let params = {
33
136
cluster: clusterName,
34
137
service: service,
35
138
taskDefinition: taskDefArn,
36
139
forceNewDeployment: forceNewDeployment
37
- }).promise();
140
+ };
141
+ // Add the desiredCount property only if it is defined and a number.
142
+ if (!isNaN(desiredCount) && desiredCount !== undefined) {
143
+ params.desiredCount = desiredCount;
144
+ }
145
+ await ecs.updateService(params).promise();
38
146
39
147
const consoleHostname = aws.config.region.startsWith('cn') ? 'console.amazonaws.cn' : 'console.aws.amazon.com';
40
148
@@ -268,13 +376,17 @@ async function run() {
268
376
const cluster = core.getInput('cluster', { required: false });
269
377
const waitForService = core.getInput('wait-for-service-stability', { required: false });
270
378
let waitForMinutes = parseInt(core.getInput('wait-for-minutes', { required: false })) || 30;
379
+ const initTaskCommand = core.getInput('init-task-command');
271
380
if (waitForMinutes > MAX_WAIT_MINUTES) {
272
381
waitForMinutes = MAX_WAIT_MINUTES;
273
382
}
274
383
275
384
const forceNewDeployInput = core.getInput('force-new-deployment', { required: false }) || 'false';
276
385
const forceNewDeployment = forceNewDeployInput.toLowerCase() === 'true';
277
386
387
+ const desiredCount = parseInt((core.getInput('desired-count', {required: false})));
388
+
389
+
278
390
// Register the task definition
279
391
core.debug('Registering the task definition');
280
392
const taskDefPath = path.isAbsolute(taskDefinitionFile) ?
@@ -314,9 +426,15 @@ async function run() {
314
426
throw new Error(`Service is ${serviceResponse.status}`);
315
427
}
316
428
429
+ if (initTaskCommand) {
430
+ await runInitTask(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, initTaskCommand);
431
+ } else {
432
+ core.debug('InitTaskCommand was not specified, no init run task.');
433
+ }
434
+
317
435
if (!serviceResponse.deploymentController || !serviceResponse.deploymentController.type || serviceResponse.deploymentController.type === 'ECS') {
318
436
// Service uses the 'ECS' deployment controller, so we can call UpdateService
319
- await updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment);
437
+ await updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount );
320
438
} else if (serviceResponse.deploymentController.type === 'CODE_DEPLOY') {
321
439
// Service uses CodeDeploy, so we should start a CodeDeploy deployment
322
440
await createCodeDeployDeployment(codedeploy, clusterName, service, taskDefArn, waitForService, waitForMinutes);
@@ -328,6 +446,7 @@ async function run() {
328
446
}
329
447
}
330
448
catch (error) {
449
+ console.log(error);
331
450
core.setFailed(error.message);
332
451
core.debug(error.stack);
333
452
}
0 commit comments