@datadog/datadog-ci 2.17.2 → 2.18.1
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/dist/commands/cloud-run/cli.d.ts +1 -0
- package/dist/commands/cloud-run/cli.js +5 -0
- package/dist/commands/cloud-run/cli.js.map +1 -0
- package/dist/commands/cloud-run/constants.d.ts +1 -0
- package/dist/commands/cloud-run/constants.js +22 -0
- package/dist/commands/cloud-run/constants.js.map +1 -0
- package/dist/commands/cloud-run/flare.d.ts +60 -0
- package/dist/commands/cloud-run/flare.js +369 -0
- package/dist/commands/cloud-run/flare.js.map +1 -0
- package/dist/commands/cloud-run/interfaces.d.ts +27 -0
- package/dist/commands/cloud-run/interfaces.js +3 -0
- package/dist/commands/cloud-run/interfaces.js.map +1 -0
- package/dist/commands/cloud-run/renderer.d.ts +4 -0
- package/dist/commands/cloud-run/renderer.js +38 -0
- package/dist/commands/cloud-run/renderer.js.map +1 -0
- package/dist/commands/flutter-symbols/renderer.js +2 -0
- package/dist/commands/flutter-symbols/renderer.js.map +1 -1
- package/dist/commands/gate/api.js +4 -0
- package/dist/commands/gate/api.js.map +1 -1
- package/dist/commands/gate/evaluate.js +5 -4
- package/dist/commands/gate/evaluate.js.map +1 -1
- package/dist/commands/gate/interfaces.d.ts +5 -0
- package/dist/commands/gate/renderer.d.ts +1 -1
- package/dist/commands/gate/renderer.js +16 -2
- package/dist/commands/gate/renderer.js.map +1 -1
- package/dist/commands/junit/utils.js +6 -13
- package/dist/commands/junit/utils.js.map +1 -1
- package/dist/commands/lambda/constants.d.ts +11 -8
- package/dist/commands/lambda/constants.js +85 -15
- package/dist/commands/lambda/constants.js.map +1 -1
- package/dist/commands/lambda/flare.d.ts +26 -40
- package/dist/commands/lambda/flare.js +275 -202
- package/dist/commands/lambda/flare.js.map +1 -1
- package/dist/commands/lambda/functions/commons.d.ts +7 -13
- package/dist/commands/lambda/functions/commons.js +39 -56
- package/dist/commands/lambda/functions/commons.js.map +1 -1
- package/dist/commands/lambda/functions/instrument.js +46 -45
- package/dist/commands/lambda/functions/instrument.js.map +1 -1
- package/dist/commands/lambda/functions/uninstrument.js +29 -28
- package/dist/commands/lambda/functions/uninstrument.js.map +1 -1
- package/dist/commands/lambda/instrument.js +20 -17
- package/dist/commands/lambda/instrument.js.map +1 -1
- package/dist/commands/lambda/prompt.d.ts +1 -2
- package/dist/commands/lambda/prompt.js +32 -32
- package/dist/commands/lambda/prompt.js.map +1 -1
- package/dist/commands/lambda/renderers/common-renderer.d.ts +0 -26
- package/dist/commands/lambda/renderers/common-renderer.js +4 -32
- package/dist/commands/lambda/renderers/common-renderer.js.map +1 -1
- package/dist/commands/lambda/renderers/instrument-uninstrument-renderer.js +27 -27
- package/dist/commands/lambda/renderers/instrument-uninstrument-renderer.js.map +1 -1
- package/dist/commands/lambda/uninstrument.js +10 -8
- package/dist/commands/lambda/uninstrument.js.map +1 -1
- package/dist/commands/sarif/api.js +2 -18
- package/dist/commands/sarif/api.js.map +1 -1
- package/dist/commands/stepfunctions/awsCommands.d.ts +2 -0
- package/dist/commands/stepfunctions/awsCommands.js +26 -2
- package/dist/commands/stepfunctions/awsCommands.js.map +1 -1
- package/dist/commands/stepfunctions/constants.d.ts +1 -0
- package/dist/commands/stepfunctions/constants.js +2 -1
- package/dist/commands/stepfunctions/constants.js.map +1 -1
- package/dist/commands/stepfunctions/helpers.d.ts +22 -0
- package/dist/commands/stepfunctions/helpers.js +62 -4
- package/dist/commands/stepfunctions/helpers.js.map +1 -1
- package/dist/commands/stepfunctions/instrument.d.ts +1 -0
- package/dist/commands/stepfunctions/instrument.js +15 -4
- package/dist/commands/stepfunctions/instrument.js.map +1 -1
- package/dist/commands/synthetics/interfaces.d.ts +2 -1
- package/dist/commands/synthetics/interfaces.js.map +1 -1
- package/dist/commands/synthetics/reporters/default.js +3 -5
- package/dist/commands/synthetics/reporters/default.js.map +1 -1
- package/dist/commands/synthetics/utils.js +3 -9
- package/dist/commands/synthetics/utils.js.map +1 -1
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +14 -1
- package/dist/constants.js.map +1 -1
- package/dist/helpers/app.d.ts +2 -0
- package/dist/helpers/app.js +17 -0
- package/dist/helpers/app.js.map +1 -0
- package/dist/helpers/flare.d.ts +19 -0
- package/dist/helpers/flare.js +81 -0
- package/dist/helpers/flare.js.map +1 -0
- package/dist/helpers/fs.d.ts +31 -0
- package/dist/helpers/fs.js +117 -0
- package/dist/helpers/fs.js.map +1 -0
- package/dist/helpers/prompt.d.ts +6 -0
- package/dist/helpers/prompt.js +39 -0
- package/dist/helpers/prompt.js.map +1 -0
- package/dist/helpers/renderer.d.ts +36 -0
- package/dist/helpers/renderer.js +47 -0
- package/dist/helpers/renderer.js.map +1 -0
- package/dist/helpers/utils.d.ts +8 -0
- package/dist/helpers/utils.js +40 -1
- package/dist/helpers/utils.js.map +1 -1
- package/package.json +8 -2
- package/dist/commands/lambda/renderers/flare-renderer.d.ts +0 -9
- package/dist/commands/lambda/renderers/flare-renderer.js +0 -18
- package/dist/commands/lambda/renderers/flare-renderer.js.map +0 -1
- package/dist/helpers/file.d.ts +0 -1
- package/dist/helpers/file.js +0 -9
- package/dist/helpers/file.js.map +0 -1
|
@@ -31,31 +31,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
31
31
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
32
|
};
|
|
33
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
-
exports.
|
|
34
|
+
exports.generateInsightsFile = exports.getFramework = exports.sleep = exports.convertToCSV = exports.getUniqueFileNames = exports.getTags = exports.getAllLogs = exports.getLogEvents = exports.getLogStreamNames = exports.validateFilePath = exports.getProjectFiles = exports.validateStartEndFlags = exports.LambdaFlareCommand = void 0;
|
|
35
35
|
const fs = __importStar(require("fs"));
|
|
36
36
|
const path = __importStar(require("path"));
|
|
37
37
|
const util_1 = __importDefault(require("util"));
|
|
38
38
|
const client_cloudwatch_logs_1 = require("@aws-sdk/client-cloudwatch-logs");
|
|
39
39
|
const client_lambda_1 = require("@aws-sdk/client-lambda");
|
|
40
|
-
const
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
41
|
const clipanion_1 = require("clipanion");
|
|
42
|
-
const form_data_1 = __importDefault(require("form-data"));
|
|
43
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
44
|
-
const jszip_1 = __importDefault(require("jszip"));
|
|
45
42
|
const constants_1 = require("../../constants");
|
|
46
|
-
const
|
|
43
|
+
const flare_1 = require("../../helpers/flare");
|
|
44
|
+
const fs_1 = require("../../helpers/fs");
|
|
45
|
+
const prompt_1 = require("../../helpers/prompt");
|
|
46
|
+
const helpersRenderer = __importStar(require("../../helpers/renderer"));
|
|
47
|
+
const utils_1 = require("../../helpers/utils");
|
|
47
48
|
const constants_2 = require("./constants");
|
|
48
49
|
const commons_1 = require("./functions/commons");
|
|
49
|
-
const
|
|
50
|
+
const prompt_2 = require("./prompt");
|
|
50
51
|
const commonRenderer = __importStar(require("./renderers/common-renderer"));
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const FLARE_OUTPUT_DIRECTORY = '.datadog-ci';
|
|
55
|
-
const LOGS_DIRECTORY = 'logs';
|
|
52
|
+
const version = require('../../../package.json').version;
|
|
53
|
+
const PROJECT_FILES_DIRECTORY = 'project_files';
|
|
54
|
+
const ADDITIONAL_FILES_DIRECTORY = 'additional_files';
|
|
56
55
|
const FUNCTION_CONFIG_FILE_NAME = 'function_config.json';
|
|
57
56
|
const TAGS_FILE_NAME = 'tags.json';
|
|
58
|
-
const
|
|
57
|
+
const INSIGHTS_FILE_NAME = 'INSIGHTS.md';
|
|
58
|
+
const FLARE_ZIP_FILE_NAME = 'lambda-flare-output.zip';
|
|
59
59
|
const MAX_LOG_STREAMS = 50;
|
|
60
60
|
const DEFAULT_LOG_STREAMS = 3;
|
|
61
61
|
const MAX_LOG_EVENTS_PER_STREAM = 1000;
|
|
@@ -67,16 +67,17 @@ class LambdaFlareCommand extends clipanion_1.Command {
|
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Entry point for the `lambda flare` command.
|
|
70
|
-
* Gathers
|
|
70
|
+
* Gathers config, logs, tags, project files, and more from a
|
|
71
|
+
* Lambda function and sends them to Datadog support.
|
|
71
72
|
* @returns 0 if the command ran successfully, 1 otherwise.
|
|
72
73
|
*/
|
|
73
74
|
execute() {
|
|
74
75
|
var _a, _b, _c;
|
|
75
76
|
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
-
this.context.stdout.write(
|
|
77
|
+
this.context.stdout.write(helpersRenderer.renderFlareHeader('Lambda', this.isDryRun));
|
|
77
78
|
// Validate function name
|
|
78
79
|
if (this.functionName === undefined) {
|
|
79
|
-
this.context.stderr.write(
|
|
80
|
+
this.context.stderr.write(helpersRenderer.renderError('No function name specified. [-f,--function]'));
|
|
80
81
|
return 1;
|
|
81
82
|
}
|
|
82
83
|
const errorMessages = [];
|
|
@@ -86,18 +87,18 @@ class LambdaFlareCommand extends clipanion_1.Command {
|
|
|
86
87
|
errorMessages.push(commonRenderer.renderNoDefaultRegionSpecifiedError());
|
|
87
88
|
}
|
|
88
89
|
// Validate Datadog API key
|
|
89
|
-
this.apiKey = (_c = process.env[
|
|
90
|
+
this.apiKey = (_c = process.env[constants_1.CI_API_KEY_ENV_VAR]) !== null && _c !== void 0 ? _c : process.env[constants_1.API_KEY_ENV_VAR];
|
|
90
91
|
if (this.apiKey === undefined) {
|
|
91
|
-
errorMessages.push(
|
|
92
|
+
errorMessages.push(helpersRenderer.renderError('No Datadog API key specified. Set an API key with the DATADOG_API_KEY environment variable.'));
|
|
92
93
|
}
|
|
93
94
|
if (!this.isDryRun) {
|
|
94
95
|
// Validate case ID
|
|
95
96
|
if (this.caseId === undefined) {
|
|
96
|
-
errorMessages.push(
|
|
97
|
+
errorMessages.push(helpersRenderer.renderError('No case ID specified. [-c,--case-id]'));
|
|
97
98
|
}
|
|
98
99
|
// Validate email
|
|
99
100
|
if (this.email === undefined) {
|
|
100
|
-
errorMessages.push(
|
|
101
|
+
errorMessages.push(helpersRenderer.renderError('No email specified. [-e,--email]'));
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
// Validate start/end flags if both are specified
|
|
@@ -109,7 +110,7 @@ class LambdaFlareCommand extends clipanion_1.Command {
|
|
|
109
110
|
}
|
|
110
111
|
catch (err) {
|
|
111
112
|
if (err instanceof Error) {
|
|
112
|
-
errorMessages.push(
|
|
113
|
+
errorMessages.push(helpersRenderer.renderError(err.message));
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
if (errorMessages.length > 0) {
|
|
@@ -119,30 +120,30 @@ class LambdaFlareCommand extends clipanion_1.Command {
|
|
|
119
120
|
return 1;
|
|
120
121
|
}
|
|
121
122
|
// Get AWS credentials
|
|
122
|
-
this.context.stdout.write('\n🔑 Getting AWS credentials...\n');
|
|
123
|
+
this.context.stdout.write(chalk_1.default.bold('\n🔑 Getting AWS credentials...\n'));
|
|
123
124
|
try {
|
|
124
125
|
this.credentials = yield commons_1.getAWSCredentials();
|
|
125
126
|
}
|
|
126
127
|
catch (err) {
|
|
127
128
|
if (err instanceof Error) {
|
|
128
|
-
this.context.stderr.write(
|
|
129
|
+
this.context.stderr.write(helpersRenderer.renderError(err.message));
|
|
129
130
|
}
|
|
130
131
|
return 1;
|
|
131
132
|
}
|
|
132
133
|
if (this.credentials === undefined) {
|
|
133
134
|
this.context.stdout.write('\n' + commonRenderer.renderNoAWSCredentialsFound());
|
|
134
135
|
try {
|
|
135
|
-
yield
|
|
136
|
+
yield prompt_2.requestAWSCredentials();
|
|
136
137
|
}
|
|
137
138
|
catch (err) {
|
|
138
139
|
if (err instanceof Error) {
|
|
139
|
-
this.context.stderr.write(
|
|
140
|
+
this.context.stderr.write(helpersRenderer.renderError(err.message));
|
|
140
141
|
}
|
|
141
142
|
return 1;
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
// Get and print Lambda function configuration
|
|
145
|
-
this.context.stdout.write('\n🔍 Fetching Lambda function configuration...\n');
|
|
146
|
+
this.context.stdout.write(chalk_1.default.bold('\n🔍 Fetching Lambda function configuration...\n'));
|
|
146
147
|
const lambdaClientConfig = {
|
|
147
148
|
region,
|
|
148
149
|
credentials: this.credentials,
|
|
@@ -154,42 +155,99 @@ class LambdaFlareCommand extends clipanion_1.Command {
|
|
|
154
155
|
}
|
|
155
156
|
catch (err) {
|
|
156
157
|
if (err instanceof Error) {
|
|
157
|
-
this.context.stderr.write(
|
|
158
|
+
this.context.stderr.write(helpersRenderer.renderError(`Unable to get Lambda function configuration: ${err.message}`));
|
|
158
159
|
}
|
|
159
160
|
return 1;
|
|
160
161
|
}
|
|
161
|
-
config =
|
|
162
|
+
config = commons_1.maskConfig(config);
|
|
162
163
|
const configStr = util_1.default.inspect(config, false, undefined, true);
|
|
163
164
|
this.context.stdout.write(`\n${configStr}\n`);
|
|
165
|
+
// Get project files
|
|
166
|
+
this.context.stdout.write(chalk_1.default.bold('\n📁 Searching for project files in current directory...\n'));
|
|
167
|
+
const projectFilePaths = yield exports.getProjectFiles();
|
|
168
|
+
let projectFilesMessage = chalk_1.default.bold(`\n✅ Found project file(s) in ${process.cwd()}:\n`);
|
|
169
|
+
if (projectFilePaths.size === 0) {
|
|
170
|
+
projectFilesMessage = helpersRenderer.renderSoftWarning('No project files found.');
|
|
171
|
+
}
|
|
172
|
+
this.context.stdout.write(projectFilesMessage);
|
|
173
|
+
for (const filePath of projectFilePaths) {
|
|
174
|
+
const fileName = path.basename(filePath);
|
|
175
|
+
this.context.stdout.write(`• ${fileName}\n`);
|
|
176
|
+
}
|
|
177
|
+
// Additional files
|
|
178
|
+
this.context.stdout.write('\n');
|
|
179
|
+
const additionalFilePaths = new Set();
|
|
180
|
+
let confirmAdditionalFiles;
|
|
181
|
+
try {
|
|
182
|
+
confirmAdditionalFiles = yield prompt_1.requestConfirmation('Do you want to specify any additional files to flare?', false);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
if (err instanceof Error) {
|
|
186
|
+
this.context.stderr.write(helpersRenderer.renderError(err.message));
|
|
187
|
+
}
|
|
188
|
+
return 1;
|
|
189
|
+
}
|
|
190
|
+
while (confirmAdditionalFiles) {
|
|
191
|
+
this.context.stdout.write('\n');
|
|
192
|
+
let filePath;
|
|
193
|
+
try {
|
|
194
|
+
filePath = yield prompt_2.requestFilePath();
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
if (err instanceof Error) {
|
|
198
|
+
this.context.stderr.write(helpersRenderer.renderError(err.message));
|
|
199
|
+
}
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
if (filePath === '') {
|
|
203
|
+
this.context.stdout.write(`Added ${additionalFilePaths.size} custom file(s):\n`);
|
|
204
|
+
for (const additionalFilePath of additionalFilePaths) {
|
|
205
|
+
const fileName = path.basename(additionalFilePath);
|
|
206
|
+
this.context.stdout.write(`• ${fileName}\n`);
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
filePath = exports.validateFilePath(filePath, projectFilePaths, additionalFilePaths);
|
|
212
|
+
additionalFilePaths.add(filePath);
|
|
213
|
+
const fileName = path.basename(filePath);
|
|
214
|
+
this.context.stdout.write(`• Added file '${fileName}'\n`);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
if (err instanceof Error) {
|
|
218
|
+
this.context.stderr.write(err.message);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
164
222
|
// Get tags
|
|
165
|
-
this.context.stdout.write('\n🏷 Getting Resource Tags...\n');
|
|
223
|
+
this.context.stdout.write(chalk_1.default.bold('\n🏷 Getting Resource Tags...\n'));
|
|
166
224
|
let tags;
|
|
167
225
|
try {
|
|
168
226
|
tags = yield exports.getTags(lambdaClient, region, config.FunctionArn);
|
|
169
227
|
}
|
|
170
228
|
catch (err) {
|
|
171
229
|
if (err instanceof Error) {
|
|
172
|
-
this.context.stderr.write(
|
|
230
|
+
this.context.stderr.write(helpersRenderer.renderError(err.message));
|
|
173
231
|
}
|
|
174
232
|
return 1;
|
|
175
233
|
}
|
|
176
234
|
const tagsLength = Object.keys(tags).length;
|
|
177
235
|
if (tagsLength === 0) {
|
|
178
|
-
this.context.stdout.write(
|
|
236
|
+
this.context.stdout.write(helpersRenderer.renderSoftWarning(`No resource tags were found.`));
|
|
179
237
|
}
|
|
180
238
|
else {
|
|
181
|
-
this.context.stdout.write(
|
|
239
|
+
this.context.stdout.write(`Found ${tagsLength} resource tag(s).\n`);
|
|
182
240
|
}
|
|
183
241
|
// Get CloudWatch logs
|
|
184
242
|
let logs = new Map();
|
|
185
243
|
if (this.withLogs) {
|
|
186
|
-
this.context.stdout.write('\n
|
|
244
|
+
this.context.stdout.write(chalk_1.default.bold('\n🌧 Getting CloudWatch logs...\n'));
|
|
187
245
|
try {
|
|
188
246
|
logs = yield exports.getAllLogs(region, this.functionName, startMillis, endMillis);
|
|
189
247
|
}
|
|
190
248
|
catch (err) {
|
|
191
249
|
if (err instanceof Error) {
|
|
192
|
-
this.context.stderr.write(
|
|
250
|
+
this.context.stderr.write(helpersRenderer.renderError(err.message));
|
|
193
251
|
}
|
|
194
252
|
return 1;
|
|
195
253
|
}
|
|
@@ -197,48 +255,81 @@ class LambdaFlareCommand extends clipanion_1.Command {
|
|
|
197
255
|
try {
|
|
198
256
|
// CloudWatch messages
|
|
199
257
|
if (this.withLogs) {
|
|
200
|
-
let message = '\n✅ Found log streams:\n';
|
|
258
|
+
let message = chalk_1.default.bold('\n✅ Found log streams:\n');
|
|
201
259
|
if (logs.size === 0) {
|
|
202
|
-
message =
|
|
260
|
+
message = helpersRenderer.renderSoftWarning('No CloudWatch log streams were found. Logs will not be retrieved or sent.');
|
|
203
261
|
}
|
|
204
262
|
this.context.stdout.write(message);
|
|
205
263
|
for (const [logStreamName, logEvents] of logs) {
|
|
206
264
|
let warningMessage = '\n';
|
|
207
265
|
if (logEvents.length === 0) {
|
|
208
|
-
warningMessage =
|
|
266
|
+
warningMessage = ` - ${helpersRenderer.renderSoftWarning('No log events found in this stream')}`;
|
|
209
267
|
}
|
|
210
268
|
this.context.stdout.write(`• ${logStreamName}${warningMessage}`);
|
|
211
269
|
}
|
|
212
270
|
}
|
|
213
271
|
// Create folders
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
const
|
|
272
|
+
const rootFolderPath = path.join(process.cwd(), constants_1.FLARE_OUTPUT_DIRECTORY);
|
|
273
|
+
const logsFolderPath = path.join(rootFolderPath, constants_1.LOGS_DIRECTORY);
|
|
274
|
+
const projectFilesFolderPath = path.join(rootFolderPath, PROJECT_FILES_DIRECTORY);
|
|
275
|
+
const additionalFilesFolderPath = path.join(rootFolderPath, ADDITIONAL_FILES_DIRECTORY);
|
|
276
|
+
this.context.stdout.write(chalk_1.default.bold(`\n💾 Saving files to ${rootFolderPath}...\n`));
|
|
217
277
|
if (fs.existsSync(rootFolderPath)) {
|
|
218
|
-
|
|
278
|
+
fs_1.deleteFolder(rootFolderPath);
|
|
279
|
+
}
|
|
280
|
+
const subFolders = [];
|
|
281
|
+
if (logs.size > 0) {
|
|
282
|
+
subFolders.push(logsFolderPath);
|
|
219
283
|
}
|
|
220
|
-
|
|
221
|
-
|
|
284
|
+
if (projectFilePaths.size > 0) {
|
|
285
|
+
subFolders.push(projectFilesFolderPath);
|
|
286
|
+
}
|
|
287
|
+
if (additionalFilePaths.size > 0) {
|
|
288
|
+
subFolders.push(additionalFilesFolderPath);
|
|
289
|
+
}
|
|
290
|
+
fs_1.createDirectories(rootFolderPath, subFolders);
|
|
291
|
+
// Write config file
|
|
222
292
|
const configFilePath = path.join(rootFolderPath, FUNCTION_CONFIG_FILE_NAME);
|
|
223
|
-
|
|
224
|
-
this.context.stdout.write(`• Saved function config to
|
|
293
|
+
fs_1.writeFile(configFilePath, JSON.stringify(config, undefined, 2));
|
|
294
|
+
this.context.stdout.write(`• Saved function config to ./${FUNCTION_CONFIG_FILE_NAME}\n`);
|
|
295
|
+
// Write tags file
|
|
225
296
|
if (tagsLength > 0) {
|
|
226
297
|
const tagsFilePath = path.join(rootFolderPath, TAGS_FILE_NAME);
|
|
227
|
-
|
|
228
|
-
this.context.stdout.write(`• Saved tags to
|
|
298
|
+
fs_1.writeFile(tagsFilePath, JSON.stringify(tags, undefined, 2));
|
|
299
|
+
this.context.stdout.write(`• Saved tags to ./${TAGS_FILE_NAME}\n`);
|
|
229
300
|
}
|
|
301
|
+
// Write log files
|
|
230
302
|
for (const [logStreamName, logEvents] of logs) {
|
|
231
303
|
if (logEvents.length === 0) {
|
|
232
304
|
continue;
|
|
233
305
|
}
|
|
234
306
|
const logFilePath = path.join(logsFolderPath, `${logStreamName.split('/').join('-')}.csv`);
|
|
235
307
|
const data = exports.convertToCSV(logEvents);
|
|
236
|
-
|
|
237
|
-
this.context.stdout.write(`• Saved logs to
|
|
308
|
+
fs_1.writeFile(logFilePath, data);
|
|
309
|
+
this.context.stdout.write(`• Saved logs to ./${constants_1.LOGS_DIRECTORY}/${logStreamName}\n`);
|
|
238
310
|
// Sleep for 1 millisecond so creation times are different
|
|
239
311
|
// This allows the logs to be sorted by creation time by the support team
|
|
240
312
|
yield exports.sleep(1);
|
|
241
313
|
}
|
|
314
|
+
// Write project files
|
|
315
|
+
for (const filePath of projectFilePaths) {
|
|
316
|
+
const fileName = path.basename(filePath);
|
|
317
|
+
const newFilePath = path.join(projectFilesFolderPath, fileName);
|
|
318
|
+
fs.copyFileSync(filePath, newFilePath);
|
|
319
|
+
this.context.stdout.write(`• Copied ${fileName} to ./${PROJECT_FILES_DIRECTORY}/${fileName}\n`);
|
|
320
|
+
}
|
|
321
|
+
// Write additional files
|
|
322
|
+
const additionalFilesMap = exports.getUniqueFileNames(additionalFilePaths);
|
|
323
|
+
for (const [originalFilePath, newFileName] of additionalFilesMap) {
|
|
324
|
+
const originalFileName = path.basename(originalFilePath);
|
|
325
|
+
const newFilePath = path.join(additionalFilesFolderPath, newFileName);
|
|
326
|
+
fs.copyFileSync(originalFilePath, newFilePath);
|
|
327
|
+
this.context.stdout.write(`• Copied ${originalFileName} to ./${ADDITIONAL_FILES_DIRECTORY}/${newFileName}\n`);
|
|
328
|
+
}
|
|
329
|
+
// Write insights file
|
|
330
|
+
const insightsFilePath = path.join(rootFolderPath, INSIGHTS_FILE_NAME);
|
|
331
|
+
exports.generateInsightsFile(insightsFilePath, this.isDryRun, config);
|
|
332
|
+
this.context.stdout.write(`• Saved insights file to ./${INSIGHTS_FILE_NAME}\n`);
|
|
242
333
|
// Exit if dry run
|
|
243
334
|
const outputMsg = `\nℹ️ Your output files are located at: ${rootFolderPath}\n\n`;
|
|
244
335
|
if (this.isDryRun) {
|
|
@@ -248,25 +339,25 @@ class LambdaFlareCommand extends clipanion_1.Command {
|
|
|
248
339
|
}
|
|
249
340
|
// Confirm before sending
|
|
250
341
|
this.context.stdout.write('\n');
|
|
251
|
-
const
|
|
252
|
-
if (!
|
|
342
|
+
const confirmSendFiles = yield prompt_1.requestConfirmation('Are you sure you want to send the flare file to Datadog Support?', false);
|
|
343
|
+
if (!confirmSendFiles) {
|
|
253
344
|
this.context.stdout.write('\n🚫 The flare files were not sent based on your selection.');
|
|
254
345
|
this.context.stdout.write(outputMsg);
|
|
255
346
|
return 0;
|
|
256
347
|
}
|
|
257
348
|
// Zip folder
|
|
258
|
-
const zipPath = path.join(rootFolderPath,
|
|
259
|
-
yield
|
|
349
|
+
const zipPath = path.join(rootFolderPath, FLARE_ZIP_FILE_NAME);
|
|
350
|
+
yield fs_1.zipContents(rootFolderPath, zipPath);
|
|
260
351
|
// Send to Datadog
|
|
261
|
-
this.context.stdout.write('\n🚀 Sending to Datadog Support...\n');
|
|
262
|
-
yield
|
|
263
|
-
this.context.stdout.write('\n✅ Successfully sent flare file to Datadog Support!\n');
|
|
352
|
+
this.context.stdout.write(chalk_1.default.bold('\n🚀 Sending to Datadog Support...\n'));
|
|
353
|
+
yield flare_1.sendToDatadog(zipPath, this.caseId, this.email, this.apiKey, rootFolderPath);
|
|
354
|
+
this.context.stdout.write(chalk_1.default.bold('\n✅ Successfully sent flare file to Datadog Support!\n'));
|
|
264
355
|
// Delete contents
|
|
265
|
-
|
|
356
|
+
fs_1.deleteFolder(rootFolderPath);
|
|
266
357
|
}
|
|
267
358
|
catch (err) {
|
|
268
359
|
if (err instanceof Error) {
|
|
269
|
-
this.context.stderr.write(
|
|
360
|
+
this.context.stderr.write(helpersRenderer.renderError(err.message));
|
|
270
361
|
}
|
|
271
362
|
return 1;
|
|
272
363
|
}
|
|
@@ -309,57 +400,42 @@ const validateStartEndFlags = (start, end) => {
|
|
|
309
400
|
};
|
|
310
401
|
exports.validateStartEndFlags = validateStartEndFlags;
|
|
311
402
|
/**
|
|
312
|
-
*
|
|
313
|
-
* @
|
|
403
|
+
* Searches current directory for project files
|
|
404
|
+
* @returns a set of file paths of project files
|
|
314
405
|
*/
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const stringifiedConfig = JSON.stringify(config, replacer);
|
|
323
|
-
return JSON.parse(stringifiedConfig);
|
|
324
|
-
};
|
|
325
|
-
exports.maskConfig = maskConfig;
|
|
326
|
-
/**
|
|
327
|
-
* Delete a folder and all its contents
|
|
328
|
-
* @param folderPath the folder to delete
|
|
329
|
-
* @throws Error if the deletion fails
|
|
330
|
-
*/
|
|
331
|
-
const deleteFolder = (folderPath) => {
|
|
332
|
-
try {
|
|
333
|
-
fs.rmSync(folderPath, { recursive: true, force: true });
|
|
334
|
-
}
|
|
335
|
-
catch (err) {
|
|
336
|
-
if (err instanceof Error) {
|
|
337
|
-
throw Error(`Failed to delete files located at ${folderPath}: ${err.message}`);
|
|
406
|
+
const getProjectFiles = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
407
|
+
const filePaths = new Set();
|
|
408
|
+
const cwd = process.cwd();
|
|
409
|
+
for (const fileName of constants_2.PROJECT_FILES) {
|
|
410
|
+
const filePath = path.join(cwd, fileName);
|
|
411
|
+
if (fs.existsSync(filePath)) {
|
|
412
|
+
filePaths.add(filePath);
|
|
338
413
|
}
|
|
339
414
|
}
|
|
340
|
-
|
|
341
|
-
|
|
415
|
+
return filePaths;
|
|
416
|
+
});
|
|
417
|
+
exports.getProjectFiles = getProjectFiles;
|
|
342
418
|
/**
|
|
343
|
-
*
|
|
344
|
-
* @param
|
|
345
|
-
* @param
|
|
346
|
-
* @param
|
|
347
|
-
* @throws Error if the
|
|
419
|
+
* Validates a path to a file
|
|
420
|
+
* @param filePath path to the file
|
|
421
|
+
* @param projectFilePaths map of file names to file paths
|
|
422
|
+
* @param additionalFiles set of additional file paths
|
|
423
|
+
* @throws Error if the file path is invalid or the file was already added
|
|
424
|
+
* @returns the full path to the file
|
|
348
425
|
*/
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
426
|
+
const validateFilePath = (filePath, projectFilePaths, additionalFiles) => {
|
|
427
|
+
const originalPath = filePath;
|
|
428
|
+
filePath = fs.existsSync(filePath) ? filePath : path.join(process.cwd(), filePath);
|
|
429
|
+
if (!fs.existsSync(filePath)) {
|
|
430
|
+
throw Error(helpersRenderer.renderError(`File path '${originalPath}' not found. Please try again.`));
|
|
355
431
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
432
|
+
filePath = path.resolve(filePath);
|
|
433
|
+
if (projectFilePaths.has(filePath) || additionalFiles.has(filePath)) {
|
|
434
|
+
throw Error(helpersRenderer.renderSoftWarning(`File '${filePath}' has already been added.`));
|
|
360
435
|
}
|
|
436
|
+
return filePath;
|
|
361
437
|
};
|
|
362
|
-
exports.
|
|
438
|
+
exports.validateFilePath = validateFilePath;
|
|
363
439
|
/**
|
|
364
440
|
* Gets the LOG_STREAM_COUNT latest log stream names, sorted by last event time
|
|
365
441
|
* @param cwlClient CloudWatch Logs client
|
|
@@ -504,22 +580,39 @@ const getTags = (lambdaClient, region, arn) => __awaiter(void 0, void 0, void 0,
|
|
|
504
580
|
});
|
|
505
581
|
exports.getTags = getTags;
|
|
506
582
|
/**
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
* @
|
|
583
|
+
* Generate unique file names
|
|
584
|
+
* If the original file name is unique, keep it as is
|
|
585
|
+
* Otherwise, replace separators in the file path with dashes
|
|
586
|
+
* @param filePaths the list of file paths
|
|
587
|
+
* @returns a mapping of file paths to new file names
|
|
511
588
|
*/
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
589
|
+
const getUniqueFileNames = (filePaths) => {
|
|
590
|
+
// Count occurrences of each filename
|
|
591
|
+
const fileNameCount = {};
|
|
592
|
+
filePaths.forEach((filePath) => {
|
|
593
|
+
const fileName = path.basename(filePath);
|
|
594
|
+
const count = fileNameCount[fileName] || 0;
|
|
595
|
+
fileNameCount[fileName] = count + 1;
|
|
596
|
+
});
|
|
597
|
+
// Create new filenames
|
|
598
|
+
const filePathsToNewFileNames = new Map();
|
|
599
|
+
filePaths.forEach((filePath) => {
|
|
600
|
+
const fileName = path.basename(filePath);
|
|
601
|
+
if (fileNameCount[fileName] > 1) {
|
|
602
|
+
// Trim leading and trailing '/'s and '\'s
|
|
603
|
+
const trimRegex = /^\/+|\/+$/g;
|
|
604
|
+
const filePathTrimmed = filePath.replace(trimRegex, '');
|
|
605
|
+
// Replace '/'s and '\'s with '-'s
|
|
606
|
+
const newFileName = filePathTrimmed.split(path.sep).join('-');
|
|
607
|
+
filePathsToNewFileNames.set(filePath, newFileName);
|
|
519
608
|
}
|
|
520
|
-
|
|
609
|
+
else {
|
|
610
|
+
filePathsToNewFileNames.set(filePath, fileName);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
return filePathsToNewFileNames;
|
|
521
614
|
};
|
|
522
|
-
exports.
|
|
615
|
+
exports.getUniqueFileNames = getUniqueFileNames;
|
|
523
616
|
/**
|
|
524
617
|
* Convert the log events to a CSV string
|
|
525
618
|
* @param logEvents array of log events
|
|
@@ -549,101 +642,81 @@ const sleep = (ms) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
549
642
|
});
|
|
550
643
|
exports.sleep = sleep;
|
|
551
644
|
/**
|
|
552
|
-
*
|
|
553
|
-
* @
|
|
554
|
-
* @param zipPath path to save the zip file
|
|
555
|
-
* @throws Error if the zip fails
|
|
645
|
+
* Get the framework used based on the files in the directory
|
|
646
|
+
* @returns the framework used or undefined if no framework is found
|
|
556
647
|
*/
|
|
557
|
-
const
|
|
558
|
-
const
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const folder = fs.statSync(folderPath);
|
|
564
|
-
if (!folder.isDirectory()) {
|
|
565
|
-
throw Error(`Path is not a directory: ${folderPath}`);
|
|
648
|
+
const getFramework = () => {
|
|
649
|
+
const frameworks = new Set();
|
|
650
|
+
const files = fs.readdirSync(process.cwd());
|
|
651
|
+
files.forEach((file) => {
|
|
652
|
+
if (constants_2.FRAMEWORK_FILES_MAPPING.has(file)) {
|
|
653
|
+
frameworks.add(constants_2.FRAMEWORK_FILES_MAPPING.get(file));
|
|
566
654
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const file = fs.statSync(fullPath);
|
|
571
|
-
if (file.isDirectory()) {
|
|
572
|
-
addFolderToZip(fullPath);
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
const data = fs.readFileSync(fullPath);
|
|
576
|
-
zip.file(path.relative(rootFolderPath, fullPath), data);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
try {
|
|
581
|
-
addFolderToZip(rootFolderPath);
|
|
582
|
-
const zipContent = yield zip.generateAsync({ type: 'nodebuffer' });
|
|
583
|
-
fs.writeFileSync(zipPath, zipContent);
|
|
584
|
-
}
|
|
585
|
-
catch (err) {
|
|
586
|
-
if (err instanceof Error) {
|
|
587
|
-
throw Error(`Unable to zip the flare files: ${err.message}`);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
exports.zipContents = zipContents;
|
|
592
|
-
/**
|
|
593
|
-
* Calculates the full endpoint URL
|
|
594
|
-
* @throws Error if the site is invalid
|
|
595
|
-
* @returns the full endpoint URL
|
|
596
|
-
*/
|
|
597
|
-
const getEndpointUrl = () => {
|
|
598
|
-
var _a, _b;
|
|
599
|
-
const baseUrl = (_b = (_a = process.env[constants_2.CI_SITE_ENV_VAR]) !== null && _a !== void 0 ? _a : process.env[constants_2.SITE_ENV_VAR]) !== null && _b !== void 0 ? _b : constants_1.DATADOG_SITE_US1;
|
|
600
|
-
// The DNS doesn't redirect to the proper endpoint when a subdomain is not present in the baseUrl.
|
|
601
|
-
// There is a DNS inconsistency
|
|
602
|
-
let endpointUrl = baseUrl;
|
|
603
|
-
if ([constants_1.DATADOG_SITE_US1, constants_1.DATADOG_SITE_EU1, constants_1.DATADOG_SITE_GOV].includes(baseUrl)) {
|
|
604
|
-
endpointUrl = 'app.' + baseUrl;
|
|
605
|
-
}
|
|
606
|
-
if (!validation_1.isValidDatadogSite(baseUrl)) {
|
|
607
|
-
throw Error(`Invalid site: ${baseUrl}. Must be one of: ${constants_1.DATADOG_SITES.join(', ')}`);
|
|
655
|
+
});
|
|
656
|
+
if (frameworks.size > 0) {
|
|
657
|
+
return Array.from(frameworks).join(', ');
|
|
608
658
|
}
|
|
609
|
-
return
|
|
659
|
+
return constants_2.DeploymentFrameworks.Unknown;
|
|
610
660
|
};
|
|
611
|
-
exports.
|
|
661
|
+
exports.getFramework = getFramework;
|
|
612
662
|
/**
|
|
613
|
-
*
|
|
614
|
-
* @param
|
|
615
|
-
* @param
|
|
616
|
-
* @param
|
|
617
|
-
* @param apiKey
|
|
618
|
-
* @param rootFolderPath
|
|
619
|
-
* @throws Error if the request fails
|
|
663
|
+
* Generate the insights file
|
|
664
|
+
* @param insightsFilePath path to the insights file
|
|
665
|
+
* @param isDryRun whether or not this is a dry run
|
|
666
|
+
* @param config Lambda function configuration
|
|
620
667
|
*/
|
|
621
|
-
const
|
|
622
|
-
var _b, _c, _d;
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
668
|
+
const generateInsightsFile = (insightsFilePath, isDryRun, config) => {
|
|
669
|
+
var _a, _b, _c, _d, _e;
|
|
670
|
+
const lines = [];
|
|
671
|
+
// Header
|
|
672
|
+
lines.push('# Flare Insights');
|
|
673
|
+
lines.push('\n_Autogenerated file from `lambda flare`_ ');
|
|
674
|
+
if (isDryRun) {
|
|
675
|
+
lines.push('_This command was run in dry mode._');
|
|
676
|
+
}
|
|
677
|
+
// AWS Lambda Configuration
|
|
678
|
+
lines.push('\n## AWS Lambda Configuration');
|
|
679
|
+
lines.push(`**Function Name**: \`${config.FunctionName}\` `);
|
|
680
|
+
lines.push(`**Function ARN**: \`${config.FunctionArn}\` `);
|
|
681
|
+
lines.push(`**Runtime**: \`${config.Runtime}\` `);
|
|
682
|
+
lines.push(`**Handler**: \`${config.Handler}\` `);
|
|
683
|
+
lines.push(`**Timeout**: \`${config.Timeout}\` `);
|
|
684
|
+
lines.push(`**Memory Size**: \`${config.MemorySize}\` `);
|
|
685
|
+
const architectures = (_a = config.Architectures) !== null && _a !== void 0 ? _a : ['Unknown'];
|
|
686
|
+
lines.push(`**Architecture**: \`${architectures.join(', ')}\` `);
|
|
687
|
+
lines.push('**Environment Variables**:');
|
|
688
|
+
const envVars = Object.entries((_c = (_b = config.Environment) === null || _b === void 0 ? void 0 : _b.Variables) !== null && _c !== void 0 ? _c : {});
|
|
689
|
+
if (envVars.length === 0) {
|
|
690
|
+
lines.push('- No environment variables found.');
|
|
691
|
+
}
|
|
692
|
+
for (const [key, value] of envVars) {
|
|
693
|
+
lines.push(`- \`${key}\`: \`${value}\``);
|
|
694
|
+
}
|
|
695
|
+
lines.push('\n**Layers**:');
|
|
696
|
+
const layers = (_d = config.Layers) !== null && _d !== void 0 ? _d : [];
|
|
697
|
+
if (layers.length === 0) {
|
|
698
|
+
lines.push(' - No layers found.');
|
|
699
|
+
}
|
|
700
|
+
let codeSize = (_e = config.CodeSize) !== null && _e !== void 0 ? _e : 0;
|
|
701
|
+
layers.forEach((layer) => {
|
|
702
|
+
var _a, _b;
|
|
703
|
+
const nameAndVersion = commons_1.getLayerNameWithVersion((_a = layer.Arn) !== null && _a !== void 0 ? _a : '');
|
|
704
|
+
if (nameAndVersion) {
|
|
705
|
+
lines.push(`- \`${nameAndVersion}\``);
|
|
642
706
|
}
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
});
|
|
646
|
-
|
|
707
|
+
codeSize += (_b = layer.CodeSize) !== null && _b !== void 0 ? _b : 0;
|
|
708
|
+
});
|
|
709
|
+
lines.push(`\n**Package Size**: \`${utils_1.formatBytes(codeSize)}\``);
|
|
710
|
+
// CLI Insights
|
|
711
|
+
lines.push('\n ## CLI');
|
|
712
|
+
lines.push(`**Run Location**: \`${process.cwd()}\` `);
|
|
713
|
+
lines.push(`**CLI Version**: \`${version}\` `);
|
|
714
|
+
const timeString = new Date().toISOString().replace('T', ' ').replace('Z', '') + ' UTC';
|
|
715
|
+
lines.push(`**Timestamp**: \`${timeString}\` `);
|
|
716
|
+
lines.push(`**Framework**: \`${exports.getFramework()}\``);
|
|
717
|
+
fs_1.writeFile(insightsFilePath, lines.join('\n'));
|
|
718
|
+
};
|
|
719
|
+
exports.generateInsightsFile = generateInsightsFile;
|
|
647
720
|
LambdaFlareCommand.addPath('lambda', 'flare');
|
|
648
721
|
LambdaFlareCommand.addOption('isDryRun', clipanion_1.Command.Boolean('-d,--dry'));
|
|
649
722
|
LambdaFlareCommand.addOption('withLogs', clipanion_1.Command.Boolean('--with-logs'));
|