1
1
const { readFileSync } = require ( 'fs' ) ;
2
+ const { execSync } = require ( 'child_process' ) ;
2
3
const _ = require ( 'lodash' ) ;
3
4
const yaml = require ( 'js-yaml' ) ;
4
- const { docker, storageLayer } = require ( '../../../config' ) ;
5
+ const { docker, containerTimeout } = require ( '../../../config' ) ;
5
6
const startListener = require ( '../StartListener' ) ;
6
7
const createStorageLayer = require ( '../../storageLayers' ) ;
7
- const { logContainer , normalizeId } = require ( '../../helpers' ) ;
8
+ const { normalizeId , logContainer } = require ( '../../helpers' ) ;
8
9
const Logger = require ( '../ContainerLogger' ) ;
9
10
11
+ const STEP_LABEL = 'codefresh-assessment-pipeline-step' ;
12
+
10
13
class PipelineRunner {
11
14
constructor ( settings = { } ) {
12
- const { path } = settings ;
15
+ const { path, pipeline , storageLayer } = settings ;
13
16
this . path = path || './pipeline.yml' ;
17
+ this . pipeline = pipeline ;
18
+ this . storageLayer = storageLayer ;
14
19
}
15
20
16
21
async run ( ) {
22
+ const executionResult = [ ] ;
23
+
17
24
try {
18
25
const pipeline = this . _loadPipeline ( ) ;
19
26
this . _validatePipeline ( pipeline ) ;
20
27
21
28
await this . _initListener ( ) ;
22
29
23
30
for ( const [ key , value ] of Object . entries ( pipeline . steps ) ) {
24
- const { StatusCode } = await this . _runStep ( { name : key , ...value } ) ;
25
- if ( StatusCode > 0 ) {
26
- throw new Error ( `Pipeline failed on step ${ key } with status code ${ StatusCode } ` ) ;
31
+ const container = await this . _runStep ( { name : key , ...value } ) ;
32
+ normalizeId ( container ) ;
33
+ executionResult . push ( { name : key , ...container } ) ;
34
+
35
+ if ( container . exitCode > 0 ) {
36
+ if ( container . exitCode === 143 ) {
37
+ throw new Error ( `Container was stopped cause of predefined max execution timeout: ${ containerTimeout / 1000 } sec` ) ;
38
+ }
39
+ throw new Error ( `Pipeline failed on step ${ key } with status code ${ container . statusCode } ` ) ;
27
40
}
28
41
}
29
42
console . log ( 'Pipeline run successfully' ) ;
30
- this . _killListener ( ) ;
43
+ this . _gracefulShutdown ( ) ;
44
+ return { executionResult, status : 'Pipeline run successfully' } ;
31
45
32
46
} catch ( err ) {
33
47
console . error ( 'Failed to run pipeline cause of: ' , err ) ;
34
- this . _killListener ( ) ;
48
+ this . _gracefulShutdown ( ) ;
49
+ return { executionResult, status : `Failed to run pipeline cause of: ${ err } ` , error : true } ;
35
50
}
36
51
}
37
52
38
53
async _runStep ( step ) {
39
- const { image : Image , commands : Cmd , name } = step ;
54
+ const { image : Image , cmd : Cmd , name } = step ;
40
55
41
56
try {
42
- const container = await docker . createContainer ( { name, Image, Cmd, AttachStdout : true , AttachStderr : true } ) ;
57
+ const container = await docker . createContainer ( {
58
+ name,
59
+ Image,
60
+ Cmd,
61
+ Labels : {
62
+ meta : STEP_LABEL
63
+ }
64
+ } ) ;
43
65
const output = await container . start ( ) ;
66
+ let running = true ;
67
+
44
68
if ( output . Error ) {
45
69
throw output . Error ;
46
70
}
71
+
72
+ setTimeout ( ( ) => {
73
+ if ( running ) {
74
+ container . stop ( ) . catch ( ( err ) => {
75
+ console . error ( 'Container timeout termination failed' , err ) ;
76
+ } ) ;
77
+ }
78
+ } , containerTimeout ) ;
79
+
47
80
const res = await container . wait ( ) ;
48
- return res ;
81
+ running = false ;
82
+
83
+ return {
84
+ exitCode : res . StatusCode ,
85
+ id : container . id
86
+ } ;
49
87
} catch ( err ) {
50
88
throw new Error ( `Failed to run step cause of: ${ err } ` ) ;
51
89
}
52
90
}
53
91
54
92
_loadPipeline ( ) {
55
93
try {
94
+ if ( this . pipeline ) {
95
+ return this . pipeline ;
96
+ }
56
97
const file = readFileSync ( this . path ) ;
57
98
return yaml . safeLoad ( file ) ;
58
99
} catch ( err ) {
@@ -67,17 +108,19 @@ class PipelineRunner {
67
108
}
68
109
69
110
_initListener ( ) {
70
- return startListener . start ( async ( container ) => {
111
+ return startListener . start ( { labels : [ `meta= ${ STEP_LABEL } ` ] } , async ( container ) => {
71
112
normalizeId ( container ) ;
113
+ logContainer ( 'Attaching to container' , container ) ;
72
114
73
- const storage = createStorageLayer ( storageLayer , 'writer' , container ) ;
115
+ const storage = createStorageLayer ( this . storageLayer , 'writer' , container ) ;
74
116
75
117
const logger = new Logger ( container . id , storage ) ;
76
118
await logger . run ( ) ;
77
119
} ) ;
78
120
}
79
121
80
- _killListener ( ) {
122
+ _gracefulShutdown ( ) {
123
+ execSync ( 'yes | docker container prune' ) ;
81
124
startListener . stop ( ) ;
82
125
}
83
126
}
0 commit comments