@datadog/datadog-ci 0.17.12 → 0.17.13

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.
Files changed (43) hide show
  1. package/README.md +6 -1
  2. package/dist/commands/junit/upload.js +1 -1
  3. package/dist/commands/lambda/__tests__/fixtures.d.ts +5 -1
  4. package/dist/commands/lambda/__tests__/fixtures.js +13 -2
  5. package/dist/commands/lambda/__tests__/functions/commons.test.js +221 -0
  6. package/dist/commands/lambda/__tests__/functions/instrument.test.js +64 -42
  7. package/dist/commands/lambda/__tests__/instrument.test.js +425 -4
  8. package/dist/commands/lambda/__tests__/prompt.test.d.ts +1 -0
  9. package/dist/commands/lambda/__tests__/prompt.test.js +216 -0
  10. package/dist/commands/lambda/__tests__/uninstrument.test.js +310 -4
  11. package/dist/commands/lambda/constants.d.ts +10 -0
  12. package/dist/commands/lambda/constants.js +36 -2
  13. package/dist/commands/lambda/functions/commons.d.ts +15 -0
  14. package/dist/commands/lambda/functions/commons.js +105 -2
  15. package/dist/commands/lambda/functions/instrument.d.ts +1 -1
  16. package/dist/commands/lambda/functions/instrument.js +17 -7
  17. package/dist/commands/lambda/functions/uninstrument.js +1 -0
  18. package/dist/commands/lambda/instrument.d.ts +2 -0
  19. package/dist/commands/lambda/instrument.js +100 -47
  20. package/dist/commands/lambda/interfaces.d.ts +4 -0
  21. package/dist/commands/lambda/prompt.d.ts +9 -0
  22. package/dist/commands/lambda/prompt.js +187 -0
  23. package/dist/commands/lambda/uninstrument.d.ts +1 -0
  24. package/dist/commands/lambda/uninstrument.js +68 -30
  25. package/dist/commands/synthetics/__tests__/cli.test.js +1 -0
  26. package/dist/commands/synthetics/__tests__/fixtures.js +1 -0
  27. package/dist/commands/synthetics/__tests__/run-test.test.js +48 -2
  28. package/dist/commands/synthetics/__tests__/utils.test.js +11 -0
  29. package/dist/commands/synthetics/command.d.ts +1 -0
  30. package/dist/commands/synthetics/command.js +11 -5
  31. package/dist/commands/synthetics/interfaces.d.ts +8 -3
  32. package/dist/commands/synthetics/interfaces.js +7 -3
  33. package/dist/commands/synthetics/reporters/default.js +5 -1
  34. package/dist/commands/synthetics/run-test.js +3 -1
  35. package/dist/commands/synthetics/utils.d.ts +3 -0
  36. package/dist/commands/synthetics/utils.js +20 -1
  37. package/dist/commands/trace/api.js +1 -1
  38. package/dist/helpers/__tests__/ci.test.js +71 -24
  39. package/dist/helpers/__tests__/user-provided-git.test.js +69 -27
  40. package/dist/helpers/ci.js +1 -1
  41. package/dist/helpers/user-provided-git.d.ts +2 -1
  42. package/dist/helpers/user-provided-git.js +18 -3
  43. package/package.json +1 -1
@@ -32,6 +32,19 @@ export declare const coerceBoolean: (fallback: boolean, ...values: any[]) => boo
32
32
  export declare const collectFunctionsByRegion: (functions: string[], defaultRegion: string | undefined) => {
33
33
  [key: string]: string[];
34
34
  };
35
+ /**
36
+ * Given a layer runtime, return its latest version.
37
+ *
38
+ * @param runtime the runtime of the layer.
39
+ * @param region the region where the layer is stored.
40
+ * @returns the latest version of the layer to find.
41
+ */
42
+ export declare const findLatestLayerVersion: (runtime: Runtime, region: string) => Promise<number>;
43
+ export declare const isMissingAWSCredentials: () => boolean;
44
+ export declare const isMissingDatadogSiteEnvVar: () => boolean;
45
+ export declare const isMissingAnyDatadogApiKeyEnvVar: () => boolean;
46
+ export declare const isMissingDatadogEnvVars: () => boolean;
47
+ export declare const getAllLambdaFunctionConfigs: (lambda: Lambda) => Promise<Lambda.FunctionConfiguration[]>;
35
48
  /**
36
49
  * Given a Lambda instance and a regular expression,
37
50
  * returns all the Function Configurations that match.
@@ -61,6 +74,7 @@ export declare const getLambdaFunctionConfigs: (lambda: Lambda, functionARNs: st
61
74
  * @returns the ARN of a **Specific Runtime Layer** with the correct region, account, architecture, and name.
62
75
  */
63
76
  export declare const getLayerArn: (config: Lambda.FunctionConfiguration, runtime: Runtime, region: string, settings?: InstrumentationSettings | undefined) => string;
77
+ export declare const getLayerNameWithVersion: (layerArn: string) => string | undefined;
64
78
  export declare const getLayers: (config: Lambda.FunctionConfiguration) => string[];
