@@ -31,7 +31,7 @@ const asyncPipeline = promisify(pipeline);
3131const V1_VERSIONS_API = 'https://artifacts-api.elastic.co/v1/versions' ;
3232
3333const { cache } = require ( './utils' ) ;
34- const { createCliError } = require ( './errors' ) ;
34+ const { createCliError, isCliError } = require ( './errors' ) ;
3535
3636const TEST_ES_SNAPSHOT_VERSION = process . env . TEST_ES_SNAPSHOT_VERSION
3737 ? process . env . TEST_ES_SNAPSHOT_VERSION
@@ -66,6 +66,25 @@ function headersToString(headers, indent = '') {
6666 ) ;
6767}
6868
69+ async function retry ( log , fn ) {
70+ async function doAttempt ( attempt ) {
71+ try {
72+ return await fn ( ) ;
73+ } catch ( error ) {
74+ if ( isCliError ( error ) || attempt >= 5 ) {
75+ throw error ;
76+ }
77+
78+ log . warning ( '...failure, retrying in 5 seconds:' , error . message ) ;
79+ await new Promise ( resolve => setTimeout ( resolve , 5000 ) ) ;
80+ log . info ( '...retrying' ) ;
81+ return await doAttempt ( attempt + 1 ) ;
82+ }
83+ }
84+
85+ return await doAttempt ( 1 ) ;
86+ }
87+
6988exports . Artifact = class Artifact {
7089 /**
7190 * Fetch an Artifact from the Artifact API for a license level and version
@@ -78,22 +97,27 @@ exports.Artifact = class Artifact {
7897 const urlBuild = encodeURIComponent ( TEST_ES_SNAPSHOT_VERSION ) ;
7998 const url = `${ V1_VERSIONS_API } /${ urlVersion } /builds/${ urlBuild } /projects/elasticsearch` ;
8099
81- log . info ( 'downloading artifact info from %s' , chalk . bold ( url ) ) ;
82- const abc = new AbortController ( ) ;
83- const resp = await fetch ( url , { signal : abc . signal } ) ;
84- const json = await resp . text ( ) ;
100+ const json = await retry ( log , async ( ) => {
101+ log . info ( 'downloading artifact info from %s' , chalk . bold ( url ) ) ;
85102
86- if ( resp . status === 404 ) {
87- abc . abort ( ) ;
88- throw createCliError (
89- `Snapshots for ${ version } /${ TEST_ES_SNAPSHOT_VERSION } are not available`
90- ) ;
91- }
103+ const abc = new AbortController ( ) ;
104+ const resp = await fetch ( url , { signal : abc . signal } ) ;
105+ const json = await resp . text ( ) ;
92106
93- if ( ! resp . ok ) {
94- abc . abort ( ) ;
95- throw new Error ( `Unable to read artifact info from ${ url } : ${ resp . statusText } \n ${ json } ` ) ;
96- }
107+ if ( resp . status === 404 ) {
108+ abc . abort ( ) ;
109+ throw createCliError (
110+ `Snapshots for ${ version } /${ TEST_ES_SNAPSHOT_VERSION } are not available`
111+ ) ;
112+ }
113+
114+ if ( ! resp . ok ) {
115+ abc . abort ( ) ;
116+ throw new Error ( `Unable to read artifact info from ${ url } : ${ resp . statusText } \n ${ json } ` ) ;
117+ }
118+
119+ return json ;
120+ } ) ;
97121
98122 // parse the api response into an array of Artifact objects
99123 const {
@@ -184,21 +208,23 @@ exports.Artifact = class Artifact {
184208 * @return {Promise<void> }
185209 */
186210 async download ( dest ) {
187- const cacheMeta = cache . readMeta ( dest ) ;
188- const tmpPath = `${ dest } .tmp` ;
211+ await retry ( this . _log , async ( ) => {
212+ const cacheMeta = cache . readMeta ( dest ) ;
213+ const tmpPath = `${ dest } .tmp` ;
189214
190- const artifactResp = await this . _download ( tmpPath , cacheMeta . etag , cacheMeta . ts ) ;
191- if ( artifactResp . cached ) {
192- return ;
193- }
215+ const artifactResp = await this . _download ( tmpPath , cacheMeta . etag , cacheMeta . ts ) ;
216+ if ( artifactResp . cached ) {
217+ return ;
218+ }
194219
195- await this . _verifyChecksum ( artifactResp ) ;
220+ await this . _verifyChecksum ( artifactResp ) ;
196221
197- // cache the etag for future downloads
198- cache . writeMeta ( dest , { etag : artifactResp . etag } ) ;
222+ // cache the etag for future downloads
223+ cache . writeMeta ( dest , { etag : artifactResp . etag } ) ;
199224
200- // rename temp download to the destination location
201- fs . renameSync ( tmpPath , dest ) ;
225+ // rename temp download to the destination location
226+ fs . renameSync ( tmpPath , dest ) ;
227+ } ) ;
202228 }
203229
204230 /**
0 commit comments