1
""" test_handler_apt_source
2
Testing various config variations of the apt_source config
10
from unittest import mock
15
from cloudinit.config import cc_apt_configure
16
from cloudinit import gpg
17
from cloudinit import util
19
from ..helpers import TestCase
21
EXPECTEDKEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
24
mI0ESuZLUgEEAKkqq3idtFP7g9hzOu1a8+v8ImawQN4TrvlygfScMU1TIS1eC7UQ
25
NUA8Qqgr9iUaGnejb0VciqftLrU9D6WYHSKz+EITefgdyJ6SoQxjoJdsCpJ7o9Jy
26
8PQnpRttiFm4qHu6BVnKnBNxw/z3ST9YMqW5kbMQpfxbGe+obRox59NpABEBAAG0
27
HUxhdW5jaHBhZCBQUEEgZm9yIFNjb3R0IE1vc2VyiLYEEwECACAFAkrmS1ICGwMG
28
CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAGILvPA2g/d3aEA/9tVjc10HOZwV29
29
OatVuTeERjjrIbxflO586GLA8cp0C9RQCwgod/R+cKYdQcHjbqVcP0HqxveLg0RZ
30
FJpWLmWKamwkABErwQLGlM/Hwhjfade8VvEQutH5/0JgKHmzRsoqfR+LMO6OS+Sm
31
S0ORP6HXET3+jC8BMG4tBWCTK/XEZw==
33
-----END PGP PUBLIC KEY BLOCK-----"""
36
def load_tfile_or_url(*args, **kwargs):
38
load file and return content after decoding
40
return util.decode_binary(util.read_file_or_url(*args, **kwargs).contents)
43
class TestAptSourceConfig(TestCase):
44
"""TestAptSourceConfig
45
Main Class to test apt_source configs
50
super(TestAptSourceConfig, self).setUp()
51
self.tmp = tempfile.mkdtemp()
52
self.addCleanup(shutil.rmtree, self.tmp)
53
self.aptlistfile = os.path.join(self.tmp, "single-deb.list")
54
self.aptlistfile2 = os.path.join(self.tmp, "single-deb2.list")
55
self.aptlistfile3 = os.path.join(self.tmp, "single-deb3.list")
56
self.join = os.path.join
57
# mock fallback filename into writable tmp dir
58
self.fallbackfn = os.path.join(self.tmp, "etc/apt/sources.list.d/",
59
"cloud_config_sources.list")
61
patcher = mock.patch("cloudinit.config.cc_apt_configure.get_release")
62
get_rel = patcher.start()
63
get_rel.return_value = self.release
64
self.addCleanup(patcher.stop)
67
def _get_default_params():
69
Get the most basic default mrror and release info to be used in tests
72
params['RELEASE'] = cc_apt_configure.get_release()
73
params['MIRROR'] = "http://archive.ubuntu.com/ubuntu"
76
def myjoin(self, *args, **kwargs):
77
"""myjoin - redir into writable tmpdir"""
78
if (args[0] == "/etc/apt/sources.list.d/" and
79
args[1] == "cloud_config_sources.list" and
81
return self.join(self.tmp, args[0].lstrip("/"), args[1])
83
return self.join(*args, **kwargs)
85
def apt_src_basic(self, filename, cfg):
87
Test Fix deb source string, has to overwrite mirror conf in params
89
params = self._get_default_params()
91
cc_apt_configure.add_apt_sources(cfg, params)
93
self.assertTrue(os.path.isfile(filename))
95
contents = load_tfile_or_url(filename)
96
self.assertTrue(re.search(r"%s %s %s %s\n" %
97
("deb", "http://archive.ubuntu.com/ubuntu",
99
"main universe multiverse restricted"),
100
contents, flags=re.IGNORECASE))
102
def test_apt_src_basic(self):
103
"""Test deb source string, overwrite mirror and filename"""
104
cfg = {'source': ('deb http://archive.ubuntu.com/ubuntu'
106
' main universe multiverse restricted'),
107
'filename': self.aptlistfile}
108
self.apt_src_basic(self.aptlistfile, [cfg])
110
def test_apt_src_basic_dict(self):
111
"""Test deb source string, overwrite mirror and filename (dict)"""
112
cfg = {self.aptlistfile: {'source':
113
('deb http://archive.ubuntu.com/ubuntu'
115
' main universe multiverse restricted')}}
116
self.apt_src_basic(self.aptlistfile, cfg)
118
def apt_src_basic_tri(self, cfg):
120
Test Fix three deb source string, has to overwrite mirror conf in
121
params. Test with filenames provided in config.
122
generic part to check three files with different content
124
self.apt_src_basic(self.aptlistfile, cfg)
126
# extra verify on two extra files of this test
127
contents = load_tfile_or_url(self.aptlistfile2)
128
self.assertTrue(re.search(r"%s %s %s %s\n" %
129
("deb", "http://archive.ubuntu.com/ubuntu",
131
"main universe multiverse restricted"),
132
contents, flags=re.IGNORECASE))
133
contents = load_tfile_or_url(self.aptlistfile3)
134
self.assertTrue(re.search(r"%s %s %s %s\n" %
135
("deb", "http://archive.ubuntu.com/ubuntu",
137
"main universe multiverse restricted"),
138
contents, flags=re.IGNORECASE))
140
def test_apt_src_basic_tri(self):
141
"""Test Fix three deb source string with filenames"""
142
cfg1 = {'source': ('deb http://archive.ubuntu.com/ubuntu'
144
' main universe multiverse restricted'),
145
'filename': self.aptlistfile}
146
cfg2 = {'source': ('deb http://archive.ubuntu.com/ubuntu'
148
' main universe multiverse restricted'),
149
'filename': self.aptlistfile2}
150
cfg3 = {'source': ('deb http://archive.ubuntu.com/ubuntu'
152
' main universe multiverse restricted'),
153
'filename': self.aptlistfile3}
154
self.apt_src_basic_tri([cfg1, cfg2, cfg3])
156
def test_apt_src_basic_dict_tri(self):
157
"""Test Fix three deb source string with filenames (dict)"""
158
cfg = {self.aptlistfile: {'source':
159
('deb http://archive.ubuntu.com/ubuntu'
161
' main universe multiverse restricted')},
162
self.aptlistfile2: {'source':
163
('deb http://archive.ubuntu.com/ubuntu'
165
' main universe multiverse restricted')},
166
self.aptlistfile3: {'source':
167
('deb http://archive.ubuntu.com/ubuntu'
169
' main universe multiverse restricted')}}
170
self.apt_src_basic_tri(cfg)
172
def test_apt_src_basic_nofn(self):
173
"""Test Fix three deb source string without filenames (dict)"""
174
cfg = {'source': ('deb http://archive.ubuntu.com/ubuntu'
176
' main universe multiverse restricted')}
177
with mock.patch.object(os.path, 'join', side_effect=self.myjoin):
178
self.apt_src_basic(self.fallbackfn, [cfg])
180
def apt_src_replacement(self, filename, cfg):
182
Test Autoreplacement of MIRROR and RELEASE in source specs
184
params = self._get_default_params()
185
cc_apt_configure.add_apt_sources(cfg, params)
187
self.assertTrue(os.path.isfile(filename))
189
contents = load_tfile_or_url(filename)
190
self.assertTrue(re.search(r"%s %s %s %s\n" %
191
("deb", params['MIRROR'], params['RELEASE'],
193
contents, flags=re.IGNORECASE))
195
def test_apt_src_replace(self):
196
"""Test Autoreplacement of MIRROR and RELEASE in source specs"""
197
cfg = {'source': 'deb $MIRROR $RELEASE multiverse',
198
'filename': self.aptlistfile}
199
self.apt_src_replacement(self.aptlistfile, [cfg])
201
def apt_src_replace_tri(self, cfg):
202
"""apt_src_replace_tri
203
Test three autoreplacements of MIRROR and RELEASE in source specs with
206
self.apt_src_replacement(self.aptlistfile, cfg)
208
# extra verify on two extra files of this test
209
params = self._get_default_params()
210
contents = load_tfile_or_url(self.aptlistfile2)
211
self.assertTrue(re.search(r"%s %s %s %s\n" %
212
("deb", params['MIRROR'], params['RELEASE'],
214
contents, flags=re.IGNORECASE))
215
contents = load_tfile_or_url(self.aptlistfile3)
216
self.assertTrue(re.search(r"%s %s %s %s\n" %
217
("deb", params['MIRROR'], params['RELEASE'],
219
contents, flags=re.IGNORECASE))
221
def test_apt_src_replace_tri(self):
222
"""Test triple Autoreplacement of MIRROR and RELEASE in source specs"""
223
cfg1 = {'source': 'deb $MIRROR $RELEASE multiverse',
224
'filename': self.aptlistfile}
225
cfg2 = {'source': 'deb $MIRROR $RELEASE main',
226
'filename': self.aptlistfile2}
227
cfg3 = {'source': 'deb $MIRROR $RELEASE universe',
228
'filename': self.aptlistfile3}
229
self.apt_src_replace_tri([cfg1, cfg2, cfg3])
231
def test_apt_src_replace_dict_tri(self):
232
"""Test triple Autoreplacement in source specs (dict)"""
233
cfg = {self.aptlistfile: {'source': 'deb $MIRROR $RELEASE multiverse'},
234
'notused': {'source': 'deb $MIRROR $RELEASE main',
235
'filename': self.aptlistfile2},
236
self.aptlistfile3: {'source': 'deb $MIRROR $RELEASE universe'}}
237
self.apt_src_replace_tri(cfg)
239
def test_apt_src_replace_nofn(self):
240
"""Test Autoreplacement of MIRROR and RELEASE in source specs nofile"""
241
cfg = {'source': 'deb $MIRROR $RELEASE multiverse'}
242
with mock.patch.object(os.path, 'join', side_effect=self.myjoin):
243
self.apt_src_replacement(self.fallbackfn, [cfg])
245
def apt_src_keyid(self, filename, cfg, keynum):
247
Test specification of a source + keyid
249
params = self._get_default_params()
251
with mock.patch.object(util, 'subp',
252
return_value=('fakekey 1234', '')) as mockobj:
253
cc_apt_configure.add_apt_sources(cfg, params)
255
# check if it added the right ammount of keys
257
for _ in range(keynum):
258
calls.append(call(('apt-key', 'add', '-'), 'fakekey 1234'))
259
mockobj.assert_has_calls(calls, any_order=True)
261
self.assertTrue(os.path.isfile(filename))
263
contents = load_tfile_or_url(filename)
264
self.assertTrue(re.search(r"%s %s %s %s\n" %
266
('http://ppa.launchpad.net/smoser/'
267
'cloud-init-test/ubuntu'),
269
contents, flags=re.IGNORECASE))
271
def test_apt_src_keyid(self):
272
"""Test specification of a source + keyid with filename being set"""
273
cfg = {'source': ('deb '
274
'http://ppa.launchpad.net/'
275
'smoser/cloud-init-test/ubuntu'
278
'filename': self.aptlistfile}
279
self.apt_src_keyid(self.aptlistfile, [cfg], 1)
281
def test_apt_src_keyid_tri(self):
282
"""Test 3x specification of a source + keyid with filename being set"""
283
cfg1 = {'source': ('deb '
284
'http://ppa.launchpad.net/'
285
'smoser/cloud-init-test/ubuntu'
288
'filename': self.aptlistfile}
289
cfg2 = {'source': ('deb '
290
'http://ppa.launchpad.net/'
291
'smoser/cloud-init-test/ubuntu'
294
'filename': self.aptlistfile2}
295
cfg3 = {'source': ('deb '
296
'http://ppa.launchpad.net/'
297
'smoser/cloud-init-test/ubuntu'
298
' xenial multiverse'),
300
'filename': self.aptlistfile3}
302
self.apt_src_keyid(self.aptlistfile, [cfg1, cfg2, cfg3], 3)
303
contents = load_tfile_or_url(self.aptlistfile2)
304
self.assertTrue(re.search(r"%s %s %s %s\n" %
306
('http://ppa.launchpad.net/smoser/'
307
'cloud-init-test/ubuntu'),
308
"xenial", "universe"),
309
contents, flags=re.IGNORECASE))
310
contents = load_tfile_or_url(self.aptlistfile3)
311
self.assertTrue(re.search(r"%s %s %s %s\n" %
313
('http://ppa.launchpad.net/smoser/'
314
'cloud-init-test/ubuntu'),
315
"xenial", "multiverse"),
316
contents, flags=re.IGNORECASE))
318
def test_apt_src_keyid_nofn(self):
319
"""Test specification of a source + keyid without filename being set"""
320
cfg = {'source': ('deb '
321
'http://ppa.launchpad.net/'
322
'smoser/cloud-init-test/ubuntu'
325
with mock.patch.object(os.path, 'join', side_effect=self.myjoin):
326
self.apt_src_keyid(self.fallbackfn, [cfg], 1)
328
def apt_src_key(self, filename, cfg):
330
Test specification of a source + key
332
params = self._get_default_params()
334
with mock.patch.object(util, 'subp') as mockobj:
335
cc_apt_configure.add_apt_sources([cfg], params)
337
mockobj.assert_called_with(('apt-key', 'add', '-'), 'fakekey 4321')
339
self.assertTrue(os.path.isfile(filename))
341
contents = load_tfile_or_url(filename)
342
self.assertTrue(re.search(r"%s %s %s %s\n" %
344
('http://ppa.launchpad.net/smoser/'
345
'cloud-init-test/ubuntu'),
347
contents, flags=re.IGNORECASE))
349
def test_apt_src_key(self):
350
"""Test specification of a source + key with filename being set"""
351
cfg = {'source': ('deb '
352
'http://ppa.launchpad.net/'
353
'smoser/cloud-init-test/ubuntu'
355
'key': "fakekey 4321",
356
'filename': self.aptlistfile}
357
self.apt_src_key(self.aptlistfile, cfg)
359
def test_apt_src_key_nofn(self):
360
"""Test specification of a source + key without filename being set"""
361
cfg = {'source': ('deb '
362
'http://ppa.launchpad.net/'
363
'smoser/cloud-init-test/ubuntu'
365
'key': "fakekey 4321"}
366
with mock.patch.object(os.path, 'join', side_effect=self.myjoin):
367
self.apt_src_key(self.fallbackfn, cfg)
369
def test_apt_src_keyonly(self):
370
"""Test specifying key without source"""
371
params = self._get_default_params()
372
cfg = {'key': "fakekey 4242",
373
'filename': self.aptlistfile}
375
with mock.patch.object(util, 'subp') as mockobj:
376
cc_apt_configure.add_apt_sources([cfg], params)
378
mockobj.assert_called_once_with(('apt-key', 'add', '-'),
381
# filename should be ignored on key only
382
self.assertFalse(os.path.isfile(self.aptlistfile))
384
def test_apt_src_keyidonly(self):
385
"""Test specification of a keyid without source"""
386
params = self._get_default_params()
387
cfg = {'keyid': "03683F77",
388
'filename': self.aptlistfile}
390
with mock.patch.object(util, 'subp',
391
return_value=('fakekey 1212', '')) as mockobj:
392
cc_apt_configure.add_apt_sources([cfg], params)
394
mockobj.assert_called_with(('apt-key', 'add', '-'), 'fakekey 1212')
396
# filename should be ignored on key only
397
self.assertFalse(os.path.isfile(self.aptlistfile))
399
def apt_src_keyid_real(self, cfg, expectedkey):
400
"""apt_src_keyid_real
401
Test specification of a keyid without source including
402
up to addition of the key (add_apt_key_raw mocked to keep the
405
params = self._get_default_params()
407
with mock.patch.object(cc_apt_configure, 'add_apt_key_raw') as mockkey:
408
with mock.patch.object(gpg, 'get_key_by_id',
409
return_value=expectedkey) as mockgetkey:
410
cc_apt_configure.add_apt_sources([cfg], params)
412
mockgetkey.assert_called_with(cfg['keyid'],
414
'keyserver.ubuntu.com'))
415
mockkey.assert_called_with(expectedkey)
417
# filename should be ignored on key only
418
self.assertFalse(os.path.isfile(self.aptlistfile))
420
def test_apt_src_keyid_real(self):
421
"""test_apt_src_keyid_real - Test keyid including key add"""
423
cfg = {'keyid': keyid,
424
'filename': self.aptlistfile}
426
self.apt_src_keyid_real(cfg, EXPECTEDKEY)
428
def test_apt_src_longkeyid_real(self):
429
"""test_apt_src_longkeyid_real - Test long keyid including key add"""
430
keyid = "B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77"
431
cfg = {'keyid': keyid,
432
'filename': self.aptlistfile}
434
self.apt_src_keyid_real(cfg, EXPECTEDKEY)
436
def test_apt_src_longkeyid_ks_real(self):
437
"""test_apt_src_longkeyid_ks_real - Test long keyid from other ks"""
438
keyid = "B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77"
439
cfg = {'keyid': keyid,
440
'keyserver': 'keys.gnupg.net',
441
'filename': self.aptlistfile}
443
self.apt_src_keyid_real(cfg, EXPECTEDKEY)
445
def test_apt_src_ppa(self):
446
"""Test adding a ppa"""
447
params = self._get_default_params()
448
cfg = {'source': 'ppa:smoser/cloud-init-test',
449
'filename': self.aptlistfile}
451
# default matcher needed for ppa
452
matcher = re.compile(r'^[\w-]+:\w').search
454
with mock.patch.object(util, 'subp') as mockobj:
455
cc_apt_configure.add_apt_sources([cfg], params,
456
aa_repo_match=matcher)
457
mockobj.assert_called_once_with(['add-apt-repository',
458
'ppa:smoser/cloud-init-test'])
460
# adding ppa should ignore filename (uses add-apt-repository)
461
self.assertFalse(os.path.isfile(self.aptlistfile))
463
def test_apt_src_ppa_tri(self):
464
"""Test adding three ppa's"""
465
params = self._get_default_params()
466
cfg1 = {'source': 'ppa:smoser/cloud-init-test',
467
'filename': self.aptlistfile}
468
cfg2 = {'source': 'ppa:smoser/cloud-init-test2',
469
'filename': self.aptlistfile2}
470
cfg3 = {'source': 'ppa:smoser/cloud-init-test3',
471
'filename': self.aptlistfile3}
473
# default matcher needed for ppa
474
matcher = re.compile(r'^[\w-]+:\w').search
476
with mock.patch.object(util, 'subp') as mockobj:
477
cc_apt_configure.add_apt_sources([cfg1, cfg2, cfg3], params,
478
aa_repo_match=matcher)
479
calls = [call(['add-apt-repository', 'ppa:smoser/cloud-init-test']),
480
call(['add-apt-repository', 'ppa:smoser/cloud-init-test2']),
481
call(['add-apt-repository', 'ppa:smoser/cloud-init-test3'])]
482
mockobj.assert_has_calls(calls, any_order=True)
484
# adding ppa should ignore all filenames (uses add-apt-repository)
485
self.assertFalse(os.path.isfile(self.aptlistfile))
486
self.assertFalse(os.path.isfile(self.aptlistfile2))
487
self.assertFalse(os.path.isfile(self.aptlistfile3))
489
def test_convert_to_new_format(self):
490
"""Test the conversion of old to new format"""
491
cfg1 = {'source': 'deb $MIRROR $RELEASE multiverse',
492
'filename': self.aptlistfile}
493
cfg2 = {'source': 'deb $MIRROR $RELEASE main',
494
'filename': self.aptlistfile2}
495
cfg3 = {'source': 'deb $MIRROR $RELEASE universe',
496
'filename': self.aptlistfile3}
497
checkcfg = {self.aptlistfile: {'filename': self.aptlistfile,
498
'source': 'deb $MIRROR $RELEASE '
500
self.aptlistfile2: {'filename': self.aptlistfile2,
501
'source': 'deb $MIRROR $RELEASE main'},
502
self.aptlistfile3: {'filename': self.aptlistfile3,
503
'source': 'deb $MIRROR $RELEASE '
506
newcfg = cc_apt_configure.convert_to_new_format([cfg1, cfg2, cfg3])
507
self.assertEqual(newcfg, checkcfg)
509
newcfg2 = cc_apt_configure.convert_to_new_format(newcfg)
510
self.assertEqual(newcfg2, checkcfg)
512
with self.assertRaises(ValueError):
513
cc_apt_configure.convert_to_new_format(5)