lib/processExec.js
function processExec(opts, createOpts, execOpts, callback) {
	this.execRaw(createOpts, (createErr, exec) => {
		if( createErr ) { callback(createErr); return }
		exec.start(execOpts, (execErr, stream) => {
			if( execErr ) { callback(execErr); return }

			if( 'live' in opts && opts.live ) {
				// If the user wants live streams, give them a function to attach to the builtin demuxStream
				callback(null, (stdout, stderr) => {
					if( stdout || stderr ) {
						this.modem.demuxStream(stream, stdout, stderr)
					}
					// Allow an .on('end').
					return stream
				})
			} else {
				const results = {}
				let callbackCalled = false
				const callbackOnce = err => {
					if( !callbackCalled ) {
						callbackCalled = true
						if( err ) {
							Object.assign(results, {error: err})
						}
						// Inspect the exec and put that information into the results as well
						// Allow this to be turned off via option later
						// Workaround: if the user only has stdin and no stdout,
						// the process will sometimes not immediately end.
						let times = 10
						const inspectLoop = () => {
							exec.inspect((inspError, inspect) => {
								if( inspect.ExitCode !== null ) {
									if( times !== 10 ) {
										inspect.tries = 10 - times
									}

									Object.assign(results, {inspect})
									callback(inspError, results)
								} else {
									times--
									setTimeout(inspectLoop, 50)
								}
							})
						}
						inspectLoop()
					}
				}

				if( execOpts.Detach ) {
					// Bitbucket the stream's data, so that it can close.
					stream.on('data', () => {})
				}

				if( opts.stdin ) {
					// Send the process whatever the user's going for.
					if( isStream(opts.stdin) ) {
						opts.stdin.pipe(stream)
					} else {
						const sender = new Stream.Readable()
						sender.push(opts.stdin)
						sender.push(null)
						sender.pipe(stream)
					}
				}

				if( opts.stdout || opts.stderr ) {
					// Set up the battery to merge in its results when it's done. If it had an error, trigger the whole thing returning.
					const battery = new StreamBattery(['stdout', 'stderr'], (battError, battResults) => {
						Object.assign(results, battResults)
						if( battError ) {
							callbackOnce(battError)
						}
					})

					// Start the stream demuxing
					this.modem.demuxStream(stream, ...battery.streams)
					stream.on('end', () => battery.end())
				}

				// Wait for the exec to end.
				stream.on('end', callbackOnce)
				stream.on('close', callbackOnce)
				stream.on('error', callbackOnce)
			}
		})
	})
}
function processExec(opts, createOpts, execOpts, callback) {
	this.execRaw(createOpts, (createErr, exec) => {
		if( createErr ) { callback(createErr); return }
		exec.start(execOpts, (execErr, stream) => {
			if( execErr ) { callback(execErr); return }

			if( 'live' in opts && opts.live ) {
				// If the user wants live streams, give them a function to attach to the builtin demuxStream
				callback(null, (stdout, stderr) => {
					if( stdout || stderr ) {
						this.modem.demuxStream(stream, stdout, stderr)
					}
					// Allow an .on('end').
					return stream
				})
			} else {
				const results = {}
				let callbackCalled = false
				const callbackOnce = err => {
					if( !callbackCalled ) {
						callbackCalled = true
						if( err ) {
							Object.assign(results, {error: err})
						}
						// Inspect the exec and put that information into the results as well
						// Allow this to be turned off via option later
						// Workaround: if the user only has stdin and no stdout,
						// the process will sometimes not immediately end.
						let times = 10
						const inspectLoop = () => {
							exec.inspect((inspError, inspect) => {
								if( inspect.ExitCode !== null ) {
									if( times !== 10 ) {
										inspect.tries = 10 - times
									}

									Object.assign(results, {inspect})
									callback(inspError, results)
								} else {
									times--
									setTimeout(inspectLoop, 50)
								}
							})
						}
						inspectLoop()
					}
				}

				if( execOpts.Detach ) {
					// Bitbucket the stream's data, so that it can close.
					stream.on('data', () => {})
				}

				if( opts.stdin ) {
					// Send the process whatever the user's going for.
					if( isStream(opts.stdin) ) {
						opts.stdin.pipe(stream)
					} else {
						const sender = new Stream.Readable()
						sender.push(opts.stdin)
						sender.push(null)
						sender.pipe(stream)
					}
				}

				if( opts.stdout || opts.stderr ) {
					// Set up the battery to merge in its results when it's done. If it had an error, trigger the whole thing returning.
					const battery = new StreamBattery(['stdout', 'stderr'], (battError, battResults) => {
						Object.assign(results, battResults)
						if( battError ) {
							callbackOnce(battError)
						}
					})

					// Start the stream demuxing
					this.modem.demuxStream(stream, ...battery.streams)
					stream.on('end', () => battery.end())
				}

				// Wait for the exec to end.
				stream.on('end', callbackOnce)
				stream.on('close', callbackOnce)
				stream.on('error', callbackOnce)
			}
		})
	})
}
function processExec ( opts , createOpts , execOpts , callback ) { this . execRaw ( createOpts , ( createErr , exec ) => { if ( createErr ) { callback ( createErr ) ; return } exec . start ( execOpts , ( execErr , stream ) => { if ( execErr ) { callback ( execErr ) ; return } if ( 'live' in opts && opts . live ) { // If the user wants live streams, give them a function to attach to the builtin demuxStream callback ( null , ( stdout , stderr ) => { if ( stdout || stderr ) { this . modem . demuxStream ( stream , stdout , stderr ) } // Allow an .on('end'). return stream } ) } else { const results = { } let callbackCalled = false const callbackOnce = err => { if ( ! callbackCalled ) { callbackCalled = true if ( err ) { Object . assign ( results , { error : err } ) } // Inspect the exec and put that information into the results as well // Allow this to be turned off via option later // Workaround: if the user only has stdin and no stdout, // the process will sometimes not immediately end. let times = 10 const inspectLoop = ( ) => { exec . inspect ( ( inspError , inspect ) => { if ( inspect . ExitCode !== null ) { if ( times !== 10 ) { inspect . tries = 10 - times } Object . assign ( results , { inspect } ) callback ( inspError , results ) } else { times -- setTimeout ( inspectLoop , 50 ) } } ) } inspectLoop ( ) } } if ( execOpts . Detach ) { // Bitbucket the stream's data, so that it can close. stream . on ( 'data' , ( ) => { } ) } if ( opts . stdin ) { // Send the process whatever the user's going for. if ( isStream ( opts . stdin ) ) { opts . stdin . pipe ( stream ) } else { const sender = new Stream . Readable ( ) sender . push ( opts . stdin ) sender . push ( null ) sender . pipe ( stream ) } } if ( opts . stdout || opts . stderr ) { // Set up the battery to merge in its results when it's done. If it had an error, trigger the whole thing returning. const battery = new StreamBattery ( [ 'stdout' , 'stderr' ] , ( battError , battResults ) => { Object . assign ( results , battResults ) if ( battError ) { callbackOnce ( battError ) } } ) // Start the stream demuxing this . modem . demuxStream ( stream , ... battery . streams ) stream . on ( 'end' , ( ) => battery . end ( ) ) } // Wait for the exec to end. stream . on ( 'end' , callbackOnce ) stream . on ( 'close' , callbackOnce ) stream . on ( 'error' , callbackOnce ) } } ) } ) }
A version of exec that wraps Dockerode's, reducing the stream handling for most use cases.
@param {Array<string>} Cmd The command to execute, in exec array form.
@param {Object} opts Object of options.
@param {boolean} opts.stdout True/false, if true, user will receive a stdout key with the output
@param {boolean} opts.stderr True/false, if true, user will receive a stderr key with the output
@param {string|Stream} opts.stdin If this key exists, whatever the user puts in here will be sent to the container.
@param {boolean} opts.live If true, the user will receive a function to plug into the demuxer, instead of finished values.
@param {Object} createOpts Args for the normal exec.create, processed via {@link processArgs}
@param {Object} execOpts Args for the normal exec.start, processed via {@link processArgs}
@param {function} callback Will be called with either a function to plug into demuxer, or an object with whatever output keys the user requested.
A version of exec that wraps Dockerode s reducing the stream handling for most use cases .