Skip to content

Commit 08da5bf

Browse files
authored
Merge pull request #1092 from docker/1064-import-changes
Add support for changes in import_image*
2 parents fab4a06 + 75497e0 commit 08da5bf

File tree

3 files changed

+141
-78
lines changed

3 files changed

+141
-78
lines changed

Makefile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
all: test
44

55
clean:
6-
rm -rf tests/__pycache__
7-
rm -rf tests/*/__pycache__
8-
docker rm -vf dpy-dind
6+
-docker rm -vf dpy-dind
7+
find -name "__pycache__" | xargs rm -rf
98

109
build:
1110
docker build -t docker-py .

docker/api/image.py

Lines changed: 97 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import os
23
import six
34
import warnings
45

@@ -42,87 +43,79 @@ def images(self, name=None, quiet=False, all=False, viz=False,
4243
return [x['Id'] for x in res]
4344
return res
4445

45-
def import_image(self, src=None, repository=None, tag=None, image=None):
46-
if src:
47-
if isinstance(src, six.string_types):
48-
try:
49-
result = self.import_image_from_file(
50-
src, repository=repository, tag=tag)
51-
except IOError:
52-
result = self.import_image_from_url(
53-
src, repository=repository, tag=tag)
54-
else:
55-
result = self.import_image_from_data(
56-
src, repository=repository, tag=tag)
57-
elif image:
58-
result = self.import_image_from_image(
59-
image, repository=repository, tag=tag)
60-
else:
61-
raise Exception("Must specify a src or image")
62-
63-
return result
64-
65-
def import_image_from_data(self, data, repository=None, tag=None):
66-
u = self._url("/images/create")
67-
params = {
68-
'fromSrc': '-',
69-
'repo': repository,
70-
'tag': tag
71-
}
72-
headers = {
73-
'Content-Type': 'application/tar',
74-
}
75-
return self._result(
76-
self._post(u, data=data, params=params, headers=headers))
46+
def import_image(self, src=None, repository=None, tag=None, image=None,
47+
changes=None, stream_src=False):
48+
if not (src or image):
49+
raise errors.DockerException(
50+
'Must specify src or image to import from'
51+
)
52+
u = self._url('/images/create')
7753

78-
def import_image_from_file(self, filename, repository=None, tag=None):
79-
u = self._url("/images/create")
80-
params = {
81-
'fromSrc': '-',
82-
'repo': repository,
83-
'tag': tag
84-
}
85-
headers = {
86-
'Content-Type': 'application/tar',
87-
}
88-
with open(filename, 'rb') as f:
54+
params = _import_image_params(
55+
repository, tag, image,
56+
src=(src if isinstance(src, six.string_types) else None),
57+
changes=changes
58+
)
59+
headers = {'Content-Type': 'application/tar'}
60+
61+
if image or params.get('fromSrc') != '-': # from image or URL
8962
return self._result(
90-
self._post(u, data=f, params=params, headers=headers,
91-
timeout=None))
63+
self._post(u, data=None, params=params)
64+
)
65+
elif isinstance(src, six.string_types): # from file path
66+
with open(src, 'rb') as f:
67+
return self._result(
68+
self._post(
69+
u, data=f, params=params, headers=headers, timeout=None
70+
)
71+
)
72+
else: # from raw data
73+
if stream_src:
74+
headers['Transfer-Encoding'] = 'chunked'
75+
return self._result(
76+
self._post(u, data=src, params=params, headers=headers)
77+
)
9278

93-
def import_image_from_stream(self, stream, repository=None, tag=None):
94-
u = self._url("/images/create")
95-
params = {
96-
'fromSrc': '-',
97-
'repo': repository,
98-
'tag': tag
99-
}
100-
headers = {
101-
'Content-Type': 'application/tar',
102-
'Transfer-Encoding': 'chunked',
103-
}
79+
def import_image_from_data(self, data, repository=None, tag=None,
80+
changes=None):
81+
u = self._url('/images/create')
82+
params = _import_image_params(
83+
repository, tag, src='-', changes=changes
84+
)
85+
headers = {'Content-Type': 'application/tar'}
10486
return self._result(
105-
self._post(u, data=stream, params=params, headers=headers))
87+
self._post(
88+
u, data=data, params=params, headers=headers, timeout=None
89+
)
90+
)
91+
return self.import_image(
92+
src=data, repository=repository, tag=tag, changes=changes
93+
)
10694

107-
def import_image_from_url(self, url, repository=None, tag=None):
108-
u = self._url("/images/create")
109-
params = {
110-
'fromSrc': url,
111-
'repo': repository,
112-
'tag': tag
113-
}
114-
return self._result(
115-
self._post(u, data=None, params=params))
95+
def import_image_from_file(self, filename, repository=None, tag=None,
96+
changes=None):
97+
return self.import_image(
98+
src=filename, repository=repository, tag=tag, changes=changes
99+
)
116100

117-
def import_image_from_image(self, image, repository=None, tag=None):
118-
u = self._url("/images/create")
119-
params = {
120-
'fromImage': image,
121-
'repo': repository,
122-
'tag': tag
123-
}
124-
return self._result(
125-
self._post(u, data=None, params=params))
101+
def import_image_from_stream(self, stream, repository=None, tag=None,
102+
changes=None):
103+
return self.import_image(
104+
src=stream, stream_src=True, repository=repository, tag=tag,
105+
changes=changes
106+
)
107+
108+
def import_image_from_url(self, url, repository=None, tag=None,
109+
changes=None):
110+
return self.import_image(
111+
src=url, repository=repository, tag=tag, changes=changes
112+
)
113+
114+
def import_image_from_image(self, image, repository=None, tag=None,
115+
changes=None):
116+
return self.import_image(
117+
image=image, repository=repository, tag=tag, changes=changes
118+
)
126119

127120
@utils.check_resource
128121
def insert(self, image, url, path):
@@ -246,3 +239,32 @@ def tag(self, image, repository, tag=None, force=False):
246239
res = self._post(url, params=params)
247240
self._raise_for_status(res)
248241
return res.status_code == 201
242+
243+
244+
def is_file(src):
245+
try:
246+
return (
247+
isinstance(src, six.string_types) and
248+
os.path.isfile(src)
249+
)
250+
except TypeError: # a data string will make isfile() raise a TypeError
251+
return False
252+
253+
254+
def _import_image_params(repo, tag, image=None, src=None,
255+
changes=None):
256+
params = {
257+
'repo': repo,
258+
'tag': tag,
259+
}
260+
if image:
261+
params['fromImage'] = image
262+
elif src and not is_file(src):
263+
params['fromSrc'] = src
264+
else:
265+
params['fromSrc'] = '-'
266+
267+
if changes:
268+
params['changes'] = changes
269+
270+
return params

tests/integration/image_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,48 @@ def test_import_from_stream(self):
208208
img_id = result['status']
209209
self.tmp_imgs.append(img_id)
210210

211+
def test_import_image_from_data_with_changes(self):
212+
with self.dummy_tar_stream(n_bytes=500) as f:
213+
content = f.read()
214+
215+
statuses = self.client.import_image_from_data(
216+
content, repository='test/import-from-bytes',
217+
changes=['USER foobar', 'CMD ["echo"]']
218+
)
219+
220+
result_text = statuses.splitlines()[-1]
221+
result = json.loads(result_text)
222+
223+
assert 'error' not in result
224+
225+
img_id = result['status']
226+
self.tmp_imgs.append(img_id)
227+
228+
img_data = self.client.inspect_image(img_id)
229+
assert img_data is not None
230+
assert img_data['Config']['Cmd'] == ['echo']
231+
assert img_data['Config']['User'] == 'foobar'
232+
233+
def test_import_image_with_changes(self):
234+
with self.dummy_tar_file(n_bytes=self.TAR_SIZE) as tar_filename:
235+
statuses = self.client.import_image(
236+
src=tar_filename, repository='test/import-from-file',
237+
changes=['USER foobar', 'CMD ["echo"]']
238+
)
239+
240+
result_text = statuses.splitlines()[-1]
241+
result = json.loads(result_text)
242+
243+
assert 'error' not in result
244+
245+
img_id = result['status']
246+
self.tmp_imgs.append(img_id)
247+
248+
img_data = self.client.inspect_image(img_id)
249+
assert img_data is not None
250+
assert img_data['Config']['Cmd'] == ['echo']
251+
assert img_data['Config']['User'] == 'foobar'
252+
211253
@contextlib.contextmanager
212254
def temporary_http_file_server(self, stream):
213255
'''Serve data from an IO stream over HTTP.'''

0 commit comments

Comments
 (0)