Skip to content

Commit 3332fed

Browse files
committed
[installer-tests] add slack webhook
1 parent 1dfc500 commit 3332fed

File tree

7 files changed

+250
-19
lines changed

7 files changed

+250
-19
lines changed

.werft/aks-installer-tests.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@ pod:
5858
valueFrom:
5959
fieldRef:
6060
fieldPath: spec.nodeName
61+
- name: IDE_SLACK_NOTIFICATION_PATH
62+
valueFrom:
63+
secretKeyRef:
64+
name: slack-webhook-urls
65+
key: ide_jobs
66+
- name: WS_SLACK_NOTIFICATION_PATH
67+
valueFrom:
68+
secretKeyRef:
69+
name: slack-webhook-urls
70+
key: workspace_jobs
71+
- name: SH_SLACK_NOTIFICATION_PATH
72+
valueFrom:
73+
secretKeyRef:
74+
name: slack-webhook-urls
75+
key: self_hosted_jobs
6176
command:
6277
- bash
6378
- -c

.werft/eks-installer-tests.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ pod:
5353
valueFrom:
5454
fieldRef:
5555
fieldPath: spec.nodeName
56+
- name: IDE_SLACK_NOTIFICATION_PATH
57+
valueFrom:
58+
secretKeyRef:
59+
name: slack-webhook-urls
60+
key: ide_jobs
61+
- name: WS_SLACK_NOTIFICATION_PATH
62+
valueFrom:
63+
secretKeyRef:
64+
name: slack-webhook-urls
65+
key: workspace_jobs
66+
- name: SH_SLACK_NOTIFICATION_PATH
67+
valueFrom:
68+
secretKeyRef:
69+
name: slack-webhook-urls
70+
key: self_hosted_jobs
5671
command:
5772
- bash
5873
- -c

.werft/gke-installer-tests.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ pod:
4040
valueFrom:
4141
fieldRef:
4242
fieldPath: spec.nodeName
43+
- name: IDE_SLACK_NOTIFICATION_PATH
44+
valueFrom:
45+
secretKeyRef:
46+
name: slack-webhook-urls
47+
key: ide_jobs
48+
- name: WS_SLACK_NOTIFICATION_PATH
49+
valueFrom:
50+
secretKeyRef:
51+
name: slack-webhook-urls
52+
key: workspace_jobs
53+
- name: SH_SLACK_NOTIFICATION_PATH
54+
valueFrom:
55+
secretKeyRef:
56+
name: slack-webhook-urls
57+
key: self_hosted_jobs
4358
command:
4459
- bash
4560
- -c

.werft/installer-tests.ts

Lines changed: 174 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as fs from "fs";
2+
import * as https from "https";
23
import { join } from "path";
34
import { exec } from "./util/shell";
45
import { Werft } from "./util/werft";
@@ -16,6 +17,11 @@ const upgrade: string = annotations.upgrade || "false"; // setting to true will
1617
const skipTests: string = annotations.skipTests || "false"; // setting to true skips the integration tests
1718
const deps: string = annotations.deps || ""; // options: ["external", "internal"] setting to `external` will ensure that all resource dependencies(storage, db, registry) will be external. if unset, a random selection will be used
1819

20+
const slackHook: { [name: string]: string} = {
21+
"self-hosted-jobs": process.env.SH_SLACK_NOTIFICATION_PATH.trim(),
22+
"workspace-jobs": process.env.WS_SLACK_NOTIFICATION_PATH.trim(),
23+
"ide-jobs": process.env.IDE_SLACK_NOTIFICATION_PATH.trim(),
24+
}
1925

2026
const makefilePath: string = join("install/tests");
2127

@@ -25,6 +31,7 @@ interface InfraConfig {
2531
phase: string;
2632
makeTarget: string;
2733
description: string;
34+
slackhook?: string;
2835
}
2936

