@@ -434,4 +434,82 @@ export class Utils {
434434 // https://dev.to/babak/exhaustive-type-checking-with-typescript-4l3f
435435 throw new Error ( `Unhandled case ${ param } ` ) ;
436436 }
437+
438+ static async dockerVolumeFileExists ( containerExecutable : string , path : string , volume : string ) : Promise < boolean > {
439+ try {
440+ await Utils . spawn ( [ containerExecutable , "run" , "--rm" , "-v" , `${ volume } :/mnt/vol` , "alpine" , "ls" , `/mnt/vol/${ path } ` ] ) ;
441+ return true ;
442+ } catch {
443+ return false ;
444+ }
445+ }
446+
447+ static gclRegistryPrefix : string = "registry.gcl.local" ;
448+ static async startDockerRegistry ( argv : Argv ) : Promise < void > {
449+ const gclRegistryCertVol = `${ this . gclRegistryPrefix } .certs` ;
450+ const gclRegistryDataVol = `${ this . gclRegistryPrefix } .data` ;
451+ const gclRegistryNet = `${ this . gclRegistryPrefix } .net` ;
452+
453+ // create cert volume
454+ try {
455+ await Utils . spawn ( `${ argv . containerExecutable } volume create ${ gclRegistryCertVol } ` . split ( " " ) ) ;
456+ } catch ( err ) {
457+ if ( err instanceof Error && "exitCode" in err && err . exitCode !== 125 )
458+ throw err ;
459+ }
460+
461+ // create self-signed cert/key files for https support
462+ if ( ! await this . dockerVolumeFileExists ( argv . containerExecutable , `${ this . gclRegistryPrefix } .crt` , gclRegistryCertVol ) ) {
463+ const opensslArgs = [
464+ "req" , "-newkey" , "rsa:4096" , "-nodes" , "-sha256" ,
465+ "-keyout" , `/certs/${ this . gclRegistryPrefix } .key` ,
466+ "-x509" , "-days" , "365" ,
467+ "-out" , `/certs/${ this . gclRegistryPrefix } .crt` ,
468+ "-subj" , `/CN=${ this . gclRegistryPrefix } ` ,
469+ "-addext" , `subjectAltName=DNS:${ this . gclRegistryPrefix } `
470+ ] ;
471+ const generateCertsInPlace = [
472+ argv . containerExecutable , "run" , "--rm" , "-v" , `${ gclRegistryCertVol } :/certs` , "--entrypoint" , "sh" , "alpine/openssl" , "-c" ,
473+ [
474+ "openssl" , ...opensslArgs ,
475+ "&&" , "mkdir" , "-p" , `/certs/${ this . gclRegistryPrefix } ` ,
476+ "&&" , "cp" , `/certs/${ this . gclRegistryPrefix } .crt` , `/certs/${ this . gclRegistryPrefix } /ca.crt` ,
477+ ] . join ( " " )
478+ ] ;
479+ await Utils . spawn ( generateCertsInPlace ) ;
480+ }
481+
482+ // create data volume
483+ try {
484+ await Utils . spawn ( [ argv . containerExecutable , "volume" , "create" , gclRegistryDataVol ] ) ;
485+ } catch ( err ) {
486+ // rethrow error if not 'already exists' (exitCode 125)
487+ if ( err instanceof Error && "exitCode" in err && err . exitCode !== 125 )
488+ throw err ;
489+ }
490+
491+ // create network
492+ try {
493+ await Utils . spawn ( [ argv . containerExecutable , "network" , "create" , gclRegistryNet ] ) ;
494+ } catch ( err ) {
495+ if ( err instanceof Error && "exitCode" in err && err . exitCode !== 125 )
496+ throw err ;
497+ }
498+
499+ await Utils . spawn ( [ argv . containerExecutable , "rm" , "-f" , this . gclRegistryPrefix ] ) ;
500+ await Utils . spawn ( [
501+ argv . containerExecutable , "run" , "-d" , "--name" , this . gclRegistryPrefix ,
502+ "--network" , gclRegistryNet ,
503+ "--volume" , `${ gclRegistryDataVol } :/var/lib/registry` ,
504+ "--volume" , `${ gclRegistryCertVol } :/certs:ro` ,
505+ "-e" , "REGISTRY_HTTP_ADDR=0.0.0.0:443" ,
506+ "-e" , `REGISTRY_HTTP_TLS_CERTIFICATE=/certs/${ this . gclRegistryPrefix } .crt` ,
507+ "-e" , `REGISTRY_HTTP_TLS_KEY=/certs/${ this . gclRegistryPrefix } .key` ,
508+ "registry"
509+ ] ) ;
510+ }
511+
512+ static async stopDockerRegistry ( containerExecutable : string ) : Promise < void > {
513+ await Utils . spawn ( [ containerExecutable , "rm" , "-f" , this . gclRegistryPrefix ] ) ;
514+ }
437515}
0 commit comments