1
1
import * as fs from "fs" ;
2
+ import * as https from "https" ;
2
3
import { join } from "path" ;
3
4
import { exec } from "./util/shell" ;
4
5
import { Werft } from "./util/werft" ;
@@ -16,6 +17,11 @@ const upgrade: string = annotations.upgrade || "false"; // setting to true will
16
17
const skipTests : string = annotations . skipTests || "false" ; // setting to true skips the integration tests
17
18
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
18
19
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
+ }
19
25
20
26
const makefilePath : string = join ( "install/tests" ) ;
21
27
@@ -25,6 +31,7 @@ interface InfraConfig {
25
31
phase : string ;
26
32
makeTarget : string ;
27
33
description : string ;
34
+ slackhook ?: string ;
28
35
}
29
36
30
37
interface TestConfig {
@@ -35,13 +42,14 @@ interface TestConfig {
35
42
36
43
const k8s_version : string = randK8sVersion ( testConfig )
37
44
const os_version : string = randOsVersion ( ) // applicable only for k3s
45
+ const op : string = preview == "true" ? "Preview" : "Test"
38
46
39
47
// Each of the TEST_CONFIGURATIONS define an integration test end-to-end
40
48
// It should be a combination of multiple INFRA_PHASES, order of PHASES slice is important
41
49
const TEST_CONFIGURATIONS : { [ name : string ] : TestConfig } = {
42
50
STANDARD_GKE_TEST : {
43
51
CLOUD : "gcp" ,
44
- DESCRIPTION : `Deploy Gitpod on GKE(version ${ k8s_version } )` ,
52
+ DESCRIPTION : `${ op } Gitpod on GKE managed cluster (version ${ k8s_version } )` ,
45
53
PHASES : [
46
54
"STANDARD_GKE_CLUSTER" ,
47
55
"CERT_MANAGER" ,
@@ -54,8 +62,7 @@ const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
54
62
} ,
55
63
STANDARD_K3S_TEST : {
56
64
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 } ` ,
59
66
PHASES : [
60
67
"STANDARD_K3S_CLUSTER_ON_GCP" ,
61
68
"CERT_MANAGER" ,
@@ -67,7 +74,7 @@ const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
67
74
} ,
68
75
STANDARD_AKS_TEST : {
69
76
CLOUD : "azure" ,
70
- DESCRIPTION : `Deploy Gitpod on AKS(version ${ k8s_version } )` ,
77
+ DESCRIPTION : `${ op } Gitpod on AKS(version ${ k8s_version } )` ,
71
78
PHASES : [
72
79
"STANDARD_AKS_CLUSTER" ,
73
80
"CERT_MANAGER" ,
@@ -81,7 +88,7 @@ const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
81
88
} ,
82
89
STANDARD_EKS_TEST : {
83
90
CLOUD : "aws" ,
84
- DESCRIPTION : `Create an EKS cluster(version ${ k8s_version } )` ,
91
+ DESCRIPTION : `${ op } an EKS cluster(version ${ k8s_version } )` ,
85
92
PHASES : [
86
93
"STANDARD_EKS_CLUSTER" ,
87
94
"CERT_MANAGER" ,
@@ -198,47 +205,52 @@ const TESTS: { [name: string]: InfraConfig } = {
198
205
WORKSPACE_TEST : {
199
206
phase : "run-workspace-tests" ,
200
207
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" ] ,
202
210
} ,
203
211
VSCODE_IDE_TEST : {
204
212
phase : "run-vscode-ide-tests" ,
205
213
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" ] ,
207
216
} ,
208
217
JB_IDE_TEST : {
209
218
phase : "run-jb-ide-tests" ,
210
219
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" ] ,
212
222
} ,
213
223
CONTENTSERVICE_TEST : {
214
224
phase : "run-cs-component-tests" ,
215
225
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\` " ,
217
227
} ,
218
228
DB_TEST : {
219
229
phase : "run-db-component-tests" ,
220
230
makeTarget : "run-db-component-tests" ,
221
- description : "Run the test for database component " ,
231
+ description : "Run the \`database\` tests \`test/tests/components/ database\` " ,
222
232
} ,
223
233
IMAGEBUILDER_TEST : {
224
234
phase : "run-ib-component-tests" ,
225
235
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\` " ,
227
237
} ,
228
238
SERVER_TEST : {
229
239
phase : "run-server-component-tests" ,
230
240
makeTarget : "run-server-component-tests" ,
231
- description : "Run the test for server component " ,
241
+ description : "Run the \`server\` tests \`test/tests/components/ server\` " ,
232
242
} ,
233
243
WS_DAEMON_TEST : {
234
244
phase : "run-wsd-component-tests" ,
235
245
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" ] ,
237
248
} ,
238
249
WS_MNGR_TEST : {
239
250
phase : "run-wsm-component-tests" ,
240
251
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" ] ,
242
254
} ,
243
255
}
244
256
@@ -259,14 +271,20 @@ export async function installerTests(config: TestConfig) {
259
271
// If the cloud variable is not set, we have a cleanup job in hand
260
272
const majorPhase : string = cloud == "" ? `create-${ cloud } -infra` : "cleanup-infra"
261
273
262
- werft . phase ( majorPhase , `Manage the infrastructure` ) ;
274
+ werft . phase ( majorPhase , `Manage the infrastructure in ${ cloud } ` ) ;
263
275
for ( let phase of config . PHASES ) {
264
276
const phaseSteps = INFRA_PHASES [ phase ] ;
265
277
const ret = callMakeTargets ( phaseSteps . phase , phaseSteps . description , phaseSteps . makeTarget ) ;
266
278
if ( ret ) {
267
279
// 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
270
288
}
271
289
}
272
290
werft . done ( majorPhase ) ;
@@ -284,6 +302,10 @@ export async function installerTests(config: TestConfig) {
284
302
const upgradePhase = INFRA_PHASES [ "KOTS_UPGRADE" ] ;
285
303
const ret = callMakeTargets ( upgradePhase . phase , upgradePhase . description , upgradePhase . makeTarget ) ;
286
304
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
+
287
309
return ;
288
310
}
289
311
}
@@ -308,6 +330,11 @@ export async function installerTests(config: TestConfig) {
308
330
exec ( `werft log result -d "KUBECONFIG Connection details" url "Follow cloud specific instructions to connect to the cluster"` ) ;
309
331
}
310
332
333
+ sendPreviewSlackAlert ( ) . catch ( ( error : Error ) => {
334
+ console . error ( "Failed to send message to Slack" , error ) ;
335
+ } ) ;
336
+
337
+
311
338
exec ( `werft log result -d "Terraform state" url "Terraform state file name is ${ process . env [ "TF_VAR_TEST_ID" ] } "` ) ;
312
339
313
340
werft . done ( "print-output" ) ;
@@ -321,12 +348,15 @@ function runIntegrationTests() {
321
348
werft . phase ( "run-integration-tests" , "Run all existing integration tests" ) ;
322
349
for ( let test in TESTS ) {
323
350
const testPhase = TESTS [ test ] ;
324
- // todo(nvn): handle the test failures by alerting teams
325
351
const ret = callMakeTargets ( testPhase . phase , testPhase . description , testPhase . makeTarget ) ;
326
352
if ( ret ) {
327
353
exec (
328
354
`werft log result -d "failed test" url "${ testPhase . description } (Phase ${ testPhase . phase } ) failed. Please refer logs."` ,
329
355
) ;
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
+ } ) ;
330
360
}
331
361
}
332
362
@@ -439,3 +469,129 @@ function cleanup() {
439
469
440
470
return ret ;
441
471
}
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