Skip to content

Commit fd6dc81

Browse files
committed
Merge pull request #32 from clue-labs/events
Add events() and eventsStream() API endpoints
2 parents 90061c2 + 9a1810f commit fd6dc81

File tree

5 files changed

+155
-0
lines changed

5 files changed

+155
-0
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ The following API endpoints take advantage of [JSON streaming](https://en.wikipe
219219
```php
220220
$client->imageCreate();
221221
$client->imagePush();
222+
$client->events();
222223
```
223224

224225
What this means is that these endpoints actually emit any number of progress
@@ -271,6 +272,7 @@ a [`Stream`](https://github.com/reactphp/stream) instance instead:
271272
```php
272273
$stream = $client->imageCreateStream();
273274
$stream = $client->imagePushStream();
275+
$stream = $client->eventsStream();
274276
```
275277

276278
The resulting stream will emit the following events:

Diff for: examples/events.php

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
// this simple example displays all docker events that happen in the next 10s.
3+
// try starting / removing a container in the meantime to see some output.
4+
5+
require __DIR__ . '/../vendor/autoload.php';
6+
7+
use React\EventLoop\Factory as LoopFactory;
8+
use Clue\React\Docker\Factory;
9+
10+
$loop = LoopFactory::create();
11+
12+
$factory = new Factory($loop);
13+
$client = $factory->createClient();
14+
15+
// get a list of all events that happened up until this point
16+
// expect this list to be limited to the last 64 (or so) events
17+
// $events = $client->events(0, microtime(true));
18+
19+
// get a list of all events that happened in the last 10 seconds
20+
// $events = $client->events(microtime(true) - 10, microtime(true));
21+
22+
// stream all events for 10 seconds
23+
$stream = $client->eventsStream(null, microtime(true) + 10.0);
24+
25+
$stream->on('progress', function ($event) {
26+
echo json_encode($event) . PHP_EOL;
27+
});
28+
29+
$stream->on('error', 'printf');
30+
31+
$loop->run();

Diff for: src/Client.php

+83
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,89 @@ public function version()
9393
return $this->browser->get('/version')->then(array($this->parser, 'expectJson'));
9494
}
9595

96+
/**
97+
* Get container events from docker
98+
*
99+
* This is a JSON streaming API endpoint that resolves with an array of all
100+
* individual progress events.
101+
*
102+
* If you need to access the individual progress events as they happen, you
103+
* should consider using `eventsStream()` instead.
104+
*
105+
* Note that this method will buffer all events until the stream closes.
106+
* This means that you SHOULD pass a timestamp for `$until` so that this
107+
* method only polls the given time interval and then resolves.
108+
*
109+
* The optional `$filters` parameter can be used to only get events for
110+
* certain event types, images and/or containers etc. like this:
111+
* <code>
112+
* $filters = array(
113+
* 'image' => array('ubuntu', 'busybox'),
114+
* 'event' => array('create')
115+
* );
116+
* </code>
117+
*
118+
* @param float|null $since timestamp used for polling
119+
* @param float|null $until timestamp used for polling
120+
* @param array $filters (optional) filters to apply (requires API v1.16+ / Docker v1.4+)
121+
* @return PromiseInterface Promise<array> array of event objects
122+
* @link https://docs.docker.com/reference/api/docker_remote_api_v1.15/#monitor-dockers-events
123+
* @uses self::eventsStream()
124+
* @see self::eventsStream()
125+
*/
126+
public function events($since = null, $until = null, $filters = array())
127+
{
128+
return $this->streamingParser->deferredStream(
129+
$this->eventsStream($since, $until, $filters),
130+
'progress'
131+
);
132+
}
133+
134+
/**
135+
* Get container events from docker
136+
*
137+
* This is a JSON streaming API endpoint that returns a stream instance.
138+
*
139+
* The resulting stream will emit the following events:
140+
* - progress: for *each* element in the update stream
141+
* - error: once if an error occurs, will close() stream then
142+
* - close: once the stream ends (either finished or after "error")
143+
*
144+
* Please note that the resulting stream does not emit any "data" events, so
145+
* you will not be able to pipe() its events into another `WritableStream`.
146+
*
147+
* The optional `$filters` parameter can be used to only get events for
148+
* certain event types, images and/or containers etc. like this:
149+
* <code>
150+
* $filters = array(
151+
* 'image' => array('ubuntu', 'busybox'),
152+
* 'event' => array('create')
153+
* );
154+
* </code>
155+
*
156+
* @param float|null $since timestamp used for polling
157+
* @param float|null $until timestamp used for polling
158+
* @param array $filters (optional) filters to apply (requires API v1.16+ / Docker v1.4+)
159+
* @return ReadableStreamInterface stream of event objects
160+
* @link https://docs.docker.com/reference/api/docker_remote_api_v1.15/#monitor-dockers-events
161+
* @see self::events()
162+
*/
163+
public function eventsStream($since = null, $until = null, $filters = array())
164+
{
165+
return $this->streamingParser->parseJsonStream(
166+
$this->browser->withOptions(array('streaming' => true))->get(
167+
$this->uri->expand(
168+
'/events{?since,until,filters}',
169+
array(
170+
'since' => $since,
171+
'until' => $until,
172+
'filters' => $filters ? json_encode($filters) : null
173+
)
174+
)
175+
)
176+
);
177+
}
178+
96179
/**
97180
* List containers
98181
*

Diff for: tests/ClientTest.php

+24
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,30 @@ public function testPing()
3737
$this->expectPromiseResolveWith($body, $this->client->ping());
3838
}
3939

40+
public function testEvents()
41+
{
42+
$json = array();
43+
$stream = $this->getMock('React\Stream\ReadableStreamInterface');
44+
45+
$this->expectRequest('GET', '/events', $this->createResponseJsonStream($json));
46+
$this->streamingParser->expects($this->once())->method('parseJsonStream')->will($this->returnValue($stream));
47+
$this->streamingParser->expects($this->once())->method('deferredStream')->with($this->equalTo($stream), $this->equalTo('progress'))->will($this->returnPromise($json));
48+
49+
$this->expectPromiseResolveWith($json, $this->client->events());
50+
}
51+
52+
public function testEventsArgs()
53+
{
54+
$json = array();
55+
$stream = $this->getMock('React\Stream\ReadableStreamInterface');
56+
57+
$this->expectRequest('GET', '/events?since=10&until=20&filters=%7B%22image%22%3A%5B%22busybox%22%2C%22ubuntu%22%5D%7D', $this->createResponseJsonStream($json));
58+
$this->streamingParser->expects($this->once())->method('parseJsonStream')->will($this->returnValue($stream));
59+
$this->streamingParser->expects($this->once())->method('deferredStream')->with($this->equalTo($stream), $this->equalTo('progress'))->will($this->returnPromise($json));
60+
61+
$this->expectPromiseResolveWith($json, $this->client->events(10, 20, array('image' => array('busybox', 'ubuntu'))));
62+
}
63+
4064
public function testContainerCreate()
4165
{
4266
$json = array();

Diff for: tests/FunctionalClientTest.php

+15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public function testCreateStartAndRemoveContainer()
4646
$this->assertNotNull($container['Id']);
4747
$this->assertNull($container['Warnings']);
4848

49+
$start = microtime(true);
50+
4951
$promise = $this->client->containerStart($container['Id']);
5052
$ret = Block\await($promise, $this->loop);
5153

@@ -55,6 +57,19 @@ public function testCreateStartAndRemoveContainer()
5557
$ret = Block\await($promise, $this->loop);
5658

5759
$this->assertEquals('', $ret);
60+
61+
$end = microtime(true);
62+
63+
// get all events between starting and removing for this container
64+
$promise = $this->client->events($start, $end, array('container' => array($container['Id'])));
65+
$ret = Block\await($promise, $this->loop);
66+
67+
// expects "start", "kill", "die", "destroy" events
68+
$this->assertEquals(4, count($ret));
69+
$this->assertEquals('start', $ret[0]['status']);
70+
$this->assertEquals('kill', $ret[1]['status']);
71+
$this->assertEquals('die', $ret[2]['status']);
72+
$this->assertEquals('destroy', $ret[3]['status']);
5873
}
5974

6075
/**

0 commit comments

Comments
 (0)