Repository navigation
Expand file tree
/
Copy pathInteractiveExecResourceTest.php
More file actions
124 lines (118 loc) · 5.53 KB
/
Copy pathInteractiveExecResourceTest.php
File metadata and controls
124 lines (118 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<?php
declare(strict_types=1);
namespace Docker\Tests\Resource;
use Docker\API\Model\ContainersCreatePostBody;
use Docker\API\Model\ContainersIdExecPostBody;
use Docker\API\Model\ExecIdStartPostBody;
use Docker\Exception\InteractiveExecTimeoutException;
use Docker\Tests\TestCase;
class InteractiveExecResourceTest extends TestCase
{
public function testTtyInputAndCombinedOutput(): void
{
$docker = self::getDocker();
$container = new ContainersCreatePostBody();
$container->setImage('busybox:latest');
$container->setCmd(['sleep', '60']);
$created = $docker->containerCreate($container);
try {
$docker->containerStart($created->getId());
$create = new ContainersIdExecPostBody();
$create->setAttachStdin(true);
$create->setAttachStdout(true);
$create->setAttachStderr(true);
$create->setTty(true);
$create->setCmd(['sh', '-c', 'read line; printf "ok:%s\\n" "$line"; printf error >&2']);
$exec = $docker->containerExec($created->getId(), $create);
$body = new ExecIdStartPostBody();
$body->setTty(true);
$session = $docker->execStartInteractive($exec->getId(), $body, 5000);
$output = '';
$session->onStdout(static function (string $chunk) use (&$output): void { $output .= $chunk; });
$session->onStderr(static function (): void { self::fail('TTY stderr was separated.'); });
self::assertSame(6, $session->writeStdin("hello\n"));
// TTY input is a terminal: EOF on the socket can cut off output.
// This command exits after one line, without needing input EOF.
$session->wait();
self::assertStringContainsString('ok:hello', $output);
self::assertStringContainsString('error', $output);
} finally {
$docker->containerDelete($created->getId(), ['force' => true]);
}
}
public function testStdinEofAndNonzeroExitAgainstDocker(): void
{
$docker = self::getDocker();
$container = new ContainersCreatePostBody();
$container->setImage('busybox:latest');
$container->setCmd(['sleep', '60']);
$created = $docker->containerCreate($container);
try {
$docker->containerStart($created->getId());
$create = new ContainersIdExecPostBody();
$create->setAttachStdin(true);
$create->setAttachStdout(true);
$create->setAttachStderr(true);
$create->setTty(false);
$create->setCmd(['sh', '-c', 'cat; printf error >&2; exit 7']);
$exec = $docker->containerExec($created->getId(), $create);
$body = new ExecIdStartPostBody();
$body->setTty(false);
$session = $docker->execStartInteractive($exec->getId(), $body, 5000);
$stdout = $stderr = '';
$session->onStdout(static function (string $chunk) use (&$stdout): void { $stdout .= $chunk; });
$session->onStderr(static function (string $chunk) use (&$stderr): void { $stderr .= $chunk; });
$input = str_repeat("binary\0\xff\n", 8192);
$offset = 0;
while ($offset < \strlen($input)) {
$offset += $session->writeStdin(substr($input, $offset));
$session->poll(1);
}
$session->closeStdin();
$session->wait();
self::assertSame($input, $stdout);
self::assertSame('error', $stderr);
$info = $docker->execInspect($exec->getId());
self::assertFalse($info->getRunning());
self::assertSame(7, $info->getExitCode());
} finally {
$docker->containerDelete($created->getId(), ['force' => true]);
}
}
public function testSilentExecHeartbeatAndContainerTimeout(): void
{
$docker = self::getDocker();
$container = new ContainersCreatePostBody();
$container->setImage('busybox:latest');
$container->setCmd(['sleep', '60']);
$created = $docker->containerCreate($container);
try {
$docker->containerStart($created->getId());
$create = new ContainersIdExecPostBody();
$create->setAttachStdout(true);
$create->setAttachStderr(true);
$create->setCmd(['timeout', '-s', 'KILL', '1', 'sleep', '30']);
$exec = $docker->containerExec($created->getId(), $create);
$session = $docker->execStartInteractive($exec->getId(), null, 180);
$ticks = 0;
try {
$session->wait(static function () use (&$ticks): void { ++$ticks; }, 30);
self::fail('The streaming deadline was ignored.');
} catch (InteractiveExecTimeoutException $expected) {
self::assertGreaterThanOrEqual(3, $ticks);
}
// Disconnecting output is not cancellation. Keep the container-side
// timeout, which terminates only this exec, not the whole container.
self::assertTrue($docker->execInspect($exec->getId())->getRunning());
$deadline = hrtime(true) + 3000000000;
do {
usleep(50000);
$info = $docker->execInspect($exec->getId());
} while ($info->getRunning() && hrtime(true) < $deadline);
self::assertFalse($info->getRunning());
self::assertNotSame(0, $info->getExitCode());
} finally {
$docker->containerDelete($created->getId(), ['force' => true]);
}
}
}