@@ -426,18 +426,37 @@ export default class Environment {
426426 } )
427427 }
428428
429+ /**
430+ * Get an array suitable for passing to `spawn` to execute a docker command with environment variables for nixster
431+ *
432+ * @param sessionParameters SessionParameters that define the container to exec inside and the command to run
433+ * @param daemonize Should the Docker command be run with the '-d' flag?
434+ */
435+ private getDockerExecCommand ( sessionParameters : SessionParameters , daemonize : boolean ) : Array < string > {
436+ const nixLocation = nix . location ( this . name )
437+ const shellArgs = [
438+ 'exec' , '--tty' ,
439+ // Prepend the environment path to the PATH variable
440+ '--env' , `PATH=${ nixLocation } /bin:${ nixLocation } /sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` ,
441+ sessionParameters . containerId , sessionParameters . command
442+ ]
443+
444+ if ( daemonize ) shellArgs . splice ( 1 , 0 , '-d' )
445+
446+ return shellArgs
447+ }
448+
429449 /**
430450 * Get an array suitable for passing to `spawn` to execute a docker command with default args for nixster
431451 *
432- * @param dockerCommand The Docker command to execute
433452 * @param sessionParameters SessionParameters to use for limiting Docker's resource usage
434453 * @param daemonize Should the Docker command be run with the '-d' flag?
435454 */
436- private async getDockerShellArgs ( dockerCommand : string , sessionParameters : SessionParameters , daemonize : boolean = false ) : Promise < Array < string > > {
455+ private getDockerRunCommand ( sessionParameters : SessionParameters , daemonize : boolean = false ) : Array < string > {
437456 const { command, cpuShares, memoryLimit } = sessionParameters
438457 const nixLocation = nix . location ( this . name )
439458 const shellArgs = [
440- dockerCommand , '--interactive' , '--tty' , '--rm' ,
459+ 'run' , '--interactive' , '--tty' , '--rm' ,
441460 // Prepend the environment path to the PATH variable
442461 '--env' , `PATH=${ nixLocation } /bin:${ nixLocation } /sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` ,
443462 // We also need to tell R where to find libraries
@@ -491,7 +510,7 @@ export default class Environment {
491510 break
492511 case Platform . DOCKER :
493512 shellName = 'docker'
494- shellArgs = await this . getDockerShellArgs ( 'run' , sessionParameters , false )
513+ shellArgs = this . getDockerRunCommand ( sessionParameters , false )
495514 break
496515 default :
497516 shellName = 'bash'
@@ -544,11 +563,26 @@ export default class Environment {
544563 }
545564 }
546565
566+ const shellProcess = this . runInShell ( shellPath , shellArgs , vars , stdout , stdin )
567+
568+ if ( platform === Platform . UNIX && command ) shellProcess . write ( command + '\r' )
569+ }
570+
571+ /**
572+ * Spawn a new shell with the default setup for nixster, attaching stdout and stdin
573+ *
574+ * @param shellPath The command to run
575+ * @param shellArgs Arguments to pass to the command in `shellpath`
576+ * @param environmentVariables Environment variables to set in the shell
577+ * @param stdout Stream
578+ * @param stdin Stream
579+ */
580+ private runInShell ( shellPath : string , shellArgs : Array < string > , environmentVariables : { [ key : string ] : string } , stdout : stream . Writable , stdin : stream . Readable ) {
547581 const shellProcess = pty . spawn ( shellPath , shellArgs , {
548582 name : 'xterm-color' ,
549583 cols : 120 ,
550584 rows : 30 ,
551- env : vars
585+ env : environmentVariables
552586 } )
553587 shellProcess . on ( 'data' , data => {
554588 stdout . write ( data )
@@ -575,8 +609,23 @@ export default class Environment {
575609 }
576610 shellProcess . write ( data )
577611 } )
612+ return shellProcess
613+ }
578614
579- if ( platform === Platform . UNIX && command ) shellProcess . write ( command + '\r' )
615+ /**
616+ * Attach to a running container.
617+ *
618+ * @param sessionParameters The stdout and stdin attributes from here are used to connect to the shell
619+ */
620+ async attach ( sessionParameters : SessionParameters ) {
621+ if ( sessionParameters . platform !== Platform . DOCKER ) {
622+ throw new Error ( 'Attach is only valid for docker' )
623+ }
624+
625+ if ( ! this . containerIsRunning ( sessionParameters . containerId ) ) {
626+ throw new Error ( `Container ${ sessionParameters . containerId } is not running.` )
627+ }
628+ this . runInShell ( 'docker' , [ 'attach' , sessionParameters . containerId ] , { } , sessionParameters . stdout , sessionParameters . stdin )
580629 }
581630
582631 /**
@@ -602,15 +651,33 @@ export default class Environment {
602651 *
603652 * Returns the short ID of the container that is running.
604653 */
605- async execute ( sessionParameters : SessionParameters ) : Promise < string > {
654+ async start ( sessionParameters : SessionParameters ) : Promise < string > {
655+ if ( sessionParameters . platform !== Platform . DOCKER ) {
656+ throw new Error ( 'Start is only valid with the Docker platform.' )
657+ }
658+
659+ const dockerProcess = await spawn ( 'docker' , this . getDockerRunCommand ( sessionParameters , true ) )
660+ return dockerProcess . toString ( ) . trim ( ) . substr ( 0 , DOCKER_CONTAINER_ID_SHORT_LENGTH )
661+ }
662+
663+ /**
664+ * Execute a command in a docker container. Will output the results of the command, or daemonize it in which case an
665+ * empty string is returned
666+ *
667+ * @param sessionParameters Contains the `containerId` and `command` that define where and what to execute
668+ * @param daemonize run the command in the background (pass -d flag to docker)
669+ */
670+ async execute ( sessionParameters : SessionParameters , daemonize : boolean = false ) : Promise < string > {
606671 if ( sessionParameters . platform !== Platform . DOCKER ) {
607672 throw new Error ( 'Execute is only valid with the Docker platform.' )
608673 }
609674
610- const shellArgs = await this . getDockerShellArgs ( 'run' , sessionParameters , true )
675+ if ( ! this . containerIsRunning ( sessionParameters . containerId ) ) {
676+ throw new Error ( `Container ${ sessionParameters . containerId } is not running` )
677+ }
611678
612- const dockerProcess = await spawn ( 'docker' , shellArgs )
613- return dockerProcess . toString ( ) . trim ( ) . substr ( 0 , DOCKER_CONTAINER_ID_SHORT_LENGTH )
679+ const dockerProcess = await spawn ( 'docker' , this . getDockerExecCommand ( sessionParameters , daemonize ) )
680+ return dockerProcess . toString ( ) . trim ( )
614681 }
615682
616683 /**
0 commit comments