1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
5 from __future__ import absolute_import
18 import arvados.commands.keepdocker as arv_keepdocker
19 from . import arvados_testutil as tutil
20 from . import run_test_server
23 class StopTest(Exception):
27 class ArvKeepdockerTestCase(unittest.TestCase, tutil.VersionChecker):
28 def run_arv_keepdocker(self, args, err):
29 sys.argv = ['arv-keepdocker'] + args
30 log_handler = logging.StreamHandler(err)
31 arv_keepdocker.logger.addHandler(log_handler)
33 return arv_keepdocker.main()
35 arv_keepdocker.logger.removeHandler(log_handler)
37 def test_unsupported_arg(self):
38 out = tutil.StringIO()
39 with tutil.redirected_streams(stdout=out, stderr=out), \
40 self.assertRaises(SystemExit):
41 self.run_arv_keepdocker(['-x=unknown'], sys.stderr)
42 self.assertRegex(out.getvalue(), 'unrecognized arguments')
44 def test_version_argument(self):
45 with tutil.redirected_streams(
46 stdout=tutil.StringIO, stderr=tutil.StringIO) as (out, err):
47 with self.assertRaises(SystemExit):
48 self.run_arv_keepdocker(['--version'], sys.stderr)
49 self.assertVersionOutput(out, err)
51 @mock.patch('arvados.commands.keepdocker.find_image_hashes',
52 return_value=['abc123'])
53 @mock.patch('arvados.commands.keepdocker.find_one_image_hash',
54 return_value='abc123')
55 def test_image_format_compatibility(self, _1, _2):
56 old_id = hashlib.sha256(b'old').hexdigest()
57 new_id = 'sha256:'+hashlib.sha256(b'new').hexdigest()
58 for supported, img_id, expect_ok in [
59 (['v1'], old_id, True),
60 (['v1'], new_id, False),
61 (None, old_id, False),
64 (['v1', 'v2'], new_id, True),
65 (['v1'], new_id, False),
66 (['v2'], new_id, True)]:
68 fakeDD = arvados.api('v1')._rootDesc
70 del fakeDD['dockerImageFormats']
72 fakeDD['dockerImageFormats'] = supported
74 err = tutil.StringIO()
75 out = tutil.StringIO()
77 with tutil.redirected_streams(stdout=out), \
78 mock.patch('arvados.api') as api, \
79 mock.patch('arvados.commands.keepdocker.popen_docker',
80 return_value=subprocess.Popen(
82 stdout=subprocess.PIPE)), \
83 mock.patch('arvados.commands.keepdocker.prep_image_file',
84 side_effect=StopTest), \
85 self.assertRaises(StopTest if expect_ok else SystemExit):
87 api()._rootDesc = fakeDD
88 self.run_arv_keepdocker(['--force', 'testimage'], err)
90 self.assertEqual(out.getvalue(), '')
93 err.getvalue(), "refusing to store",
94 msg=repr((supported, img_id)))
97 err.getvalue(), "refusing to store",
98 msg=repr((supported, img_id)))
102 "server does not specify supported image formats",
103 msg=repr((supported, img_id)))
105 fakeDD = arvados.api('v1')._rootDesc
106 fakeDD['dockerImageFormats'] = ['v1']
107 err = tutil.StringIO()
108 out = tutil.StringIO()
109 with tutil.redirected_streams(stdout=out), \
110 mock.patch('arvados.api') as api, \
111 mock.patch('arvados.commands.keepdocker.popen_docker',
112 return_value=subprocess.Popen(
114 stdout=subprocess.PIPE)), \
115 mock.patch('arvados.commands.keepdocker.prep_image_file',
116 side_effect=StopTest), \
117 self.assertRaises(StopTest):
118 api()._rootDesc = fakeDD
119 self.run_arv_keepdocker(
120 ['--force', '--force-image-format', 'testimage'], err)
121 self.assertRegex(err.getvalue(), "forcing incompatible image")
123 def test_tag_given_twice(self):
124 with tutil.redirected_streams(stdout=tutil.StringIO, stderr=tutil.StringIO) as (out, err):
125 with self.assertRaises(SystemExit):
126 self.run_arv_keepdocker(['myrepo:mytag', 'extratag'], sys.stderr)
127 self.assertRegex(err.getvalue(), "cannot add tag argument 'extratag'")
129 def test_image_given_as_repo_colon_tag(self):
130 with self.assertRaises(StopTest), \
131 mock.patch('arvados.commands.keepdocker.find_one_image_hash',
132 side_effect=StopTest) as find_image_mock:
133 self.run_arv_keepdocker(['repo:tag'], sys.stderr)
134 find_image_mock.assert_called_with('repo', 'tag')
136 with self.assertRaises(StopTest), \
137 mock.patch('arvados.commands.keepdocker.find_one_image_hash',
138 side_effect=StopTest) as find_image_mock:
139 self.run_arv_keepdocker(['myreg.example:8888/repo/img:tag'], sys.stderr)
140 find_image_mock.assert_called_with('myreg.example:8888/repo/img', 'tag')
142 def test_image_has_colons(self):
143 with self.assertRaises(StopTest), \
144 mock.patch('arvados.commands.keepdocker.find_one_image_hash',
145 side_effect=StopTest) as find_image_mock:
146 self.run_arv_keepdocker(['[::1]:8888/repo/img'], sys.stderr)
147 find_image_mock.assert_called_with('[::1]:8888/repo/img', 'latest')
149 with self.assertRaises(StopTest), \
150 mock.patch('arvados.commands.keepdocker.find_one_image_hash',
151 side_effect=StopTest) as find_image_mock:
152 self.run_arv_keepdocker(['[::1]/repo/img'], sys.stderr)
153 find_image_mock.assert_called_with('[::1]/repo/img', 'latest')
155 @mock.patch('arvados.commands.keepdocker.find_image_hashes',
156 return_value=['abc123'])
157 @mock.patch('arvados.commands.keepdocker.find_one_image_hash',
158 return_value='abc123')
159 def test_collection_property_update(self, _1, _2):
160 image_id = 'sha256:'+hashlib.sha256(b'image').hexdigest()
161 fakeDD = arvados.api('v1')._rootDesc
162 fakeDD['dockerImageFormats'] = ['v2']
164 err = tutil.StringIO()
165 out = tutil.StringIO()
166 File = collections.namedtuple('File', ['name'])
167 mocked_file = File(name='docker_image')
168 mocked_collection = {
169 'uuid': 'new-collection-uuid',
171 'responsible_person_uuid': 'person_uuid',
175 with tutil.redirected_streams(stdout=out), \
176 mock.patch('arvados.api') as api, \
177 mock.patch('arvados.commands.keepdocker.popen_docker',
178 return_value=subprocess.Popen(
180 stdout=subprocess.PIPE)), \
181 mock.patch('arvados.commands.keepdocker.prep_image_file',
182 return_value=(mocked_file, False)), \
183 mock.patch('arvados.commands.put.main',
184 return_value='new-collection-uuid'), \
185 self.assertRaises(StopTest):
187 api()._rootDesc = fakeDD
188 api().collections().get().execute.return_value = copy.deepcopy(mocked_collection)
189 api().collections().update().execute.side_effect = StopTest
190 self.run_arv_keepdocker(['--force', 'testimage'], err)
192 updated_properties = mocked_collection['properties']
193 updated_properties.update({'docker-image-repo-tag': 'testimage:latest'})
194 api().collections().update.assert_called_with(
195 uuid=mocked_collection['uuid'],
196 body={'properties': updated_properties})