3037
interface TestConfig {
@@ -35,13 +42,14 @@ interface TestConfig {
3542

3643
const k8s_version: string = randK8sVersion(testConfig)
3744
const os_version: string = randOsVersion() // applicable only for k3s
45+
const op: string = preview == "true" ? "Preview" : "Test"
3846

3947
// Each of the TEST_CONFIGURATIONS define an integration test end-to-end
4048
// It should be a combination of multiple INFRA_PHASES, order of PHASES slice is important
4149
const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
4250
STANDARD_GKE_TEST: {
4351
CLOUD: "gcp",
44-
DESCRIPTION: `Deploy Gitpod on GKE(version ${k8s_version})`,
52+
DESCRIPTION: `${op} Gitpod on GKE managed cluster(version ${k8s_version})`,
4553
PHASES: [
4654
"STANDARD_GKE_CLUSTER",
4755
"CERT_MANAGER",
@@ -54,8 +62,7 @@ const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
5462
},
5563
STANDARD_K3S_TEST: {
5664
CLOUD: "gcp", // the cloud provider is still GCP
57-
DESCRIPTION:
58-
`Deploy Gitpod on a K3s cluster(version ${k8s_version}), on a GCP instance with ubuntu ${os_version}`,
65+
DESCRIPTION: `${op} Gitpod on a K3s cluster(version ${k8s_version}) on a GCP instance with ubuntu ${os_version}`,
5966
PHASES: [
6067
"STANDARD_K3S_CLUSTER_ON_GCP",
6168
"CERT_MANAGER",
@@ -67,7 +74,7 @@ const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
6774
},
6875
STANDARD_AKS_TEST: {
6976
CLOUD: "azure",
70-
DESCRIPTION: `Deploy Gitpod on AKS(version ${k8s_version})`,
77+
DESCRIPTION: `${op} Gitpod on AKS(version ${k8s_version})`,
7178
PHASES: [
7279
"STANDARD_AKS_CLUSTER",
7380
"CERT_MANAGER",
@@ -81,7 +88,7 @@ const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
8188
},
8289
STANDARD_EKS_TEST: {
8390
CLOUD: "aws",
84-
DESCRIPTION: `Create an EKS cluster(version ${k8s_version})`,
91+
DESCRIPTION: `${op} an EKS cluster(version ${k8s_version})`,
8592
PHASES: [
8693
"STANDARD_EKS_CLUSTER",
8794
"CERT_MANAGER",
@@ -198,47 +205,52 @@ const TESTS: { [name: string]: InfraConfig } = {
198205
WORKSPACE_TEST: {
199206
phase: "run-workspace-tests",
200207
makeTarget: "run-workspace-tests",
201-
description: "Run the test for workspaces",
208+
description: "Run the \`workspace\` tests \`test/tests/workspace\`",
209+
slackhook: slackHook["workspace-jobs"],
202210
},
203211
VSCODE_IDE_TEST: {
204212
phase: "run-vscode-ide-tests",
205213
makeTarget: "run-vscode-ide-tests",
206-
description: "Run the test for vscode IDE",
214+
description: "Run the \`vscode IDE\` tests \`test/tests/ide/vscode\`",
215+
slackhook: slackHook["ide-jobs"],
207216
},
208217
JB_IDE_TEST: {
209218
phase: "run-jb-ide-tests",
210219
makeTarget: "run-jb-ide-tests",
211-
description: "Run the test for jetbrains IDE",
220+
description: "Run the \`jetbrains IDE\` tests \`test/tests/ide/jetbrains\`",
221+
slackhook: slackHook["ide-jobs"],
212222
},
213223
CONTENTSERVICE_TEST: {
214224
phase: "run-cs-component-tests",
215225
makeTarget: "run-cs-component-tests",
216-
description: "Run the test for content-service component",
226+
description: "Run the \`content-service\` tests \`test/tests/components/content-service\`",
217227
},
218228
DB_TEST: {
219229
phase: "run-db-component-tests",
220230
makeTarget: "run-db-component-tests",
221-
description: "Run the test for database component",
231+
description: "Run the \`database\` tests \`test/tests/components/database\`",
222232
},
223233
IMAGEBUILDER_TEST: {
224234
phase: "run-ib-component-tests",
225235
makeTarget: "run-ib-component-tests",
226-
description: "Run the test for image-builder component",
236+
description: "Run the \`image-builder\` tests \`test/tests/components/image-builder\`",
227237
},
228238
SERVER_TEST: {
229239
phase: "run-server-component-tests",
230240
makeTarget: "run-server-component-tests",
231-
description: "Run the test for server component",
241+
description: "Run the \`server\` tests \`test/tests/components/server\`",
232242
},
233243
WS_DAEMON_TEST: {
234244
phase: "run-wsd-component-tests",
235245
makeTarget: "run-wsd-component-tests",
236-
description: "Run the test for ws-daemon component",
246+
description: "Run the \`ws-daemon\` tests \`test/tests/components/ws-daemon\`",
247+
slackhook: slackHook["workspace-jobs"],
237248
},
238249
WS_MNGR_TEST: {
239250
phase: "run-wsm-component-tests",
240251
makeTarget: "run-wsm-component-tests",
241-
description: "Run the test for ws-manager component",
252+
description: "Run the \`ws-manager\` tests \`test/tests/components/ws-manager\`",
253+
slackhook: slackHook["workspace-jobs"],
242254
},
243255
}
244256

@@ -259,14 +271,20 @@ export async function installerTests(config: TestConfig) {
259271
// If the cloud variable is not set, we have a cleanup job in hand
260272
const majorPhase: string = cloud == "" ? `create-${cloud}-infra` : "cleanup-infra"
261273

262-
werft.phase(majorPhase, `Manage the infrastructure`);
274+
werft.phase(majorPhase, `Manage the infrastructure in ${cloud}`);
263275
for (let phase of config.PHASES) {
264276
const phaseSteps = INFRA_PHASES[phase];
265277
const ret = callMakeTargets(phaseSteps.phase, phaseSteps.description, phaseSteps.makeTarget);
266278
if (ret) {
267279
// there is not point in continuing if one stage fails for infra setup
268-
werft.fail(`create-${cloud}-infra`, "Cluster creation failed");
269-
break;
280+
const err: Error = new Error("Cluster creation failed")
281+
werft.fail(`create-${cloud}-infra`, err.message);
282+
283+
sendFailureSlackAlert(phaseSteps.description, err, slackHook["self-hosted-jobs"]).catch((error: Error) => {
284+
console.error("Failed to send message to Slack", error);
285+
});
286+
287+
throw err
270288
}
271289
}
272290
werft.done(majorPhase);
@@ -284,6 +302,10 @@ export async function installerTests(config: TestConfig) {
284302
const upgradePhase = INFRA_PHASES["KOTS_UPGRADE"];
285303
const ret = callMakeTargets(upgradePhase.phase, upgradePhase.description, upgradePhase.makeTarget);
286304
if (ret) {
305+
sendFailureSlackAlert(upgradePhase.description, new Error("Upgrade test failed"), slackHook["self-hosted-jobs"]).catch((error: Error) => {
306+
console.error("Failed to send message to Slack", error);
307+
});
308+
287309
return;
288310
}
289311
}
@@ -308,6 +330,11 @@ export async function installerTests(config: TestConfig) {
308330
exec(`werft log result -d "KUBECONFIG Connection details" url "Follow cloud specific instructions to connect to the cluster"`);
309331
}
310332

333+
sendPreviewSlackAlert().catch((error: Error) => {
334+
console.error("Failed to send message to Slack", error);
335+
});
336+
337+
311338
exec(`werft log result -d "Terraform state" url "Terraform state file name is ${process.env["TF_VAR_TEST_ID"]}"`);
312339

313340
werft.done("print-output");
@@ -321,12 +348,15 @@ function runIntegrationTests() {
321348
werft.phase("run-integration-tests", "Run all existing integration tests");
322349
for (let test in TESTS) {
323350
const testPhase = TESTS[test];
324-
// todo(nvn): handle the test failures by alerting teams
325351
const ret = callMakeTargets(testPhase.phase, testPhase.description, testPhase.makeTarget);
326352
if (ret) {
327353
exec(
328354
`werft log result -d "failed test" url "${testPhase.description}(Phase ${testPhase.phase}) failed. Please refer logs."`,
329355
);
356+
357+
sendFailureSlackAlert(testPhase.description, new Error("Integration test failed"), testPhase.slackhook).catch((error: Error) => {
358+
console.error("Failed to send message to Slack", error);
359+
});
330360
}
331361
}
332362

@@ -439,3 +469,129 @@ function cleanup() {
439469

440470
return ret;
441471
}
472+
473+
export async function sendFailureSlackAlert(phase: string, err: Error, hook: string): Promise<void> {
474+
if (typeof hook === 'undefined' || hook === null) {
475+
return
476+
}
477+
478+
const repo = context.Repository.host + "/" + context.Repository.owner + "/" + context.Repository.repo;
479+
const data = JSON.stringify({
480+
blocks: [
481+
{
482+
type: "section",
483+
text: {
484+
type: "mrkdwn",
485+
text: ":X: *self-hosted test failure*\n_Test configuration:_ `" + config.DESCRIPTION + "`\n_Build:_ `" + context.Name + "`",
486+
},
487+
accessory: {
488+
type: "button",
489+
text: {
490+
type: "plain_text",
491+
text: "Go to Werft",
492+
emoji: true
493+
},
494+
value: "click_me_123",
495+
url: "https://werft.gitpod-dev.com/job/" + context.Name,
496+
action_id: "button-action"
497+
}
498+
},
499+
{
500+
type: "section",
501+
fields: [
502+
{
503+
type: "mrkdwn",
504+
text: "*Failed step:*\n" + phase + "\n",
505+
},
506+
{
507+
type: "mrkdwn",
508+
text: "*Error:*\n" + err + "\n",
509+
},
510+
]
511+
},
512+
{
513+
type: "section",
514+
fields: [
515+
{
516+
type: "mrkdwn",
517+
text: "*Replicated channel:*\n" + channel + "\n",
518+
},
519+
]
520+
},
521+
],
522+
});
523+
return new Promise((resolve, reject) => {
524+
const req = https.request(
525+
{
526+
hostname: "hooks.slack.com",
527+
port: 443,
528+
path: hook,
529+
method: "POST",
530+
headers: {
531+
"Content-Type": "application/json",
532+
"Content-Length": data.length,
533+
},
534+
},
535+
() => resolve(),
536+
);
537+
req.on("error", (error: Error) => reject(error));
538+
req.write(data);
539+
req.end();
540+
});
541+
}
542+
543+
export async function sendPreviewSlackAlert(): Promise<void> {
544+
const data = JSON.stringify({
545+
blocks: [
546+
{
547+
type: "section",
548+
text: {
549+
type: "mrkdwn",
550+
text: ":white_check_mark: *self-hosted preview environment\n_Test configuration:_ `" + config.DESCRIPTION + "`*\n_Build:_ `" + context.Name + "`",
551+
},
552+
accessory: {
553+
type: "button",
554+
text: {
555+
type: "plain_text",
556+
text: "Go to Werft",
557+
emoji: true
558+
},
559+
value: "click_me_123",
560+
url: "https://werft.gitpod-dev.com/job/" + context.Name,
561+
action_id: "button-action"
562+
}
563+
},
564+
{
565+
type: "section",
566+
fields: [
567+
{
568+
type: "mrkdwn",
569+
text: "*URL:*\n<https://" + process.env["TF_VAR_TEST_ID"] + ".tests.gitpod-self-hosted.com|Access preview setup>",
570+
},
571+
{
572+
type: "mrkdwn",
573+
text: "*Terraform workspace:*\n`" + process.env["TF_VAR_TEST_ID"] + "`\n",
574+
},
575+
]
576+
},
577+
],
578+
});
579+
return new Promise((resolve, reject) => {
580+
const req = https.request(
581+
{
582+
hostname: "hooks.slack.com",
583+
port: 443,
584+
path: process.env.SH_SLACK_NOTIFICATION_PATH.trim(),
585+
method: "POST",
586+
headers: {
587+
"Content-Type": "application/json",
588+
"Content-Length": data.length,
589+
},
590+
},
591+
() => resolve(),
592+
);
593+
req.on("error", (error: Error) => reject(error));
594+
req.write(data);
595+
req.end();
596+
});
597+
}

0 commit comments

Comments
 (0)