65
79
  /**
66
80
  * Call the aws-sdk Lambda api to get a Function given
@@ -99,3 +113,4 @@ export declare const isLambdaActive: (lambda: Lambda, config: Lambda.FunctionCon
99
113
  export declare const isSupportedRuntime: (runtime?: string | undefined) => runtime is Runtime;
100
114
  export declare const sentenceMatchesRegEx: (sentence: string, regex: RegExp) => RegExpMatchArray | null;
101
115
  export declare const updateLambdaFunctionConfigs: (lambda: Lambda, cloudWatch: CloudWatchLogs, configs: FunctionConfiguration[]) => Promise<void>;
116
+ export declare const willUpdateFunctionConfigs: (configs: FunctionConfiguration[]) => boolean;
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.updateLambdaFunctionConfigs = exports.sentenceMatchesRegEx = exports.isSupportedRuntime = exports.isLambdaActive = exports.getRegion = exports.getLambdaFunctionConfig = exports.getLayers = exports.getLayerArn = exports.getLambdaFunctionConfigs = exports.getLambdaFunctionConfigsFromRegex = exports.collectFunctionsByRegion = exports.coerceBoolean = exports.addLayerArn = void 0;
12
+ exports.willUpdateFunctionConfigs = exports.updateLambdaFunctionConfigs = exports.sentenceMatchesRegEx = exports.isSupportedRuntime = exports.isLambdaActive = exports.getRegion = exports.getLambdaFunctionConfig = exports.getLayers = exports.getLayerNameWithVersion = exports.getLayerArn = exports.getLambdaFunctionConfigs = exports.getLambdaFunctionConfigsFromRegex = exports.getAllLambdaFunctionConfigs = exports.isMissingDatadogEnvVars = exports.isMissingAnyDatadogApiKeyEnvVar = exports.isMissingDatadogSiteEnvVar = exports.isMissingAWSCredentials = exports.findLatestLayerVersion = exports.collectFunctionsByRegion = exports.coerceBoolean = exports.addLayerArn = void 0;
13
+ const aws_sdk_1 = require("aws-sdk");
13
14
  const constants_1 = require("../constants");
14
15
  const loggroup_1 = require("../loggroup");
15
16
  const tags_1 = require("../tags");
@@ -91,6 +92,87 @@ const collectFunctionsByRegion = (functions, defaultRegion) => {
91
92
  return groups;
92
93
  };
93
94
  exports.collectFunctionsByRegion = collectFunctionsByRegion;
95
+ /**
96
+ * Given a layer runtime, return its latest version.
97
+ *
98
+ * @param runtime the runtime of the layer.
99
+ * @param region the region where the layer is stored.
100
+ * @returns the latest version of the layer to find.
101
+ */
102
+ const findLatestLayerVersion = (runtime, region) => __awaiter(void 0, void 0, void 0, function* () {
103
+ let latestVersion = 0;
104
+ let searchStep = latestVersion > 0 ? 1 : 100;
105
+ let layerVersion = latestVersion + searchStep;
106
+ const account = region.startsWith('us-gov') ? constants_1.GOVCLOUD_LAYER_AWS_ACCOUNT : constants_1.DEFAULT_LAYER_AWS_ACCOUNT;
107
+ const layerName = constants_1.RUNTIME_LAYER_LOOKUP[runtime];
108
+ let foundLatestVersion = false;
109
+ const lambda = new aws_sdk_1.Lambda({ region });
110
+ while (!foundLatestVersion) {
111
+ try {
112
+ // Search next version
113
+ yield lambda
114
+ .getLayerVersion({
115
+ LayerName: `arn:aws:lambda:${region}:${account}:layer:${layerName}`,
116
+ VersionNumber: layerVersion,
117
+ })
118
+ .promise();
119
+ latestVersion = layerVersion;
120
+ // Increase layer version
121
+ layerVersion += searchStep;
122
+ }
123
+ catch (e) {
124
+ // Search step is too big, reset target to previous version
125
+ // with a smaller search step
126
+ if (searchStep > 1) {
127
+ layerVersion -= searchStep;
128
+ searchStep /= 10;
129
+ layerVersion += searchStep;
130
+ }
131
+ else {
132
+ // Search step is 1, current version was not found.
133
+ // It is likely that the last checked is the latest.
134
+ // Check the next version to be certain, since
135
+ // current version could've been deleted by accident.
136
+ try {
137
+ layerVersion += searchStep;
138
+ yield lambda
139
+ .getLayerVersion({
140
+ LayerName: `arn:aws:lambda:${region}:${account}:layer:${layerName}`,
141
+ VersionNumber: layerVersion,
142
+ })
143
+ .promise();
144
+ latestVersion = layerVersion;
145
+ // Continue the search if the next version does exist (unlikely event)
146
+ layerVersion += searchStep;
147
+ }
148
+ catch (e) {
149
+ // The next version doesn't exist either, so the previous version is indeed the latest
150
+ foundLatestVersion = true;
151
+ }
152
+ }
153
+ }
154
+ }
155
+ return latestVersion;
156
+ });
157
+ exports.findLatestLayerVersion = findLatestLayerVersion;
158
+ const isMissingAWSCredentials = () => process.env[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR] === undefined || process.env[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR] === undefined;
159
+ exports.isMissingAWSCredentials = isMissingAWSCredentials;
160
+ const isMissingDatadogSiteEnvVar = () => {
161
+ const site = process.env[constants_1.CI_SITE_ENV_VAR];
162
+ if (site !== undefined) {
163
+ return !constants_1.SITES.includes(site);
164
+ }
165
+ return true;
166
+ };
167
+ exports.isMissingDatadogSiteEnvVar = isMissingDatadogSiteEnvVar;
168
+ const isMissingAnyDatadogApiKeyEnvVar = () => !(process.env[constants_1.CI_API_KEY_ENV_VAR] ||
169
+ process.env[constants_1.CI_KMS_API_KEY_ENV_VAR] ||
170
+ process.env[constants_1.CI_API_KEY_SECRET_ARN_ENV_VAR]);
171
+ exports.isMissingAnyDatadogApiKeyEnvVar = isMissingAnyDatadogApiKeyEnvVar;
172
+ const isMissingDatadogEnvVars = () => exports.isMissingDatadogSiteEnvVar() || exports.isMissingAnyDatadogApiKeyEnvVar();
173
+ exports.isMissingDatadogEnvVars = isMissingDatadogEnvVars;
174
+ const getAllLambdaFunctionConfigs = (lambda) => __awaiter(void 0, void 0, void 0, function* () { return exports.getLambdaFunctionConfigsFromRegex(lambda, '.'); });
175
+ exports.getAllLambdaFunctionConfigs = getAllLambdaFunctionConfigs;
94
176
  /**
95
177
  * Given a Lambda instance and a regular expression,
96
178
  * returns all the Function Configurations that match.
@@ -163,7 +245,12 @@ const getLayerArn = (config, runtime, region, settings) => {
163
245
  return `arn:aws:lambda:${region}:${account}:layer:${layerName}`;
164
246
  };
165
247
  exports.getLayerArn = getLayerArn;
166
- const getLayers = (config) => { var _a; return ((_a = config.Layers) !== null && _a !== void 0 ? _a : []).map((layer) => { var _a; return (_a = layer.Arn) !== null && _a !== void 0 ? _a : ''; }); };
248
+ const getLayerNameWithVersion = (layerArn) => {
249
+ const [, , , , , , name, version] = layerArn.split(':');
250
+ return name && version ? `${name}:${version}` : undefined;
251
+ };
252
+ exports.getLayerNameWithVersion = getLayerNameWithVersion;
253
+ const getLayers = (config) => { var _a; return ((_a = config.Layers) !== null && _a !== void 0 ? _a : []).map((layer) => layer.Arn); };
167
254
  exports.getLayers = getLayers;
168
255
  /**
169
256
  * Call the aws-sdk Lambda api to get a Function given
@@ -250,6 +337,22 @@ const updateLambdaFunctionConfigs = (lambda, cloudWatch, configs) => __awaiter(v
250
337
  yield Promise.all(results);
251
338
  });
252
339
  exports.updateLambdaFunctionConfigs = updateLambdaFunctionConfigs;
340
+ const willUpdateFunctionConfigs = (configs) => {
341
+ var _a, _b, _c;
342
+ let willUpdate = false;
343
+ for (const config of configs) {
344
+ if (config.updateRequest !== undefined ||
345
+ ((_a = config.logGroupConfiguration) === null || _a === void 0 ? void 0 : _a.createLogGroupRequest) !== undefined ||
346
+ ((_b = config.logGroupConfiguration) === null || _b === void 0 ? void 0 : _b.deleteSubscriptionFilterRequest) !== undefined ||
347
+ ((_c = config.logGroupConfiguration) === null || _c === void 0 ? void 0 : _c.subscriptionFilterRequest) !== undefined ||
348
+ (config === null || config === void 0 ? void 0 : config.tagConfiguration) !== undefined) {
349
+ willUpdate = true;
350
+ break;
351
+ }
352
+ }
353
+ return willUpdate;
354
+ };
355
+ exports.willUpdateFunctionConfigs = willUpdateFunctionConfigs;
253
356
  /**
254
357
  * Waits for n ms
255
358
  *
@@ -4,4 +4,4 @@ import { FunctionConfiguration, InstrumentationSettings } from '../interfaces';
4
4
  export declare const getInstrumentedFunctionConfigs: (lambda: Lambda, cloudWatch: CloudWatchLogs, region: string, functionARNs: string[], settings: InstrumentationSettings) => Promise<FunctionConfiguration[]>;
5
5
  export declare const getInstrumentedFunctionConfig: (lambda: Lambda, cloudWatch: CloudWatchLogs, config: Lambda.FunctionConfiguration, region: string, settings: InstrumentationSettings) => Promise<FunctionConfiguration>;
6
6
  export declare const getInstrumentedFunctionConfigsFromRegEx: (lambda: Lambda, cloudWatch: CloudWatchLogs, region: string, pattern: string, settings: InstrumentationSettings) => Promise<FunctionConfiguration[]>;
7
- export declare const calculateUpdateRequest: (config: Lambda.FunctionConfiguration, settings: InstrumentationSettings, region: string, runtime: Runtime) => Lambda.UpdateFunctionConfigurationRequest | undefined;
7
+ export declare const calculateUpdateRequest: (config: Lambda.FunctionConfiguration, settings: InstrumentationSettings, region: string, runtime: Runtime) => Promise<Lambda.UpdateFunctionConfigurationRequest | undefined>;
@@ -31,7 +31,7 @@ const getInstrumentedFunctionConfig = (lambda, cloudWatch, config, region, setti
31
31
  throw Error(`Can't instrument ${functionARN}, runtime ${runtime} not supported`);
32
32
  }
33
33
  yield commons_1.isLambdaActive(lambda, config, functionARN);
34
- const updateRequest = exports.calculateUpdateRequest(config, settings, region, runtime);
34
+ const updateRequest = yield exports.calculateUpdateRequest(config, settings, region, runtime);
35
35
  let logGroupConfiguration;
36
36
  if (settings.forwarderARN !== undefined) {
37
37
  const logGroupName = `/aws/lambda/${config.FunctionName}`;
@@ -57,7 +57,7 @@ const getInstrumentedFunctionConfigsFromRegEx = (lambda, cloudWatch, region, pat
57
57
  return functionsToUpdate;
58
58
  });
59
59
  exports.getInstrumentedFunctionConfigsFromRegEx = getInstrumentedFunctionConfigsFromRegEx;
60
- const calculateUpdateRequest = (config, settings, region, runtime) => {
60
+ const calculateUpdateRequest = (config, settings, region, runtime) => __awaiter(void 0, void 0, void 0, function* () {
61
61
  var _a, _b, _c, _d;
62
62
  const oldEnvVars = Object.assign({}, (_a = config.Environment) === null || _a === void 0 ? void 0 : _a.Variables);
63
63
  const changedEnvVars = {};
@@ -116,6 +116,7 @@ const calculateUpdateRequest = (config, settings, region, runtime) => {
116
116
  changedEnvVars[constants_1.SITE_ENV_VAR] = 'datadoghq.com';
117
117
  }
118
118
  const environmentVarsTupleArray = [
119
+ ['captureLambdaPayload', constants_1.CAPTURE_LAMBDA_PAYLOAD_ENV_VAR],
119
120
  ['environment', constants_1.ENVIRONMENT_ENV_VAR],
120
121
  ['extraTags', constants_1.EXTRA_TAGS_ENV_VAR],
121
122
  ['mergeXrayTraces', constants_1.MERGE_XRAY_TRACES_ENV_VAR],
@@ -129,6 +130,7 @@ const calculateUpdateRequest = (config, settings, region, runtime) => {
129
130
  changedEnvVars[environmentVar] = settings[key].toString();
130
131
  }
131
132
  }
133
+ // Skip adding DD_FLUSH_TO_LOGS when using Extension
132
134
  const isUsingExtension = settings.extensionVersion !== undefined;
133
135
  if (!isUsingExtension &&
134
136
  settings.flushMetricsToLogs !== undefined &&
@@ -153,13 +155,21 @@ const calculateUpdateRequest = (config, settings, region, runtime) => {
153
155
  const lambdaLibraryLayerArn = commons_1.getLayerArn(config, config.Runtime, region, settings);
154
156
  const lambraLibraryLayerName = constants_1.RUNTIME_LAYER_LOOKUP[runtime];
155
157
  let fullLambdaLibraryLayerARN;
156
- if (settings.layerVersion !== undefined) {
157
- fullLambdaLibraryLayerARN = `${lambdaLibraryLayerArn}:${settings.layerVersion}`;
158
+ if (settings.layerVersion !== undefined || settings.interactive) {
159
+ let layerVersion = settings.layerVersion;
160
+ if (settings.interactive && !settings.layerVersion) {
161
+ layerVersion = yield commons_1.findLatestLayerVersion(config.Runtime, region);
162
+ }
163
+ fullLambdaLibraryLayerARN = `${lambdaLibraryLayerArn}:${layerVersion}`;
158
164
  }
159
165
  const lambdaExtensionLayerArn = commons_1.getLayerArn(config, constants_1.EXTENSION_LAYER_KEY, region, settings);
160
166
  let fullExtensionLayerARN;
161
- if (settings.extensionVersion !== undefined) {
162
- fullExtensionLayerARN = `${lambdaExtensionLayerArn}:${settings.extensionVersion}`;
167
+ if (settings.extensionVersion !== undefined || settings.interactive) {
168
+ let extensionVersion = settings.extensionVersion;
169
+ if (settings.interactive && !settings.extensionVersion) {
170
+ extensionVersion = yield commons_1.findLatestLayerVersion(constants_1.EXTENSION_LAYER_KEY, region);
171
+ }
172
+ fullExtensionLayerARN = `${lambdaExtensionLayerArn}:${extensionVersion}`;
163
173
  }
164
174
  let layerARNs = commons_1.getLayers(config);
165
175
  const originalLayerARNs = layerARNs;
@@ -182,5 +192,5 @@ const calculateUpdateRequest = (config, settings, region, runtime) => {
182
192
  }
183
193
  });
184
194
  return needsUpdate ? updateRequest : undefined;
185
- };
195
+ });
186
196
  exports.calculateUpdateRequest = calculateUpdateRequest;
@@ -83,6 +83,7 @@ const calculateUpdateRequest = (config, runtime) => {
83
83
  constants_1.API_KEY_SECRET_ARN_ENV_VAR,
84
84
  constants_1.KMS_API_KEY_ENV_VAR,
85
85
  constants_1.SITE_ENV_VAR,
86
+ constants_1.CAPTURE_LAMBDA_PAYLOAD_ENV_VAR,
86
87
  constants_1.ENVIRONMENT_ENV_VAR,
87
88
  constants_1.EXTRA_TAGS_ENV_VAR,
88
89
  constants_1.FLUSH_TO_LOG_ENV_VAR,
@@ -1,5 +1,6 @@
1
1
  import { Command } from 'clipanion';
2
2
  export declare class InstrumentCommand extends Command {
3
+ private captureLambdaPayload?;
3
4
  private config;
4
5
  private configPath?;
5
6
  private dryRun;
@@ -9,6 +10,7 @@ export declare class InstrumentCommand extends Command {
9
10
  private flushMetricsToLogs?;
10
11
  private forwarder?;
11
12
  private functions;
13
+ private interactive;
12
14
  private layerAWSAccount?;
13
15
  private layerVersion?;
14
16
  private logLevel?;
@@ -19,74 +19,116 @@ const upload_1 = require("../git-metadata/upload");
19
19
  const constants_1 = require("./constants");
20
20
  const commons_1 = require("./functions/commons");
21
21
  const instrument_1 = require("./functions/instrument");
22
+ const prompt_1 = require("./prompt");
22
23
  class InstrumentCommand extends clipanion_1.Command {
23
24
  constructor() {
24
25
  super(...arguments);
25
26
  this.config = {
26
27
  functions: [],
27
- region: process.env.AWS_DEFAULT_REGION,
28
+ region: process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR],
28
29
  tracing: 'true',
29
30
  };
30
31
  this.dryRun = false;
31
32
  this.functions = [];
33
+ this.interactive = false;
32
34
  this.sourceCodeIntegration = false;
33
35
  }
34
36
  execute() {
37
+ var _a, _b, _c;
35
38
  return __awaiter(this, void 0, void 0, function* () {
36
39
  const lambdaConfig = { lambda: this.config };
37
40
  this.config = (yield utils_1.parseConfigFile(lambdaConfig, this.configPath)).lambda;
41
+ let hasSpecifiedFunctions = this.functions.length !== 0 || this.config.functions.length !== 0;
42
+ if (this.interactive) {
43
+ try {
44
+ if (commons_1.isMissingAWSCredentials()) {
45
+ this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No existing AWS credentials found, let's set them up!\n`);
46
+ yield prompt_1.requestAWSCredentials();
47
+ }
48
+ if (commons_1.isMissingDatadogEnvVars()) {
49
+ this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} Configure Datadog settings.\n`);
50
+ yield prompt_1.requestDatadogEnvVars();
51
+ }
52
+ }
53
+ catch (e) {
54
+ this.context.stdout.write(`${chalk_1.red('[Error]')} ${e}\n`);
55
+ return 1;
56
+ }
57
+ const region = (_b = (_a = this.region) !== null && _a !== void 0 ? _a : this.config.region) !== null && _b !== void 0 ? _b : process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR];
58
+ this.region = region;
59
+ // If user doesn't specify functions, allow them
60
+ // to select from all of the functions from the
61
+ // requested region.
62
+ if (!hasSpecifiedFunctions) {
63
+ try {
64
+ const lambda = new aws_sdk_1.Lambda({ region });
65
+ this.context.stdout.write('Fetching Lambda functions, this might take a while.\n');
66
+ const functionNames = (_c = (yield commons_1.getAllLambdaFunctionConfigs(lambda)).map((config) => config.FunctionName).sort()) !== null && _c !== void 0 ? _c : [];
67
+ if (functionNames.length === 0) {
68
+ this.context.stdout.write(`${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified region.\n`);
69
+ return 1;
70
+ }
71
+ const functions = yield prompt_1.requestFunctionSelection(functionNames);
72
+ this.functions = functions;
73
+ }
74
+ catch (err) {
75
+ this.context.stdout.write(`${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. ${err}\n`);
76
+ return 1;
77
+ }
78
+ }
79
+ }
38
80
  const settings = this.getSettings();
39
81
  if (settings === undefined) {
40
82
  return 1;
41
83
  }
42
- const hasSpecifiedFuntions = this.functions.length !== 0 || this.config.functions.length !== 0;
84
+ hasSpecifiedFunctions = this.functions.length !== 0 || this.config.functions.length !== 0;
43
85
  const hasSpecifiedRegExPattern = this.regExPattern !== undefined && this.regExPattern !== '';
44
- if (!hasSpecifiedFuntions && !hasSpecifiedRegExPattern) {
45
- this.context.stdout.write('No functions specified for instrumentation.\n');
86
+ if (!hasSpecifiedFunctions && !hasSpecifiedRegExPattern) {
87
+ this.context.stdout.write(`${chalk_1.red('[Error]')} No functions specified for instrumentation.\n`);
46
88
  return 1;
47
89
  }
48
90
  if (settings.extensionVersion && settings.forwarderARN) {
49
- this.context.stdout.write('"extensionVersion" and "forwarder" should not be used at the same time.\n');
91
+ this.context.stdout.write(`${chalk_1.red('[Error]')} "extensionVersion" and "forwarder" should not be used at the same time.\n`);
50
92
  return 1;
51
93
  }
52
94
  if (this.sourceCodeIntegration) {
53
95
  if (!process.env.DATADOG_API_KEY) {
54
- this.context.stdout.write('Missing DATADOG_API_KEY in your environment\n');
96
+ this.context.stdout.write(`${chalk_1.red('[Error]')} Missing DATADOG_API_KEY in your environment\n`);
55
97
  return 1;
56
98
  }
57
99
  try {
58
100
  yield this.getGitDataAndUpload(settings);
59
101
  }
60
102
  catch (err) {
61
- this.context.stdout.write(`${err}\n`);
103
+ this.context.stdout.write(`${chalk_1.red('[Error]')} ${err}\n`);
62
104
  return 1;
63
105
  }
64
106
  }
65
107
  const configGroups = [];
66
108
  if (hasSpecifiedRegExPattern) {
67
- if (hasSpecifiedFuntions) {
109
+ if (hasSpecifiedFunctions) {
68
110
  const usedCommand = this.functions.length !== 0 ? '"--functions"' : 'Functions in config file';
69
- this.context.stdout.write(`${usedCommand} and "--functions-regex" should not be used at the same time.\n`);
111
+ this.context.stdout.write(`${chalk_1.red('[Error]')} ${usedCommand} and "--functions-regex" should not be used at the same time.\n`);
70
112
  return 1;
71
113
  }
72
114
  if (this.regExPattern.match(':')) {
73
- this.context.stdout.write(`"--functions-regex" isn't meant to be used with ARNs.\n`);
115
+ this.context.stdout.write(`${chalk_1.red('[Error]')} "--functions-regex" isn't meant to be used with ARNs.\n`);
74
116
  return 1;
75
117
  }
76
118
  const region = this.region || this.config.region;
77
119
  if (!region) {
78
- this.context.stdout.write('No default region specified. Use `-r`, `--region`.');
120
+ this.context.stdout.write(`${chalk_1.red('[Error]')} No default region specified. Use \`-r\`, \`--region\`.\n`);
79
121
  return 1;
80
122
  }
81
123
  try {
82
124
  const cloudWatchLogs = new aws_sdk_1.CloudWatchLogs({ region });
83
125
  const lambda = new aws_sdk_1.Lambda({ region });
84
- this.context.stdout.write('Fetching lambda functions, this might take a while.\n');
126
+ this.context.stdout.write('Fetching Lambda functions, this might take a while.\n');
85
127
  const configs = yield instrument_1.getInstrumentedFunctionConfigsFromRegEx(lambda, cloudWatchLogs, region, this.regExPattern, settings);
86
128
  configGroups.push({ configs, lambda, cloudWatchLogs, region: region });
87
129
  }
88
130
  catch (err) {
89
- this.context.stdout.write(`Couldn't fetch lambda functions. ${err}\n`);
131
+ this.context.stdout.write(`${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. ${err}\n`);
90
132
  return 1;
91
133
  }
92
134
  }
@@ -96,7 +138,7 @@ class InstrumentCommand extends clipanion_1.Command {
96
138
  functionGroups = commons_1.collectFunctionsByRegion(this.functions.length !== 0 ? this.functions : this.config.functions, this.region || this.config.region);
97
139
  }
98
140
  catch (err) {
99
- this.context.stdout.write(`Couldn't group functions. ${err}`);
141
+ this.context.stdout.write(`${chalk_1.red('[Error]')} Couldn't group functions. ${err}`);
100
142
  return 1;
101
143
  }
102
144
  for (const [region, functionList] of Object.entries(functionGroups)) {
@@ -107,7 +149,7 @@ class InstrumentCommand extends clipanion_1.Command {
107
149
  configGroups.push({ configs, lambda, cloudWatchLogs, region });
108
150
  }
109
151
  catch (err) {
110
- this.context.stdout.write(`Couldn't fetch lambda functions. ${err}\n`);
152
+ this.context.stdout.write(`${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. ${err}\n`);
111
153
  return 1;
112
154
  }
113
155
  }
@@ -117,12 +159,21 @@ class InstrumentCommand extends clipanion_1.Command {
117
159
  if (this.dryRun || configList.length === 0) {
118
160
  return 0;
119
161
  }
162
+ const willUpdate = commons_1.willUpdateFunctionConfigs(configList);
163
+ if (this.interactive && willUpdate) {
164
+ this.context.stdout.write(`${chalk_1.yellow('[!]')} Confirmation needed.\n`);
165
+ const isConfirmed = yield prompt_1.requestChangesConfirmation('Do you want to apply the changes?');
166
+ if (!isConfirmed) {
167
+ return 0;
168
+ }
169
+ this.context.stdout.write(`${chalk_1.yellow('[!]')} Instrumenting functions.\n`);
170
+ }
120
171
  const promises = Object.values(configGroups).map((group) => commons_1.updateLambdaFunctionConfigs(group.lambda, group.cloudWatchLogs, group.configs));
121
172
  try {
122
173
  yield Promise.all(promises);
123
174
  }
124
175
  catch (err) {
125
- this.context.stdout.write(`Failure during update. ${err}\n`);
176
+ this.context.stdout.write(`${chalk_1.red('[Error]')} Failure during update. ${err}\n`);
126
177
  return 1;
127
178
  }
128
179
  return 0;
@@ -170,7 +221,7 @@ class InstrumentCommand extends clipanion_1.Command {
170
221
  });
171
222
  }
172
223
  getSettings() {
173
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
224
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
174
225
  const layerVersionStr = (_a = this.layerVersion) !== null && _a !== void 0 ? _a : this.config.layerVersion;
175
226
  const extensionVersionStr = (_b = this.extensionVersion) !== null && _b !== void 0 ? _b : this.config.extensionVersion;
176
227
  const layerAWSAccount = (_c = this.layerAWSAccount) !== null && _c !== void 0 ? _c : this.config.layerAWSAccount;
@@ -192,9 +243,10 @@ class InstrumentCommand extends clipanion_1.Command {
192
243
  return;
193
244
  }
194
245
  const stringBooleansMap = {
195
- flushMetricsToLogs: (_e = this.flushMetricsToLogs) !== null && _e !== void 0 ? _e : this.config.flushMetricsToLogs,
196
- mergeXrayTraces: (_f = this.mergeXrayTraces) !== null && _f !== void 0 ? _f : this.config.mergeXrayTraces,
197
- tracing: (_g = this.tracing) !== null && _g !== void 0 ? _g : this.config.tracing,
246
+ captureLambdaPayload: (_e = this.captureLambdaPayload) !== null && _e !== void 0 ? _e : this.config.captureLambdaPayload,
247
+ flushMetricsToLogs: (_f = this.flushMetricsToLogs) !== null && _f !== void 0 ? _f : this.config.flushMetricsToLogs,
248
+ mergeXrayTraces: (_g = this.mergeXrayTraces) !== null && _g !== void 0 ? _g : this.config.mergeXrayTraces,
249
+ tracing: (_h = this.tracing) !== null && _h !== void 0 ? _h : this.config.tracing,
198
250
  };
199
251
  for (const [stringBoolean, value] of Object.entries(stringBooleansMap)) {
200
252
  if (!['true', 'false', undefined].includes(value === null || value === void 0 ? void 0 : value.toString().toLowerCase())) {
@@ -202,13 +254,15 @@ class InstrumentCommand extends clipanion_1.Command {
202
254
  return;
203
255
  }
204
256
  }
257
+ const captureLambdaPayload = commons_1.coerceBoolean(false, this.captureLambdaPayload, this.config.captureLambdaPayload);
205
258
  const flushMetricsToLogs = commons_1.coerceBoolean(true, this.flushMetricsToLogs, this.config.flushMetricsToLogs);
206
259
  const mergeXrayTraces = commons_1.coerceBoolean(false, this.mergeXrayTraces, this.config.mergeXrayTraces);
207
260
  const tracingEnabled = commons_1.coerceBoolean(true, this.tracing, this.config.tracing);
208
- const logLevel = (_h = this.logLevel) !== null && _h !== void 0 ? _h : this.config.logLevel;
209
- const service = (_j = this.service) !== null && _j !== void 0 ? _j : this.config.service;
210
- const environment = (_k = this.environment) !== null && _k !== void 0 ? _k : this.config.environment;
211
- const version = (_l = this.version) !== null && _l !== void 0 ? _l : this.config.version;
261
+ const interactive = commons_1.coerceBoolean(false, this.interactive, this.config.interactive);
262
+ const logLevel = (_j = this.logLevel) !== null && _j !== void 0 ? _j : this.config.logLevel;
263
+ const service = (_k = this.service) !== null && _k !== void 0 ? _k : this.config.service;
264
+ const environment = (_l = this.environment) !== null && _l !== void 0 ? _l : this.config.environment;
265
+ const version = (_m = this.version) !== null && _m !== void 0 ? _m : this.config.version;
212
266
  const tagsMap = {
213
267
  environment,
214
268
  service,
@@ -225,17 +279,19 @@ class InstrumentCommand extends clipanion_1.Command {
225
279
  const plural = tagsMissing.length > 1;
226
280
  this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[Warning]'))} The ${tags} tag${plural ? 's have' : ' has'} not been configured. Learn more about Datadog unified service tagging: ${chalk_1.underline(chalk_1.blueBright('https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging/#serverless-environment.'))}\n`);
227
281
  }
228
- const extraTags = (_o = (_m = this.extraTags) === null || _m === void 0 ? void 0 : _m.toLowerCase()) !== null && _o !== void 0 ? _o : (_p = this.config.extraTags) === null || _p === void 0 ? void 0 : _p.toLowerCase();
282
+ const extraTags = (_p = (_o = this.extraTags) === null || _o === void 0 ? void 0 : _o.toLowerCase()) !== null && _p !== void 0 ? _p : (_q = this.config.extraTags) === null || _q === void 0 ? void 0 : _q.toLowerCase();
229
283
  if (extraTags && !commons_1.sentenceMatchesRegEx(extraTags, constants_1.EXTRA_TAGS_REG_EXP)) {
230
284
  this.context.stdout.write('Extra tags do not comply with the <key>:<value> array.\n');
231
285
  return;
232
286
  }
233
287
  return {
288
+ captureLambdaPayload,
234
289
  environment,
235
290
  extensionVersion,
236
291
  extraTags,
237
292
  flushMetricsToLogs,
238
293
  forwarderARN,
294
+ interactive,
239
295
  layerAWSAccount,
240
296
  layerVersion,
241
297
  logLevel,
@@ -246,20 +302,9 @@ class InstrumentCommand extends clipanion_1.Command {
246
302
  };
247
303
  }
248
304
  printPlannedActions(configs) {
249
- var _a, _b, _c;
250
305
  const prefix = this.dryRun ? chalk_1.bold(chalk_1.cyan('[Dry Run] ')) : '';
251
- let anyUpdates = false;
252
- for (const config of configs) {
253
- if (config.updateRequest !== undefined ||
254
- ((_a = config.logGroupConfiguration) === null || _a === void 0 ? void 0 : _a.createLogGroupRequest) !== undefined ||
255
- ((_b = config.logGroupConfiguration) === null || _b === void 0 ? void 0 : _b.deleteSubscriptionFilterRequest) !== undefined ||
256
- ((_c = config.logGroupConfiguration) === null || _c === void 0 ? void 0 : _c.subscriptionFilterRequest) !== undefined ||
257
- (config === null || config === void 0 ? void 0 : config.tagConfiguration) !== undefined) {
258
- anyUpdates = true;
259
- break;
260
- }
261
- }
262
- if (!anyUpdates) {
306
+ const willUpdate = commons_1.willUpdateFunctionConfigs(configs);
307
+ if (!willUpdate) {
263
308
  this.context.stdout.write(`\n${prefix}No updates will be applied\n`);
264
309
  return;
265
310
  }
@@ -267,6 +312,12 @@ class InstrumentCommand extends clipanion_1.Command {
267
312
  this.context.stdout.write(`\n${chalk_1.bold(chalk_1.yellow('[!]'))} Functions to be updated:\n`);
268
313
  for (const config of configs) {
269
314
  this.context.stdout.write(`\t- ${chalk_1.bold(config.functionARN)}\n`);
315
+ // Later, we should inform which layer is the latest.
316
+ if (this.interactive) {
317
+ if (!this.extensionVersion || !this.extensionVersion) {
318
+ this.context.stdout.write(`\t${chalk_1.bold(chalk_1.yellow('[Warning]'))} At least one latest layer version is being used. Ensure to lock in versions for production applications using \`--layerVersion\` and \`--extensionVersion\`.\n`);
319
+ }
320
+ }
270
321
  }
271
322
  this.context.stdout.write(`\n${prefix}Will apply the following updates:\n`);
272
323
  for (const config of configs) {
@@ -302,20 +353,22 @@ class InstrumentCommand extends clipanion_1.Command {
302
353
  exports.InstrumentCommand = InstrumentCommand;
303
354
  InstrumentCommand.addPath('lambda', 'instrument');
304
355
  InstrumentCommand.addOption('functions', clipanion_1.Command.Array('-f,--function'));
305
- InstrumentCommand.addOption('regExPattern', clipanion_1.Command.String('--functions-regex'));
356
+ InstrumentCommand.addOption('regExPattern', clipanion_1.Command.String('--functions-regex,--functionsRegex'));
306
357
  InstrumentCommand.addOption('region', clipanion_1.Command.String('-r,--region'));
307
- InstrumentCommand.addOption('extensionVersion', clipanion_1.Command.String('-e,--extensionVersion'));
308
- InstrumentCommand.addOption('layerVersion', clipanion_1.Command.String('-v,--layerVersion'));
309
- InstrumentCommand.addOption('layerAWSAccount', clipanion_1.Command.String('-a,--layerAccount', { hidden: true }));
358
+ InstrumentCommand.addOption('extensionVersion', clipanion_1.Command.String('-e,--extension-version,--extensionVersion'));
359
+ InstrumentCommand.addOption('layerVersion', clipanion_1.Command.String('-v,--layer-version,--layerVersion'));
360
+ InstrumentCommand.addOption('layerAWSAccount', clipanion_1.Command.String('-a,--layer-account,--layerAccount', { hidden: true }));
310
361
  InstrumentCommand.addOption('tracing', clipanion_1.Command.String('--tracing'));
311
- InstrumentCommand.addOption('mergeXrayTraces', clipanion_1.Command.String('--mergeXrayTraces'));
312
- InstrumentCommand.addOption('flushMetricsToLogs', clipanion_1.Command.String('--flushMetricsToLogs'));
362
+ InstrumentCommand.addOption('mergeXrayTraces', clipanion_1.Command.String('--merge-xray-traces,--mergeXrayTraces'));
363
+ InstrumentCommand.addOption('flushMetricsToLogs', clipanion_1.Command.String('--flush-metrics-to-logs,--flushMetricsToLogs'));
313
364
  InstrumentCommand.addOption('dryRun', clipanion_1.Command.Boolean('-d,--dry'));
314
365
  InstrumentCommand.addOption('configPath', clipanion_1.Command.String('--config'));
315
366
  InstrumentCommand.addOption('forwarder', clipanion_1.Command.String('--forwarder'));
316
- InstrumentCommand.addOption('logLevel', clipanion_1.Command.String('--logLevel'));
367
+ InstrumentCommand.addOption('logLevel', clipanion_1.Command.String('--log-level,--logLevel'));
317
368
  InstrumentCommand.addOption('service', clipanion_1.Command.String('--service'));
318
369
  InstrumentCommand.addOption('environment', clipanion_1.Command.String('--env'));
319
370
  InstrumentCommand.addOption('version', clipanion_1.Command.String('--version'));
320
- InstrumentCommand.addOption('extraTags', clipanion_1.Command.String('--extra-tags'));
321
- InstrumentCommand.addOption('sourceCodeIntegration', clipanion_1.Command.Boolean('-s,--source-code-integration'));
371
+ InstrumentCommand.addOption('extraTags', clipanion_1.Command.String('--extra-tags,--extraTags'));
372
+ InstrumentCommand.addOption('sourceCodeIntegration', clipanion_1.Command.Boolean('-s,--source-code-integration,--sourceCodeIntegration'));
373
+ InstrumentCommand.addOption('interactive', clipanion_1.Command.Boolean('-i,--interactive'));
374
+ InstrumentCommand.addOption('captureLambdaPayload', clipanion_1.Command.String('--capture-lambda-payload,--captureLambdaPayload'));
@@ -4,12 +4,14 @@ import { CloudWatchLogs, Lambda } from 'aws-sdk';
4
4
  * the CLI in order to instrument properly.
5
5
  */
