1- import needle = require( 'needle' ) ;
1+ import * as needle from 'needle' ;
2+ import { NeedleResponse , NeedleHttpVerbs } from 'needle' ;
3+ import * as sleep from 'sleep-promise' ;
24import * as config from '../common/config' ;
35import logger = require( '../common/logger' ) ;
4- import { IDeleteWorkloadPayload , IDepGraphPayload , IWorkloadMetadataPayload } from './types' ;
6+ import { IDeleteWorkloadPayload , IDepGraphPayload , IWorkloadMetadataPayload , IResponseWithAttempts } from './types' ;
57
68const upstreamUrl = config . INTEGRATION_API || config . DEFAULT_KUBERNETES_UPSTREAM_URL ;
79
8- function isSuccessStatusCode ( statusCode : number | undefined ) : boolean {
9- return statusCode !== undefined && statusCode > 100 && statusCode < 400 ;
10- }
11-
1210export async function sendDepGraph ( ...payloads : IDepGraphPayload [ ] ) : Promise < void > {
1311 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 ;
1415 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' )
2321 }
2422 } 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 ;
2823 logger . error ( { error, payload : payloadWithoutDepGraph } , 'could not send the dependency scan result to Homebase' ) ;
2924 }
3025 }
@@ -33,16 +28,12 @@ export async function sendDepGraph(...payloads: IDepGraphPayload[]): Promise<voi
3328export async function sendWorkloadMetadata ( payload : IWorkloadMetadataPayload ) : Promise < void > {
3429 try {
3530 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- ) ;
4131
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 } ` ) ;
4435 } else {
45- logger . info ( { workloadLocator : payload . workloadLocator } , 'workload metadata sent upstream successfully' )
36+ logger . info ( { workloadLocator : payload . workloadLocator , attempt } , 'workload metadata sent upstream successfully' )
4637 }
4738 } catch ( error ) {
4839 logger . error ( { error, workloadLocator : payload . workloadLocator } , 'could not send workload metadata to Homebase' ) ;
@@ -51,22 +42,48 @@ export async function sendWorkloadMetadata(payload: IWorkloadMetadataPayload): P
5142
5243export async function deleteHomebaseWorkload ( payload : IDeleteWorkloadPayload ) : Promise < void > {
5344 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 ) {
6147 const msg = 'attempted to delete a workload Homebase could not find, maybe we are still building it?' ;
6248 logger . info ( { payload} , msg ) ;
6349 return ;
6450 }
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' )
6855 }
6956 } catch ( error ) {
7057 logger . error ( { error, payload} , 'could not send workload to delete to Homebase' ) ;
7158 }
7259}
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