1
- import needle = require( 'needle' ) ;
1
+ import * as needle from 'needle' ;
2
+ import { NeedleResponse , NeedleHttpVerbs } from 'needle' ;
3
+ import * as sleep from 'sleep-promise' ;
2
4
import * as config from '../common/config' ;
3
5
import logger = require( '../common/logger' ) ;
4
- import { IDeleteWorkloadPayload , IDepGraphPayload , IWorkloadMetadataPayload } from './types' ;
6
+ import { IDeleteWorkloadPayload , IDepGraphPayload , IWorkloadMetadataPayload , IResponseWithAttempts } from './types' ;
5
7
6
8
const upstreamUrl = config . INTEGRATION_API || config . DEFAULT_KUBERNETES_UPSTREAM_URL ;
7
9
8
- function isSuccessStatusCode ( statusCode : number | undefined ) : boolean {
9
- return statusCode !== undefined && statusCode > 100 && statusCode < 400 ;
10
- }
11
-
12
10
export async function sendDepGraph ( ...payloads : IDepGraphPayload [ ] ) : Promise < void > {
13
11
for ( const payload of payloads ) {
12
+ // Intentionally removing dependencyGraph as it would be too big to log
13
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
14
+ const { dependencyGraph, ...payloadWithoutDepGraph } = payload ;
14
15
try {
15
- const result = await needle ( 'post' , `${ upstreamUrl } /api/v1/dependency-graph` , payload , {
16
- json : true ,
17
- compressed : true ,
18
- } ,
19
- ) ;
20
-
21
- if ( ! isSuccessStatusCode ( result . statusCode ) ) {
22
- throw new Error ( `${ result . statusCode } ${ result . statusMessage } ` ) ;
16
+ const { response, attempt} = await retryRequest ( 'post' , `${ upstreamUrl } /api/v1/dependency-graph` , payload ) ;
17
+ if ( ! isSuccessStatusCode ( response . statusCode ) ) {
18
+ throw new Error ( `${ response . statusCode } ${ response . statusMessage } ` ) ;
19
+ } else {
20
+ logger . info ( { payload : payloadWithoutDepGraph , attempt} , 'dependency graph sent upstream successfully' )
23
21
}
24
22
} catch ( error ) {
25
- // Intentionally removing dependencyGraph as it would be too big to log
26
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
- const { dependencyGraph, ...payloadWithoutDepGraph } = payload ;
28
23
logger . error ( { error, payload : payloadWithoutDepGraph } , 'could not send the dependency scan result to Homebase' ) ;
29
24
}
30
25
}
@@ -33,16 +28,12 @@ export async function sendDepGraph(...payloads: IDepGraphPayload[]): Promise<voi
33
28
export async function sendWorkloadMetadata ( payload : IWorkloadMetadataPayload ) : Promise < void > {
34
29
try {
35
30
logger . info ( { workloadLocator : payload . workloadLocator } , 'attempting to send workload metadata upstream' )
36
- const result = await needle ( 'post' , `${ upstreamUrl } /api/v1/workload` , payload , {
37
- json : true ,
38
- compressed : true ,
39
- } ,
40
- ) ;
41
31
42
- if ( ! isSuccessStatusCode ( result . statusCode ) ) {
43
- throw new Error ( `${ result . statusCode } ${ result . statusMessage } ` ) ;
32
+ const { response, attempt} = await retryRequest ( 'post' , `${ upstreamUrl } /api/v1/workload` , payload ) ;
33
+ if ( ! isSuccessStatusCode ( response . statusCode ) ) {
34
+ throw new Error ( `${ response . statusCode } ${ response . statusMessage } ` ) ;
44
35
} else {
45
- logger . info ( { workloadLocator : payload . workloadLocator } , 'workload metadata sent upstream successfully' )
36
+ logger . info ( { workloadLocator : payload . workloadLocator , attempt } , 'workload metadata sent upstream successfully' )
46
37
}
47
38
} catch ( error ) {
48
39
logger . error ( { error, workloadLocator : payload . workloadLocator } , 'could not send workload metadata to Homebase' ) ;
@@ -51,22 +42,48 @@ export async function sendWorkloadMetadata(payload: IWorkloadMetadataPayload): P
51
42
52
43
export async function deleteHomebaseWorkload ( payload : IDeleteWorkloadPayload ) : Promise < void > {
53
44
try {
54
- const result = await needle ( 'delete' , `${ upstreamUrl } /api/v1/workload` , payload , {
55
- json : true ,
56
- compressed : true ,
57
- } ,
58
- ) ;
59
-
60
- if ( result . statusCode === 404 ) {
45
+ const { response, attempt} = await retryRequest ( 'delete' , `${ upstreamUrl } /api/v1/workload` , payload ) ;
46
+ if ( response . statusCode === 404 ) {
61
47
const msg = 'attempted to delete a workload Homebase could not find, maybe we are still building it?' ;
62
48
logger . info ( { payload} , msg ) ;
63
49
return ;
64
50
}
65
-
66
- if ( ! isSuccessStatusCode ( result . statusCode ) ) {
67
- throw new Error ( `${ result . statusCode } ${ result . statusMessage } ` ) ;
51
+ if ( ! isSuccessStatusCode ( response . statusCode ) ) {
52
+ throw new Error ( `${ response . statusCode } ${ response . statusMessage } ` ) ;
53
+ } else {
54
+ logger . info ( { workloadLocator : payload . workloadLocator , attempt} , 'workload deleted successfully' )
68
55
}
69
56
} catch ( error ) {
70
57
logger . error ( { error, payload} , 'could not send workload to delete to Homebase' ) ;
71
58
}
72
59
}
60
+
61
+ function isSuccessStatusCode ( statusCode : number | undefined ) : boolean {
62
+ return statusCode !== undefined && statusCode > 100 && statusCode < 400 ;
63
+ }
64
+
65
+ async function retryRequest ( verb : NeedleHttpVerbs , url : string , payload : object ) : Promise < IResponseWithAttempts > {
66
+ const retry = {
67
+ attempts : 3 ,
68
+ intervalSeconds : 2 ,
69
+ }
70
+ const options = {
71
+ json : true ,
72
+ compressed : true ,
73
+ } ;
74
+
75
+ let response : NeedleResponse ;
76
+ let attempt = 1 ;
77
+
78
+ response = await needle ( verb , url , payload , options ) ;
79
+ for ( ; attempt <= retry . attempts ; attempt ++ ) {
80
+ if ( response . statusCode === 502 && attempt + 1 < retry . attempts ) {
81
+ await sleep ( retry . intervalSeconds * 1000 ) ;
82
+ response = await needle ( verb , url , payload , options ) ;
83
+ } else {
84
+ break ;
85
+ }
86
+ }
87
+
88
+ return { response, attempt} ;
89
+ }
0 commit comments