From 8bab36469dd0854a1b76f56f2a0a5f4367b30c0e Mon Sep 17 00:00:00 2001 From: Enrico Date: Fri, 5 Jul 2013 10:52:40 -0300 Subject: [PATCH 01/12] Added Makefile with test command. --- Makefile | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..646fb7cf --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +test: + @python tests/test.py From 5831a3b189aeefa04b160a8b1f6d8c30daf8b91e Mon Sep 17 00:00:00 2001 From: Enrico Date: Fri, 5 Jul 2013 11:03:04 -0300 Subject: [PATCH 02/12] Added setup to makefile. --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 646fb7cf..2e46c279 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,4 @@ +setup: + @pip install -e . test: @python tests/test.py From 7f458a323249507fed8a67136e9b308d3213da99 Mon Sep 17 00:00:00 2001 From: Enrico Date: Fri, 5 Jul 2013 11:05:59 -0300 Subject: [PATCH 03/12] Added testing info to readme. --- README.md | 78 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index b1f8658e..f6a3cd71 100644 --- a/README.md +++ b/README.md @@ -6,93 +6,111 @@ An API client for docker written in Python API === -`client.Client(base_url='http://localhost:4243')` +`client.Client(base_url='http://localhost:4243')` Client class. `base_url` refers to the protocol+hostname+port where the docker server is hosted. -* `c.build(dockerfile)` +* `c.build(dockerfile)` Identical to the `docker build` command. `dockerfile` is a file-like object or a bytes collection. -* `c.commit(container, repository=None, tag=None, message=None, author=None, conf=None)` +* `c.commit(container, repository=None, tag=None, message=None, author=None, conf=None)` Identical to the `docker commit` command. -* `c.containers(quiet=False, all=False, trunc=True, latest=False, since=None, before=None, limit=-1)` +* `c.containers(quiet=False, all=False, trunc=True, latest=False, since=None, before=None, limit=-1)` Identical to the `docker ps` command. -* `c.create_container(image, command, hostname=None, user=None, detach=False, stdin_open=False, tty=False, mem_limit=0, ports=None, environment=None, dns=None, volumes=None, volumes_from=None)` +* `c.create_container(image, command, hostname=None, user=None, detach=False, stdin_open=False, tty=False, mem_limit=0, ports=None, environment=None, dns=None, volumes=None, volumes_from=None)` Creates a container that can then be `start`ed. Parameters are similar to those for the `docker run` command except it doesn't support the attach options (`-a`) -* `c.diff(container)` +* `c.diff(container)` Identical to the `docker diff` command. -* `c.export(container)` +* `c.export(container)` Identical to the `docker export` command. -* `c.history(image)` +* `c.history(image)` Identical to the `docker history` command. -* `c.images(name=None, quiet=False, all=False, viz=False)` +* `c.images(name=None, quiet=False, all=False, viz=False)` Identical to the `docker images` command. -* `c.import_image(src, repository=None, tag=None)` +* `c.import_image(src, repository=None, tag=None)` Identical to the `docker import` command. If `src` is a string or unicode string, it will be treated as a URL -to fetch the image from. To import an image from the local machine, `src` needs to be a file-like object or +to fetch the image from. To import an image from the local machine, `src` needs to be a file-like object or bytes collection. -* `c.info()` +* `c.info()` Identical to the `docker info` command. -* `c.insert(url, path)` +* `c.insert(url, path)` Identical to the `docker insert` command. -* `c.inspect_container(container_id)` +* `c.inspect_container(container_id)` Identical to the `docker inspect` command, but can only be used with a container ID. -* `c.inspect_image(container_id)` +* `c.inspect_image(container_id)` Identical to the `docker inspect` command, but can only be used with an image ID. -* `c.kill(containers...)` +* `c.kill(containers...)` Identical to the `docker kill` command. -* `c.login(username, password=None, email=None)` +* `c.login(username, password=None, email=None)` Identical to the `docker login` command (but non-interactive, obviously). -* `c.logs(container)` +* `c.logs(container)` Identical to the `docker logs` command. -* `c.port(container, private_port)` +* `c.port(container, private_port)` Identical to the `docker port` command. -* `c.pull(repository, tag=None, registry=None)` +* `c.pull(repository, tag=None, registry=None)` Identical to the `docker pull` command. * `c.push(repository, registry=None)` Identical to the `docker push` command. -* `c.remove_container(containers..., v=False)` +* `c.remove_container(containers..., v=False)` Identical to the `docker rm` command. -* `c.remove_image(images...)` +* `c.remove_image(images...)` Identical to the `docker rmi` command. -* `c.restart(containers..., t=10)` +* `c.restart(containers..., t=10)` Identical to the `docker restart` command. -* `c.search(term)` +* `c.search(term)` Identical to the `docker search` command. -* `c.start(container)` -Identical to the `docker start` command, but doesn't support attach options. Use `docker logs` to +* `c.start(container)` +Identical to the `docker start` command, but doesn't support attach options. Use `docker logs` to recover `stdout`/`stderr` -* `c.stop(containers..., t=10)` +* `c.stop(containers..., t=10)` Identical to the `docker stop` command. -* `c.tag(image, repository, tag=None, force=False)` +* `c.tag(image, repository, tag=None, force=False)` Identical to the `docker tag` command. -* `c.version()` +* `c.version()` Identical to the `docker version` command. -* `c.wait(containers...)` +* `c.wait(containers...)` Identical to the `docker wait` command. + + +Testing +======= + +* [Install](http://www.docker.io/gettingstarted/) docker. +* Create a virtualenv +* Install dependencies: + +```sh +make setup +``` + +* Run tests: + +```sh +make test +``` From c2a145c2d8d3eac63f9a4126c9043d50f8597dbc Mon Sep 17 00:00:00 2001 From: Enrico Date: Fri, 5 Jul 2013 15:25:52 -0300 Subject: [PATCH 04/12] Added ability to create binds when starting a container. --- docker/client.py | 13 +++++++++++-- tests/test.py | 25 +++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/docker/client.py b/docker/client.py index e48ee1ba..93a82d3a 100644 --- a/docker/client.py +++ b/docker/client.py @@ -280,10 +280,19 @@ class Client(requests.Session): return self._result(self.get(self._url("/images/search"), params={'term': term}), True) - def start(self, *args): + def start(self, *args, **kwargs): + start_config = {} + binds = kwargs.pop('binds', '') + if binds: + bind_pairs = ['{0}:{1}'.format(host, dest) for host, dest in binds.items()] + start_config = { + 'Binds': bind_pairs, + } + for name in args: url = self._url("/containers/{0}/start".format(name)) - self.post(url, None) + self.post_json( + url, start_config, headers={"Content-Type":"application/json"}) def stop(self, *args, **kwargs): params = { diff --git a/tests/test.py b/tests/test.py index dce9f766..89326783 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,5 +1,5 @@ +import os import unittest -import time import docker @@ -97,6 +97,27 @@ class TestCreateContainer(BaseTestCase): self.assertIn('Id', res) self.tmp_containers.append(res['Id']) +class TestCreateContainerWithBinds(BaseTestCase): + def runTest(self): + mount_dest = '/mnt' + mount_origin = os.getcwd() + + filename = 'shared.txt' + shared_file = os.path.join(mount_origin, filename) + + with open(shared_file, 'w'): + container = self.client.create_container('busybox', + ['ls', mount_dest], volumes={mount_dest: {}}) + container_id = container['Id'] + self.client.start(container_id, binds={mount_origin: mount_dest}) + self.tmp_containers.append(container_id) + exitcode = self.client.wait(container_id) + self.assertEqual(exitcode, 0) + logs = self.client.logs(container_id) + + os.unlink(shared_file) + self.assertIn(filename, logs) + class TestStartContainer(BaseTestCase): def runTest(self): res = self.client.create_container('busybox', 'true') @@ -297,4 +318,4 @@ class TestRunShlex(BaseTestCase): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() From 835f350bd736f26f2641ca8b447ab72086eb1a2b Mon Sep 17 00:00:00 2001 From: Enrico Date: Fri, 5 Jul 2013 15:45:58 -0300 Subject: [PATCH 05/12] Added content-type header to the `post_json` method, some routes need it. --- docker/client.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/client.py b/docker/client.py index 93a82d3a..d4c88418 100644 --- a/docker/client.py +++ b/docker/client.py @@ -72,6 +72,9 @@ class Client(requests.Session): if v is not None: data2[k] = v + if 'headers' not in kwargs: + kwargs['headers'] = {} + kwargs['headers']["Content-Type"] = "application/json" return self.post(url, json.dumps(data2), **kwargs) def attach(self, container): @@ -291,8 +294,7 @@ class Client(requests.Session): for name in args: url = self._url("/containers/{0}/start".format(name)) - self.post_json( - url, start_config, headers={"Content-Type":"application/json"}) + self.post_json(url, start_config) def stop(self, *args, **kwargs): params = { From 76d2aa06da444b88d4af07326f4e85f44a785987 Mon Sep 17 00:00:00 2001 From: Enrico Date: Fri, 5 Jul 2013 15:53:53 -0300 Subject: [PATCH 06/12] Added documentation about the `binds` argument in the `start` method. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f6a3cd71..fbeeb44b 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,12 @@ Identical to the `docker search` command. Identical to the `docker start` command, but doesn't support attach options. Use `docker logs` to recover `stdout`/`stderr` +* `c.start(container, binds={'/host': '/mnt'})` +Allows to bind a directory in the host to the container. +Similar to the `docker run` command with the `-b="/host:/mnt"`. +Requires the container to be created with the volumes argument: +`c.create_container(..., volumes={'/mnt': {}})` + * `c.stop(containers..., t=10)` Identical to the `docker stop` command. From e1c8a57c85d59fa92d862545284085ab0b072ad1 Mon Sep 17 00:00:00 2001 From: Paulo Sousa Date: Thu, 11 Jul 2013 16:03:45 -0300 Subject: [PATCH 07/12] Added option to attach on containers and be verbose on build process --- docker/client.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docker/client.py b/docker/client.py index f3d84e0e..e976ac43 100644 --- a/docker/client.py +++ b/docker/client.py @@ -102,8 +102,8 @@ class Client(requests.Session): else: break - def build(self, dockerfile, tag=None, logger=None): - bc = BuilderClient(self, logger) + def build(self, dockerfile, tag=None, logger=None, follow_build_steps=False): + bc = BuilderClient(self, follow_build_steps, logger) img_id = None try: img_id = bc.build(dockerfile, tag=tag) @@ -337,8 +337,9 @@ class Client(requests.Session): class BuilderClient(object): - def __init__(self, client, logger=None): + def __init__(self, client, follow_build_steps=False, logger=None): self.client = client + self.follow_build_steps = follow_build_steps self.tmp_containers = {} self.tmp_images = {} self.image = None @@ -439,6 +440,9 @@ class BuilderClient(object): for warning in container['Warnings']: self.logger.warning(warning) self.client.start(container['Id']) + if self.follow_build_steps: + for log_line in self.client.attach(container['Id']): + self.logger.info(" {0}".format(log_line)) self.tmp_containers[container['Id']] = {} status = self.client.wait(container['Id']) if status != 0: From be9c96473044f23c551cf920afa966074d9a100e Mon Sep 17 00:00:00 2001 From: Thatcher Date: Fri, 5 Jul 2013 18:20:38 -0700 Subject: [PATCH 08/12] Fixed bug by which c.port('', ) would fail because when port not a string e.g. c.port('e087bc23f8d5', 80) would fail and c.port('e087bc23f8d5', '80') would work. I now inserted str() so it should always work. --- docker/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/client.py b/docker/client.py index e976ac43..e83ffacc 100644 --- a/docker/client.py +++ b/docker/client.py @@ -244,7 +244,7 @@ class Client(requests.Session): def port(self, container, private_port): res = self.get(self._url("/containers/{0}/json".format(container))) json_ = res.json() - return json_['NetworkSettings']['PortMapping'][private_port] + return json_['NetworkSettings']['PortMapping'][str(private_port)] def pull(self, repository, tag=None, registry=None): if repository.count(":") == 1: From a863acce87c9a884dc9faf8beadbc59f30e41d4a Mon Sep 17 00:00:00 2001 From: Enrico Date: Fri, 5 Jul 2013 15:25:52 -0300 Subject: [PATCH 09/12] Added ability to create binds when starting a container. --- tests/test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.py b/tests/test.py index 289bf608..6119e47d 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,5 +1,6 @@ import os import six +import os import unittest import docker From 3438bd914b561d3f323778fa2c01a762a6940061 Mon Sep 17 00:00:00 2001 From: Enrico Date: Thu, 11 Jul 2013 17:37:35 -0300 Subject: [PATCH 10/12] Revert "Added Makefile with test command." This reverts commit 8bab36469dd0854a1b76f56f2a0a5f4367b30c0e. Conflicts: Makefile --- Makefile | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 2e46c279..00000000 --- a/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -setup: - @pip install -e . -test: - @python tests/test.py From be5347533d0c74b485db60679659036aad43702c Mon Sep 17 00:00:00 2001 From: Enrico Date: Thu, 11 Jul 2013 17:49:23 -0300 Subject: [PATCH 11/12] Fixed whitespaces on readme and removed makefile references. --- README.md | 81 ++++++++++++++++++++++--------------------------------- 1 file changed, 32 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index fbeeb44b..0a29bf06 100644 --- a/README.md +++ b/README.md @@ -6,117 +6,100 @@ An API client for docker written in Python API === -`client.Client(base_url='http://localhost:4243')` +`client.Client(base_url='http://localhost:4243')` Client class. `base_url` refers to the protocol+hostname+port where the docker server is hosted. -* `c.build(dockerfile)` +* `c.build(dockerfile)` Identical to the `docker build` command. `dockerfile` is a file-like object or a bytes collection. -* `c.commit(container, repository=None, tag=None, message=None, author=None, conf=None)` +* `c.commit(container, repository=None, tag=None, message=None, author=None, conf=None)` Identical to the `docker commit` command. -* `c.containers(quiet=False, all=False, trunc=True, latest=False, since=None, before=None, limit=-1)` +* `c.containers(quiet=False, all=False, trunc=True, latest=False, since=None, before=None, limit=-1)` Identical to the `docker ps` command. -* `c.create_container(image, command, hostname=None, user=None, detach=False, stdin_open=False, tty=False, mem_limit=0, ports=None, environment=None, dns=None, volumes=None, volumes_from=None)` +* `c.create_container(image, command, hostname=None, user=None, detach=False, stdin_open=False, tty=False, mem_limit=0, ports=None, environment=None, dns=None, volumes=None, volumes_from=None)` Creates a container that can then be `start`ed. Parameters are similar to those for the `docker run` command except it doesn't support the attach options (`-a`) -* `c.diff(container)` +* `c.diff(container)` Identical to the `docker diff` command. -* `c.export(container)` +* `c.export(container)` Identical to the `docker export` command. -* `c.history(image)` +* `c.history(image)` Identical to the `docker history` command. -* `c.images(name=None, quiet=False, all=False, viz=False)` +* `c.images(name=None, quiet=False, all=False, viz=False)` Identical to the `docker images` command. -* `c.import_image(src, repository=None, tag=None)` +* `c.import_image(src, repository=None, tag=None)` Identical to the `docker import` command. If `src` is a string or unicode string, it will be treated as a URL -to fetch the image from. To import an image from the local machine, `src` needs to be a file-like object or +to fetch the image from. To import an image from the local machine, `src` needs to be a file-like object or bytes collection. -* `c.info()` +* `c.info()` Identical to the `docker info` command. -* `c.insert(url, path)` +* `c.insert(url, path)` Identical to the `docker insert` command. -* `c.inspect_container(container_id)` +* `c.inspect_container(container_id)` Identical to the `docker inspect` command, but can only be used with a container ID. -* `c.inspect_image(container_id)` +* `c.inspect_image(container_id)` Identical to the `docker inspect` command, but can only be used with an image ID. -* `c.kill(containers...)` +* `c.kill(containers...)` Identical to the `docker kill` command. -* `c.login(username, password=None, email=None)` +* `c.login(username, password=None, email=None)` Identical to the `docker login` command (but non-interactive, obviously). -* `c.logs(container)` +* `c.logs(container)` Identical to the `docker logs` command. -* `c.port(container, private_port)` +* `c.port(container, private_port)` Identical to the `docker port` command. -* `c.pull(repository, tag=None, registry=None)` +* `c.pull(repository, tag=None, registry=None)` Identical to the `docker pull` command. -* `c.push(repository, registry=None)` +* `c.push(repository, registry=None)` Identical to the `docker push` command. -* `c.remove_container(containers..., v=False)` +* `c.remove_container(containers..., v=False)` Identical to the `docker rm` command. -* `c.remove_image(images...)` +* `c.remove_image(images...)` Identical to the `docker rmi` command. -* `c.restart(containers..., t=10)` +* `c.restart(containers..., t=10)` Identical to the `docker restart` command. -* `c.search(term)` +* `c.search(term)` Identical to the `docker search` command. -* `c.start(container)` -Identical to the `docker start` command, but doesn't support attach options. Use `docker logs` to +* `c.start(container)` +Identical to the `docker start` command, but doesn't support attach options. Use `docker logs` to recover `stdout`/`stderr` -* `c.start(container, binds={'/host': '/mnt'})` +* `c.start(container, binds={'/host': '/mnt'})` Allows to bind a directory in the host to the container. Similar to the `docker run` command with the `-b="/host:/mnt"`. Requires the container to be created with the volumes argument: -`c.create_container(..., volumes={'/mnt': {}})` +`c.create_container(..., volumes={'/mnt': {}})` * `c.stop(containers..., t=10)` Identical to the `docker stop` command. -* `c.tag(image, repository, tag=None, force=False)` +* `c.tag(image, repository, tag=None, force=False)` Identical to the `docker tag` command. -* `c.version()` +* `c.version()` Identical to the `docker version` command. -* `c.wait(containers...)` +* `c.wait(containers...)` Identical to the `docker wait` command. - -Testing -======= - -* [Install](http://www.docker.io/gettingstarted/) docker. -* Create a virtualenv -* Install dependencies: - -```sh -make setup -``` - -* Run tests: - -```sh -make test -``` From cf47df1b3ed7ff1ed651cbd4c13b5eed391c7804 Mon Sep 17 00:00:00 2001 From: Enrico Date: Thu, 11 Jul 2013 18:05:19 -0300 Subject: [PATCH 12/12] Removed duplicated import. --- tests/test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test.py b/tests/test.py index 6119e47d..289bf608 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,6 +1,5 @@ import os import six -import os import unittest import docker