6
6
  export interface LambdaConfigOptions {
7
+ captureLambdaPayload?: string;
7
8
  environment?: string;
8
9
  extensionVersion?: string;
9
10
  extraTags?: string;
10
11
  flushMetricsToLogs?: string;
11
12
  forwarder?: string;
12
13
  functions: string[];
14
+ interactive?: boolean;
13
15
  layerAWSAccount?: string;
14
16
  layerVersion?: string;
15
17
  logLevel?: string;
@@ -36,9 +38,11 @@ export interface FunctionConfiguration {
36
38
  * lambda to be instrumented.
37
39
  */
38
40
  export interface InstrumentationSettings extends InstrumentationTags {
41
+ captureLambdaPayload?: boolean;
39
42
  extensionVersion?: number;
40
43
  flushMetricsToLogs: boolean;
41
44
  forwarderARN?: string;
45
+ interactive?: boolean;
42
46
  layerAWSAccount?: string;
43
47
  layerVersion?: number;
44
48
  logLevel?: string;
@@ -0,0 +1,9 @@
1
+ import { CheckboxQuestion, ConfirmQuestion, InputQuestion, ListQuestion } from 'inquirer';
2
+ export declare const datadogApiKeyTypeQuestion: (datadogSite: string) => ListQuestion;
3
+ export declare const datadogEnvVarsQuestions: (datadogApiKeyType: Record<string, any>) => InputQuestion;
4
+ export declare const confirmationQuestion: (message: string) => ConfirmQuestion;
5
+ export declare const functionSelectionQuestion: (functionNames: string[]) => CheckboxQuestion;
6
+ export declare const requestAWSCredentials: () => Promise<void>;
7
+ export declare const requestDatadogEnvVars: () => Promise<void>;
8
+ export declare const requestChangesConfirmation: (message: string) => Promise<any>;
9
+ export declare const requestFunctionSelection: (functionNames: string[]) => Promise<any>;