Skip to content

Commit

Permalink
Save and load for docker images (#220)
Browse files Browse the repository at this point in the history
* added docker save command and test for alpine package

* added chunked post method for docker client

* added import method to load image tarball to docker

* changed return element of export method

* fixes of flake8 errors and warnings

* import image tested over saved hello-world image file

* support python 3.5 for load file by using async generators

* test with asyncio.streamer

* removed unused async generator functions

* removed unused dependencies from test requirements

* Contributers as alphabetical order

* added 219 news to CHANGES folder
  • Loading branch information
alkimake authored and Christian Barra committed Apr 17, 2018
1 parent c06035f commit 6dab874
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES/219.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Feature: Add support for docker save and load api methods
3 changes: 2 additions & 1 deletion CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Alkim Gozen
Andrew Svetlov
Anton Patrushev
Byeongjun Park
Expand All @@ -12,4 +13,4 @@ Joshua Chung
Martin Koppehel
Nikolay Novik
Paul Tagliamonte
Tianon Gravi
Tianon Gravi
22 changes: 20 additions & 2 deletions aiodocker/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def _canonicalize_url(self, path):

async def _query(self, path, method='GET', *,
params=None, data=None, headers=None,
timeout=None):
timeout=None, chunked=None):
'''
Get the response object by performing the HTTP request.
The caller is responsible to finalize the response object.
Expand All @@ -150,7 +150,8 @@ async def _query(self, path, method='GET', *,
params=httpize(params),
headers=headers,
data=data,
timeout=timeout)
timeout=timeout,
chunked=chunked)
except asyncio.TimeoutError:
raise
if (response.status // 100) in [4, 5]:
Expand Down Expand Up @@ -183,6 +184,23 @@ async def _query_json(self, path, method='GET', *,
data = await parse_result(response)
return data

async def _query_chunked_post(self, path, method='POST', *,
params=None, data=None, headers=None,
timeout=None):
'''
A shorthand for uploading data by chunks
'''
if headers is None:
headers = {}
if headers and 'content-type' not in headers:
headers['content-type'] = 'application/octet-stream'
response = await self._query(
path, method,
params=params, data=data, headers=headers,
timeout=timeout, chunked=1024)
data = await parse_result(response)
return data

async def _websocket(self, path, **params):
if not params:
params = {
Expand Down
37 changes: 37 additions & 0 deletions aiodocker/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,40 @@ async def build(self, *,
)

return (await json_stream_result(response, stream=stream))

async def export_image(self, name: str):
"""
Get a tarball of an image by name or id.
Args:
name: name/id of the image to be exported
Returns:
Streamreader of tarball image
"""
response = await self.docker._query(
"images/{name}/get".format(name=name),
"GET",
)
return response.content

async def import_image(self, data):
"""
Import tarball of image to docker.
Args:
data: tarball data of image to be imported
Returns:
Tarball of the image
"""
headers = {
"Content-Type": "application/x-tar",
}
response = await self.docker._query_chunked_post(
"images/load",
"POST",
data=data,
headers=headers
)
return response
Binary file added tests/docker/hello-world.img.tar
Binary file not shown.
29 changes: 29 additions & 0 deletions tests/test_images.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import os
from io import BytesIO

import pytest
from aiodocker import utils
from aiodocker.exceptions import DockerError
import aiohttp


@pytest.mark.asyncio
Expand Down Expand Up @@ -112,6 +114,33 @@ async def test_build_from_tar(docker, random_name):
assert image


@pytest.mark.asyncio
async def test_export_image(docker):
name = "alpine:latest"
exported_image = await docker.images.export_image(name=name)
assert exported_image


@pytest.mark.asyncio
async def test_import_image(docker):

@aiohttp.streamer
async def file_sender(writer, file_name=None):
with open(file_name, 'rb') as f:
chunk = f.read(2**16)
while chunk:
await writer.write(chunk)
chunk = f.read(2**16)

dir = os.path.dirname(__file__)
hello_world = os.path.join(dir, 'docker/hello-world.img.tar')
response = await docker.images.import_image(
data=file_sender(file_name=hello_world))
assert 'error' not in response
image = await docker.images.get(name='hello-world:latest')
assert image


@pytest.mark.asyncio
async def test_pups_image_auth(docker):
name = "alpine:latest"
Expand Down

0 comments on commit 6dab874

Please sign in to comment.