Skip to content

Commit da95147

Browse files
committed
Merge pull request #39 from clue-labs/execcreate
Add execCreate() params instead of config object
2 parents 5e82b2b + fcb20e6 commit da95147

7 files changed

Lines changed: 119 additions & 27 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,12 @@ $stream->on('close', function () {
223223

224224
See also the [streaming exec example](examples/exec-stream.php) and the [exec benchmark example](examples/benchmark-exec.php).
225225

226+
The TTY mode should be set depending on whether your command needs a TTY
227+
or not. Note that toggling the TTY mode affects how/whether you can access
228+
the STDERR stream and also has a significant impact on performance for
229+
larger streams (relevant for 100 MiB and above). See also the TTY mode
230+
on the `execStart*()` call.
231+
226232
Running this benchmark on my personal (rather mediocre) VM setup reveals that
227233
the benchmark achieves a throughput of ~300 MiB/s while the (totally unfair)
228234
comparison script using the plain Docker client only yields ~100 MiB/s.

examples/benchmark-exec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
$factory = new Factory($loop);
2424
$client = $factory->createClient();
2525

26-
$client->execCreate($container, array('Cmd' => $cmd, 'AttachStdout' => true, 'AttachStderr' => true))->then(function ($info) use ($client) {
26+
$client->execCreate($container, $cmd)->then(function ($info) use ($client) {
2727
$stream = $client->execStartStream($info['Id'], true);
2828

2929
$start = microtime(true);

examples/exec-inspect.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
$factory = new Factory($loop);
2424
$client = $factory->createClient();
2525

26-
$client->execCreate($container, array('Cmd' => $cmd, 'AttachStdout' => true, 'AttachStderr' => true, 'Tty' => true))->then(function ($info) use ($client) {
26+
$client->execCreate($container, $cmd, true)->then(function ($info) use ($client) {
2727
echo 'Created with info: ' . json_encode($info) . PHP_EOL;
2828

2929
return $client->execInspect($info['Id']);

examples/exec-stream.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
$out = new Stream(STDOUT, $loop);
2828
$out->pause();
2929

30-
$client->execCreate($container, array('Cmd' => $cmd, 'AttachStdout' => true, 'AttachStderr' => true, 'Tty' => true))->then(function ($info) use ($client, $out) {
30+
$client->execCreate($container, $cmd, true)->then(function ($info) use ($client, $out) {
3131
$stream = $client->execStartStream($info['Id'], true);
3232
$stream->pipe($out);
3333

src/Client.php

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -882,21 +882,61 @@ public function imageSearch($term)
882882
/**
883883
* Sets up an exec instance in a running container id
884884
*
885-
* @param string $container container ID
886-
* @param array $config `array('Cmd' => 'date')` (see link)
885+
* The $command should be given as an array of strings (the command plus
886+
* arguments). Alternatively, you can also pass a single command line string
887+
* which will then be wrapped in a shell process.
888+
*
889+
* The TTY mode should be set depending on whether your command needs a TTY
890+
* or not. Note that toggling the TTY mode affects how/whether you can access
891+
* the STDERR stream and also has a significant impact on performance for
892+
* larger streams (relevant for 100 MiB and above). See also the TTY mode
893+
* on the `execStart*()` call:
894+
* - create=false, start=false:
895+
* STDOUT/STDERR are multiplexed into separate streams + quite fast.
896+
* This is the default mode, also for `docker exec`.
897+
* - create=true, start=true:
898+
* STDOUT and STDERR are mixed into a single stream + relatively slow.
899+
* This is how `docker exec -t` works internally.
900+
* - create=false, start=true
901+
* STDOUT is streamed, STDERR can not be accessed at all + fastest mode.
902+
* This looks strange to you? It probably is. See also the benchmarking example.
903+
* - create=true, start=false
904+
* STDOUT/STDERR are multiplexed into separate streams + relatively slow
905+
* This looks strange to you? It probably is. Consider using the first option instead.
906+
*
907+
* @param string $container container ID
908+
* @param string|array $cmd Command to run specified as an array of strings or a single command string
909+
* @param boolean $tty TTY mode
910+
* @param boolean $stdin attaches to STDIN of the exec command
911+
* @param boolean $stdout attaches to STDOUT of the exec command
912+
* @param boolean $stderr attaches to STDERR of the exec command
913+
* @param string|int $user user-specific exec, otherwise defaults to main container user (requires API v1.19+ / Docker v1.7+)
914+
* @param boolean $privileged privileged exec with all capabilities enabled (requires API v1.19+ / Docker v1.7+)
887915
* @return PromiseInterface Promise<array> with exec ID in the form of `array("Id" => $execId)`
888916
* @link https://docs.docker.com/reference/api/docker_remote_api_v1.15/#exec-create
889917
*/
890-
public function execCreate($container, $config)
918+
public function execCreate($container, $cmd, $tty = false, $stdin = false, $stdout = true, $stderr = true, $user = '', $privileged = false)
891919
{
920+
if (!is_array($cmd)) {
921+
$cmd = array('sh', '-c', (string)$cmd);
922+
}
923+
892924
return $this->postJson(
893925
$this->uri->expand(
894926
'/containers/{container}/exec',
895927
array(
896928
'container' => $container
897929
)
898930
),
899-
$config
931+
array(
932+
'Cmd' => $cmd,
933+
'Tty' => !!$tty,
934+
'AttachStdin' => !!$stdin,
935+
'AttachStdout' => !!$stdout,
936+
'AttachStderr' => !!$stderr,
937+
'User' => $user,
938+
'Privileged' => !!$privileged,
939+
)
900940
)->then(array($this->parser, 'expectJson'));
901941
}
902942

tests/ClientTest.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,17 @@ public function testImageSearch()
379379
public function testExecCreate()
380380
{
381381
$json = array();
382-
$config = array();
383382
$this->expectRequestFlow('post', '/containers/123/exec', $this->createResponseJson($json), 'expectJson');
384383

385-
$this->expectPromiseResolveWith($json, $this->client->execCreate(123, $config));
384+
$this->expectPromiseResolveWith($json, $this->client->execCreate(123, array('env')));
385+
}
386+
387+
public function testExecCreateStringCommand()
388+
{
389+
$json = array();
390+
$this->expectRequestFlow('post', '/containers/123/exec', $this->createResponseJson($json), 'expectJson');
391+
392+
$this->expectPromiseResolveWith($json, $this->client->execCreate(123, 'env'));
386393
}
387394

388395
public function testExecDetached()

tests/FunctionalClientTest.php

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,7 @@ public function testStartRunning()
104104
*/
105105
public function testExecCreateWhileRunning($container)
106106
{
107-
$promise = $this->client->execCreate($container, array(
108-
'Cmd' => array('echo', '-n', 'hello', 'world'),
109-
'AttachStdout' => true,
110-
'AttachStderr' => true,
111-
'Tty' => true
112-
));
107+
$promise = $this->client->execCreate($container, array('echo', '-n', 'hello', 'world'));
113108
$exec = Block\await($promise, $this->loop);
114109

115110
$this->assertTrue(is_array($exec));
@@ -158,18 +153,67 @@ public function testExecInspectAfterRunning($exec)
158153
$this->assertEquals(0, $info['ExitCode']);
159154
}
160155

156+
/**
157+
* @depends testStartRunning
158+
* @param string $container
159+
*/
160+
public function testExecStringCommandWithOutputWhileRunning($container)
161+
{
162+
$promise = $this->client->execCreate($container, 'echo -n hello world');
163+
$exec = Block\await($promise, $this->loop);
164+
165+
$this->assertTrue(is_array($exec));
166+
$this->assertTrue(is_string($exec['Id']));
167+
168+
$promise = $this->client->execStart($exec['Id'], true);
169+
$output = Block\await($promise, $this->loop);
170+
171+
$this->assertEquals('hello world', $output);
172+
}
173+
174+
/**
175+
* @depends testStartRunning
176+
* @param string $container
177+
*/
178+
public function testExecUserSpecificCommandWithOutputWhileRunning($container)
179+
{
180+
$promise = $this->client->execCreate($container, 'whoami', true, false, true, true, 'nobody');
181+
$exec = Block\await($promise, $this->loop);
182+
183+
$this->assertTrue(is_array($exec));
184+
$this->assertTrue(is_string($exec['Id']));
185+
186+
$promise = $this->client->execStart($exec['Id'], true);
187+
$output = Block\await($promise, $this->loop);
188+
189+
$this->assertEquals('nobody', rtrim($output));
190+
}
191+
192+
/**
193+
* @depends testStartRunning
194+
* @param string $container
195+
*/
196+
public function testExecStringCommandWithStderrOutputWhileRunning($container)
197+
{
198+
$promise = $this->client->execCreate($container, 'echo -n hello world >&2', true);
199+
$exec = Block\await($promise, $this->loop);
200+
201+
$this->assertTrue(is_array($exec));
202+
$this->assertTrue(is_string($exec['Id']));
203+
204+
$promise = $this->client->execStart($exec['Id'], true);
205+
$output = Block\await($promise, $this->loop);
206+
207+
$this->assertEquals('hello world', $output);
208+
}
209+
161210
/**
162211
* @depends testStartRunning
163212
* @param string $container
164213
*/
165214
public function testExecStreamEmptyOutputWhileRunning($container)
166215
{
167-
$promise = $this->client->execCreate($container, array(
168-
'Cmd' => array('true'),
169-
'AttachStdout' => true,
170-
'AttachStderr' => true,
171-
'Tty' => true
172-
));
216+
$promise = $this->client->execCreate($container, array('true'));
173217
$exec = Block\await($promise, $this->loop);
174218

175219
$this->assertTrue(is_array($exec));
@@ -189,12 +233,7 @@ public function testExecStreamEmptyOutputWhileRunning($container)
189233
*/
190234
public function testExecDetachedWhileRunning($container)
191235
{
192-
$promise = $this->client->execCreate($container, array(
193-
'Cmd' => array('sleep', '10'),
194-
'AttachStdout' => true,
195-
'AttachStderr' => true,
196-
'Tty' => true
197-
));
236+
$promise = $this->client->execCreate($container, array('sleep', '10'));
198237
$exec = Block\await($promise, $this->loop);
199238

200239
$this->assertTrue(is_array($exec));

0 commit comments

Comments
 (0)