diff --git a/README.md b/README.md
index 42df5d3..02660c2 100644
--- a/README.md
+++ b/README.md
@@ -219,6 +219,7 @@ The following API endpoints take advantage of [JSON streaming](https://en.wikipe
```php
$client->imageCreate();
$client->imagePush();
+$client->events();
```
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:
```php
$stream = $client->imageCreateStream();
$stream = $client->imagePushStream();
+$stream = $client->eventsStream();
```
The resulting stream will emit the following events:
diff --git a/examples/events.php b/examples/events.php
new file mode 100644
index 0000000..8750fb7
--- /dev/null
+++ b/examples/events.php
@@ -0,0 +1,31 @@
+createClient();
+
+// get a list of all events that happened up until this point
+// expect this list to be limited to the last 64 (or so) events
+// $events = $client->events(0, microtime(true));
+
+// get a list of all events that happened in the last 10 seconds
+// $events = $client->events(microtime(true) - 10, microtime(true));
+
+// stream all events for 10 seconds
+$stream = $client->eventsStream(null, microtime(true) + 10.0);
+
+$stream->on('progress', function ($event) {
+ echo json_encode($event) . PHP_EOL;
+});
+
+$stream->on('error', 'printf');
+
+$loop->run();
diff --git a/src/Client.php b/src/Client.php
index 8c44da2..692b2ca 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -93,6 +93,89 @@ public function version()
return $this->browser->get('/version')->then(array($this->parser, 'expectJson'));
}
+ /**
+ * Get container events from docker
+ *
+ * This is a JSON streaming API endpoint that resolves with an array of all
+ * individual progress events.
+ *
+ * If you need to access the individual progress events as they happen, you
+ * should consider using `eventsStream()` instead.
+ *
+ * Note that this method will buffer all events until the stream closes.
+ * This means that you SHOULD pass a timestamp for `$until` so that this
+ * method only polls the given time interval and then resolves.
+ *
+ * The optional `$filters` parameter can be used to only get events for
+ * certain event types, images and/or containers etc. like this:
+ *
+ * $filters = array(
+ * 'image' => array('ubuntu', 'busybox'),
+ * 'event' => array('create')
+ * );
+ *
+ *
+ * @param float|null $since timestamp used for polling
+ * @param float|null $until timestamp used for polling
+ * @param array $filters (optional) filters to apply (requires API v1.16+ / Docker v1.4+)
+ * @return PromiseInterface Promise array of event objects
+ * @link https://docs.docker.com/reference/api/docker_remote_api_v1.15/#monitor-dockers-events
+ * @uses self::eventsStream()
+ * @see self::eventsStream()
+ */
+ public function events($since = null, $until = null, $filters = array())
+ {
+ return $this->streamingParser->deferredStream(
+ $this->eventsStream($since, $until, $filters),
+ 'progress'
+ );
+ }
+
+ /**
+ * Get container events from docker
+ *
+ * This is a JSON streaming API endpoint that returns a stream instance.
+ *
+ * The resulting stream will emit the following events:
+ * - progress: for *each* element in the update stream
+ * - error: once if an error occurs, will close() stream then
+ * - close: once the stream ends (either finished or after "error")
+ *
+ * Please note that the resulting stream does not emit any "data" events, so
+ * you will not be able to pipe() its events into another `WritableStream`.
+ *
+ * The optional `$filters` parameter can be used to only get events for
+ * certain event types, images and/or containers etc. like this:
+ *
+ * $filters = array(
+ * 'image' => array('ubuntu', 'busybox'),
+ * 'event' => array('create')
+ * );
+ *
+ *
+ * @param float|null $since timestamp used for polling
+ * @param float|null $until timestamp used for polling
+ * @param array $filters (optional) filters to apply (requires API v1.16+ / Docker v1.4+)
+ * @return ReadableStreamInterface stream of event objects
+ * @link https://docs.docker.com/reference/api/docker_remote_api_v1.15/#monitor-dockers-events
+ * @see self::events()
+ */
+ public function eventsStream($since = null, $until = null, $filters = array())
+ {
+ return $this->streamingParser->parseJsonStream(
+ $this->browser->withOptions(array('streaming' => true))->get(
+ $this->uri->expand(
+ '/events{?since,until,filters}',
+ array(
+ 'since' => $since,
+ 'until' => $until,
+ 'filters' => $filters ? json_encode($filters) : null
+ )
+ )
+ )
+ );
+ }
+
/**
* List containers
*
diff --git a/tests/ClientTest.php b/tests/ClientTest.php
index 2356da7..d0d53f1 100644
--- a/tests/ClientTest.php
+++ b/tests/ClientTest.php
@@ -37,6 +37,30 @@ public function testPing()
$this->expectPromiseResolveWith($body, $this->client->ping());
}
+ public function testEvents()
+ {
+ $json = array();
+ $stream = $this->getMock('React\Stream\ReadableStreamInterface');
+
+ $this->expectRequest('GET', '/events', $this->createResponseJsonStream($json));
+ $this->streamingParser->expects($this->once())->method('parseJsonStream')->will($this->returnValue($stream));
+ $this->streamingParser->expects($this->once())->method('deferredStream')->with($this->equalTo($stream), $this->equalTo('progress'))->will($this->returnPromise($json));
+
+ $this->expectPromiseResolveWith($json, $this->client->events());
+ }
+
+ public function testEventsArgs()
+ {
+ $json = array();
+ $stream = $this->getMock('React\Stream\ReadableStreamInterface');
+
+ $this->expectRequest('GET', '/events?since=10&until=20&filters=%7B%22image%22%3A%5B%22busybox%22%2C%22ubuntu%22%5D%7D', $this->createResponseJsonStream($json));
+ $this->streamingParser->expects($this->once())->method('parseJsonStream')->will($this->returnValue($stream));
+ $this->streamingParser->expects($this->once())->method('deferredStream')->with($this->equalTo($stream), $this->equalTo('progress'))->will($this->returnPromise($json));
+
+ $this->expectPromiseResolveWith($json, $this->client->events(10, 20, array('image' => array('busybox', 'ubuntu'))));
+ }
+
public function testContainerCreate()
{
$json = array();
diff --git a/tests/FunctionalClientTest.php b/tests/FunctionalClientTest.php
index f3092e7..6111d38 100644
--- a/tests/FunctionalClientTest.php
+++ b/tests/FunctionalClientTest.php
@@ -46,6 +46,8 @@ public function testCreateStartAndRemoveContainer()
$this->assertNotNull($container['Id']);
$this->assertNull($container['Warnings']);
+ $start = microtime(true);
+
$promise = $this->client->containerStart($container['Id']);
$ret = Block\await($promise, $this->loop);
@@ -55,6 +57,19 @@ public function testCreateStartAndRemoveContainer()
$ret = Block\await($promise, $this->loop);
$this->assertEquals('', $ret);
+
+ $end = microtime(true);
+
+ // get all events between starting and removing for this container
+ $promise = $this->client->events($start, $end, array('container' => array($container['Id'])));
+ $ret = Block\await($promise, $this->loop);
+
+ // expects "start", "kill", "die", "destroy" events
+ $this->assertEquals(4, count($ret));
+ $this->assertEquals('start', $ret[0]['status']);
+ $this->assertEquals('kill', $ret[1]['status']);
+ $this->assertEquals('die', $ret[2]['status']);
+ $this->assertEquals('destroy', $ret[3]['status']);
}
/**