Skip to content

Commit b7ed5de

Browse files
authored
Validate API and APP key for monitor deployment (#675)
* Validate API and APP key for monitor deployment When packaging then deploying as two separate steps, validating that the API and APP keys were present didn't happen before we tried to deploy. This caused the monitors deployment to fail silently without logging anything. Add the same validation that is done already to the beforeDeploy step as well. Github Issue: #494 JIRA: https://datadoghq.atlassian.net/browse/SVLS-5045 * fix lint
1 parent a9a86c6 commit b7ed5de

2 files changed

Lines changed: 92 additions & 0 deletions

File tree

src/index.spec.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,84 @@ describe("ServerlessPlugin", () => {
971971
expect(threwError).toBe(false);
972972
});
973973

974+
describe("beforeDeploy", () => {
975+
afterEach(() => {
976+
mock.restore();
977+
});
978+
979+
it("logs and does not throw when validateConfiguration fails and failOnError is false", async () => {
980+
mock({});
981+
let logs = "";
982+
const serverless = {
983+
cli: { log: (m: string) => (logs += m) },
984+
getProvider: (_name: string) => awsMock(),
985+
service: {
986+
getServiceName: () => "dev",
987+
provider: { region: "us-east-1" },
988+
functions: {},
989+
custom: {
990+
datadog: {
991+
site: "datadogehq.com", // invalid to trigger validateConfiguration error
992+
failOnError: false,
993+
},
994+
},
995+
},
996+
};
997+
const plugin = new ServerlessPlugin(serverless, {});
998+
await expect(plugin.hooks["before:deploy:deploy"]()).resolves.not.toThrow();
999+
expect(logs).toContain("Warning: Invalid site URL. Must be one of");
1000+
});
1001+
1002+
it("throws when validateConfiguration fails and failOnError is true", async () => {
1003+
mock({});
1004+
const serverless = {
1005+
cli: { log: () => {} },
1006+
getProvider: (_name: string) => awsMock(),
1007+
service: {
1008+
getServiceName: () => "dev",
1009+
provider: { region: "us-east-1" },
1010+
functions: {},
1011+
custom: {
1012+
datadog: {
1013+
site: "datadogehq.com", // invalid to trigger validateConfiguration error
1014+
failOnError: true,
1015+
},
1016+
},
1017+
},
1018+
};
1019+
const plugin = new ServerlessPlugin(serverless, {});
1020+
await expect(plugin.hooks["before:deploy:deploy"]()).rejects.toThrow("Warning: Invalid site URL. Must be one of");
1021+
});
1022+
1023+
it("logs and does not throw when monitors are enabled but API or APP key is missing and failOnError is false", async () => {
1024+
process.env = {};
1025+
mock({});
1026+
let logs = "";
1027+
const serverless = {
1028+
cli: { log: (m: string) => (logs += m) },
1029+
getProvider: (_name: string) => awsMock(),
1030+
service: {
1031+
getServiceName: () => "dev",
1032+
provider: { region: "us-east-1" },
1033+
functions: {},
1034+
custom: {
1035+
datadog: {
1036+
monitors: true,
1037+
testingMode: false,
1038+
integrationTesting: false,
1039+
failOnError: false,
1040+
addExtension: false,
1041+
},
1042+
},
1043+
},
1044+
};
1045+
const plugin = new ServerlessPlugin(serverless, {});
1046+
await expect(plugin.hooks["before:deploy:deploy"]()).resolves.not.toThrow();
1047+
expect(logs).toContain(
1048+
"When `monitors` is enabled, `DATADOG_API_KEY` and `DATADOG_APP_KEY` environment variables must be set.",
1049+
);
1050+
});
1051+
});
9741052
it("throws an error if not all keys required by monitors are defined", async () => {
9751053
process.env = {};
9761054
mock({});

src/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ module.exports = class ServerlessPlugin {
7878
"before:deploy:function:packageFunction": this.beforePackageFunction.bind(this),
7979
"before:offline:start:init": this.beforePackageFunction.bind(this),
8080
"before:step-functions-offline:start": this.beforePackageFunction.bind(this),
81+
"before:deploy:deploy": this.beforeDeploy.bind(this),
8182
"after:deploy:deploy": this.afterDeploy.bind(this),
8283
"before:package:finalize": this.afterPackageFunction.bind(this),
8384
};
@@ -364,6 +365,19 @@ This is expected if you only deploy part of the stack.`);
364365
}
365366
}
366367

368+
private async beforeDeploy(): Promise<void> {
369+
const config = getConfig(this.serverless.service);
370+
if (config.enabled === false) return;
371+
try {
372+
validateConfiguration(config);
373+
} catch (error) {
374+
this.serverless.cli.log(`Error occurred when validating configuration: ${error}`);
375+
if (config.failOnError) {
376+
throw error;
377+
}
378+
}
379+
}
380+
367381
private async afterDeploy(): Promise<void> {
368382
const config = getConfig(this.serverless.service);
369383
const custom = this.serverless.service.custom ?? {};

0 commit comments

Comments
 (0)