@@ -21,6 +21,7 @@ import * as os from "os";
2121import * as crypto from "crypto" ;
2222import * as childProcess from "child_process" ;
2323import * as fse from "fs-extra" ;
24+ import * as net from "net" ;
2425
2526import PluginEvents = Cypress . PluginEvents ;
2627import PluginConfigOptions = Cypress . PluginConfigOptions ;
@@ -31,11 +32,13 @@ import PluginConfigOptions = Cypress.PluginConfigOptions;
3132interface SynapseConfig {
3233 configDir : string ;
3334 registrationSecret : string ;
35+ // Synapse must be configured with its public_baseurl so we have to allocate a port & url at this stage
36+ baseUrl : string ;
37+ port : number ;
3438}
3539
3640export interface SynapseInstance extends SynapseConfig {
3741 synapseId : string ;
38- port : number ;
3942}
4043
4144const synapses = new Map < string , SynapseInstance > ( ) ;
@@ -44,6 +47,16 @@ function randB64Bytes(numBytes: number): string {
4447 return crypto . randomBytes ( numBytes ) . toString ( "base64" ) . replace ( / = * $ / , "" ) ;
4548}
4649
50+ async function getFreePort ( ) : Promise < number > {
51+ return new Promise < number > ( resolve => {
52+ const srv = net . createServer ( ) ;
53+ srv . listen ( 0 , ( ) => {
54+ const port = ( < net . AddressInfo > srv . address ( ) ) . port ;
55+ srv . close ( ( ) => resolve ( port ) ) ;
56+ } ) ;
57+ } ) ;
58+ }
59+
4760async function cfgDirFromTemplate ( template : string ) : Promise < SynapseConfig > {
4861 const templateDir = path . join ( __dirname , "templates" , template ) ;
4962
@@ -64,12 +77,16 @@ async function cfgDirFromTemplate(template: string): Promise<SynapseConfig> {
6477 const macaroonSecret = randB64Bytes ( 16 ) ;
6578 const formSecret = randB64Bytes ( 16 ) ;
6679
67- // now copy homeserver.yaml, applying sustitutions
80+ const port = await getFreePort ( ) ;
81+ const baseUrl = `http://localhost:${ port } ` ;
82+
83+ // now copy homeserver.yaml, applying substitutions
6884 console . log ( `Gen ${ path . join ( templateDir , "homeserver.yaml" ) } ` ) ;
6985 let hsYaml = await fse . readFile ( path . join ( templateDir , "homeserver.yaml" ) , "utf8" ) ;
7086 hsYaml = hsYaml . replace ( / { { REGISTRATION_ S E C R E T } } / g, registrationSecret ) ;
7187 hsYaml = hsYaml . replace ( / { { MACAROON_ S E C R E T _ K E Y } } / g, macaroonSecret ) ;
7288 hsYaml = hsYaml . replace ( / { { FORM_ S E C R E T } } / g, formSecret ) ;
89+ hsYaml = hsYaml . replace ( / { { PUBLIC_ B A S E U R L } } / g, baseUrl ) ;
7390 await fse . writeFile ( path . join ( tempDir , "homeserver.yaml" ) , hsYaml ) ;
7491
7592 // now generate a signing key (we could use synapse's config generation for
@@ -80,6 +97,8 @@ async function cfgDirFromTemplate(template: string): Promise<SynapseConfig> {
8097 await fse . writeFile ( path . join ( tempDir , "localhost.signing.key" ) , `ed25519 x ${ signingKey } ` ) ;
8198
8299 return {
100+ port,
101+ baseUrl,
83102 configDir : tempDir ,
84103 registrationSecret,
85104 } ;
@@ -101,7 +120,7 @@ async function synapseStart(template: string): Promise<SynapseInstance> {
101120 "--name" , containerName ,
102121 "-d" ,
103122 "-v" , `${ synCfg . configDir } :/data` ,
104- "-p" , " 8008/tcp" ,
123+ "-p" , ` ${ synCfg . port } : 8008/tcp` ,
105124 "matrixdotorg/synapse:develop" ,
106125 "run" ,
107126 ] , ( err , stdout ) => {
@@ -110,26 +129,27 @@ async function synapseStart(template: string): Promise<SynapseInstance> {
110129 } ) ;
111130 } ) ;
112131
113- // Get the port that docker allocated: specifying only one
114- // port above leaves docker to just grab a free one, although
115- // in hindsight we need to put the port in public_baseurl in the
116- // config really, so this will probably need changing to use a fixed
117- // / configured port.
118- const port = await new Promise < number > ( ( resolve , reject ) => {
119- childProcess . execFile ( 'docker' , [
120- "port" , synapseId , "8008" ,
132+ synapses . set ( synapseId , { synapseId, ...synCfg } ) ;
133+
134+ console . log ( `Started synapse with id ${ synapseId } on port ${ synCfg . port } .` ) ;
135+
136+ // Await Synapse healthcheck
137+ await new Promise < void > ( ( resolve , reject ) => {
138+ childProcess . execFile ( "docker" , [
139+ "exec" , synapseId ,
140+ "curl" ,
141+ "--connect-timeout" , "30" ,
142+ "--retry" , "30" ,
143+ "--retry-delay" , "1" ,
144+ "--retry-all-errors" ,
145+ "--silent" ,
146+ "http://localhost:8008/health" ,
121147 ] , { encoding : 'utf8' } , ( err , stdout ) => {
122148 if ( err ) reject ( err ) ;
123- resolve ( Number ( stdout . trim ( ) . split ( ":" ) [ 1 ] ) ) ;
149+ else resolve ( ) ;
124150 } ) ;
125151 } ) ;
126152
127- synapses . set ( synapseId , Object . assign ( {
128- port,
129- synapseId,
130- } , synCfg ) ) ;
131-
132- console . log ( `Started synapse with id ${ synapseId } on port ${ port } .` ) ;
133153 return synapses . get ( synapseId ) ;
134154}
135155
0 commit comments