11import { exec } from 'child_process' ;
22import http from 'http' ;
33import { createHash } from 'crypto' ;
4- import { Command , IAM , Identity , IdentityEnum } from 'cli-testing' ;
4+ import { Command , coordinate , IAM , Identity , IdentityEnum } from 'cli-testing' ;
55import {
66 AttachedPolicy ,
77 Group ,
@@ -12,7 +12,6 @@ import {
1212import { AWSCliOptions } from 'cli-testing' ;
1313import Zenko from 'world/Zenko' ;
1414import fs from 'fs' ;
15- import lockFile from 'proper-lockfile' ;
1615import { ITestCaseHookParameter } from '@cucumber/cucumber' ;
1716import { AWSCredentials , Constants , Utils } from 'cli-testing' ;
1817import { createBucketWithConfiguration , putObject } from '../steps/utils/utils' ;
@@ -323,83 +322,64 @@ export async function prepareMetricsScenarios(
323322 const { gherkinDocument, pickle } = scenarioConfiguration ;
324323 const featureName = gherkinDocument . feature ?. name ?. replace ( / / g, '-' ) . toLowerCase ( ) || 'metrics' ;
325324 const filePath = `/tmp/${ featureName } ` ;
326- let initiated = false ;
327- let releaseLock : ( ( ) => Promise < void > ) | false = false ;
328- const output : Record < string , AWSCredentials > = { } ;
329325
330326 const {
331327 versioning = '' ,
332328 jobName = 'end2end-ops-count-items' ,
333329 jobNamespace = `${ featureName } -setup`
334330 } = options ;
335331
336- if ( ! fs . existsSync ( filePath ) ) {
337- fs . writeFileSync ( filePath , JSON . stringify ( {
338- ready : false ,
339- } ) ) ;
340- } else {
341- initiated = true ;
342- }
343-
344- if ( ! initiated ) {
345- try {
346- releaseLock = await lockFile . lock ( filePath , { stale : Constants . DEFAULT_TIMEOUT / 2 } ) ;
347- } catch ( err ) {
348- world . logger . error ( 'Unable to acquire lock' , { err } ) ;
349- releaseLock = false ;
350- }
351- }
352-
353- if ( releaseLock ) {
354- const scenarioIds = new Set < string > ( ) ;
355-
356- for ( const scenario of gherkinDocument . feature ?. children || [ ] ) {
357- for ( const example of scenario . scenario ?. examples || [ ] ) {
358- for ( const values of example . tableBody || [ ] ) {
359- const scenarioWithExampleID = hashStringAndKeepFirst20Characters ( `${ values . id } ` ) ;
360- scenarioIds . add ( scenarioWithExampleID ) ;
332+ await coordinate (
333+ {
334+ lockName : featureName ,
335+ timeout : Constants . DEFAULT_TIMEOUT ,
336+ logger : world . logger ,
337+ } ,
338+ // Work function: executed exactly once
339+ async ( ) => {
340+ const output : Record < string , AWSCredentials > = { } ;
341+ const scenarioIds = new Set < string > ( ) ;
342+
343+ for ( const scenario of gherkinDocument . feature ?. children || [ ] ) {
344+ for ( const example of scenario . scenario ?. examples || [ ] ) {
345+ for ( const values of example . tableBody || [ ] ) {
346+ const scenarioWithExampleID = hashStringAndKeepFirst20Characters ( `${ values . id } ` ) ;
347+ scenarioIds . add ( scenarioWithExampleID ) ;
348+ }
361349 }
362350 }
363- }
364-
365- for ( const scenarioId of scenarioIds ) {
366- await world . createAccount ( scenarioId , true ) ;
367- await createBucketWithConfiguration ( world , scenarioId , versioning ) ;
368- await putObject ( world ) ;
369- output [ scenarioId ] = Identity . getCurrentCredentials ( ) ! ;
370- }
371-
372- await createJobAndWaitForCompletion ( world , jobName , jobNamespace ) ;
373-
374- await Utils . sleep ( 2000 ) ;
375- fs . writeFileSync ( filePath , JSON . stringify ( {
376- ready : true ,
377- ...output ,
378- } ) ) ;
379-
380- await releaseLock ( ) ;
381- } else {
382- while ( ! fs . existsSync ( filePath ) ) {
383- await Utils . sleep ( 100 ) ;
384- }
351+
352+ for ( const scenarioId of scenarioIds ) {
353+ await world . createAccount ( scenarioId , true ) ;
354+ await createBucketWithConfiguration ( world , scenarioId , versioning ) ;
355+ await putObject ( world ) ;
356+ output [ scenarioId ] = Identity . getCurrentCredentials ( ) ! ;
357+ }
385358
386- let configuration : { ready : boolean } = JSON . parse ( fs . readFileSync ( filePath , 'utf8' ) ) as { ready : boolean } ;
387- while ( ! configuration . ready ) {
388- await Utils . sleep ( 100 ) ;
389- configuration = JSON . parse ( fs . readFileSync ( filePath , 'utf8' ) ) as { ready : boolean } ;
359+ await createJobAndWaitForCompletion ( world , jobName , jobNamespace ) ;
360+
361+ await Utils . sleep ( 2000 ) ;
362+
363+ // Store output in a temporary file for other workers to read
364+ fs . writeFileSync ( filePath , JSON . stringify ( {
365+ ready : true ,
366+ ...output ,
367+ } ) ) ;
368+ } ,
369+ // Post-processing: all workers read the configuration
370+ async ( ) => {
371+ const configuration : Record < string , AWSCredentials > = JSON . parse ( fs . readFileSync ( filePath , 'utf8' ) ) ;
372+ const key = hashStringAndKeepFirst20Characters ( `${ pickle . astNodeIds [ 1 ] } ` ) ;
373+ world . logger . debug ( 'Scenario key' , { key, from : `${ pickle . astNodeIds [ 1 ] } ` , configuration } ) ;
374+
375+ world . addToSaved ( 'bucketName' , key ) ;
376+ world . addToSaved ( 'accountName' , key ) ;
377+ world . addToSaved ( 'accountNameForScenario' , key ) ;
378+ world . addToSaved ( 'metricsEnvironmentSetup' , true ) ;
379+
380+ if ( configuration [ key ] ) {
381+ Identity . addIdentity ( IdentityEnum . ACCOUNT , key , configuration [ key ] , undefined , true , true ) ;
382+ }
390383 }
391- }
392-
393- const configuration : typeof output = JSON . parse ( fs . readFileSync ( filePath , 'utf8' ) ) as typeof output ;
394- const key = hashStringAndKeepFirst20Characters ( `${ pickle . astNodeIds [ 1 ] } ` ) ;
395- world . logger . debug ( 'Scenario key' , { key, from : `${ pickle . astNodeIds [ 1 ] } ` , configuration } ) ;
396-
397- world . addToSaved ( 'bucketName' , key ) ;
398- world . addToSaved ( 'accountName' , key ) ;
399- world . addToSaved ( 'accountNameForScenario' , key ) ;
400- world . addToSaved ( 'metricsEnvironmentSetup' , true ) ;
401-
402- if ( configuration [ key ] ) {
403- Identity . addIdentity ( IdentityEnum . ACCOUNT , key , configuration [ key ] , undefined , true , true ) ;
404- }
384+ ) ;
405385}
0 commit comments