carlin 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +378 -0
- package/bin/carlin +2 -0
- package/dist/cli.js +250 -0
- package/dist/config.js +10 -0
- package/dist/deploy/addDefaults.cloudFormation.js +138 -0
- package/dist/deploy/baseStack/command.js +9 -0
- package/dist/deploy/baseStack/config.js +30 -0
- package/dist/deploy/baseStack/deployBaseStack.js +61 -0
- package/dist/deploy/baseStack/getBaseStackResource.js +26 -0
- package/dist/deploy/baseStack/getBucket.template.js +44 -0
- package/dist/deploy/baseStack/getLambdaImageBuilder.template.js +188 -0
- package/dist/deploy/baseStack/getLambdaLayerBuilder.template.js +140 -0
- package/dist/deploy/baseStack/getVpc.template.js +169 -0
- package/dist/deploy/cicd/cicd.template.js +872 -0
- package/dist/deploy/cicd/command.js +84 -0
- package/dist/deploy/cicd/config.js +7 -0
- package/dist/deploy/cicd/deployCicd.js +114 -0
- package/dist/deploy/cicd/ecsTaskReportCommand.js +53 -0
- package/dist/deploy/cicd/getCicdStackName.js +11 -0
- package/dist/deploy/cicd/getTriggerPipelineObjectKey.js +12 -0
- package/dist/deploy/cicd/lambdas/cicdApiV1.handler.js +124 -0
- package/dist/deploy/cicd/lambdas/ecsTaskReport.handler.js +79 -0
- package/dist/deploy/cicd/lambdas/executeTasks.js +91 -0
- package/dist/deploy/cicd/lambdas/getProcessEnvVariable.js +10 -0
- package/dist/deploy/cicd/lambdas/githubWebhooksApiV1.handler.js +143 -0
- package/dist/deploy/cicd/lambdas/index.js +11 -0
- package/dist/deploy/cicd/lambdas/pipelines.handler.js +154 -0
- package/dist/deploy/cicd/lambdas/putApprovalResultManualTask.js +52 -0
- package/dist/deploy/cicd/pipelines.js +52 -0
- package/dist/deploy/cloudFormation.core.js +286 -0
- package/dist/deploy/cloudFormation.js +201 -0
- package/dist/deploy/command.js +211 -0
- package/dist/deploy/lambda.js +177 -0
- package/dist/deploy/lambdaLayer/command.js +51 -0
- package/dist/deploy/lambdaLayer/deployLambdaLayer.js +142 -0
- package/dist/deploy/s3.js +167 -0
- package/dist/deploy/stackName.js +78 -0
- package/dist/deploy/staticApp/command.js +101 -0
- package/dist/deploy/staticApp/getOriginShieldRegion.js +30 -0
- package/dist/deploy/staticApp/staticApp.js +206 -0
- package/dist/deploy/staticApp/staticApp.template.js +874 -0
- package/dist/deploy/utils.js +31 -0
- package/dist/index.js +7 -0
- package/dist/utils/addGroupToOptions.js +11 -0
- package/dist/utils/buildLambdaFiles.js +99 -0
- package/dist/utils/cloudFormationTemplate.js +134 -0
- package/dist/utils/codeBuild.js +52 -0
- package/dist/utils/environmentVariables.js +11 -0
- package/dist/utils/exec.js +24 -0
- package/dist/utils/formatCode.js +30 -0
- package/dist/utils/getAwsAccountId.js +10 -0
- package/dist/utils/getCurrentBranch.js +35 -0
- package/dist/utils/getEnvironment.js +6 -0
- package/dist/utils/getIamPath.js +6 -0
- package/dist/utils/getProjectName.js +28 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/packageJson.js +46 -0
- package/dist/utils/readCloudFormationTemplate.js +35 -0
- package/dist/utils/readObjectFile.js +50 -0
- package/package.json +83 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.deployCicdCommand = exports.options = exports.readSSHKey = void 0;
|
|
7
|
+
/* eslint-disable no-param-reassign */
|
|
8
|
+
const change_case_1 = require("change-case");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const npmlog_1 = __importDefault(require("npmlog"));
|
|
11
|
+
const config_1 = require("../../config");
|
|
12
|
+
const utils_1 = require("../../utils");
|
|
13
|
+
const pipelines_1 = require("./pipelines");
|
|
14
|
+
const deployCicd_1 = require("./deployCicd");
|
|
15
|
+
const logPrefix = 'deploy-cicd';
|
|
16
|
+
/**
|
|
17
|
+
* Created to allow mocking.
|
|
18
|
+
*/
|
|
19
|
+
const readSSHKey = (dir) => fs_1.default.readFileSync(dir, 'utf-8');
|
|
20
|
+
exports.readSSHKey = readSSHKey;
|
|
21
|
+
exports.options = {
|
|
22
|
+
cpu: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
},
|
|
25
|
+
memory: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
},
|
|
28
|
+
pipelines: {
|
|
29
|
+
choices: pipelines_1.pipelines,
|
|
30
|
+
coerce: (values) => values.map((value) => change_case_1.camelCase(value)),
|
|
31
|
+
default: [],
|
|
32
|
+
description: 'Pipelines that will be implemented with the CICD stack.',
|
|
33
|
+
type: 'array',
|
|
34
|
+
},
|
|
35
|
+
'update-repository': {
|
|
36
|
+
alias: ['ur'],
|
|
37
|
+
description: 'Determine if the repository image will be updated.',
|
|
38
|
+
default: true,
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
},
|
|
41
|
+
'ssh-key': {
|
|
42
|
+
demandOption: true,
|
|
43
|
+
type: 'string',
|
|
44
|
+
},
|
|
45
|
+
'ssh-url': {
|
|
46
|
+
demandOption: true,
|
|
47
|
+
type: 'string',
|
|
48
|
+
},
|
|
49
|
+
'slack-webhook-url': {
|
|
50
|
+
type: 'string',
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* This option has the format:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* Array<{
|
|
57
|
+
* name: string,
|
|
58
|
+
* value: string,
|
|
59
|
+
* }>
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
'task-environment': {
|
|
63
|
+
alias: ['te'],
|
|
64
|
+
default: [],
|
|
65
|
+
describe: 'A list of environment variables that will be passed to the ECS container task.',
|
|
66
|
+
type: 'array',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
exports.deployCicdCommand = {
|
|
70
|
+
command: 'cicd',
|
|
71
|
+
describe: 'Deploy CICD.',
|
|
72
|
+
builder: (yargs) => yargs.options(utils_1.addGroupToOptions(exports.options, 'Deploy CICD Options')),
|
|
73
|
+
handler: ({ destroy, ...rest }) => {
|
|
74
|
+
if (destroy) {
|
|
75
|
+
npmlog_1.default.info(logPrefix, `${config_1.NAME} doesn't destroy CICD stack.`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
deployCicd_1.deployCicd({
|
|
79
|
+
...rest,
|
|
80
|
+
sshKey: exports.readSSHKey(rest['ssh-key']),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PIPELINE_ECS_TASK_EXECUTION_MANUAL_APPROVAL_ACTION_NAME = exports.PIPELINE_ECS_TASK_EXECUTION_STAGE_NAME = exports.ECS_TASK_DEFAULT_MEMORY = exports.ECS_TASK_DEFAULT_CPU = void 0;
|
|
4
|
+
exports.ECS_TASK_DEFAULT_CPU = '2048';
|
|
5
|
+
exports.ECS_TASK_DEFAULT_MEMORY = '4096';
|
|
6
|
+
exports.PIPELINE_ECS_TASK_EXECUTION_STAGE_NAME = `PipelineRunECSTasksStage`;
|
|
7
|
+
exports.PIPELINE_ECS_TASK_EXECUTION_MANUAL_APPROVAL_ACTION_NAME = `PipelineRunECSTasksApproval`;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.deployCicd = exports.getLambdaInput = void 0;
|
|
26
|
+
const fs = __importStar(require("fs"));
|
|
27
|
+
const npmlog_1 = __importDefault(require("npmlog"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const utils_1 = require("../../utils");
|
|
30
|
+
const cloudFormation_core_1 = require("../cloudFormation.core");
|
|
31
|
+
const utils_2 = require("../utils");
|
|
32
|
+
const lambda_1 = require("../lambda");
|
|
33
|
+
const cicd_template_1 = require("./cicd.template");
|
|
34
|
+
const getCicdStackName_1 = require("./getCicdStackName");
|
|
35
|
+
const logPrefix = 'cicd';
|
|
36
|
+
const getLambdaInput = (extension) => path.resolve(__dirname, `lambdas/index.${extension}`);
|
|
37
|
+
exports.getLambdaInput = getLambdaInput;
|
|
38
|
+
const deployCicdLambdas = async ({ stackName }) => {
|
|
39
|
+
const lambdaInput = (() => {
|
|
40
|
+
/**
|
|
41
|
+
* This case happens when carlin command is executed when the package is
|
|
42
|
+
* built.
|
|
43
|
+
*/
|
|
44
|
+
if (fs.existsSync(exports.getLambdaInput('js'))) {
|
|
45
|
+
return exports.getLambdaInput('js');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The package isn't built.
|
|
49
|
+
*/
|
|
50
|
+
if (fs.existsSync(exports.getLambdaInput('ts'))) {
|
|
51
|
+
return exports.getLambdaInput('ts');
|
|
52
|
+
}
|
|
53
|
+
throw new Error('Cannot read CICD lambdas file.');
|
|
54
|
+
})();
|
|
55
|
+
const s3 = await lambda_1.deployLambdaCode({
|
|
56
|
+
lambdaInput,
|
|
57
|
+
lambdaExternals: [],
|
|
58
|
+
/**
|
|
59
|
+
* Needs stackName to define the S3 key.
|
|
60
|
+
*/
|
|
61
|
+
stackName,
|
|
62
|
+
});
|
|
63
|
+
if (!s3 || !s3.bucket) {
|
|
64
|
+
throw new Error('Cannot retrieve bucket in which Lambda code was deployed.');
|
|
65
|
+
}
|
|
66
|
+
return s3;
|
|
67
|
+
};
|
|
68
|
+
const waitRepositoryImageUpdate = async ({ stackName, }) => {
|
|
69
|
+
npmlog_1.default.info(logPrefix, 'Starting repository image update...');
|
|
70
|
+
const { OutputValue: projectName } = await cloudFormation_core_1.getStackOutput({
|
|
71
|
+
stackName,
|
|
72
|
+
outputKey: cicd_template_1.REPOSITORY_IMAGE_CODE_BUILD_PROJECT_LOGICAL_ID,
|
|
73
|
+
});
|
|
74
|
+
if (!projectName) {
|
|
75
|
+
throw new Error(`Cannot retrieve repository image CodeBuild project name.`);
|
|
76
|
+
}
|
|
77
|
+
const build = await utils_1.startCodeBuildBuild({ projectName });
|
|
78
|
+
if (build.id) {
|
|
79
|
+
await utils_1.waitCodeBuildFinish({ buildId: build.id, name: stackName });
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const deployCicd = async ({ cpu, memory, pipelines, updateRepository, slackWebhookUrl, sshKey, sshUrl, taskEnvironment, }) => {
|
|
83
|
+
try {
|
|
84
|
+
const { stackName } = await utils_2.handleDeployInitialization({
|
|
85
|
+
logPrefix,
|
|
86
|
+
stackName: getCicdStackName_1.getCicdStackName(),
|
|
87
|
+
});
|
|
88
|
+
await cloudFormation_core_1.deploy({
|
|
89
|
+
template: cicd_template_1.getCicdTemplate({
|
|
90
|
+
cpu,
|
|
91
|
+
memory,
|
|
92
|
+
pipelines,
|
|
93
|
+
s3: await deployCicdLambdas({ stackName }),
|
|
94
|
+
slackWebhookUrl,
|
|
95
|
+
taskEnvironment,
|
|
96
|
+
}),
|
|
97
|
+
params: {
|
|
98
|
+
StackName: stackName,
|
|
99
|
+
Parameters: [
|
|
100
|
+
{ ParameterKey: 'SSHUrl', ParameterValue: sshUrl },
|
|
101
|
+
{ ParameterKey: 'SSHKey', ParameterValue: sshKey },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
terminationProtection: true,
|
|
105
|
+
});
|
|
106
|
+
if (updateRepository) {
|
|
107
|
+
await waitRepositoryImageUpdate({ stackName });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
utils_2.handleDeployError({ error, logPrefix });
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
exports.deployCicd = deployCicd;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ecsTaskReportCommand = void 0;
|
|
7
|
+
const aws_sdk_1 = __importDefault(require("aws-sdk"));
|
|
8
|
+
const npmlog_1 = __importDefault(require("npmlog"));
|
|
9
|
+
const logPrefix = 'cicd-ecs-task-report';
|
|
10
|
+
/**
|
|
11
|
+
* This method create the payload to send to Lambda ECS task report handler.
|
|
12
|
+
*
|
|
13
|
+
* @param param.status execution status.
|
|
14
|
+
*/
|
|
15
|
+
const sendEcsTaskReport = async ({ status }) => {
|
|
16
|
+
if (!process.env.ECS_TASK_REPORT_HANDLER_NAME) {
|
|
17
|
+
npmlog_1.default.info(logPrefix, 'ECS_TASK_REPORT_HANDLER_NAME not defined.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const lambda = new aws_sdk_1.default.Lambda();
|
|
21
|
+
const payload = { status };
|
|
22
|
+
if (process.env.ECS_TASK_ARN) {
|
|
23
|
+
payload.ecsTaskArn = process.env.ECS_TASK_ARN;
|
|
24
|
+
}
|
|
25
|
+
if (process.env.PIPELINE_NAME) {
|
|
26
|
+
payload.pipelineName = process.env.PIPELINE_NAME;
|
|
27
|
+
}
|
|
28
|
+
await lambda
|
|
29
|
+
.invokeAsync({
|
|
30
|
+
FunctionName: process.env.ECS_TASK_REPORT_HANDLER_NAME,
|
|
31
|
+
InvokeArgs: JSON.stringify(payload),
|
|
32
|
+
})
|
|
33
|
+
.promise();
|
|
34
|
+
npmlog_1.default.info(logPrefix, 'Report sent.');
|
|
35
|
+
};
|
|
36
|
+
const options = {
|
|
37
|
+
status: {
|
|
38
|
+
choices: ['Approved', 'Rejected'],
|
|
39
|
+
demandOption: true,
|
|
40
|
+
type: 'string',
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Used to send report to ECS Task Report Handler Lambda.
|
|
45
|
+
*/
|
|
46
|
+
exports.ecsTaskReportCommand = {
|
|
47
|
+
command: 'cicd-ecs-task-report',
|
|
48
|
+
describe: false,
|
|
49
|
+
builder: (yargs) => yargs.options(options),
|
|
50
|
+
handler: async (args) => {
|
|
51
|
+
return sendEcsTaskReport(args);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCicdStackName = void 0;
|
|
4
|
+
const change_case_1 = require("change-case");
|
|
5
|
+
const config_1 = require("../../config");
|
|
6
|
+
const getProjectName_1 = require("../../utils/getProjectName");
|
|
7
|
+
const getCicdStackName = () => {
|
|
8
|
+
const project = getProjectName_1.getProjectName();
|
|
9
|
+
return change_case_1.pascalCase([config_1.NAME, 'Cicd', project].join(' '));
|
|
10
|
+
};
|
|
11
|
+
exports.getCicdStackName = getCicdStackName;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTriggerPipelinesObjectKey = exports.TRIGGER_PIPELINES_OBJECT_KEY_PREFIX = void 0;
|
|
4
|
+
exports.TRIGGER_PIPELINES_OBJECT_KEY_PREFIX = 'cicd/pipelines/triggers/';
|
|
5
|
+
/**
|
|
6
|
+
* The file with this key inside the source S3 key of main and tag pipelines
|
|
7
|
+
* will trigger those pipelines.
|
|
8
|
+
*/
|
|
9
|
+
const getTriggerPipelinesObjectKey = (pipeline) => {
|
|
10
|
+
return `${exports.TRIGGER_PIPELINES_OBJECT_KEY_PREFIX}${pipeline}.zip`;
|
|
11
|
+
};
|
|
12
|
+
exports.getTriggerPipelinesObjectKey = getTriggerPipelinesObjectKey;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cicdApiV1Handler = void 0;
|
|
4
|
+
const aws_sdk_1 = require("aws-sdk");
|
|
5
|
+
const executeTasks_1 = require("./executeTasks");
|
|
6
|
+
const getProcessEnvVariable_1 = require("./getProcessEnvVariable");
|
|
7
|
+
const codebuild = new aws_sdk_1.CodeBuild({ apiVersion: '2016-10-06' });
|
|
8
|
+
/**
|
|
9
|
+
* The CI/CD REST API is responsible to update the image of the repository and
|
|
10
|
+
* running tasks inside a container using [ECS Fargate](https://aws.amazon.com/fargate/).
|
|
11
|
+
* The API URL has the format of an [AWS API Gateway](https://aws.amazon.com/api-gateway/) URL:
|
|
12
|
+
*
|
|
13
|
+
* ```sh
|
|
14
|
+
* https://<api-id>.execute-api.<region>.amazonaws.com/v1
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* It can be found on the "Outputs" tab if you access the CI/CD stack details
|
|
18
|
+
* from AWS Console or by the output `ApiV1Endpoint` that is printed after the
|
|
19
|
+
* stack creation.
|
|
20
|
+
*
|
|
21
|
+
* Such API has the `/cicd` endpoint, that is consumed by a POST method. This
|
|
22
|
+
* endpoint has two actions, defined by the `action` property, passed inside
|
|
23
|
+
* the body of the POST method.
|
|
24
|
+
*
|
|
25
|
+
* - **updateRepository**
|
|
26
|
+
*
|
|
27
|
+
* This action update the repository image on AWS ECR. This command is useful
|
|
28
|
+
* to you update your _node_modules_ folder or your [Yarn cache](https://classic.yarnpkg.com/en/docs/cli/cache/).
|
|
29
|
+
*
|
|
30
|
+
* Body interface:
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* {
|
|
34
|
+
* action: "updateRepository";
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* - **executeTask**
|
|
39
|
+
*
|
|
40
|
+
* This action create an execution of your repository image using [ECS Fargate](https://aws.amazon.com/fargate/).
|
|
41
|
+
* The commands that will be executed is passed by the `commands` property,
|
|
42
|
+
* which receives an array of commands. You can also provide the following
|
|
43
|
+
* properties:
|
|
44
|
+
*
|
|
45
|
+
* - `cpu`: CPU value reserved for the task.
|
|
46
|
+
* - `memory`: memory value reserved for the task.
|
|
47
|
+
* - `taskEnvironment`: list of environment variables that can be accessed
|
|
48
|
+
* on task execution.
|
|
49
|
+
*
|
|
50
|
+
* Body interface:
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* {
|
|
54
|
+
* action: "executeTask";
|
|
55
|
+
* commands: string[];
|
|
56
|
+
* cpu?: string;
|
|
57
|
+
* memory?: string;
|
|
58
|
+
* taskEnvironment?: Array<{ name: string; value: string }>;
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* Example:
|
|
63
|
+
*
|
|
64
|
+
* ```json
|
|
65
|
+
* {
|
|
66
|
+
* "action": "executeTask",
|
|
67
|
+
* "commands": [
|
|
68
|
+
* "git status",
|
|
69
|
+
* "git pull origin main",
|
|
70
|
+
* "yarn",
|
|
71
|
+
* "yarn test"
|
|
72
|
+
* ],
|
|
73
|
+
* "cpu": "1024",
|
|
74
|
+
* "memory": "2048",
|
|
75
|
+
* "taskEnvironment": [
|
|
76
|
+
* {
|
|
77
|
+
* "name": "CI",
|
|
78
|
+
* "value": "true"
|
|
79
|
+
* }
|
|
80
|
+
* ]
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
*/
|
|
85
|
+
const cicdApiV1Handler = async (event) => {
|
|
86
|
+
try {
|
|
87
|
+
const body = JSON.parse(event.body || JSON.stringify({}));
|
|
88
|
+
let response;
|
|
89
|
+
if (body.action === 'updateRepository') {
|
|
90
|
+
response = await codebuild
|
|
91
|
+
.startBuild({
|
|
92
|
+
projectName: getProcessEnvVariable_1.getProcessEnvVariable('PROCESS_ENV_REPOSITORY_IMAGE_CODE_BUILD_PROJECT_NAME'),
|
|
93
|
+
})
|
|
94
|
+
.promise();
|
|
95
|
+
}
|
|
96
|
+
if (body.action === 'executeTask') {
|
|
97
|
+
const { commands = [], cpu, memory, taskEnvironment = [] } = body;
|
|
98
|
+
if (commands.length === 0) {
|
|
99
|
+
throw new Error('Commands were not provided.');
|
|
100
|
+
}
|
|
101
|
+
response = await executeTasks_1.executeTasks({ commands, cpu, memory, taskEnvironment });
|
|
102
|
+
}
|
|
103
|
+
if (response) {
|
|
104
|
+
return {
|
|
105
|
+
statusCode: 200,
|
|
106
|
+
headers: {
|
|
107
|
+
'Content-Type': 'application/json',
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify(response),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
statusCode: 403,
|
|
114
|
+
body: 'Execute access forbidden',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
return {
|
|
119
|
+
statusCode: 400,
|
|
120
|
+
body: err.message,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
exports.cicdApiV1Handler = cicdApiV1Handler;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ecsTaskReportHandler = exports.getEcsTaskLogsUrl = void 0;
|
|
4
|
+
const webhook_1 = require("@slack/webhook");
|
|
5
|
+
const putApprovalResultManualTask_1 = require("./putApprovalResultManualTask");
|
|
6
|
+
const getEcsTaskLogsUrl = ({ ecsTaskArn }) => {
|
|
7
|
+
if (!process.env.ECS_TASK_CONTAINER_NAME ||
|
|
8
|
+
!process.env.ECS_TASK_LOGS_LOG_GROUP) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Arn has the following format:
|
|
13
|
+
* arn:aws:ecs:us-east-1:483684946879:task/CarlinCicdCarlinMonorepo-RepositoryTasksECSCluster-1J6saGT91hCr/6fcc78682de442ae89a0b7339ac7d981
|
|
14
|
+
*
|
|
15
|
+
* We want the "6fcc78682de442ae89a0b7339ac7d981" part.
|
|
16
|
+
*/
|
|
17
|
+
const ecsTaskId = ecsTaskArn.split('/')[2];
|
|
18
|
+
const ecsTaskLogsUrl = [
|
|
19
|
+
/**
|
|
20
|
+
* https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime
|
|
21
|
+
*/
|
|
22
|
+
`https://console.aws.amazon.com/cloudwatch/home?region=${process.env.AWS_REGION}#logsV2:log-groups`,
|
|
23
|
+
'log-group',
|
|
24
|
+
process.env.ECS_TASK_LOGS_LOG_GROUP,
|
|
25
|
+
'log-events',
|
|
26
|
+
`ecs/${process.env.ECS_TASK_CONTAINER_NAME}/${ecsTaskId}`.replace(/\//g, '%252F'),
|
|
27
|
+
].join('/');
|
|
28
|
+
return ecsTaskLogsUrl;
|
|
29
|
+
};
|
|
30
|
+
exports.getEcsTaskLogsUrl = getEcsTaskLogsUrl;
|
|
31
|
+
/**
|
|
32
|
+
* This method is invoked when an ECS task is executed and the success or
|
|
33
|
+
* failure commands calls `carlin cicd-ecs-task-report --status=<status>`.
|
|
34
|
+
*/
|
|
35
|
+
const ecsTaskReportHandler = async ({ ecsTaskArn, status, pipelineName, }) => {
|
|
36
|
+
const logs = ecsTaskArn && exports.getEcsTaskLogsUrl({ ecsTaskArn });
|
|
37
|
+
const handleApprovalResult = async () => {
|
|
38
|
+
if (pipelineName) {
|
|
39
|
+
await putApprovalResultManualTask_1.putApprovalResultManualTask({
|
|
40
|
+
pipelineName,
|
|
41
|
+
result: {
|
|
42
|
+
status,
|
|
43
|
+
summary: JSON.stringify({ status, logs }),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const handleStackNotification = async () => {
|
|
49
|
+
const url = process.env.SLACK_WEBHOOK_URL;
|
|
50
|
+
if (!url) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const webhook = new webhook_1.IncomingWebhook(url);
|
|
54
|
+
await webhook.send({
|
|
55
|
+
blocks: [
|
|
56
|
+
{
|
|
57
|
+
type: 'section',
|
|
58
|
+
text: {
|
|
59
|
+
type: 'mrkdwn',
|
|
60
|
+
text: `<${logs}|Logs>`,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'section',
|
|
65
|
+
text: {
|
|
66
|
+
type: 'mrkdwn',
|
|
67
|
+
text: `\`\`\`${JSON.stringify({
|
|
68
|
+
status,
|
|
69
|
+
pipelineName,
|
|
70
|
+
}, null, 2)}\`\`\``,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
await Promise.all([handleApprovalResult(), handleStackNotification()]);
|
|
77
|
+
return { statusCode: 200, body: 'ok' };
|
|
78
|
+
};
|
|
79
|
+
exports.ecsTaskReportHandler = ecsTaskReportHandler;
|