Merge lp:~ajkavanagh/charm-helpers/ppc64le_proposed_fix_plus_refactor into lp:charm-helpers
- ppc64le_proposed_fix_plus_refactor
- Merge into devel
Status: | Superseded |
---|---|
Proposed branch: | lp:~ajkavanagh/charm-helpers/ppc64le_proposed_fix_plus_refactor |
Merge into: | lp:charm-helpers |
Diff against target: |
3645 lines (+1602/-1375) 11 files modified
Makefile (+1/-0) charmhelpers/__init__.py (+61/-0) charmhelpers/contrib/openstack/utils.py (+78/-142) charmhelpers/fetch/__init__.py (+17/-9) charmhelpers/fetch/centos.py (+1/-1) charmhelpers/fetch/ubuntu.py (+244/-57) tests/contrib/openstack/test_openstack_utils.py (+163/-112) tests/contrib/openstack/test_os_utils.py (+1/-1) tests/fetch/test_fetch.py (+9/-1053) tests/fetch/test_fetch_centos.py (+315/-0) tests/fetch/test_fetch_ubuntu.py (+712/-0) |
To merge this branch: | bzr merge lp:~ajkavanagh/charm-helpers/ppc64le_proposed_fix_plus_refactor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ryan Beisner (community) | Needs Fixing | ||
James Page | Pending | ||
Review via email: mp+303495@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-04-10.
Commit message
Description of the change
Refactor configure_
Bug#1611134: fetch helper assumes amd64 arch (cannot use with ports such as ppc64el)
The refactor is because the two functions do almost exactly the same thing in two
different places, and the core function does OpenStack sources, which ought to be in
the contrib/openstack module set.
After the refactor, the bug fix was implemented on the 'proposed' source to pick the
correct ports specification for ppc64le:
deb http://
where {} is the ubuntu release.
- 622. By Alex Kavanagh
-
Merge in r619 in prep for r620 which is very different
- 623. By Alex Kavanagh
-
Reworking of the ports_fetch code and the dbuliga CentOS support
The CentOS support changed the same code as the fetch code for
ports (i.e. other architectures that x86_64). This merge changes
the way the testing works for the CentOS/Ubuntu dbuliga work by
moving the specific tests for each distro into their own files
and keeping a common file. This has simplified the testing, and
also fixed a few tests.Also, this merge tidies up quite a few PEP8 issues with the library.
- 624. By Alex Kavanagh
-
Merge main branch to 626
- 625. By Alex Kavanagh
-
Merge/resolve conflicts with main 627
- 626. By Alex Kavanagh
-
Add back in DEBUG to c/o/utils.py
- 627. By Alex Kavanagh
-
Merge/resolve conflicts to main branch 628
- 628. By Alex Kavanagh
-
Merge to main branch r630
- 629. By Alex Kavanagh
-
Merge/resolve conflicts with main branch r631
- 630. By Alex Kavanagh
-
Merge to main branch r647
- 631. By Alex Kavanagh
-
Merge/resolve conflicts with main branch r648
- 632. By Alex Kavanagh
-
Merge up to main branch r657
- 633. By Alex Kavanagh
-
Merge/resolve conflicts for main branch r658
The issue is that the different architecture ports has changed where the
cloud pockets are represented in the code, and simplified them slightly.
This merge resolves the conflicts with r658 and also fixes a bug with
'zesty' in the list of OpenStack releases (which confuses them with
Ubuntu release names). - 634. By Alex Kavanagh
-
Merge to main branch r660
- 635. By Alex Kavanagh
-
Merge r661 from main branch + resolve conflicts
- 636. By Alex Kavanagh
-
Merge & resolve conflicts up to main branch r663
This is because the ports refactor has removed one set of (obsolete)
cloud pockets. - 637. By Alex Kavanagh
-
Merge up to r671 from main devel branch
Ryan Beisner (1chb1n) wrote : | # |
A test deployment on arm64 xenial-mitaka, with openstack-origin set to "distro-proposed" failed:
2017-03-30 03:05:04 DEBUG juju-log No hardening applied to 'install'
2017-03-30 03:05:05 INFO juju-log DEPRECATION WARNING: Function configure_
2017-03-30 03:05:05 ERROR juju-log FATAL ERROR: Arch aarch64 not supported for (distro-)proposed
2017-03-30 03:05:05 ERROR juju.worker.
Ryan Beisner (1chb1n) wrote : | # |
FYI, this (in my WIP temp branch) resolved the failure in my previous comment:
- http://
Alex Kavanagh (ajkavanagh) wrote : | # |
Hi Ryan
Thanks for taking a look at it. I'm going to swing back to this shortly and have another go at bring it up to date. I'll also test it against the glance charm to check that it does indeed work.
Thanks
Alex.
- 638. By Alex Kavanagh
-
Remove redundant plugins() funct from c.f.ubuntu.py
- 639. By Alex Kavanagh
-
Remove duplicated code in fetch module(s)
- 640. By Alex Kavanagh
-
bring upto date to r704
- 641. By Alex Kavanagh
-
Merge in r705 (retry-
add-apt- repository) This needed a little work as the fetch code in this branch is very different
and reorganised from the dev branch. - 642. By Alex Kavanagh
-
Merge up to r731
- 643. By Alex Kavanagh
-
Add ARM 64 arch to proposed pockets for OpenStack
Unmerged revisions
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2017-01-25 17:11:37 +0000 |
3 | +++ Makefile 2017-04-10 18:12:16 +0000 |
4 | @@ -27,6 +27,7 @@ |
5 | -python setup.py clean |
6 | rm -rf build/ MANIFEST |
7 | find . -name '*.pyc' -delete |
8 | + find . -name '__pycache__' -delete |
9 | rm -rf dist/* |
10 | rm -rf .venv |
11 | rm -rf .venv3 |
12 | |
13 | === modified file 'charmhelpers/__init__.py' |
14 | --- charmhelpers/__init__.py 2016-07-06 14:41:05 +0000 |
15 | +++ charmhelpers/__init__.py 2017-04-10 18:12:16 +0000 |
16 | @@ -14,6 +14,11 @@ |
17 | |
18 | # Bootstrap charm-helpers, installing its dependencies if necessary using |
19 | # only standard libraries. |
20 | +from __future__ import print_function |
21 | +from __future__ import absolute_import |
22 | + |
23 | +import functools |
24 | +import inspect |
25 | import subprocess |
26 | import sys |
27 | |
28 | @@ -34,3 +39,59 @@ |
29 | else: |
30 | subprocess.check_call(['apt-get', 'install', '-y', 'python3-yaml']) |
31 | import yaml # flake8: noqa |
32 | + |
33 | + |
34 | +# Holds a list of mapping of mangled function names that have been deprecated |
35 | +# using the @deprecate decorator below. This is so that the warning is only |
36 | +# printed once for each usage of the function. |
37 | +__deprecated_functions = {} |
38 | + |
39 | + |
40 | +def deprecate(warning, date=None, log=None): |
41 | + """Add a deprecation warning the first time the function is used. |
42 | + The date, which is a string in semi-ISO8660 format indicate the year-month |
43 | + that the function is officially going to be removed. |
44 | + |
45 | + usage: |
46 | + |
47 | + @deprecate('use core/fetch/add_source() instead', '2017-04') |
48 | + def contributed_add_source_thing(...): |
49 | + ... |
50 | + |
51 | + And it then prints to the log ONCE that the function is deprecated. |
52 | + The reason for passing the logging function (log) is so that hookenv.log |
53 | + can be used for a charm if needed. |
54 | + |
55 | + :param warning: String to indicat where it has moved ot. |
56 | + :param date: optional sting, in YYYY-MM format to indicate when the |
57 | + function will definitely (probably) be removed. |
58 | + :param log: The log function to call to log. If not, logs to stdout |
59 | + """ |
60 | + def wrap(f): |
61 | + |
62 | + @functools.wraps(f) |
63 | + def wrapped_f(*args, **kwargs): |
64 | + try: |
65 | + module = inspect.getmodule(f) |
66 | + file = inspect.getsourcefile(f) |
67 | + lines = inspect.getsourcelines(f) |
68 | + f_name = "{}-{}-{}..{}-{}".format( |
69 | + module.__name__, file, lines[0], lines[-1], f.__name__) |
70 | + except (IOError, TypeError): |
71 | + # assume it was local, so just use the name of the function |
72 | + f_name = f.__name__ |
73 | + if f_name not in __deprecated_functions: |
74 | + __deprecated_functions[f_name] = True |
75 | + s = "DEPRECATION WARNING: Function {} is being removed".format( |
76 | + f.__name__) |
77 | + if date: |
78 | + s = "{} on/around {}".format(s, date) |
79 | + if warning: |
80 | + s = "{} : {}".format(s, warning) |
81 | + if log: |
82 | + log(s) |
83 | + else: |
84 | + print(s) |
85 | + return f(*args, **kwargs) |
86 | + return wrapped_f |
87 | + return wrap |
88 | |
89 | === modified file 'charmhelpers/contrib/openstack/utils.py' |
90 | --- charmhelpers/contrib/openstack/utils.py 2017-03-24 16:50:31 +0000 |
91 | +++ charmhelpers/contrib/openstack/utils.py 2017-04-10 18:12:16 +0000 |
92 | @@ -26,11 +26,12 @@ |
93 | import shutil |
94 | |
95 | import six |
96 | -import tempfile |
97 | import traceback |
98 | import uuid |
99 | import yaml |
100 | |
101 | +from charmhelpers import deprecate |
102 | + |
103 | from charmhelpers.contrib.network import ip |
104 | |
105 | from charmhelpers.core import unitdata |
106 | @@ -41,7 +42,6 @@ |
107 | config, |
108 | log as juju_log, |
109 | charm_dir, |
110 | - DEBUG, |
111 | INFO, |
112 | ERROR, |
113 | related_units, |
114 | @@ -82,9 +82,12 @@ |
115 | restart_on_change_helper, |
116 | ) |
117 | from charmhelpers.fetch import ( |
118 | - apt_install, |
119 | apt_cache, |
120 | install_remote, |
121 | + import_key as fetch_import_key, |
122 | + add_source as fetch_add_source, |
123 | + SourceConfigError, |
124 | + GPGKeyError, |
125 | get_upstream_version |
126 | ) |
127 | from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk |
128 | @@ -436,13 +439,14 @@ |
129 | # error_out(e) |
130 | |
131 | |
132 | -os_rel = None |
133 | +# Module local cache variable for the os_release. |
134 | +_os_rel = None |
135 | |
136 | |
137 | def reset_os_release(): |
138 | '''Unset the cached os_release version''' |
139 | - global os_rel |
140 | - os_rel = None |
141 | + global _os_rel |
142 | + _os_rel = None |
143 | |
144 | |
145 | def os_release(package, base='essex', reset_cache=False): |
146 | @@ -456,144 +460,77 @@ |
147 | the installation source, the earliest release supported by the charm should |
148 | be returned. |
149 | ''' |
150 | - global os_rel |
151 | + global _os_rel |
152 | if reset_cache: |
153 | reset_os_release() |
154 | - if os_rel: |
155 | - return os_rel |
156 | - os_rel = (git_os_codename_install_source(config('openstack-origin-git')) or |
157 | - get_os_codename_package(package, fatal=False) or |
158 | - get_os_codename_install_source(config('openstack-origin')) or |
159 | - base) |
160 | - return os_rel |
161 | - |
162 | - |
163 | + if _os_rel: |
164 | + return _os_rel |
165 | + _os_rel = ( |
166 | + git_os_codename_install_source(config('openstack-origin-git')) or |
167 | + get_os_codename_package(package, fatal=False) or |
168 | + get_os_codename_install_source(config('openstack-origin')) or |
169 | + base) |
170 | + return _os_rel |
171 | + |
172 | + |
173 | +@deprecate("moved to charmhelpers.fetch.import_key()", "2017-07", log=juju_log) |
174 | def import_key(keyid): |
175 | - key = keyid.strip() |
176 | - if (key.startswith('-----BEGIN PGP PUBLIC KEY BLOCK-----') and |
177 | - key.endswith('-----END PGP PUBLIC KEY BLOCK-----')): |
178 | - juju_log("PGP key found (looks like ASCII Armor format)", level=DEBUG) |
179 | - juju_log("Importing ASCII Armor PGP key", level=DEBUG) |
180 | - with tempfile.NamedTemporaryFile() as keyfile: |
181 | - with open(keyfile.name, 'w') as fd: |
182 | - fd.write(key) |
183 | - fd.write("\n") |
184 | - |
185 | - cmd = ['apt-key', 'add', keyfile.name] |
186 | - try: |
187 | - subprocess.check_call(cmd) |
188 | - except subprocess.CalledProcessError: |
189 | - error_out("Error importing PGP key '%s'" % key) |
190 | - else: |
191 | - juju_log("PGP key found (looks like Radix64 format)", level=DEBUG) |
192 | - juju_log("Importing PGP key from keyserver", level=DEBUG) |
193 | - cmd = ['apt-key', 'adv', '--keyserver', |
194 | - 'hkp://keyserver.ubuntu.com:80', '--recv-keys', key] |
195 | - try: |
196 | - subprocess.check_call(cmd) |
197 | - except subprocess.CalledProcessError: |
198 | - error_out("Error importing PGP key '%s'" % key) |
199 | - |
200 | - |
201 | -def get_source_and_pgp_key(input): |
202 | - """Look for a pgp key ID or ascii-armor key in the given input.""" |
203 | - index = input.strip() |
204 | - index = input.rfind('|') |
205 | - if index < 0: |
206 | - return input, None |
207 | - |
208 | - key = input[index + 1:].strip('|') |
209 | - source = input[:index] |
210 | - return source, key |
211 | - |
212 | - |
213 | -def configure_installation_source(rel): |
214 | - '''Configure apt installation source.''' |
215 | - if rel == 'distro': |
216 | - return |
217 | - elif rel == 'distro-proposed': |
218 | - ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] |
219 | - with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f: |
220 | - f.write(DISTRO_PROPOSED % ubuntu_rel) |
221 | - elif rel[:4] == "ppa:": |
222 | - src, key = get_source_and_pgp_key(rel) |
223 | - if key: |
224 | - import_key(key) |
225 | - |
226 | - subprocess.check_call(["add-apt-repository", "-y", src]) |
227 | - elif rel[:3] == "deb": |
228 | - src, key = get_source_and_pgp_key(rel) |
229 | - if key: |
230 | - import_key(key) |
231 | - |
232 | - with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f: |
233 | - f.write(src) |
234 | - elif rel[:6] == 'cloud:': |
235 | - ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] |
236 | - rel = rel.split(':')[1] |
237 | - u_rel = rel.split('-')[0] |
238 | - ca_rel = rel.split('-')[1] |
239 | - |
240 | - if u_rel != ubuntu_rel: |
241 | - e = 'Cannot install from Cloud Archive pocket %s on this Ubuntu '\ |
242 | - 'version (%s)' % (ca_rel, ubuntu_rel) |
243 | - error_out(e) |
244 | - |
245 | - if 'staging' in ca_rel: |
246 | - # staging is just a regular PPA. |
247 | - os_rel = ca_rel.split('/')[0] |
248 | - ppa = 'ppa:ubuntu-cloud-archive/%s-staging' % os_rel |
249 | - cmd = 'add-apt-repository -y %s' % ppa |
250 | - subprocess.check_call(cmd.split(' ')) |
251 | - return |
252 | - |
253 | - # map charm config options to actual archive pockets. |
254 | - pockets = { |
255 | - 'folsom': 'precise-updates/folsom', |
256 | - 'folsom/updates': 'precise-updates/folsom', |
257 | - 'folsom/proposed': 'precise-proposed/folsom', |
258 | - 'grizzly': 'precise-updates/grizzly', |
259 | - 'grizzly/updates': 'precise-updates/grizzly', |
260 | - 'grizzly/proposed': 'precise-proposed/grizzly', |
261 | - 'havana': 'precise-updates/havana', |
262 | - 'havana/updates': 'precise-updates/havana', |
263 | - 'havana/proposed': 'precise-proposed/havana', |
264 | - 'icehouse': 'precise-updates/icehouse', |
265 | - 'icehouse/updates': 'precise-updates/icehouse', |
266 | - 'icehouse/proposed': 'precise-proposed/icehouse', |
267 | - 'juno': 'trusty-updates/juno', |
268 | - 'juno/updates': 'trusty-updates/juno', |
269 | - 'juno/proposed': 'trusty-proposed/juno', |
270 | - 'kilo': 'trusty-updates/kilo', |
271 | - 'kilo/updates': 'trusty-updates/kilo', |
272 | - 'kilo/proposed': 'trusty-proposed/kilo', |
273 | - 'liberty': 'trusty-updates/liberty', |
274 | - 'liberty/updates': 'trusty-updates/liberty', |
275 | - 'liberty/proposed': 'trusty-proposed/liberty', |
276 | - 'mitaka': 'trusty-updates/mitaka', |
277 | - 'mitaka/updates': 'trusty-updates/mitaka', |
278 | - 'mitaka/proposed': 'trusty-proposed/mitaka', |
279 | - 'newton': 'xenial-updates/newton', |
280 | - 'newton/updates': 'xenial-updates/newton', |
281 | - 'newton/proposed': 'xenial-proposed/newton', |
282 | - 'ocata': 'xenial-updates/ocata', |
283 | - 'ocata/updates': 'xenial-updates/ocata', |
284 | - 'ocata/proposed': 'xenial-proposed/ocata', |
285 | - } |
286 | - |
287 | - try: |
288 | - pocket = pockets[ca_rel] |
289 | - except KeyError: |
290 | - e = 'Invalid Cloud Archive release specified: %s' % rel |
291 | - error_out(e) |
292 | - |
293 | - src = "deb %s %s main" % (CLOUD_ARCHIVE_URL, pocket) |
294 | - apt_install('ubuntu-cloud-keyring', fatal=True) |
295 | - |
296 | - with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as f: |
297 | - f.write(src) |
298 | - else: |
299 | - error_out("Invalid openstack-release specified: %s" % rel) |
300 | + """Import a key, either ASCII armored, or a GPG key id. |
301 | + |
302 | + @param keyid: the key in ASCII armor format, or a GPG key id. |
303 | + @raises SystemExit() via sys.exit() on failure. |
304 | + """ |
305 | + try: |
306 | + return fetch_import_key(keyid) |
307 | + except GPGKeyError as e: |
308 | + error_out("Could not import key: {}".format(str(e))) |
309 | + |
310 | + |
311 | +def get_source_and_pgp_key(source_and_key): |
312 | + """Look for a pgp key ID or ascii-armor key in the given input. |
313 | + |
314 | + :param source_and_key: Sting, "source_spec|keyid" where '|keyid' is |
315 | + optional. |
316 | + :returns (source_spec, key_id OR None) as a tuple. Returns None for key_id |
317 | + if there was no '|' in the source_and_key string. |
318 | + """ |
319 | + try: |
320 | + source, key = source_and_key.split('|', 2) |
321 | + return source, key or None |
322 | + except ValueError: |
323 | + return source_and_key, None |
324 | + |
325 | + |
326 | +@deprecate("use charmhelpers.fetch.add_source() instead.", |
327 | + "2017-07", log=juju_log) |
328 | +def configure_installation_source(source_plus_key): |
329 | + """Configure an installation source. |
330 | + |
331 | + The functionality is provided by charmhelpers.fetch.add_source() |
332 | + The difference between the two functions is that add_source() signature |
333 | + requires the key to be passed directly, whereas this function passes an |
334 | + optional key by appending '|<key>' to the end of the source specificiation |
335 | + 'source'. |
336 | + |
337 | + Another difference from add_source() is that the function calls sys.exit(1) |
338 | + if the configuration fails, whereas add_source() raises |
339 | + SourceConfigurationError(). Another difference, is that add_source() |
340 | + silently fails (with a juju_log command) if there is no matching source to |
341 | + configure, whereas this function fails with a sys.exit(1) |
342 | + |
343 | + :param source: String_plus_key -- see above for details. |
344 | + |
345 | + Note that the behaviour on error is to log the error to the juju log and |
346 | + then call sys.exit(1). |
347 | + """ |
348 | + # extract the key if there is one, denoted by a '|' in the rel |
349 | + source, key = get_source_and_pgp_key(source_plus_key) |
350 | + |
351 | + # handle the ordinary sources via add_source |
352 | + try: |
353 | + fetch_add_source(source, key, fail_invalid=True) |
354 | + except SourceConfigError as se: |
355 | + error_out(str(se)) |
356 | |
357 | |
358 | def config_value_changed(option): |
359 | @@ -638,7 +575,6 @@ |
360 | |
361 | :returns: bool: : Returns True if configured installation source offers |
362 | a newer version of package. |
363 | - |
364 | """ |
365 | |
366 | import apt_pkg as apt |
367 | |
368 | === modified file 'charmhelpers/fetch/__init__.py' |
369 | --- charmhelpers/fetch/__init__.py 2016-09-20 17:07:51 +0000 |
370 | +++ charmhelpers/fetch/__init__.py 2017-04-10 18:12:16 +0000 |
371 | @@ -48,6 +48,13 @@ |
372 | pass |
373 | |
374 | |
375 | +class GPGKeyError(Exception): |
376 | + """Exception occurs when a GPG key cannot be fetched or used. The message |
377 | + indicates what the problem is. |
378 | + """ |
379 | + pass |
380 | + |
381 | + |
382 | class BaseFetchHandler(object): |
383 | |
384 | """Base class for FetchHandler implementations in fetch plugins""" |
385 | @@ -77,21 +84,22 @@ |
386 | fetch = importlib.import_module(module) |
387 | |
388 | filter_installed_packages = fetch.filter_installed_packages |
389 | -install = fetch.install |
390 | -upgrade = fetch.upgrade |
391 | -update = fetch.update |
392 | -purge = fetch.purge |
393 | +install = fetch.apt_install |
394 | +upgrade = fetch.apt_upgrade |
395 | +update = _fetch_update = fetch.apt_update |
396 | +purge = fetch.apt_purge |
397 | add_source = fetch.add_source |
398 | |
399 | if __platform__ == "ubuntu": |
400 | apt_cache = fetch.apt_cache |
401 | - apt_install = fetch.install |
402 | - apt_update = fetch.update |
403 | - apt_upgrade = fetch.upgrade |
404 | - apt_purge = fetch.purge |
405 | + apt_install = fetch.apt_install |
406 | + apt_update = fetch.apt_update |
407 | + apt_upgrade = fetch.apt_upgrade |
408 | + apt_purge = fetch.apt_purge |
409 | apt_mark = fetch.apt_mark |
410 | apt_hold = fetch.apt_hold |
411 | apt_unhold = fetch.apt_unhold |
412 | + import_key = fetch.import_key |
413 | get_upstream_version = fetch.get_upstream_version |
414 | elif __platform__ == "centos": |
415 | yum_search = fetch.yum_search |
416 | @@ -135,7 +143,7 @@ |
417 | for source, key in zip(sources, keys): |
418 | add_source(source, key) |
419 | if update: |
420 | - fetch.update(fatal=True) |
421 | + _fetch_update(fatal=True) |
422 | |
423 | |
424 | def install_remote(source, *args, **kwargs): |
425 | |
426 | === modified file 'charmhelpers/fetch/centos.py' |
427 | --- charmhelpers/fetch/centos.py 2016-08-11 15:47:44 +0000 |
428 | +++ charmhelpers/fetch/centos.py 2017-04-10 18:12:16 +0000 |
429 | @@ -132,7 +132,7 @@ |
430 | key_file.write(key) |
431 | key_file.flush() |
432 | key_file.seek(0) |
433 | - subprocess.check_call(['rpm', '--import', key_file]) |
434 | + subprocess.check_call(['rpm', '--import', key_file.name]) |
435 | else: |
436 | subprocess.check_call(['rpm', '--import', key]) |
437 | |
438 | |
439 | === modified file 'charmhelpers/fetch/ubuntu.py' |
440 | --- charmhelpers/fetch/ubuntu.py 2017-03-03 20:37:37 +0000 |
441 | +++ charmhelpers/fetch/ubuntu.py 2017-04-10 18:12:16 +0000 |
442 | @@ -12,29 +12,46 @@ |
443 | # See the License for the specific language governing permissions and |
444 | # limitations under the License. |
445 | |
446 | +from collections import OrderedDict |
447 | import os |
448 | +import platform |
449 | +import re |
450 | import six |
451 | import time |
452 | import subprocess |
453 | - |
454 | from tempfile import NamedTemporaryFile |
455 | + |
456 | from charmhelpers.core.host import ( |
457 | lsb_release |
458 | ) |
459 | -from charmhelpers.core.hookenv import log |
460 | -from charmhelpers.fetch import SourceConfigError |
461 | +from charmhelpers.core.hookenv import ( |
462 | + log, |
463 | + DEBUG, |
464 | +) |
465 | +from charmhelpers.fetch import SourceConfigError, GPGKeyError |
466 | |
467 | +PROPOSED_POCKET = ( |
468 | + "# Proposed\n" |
469 | + "deb http://archive.ubuntu.com/ubuntu {}-proposed main universe " |
470 | + "multiverse restricted\n") |
471 | +PROPOSED_PORTS_POCKET = ( |
472 | + "# Proposed\n" |
473 | + "deb http://ports.ubuntu.com/ubuntu-ports {}-proposed main universe " |
474 | + "multiverse restricted\n") |
475 | +# Only supports 64bit and ppc64 at the moment. |
476 | +ARCH_TO_PROPOSED_POCKET = { |
477 | + 'x86_64': PROPOSED_POCKET, |
478 | + 'ppc64le': PROPOSED_PORTS_POCKET, |
479 | +} |
480 | +CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu" |
481 | +CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA' |
482 | CLOUD_ARCHIVE = """# Ubuntu Cloud Archive |
483 | deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main |
484 | """ |
485 | - |
486 | -PROPOSED_POCKET = """# Proposed |
487 | -deb http://archive.ubuntu.com/ubuntu {}-proposed main universe multiverse restricted |
488 | -""" |
489 | - |
490 | CLOUD_ARCHIVE_POCKETS = { |
491 | # Folsom |
492 | 'folsom': 'precise-updates/folsom', |
493 | + 'folsom/updates': 'precise-updates/folsom', |
494 | 'precise-folsom': 'precise-updates/folsom', |
495 | 'precise-folsom/updates': 'precise-updates/folsom', |
496 | 'precise-updates/folsom': 'precise-updates/folsom', |
497 | @@ -43,6 +60,7 @@ |
498 | 'precise-proposed/folsom': 'precise-proposed/folsom', |
499 | # Grizzly |
500 | 'grizzly': 'precise-updates/grizzly', |
501 | + 'grizzly/updates': 'precise-updates/grizzly', |
502 | 'precise-grizzly': 'precise-updates/grizzly', |
503 | 'precise-grizzly/updates': 'precise-updates/grizzly', |
504 | 'precise-updates/grizzly': 'precise-updates/grizzly', |
505 | @@ -51,6 +69,7 @@ |
506 | 'precise-proposed/grizzly': 'precise-proposed/grizzly', |
507 | # Havana |
508 | 'havana': 'precise-updates/havana', |
509 | + 'havana/updates': 'precise-updates/havana', |
510 | 'precise-havana': 'precise-updates/havana', |
511 | 'precise-havana/updates': 'precise-updates/havana', |
512 | 'precise-updates/havana': 'precise-updates/havana', |
513 | @@ -59,6 +78,7 @@ |
514 | 'precise-proposed/havana': 'precise-proposed/havana', |
515 | # Icehouse |
516 | 'icehouse': 'precise-updates/icehouse', |
517 | + 'icehouse/updates': 'precise-updates/icehouse', |
518 | 'precise-icehouse': 'precise-updates/icehouse', |
519 | 'precise-icehouse/updates': 'precise-updates/icehouse', |
520 | 'precise-updates/icehouse': 'precise-updates/icehouse', |
521 | @@ -67,6 +87,7 @@ |
522 | 'precise-proposed/icehouse': 'precise-proposed/icehouse', |
523 | # Juno |
524 | 'juno': 'trusty-updates/juno', |
525 | + 'juno/updates': 'trusty-updates/juno', |
526 | 'trusty-juno': 'trusty-updates/juno', |
527 | 'trusty-juno/updates': 'trusty-updates/juno', |
528 | 'trusty-updates/juno': 'trusty-updates/juno', |
529 | @@ -75,6 +96,7 @@ |
530 | 'trusty-proposed/juno': 'trusty-proposed/juno', |
531 | # Kilo |
532 | 'kilo': 'trusty-updates/kilo', |
533 | + 'kilo/updates': 'trusty-updates/kilo', |
534 | 'trusty-kilo': 'trusty-updates/kilo', |
535 | 'trusty-kilo/updates': 'trusty-updates/kilo', |
536 | 'trusty-updates/kilo': 'trusty-updates/kilo', |
537 | @@ -83,6 +105,7 @@ |
538 | 'trusty-proposed/kilo': 'trusty-proposed/kilo', |
539 | # Liberty |
540 | 'liberty': 'trusty-updates/liberty', |
541 | + 'liberty/updates': 'trusty-updates/liberty', |
542 | 'trusty-liberty': 'trusty-updates/liberty', |
543 | 'trusty-liberty/updates': 'trusty-updates/liberty', |
544 | 'trusty-updates/liberty': 'trusty-updates/liberty', |
545 | @@ -91,6 +114,7 @@ |
546 | 'trusty-proposed/liberty': 'trusty-proposed/liberty', |
547 | # Mitaka |
548 | 'mitaka': 'trusty-updates/mitaka', |
549 | + 'mitaka/updates': 'trusty-updates/mitaka', |
550 | 'trusty-mitaka': 'trusty-updates/mitaka', |
551 | 'trusty-mitaka/updates': 'trusty-updates/mitaka', |
552 | 'trusty-updates/mitaka': 'trusty-updates/mitaka', |
553 | @@ -99,6 +123,7 @@ |
554 | 'trusty-proposed/mitaka': 'trusty-proposed/mitaka', |
555 | # Newton |
556 | 'newton': 'xenial-updates/newton', |
557 | + 'newton/updates': 'xenial-updates/newton', |
558 | 'xenial-newton': 'xenial-updates/newton', |
559 | 'xenial-newton/updates': 'xenial-updates/newton', |
560 | 'xenial-updates/newton': 'xenial-updates/newton', |
561 | @@ -107,6 +132,7 @@ |
562 | 'xenial-proposed/newton': 'xenial-proposed/newton', |
563 | # Ocata |
564 | 'ocata': 'xenial-updates/ocata', |
565 | + 'ocata/updates': 'xenial-updates/ocata', |
566 | 'xenial-ocata': 'xenial-updates/ocata', |
567 | 'xenial-ocata/updates': 'xenial-updates/ocata', |
568 | 'xenial-updates/ocata': 'xenial-updates/ocata', |
569 | @@ -115,6 +141,7 @@ |
570 | 'xenial-ocata/newton': 'xenial-proposed/ocata', |
571 | } |
572 | |
573 | + |
574 | APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT. |
575 | CMD_RETRY_DELAY = 10 # Wait 10 seconds between command retries. |
576 | CMD_RETRY_COUNT = 30 # Retry a failing fatal command X times. |
577 | @@ -145,7 +172,7 @@ |
578 | return apt_pkg.Cache(progress) |
579 | |
580 | |
581 | -def install(packages, options=None, fatal=False): |
582 | +def apt_install(packages, options=None, fatal=False): |
583 | """Install one or more packages.""" |
584 | if options is None: |
585 | options = ['--option=Dpkg::Options::=--force-confold'] |
586 | @@ -162,7 +189,7 @@ |
587 | _run_apt_command(cmd, fatal) |
588 | |
589 | |
590 | -def upgrade(options=None, fatal=False, dist=False): |
591 | +def apt_upgrade(options=None, fatal=False, dist=False): |
592 | """Upgrade all packages.""" |
593 | if options is None: |
594 | options = ['--option=Dpkg::Options::=--force-confold'] |
595 | @@ -177,13 +204,13 @@ |
596 | _run_apt_command(cmd, fatal) |
597 | |
598 | |
599 | -def update(fatal=False): |
600 | +def apt_update(fatal=False): |
601 | """Update local apt cache.""" |
602 | cmd = ['apt-get', 'update'] |
603 | _run_apt_command(cmd, fatal) |
604 | |
605 | |
606 | -def purge(packages, fatal=False): |
607 | +def apt_purge(packages, fatal=False): |
608 | """Purge one or more packages.""" |
609 | cmd = ['apt-get', '--assume-yes', 'purge'] |
610 | if isinstance(packages, six.string_types): |
611 | @@ -217,7 +244,45 @@ |
612 | return apt_mark(packages, 'unhold', fatal=fatal) |
613 | |
614 | |
615 | -def add_source(source, key=None): |
616 | +def import_key(keyid): |
617 | + """Import a key in either ASCII Armor or Radix64 format. |
618 | + |
619 | + `keyid` is either the keyid to fetch from a PGP server, or |
620 | + the key in ASCII armor foramt. |
621 | + |
622 | + :param keyid: String of key (or key id). |
623 | + :raises: GPGKeyError if the key could not be imported |
624 | + """ |
625 | + key = keyid.strip() |
626 | + if (key.startswith('-----BEGIN PGP PUBLIC KEY BLOCK-----') and |
627 | + key.endswith('-----END PGP PUBLIC KEY BLOCK-----')): |
628 | + log("PGP key found (looks like ASCII Armor format)", level=DEBUG) |
629 | + log("Importing ASCII Armor PGP key", level=DEBUG) |
630 | + with NamedTemporaryFile() as keyfile: |
631 | + with open(keyfile.name, 'w') as fd: |
632 | + fd.write(key) |
633 | + fd.write("\n") |
634 | + cmd = ['apt-key', 'add', keyfile.name] |
635 | + try: |
636 | + subprocess.check_call(cmd) |
637 | + except subprocess.CalledProcessError: |
638 | + error = "Error importing PGP key '{}'".format(key) |
639 | + log(error) |
640 | + raise GPGKeyError(error) |
641 | + else: |
642 | + log("PGP key found (looks like Radix64 format)", level=DEBUG) |
643 | + log("Importing PGP key from keyserver", level=DEBUG) |
644 | + cmd = ['apt-key', 'adv', '--keyserver', |
645 | + 'hkp://keyserver.ubuntu.com:80', '--recv-keys', key] |
646 | + try: |
647 | + subprocess.check_call(cmd) |
648 | + except subprocess.CalledProcessError: |
649 | + error = "Error importing PGP key '{}'".format(key) |
650 | + log(error) |
651 | + raise GPGKeyError(error) |
652 | + |
653 | + |
654 | +def add_source(source, key=None, fail_invalid=False): |
655 | """Add a package source to this system. |
656 | |
657 | @param source: a URL or sources.list entry, as supported by |
658 | @@ -233,6 +298,33 @@ |
659 | such as 'cloud:icehouse' |
660 | 'distro' may be used as a noop |
661 | |
662 | + Full list of source specifications supported by the function are: |
663 | + |
664 | + 'distro': A NOP; i.e. it has no effect. |
665 | + 'proposed': the proposed deb spec [2] is wrtten to |
666 | + /etc/apt/sources.list/proposed |
667 | + 'distro-proposed': adds <version>-proposed to the debs [2] |
668 | + 'ppa:<ppa-name>': add-apt-repository --yes <ppa_name> |
669 | + 'deb <deb-spec>': add-apt-repository --yes deb <deb-spec> |
670 | + 'http://....': add-apt-repository --yes http://... |
671 | + 'cloud-archive:<spec>': add-apt-repository -yes cloud-archive:<spec> |
672 | + 'cloud:<release>[-staging]': specify a Cloud Archive pocket <release> with |
673 | + optional staging version. If staging is used then the staging PPA [2] |
674 | + with be used. If staging is NOT used then the cloud archive [3] will be |
675 | + added, and the 'ubuntu-cloud-keyring' package will be added for the |
676 | + current distro. |
677 | + |
678 | + Otherwise the source is not recognised and this is logged to the juju log. |
679 | + However, no error is raised, unless sys_error_on_exit is True. |
680 | + |
681 | + [1] deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main |
682 | + where {} is replaced with the derived pocket name. |
683 | + [2] deb http://archive.ubuntu.com/ubuntu {}-proposed \ |
684 | + main universe multiverse restricted |
685 | + where {} is replaced with the lsb_release codename (e.g. xenial) |
686 | + [3] deb http://ubuntu-cloud.archive.canonical.com/ubuntu <pocket> |
687 | + to /etc/apt/sources.list.d/cloud-archive-list |
688 | + |
689 | @param key: A key to be added to the system's APT keyring and used |
690 | to verify the signatures on packages. Ideally, this should be an |
691 | ASCII format GPG public key including the block headers. A GPG key |
692 | @@ -240,51 +332,141 @@ |
693 | available to retrieve the actual public key from a public keyserver |
694 | placing your Juju environment at risk. ppa and cloud archive keys |
695 | are securely added automtically, so sould not be provided. |
696 | + |
697 | + @param fail_invalid: (boolean) if True, then the function raises a |
698 | + SourceConfigError is there is no matching installation source. |
699 | + |
700 | + @raises SourceConfigError() if for cloud:<pocket>, the <pocket> is not a |
701 | + valid pocket in CLOUD_ARCHIVE_POCKETS |
702 | """ |
703 | + _mapping = OrderedDict([ |
704 | + (r"^distro$", lambda: None), # This is a NOP |
705 | + (r"^(?:proposed|distro-proposed)$", _add_proposed), |
706 | + (r"^cloud-archive:(.*)$", _add_apt_repository), |
707 | + (r"^((?:deb |http:|https:|ppa:).*)$", _add_apt_repository), |
708 | + (r"^cloud:(.*)-(.*)\/staging$", _add_cloud_staging), |
709 | + (r"^cloud:(.*)-(.*)$", _add_cloud_distro_check), |
710 | + (r"^cloud:(.*)$", _add_cloud_pocket), |
711 | + ]) |
712 | if source is None: |
713 | - log('Source is not present. Skipping') |
714 | - return |
715 | - |
716 | - if (source.startswith('ppa:') or |
717 | - source.startswith('http') or |
718 | - source.startswith('deb ') or |
719 | - source.startswith('cloud-archive:')): |
720 | - cmd = ['add-apt-repository', '--yes', source] |
721 | - _run_with_retries(cmd) |
722 | - elif source.startswith('cloud:'): |
723 | - install(filter_installed_packages(['ubuntu-cloud-keyring']), |
724 | + source = '' |
725 | + for r, fn in six.iteritems(_mapping): |
726 | + m = re.match(r, source) |
727 | + if m: |
728 | + # call the assoicated function with the captured groups |
729 | + # raises SourceConfigError on error. |
730 | + fn(*m.groups()) |
731 | + if key: |
732 | + try: |
733 | + import_key(key) |
734 | + except GPGKeyError as e: |
735 | + raise SourceConfigError(str(e)) |
736 | + break |
737 | + else: |
738 | + # nothing matched. log an error and maybe sys.exit |
739 | + err = "Unknown source: {!r}".format(source) |
740 | + log(err) |
741 | + if fail_invalid: |
742 | + raise SourceConfigError(err) |
743 | + |
744 | + |
745 | +def _add_proposed(): |
746 | + """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list |
747 | + |
748 | + Uses lsb_release()['DISTRIB_CODENAME'] to determine the correct staza for |
749 | + the deb line. |
750 | + |
751 | + For intel architecutres PROPOSED_POCKET is used for the release, but for |
752 | + other architectures PROPOSED_PORTS_POCKET is used for the release. |
753 | + """ |
754 | + release = lsb_release()['DISTRIB_CODENAME'] |
755 | + arch = platform.machine() |
756 | + if arch not in six.iterkeys(ARCH_TO_PROPOSED_POCKET): |
757 | + raise SourceConfigError("Arch {} not supported for (distro-)proposed" |
758 | + .format(arch)) |
759 | + with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: |
760 | + apt.write(ARCH_TO_PROPOSED_POCKET[arch].format(release)) |
761 | + |
762 | + |
763 | +def _add_apt_repository(spec): |
764 | + """Add the spec using add_apt_repository |
765 | + |
766 | + :param spec: the parameter to pass to add_apt_repository |
767 | + """ |
768 | + _run_with_retries(['add-apt-repository', '--yes', spec]) |
769 | + |
770 | + |
771 | +def _add_cloud_pocket(pocket): |
772 | + """Add a cloud pocket as /etc/apt/sources.d/cloud-archive.list |
773 | + |
774 | + Note that this overwrites the existing file if there is one. |
775 | + |
776 | + This function also converts the simple pocket in to the actual pocket using |
777 | + the CLOUD_ARCHIVE_POCKETS mapping. |
778 | + |
779 | + :param pocket: string representing the pocket to add a deb spec for. |
780 | + :raises: SourceConfigError if the cloud pocket doesn't exist or the |
781 | + requested release doesn't match the current distro version. |
782 | + """ |
783 | + apt_install(filter_installed_packages(['ubuntu-cloud-keyring']), |
784 | fatal=True) |
785 | - pocket = source.split(':')[-1] |
786 | - if pocket not in CLOUD_ARCHIVE_POCKETS: |
787 | - raise SourceConfigError( |
788 | - 'Unsupported cloud: source option %s' % |
789 | - pocket) |
790 | - actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] |
791 | - with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: |
792 | - apt.write(CLOUD_ARCHIVE.format(actual_pocket)) |
793 | - elif source == 'proposed': |
794 | - release = lsb_release()['DISTRIB_CODENAME'] |
795 | - with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: |
796 | - apt.write(PROPOSED_POCKET.format(release)) |
797 | - elif source == 'distro': |
798 | - pass |
799 | - else: |
800 | - log("Unknown source: {!r}".format(source)) |
801 | - |
802 | - if key: |
803 | - if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key: |
804 | - with NamedTemporaryFile('w+') as key_file: |
805 | - key_file.write(key) |
806 | - key_file.flush() |
807 | - key_file.seek(0) |
808 | - subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file) |
809 | - else: |
810 | - # Note that hkp: is in no way a secure protocol. Using a |
811 | - # GPG key id is pointless from a security POV unless you |
812 | - # absolutely trust your network and DNS. |
813 | - subprocess.check_call(['apt-key', 'adv', '--keyserver', |
814 | - 'hkp://keyserver.ubuntu.com:80', '--recv', |
815 | - key]) |
816 | + if pocket not in CLOUD_ARCHIVE_POCKETS: |
817 | + raise SourceConfigError( |
818 | + 'Unsupported cloud: source option %s' % |
819 | + pocket) |
820 | + actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] |
821 | + with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: |
822 | + apt.write(CLOUD_ARCHIVE.format(actual_pocket)) |
823 | + |
824 | + |
825 | +def _add_cloud_staging(cloud_archive_release, openstack_release): |
826 | + """Add the cloud staging repository which is in |
827 | + ppa:ubuntu-cloud-archive/<openstack_release>-staging |
828 | + |
829 | + This function checks that the cloud_archive_release matches the current |
830 | + codename for the distro that charm is being installed on. |
831 | + |
832 | + :param cloud_archive_release: string, codename for the release. |
833 | + :param openstack_release: String, codename for the openstack release. |
834 | + :raises: SourceConfigError if the cloud_archive_release doesn't match the |
835 | + current version of the os. |
836 | + """ |
837 | + _verify_is_ubuntu_rel(cloud_archive_release, openstack_release) |
838 | + ppa = 'ppa:ubuntu-cloud-archive/{}-staging'.format(openstack_release) |
839 | + cmd = 'add-apt-repository -y {}'.format(ppa) |
840 | + _run_with_retries(cmd.split(' ')) |
841 | + |
842 | + |
843 | +def _add_cloud_distro_check(cloud_archive_release, openstack_release): |
844 | + """Add the cloud pocket, but also check the cloud_archive_release against |
845 | + the current distro, and use the openstack_release as the full lookup. |
846 | + |
847 | + This just calls _add_cloud_pocket() with the openstack_release as pocket |
848 | + to get the correct cloud-archive.list for dpkg to work with. |
849 | + |
850 | + :param cloud_archive_release:String, codename for the distro release. |
851 | + :param openstack_release: String, spec for the release to look up in the |
852 | + CLOUD_ARCHIVE_POCKETS |
853 | + :raises: SourceConfigError if this is the wrong distro, or the pocket spec |
854 | + doesn't exist. |
855 | + """ |
856 | + _verify_is_ubuntu_rel(cloud_archive_release, openstack_release) |
857 | + _add_cloud_pocket("{}-{}".format(cloud_archive_release, openstack_release)) |
858 | + |
859 | + |
860 | +def _verify_is_ubuntu_rel(release, os_release): |
861 | + """Verify that the release is in the same as the current ubuntu release. |
862 | + |
863 | + :param release: String, lowercase for the release. |
864 | + :param os_release: String, the os_release being asked for |
865 | + :raises: SourceConfigError if the release is not the same as the ubuntu |
866 | + release. |
867 | + """ |
868 | + ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] |
869 | + if release != ubuntu_rel: |
870 | + raise SourceConfigError( |
871 | + 'Invalid Cloud Archive release specified: {}-{} on this Ubuntu' |
872 | + 'version ({})'.format(release, os_release, ubuntu_rel)) |
873 | |
874 | |
875 | def _run_with_retries(cmd, max_retries=CMD_RETRY_COUNT, retry_exitcodes=(1,), |
876 | @@ -300,9 +482,12 @@ |
877 | :param: cmd_env: dict: Environment variables to add to the command run. |
878 | """ |
879 | |
880 | - env = os.environ.copy() |
881 | + env = None |
882 | + kwargs = {} |
883 | if cmd_env: |
884 | + env = os.environ.copy() |
885 | env.update(cmd_env) |
886 | + kwargs['env'] = env |
887 | |
888 | if not retry_message: |
889 | retry_message = "Failed executing '{}'".format(" ".join(cmd)) |
890 | @@ -314,7 +499,8 @@ |
891 | retry_results = (None,) + retry_exitcodes |
892 | while result in retry_results: |
893 | try: |
894 | - result = subprocess.check_call(cmd, env=env) |
895 | + # result = subprocess.check_call(cmd, env=env) |
896 | + result = subprocess.check_call(cmd, **kwargs) |
897 | except subprocess.CalledProcessError as e: |
898 | retry_count = retry_count + 1 |
899 | if retry_count > max_retries: |
900 | @@ -327,6 +513,7 @@ |
901 | def _run_apt_command(cmd, fatal=False): |
902 | """Run an apt command with optional retries. |
903 | |
904 | + :param: cmd: str: The apt command to run. |
905 | :param: fatal: bool: Whether the command's output should be checked and |
906 | retried. |
907 | """ |
908 | |
909 | === modified file 'tests/contrib/openstack/test_openstack_utils.py' |
910 | --- tests/contrib/openstack/test_openstack_utils.py 2016-12-14 18:51:15 +0000 |
911 | +++ tests/contrib/openstack/test_openstack_utils.py 2017-04-10 18:12:16 +0000 |
912 | @@ -1,13 +1,13 @@ |
913 | import io |
914 | import os |
915 | -import subprocess |
916 | -import tempfile |
917 | import contextlib |
918 | import unittest |
919 | from copy import copy |
920 | +from tests.helpers import patch_open |
921 | from testtools import TestCase |
922 | from mock import MagicMock, patch, call |
923 | |
924 | +from charmhelpers.fetch import ubuntu as fetch |
925 | import charmhelpers.contrib.openstack.utils as openstack |
926 | |
927 | import six |
928 | @@ -123,23 +123,9 @@ |
929 | |
930 | # Mock python-dnspython resolver used by get_host_ip() |
931 | |
932 | -PGP_KEY_ASCII_ARMOR = """-----BEGIN PGP PUBLIC KEY BLOCK----- |
933 | -Version: SKS 1.1.5 |
934 | -Comment: Hostname: keyserver.ubuntu.com |
935 | - |
936 | -mI0EUCEyTAEEAMuUxyfiegCCwn4J/c0nw5PUTSJdn5FqiUTq6iMfij65xf1vl0g/Mxqw0gfg |
937 | -AJIsCDvO9N9dloLAwF6FUBMg5My7WyhRPTAKF505TKJboyX3Pp4J1fU1LV8QFVOp87vUh1Rz |
938 | -B6GU7cSglhnbL85gmbJTllkzkb3h4Yw7W+edjcQ/ABEBAAG0K0xhdW5jaHBhZCBQUEEgZm9y |
939 | -IFVidW50dSBDbG91ZCBBcmNoaXZlIFRlYW2IuAQTAQIAIgUCUCEyTAIbAwYLCQgHAwIGFQgC |
940 | -CQoLBBYCAwECHgECF4AACgkQimhEop9oEE7kJAP/eTBgq3Mhbvo0d8elMOuqZx3nmU7gSyPh |
941 | -ep0zYIRZ5TJWl/7PRtvp0CJA6N6ZywYTQ/4ANHhpibcHZkh8K0AzUvsGXnJRSFoJeqyDbD91 |
942 | -EhoO+4ZfHs2HvRBQEDZILMa2OyuB497E5Mmyua3HDEOrG2cVLllsUZzpTFCx8NgeMHk= |
943 | -=jLBm |
944 | ------END PGP PUBLIC KEY BLOCK----- |
945 | -""" |
946 | - |
947 | |
948 | class FakeAnswer(object): |
949 | + |
950 | def __init__(self, ip): |
951 | self.ip = ip |
952 | |
953 | @@ -148,6 +134,7 @@ |
954 | |
955 | |
956 | class FakeResolver(object): |
957 | + |
958 | def __init__(self, ip): |
959 | self.ip = ip |
960 | |
961 | @@ -159,16 +146,19 @@ |
962 | |
963 | |
964 | class FakeReverse(object): |
965 | + |
966 | def from_address(self, address): |
967 | return '156.94.189.91.in-addr.arpa' |
968 | |
969 | |
970 | class FakeDNSName(object): |
971 | + |
972 | def __init__(self, dnsname): |
973 | pass |
974 | |
975 | |
976 | class FakeDNS(object): |
977 | + |
978 | def __init__(self, ip): |
979 | self.resolver = FakeResolver(ip) |
980 | self.reversename = FakeReverse() |
981 | @@ -177,6 +167,7 @@ |
982 | |
983 | |
984 | class OpenStackHelpersTestCase(TestCase): |
985 | + |
986 | def _apt_cache(self): |
987 | # mocks out the apt cache |
988 | def cache_get(package): |
989 | @@ -197,7 +188,7 @@ |
990 | |
991 | @patch('charmhelpers.contrib.openstack.utils.lsb_release') |
992 | def test_os_codename_from_install_source(self, mocked_lsb): |
993 | - '''Test mapping install source to OpenStack release name''' |
994 | + """Test mapping install source to OpenStack release name""" |
995 | mocked_lsb.return_value = FAKE_RELEASE |
996 | |
997 | # the openstack release shipped with respective ubuntu/lsb release. |
998 | @@ -236,7 +227,7 @@ |
999 | |
1000 | @patch('charmhelpers.contrib.openstack.utils.lsb_release') |
1001 | def test_os_codename_from_bad_install_source(self, mocked_lsb): |
1002 | - '''Test mapping install source to OpenStack release name''' |
1003 | + """Test mapping install source to OpenStack release name""" |
1004 | _fake_release = copy(FAKE_RELEASE) |
1005 | _fake_release['DISTRIB_CODENAME'] = 'natty' |
1006 | |
1007 | @@ -249,32 +240,32 @@ |
1008 | mocked_err.assert_called_with(_er) |
1009 | |
1010 | def test_os_codename_from_version(self): |
1011 | - '''Test mapping OpenStack numerical versions to code name''' |
1012 | + """Test mapping OpenStack numerical versions to code name""" |
1013 | self.assertEquals(openstack.get_os_codename_version('2013.1'), |
1014 | 'grizzly') |
1015 | |
1016 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1017 | def test_os_codename_from_bad_version(self, mocked_error): |
1018 | - '''Test mapping a bad OpenStack numerical versions to code name''' |
1019 | + """Test mapping a bad OpenStack numerical versions to code name""" |
1020 | openstack.get_os_codename_version('2014.5.5') |
1021 | expected_err = ('Could not determine OpenStack codename for ' |
1022 | 'version 2014.5.5') |
1023 | mocked_error.assert_called_with(expected_err) |
1024 | |
1025 | def test_os_version_from_codename(self): |
1026 | - '''Test mapping a OpenStack codename to numerical version''' |
1027 | + """Test mapping a OpenStack codename to numerical version""" |
1028 | self.assertEquals(openstack.get_os_version_codename('folsom'), |
1029 | '2012.2') |
1030 | |
1031 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1032 | def test_os_version_from_bad_codename(self, mocked_error): |
1033 | - '''Test mapping a bad OpenStack codename to numerical version''' |
1034 | + """Test mapping a bad OpenStack codename to numerical version""" |
1035 | openstack.get_os_version_codename('foo') |
1036 | expected_err = 'Could not derive OpenStack version for codename: foo' |
1037 | mocked_error.assert_called_with(expected_err) |
1038 | |
1039 | def test_os_version_swift_from_codename(self): |
1040 | - '''Test mapping a swift codename to numerical version''' |
1041 | + """Test mapping a swift codename to numerical version""" |
1042 | self.assertEquals(openstack.get_os_version_codename_swift('liberty'), |
1043 | '2.5.0') |
1044 | |
1045 | @@ -283,7 +274,7 @@ |
1046 | |
1047 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1048 | def test_os_version_swift_from_bad_codename(self, mocked_error): |
1049 | - '''Test mapping a bad swift codename to numerical version''' |
1050 | + """Test mapping a bad swift codename to numerical version""" |
1051 | openstack.get_os_version_codename_swift('foo') |
1052 | expected_err = 'Could not derive swift version for codename: foo' |
1053 | mocked_error.assert_called_with(expected_err) |
1054 | @@ -302,7 +293,7 @@ |
1055 | self.assertEquals(openstack.get_swift_codename('1.2.3'), None) |
1056 | |
1057 | def test_os_codename_from_package(self): |
1058 | - '''Test deriving OpenStack codename from an installed package''' |
1059 | + """Test deriving OpenStack codename from an installed package""" |
1060 | with patch('apt_pkg.Cache') as cache: |
1061 | cache.return_value = self._apt_cache() |
1062 | for pkg, vers in six.iteritems(FAKE_REPO): |
1063 | @@ -316,7 +307,7 @@ |
1064 | |
1065 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1066 | def test_os_codename_from_bad_package_version(self, mocked_error): |
1067 | - '''Test deriving OpenStack codename for a poorly versioned package''' |
1068 | + """Test deriving OpenStack codename for a poorly versioned package""" |
1069 | with patch('apt_pkg.Cache') as cache: |
1070 | cache.return_value = self._apt_cache() |
1071 | openstack.get_os_codename_package('bad-version') |
1072 | @@ -325,7 +316,7 @@ |
1073 | |
1074 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1075 | def test_os_codename_from_bad_package(self, mocked_error): |
1076 | - '''Test deriving OpenStack codename from an unavailable package''' |
1077 | + """Test deriving OpenStack codename from an unavailable package""" |
1078 | with patch('apt_pkg.Cache') as cache: |
1079 | cache.return_value = self._apt_cache() |
1080 | try: |
1081 | @@ -339,7 +330,7 @@ |
1082 | mocked_error.assert_called_with(e) |
1083 | |
1084 | def test_os_codename_from_bad_package_nonfatal(self): |
1085 | - '''Test OpenStack codename from an unavailable package is non-fatal''' |
1086 | + """Test OpenStack codename from an unavailable package is non-fatal""" |
1087 | with patch('apt_pkg.Cache') as cache: |
1088 | cache.return_value = self._apt_cache() |
1089 | self.assertEquals( |
1090 | @@ -349,7 +340,7 @@ |
1091 | |
1092 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1093 | def test_os_codename_from_uninstalled_package(self, mock_error): |
1094 | - '''Test OpenStack codename from an available but uninstalled pkg''' |
1095 | + """Test OpenStack codename from an available but uninstalled pkg""" |
1096 | with patch('apt_pkg.Cache') as cache: |
1097 | cache.return_value = self._apt_cache() |
1098 | try: |
1099 | @@ -361,7 +352,7 @@ |
1100 | mock_error.assert_called_with(e) |
1101 | |
1102 | def test_os_codename_from_uninstalled_package_nonfatal(self): |
1103 | - '''Test OpenStack codename from avail uninstalled pkg is non fatal''' |
1104 | + """Test OpenStack codename from avail uninstalled pkg is non fatal""" |
1105 | with patch('apt_pkg.Cache') as cache: |
1106 | cache.return_value = self._apt_cache() |
1107 | self.assertEquals( |
1108 | @@ -371,7 +362,7 @@ |
1109 | |
1110 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1111 | def test_os_version_from_package(self, mocked_error): |
1112 | - '''Test deriving OpenStack version from an installed package''' |
1113 | + """Test deriving OpenStack version from an installed package""" |
1114 | with patch('apt_pkg.Cache') as cache: |
1115 | cache.return_value = self._apt_cache() |
1116 | for pkg, vers in six.iteritems(FAKE_REPO): |
1117 | @@ -384,7 +375,7 @@ |
1118 | |
1119 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1120 | def test_os_version_from_bad_package(self, mocked_error): |
1121 | - '''Test deriving OpenStack version from an uninstalled package''' |
1122 | + """Test deriving OpenStack version from an uninstalled package""" |
1123 | with patch('apt_pkg.Cache') as cache: |
1124 | cache.return_value = self._apt_cache() |
1125 | try: |
1126 | @@ -398,7 +389,7 @@ |
1127 | mocked_error.assert_called_with(e) |
1128 | |
1129 | def test_os_version_from_bad_package_nonfatal(self): |
1130 | - '''Test OpenStack version from an uninstalled package is non-fatal''' |
1131 | + """Test OpenStack version from an uninstalled package is non-fatal""" |
1132 | with patch('apt_pkg.Cache') as cache: |
1133 | cache.return_value = self._apt_cache() |
1134 | self.assertEquals( |
1135 | @@ -410,7 +401,7 @@ |
1136 | @patch.object(openstack, 'git_os_codename_install_source') |
1137 | @patch('charmhelpers.contrib.openstack.utils.config') |
1138 | def test_os_release_uncached(self, config, git_cn, get_cn): |
1139 | - openstack.os_rel = None |
1140 | + openstack._os_rel = None |
1141 | get_cn.return_value = 'folsom' |
1142 | git_cn.return_value = None |
1143 | |
1144 | @@ -420,69 +411,142 @@ |
1145 | self.assertEquals('folsom', openstack.os_release('nova-common')) |
1146 | |
1147 | def test_os_release_cached(self): |
1148 | - openstack.os_rel = 'foo' |
1149 | + openstack._os_rel = 'foo' |
1150 | self.assertEquals('foo', openstack.os_release('nova-common')) |
1151 | |
1152 | @patch.object(openstack, 'juju_log') |
1153 | @patch('sys.exit') |
1154 | def test_error_out(self, mocked_exit, juju_log): |
1155 | - '''Test erroring out''' |
1156 | + """Test erroring out""" |
1157 | openstack.error_out('Everything broke.') |
1158 | _log = 'FATAL ERROR: Everything broke.' |
1159 | juju_log.assert_called_with(_log, level='ERROR') |
1160 | mocked_exit.assert_called_with(1) |
1161 | |
1162 | + def test_get_source_and_pgp_key(self): |
1163 | + tests = { |
1164 | + "source|key": ('source', 'key'), |
1165 | + "source|": ('source', None), |
1166 | + "|key": ('', 'key'), |
1167 | + "source": ('source', None), |
1168 | + } |
1169 | + for k, v in six.iteritems(tests): |
1170 | + self.assertEqual(openstack.get_source_and_pgp_key(k), v) |
1171 | + |
1172 | + # These should still work, even though the bulk of the functionality has |
1173 | + # moved to charmhelpers.fetch.add_source() |
1174 | def test_configure_install_source_distro(self): |
1175 | - '''Test configuring installation from distro''' |
1176 | + """Test configuring installation from distro""" |
1177 | self.assertIsNone(openstack.configure_installation_source('distro')) |
1178 | |
1179 | def test_configure_install_source_ppa(self): |
1180 | - '''Test configuring installation source from PPA''' |
1181 | + """Test configuring installation source from PPA""" |
1182 | with patch('subprocess.check_call') as mock: |
1183 | src = 'ppa:gandelman-a/openstack' |
1184 | openstack.configure_installation_source(src) |
1185 | - ex_cmd = ['add-apt-repository', '-y', 'ppa:gandelman-a/openstack'] |
1186 | + ex_cmd = [ |
1187 | + 'add-apt-repository', '--yes', 'ppa:gandelman-a/openstack'] |
1188 | mock.assert_called_with(ex_cmd) |
1189 | |
1190 | - @patch(builtin_open) |
1191 | - @patch('charmhelpers.contrib.openstack.utils.juju_log') |
1192 | - @patch('charmhelpers.contrib.openstack.utils.import_key') |
1193 | - def test_configure_install_source_deb_url(self, _import, _log, _open): |
1194 | - '''Test configuring installation source from deb repo url''' |
1195 | - _file = MagicMock(spec=io.FileIO) |
1196 | - _open.return_value = _file |
1197 | + @patch('subprocess.check_call') |
1198 | + @patch.object(fetch, 'import_key') |
1199 | + def test_configure_install_source_deb_url(self, _import, _spcc): |
1200 | + """Test configuring installation source from deb repo url""" |
1201 | src = ('deb http://ubuntu-cloud.archive.canonical.com/ubuntu ' |
1202 | 'precise-havana main|KEYID') |
1203 | openstack.configure_installation_source(src) |
1204 | _import.assert_called_with('KEYID') |
1205 | - _file.__enter__().write.assert_called_with(src.split('|')[0]) |
1206 | - src = ('deb http://ubuntu-cloud.archive.canonical.com/ubuntu ' |
1207 | - 'precise-havana main') |
1208 | - openstack.configure_installation_source(src) |
1209 | - _file.__enter__().write.assert_called_with(src) |
1210 | + _spcc.assert_called_once_with( |
1211 | + ['add-apt-repository', '--yes', |
1212 | + 'deb http://ubuntu-cloud.archive.canonical.com/ubuntu ' |
1213 | + 'precise-havana main']) |
1214 | |
1215 | - @patch('charmhelpers.contrib.openstack.utils.lsb_release') |
1216 | + @patch.object(fetch, 'lsb_release') |
1217 | @patch(builtin_open) |
1218 | - @patch('charmhelpers.contrib.openstack.utils.juju_log') |
1219 | - def test_configure_install_source_distro_proposed(self, _log, _open, _lsb): |
1220 | - '''Test configuring installation source from deb repo url''' |
1221 | + @patch('subprocess.check_call') |
1222 | + def test_configure_install_source_distro_proposed( |
1223 | + self, _spcc, _open, _lsb): |
1224 | + """Test configuring installation source from deb repo url""" |
1225 | _lsb.return_value = FAKE_RELEASE |
1226 | _file = MagicMock(spec=io.FileIO) |
1227 | _open.return_value = _file |
1228 | openstack.configure_installation_source('distro-proposed') |
1229 | + _file.__enter__().write.assert_called_once_with( |
1230 | + '# Proposed\ndeb http://archive.ubuntu.com/ubuntu ' |
1231 | + 'precise-proposed main universe multiverse restricted\n') |
1232 | src = ('deb http://archive.ubuntu.com/ubuntu/ precise-proposed ' |
1233 | 'restricted main multiverse universe') |
1234 | openstack.configure_installation_source(src) |
1235 | - _file.__enter__().write.assert_called_with(src) |
1236 | + _spcc.assert_called_once_with( |
1237 | + ['add-apt-repository', '--yes', |
1238 | + 'deb http://archive.ubuntu.com/ubuntu/ precise-proposed ' |
1239 | + 'restricted main multiverse universe']) |
1240 | + |
1241 | + @patch('charmhelpers.fetch.filter_installed_packages') |
1242 | + @patch('charmhelpers.fetch.apt_install') |
1243 | + @patch.object(openstack, 'error_out') |
1244 | + @patch.object(openstack, 'juju_log') |
1245 | + def test_add_source_cloud_invalid_pocket(self, _log, _out, |
1246 | + apt_install, filter_pkg): |
1247 | + openstack.configure_installation_source("cloud:havana-updates") |
1248 | + _e = ('Invalid Cloud Archive release specified: ' |
1249 | + 'havana-updates on this Ubuntuversion') |
1250 | + _s = _out.call_args[0][0] |
1251 | + self.assertTrue(_s.startswith(_e)) |
1252 | + |
1253 | + @patch.object(fetch, 'filter_installed_packages') |
1254 | + @patch.object(fetch, 'apt_install') |
1255 | + @patch.object(fetch, 'lsb_release') |
1256 | + def test_add_source_cloud_pocket_style(self, lsb_release, |
1257 | + apt_install, filter_pkg): |
1258 | + source = "cloud:precise-updates/havana" |
1259 | + lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'} |
1260 | + result = ( |
1261 | + "# Ubuntu Cloud Archive\n" |
1262 | + "deb http://ubuntu-cloud.archive.canonical.com/ubuntu " |
1263 | + "precise-updates/havana main\n") |
1264 | + with patch_open() as (mock_open, mock_file): |
1265 | + openstack.configure_installation_source(source) |
1266 | + mock_file.write.assert_called_with(result) |
1267 | + filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
1268 | + |
1269 | + @patch.object(fetch, 'filter_installed_packages') |
1270 | + @patch.object(fetch, 'apt_install') |
1271 | + @patch.object(fetch, 'lsb_release') |
1272 | + def test_add_source_cloud_os_style(self, lsb_release, |
1273 | + apt_install, filter_pkg): |
1274 | + source = "cloud:precise-havana" |
1275 | + lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'} |
1276 | + result = ( |
1277 | + "# Ubuntu Cloud Archive\n" |
1278 | + "deb http://ubuntu-cloud.archive.canonical.com/ubuntu " |
1279 | + "precise-updates/havana main\n") |
1280 | + with patch_open() as (mock_open, mock_file): |
1281 | + openstack.configure_installation_source(source) |
1282 | + mock_file.write.assert_called_with(result) |
1283 | + filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
1284 | + |
1285 | + @patch.object(fetch, 'filter_installed_packages') |
1286 | + @patch.object(fetch, 'apt_install') |
1287 | + def test_add_source_cloud_distroless_style(self, apt_install, filter_pkg): |
1288 | + source = "cloud:havana" |
1289 | + result = ( |
1290 | + "# Ubuntu Cloud Archive\n" |
1291 | + "deb http://ubuntu-cloud.archive.canonical.com/ubuntu " |
1292 | + "precise-updates/havana main\n") |
1293 | + with patch_open() as (mock_open, mock_file): |
1294 | + openstack.configure_installation_source(source) |
1295 | + mock_file.write.assert_called_with(result) |
1296 | + filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
1297 | |
1298 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1299 | def test_configure_bad_install_source(self, _error): |
1300 | openstack.configure_installation_source('foo') |
1301 | - _error.assert_called_with('Invalid openstack-release specified: foo') |
1302 | + _error.assert_called_with("Unknown source: 'foo'") |
1303 | |
1304 | - @patch('charmhelpers.contrib.openstack.utils.lsb_release') |
1305 | + @patch.object(fetch, 'lsb_release') |
1306 | def test_configure_install_source_uca_staging(self, _lsb): |
1307 | - '''Test configuring installation source from UCA staging sources''' |
1308 | + """Test configuring installation source from UCA staging sources""" |
1309 | _lsb.return_value = FAKE_RELEASE |
1310 | # staging pockets are configured as PPAs |
1311 | with patch('subprocess.check_call') as _subp: |
1312 | @@ -493,76 +557,63 @@ |
1313 | _subp.assert_called_with(cmd) |
1314 | |
1315 | @patch(builtin_open) |
1316 | - @patch('charmhelpers.contrib.openstack.utils.apt_install') |
1317 | - @patch('charmhelpers.contrib.openstack.utils.lsb_release') |
1318 | - def test_configure_install_source_uca_repos(self, _lsb, _install, _open): |
1319 | - '''Test configuring installation source from UCA sources''' |
1320 | + @patch.object(fetch, 'apt_install') |
1321 | + @patch.object(fetch, 'lsb_release') |
1322 | + @patch.object(fetch, 'filter_installed_packages') |
1323 | + def test_configure_install_source_uca_repos( |
1324 | + self, _fip, _lsb, _install, _open): |
1325 | + """Test configuring installation source from UCA sources""" |
1326 | _lsb.return_value = FAKE_RELEASE |
1327 | _file = MagicMock(spec=io.FileIO) |
1328 | _open.return_value = _file |
1329 | + _fip.side_effect = lambda x: x |
1330 | for src, url in UCA_SOURCES: |
1331 | + actual_url = "# Ubuntu Cloud Archive\n{}\n".format(url) |
1332 | openstack.configure_installation_source(src) |
1333 | - _install.assert_called_with('ubuntu-cloud-keyring', |
1334 | + _install.assert_called_with(['ubuntu-cloud-keyring'], |
1335 | fatal=True) |
1336 | _open.assert_called_with( |
1337 | '/etc/apt/sources.list.d/cloud-archive.list', |
1338 | 'w' |
1339 | ) |
1340 | - _file.__enter__().write.assert_called_with(url) |
1341 | + _file.__enter__().write.assert_called_with(actual_url) |
1342 | |
1343 | @patch('charmhelpers.contrib.openstack.utils.error_out') |
1344 | def test_configure_install_source_bad_uca(self, mocked_error): |
1345 | - '''Test configuring installation source from bad UCA source''' |
1346 | + """Test configuring installation source from bad UCA source""" |
1347 | try: |
1348 | openstack.configure_installation_source('cloud:foo-bar') |
1349 | except: |
1350 | # ignore exceptions that raise when error_out is mocked |
1351 | # and doesn't sys.exit(1) |
1352 | pass |
1353 | - _e = 'Invalid Cloud Archive release specified: foo-bar' |
1354 | - mocked_error.assert_called_with(_e) |
1355 | - |
1356 | - @patch.object(openstack, 'juju_log', lambda *args, **kwargs: None) |
1357 | - def test_import_apt_key_radix(self): |
1358 | - '''Ensure shell out apt-key during key import''' |
1359 | - with patch('subprocess.check_call') as _subp: |
1360 | - openstack.import_key('foo') |
1361 | - cmd = ['apt-key', 'adv', '--keyserver', |
1362 | - 'hkp://keyserver.ubuntu.com:80', '--recv-keys', 'foo'] |
1363 | - _subp.assert_called_with(cmd) |
1364 | - |
1365 | - @patch.object(openstack, 'juju_log', lambda *args, **kwargs: None) |
1366 | - def test_import_apt_key_ascii_armor(self): |
1367 | - with tempfile.NamedTemporaryFile() as tmp: |
1368 | - with patch.object(openstack, 'tempfile') as \ |
1369 | - mock_tmpfile: |
1370 | - tmpfile = mock_tmpfile.NamedTemporaryFile.return_value |
1371 | - tmpfile.__enter__.return_value = tmpfile |
1372 | - tmpfile.name = tmp.name |
1373 | - with patch('subprocess.check_call') as _subp: |
1374 | - openstack.import_key(PGP_KEY_ASCII_ARMOR) |
1375 | - cmd = ['apt-key', 'add', tmp.name] |
1376 | - _subp.assert_called_with(cmd) |
1377 | - |
1378 | - @patch.object(openstack, 'juju_log', lambda *args, **kwargs: None) |
1379 | - @patch('charmhelpers.contrib.openstack.utils.error_out') |
1380 | - def test_import_bad_apt_key(self, mocked_error): |
1381 | - '''Ensure error when importing apt key fails''' |
1382 | - with patch('subprocess.check_call') as _subp: |
1383 | - cmd = ['apt-key', 'adv', '--keyserver', |
1384 | - 'hkp://keyserver.ubuntu.com:80', '--recv-keys', 'foo'] |
1385 | - _subp.side_effect = subprocess.CalledProcessError(1, cmd, '') |
1386 | - openstack.import_key('foo') |
1387 | - cmd = ['apt-key', 'adv', '--keyserver', |
1388 | - 'hkp://keyserver.ubuntu.com:80', '--recv-keys', 'foo'] |
1389 | - mocked_error.assert_called_with("Error importing PGP key 'foo'") |
1390 | + _e = ('Invalid Cloud Archive release specified: foo-bar' |
1391 | + ' on this Ubuntuversion') |
1392 | + _s = mocked_error.call_args[0][0] |
1393 | + self.assertTrue(_s.startswith(_e)) |
1394 | + |
1395 | + @patch.object(openstack, 'fetch_import_key') |
1396 | + def test_import_key_calls_fetch_import_key(self, fetch_import_key): |
1397 | + openstack.import_key('random-string') |
1398 | + fetch_import_key.assert_called_once_with('random-string') |
1399 | + |
1400 | + @patch.object(openstack, 'fetch_import_key') |
1401 | + @patch.object(openstack, 'sys') |
1402 | + def test_import_key_calls_sys_exit_on_error(self, mock_sys, |
1403 | + fetch_import_key): |
1404 | + |
1405 | + def raiser(_): |
1406 | + raise openstack.GPGKeyError("an error occurred") |
1407 | + fetch_import_key.side_effect = raiser |
1408 | + openstack.import_key('random failure') |
1409 | + mock_sys.exit.assert_called_once_with(1) |
1410 | |
1411 | @patch('os.mkdir') |
1412 | @patch('os.path.exists') |
1413 | @patch('charmhelpers.contrib.openstack.utils.charm_dir') |
1414 | @patch(builtin_open) |
1415 | def test_save_scriptrc(self, _open, _charm_dir, _exists, _mkdir): |
1416 | - '''Test generation of scriptrc from environment''' |
1417 | + """Test generation of scriptrc from environment""" |
1418 | scriptrc = ['#!/bin/bash\n', |
1419 | 'export setting1=foo\n', |
1420 | 'export setting2=bar\n'] |
1421 | @@ -618,14 +669,14 @@ |
1422 | @patch.object(openstack, 'is_block_device') |
1423 | @patch.object(openstack, 'error_out') |
1424 | def test_ensure_block_device_bad_config(self, err, is_bd): |
1425 | - '''Test it doesn't prepare storage with bad config''' |
1426 | + """Test it doesn't prepare storage with bad config""" |
1427 | openstack.ensure_block_device(block_device='none') |
1428 | self.assertTrue(err.called) |
1429 | |
1430 | @patch.object(openstack, 'is_block_device') |
1431 | @patch.object(openstack, 'ensure_loopback_device') |
1432 | def test_ensure_block_device_loopback(self, ensure_loopback, is_bd): |
1433 | - '''Test it ensures loopback device when checking block device''' |
1434 | + """Test it ensures loopback device when checking block device""" |
1435 | defsize = openstack.DEFAULT_LOOPBACK_SIZE |
1436 | is_bd.return_value = True |
1437 | |
1438 | @@ -641,7 +692,7 @@ |
1439 | |
1440 | @patch.object(openstack, 'is_block_device') |
1441 | def test_ensure_standard_block_device(self, is_bd): |
1442 | - '''Test it looks for storage at both relative and full device path''' |
1443 | + """Test it looks for storage at both relative and full device path""" |
1444 | for dev in ['vdb', '/dev/vdb']: |
1445 | openstack.ensure_block_device(dev) |
1446 | is_bd.assert_called_with('/dev/vdb') |
1447 | @@ -649,7 +700,7 @@ |
1448 | @patch.object(openstack, 'is_block_device') |
1449 | @patch.object(openstack, 'error_out') |
1450 | def test_ensure_nonexistent_block_device(self, error_out, is_bd): |
1451 | - '''Test it will not ensure a non-existant block device''' |
1452 | + """Test it will not ensure a non-existant block device""" |
1453 | is_bd.return_value = False |
1454 | openstack.ensure_block_device(block_device='foo') |
1455 | self.assertTrue(error_out.called) |
1456 | @@ -660,7 +711,7 @@ |
1457 | @patch.object(openstack, 'zap_disk') |
1458 | @patch.object(openstack, 'is_lvm_physical_volume') |
1459 | def test_clean_storage_unmount(self, is_pv, zap_disk, mounts, umount, log): |
1460 | - '''Test it unmounts block device when cleaning storage''' |
1461 | + """Test it unmounts block device when cleaning storage""" |
1462 | is_pv.return_value = False |
1463 | zap_disk.return_value = True |
1464 | mounts.return_value = MOUNTS |
1465 | @@ -673,7 +724,7 @@ |
1466 | @patch.object(openstack, 'mounts') |
1467 | @patch.object(openstack, 'is_lvm_physical_volume') |
1468 | def test_clean_storage_lvm_wipe(self, is_pv, mounts, rm_lv, rm_vg, log): |
1469 | - '''Test it removes traces of LVM when cleaning storage''' |
1470 | + """Test it removes traces of LVM when cleaning storage""" |
1471 | mounts.return_value = [] |
1472 | is_pv.return_value = True |
1473 | openstack.clean_storage('/dev/vdb') |
1474 | @@ -684,7 +735,7 @@ |
1475 | @patch.object(openstack, 'is_lvm_physical_volume') |
1476 | @patch.object(openstack, 'mounts') |
1477 | def test_clean_storage_zap_disk(self, mounts, is_pv, zap_disk): |
1478 | - '''It removes traces of LVM when cleaning storage''' |
1479 | + """It removes traces of LVM when cleaning storage""" |
1480 | mounts.return_value = [] |
1481 | is_pv.return_value = False |
1482 | openstack.clean_storage('/dev/vdb') |
1483 | |
1484 | === modified file 'tests/contrib/openstack/test_os_utils.py' |
1485 | --- tests/contrib/openstack/test_os_utils.py 2017-03-24 16:50:31 +0000 |
1486 | +++ tests/contrib/openstack/test_os_utils.py 2017-04-10 18:12:16 +0000 |
1487 | @@ -126,7 +126,7 @@ |
1488 | mock_git_os_codename_install_source, |
1489 | mock_config): |
1490 | # Wipe the modules cached os_rel |
1491 | - utils.os_rel = None |
1492 | + utils._os_rel = None |
1493 | mock_get_os_codename_install_source.return_value = None |
1494 | mock_get_os_codename_package.return_value = None |
1495 | mock_git_os_codename_install_source.return_value = None |
1496 | |
1497 | === modified file 'tests/fetch/test_fetch.py' |
1498 | --- tests/fetch/test_fetch.py 2017-03-03 20:18:47 +0000 |
1499 | +++ tests/fetch/test_fetch.py 2017-04-10 18:12:16 +0000 |
1500 | @@ -1,25 +1,22 @@ |
1501 | -import subprocess |
1502 | import six |
1503 | import os |
1504 | import yaml |
1505 | -import imp |
1506 | -import tempfile |
1507 | |
1508 | -from charmhelpers import osplatform |
1509 | -from tests.helpers import patch_open |
1510 | from testtools import TestCase |
1511 | from mock import ( |
1512 | patch, |
1513 | MagicMock, |
1514 | call, |
1515 | - sentinel, |
1516 | ) |
1517 | + |
1518 | from charmhelpers import fetch |
1519 | -from six.moves import StringIO |
1520 | + |
1521 | if six.PY3: |
1522 | from urllib.parse import urlparse |
1523 | + builtin_open = 'builtins.open' |
1524 | else: |
1525 | from urlparse import urlparse |
1526 | + builtin_open = '__builtin__.open' |
1527 | |
1528 | |
1529 | FAKE_APT_CACHE = { |
1530 | @@ -60,479 +57,7 @@ |
1531 | |
1532 | class FetchTest(TestCase): |
1533 | |
1534 | - @patch("charmhelpers.fetch.ubuntu.log") |
1535 | - @patch.object(osplatform, 'get_platform') |
1536 | - @patch('apt_pkg.Cache') |
1537 | - def test_filter_packages_missing_ubuntu(self, cache, platform, log): |
1538 | - platform.return_value = 'ubuntu' |
1539 | - imp.reload(fetch) |
1540 | - |
1541 | - cache.side_effect = fake_apt_cache |
1542 | - result = fetch.filter_installed_packages(['vim', 'emacs']) |
1543 | - self.assertEquals(result, ['emacs']) |
1544 | - |
1545 | - @patch("charmhelpers.fetch.centos.log") |
1546 | - @patch('yum.YumBase.doPackageLists') |
1547 | - @patch.object(osplatform, 'get_platform') |
1548 | - def test_filter_packages_missing_centos(self, platform, yumBase, log): |
1549 | - platform.return_value = 'centos' |
1550 | - imp.reload(fetch) |
1551 | - |
1552 | - class MockPackage: |
1553 | - def __init__(self, name): |
1554 | - self.base_package_name = name |
1555 | - |
1556 | - yum_dict = { |
1557 | - 'installed': { |
1558 | - MockPackage('vim') |
1559 | - }, |
1560 | - 'available': { |
1561 | - MockPackage('vim') |
1562 | - } |
1563 | - } |
1564 | - import yum |
1565 | - yum.YumBase.return_value.doPackageLists.return_value = yum_dict |
1566 | - result = fetch.filter_installed_packages(['vim', 'emacs']) |
1567 | - self.assertEquals(result, ['emacs']) |
1568 | - |
1569 | - @patch("charmhelpers.fetch.ubuntu.log") |
1570 | - @patch.object(osplatform, 'get_platform') |
1571 | - @patch('apt_pkg.Cache') |
1572 | - def test_filter_packages_none_missing_ubuntu(self, cache, platform, log): |
1573 | - platform.return_value = 'ubuntu' |
1574 | - imp.reload(fetch) |
1575 | - |
1576 | - cache.side_effect = fake_apt_cache |
1577 | - result = fetch.filter_installed_packages(['vim']) |
1578 | - self.assertEquals(result, []) |
1579 | - |
1580 | - @patch("charmhelpers.fetch.centos.log") |
1581 | - @patch.object(osplatform, 'get_platform') |
1582 | - def test_filter_packages_none_missing_centos(self, platform, log): |
1583 | - platform.return_value = 'centos' |
1584 | - imp.reload(fetch) |
1585 | - |
1586 | - class MockPackage: |
1587 | - def __init__(self, name): |
1588 | - self.base_package_name = name |
1589 | - |
1590 | - yum_dict = { |
1591 | - 'installed': { |
1592 | - MockPackage('vim') |
1593 | - }, |
1594 | - 'available': { |
1595 | - MockPackage('vim') |
1596 | - } |
1597 | - } |
1598 | - import yum |
1599 | - yum.yumBase.return_value.doPackageLists.return_value = yum_dict |
1600 | - result = fetch.filter_installed_packages(['vim']) |
1601 | - self.assertEquals(result, []) |
1602 | - |
1603 | - @patch.object(osplatform, 'get_platform') |
1604 | - @patch('charmhelpers.fetch.ubuntu.log') |
1605 | - @patch('apt_pkg.Cache') |
1606 | - def test_filter_packages_not_available_ubuntu(self, cache, log, platform): |
1607 | - platform.return_value = 'ubuntu' |
1608 | - imp.reload(fetch) |
1609 | - |
1610 | - cache.side_effect = fake_apt_cache |
1611 | - result = fetch.filter_installed_packages(['vim', 'joe']) |
1612 | - self.assertEquals(result, ['joe']) |
1613 | - log.assert_called_with('Package joe has no installation candidate.', |
1614 | - level='WARNING') |
1615 | - |
1616 | - @patch.object(osplatform, 'get_platform') |
1617 | - @patch('charmhelpers.fetch.centos.log') |
1618 | - @patch('yum.YumBase.doPackageLists') |
1619 | - def test_filter_packages_not_available_centos(self, yumBase, |
1620 | - log, platform): |
1621 | - platform.return_value = 'centos' |
1622 | - imp.reload(fetch) |
1623 | - |
1624 | - class MockPackage: |
1625 | - def __init__(self, name): |
1626 | - self.base_package_name = name |
1627 | - |
1628 | - yum_dict = { |
1629 | - 'installed': { |
1630 | - MockPackage('vim') |
1631 | - } |
1632 | - } |
1633 | - import yum |
1634 | - yum.YumBase.return_value.doPackageLists.return_value = yum_dict |
1635 | - |
1636 | - result = fetch.filter_installed_packages(['vim', 'joe']) |
1637 | - self.assertEquals(result, ['joe']) |
1638 | - |
1639 | - @patch.object(osplatform, 'get_platform') |
1640 | - @patch('charmhelpers.fetch.ubuntu.log') |
1641 | - def test_add_source_none_ubuntu(self, log, platform): |
1642 | - platform.return_value = 'ubuntu' |
1643 | - imp.reload(fetch) |
1644 | - |
1645 | - fetch.add_source(source=None) |
1646 | - self.assertTrue(log.called) |
1647 | - |
1648 | - @patch.object(osplatform, 'get_platform') |
1649 | - @patch('charmhelpers.fetch.centos.log') |
1650 | - def test_add_source_none_centos(self, log, platform): |
1651 | - platform.return_value = 'centos' |
1652 | - imp.reload(fetch) |
1653 | - |
1654 | - fetch.add_source(source=None) |
1655 | - self.assertTrue(log.called) |
1656 | - |
1657 | - @patch.object(osplatform, 'get_platform') |
1658 | - @patch('subprocess.check_call') |
1659 | - def test_add_source_ppa(self, check_call, platform): |
1660 | - platform.return_value = 'ubuntu' |
1661 | - imp.reload(fetch) |
1662 | - |
1663 | - source = "ppa:test-ppa" |
1664 | - fetch.add_source(source=source) |
1665 | - check_call.assert_called_with( |
1666 | - ['add-apt-repository', '--yes', source], env=getenv()) |
1667 | - |
1668 | - @patch("charmhelpers.fetch.ubuntu.log") |
1669 | - @patch.object(osplatform, 'get_platform') |
1670 | - @patch('subprocess.check_call') |
1671 | - @patch('time.sleep') |
1672 | - def test_add_source_ppa_retries_30_times(self, sleep, check_call, |
1673 | - platform, log): |
1674 | - platform.return_value = 'ubuntu' |
1675 | - imp.reload(fetch) |
1676 | - |
1677 | - self.call_count = 0 |
1678 | - |
1679 | - def side_effect(*args, **kwargs): |
1680 | - """Raise an 3 times, then return 0 """ |
1681 | - self.call_count += 1 |
1682 | - if self.call_count <= fetch.ubuntu.CMD_RETRY_COUNT: |
1683 | - raise subprocess.CalledProcessError( |
1684 | - returncode=1, cmd="some add-apt-repository command") |
1685 | - else: |
1686 | - return 0 |
1687 | - check_call.side_effect = side_effect |
1688 | - |
1689 | - source = "ppa:test-ppa" |
1690 | - fetch.add_source(source=source) |
1691 | - check_call.assert_called_with( |
1692 | - ['add-apt-repository', '--yes', source], env=getenv()) |
1693 | - sleep.assert_called_with(10) |
1694 | - self.assertTrue(fetch.ubuntu.CMD_RETRY_COUNT, sleep.call_count) |
1695 | - |
1696 | - @patch('charmhelpers.fetch.ubuntu.log') |
1697 | - @patch.object(osplatform, 'get_platform') |
1698 | - @patch('subprocess.check_call') |
1699 | - def test_add_source_http_ubuntu(self, check_call, platform, log): |
1700 | - platform.return_value = 'ubuntu' |
1701 | - imp.reload(fetch) |
1702 | - |
1703 | - source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
1704 | - fetch.add_source(source=source) |
1705 | - check_call.assert_called_with( |
1706 | - ['add-apt-repository', '--yes', source], env=getenv()) |
1707 | - |
1708 | - @patch('charmhelpers.fetch.centos.log') |
1709 | - @patch.object(osplatform, 'get_platform') |
1710 | - @patch('os.listdir') |
1711 | - def test_add_source_http_centos(self, listdir, platform, log): |
1712 | - platform.return_value = 'centos' |
1713 | - imp.reload(fetch) |
1714 | - |
1715 | - source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
1716 | - with patch_open() as (mock_open, mock_file): |
1717 | - fetch.add_source(source=source) |
1718 | - listdir.assert_called_with('/etc/yum.repos.d/') |
1719 | - mock_file.write.assert_has_calls([ |
1720 | - call("[archive.ubuntu.com_ubuntu raring-backports main]\n"), |
1721 | - call("name=archive.ubuntu.com/ubuntu raring-backports main\n"), |
1722 | - call("baseurl=http://archive.ubuntu.com/ubuntu raring" |
1723 | - "-backports main\n\n")]) |
1724 | - |
1725 | - @patch('charmhelpers.fetch.ubuntu.log') |
1726 | - @patch.object(osplatform, 'get_platform') |
1727 | - @patch('subprocess.check_call') |
1728 | - def test_add_source_https(self, check_call, platform, log): |
1729 | - platform.return_value = 'ubuntu' |
1730 | - imp.reload(fetch) |
1731 | - |
1732 | - source = "https://example.com" |
1733 | - fetch.add_source(source=source) |
1734 | - check_call.assert_called_with( |
1735 | - ['add-apt-repository', '--yes', source], env=getenv()) |
1736 | - |
1737 | - @patch('charmhelpers.fetch.ubuntu.log') |
1738 | - @patch.object(osplatform, 'get_platform') |
1739 | - @patch('subprocess.check_call') |
1740 | - def test_add_source_deb(self, check_call, platform, log): |
1741 | - """add-apt-repository behaves differently when using the deb prefix. |
1742 | - |
1743 | - $ add-apt-repository --yes "http://special.example.com/ubuntu |
1744 | - precise-special main" |
1745 | - $ grep special /etc/apt/sources.list |
1746 | - deb http://special.example.com/ubuntu precise precise-special main |
1747 | - deb-src http://special.example.com/ubuntu precise precise-special main |
1748 | - |
1749 | - $ add-apt-repository --yes "deb http://special.example.com/ubuntu |
1750 | - precise-special main" |
1751 | - $ grep special /etc/apt/sources.list |
1752 | - deb http://special.example.com/ubuntu precise precise-special main |
1753 | - deb-src http://special.example.com/ubuntu precise precise-special main |
1754 | - deb http://special.example.com/ubuntu precise-special main |
1755 | - deb-src http://special.example.com/ubuntu precise-special main |
1756 | - """ |
1757 | - platform.return_value = 'ubuntu' |
1758 | - imp.reload(fetch) |
1759 | - |
1760 | - source = "deb http://archive.ubuntu.com/ubuntu raring-backports main" |
1761 | - fetch.add_source(source=source) |
1762 | - check_call.assert_called_with( |
1763 | - ['add-apt-repository', '--yes', source], env=getenv()) |
1764 | - |
1765 | - @patch.object(osplatform, 'get_platform') |
1766 | - @patch.object(fetch.ubuntu, 'filter_installed_packages') |
1767 | - @patch.object(fetch.ubuntu, 'install') |
1768 | - def test_add_source_cloud_invalid_pocket(self, install, |
1769 | - filter_pkg, platform): |
1770 | - platform.return_value = 'ubuntu' |
1771 | - imp.reload(fetch) |
1772 | - |
1773 | - source = "cloud:havana-updates" |
1774 | - self.assertRaises(fetch.ubuntu.SourceConfigError, |
1775 | - fetch.add_source, source) |
1776 | - filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
1777 | - |
1778 | - @patch('charmhelpers.fetch.ubuntu.log') |
1779 | - @patch.object(osplatform, 'get_platform') |
1780 | - @patch.object(fetch.ubuntu, 'filter_installed_packages') |
1781 | - @patch.object(fetch.ubuntu, 'install') |
1782 | - def test_add_source_cloud_pocket_style(self, install, filter_pkg, |
1783 | - platform, log): |
1784 | - platform.return_value = 'ubuntu' |
1785 | - imp.reload(fetch) |
1786 | - |
1787 | - source = "cloud:precise-updates/havana" |
1788 | - result = ('# Ubuntu Cloud Archive\n' |
1789 | - 'deb http://ubuntu-cloud.archive.canonical.com/ubuntu' |
1790 | - ' precise-updates/havana main\n') |
1791 | - |
1792 | - with patch_open() as (mock_open, mock_file): |
1793 | - fetch.add_source(source=source) |
1794 | - mock_file.write.assert_called_with(result) |
1795 | - filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
1796 | - |
1797 | - @patch('charmhelpers.fetch.ubuntu.log') |
1798 | - @patch.object(osplatform, 'get_platform') |
1799 | - @patch.object(fetch.ubuntu, 'filter_installed_packages') |
1800 | - @patch.object(fetch.ubuntu, 'install') |
1801 | - def test_add_source_cloud_os_style(self, install, filter_pkg, |
1802 | - platform, log): |
1803 | - platform.return_value = 'ubuntu' |
1804 | - imp.reload(fetch) |
1805 | - |
1806 | - source = "cloud:precise-havana" |
1807 | - result = ('# Ubuntu Cloud Archive\n' |
1808 | - 'deb http://ubuntu-cloud.archive.canonical.com/ubuntu' |
1809 | - ' precise-updates/havana main\n') |
1810 | - with patch_open() as (mock_open, mock_file): |
1811 | - fetch.add_source(source=source) |
1812 | - mock_file.write.assert_called_with(result) |
1813 | - filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
1814 | - |
1815 | - @patch('charmhelpers.fetch.ubuntu.log') |
1816 | - @patch.object(osplatform, 'get_platform') |
1817 | - @patch.object(fetch.ubuntu, 'filter_installed_packages') |
1818 | - @patch.object(fetch.ubuntu, 'install') |
1819 | - def test_add_source_cloud_distroless_style(self, install, filter_pkg, |
1820 | - platform, log): |
1821 | - platform.return_value = 'ubuntu' |
1822 | - imp.reload(fetch) |
1823 | - |
1824 | - source = "cloud:havana" |
1825 | - result = ('# Ubuntu Cloud Archive\n' |
1826 | - 'deb http://ubuntu-cloud.archive.canonical.com/ubuntu' |
1827 | - ' precise-updates/havana main\n') |
1828 | - with patch_open() as (mock_open, mock_file): |
1829 | - fetch.add_source(source=source) |
1830 | - mock_file.write.assert_called_with(result) |
1831 | - filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
1832 | - |
1833 | - @patch('charmhelpers.fetch.ubuntu.log') |
1834 | - @patch.object(osplatform, 'get_platform') |
1835 | - @patch.object(fetch.ubuntu, 'lsb_release') |
1836 | - def test_add_source_proposed(self, lsb_release, platform, log): |
1837 | - platform.return_value = 'ubuntu' |
1838 | - imp.reload(fetch) |
1839 | - |
1840 | - source = "proposed" |
1841 | - result = ('# Proposed\n' |
1842 | - 'deb http://archive.ubuntu.com/ubuntu precise-proposed' |
1843 | - ' main universe multiverse restricted\n') |
1844 | - lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'} |
1845 | - with patch_open() as (mock_open, mock_file): |
1846 | - fetch.add_source(source=source) |
1847 | - mock_file.write.assert_called_with(result) |
1848 | - |
1849 | - @patch('charmhelpers.fetch.ubuntu.log') |
1850 | - @patch.object(osplatform, 'get_platform') |
1851 | - @patch('subprocess.check_call') |
1852 | - def test_add_source_http_and_key_id_ubuntu(self, check_call, |
1853 | - platform, log): |
1854 | - platform.return_value = 'ubuntu' |
1855 | - imp.reload(fetch) |
1856 | - |
1857 | - source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
1858 | - key_id = "akey" |
1859 | - check_call.return_value = 0 # Successful exit code |
1860 | - fetch.add_source(source=source, key=key_id) |
1861 | - check_call.assert_has_calls([ |
1862 | - call(['add-apt-repository', '--yes', source], env=getenv()), |
1863 | - call(['apt-key', 'adv', '--keyserver', |
1864 | - 'hkp://keyserver.ubuntu.com:80', '--recv', key_id]) |
1865 | - ]) |
1866 | - |
1867 | - @patch('charmhelpers.fetch.centos.log') |
1868 | - @patch('os.listdir') |
1869 | - @patch.object(osplatform, 'get_platform') |
1870 | - @patch('subprocess.check_call') |
1871 | - def test_add_source_http_and_key_id_centos(self, check_call, |
1872 | - platform, listdir, log): |
1873 | - platform.return_value = 'centos' |
1874 | - imp.reload(fetch) |
1875 | - |
1876 | - source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
1877 | - key_id = "akey" |
1878 | - with patch_open() as (mock_open, mock_file): |
1879 | - fetch.add_source(source=source, key=key_id) |
1880 | - listdir.assert_called_with('/etc/yum.repos.d/') |
1881 | - mock_file.write.assert_has_calls([ |
1882 | - call("[archive.ubuntu.com_ubuntu raring-backports main]\n"), |
1883 | - call("name=archive.ubuntu.com/ubuntu raring-backports main\n"), |
1884 | - call("baseurl=http://archive.ubuntu.com/ubuntu raring" |
1885 | - "-backports main\n\n")]) |
1886 | - check_call.assert_called_with(['rpm', '--import', key_id]) |
1887 | - |
1888 | - @patch('charmhelpers.fetch.ubuntu.log') |
1889 | - @patch.object(osplatform, 'get_platform') |
1890 | - @patch('subprocess.check_call') |
1891 | - def test_add_source_https_and_key_id_ubuntu(self, check_call, |
1892 | - platform, log): |
1893 | - check_call.return_value = 0 # Success from both calls |
1894 | - platform.return_value = 'ubuntu' |
1895 | - imp.reload(fetch) |
1896 | - |
1897 | - source = "https://USER:PASS@private-ppa.launchpad.net/project/awesome" |
1898 | - key_id = "GPGPGP" |
1899 | - fetch.add_source(source=source, key=key_id) |
1900 | - check_call.assert_has_calls([ |
1901 | - call(['add-apt-repository', '--yes', source], env=getenv()), |
1902 | - call(['apt-key', 'adv', '--keyserver', |
1903 | - 'hkp://keyserver.ubuntu.com:80', '--recv', key_id]) |
1904 | - ]) |
1905 | - |
1906 | - @patch('charmhelpers.fetch.centos.log') |
1907 | - @patch('os.listdir') |
1908 | - @patch.object(osplatform, 'get_platform') |
1909 | - @patch('subprocess.check_call') |
1910 | - def test_add_source_https_and_key_id_centos(self, check_call, |
1911 | - platform, listdir, log): |
1912 | - platform.return_value = 'centos' |
1913 | - imp.reload(fetch) |
1914 | - |
1915 | - source = "https://USER:PASS@private-ppa.launchpad.net/project/awesome" |
1916 | - key_id = "GPGPGP" |
1917 | - with patch_open() as (mock_open, mock_file): |
1918 | - fetch.add_source(source=source, key=key_id) |
1919 | - listdir.assert_called_with('/etc/yum.repos.d/') |
1920 | - mock_file.write.assert_has_calls([ |
1921 | - call("[_USER:PASS@private-ppa.launchpad" |
1922 | - ".net_project_awesome]\n"), |
1923 | - call("name=/USER:PASS@private-ppa.launchpad.net" |
1924 | - "/project/awesome\n"), |
1925 | - call("baseurl=https://USER:PASS@private-ppa.launchpad.net" |
1926 | - "/project/awesome\n\n")]) |
1927 | - check_call.assert_called_with(['rpm', '--import', key_id]) |
1928 | - |
1929 | - @patch('charmhelpers.fetch.ubuntu.log') |
1930 | - @patch.object(osplatform, 'get_platform') |
1931 | - @patch('subprocess.check_call') |
1932 | - def test_add_source_http_and_key_ubuntu(self, check_call, platform, log): |
1933 | - platform.return_value = 'ubuntu' |
1934 | - imp.reload(fetch) |
1935 | - |
1936 | - source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
1937 | - key = ''' |
1938 | - -----BEGIN PGP PUBLIC KEY BLOCK----- |
1939 | - [...] |
1940 | - -----END PGP PUBLIC KEY BLOCK----- |
1941 | - ''' |
1942 | - |
1943 | - received_args = [] |
1944 | - received_key = StringIO() |
1945 | - |
1946 | - def _check_call(arg, stdin=None, env=None): |
1947 | - '''side_effect to store the stdin passed to check_call process.''' |
1948 | - if stdin is not None: |
1949 | - received_args.extend(arg) |
1950 | - received_key.write(stdin.read()) |
1951 | - return 0 # Successful return code checked by add-apt-repository |
1952 | - |
1953 | - with patch('subprocess.check_call', |
1954 | - side_effect=_check_call) as check_call: |
1955 | - fetch.add_source(source=source, key=key) |
1956 | - check_call.assert_any_call( |
1957 | - ['add-apt-repository', '--yes', source], env=getenv()) |
1958 | - self.assertEqual(['apt-key', 'add', '-'], received_args) |
1959 | - self.assertEqual(key, received_key.getvalue()) |
1960 | - |
1961 | - @patch('charmhelpers.fetch.centos.log') |
1962 | - @patch.object(tempfile, 'NamedTemporaryFile') |
1963 | - @patch('os.listdir') |
1964 | - @patch.object(osplatform, 'get_platform') |
1965 | - @patch('subprocess.check_call') |
1966 | - def test_add_source_http_and_key_centos(self, check_call, platform, |
1967 | - listdir, temp_file, log): |
1968 | - platform.return_value = 'centos' |
1969 | - imp.reload(fetch) |
1970 | - |
1971 | - source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
1972 | - key = ''' |
1973 | - -----BEGIN PGP PUBLIC KEY BLOCK----- |
1974 | - [...] |
1975 | - -----END PGP PUBLIC KEY BLOCK----- |
1976 | - ''' |
1977 | - temp_file.return_value.__enter__.return_value = key |
1978 | - temp_file.return_value.__exit__.return_value = key |
1979 | - |
1980 | - with patch_open() as (mock_open, mock_file): |
1981 | - fetch.add_source(source=source, key=key) |
1982 | - listdir.assert_called_with('/etc/yum.repos.d/') |
1983 | - self.assertTrue(log.called) |
1984 | - check_call.assert_called_with(['rpm', '--import', key]) |
1985 | - |
1986 | - @patch.object(osplatform, 'get_platform') |
1987 | - @patch('charmhelpers.fetch.ubuntu.log') |
1988 | - def test_add_unparsable_source(self, log_, platform): |
1989 | - platform.return_value = 'ubuntu' |
1990 | - imp.reload(fetch) |
1991 | - |
1992 | - source = "propsed" # Minor typo |
1993 | - fetch.add_source(source=source) |
1994 | - self.assertEqual(1, log_.call_count) |
1995 | - |
1996 | - @patch('charmhelpers.fetch.ubuntu.log') |
1997 | - @patch.object(osplatform, 'get_platform') |
1998 | - def test_add_distro_source(self, platform, log): |
1999 | - platform.return_value = 'ubuntu' |
2000 | - imp.reload(fetch) |
2001 | - |
2002 | - source = "distro" |
2003 | - # distro is a noop but test validate no exception is thrown |
2004 | - fetch.add_source(source=source) |
2005 | - |
2006 | - @patch('charmhelpers.fetch.ubuntu.log') |
2007 | + @patch('charmhelpers.fetch.log') |
2008 | @patch.object(fetch, 'config') |
2009 | @patch.object(fetch, 'add_source') |
2010 | def test_configure_sources_single_source(self, add_source, config, log): |
2011 | @@ -587,12 +112,11 @@ |
2012 | ] |
2013 | self.assertRaises(fetch.SourceConfigError, fetch.configure_sources) |
2014 | |
2015 | - @patch.object(osplatform, 'get_platform') |
2016 | - @patch('charmhelpers.fetch.ubuntu.update') |
2017 | + @patch.object(fetch, '_fetch_update') |
2018 | @patch.object(fetch, 'config') |
2019 | @patch.object(fetch, 'add_source') |
2020 | def test_configure_sources_update_called_ubuntu(self, add_source, config, |
2021 | - update, platform): |
2022 | + update): |
2023 | config.side_effect = ['source', 'key'] |
2024 | fetch.configure_sources(update=True) |
2025 | add_source.assert_called_with('source', 'key') |
2026 | @@ -703,7 +227,8 @@ |
2027 | self.assertEqual(2, log_.call_count) |
2028 | |
2029 | @patch('charmhelpers.fetch.log') |
2030 | - def test_plugins_are_valid(self, log_): |
2031 | + @patch.object(fetch.importlib, 'import_module') |
2032 | + def test_plugins_are_valid(self, import_module, log_): |
2033 | plugins = fetch.plugins() |
2034 | self.assertEqual(len(fetch.FETCH_HANDLERS), len(plugins)) |
2035 | |
2036 | @@ -742,572 +267,3 @@ |
2037 | expected_url = "http://example.com/foo" |
2038 | u = self.fh.base_url(sample_url) |
2039 | self.assertEqual(u, expected_url) |
2040 | - |
2041 | - |
2042 | -class AptTests(TestCase): |
2043 | - |
2044 | - @patch.object(osplatform, 'get_platform') |
2045 | - @patch('subprocess.call') |
2046 | - @patch('charmhelpers.fetch.ubuntu.log') |
2047 | - def test_apt_upgrade_non_fatal(self, log, mock_call, platform): |
2048 | - platform.return_value = 'ubuntu' |
2049 | - imp.reload(fetch) |
2050 | - |
2051 | - options = ['--foo', '--bar'] |
2052 | - fetch.apt_upgrade(options) |
2053 | - |
2054 | - mock_call.assert_called_with( |
2055 | - ['apt-get', '--assume-yes', |
2056 | - '--foo', '--bar', 'upgrade'], |
2057 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2058 | - |
2059 | - @patch.object(osplatform, 'get_platform') |
2060 | - @patch('subprocess.check_call') |
2061 | - @patch('charmhelpers.fetch.ubuntu.log') |
2062 | - def test_apt_upgrade_fatal(self, log, mock_call, platform): |
2063 | - platform.return_value = 'ubuntu' |
2064 | - imp.reload(fetch) |
2065 | - |
2066 | - options = ['--foo', '--bar'] |
2067 | - fetch.apt_upgrade(options, fatal=True) |
2068 | - |
2069 | - mock_call.assert_called_with( |
2070 | - ['apt-get', '--assume-yes', |
2071 | - '--foo', '--bar', 'upgrade'], |
2072 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2073 | - |
2074 | - @patch.object(osplatform, 'get_platform') |
2075 | - @patch('subprocess.check_call') |
2076 | - @patch('charmhelpers.fetch.ubuntu.log') |
2077 | - def test_apt_dist_upgrade_fatal(self, log, mock_call, platform): |
2078 | - platform.return_value = 'ubuntu' |
2079 | - imp.reload(fetch) |
2080 | - |
2081 | - options = ['--foo', '--bar'] |
2082 | - fetch.apt_upgrade(options, fatal=True, dist=True) |
2083 | - |
2084 | - mock_call.assert_called_with( |
2085 | - ['apt-get', '--assume-yes', |
2086 | - '--foo', '--bar', 'dist-upgrade'], |
2087 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2088 | - |
2089 | - @patch.object(osplatform, 'get_platform') |
2090 | - @patch('subprocess.call') |
2091 | - @patch('charmhelpers.fetch.ubuntu.log') |
2092 | - def test_installs_apt_packages(self, log, mock_call, platform): |
2093 | - platform.return_value = 'ubuntu' |
2094 | - imp.reload(fetch) |
2095 | - |
2096 | - packages = ['foo', 'bar'] |
2097 | - options = ['--foo', '--bar'] |
2098 | - |
2099 | - fetch.apt_install(packages, options) |
2100 | - |
2101 | - mock_call.assert_called_with( |
2102 | - ['apt-get', '--assume-yes', |
2103 | - '--foo', '--bar', 'install', 'foo', 'bar'], |
2104 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2105 | - |
2106 | - @patch.object(osplatform, 'get_platform') |
2107 | - @patch('subprocess.call') |
2108 | - @patch('charmhelpers.fetch.ubuntu.log') |
2109 | - def test_installs_apt_packages_without_options(self, log, mock_call, |
2110 | - platform): |
2111 | - platform.return_value = 'ubuntu' |
2112 | - imp.reload(fetch) |
2113 | - |
2114 | - packages = ['foo', 'bar'] |
2115 | - |
2116 | - fetch.apt_install(packages) |
2117 | - |
2118 | - mock_call.assert_called_with( |
2119 | - ['apt-get', '--assume-yes', |
2120 | - '--option=Dpkg::Options::=--force-confold', |
2121 | - 'install', 'foo', 'bar'], |
2122 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2123 | - |
2124 | - @patch.object(osplatform, 'get_platform') |
2125 | - @patch('subprocess.call') |
2126 | - @patch('charmhelpers.fetch.ubuntu.log') |
2127 | - def test_installs_apt_packages_as_string(self, log, mock_call, platform): |
2128 | - platform.return_value = 'ubuntu' |
2129 | - imp.reload(fetch) |
2130 | - |
2131 | - packages = 'foo bar' |
2132 | - options = ['--foo', '--bar'] |
2133 | - |
2134 | - fetch.apt_install(packages, options) |
2135 | - |
2136 | - mock_call.assert_called_with( |
2137 | - ['apt-get', '--assume-yes', |
2138 | - '--foo', '--bar', 'install', 'foo bar'], |
2139 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2140 | - |
2141 | - @patch.object(osplatform, 'get_platform') |
2142 | - @patch('subprocess.check_call') |
2143 | - @patch('charmhelpers.fetch.ubuntu.log') |
2144 | - def test_installs_apt_packages_with_possible_errors(self, log, |
2145 | - check_call, platform): |
2146 | - platform.return_value = 'ubuntu' |
2147 | - imp.reload(fetch) |
2148 | - |
2149 | - packages = ['foo', 'bar'] |
2150 | - options = ['--foo', '--bar'] |
2151 | - |
2152 | - fetch.apt_install(packages, options, fatal=True) |
2153 | - |
2154 | - check_call.assert_called_with( |
2155 | - ['apt-get', '--assume-yes', |
2156 | - '--foo', '--bar', 'install', 'foo', 'bar'], |
2157 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2158 | - |
2159 | - @patch.object(osplatform, 'get_platform') |
2160 | - @patch('subprocess.check_call') |
2161 | - @patch('charmhelpers.fetch.ubuntu.log') |
2162 | - def test_purges_apt_packages_as_string_fatal(self, log, mock_call, |
2163 | - platform): |
2164 | - platform.return_value = 'ubuntu' |
2165 | - imp.reload(fetch) |
2166 | - |
2167 | - packages = 'irrelevant names' |
2168 | - mock_call.side_effect = OSError('fail') |
2169 | - |
2170 | - self.assertRaises(OSError, fetch.apt_purge, packages, fatal=True) |
2171 | - self.assertTrue(log.called) |
2172 | - |
2173 | - @patch.object(osplatform, 'get_platform') |
2174 | - @patch('subprocess.check_call') |
2175 | - @patch('charmhelpers.fetch.ubuntu.log') |
2176 | - def test_purges_apt_packages_fatal(self, log, mock_call, platform): |
2177 | - platform.return_value = 'ubuntu' |
2178 | - imp.reload(fetch) |
2179 | - |
2180 | - packages = ['irrelevant', 'names'] |
2181 | - mock_call.side_effect = OSError('fail') |
2182 | - |
2183 | - self.assertRaises(OSError, fetch.apt_purge, packages, fatal=True) |
2184 | - self.assertTrue(log.called) |
2185 | - |
2186 | - @patch.object(osplatform, 'get_platform') |
2187 | - @patch('subprocess.call') |
2188 | - @patch('charmhelpers.fetch.ubuntu.log') |
2189 | - def test_purges_apt_packages_as_string_nofatal(self, log, mock_call, |
2190 | - platform): |
2191 | - platform.return_value = 'ubuntu' |
2192 | - imp.reload(fetch) |
2193 | - |
2194 | - packages = 'foo bar' |
2195 | - |
2196 | - fetch.apt_purge(packages) |
2197 | - |
2198 | - self.assertTrue(log.called) |
2199 | - mock_call.assert_called_with( |
2200 | - ['apt-get', '--assume-yes', 'purge', 'foo bar'], |
2201 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2202 | - |
2203 | - @patch.object(osplatform, 'get_platform') |
2204 | - @patch('subprocess.call') |
2205 | - @patch('charmhelpers.fetch.ubuntu.log') |
2206 | - def test_purges_apt_packages_nofatal(self, log, mock_call, platform): |
2207 | - platform.return_value = 'ubuntu' |
2208 | - imp.reload(fetch) |
2209 | - |
2210 | - packages = ['foo', 'bar'] |
2211 | - |
2212 | - fetch.apt_purge(packages) |
2213 | - |
2214 | - self.assertTrue(log.called) |
2215 | - mock_call.assert_called_with( |
2216 | - ['apt-get', '--assume-yes', 'purge', 'foo', 'bar'], |
2217 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2218 | - |
2219 | - @patch.object(osplatform, 'get_platform') |
2220 | - @patch('subprocess.check_call') |
2221 | - @patch('charmhelpers.fetch.ubuntu.log') |
2222 | - def test_mark_apt_packages_as_string_fatal(self, log, mock_call, |
2223 | - platform): |
2224 | - packages = 'irrelevant names' |
2225 | - mock_call.side_effect = OSError('fail') |
2226 | - |
2227 | - self.assertRaises(OSError, fetch.apt_mark, packages, sentinel.mark, |
2228 | - fatal=True) |
2229 | - self.assertTrue(log.called) |
2230 | - |
2231 | - @patch.object(osplatform, 'get_platform') |
2232 | - @patch('subprocess.check_call') |
2233 | - @patch('charmhelpers.fetch.ubuntu.log') |
2234 | - def test_mark_apt_packages_fatal(self, log, mock_call, platform): |
2235 | - platform.return_value = 'ubuntu' |
2236 | - imp.reload(fetch) |
2237 | - |
2238 | - packages = ['irrelevant', 'names'] |
2239 | - mock_call.side_effect = OSError('fail') |
2240 | - |
2241 | - self.assertRaises(OSError, fetch.apt_mark, packages, sentinel.mark, |
2242 | - fatal=True) |
2243 | - self.assertTrue(log.called) |
2244 | - |
2245 | - @patch.object(osplatform, 'get_platform') |
2246 | - @patch('subprocess.call') |
2247 | - @patch('charmhelpers.fetch.ubuntu.log') |
2248 | - def test_mark_apt_packages_as_string_nofatal(self, log, mock_call, |
2249 | - platform): |
2250 | - platform.return_value = 'ubuntu' |
2251 | - imp.reload(fetch) |
2252 | - |
2253 | - packages = 'foo bar' |
2254 | - |
2255 | - fetch.apt_mark(packages, sentinel.mark) |
2256 | - |
2257 | - self.assertTrue(log.called) |
2258 | - mock_call.assert_called_with( |
2259 | - ['apt-mark', sentinel.mark, 'foo bar'], |
2260 | - universal_newlines=True) |
2261 | - |
2262 | - @patch.object(osplatform, 'get_platform') |
2263 | - @patch('subprocess.call') |
2264 | - @patch('charmhelpers.fetch.ubuntu.log') |
2265 | - def test_mark_apt_packages_nofatal(self, log, mock_call, |
2266 | - platform): |
2267 | - platform.return_value = 'ubuntu' |
2268 | - imp.reload(fetch) |
2269 | - |
2270 | - packages = ['foo', 'bar'] |
2271 | - |
2272 | - fetch.apt_mark(packages, sentinel.mark) |
2273 | - |
2274 | - self.assertTrue(log.called) |
2275 | - mock_call.assert_called_with( |
2276 | - ['apt-mark', sentinel.mark, 'foo', 'bar'], |
2277 | - universal_newlines=True) |
2278 | - |
2279 | - @patch.object(osplatform, 'get_platform') |
2280 | - @patch('subprocess.check_call') |
2281 | - @patch('charmhelpers.fetch.ubuntu.log') |
2282 | - def test_mark_apt_packages_nofatal_abortonfatal(self, log, mock_call, |
2283 | - platform): |
2284 | - platform.return_value = 'ubuntu' |
2285 | - imp.reload(fetch) |
2286 | - |
2287 | - packages = ['foo', 'bar'] |
2288 | - |
2289 | - fetch.apt_mark(packages, sentinel.mark, fatal=True) |
2290 | - |
2291 | - self.assertTrue(log.called) |
2292 | - mock_call.assert_called_with( |
2293 | - ['apt-mark', sentinel.mark, 'foo', 'bar'], |
2294 | - universal_newlines=True) |
2295 | - |
2296 | - @patch.object(osplatform, 'get_platform') |
2297 | - @patch('charmhelpers.fetch.ubuntu.apt_mark') |
2298 | - def test_apt_hold(self, apt_mark, platform): |
2299 | - platform.return_value = 'ubuntu' |
2300 | - imp.reload(fetch) |
2301 | - |
2302 | - fetch.apt_hold(sentinel.packages) |
2303 | - apt_mark.assert_called_once_with(sentinel.packages, 'hold', |
2304 | - fatal=False) |
2305 | - |
2306 | - @patch.object(osplatform, 'get_platform') |
2307 | - @patch('charmhelpers.fetch.ubuntu.apt_mark') |
2308 | - def test_apt_hold_fatal(self, apt_mark, platform): |
2309 | - platform.return_value = 'ubuntu' |
2310 | - imp.reload(fetch) |
2311 | - |
2312 | - fetch.apt_hold(sentinel.packages, fatal=sentinel.fatal) |
2313 | - apt_mark.assert_called_once_with(sentinel.packages, 'hold', |
2314 | - fatal=sentinel.fatal) |
2315 | - |
2316 | - @patch.object(osplatform, 'get_platform') |
2317 | - @patch('charmhelpers.fetch.ubuntu.apt_mark') |
2318 | - def test_apt_unhold(self, apt_mark, platform): |
2319 | - platform.return_value = 'ubuntu' |
2320 | - imp.reload(fetch) |
2321 | - |
2322 | - fetch.apt_unhold(sentinel.packages) |
2323 | - apt_mark.assert_called_once_with(sentinel.packages, 'unhold', |
2324 | - fatal=False) |
2325 | - |
2326 | - @patch.object(osplatform, 'get_platform') |
2327 | - @patch('charmhelpers.fetch.ubuntu.apt_mark') |
2328 | - def test_apt_unhold_fatal(self, apt_mark, platform): |
2329 | - platform.return_value = 'ubuntu' |
2330 | - imp.reload(fetch) |
2331 | - |
2332 | - fetch.apt_unhold(sentinel.packages, fatal=sentinel.fatal) |
2333 | - apt_mark.assert_called_once_with(sentinel.packages, 'unhold', |
2334 | - fatal=sentinel.fatal) |
2335 | - |
2336 | - @patch.object(osplatform, 'get_platform') |
2337 | - @patch('subprocess.check_call') |
2338 | - def test_apt_update_fatal(self, check_call, platform): |
2339 | - platform.return_value = 'ubuntu' |
2340 | - imp.reload(fetch) |
2341 | - |
2342 | - fetch.apt_update(fatal=True) |
2343 | - check_call.assert_called_with( |
2344 | - ['apt-get', 'update'], |
2345 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2346 | - |
2347 | - @patch.object(osplatform, 'get_platform') |
2348 | - @patch('subprocess.call') |
2349 | - def test_apt_update_nonfatal(self, call, platform): |
2350 | - platform.return_value = 'ubuntu' |
2351 | - imp.reload(fetch) |
2352 | - |
2353 | - fetch.apt_update() |
2354 | - call.assert_called_with( |
2355 | - ['apt-get', 'update'], |
2356 | - env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
2357 | - |
2358 | - @patch.object(osplatform, 'get_platform') |
2359 | - @patch('subprocess.check_call') |
2360 | - @patch('time.sleep') |
2361 | - def test_run_apt_command_retries_if_fatal(self, check_call, |
2362 | - sleep, platform): |
2363 | - """The _run_apt_command function retries the command if it can't get |
2364 | - the APT lock.""" |
2365 | - platform.return_value = 'ubuntu' |
2366 | - imp.reload(fetch) |
2367 | - |
2368 | - self.called = False |
2369 | - |
2370 | - def side_effect(*args, **kwargs): |
2371 | - """ |
2372 | - First, raise an exception (can't acquire lock), then return 0 |
2373 | - (the lock is grabbed). |
2374 | - """ |
2375 | - if not self.called: |
2376 | - self.called = True |
2377 | - raise subprocess.CalledProcessError( |
2378 | - returncode=100, cmd="some command") |
2379 | - else: |
2380 | - return 0 |
2381 | - |
2382 | - check_call.side_effect = side_effect |
2383 | - check_call.return_value = 0 |
2384 | - |
2385 | - from charmhelpers.fetch.ubuntu import _run_apt_command |
2386 | - _run_apt_command(["some", "command"], fatal=True) |
2387 | - self.assertTrue(sleep.called) |
2388 | - |
2389 | - |
2390 | -class YumTests(TestCase): |
2391 | - |
2392 | - @patch.object(osplatform, 'get_platform') |
2393 | - @patch('subprocess.call') |
2394 | - @patch('charmhelpers.fetch.centos.log') |
2395 | - def test_yum_upgrade_non_fatal(self, log, mock_call, platform): |
2396 | - platform.return_value = 'centos' |
2397 | - imp.reload(fetch) |
2398 | - |
2399 | - options = ['--foo', '--bar'] |
2400 | - fetch.upgrade(options) |
2401 | - |
2402 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2403 | - '--foo', '--bar', 'upgrade'], |
2404 | - env=getenv()) |
2405 | - |
2406 | - @patch.object(osplatform, 'get_platform') |
2407 | - @patch('subprocess.call') |
2408 | - @patch('charmhelpers.fetch.centos.log') |
2409 | - def test_yum_upgrade_fatal(self, log, mock_call, platform): |
2410 | - platform.return_value = 'centos' |
2411 | - imp.reload(fetch) |
2412 | - |
2413 | - options = ['--foo', '--bar'] |
2414 | - fetch.upgrade(options, fatal=True) |
2415 | - |
2416 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2417 | - '--foo', '--bar', 'upgrade'], |
2418 | - env=getenv()) |
2419 | - |
2420 | - @patch.object(osplatform, 'get_platform') |
2421 | - @patch('subprocess.call') |
2422 | - @patch('charmhelpers.fetch.centos.log') |
2423 | - def test_installs_yum_packages(self, log, mock_call, platform): |
2424 | - platform.return_value = 'centos' |
2425 | - imp.reload(fetch) |
2426 | - |
2427 | - packages = ['foo', 'bar'] |
2428 | - options = ['--foo', '--bar'] |
2429 | - |
2430 | - fetch.install(packages, options) |
2431 | - |
2432 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2433 | - '--foo', '--bar', 'install', |
2434 | - 'foo', 'bar'], |
2435 | - env=getenv()) |
2436 | - |
2437 | - @patch.object(osplatform, 'get_platform') |
2438 | - @patch('subprocess.call') |
2439 | - @patch('charmhelpers.fetch.centos.log') |
2440 | - def test_installs_yum_packages_without_options(self, log, mock_call, |
2441 | - platform): |
2442 | - platform.return_value = 'centos' |
2443 | - imp.reload(fetch) |
2444 | - |
2445 | - packages = ['foo', 'bar'] |
2446 | - fetch.install(packages) |
2447 | - |
2448 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2449 | - 'install', 'foo', 'bar'], |
2450 | - env=getenv()) |
2451 | - |
2452 | - @patch.object(osplatform, 'get_platform') |
2453 | - @patch('subprocess.call') |
2454 | - @patch('charmhelpers.fetch.centos.log') |
2455 | - def test_installs_yum_packages_as_string(self, log, mock_call, |
2456 | - platform): |
2457 | - platform.return_value = 'centos' |
2458 | - imp.reload(fetch) |
2459 | - |
2460 | - packages = 'foo bar' |
2461 | - fetch.install(packages) |
2462 | - |
2463 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2464 | - 'install', 'foo bar'], |
2465 | - env=getenv()) |
2466 | - |
2467 | - @patch.object(osplatform, 'get_platform') |
2468 | - @patch('subprocess.call') |
2469 | - @patch('charmhelpers.fetch.centos.log') |
2470 | - def test_installs_yum_packages_with_possible_errors(self, log, mock_call, |
2471 | - platform): |
2472 | - platform.return_value = 'centos' |
2473 | - imp.reload(fetch) |
2474 | - |
2475 | - packages = ['foo', 'bar'] |
2476 | - options = ['--foo', '--bar'] |
2477 | - |
2478 | - fetch.install(packages, options, fatal=True) |
2479 | - |
2480 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2481 | - '--foo', '--bar', |
2482 | - 'install', 'foo', 'bar'], |
2483 | - env=getenv()) |
2484 | - |
2485 | - @patch.object(osplatform, 'get_platform') |
2486 | - @patch('subprocess.check_call') |
2487 | - @patch('charmhelpers.fetch.centos.log') |
2488 | - def test_purges_yum_packages_as_string_fatal(self, log, mock_call, |
2489 | - platform): |
2490 | - platform.return_value = 'centos' |
2491 | - imp.reload(fetch) |
2492 | - |
2493 | - packages = 'irrelevant names' |
2494 | - mock_call.side_effect = OSError('fail') |
2495 | - |
2496 | - self.assertRaises(OSError, fetch.purge, packages, fatal=True) |
2497 | - self.assertTrue(log.called) |
2498 | - |
2499 | - @patch.object(osplatform, 'get_platform') |
2500 | - @patch('subprocess.check_call') |
2501 | - @patch('charmhelpers.fetch.centos.log') |
2502 | - def test_purges_yum_packages_fatal(self, log, mock_call, platform): |
2503 | - platform.return_value = 'centos' |
2504 | - imp.reload(fetch) |
2505 | - |
2506 | - packages = ['irrelevant', 'names'] |
2507 | - mock_call.side_effect = OSError('fail') |
2508 | - |
2509 | - self.assertRaises(OSError, fetch.purge, packages, fatal=True) |
2510 | - self.assertTrue(log.called) |
2511 | - |
2512 | - @patch.object(osplatform, 'get_platform') |
2513 | - @patch('subprocess.call') |
2514 | - @patch('charmhelpers.fetch.centos.log') |
2515 | - def test_purges_yum_packages_as_string_nofatal(self, log, mock_call, |
2516 | - platform): |
2517 | - platform.return_value = 'centos' |
2518 | - imp.reload(fetch) |
2519 | - |
2520 | - packages = 'foo bar' |
2521 | - fetch.purge(packages) |
2522 | - |
2523 | - self.assertTrue(log.called) |
2524 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2525 | - 'remove', 'foo bar'], |
2526 | - env=getenv()) |
2527 | - |
2528 | - @patch.object(osplatform, 'get_platform') |
2529 | - @patch('subprocess.call') |
2530 | - @patch('charmhelpers.fetch.centos.log') |
2531 | - def test_purges_yum_packages_nofatal(self, log, mock_call, |
2532 | - platform): |
2533 | - platform.return_value = 'centos' |
2534 | - imp.reload(fetch) |
2535 | - |
2536 | - packages = ['foo', 'bar'] |
2537 | - fetch.purge(packages) |
2538 | - |
2539 | - self.assertTrue(log.called) |
2540 | - mock_call.assert_called_with(['yum', '--assumeyes', |
2541 | - 'remove', 'foo', 'bar'], |
2542 | - env=getenv()) |
2543 | - |
2544 | - @patch.object(osplatform, 'get_platform') |
2545 | - @patch('subprocess.check_call') |
2546 | - @patch('charmhelpers.fetch.centos.log') |
2547 | - def test_yum_update_fatal(self, log, check_call, platform): |
2548 | - platform.return_value = 'centos' |
2549 | - imp.reload(fetch) |
2550 | - |
2551 | - fetch.update(fatal=True) |
2552 | - check_call.assert_called_with(['yum', '--assumeyes', 'update'], |
2553 | - env=getenv()) |
2554 | - self.assertTrue(log.called) |
2555 | - |
2556 | - @patch.object(osplatform, 'get_platform') |
2557 | - @patch('subprocess.check_output') |
2558 | - @patch('charmhelpers.fetch.centos.log') |
2559 | - def test_yum_search(self, log, check_output, platform): |
2560 | - platform.return_value = 'centos' |
2561 | - imp.reload(fetch) |
2562 | - |
2563 | - package = ['irrelevant'] |
2564 | - |
2565 | - from charmhelpers.fetch.centos import yum_search |
2566 | - yum_search(package) |
2567 | - check_output.assert_called_with(['yum', 'search', 'irrelevant']) |
2568 | - self.assertTrue(log.called) |
2569 | - |
2570 | - @patch.object(osplatform, 'get_platform') |
2571 | - @patch('subprocess.check_call') |
2572 | - @patch('time.sleep') |
2573 | - def test_run_yum_command_retries_if_fatal(self, check_call, |
2574 | - sleep, platform): |
2575 | - """The _run_yum_command function retries the command if it can't get |
2576 | - the YUM lock.""" |
2577 | - platform.return_value = 'centos' |
2578 | - imp.reload(fetch) |
2579 | - |
2580 | - self.called = False |
2581 | - |
2582 | - def side_effect(*args, **kwargs): |
2583 | - """ |
2584 | - First, raise an exception (can't acquire lock), then return 0 |
2585 | - (the lock is grabbed). |
2586 | - """ |
2587 | - if not self.called: |
2588 | - self.called = True |
2589 | - raise subprocess.CalledProcessError( |
2590 | - returncode=1, cmd="some command") |
2591 | - else: |
2592 | - return 0 |
2593 | - |
2594 | - check_call.side_effect = side_effect |
2595 | - check_call.return_value = 0 |
2596 | - from charmhelpers.fetch.centos import _run_yum_command |
2597 | - _run_yum_command(["some", "command"], fatal=True) |
2598 | - self.assertTrue(sleep.called) |
2599 | - |
2600 | - @patch.object(osplatform, 'get_platform') |
2601 | - @patch('apt_pkg.Cache') |
2602 | - def test_get_upstream_version(self, cache, platform): |
2603 | - platform.return_value = 'ubuntu' |
2604 | - imp.reload(fetch) |
2605 | - cache.side_effect = fake_apt_cache |
2606 | - self.assertEqual(fetch.get_upstream_version('vim'), '7.3.547') |
2607 | - self.assertEqual(fetch.get_upstream_version('emacs'), None) |
2608 | - self.assertEqual(fetch.get_upstream_version('unknown'), None) |
2609 | |
2610 | === added file 'tests/fetch/test_fetch_centos.py' |
2611 | --- tests/fetch/test_fetch_centos.py 1970-01-01 00:00:00 +0000 |
2612 | +++ tests/fetch/test_fetch_centos.py 2017-04-10 18:12:16 +0000 |
2613 | @@ -0,0 +1,315 @@ |
2614 | +import subprocess |
2615 | +import os |
2616 | + |
2617 | +from tests.helpers import patch_open |
2618 | +from testtools import TestCase |
2619 | +from mock import ( |
2620 | + patch, |
2621 | + MagicMock, |
2622 | + call, |
2623 | +) |
2624 | +from charmhelpers.fetch import centos as fetch |
2625 | + |
2626 | + |
2627 | +def getenv(update=None): |
2628 | + # return a copy of os.environ with update applied. |
2629 | + # this was necessary because some modules modify os.environment directly |
2630 | + copy = os.environ.copy() |
2631 | + if update is not None: |
2632 | + copy.update(update) |
2633 | + return copy |
2634 | + |
2635 | + |
2636 | +class FetchTest(TestCase): |
2637 | + |
2638 | + @patch("charmhelpers.fetch.log") |
2639 | + @patch('yum.YumBase.doPackageLists') |
2640 | + def test_filter_packages_missing_centos(self, yumBase, log): |
2641 | + |
2642 | + class MockPackage: |
2643 | + def __init__(self, name): |
2644 | + self.base_package_name = name |
2645 | + |
2646 | + yum_dict = { |
2647 | + 'installed': { |
2648 | + MockPackage('vim') |
2649 | + }, |
2650 | + 'available': { |
2651 | + MockPackage('vim') |
2652 | + } |
2653 | + } |
2654 | + import yum |
2655 | + yum.YumBase.return_value.doPackageLists.return_value = yum_dict |
2656 | + result = fetch.filter_installed_packages(['vim', 'emacs']) |
2657 | + self.assertEquals(result, ['emacs']) |
2658 | + |
2659 | + @patch("charmhelpers.fetch.log") |
2660 | + def test_filter_packages_none_missing_centos(self, log): |
2661 | + |
2662 | + class MockPackage: |
2663 | + def __init__(self, name): |
2664 | + self.base_package_name = name |
2665 | + |
2666 | + yum_dict = { |
2667 | + 'installed': { |
2668 | + MockPackage('vim') |
2669 | + }, |
2670 | + 'available': { |
2671 | + MockPackage('vim') |
2672 | + } |
2673 | + } |
2674 | + import yum |
2675 | + yum.yumBase.return_value.doPackageLists.return_value = yum_dict |
2676 | + result = fetch.filter_installed_packages(['vim']) |
2677 | + self.assertEquals(result, []) |
2678 | + |
2679 | + @patch('charmhelpers.fetch.centos.log') |
2680 | + @patch('yum.YumBase.doPackageLists') |
2681 | + def test_filter_packages_not_available_centos(self, yumBase, log): |
2682 | + |
2683 | + class MockPackage: |
2684 | + def __init__(self, name): |
2685 | + self.base_package_name = name |
2686 | + |
2687 | + yum_dict = { |
2688 | + 'installed': { |
2689 | + MockPackage('vim') |
2690 | + } |
2691 | + } |
2692 | + import yum |
2693 | + yum.YumBase.return_value.doPackageLists.return_value = yum_dict |
2694 | + |
2695 | + result = fetch.filter_installed_packages(['vim', 'joe']) |
2696 | + self.assertEquals(result, ['joe']) |
2697 | + |
2698 | + @patch('charmhelpers.fetch.centos.log') |
2699 | + def test_add_source_none_centos(self, log): |
2700 | + fetch.add_source(source=None) |
2701 | + self.assertTrue(log.called) |
2702 | + |
2703 | + @patch('charmhelpers.fetch.centos.log') |
2704 | + @patch('os.listdir') |
2705 | + def test_add_source_http_centos(self, listdir, log): |
2706 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
2707 | + with patch_open() as (mock_open, mock_file): |
2708 | + fetch.add_source(source=source) |
2709 | + listdir.assert_called_with('/etc/yum.repos.d/') |
2710 | + mock_file.write.assert_has_calls([ |
2711 | + call("[archive.ubuntu.com_ubuntu raring-backports main]\n"), |
2712 | + call("name=archive.ubuntu.com/ubuntu raring-backports main\n"), |
2713 | + call("baseurl=http://archive.ubuntu.com/ubuntu raring" |
2714 | + "-backports main\n\n")]) |
2715 | + |
2716 | + @patch('charmhelpers.fetch.centos.log') |
2717 | + @patch('os.listdir') |
2718 | + @patch('subprocess.check_call') |
2719 | + def test_add_source_http_and_key_id_centos(self, check_call, |
2720 | + listdir, log): |
2721 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
2722 | + key_id = "akey" |
2723 | + with patch_open() as (mock_open, mock_file): |
2724 | + fetch.add_source(source=source, key=key_id) |
2725 | + listdir.assert_called_with('/etc/yum.repos.d/') |
2726 | + mock_file.write.assert_has_calls([ |
2727 | + call("[archive.ubuntu.com_ubuntu raring-backports main]\n"), |
2728 | + call("name=archive.ubuntu.com/ubuntu raring-backports main\n"), |
2729 | + call("baseurl=http://archive.ubuntu.com/ubuntu raring" |
2730 | + "-backports main\n\n")]) |
2731 | + check_call.assert_called_with(['rpm', '--import', key_id]) |
2732 | + |
2733 | + @patch('charmhelpers.fetch.centos.log') |
2734 | + @patch('os.listdir') |
2735 | + @patch('subprocess.check_call') |
2736 | + def test_add_source_https_and_key_id_centos(self, check_call, |
2737 | + listdir, log): |
2738 | + source = "https://USER:PASS@private-ppa.launchpad.net/project/awesome" |
2739 | + key_id = "GPGPGP" |
2740 | + with patch_open() as (mock_open, mock_file): |
2741 | + fetch.add_source(source=source, key=key_id) |
2742 | + listdir.assert_called_with('/etc/yum.repos.d/') |
2743 | + mock_file.write.assert_has_calls([ |
2744 | + call("[_USER:PASS@private-ppa.launchpad" |
2745 | + ".net_project_awesome]\n"), |
2746 | + call("name=/USER:PASS@private-ppa.launchpad.net" |
2747 | + "/project/awesome\n"), |
2748 | + call("baseurl=https://USER:PASS@private-ppa.launchpad.net" |
2749 | + "/project/awesome\n\n")]) |
2750 | + check_call.assert_called_with(['rpm', '--import', key_id]) |
2751 | + |
2752 | + @patch('charmhelpers.fetch.centos.log') |
2753 | + @patch.object(fetch, 'NamedTemporaryFile') |
2754 | + @patch('os.listdir') |
2755 | + @patch('subprocess.check_call') |
2756 | + def test_add_source_http_and_key_centos(self, check_call, |
2757 | + listdir, temp_file, log): |
2758 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
2759 | + key = ''' |
2760 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
2761 | + [...] |
2762 | + -----END PGP PUBLIC KEY BLOCK----- |
2763 | + ''' |
2764 | + file_mock = MagicMock() |
2765 | + file_mock.name = 'temporary_file' |
2766 | + temp_file.return_value.__enter__.return_value = file_mock |
2767 | + listdir.return_value = [] |
2768 | + |
2769 | + with patch_open() as (mock_open, mock_file): |
2770 | + fetch.add_source(source=source, key=key) |
2771 | + listdir.assert_called_with('/etc/yum.repos.d/') |
2772 | + self.assertTrue(log.called) |
2773 | + check_call.assert_called_with(['rpm', '--import', file_mock.name]) |
2774 | + file_mock.write.assert_called_once_with(key) |
2775 | + file_mock.flush.assert_called_once_with() |
2776 | + file_mock.seek.assert_called_once_with(0) |
2777 | + |
2778 | + |
2779 | +class YumTests(TestCase): |
2780 | + |
2781 | + @patch('subprocess.call') |
2782 | + @patch('charmhelpers.fetch.centos.log') |
2783 | + def test_yum_upgrade_non_fatal(self, log, mock_call): |
2784 | + options = ['--foo', '--bar'] |
2785 | + fetch.upgrade(options) |
2786 | + |
2787 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2788 | + '--foo', '--bar', 'upgrade'], |
2789 | + env=getenv()) |
2790 | + |
2791 | + @patch('subprocess.check_call') |
2792 | + @patch('charmhelpers.fetch.centos.log') |
2793 | + def test_yum_upgrade_fatal(self, log, mock_call): |
2794 | + options = ['--foo', '--bar'] |
2795 | + fetch.upgrade(options, fatal=True) |
2796 | + |
2797 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2798 | + '--foo', '--bar', 'upgrade'], |
2799 | + env=getenv()) |
2800 | + |
2801 | + @patch('subprocess.call') |
2802 | + @patch('charmhelpers.fetch.centos.log') |
2803 | + def test_installs_yum_packages(self, log, mock_call): |
2804 | + packages = ['foo', 'bar'] |
2805 | + options = ['--foo', '--bar'] |
2806 | + |
2807 | + fetch.install(packages, options) |
2808 | + |
2809 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2810 | + '--foo', '--bar', 'install', |
2811 | + 'foo', 'bar'], |
2812 | + env=getenv()) |
2813 | + |
2814 | + @patch('subprocess.call') |
2815 | + @patch('charmhelpers.fetch.centos.log') |
2816 | + def test_installs_yum_packages_without_options(self, log, mock_call): |
2817 | + packages = ['foo', 'bar'] |
2818 | + fetch.install(packages) |
2819 | + |
2820 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2821 | + 'install', 'foo', 'bar'], |
2822 | + env=getenv()) |
2823 | + |
2824 | + @patch('subprocess.call') |
2825 | + @patch('charmhelpers.fetch.centos.log') |
2826 | + def test_installs_yum_packages_as_string(self, log, mock_call): |
2827 | + packages = 'foo bar' |
2828 | + fetch.install(packages) |
2829 | + |
2830 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2831 | + 'install', 'foo bar'], |
2832 | + env=getenv()) |
2833 | + |
2834 | + @patch('subprocess.check_call') |
2835 | + @patch('charmhelpers.fetch.centos.log') |
2836 | + def test_installs_yum_packages_with_possible_errors(self, log, mock_call): |
2837 | + packages = ['foo', 'bar'] |
2838 | + options = ['--foo', '--bar'] |
2839 | + |
2840 | + fetch.install(packages, options, fatal=True) |
2841 | + |
2842 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2843 | + '--foo', '--bar', |
2844 | + 'install', 'foo', 'bar'], |
2845 | + env=getenv()) |
2846 | + |
2847 | + @patch('subprocess.check_call') |
2848 | + @patch('charmhelpers.fetch.centos.log') |
2849 | + def test_purges_yum_packages_as_string_fatal(self, log, mock_call): |
2850 | + packages = 'irrelevant names' |
2851 | + mock_call.side_effect = OSError('fail') |
2852 | + |
2853 | + self.assertRaises(OSError, fetch.purge, packages, fatal=True) |
2854 | + self.assertTrue(log.called) |
2855 | + |
2856 | + @patch('subprocess.check_call') |
2857 | + @patch('charmhelpers.fetch.centos.log') |
2858 | + def test_purges_yum_packages_fatal(self, log, mock_call): |
2859 | + packages = ['irrelevant', 'names'] |
2860 | + mock_call.side_effect = OSError('fail') |
2861 | + |
2862 | + self.assertRaises(OSError, fetch.purge, packages, fatal=True) |
2863 | + self.assertTrue(log.called) |
2864 | + |
2865 | + @patch('subprocess.call') |
2866 | + @patch('charmhelpers.fetch.centos.log') |
2867 | + def test_purges_yum_packages_as_string_nofatal(self, log, mock_call): |
2868 | + packages = 'foo bar' |
2869 | + fetch.purge(packages) |
2870 | + |
2871 | + self.assertTrue(log.called) |
2872 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2873 | + 'remove', 'foo bar'], |
2874 | + env=getenv()) |
2875 | + |
2876 | + @patch('subprocess.call') |
2877 | + @patch('charmhelpers.fetch.centos.log') |
2878 | + def test_purges_yum_packages_nofatal(self, log, mock_call): |
2879 | + packages = ['foo', 'bar'] |
2880 | + fetch.purge(packages) |
2881 | + |
2882 | + self.assertTrue(log.called) |
2883 | + mock_call.assert_called_with(['yum', '--assumeyes', |
2884 | + 'remove', 'foo', 'bar'], |
2885 | + env=getenv()) |
2886 | + |
2887 | + @patch('subprocess.check_call') |
2888 | + @patch('charmhelpers.fetch.centos.log') |
2889 | + def test_yum_update_fatal(self, log, check_call): |
2890 | + fetch.update(fatal=True) |
2891 | + check_call.assert_called_with(['yum', '--assumeyes', 'update'], |
2892 | + env=getenv()) |
2893 | + self.assertTrue(log.called) |
2894 | + |
2895 | + @patch('subprocess.check_output') |
2896 | + @patch('charmhelpers.fetch.centos.log') |
2897 | + def test_yum_search(self, log, check_output): |
2898 | + package = ['irrelevant'] |
2899 | + |
2900 | + from charmhelpers.fetch.centos import yum_search |
2901 | + yum_search(package) |
2902 | + check_output.assert_called_with(['yum', 'search', 'irrelevant']) |
2903 | + self.assertTrue(log.called) |
2904 | + |
2905 | + @patch('subprocess.check_call') |
2906 | + @patch('time.sleep') |
2907 | + def test_run_yum_command_retries_if_fatal(self, check_call, sleep): |
2908 | + """The _run_yum_command function retries the command if it can't get |
2909 | + the YUM lock.""" |
2910 | + self.called = False |
2911 | + |
2912 | + def side_effect(*args, **kwargs): |
2913 | + """ |
2914 | + First, raise an exception (can't acquire lock), then return 0 |
2915 | + (the lock is grabbed). |
2916 | + """ |
2917 | + if not self.called: |
2918 | + self.called = True |
2919 | + raise subprocess.CalledProcessError( |
2920 | + returncode=1, cmd="some command") |
2921 | + else: |
2922 | + return 0 |
2923 | + |
2924 | + check_call.side_effect = side_effect |
2925 | + check_call.return_value = 0 |
2926 | + from charmhelpers.fetch.centos import _run_yum_command |
2927 | + _run_yum_command(["some", "command"], fatal=True) |
2928 | + self.assertTrue(sleep.called) |
2929 | |
2930 | === added file 'tests/fetch/test_fetch_ubuntu.py' |
2931 | --- tests/fetch/test_fetch_ubuntu.py 1970-01-01 00:00:00 +0000 |
2932 | +++ tests/fetch/test_fetch_ubuntu.py 2017-04-10 18:12:16 +0000 |
2933 | @@ -0,0 +1,712 @@ |
2934 | +import six |
2935 | +import subprocess |
2936 | +import io |
2937 | +import os |
2938 | +import tempfile |
2939 | + |
2940 | +from tests.helpers import patch_open |
2941 | +from testtools import TestCase |
2942 | +from mock import ( |
2943 | + patch, |
2944 | + MagicMock, |
2945 | + call, |
2946 | + sentinel, |
2947 | + ANY, |
2948 | +) |
2949 | +from charmhelpers.fetch import ubuntu as fetch |
2950 | + |
2951 | +if six.PY3: |
2952 | + builtin_open = 'builtins.open' |
2953 | +else: |
2954 | + builtin_open = '__builtin__.open' |
2955 | + |
2956 | +# mocked return of openstack.lsb_release() |
2957 | +FAKE_RELEASE = { |
2958 | + 'DISTRIB_CODENAME': 'precise', |
2959 | + 'DISTRIB_RELEASE': '12.04', |
2960 | + 'DISTRIB_ID': 'Ubuntu', |
2961 | + 'DISTRIB_DESCRIPTION': '"Ubuntu 12.04"' |
2962 | +} |
2963 | + |
2964 | +url = 'deb ' + fetch.CLOUD_ARCHIVE_URL |
2965 | +UCA_SOURCES = [ |
2966 | + ('cloud:precise-folsom/proposed', url + ' precise-proposed/folsom main'), |
2967 | + ('cloud:precise-folsom', url + ' precise-updates/folsom main'), |
2968 | + ('cloud:precise-folsom/updates', url + ' precise-updates/folsom main'), |
2969 | + ('cloud:precise-grizzly/proposed', url + ' precise-proposed/grizzly main'), |
2970 | + ('cloud:precise-grizzly', url + ' precise-updates/grizzly main'), |
2971 | + ('cloud:precise-grizzly/updates', url + ' precise-updates/grizzly main'), |
2972 | + ('cloud:precise-havana/proposed', url + ' precise-proposed/havana main'), |
2973 | + ('cloud:precise-havana', url + ' precise-updates/havana main'), |
2974 | + ('cloud:precise-havana/updates', url + ' precise-updates/havana main'), |
2975 | + ('cloud:precise-icehouse/proposed', |
2976 | + url + ' precise-proposed/icehouse main'), |
2977 | + ('cloud:precise-icehouse', url + ' precise-updates/icehouse main'), |
2978 | + ('cloud:precise-icehouse/updates', url + ' precise-updates/icehouse main'), |
2979 | +] |
2980 | + |
2981 | +PGP_KEY_ASCII_ARMOR = """-----BEGIN PGP PUBLIC KEY BLOCK----- |
2982 | +Version: SKS 1.1.5 |
2983 | +Comment: Hostname: keyserver.ubuntu.com |
2984 | + |
2985 | +mI0EUCEyTAEEAMuUxyfiegCCwn4J/c0nw5PUTSJdn5FqiUTq6iMfij65xf1vl0g/Mxqw0gfg |
2986 | +AJIsCDvO9N9dloLAwF6FUBMg5My7WyhRPTAKF505TKJboyX3Pp4J1fU1LV8QFVOp87vUh1Rz |
2987 | +B6GU7cSglhnbL85gmbJTllkzkb3h4Yw7W+edjcQ/ABEBAAG0K0xhdW5jaHBhZCBQUEEgZm9y |
2988 | +IFVidW50dSBDbG91ZCBBcmNoaXZlIFRlYW2IuAQTAQIAIgUCUCEyTAIbAwYLCQgHAwIGFQgC |
2989 | +CQoLBBYCAwECHgECF4AACgkQimhEop9oEE7kJAP/eTBgq3Mhbvo0d8elMOuqZx3nmU7gSyPh |
2990 | +ep0zYIRZ5TJWl/7PRtvp0CJA6N6ZywYTQ/4ANHhpibcHZkh8K0AzUvsGXnJRSFoJeqyDbD91 |
2991 | +EhoO+4ZfHs2HvRBQEDZILMa2OyuB497E5Mmyua3HDEOrG2cVLllsUZzpTFCx8NgeMHk= |
2992 | +=jLBm |
2993 | +-----END PGP PUBLIC KEY BLOCK----- |
2994 | +""" |
2995 | + |
2996 | +FAKE_APT_CACHE = { |
2997 | + # an installed package |
2998 | + 'vim': { |
2999 | + 'current_ver': '2:7.3.547-6ubuntu5' |
3000 | + }, |
3001 | + # a uninstalled installation candidate |
3002 | + 'emacs': { |
3003 | + } |
3004 | +} |
3005 | + |
3006 | + |
3007 | +def fake_apt_cache(in_memory=True, progress=None): |
3008 | + def _get(package): |
3009 | + pkg = MagicMock() |
3010 | + if package not in FAKE_APT_CACHE: |
3011 | + raise KeyError |
3012 | + pkg.name = package |
3013 | + if 'current_ver' in FAKE_APT_CACHE[package]: |
3014 | + pkg.current_ver.ver_str = FAKE_APT_CACHE[package]['current_ver'] |
3015 | + else: |
3016 | + pkg.current_ver = None |
3017 | + return pkg |
3018 | + cache = MagicMock() |
3019 | + cache.__getitem__.side_effect = _get |
3020 | + return cache |
3021 | + |
3022 | + |
3023 | +def getenv(update=None): |
3024 | + # return a copy of os.environ with update applied. |
3025 | + # this was necessary because some modules modify os.environment directly |
3026 | + copy = os.environ.copy() |
3027 | + if update is not None: |
3028 | + copy.update(update) |
3029 | + return copy |
3030 | + |
3031 | + |
3032 | +class FetchTest(TestCase): |
3033 | + |
3034 | + @patch("charmhelpers.fetch.ubuntu.log") |
3035 | + @patch('apt_pkg.Cache') |
3036 | + def test_filter_packages_missing_ubuntu(self, cache, log): |
3037 | + cache.side_effect = fake_apt_cache |
3038 | + result = fetch.filter_installed_packages(['vim', 'emacs']) |
3039 | + self.assertEquals(result, ['emacs']) |
3040 | + |
3041 | + @patch("charmhelpers.fetch.ubuntu.log") |
3042 | + @patch('apt_pkg.Cache') |
3043 | + def test_filter_packages_none_missing_ubuntu(self, cache, log): |
3044 | + cache.side_effect = fake_apt_cache |
3045 | + result = fetch.filter_installed_packages(['vim']) |
3046 | + self.assertEquals(result, []) |
3047 | + |
3048 | + @patch('charmhelpers.fetch.ubuntu.log') |
3049 | + @patch('apt_pkg.Cache') |
3050 | + def test_filter_packages_not_available_ubuntu(self, cache, log): |
3051 | + cache.side_effect = fake_apt_cache |
3052 | + result = fetch.filter_installed_packages(['vim', 'joe']) |
3053 | + self.assertEquals(result, ['joe']) |
3054 | + log.assert_called_with('Package joe has no installation candidate.', |
3055 | + level='WARNING') |
3056 | + |
3057 | + @patch.object(fetch, 'log', lambda *args, **kwargs: None) |
3058 | + def test_import_apt_key_radix(self): |
3059 | + """Ensure shell out apt-key during key import""" |
3060 | + with patch('subprocess.check_call') as _subp: |
3061 | + fetch.import_key('foo') |
3062 | + cmd = ['apt-key', 'adv', '--keyserver', |
3063 | + 'hkp://keyserver.ubuntu.com:80', '--recv-keys', 'foo'] |
3064 | + _subp.assert_called_with(cmd) |
3065 | + |
3066 | + @patch.object(fetch, 'log', lambda *args, **kwargs: None) |
3067 | + def test_import_apt_key_ascii_armor(self): |
3068 | + with tempfile.NamedTemporaryFile() as tmp: |
3069 | + with patch.object(fetch, 'NamedTemporaryFile') as mock_tmpfile: |
3070 | + tmpfile = mock_tmpfile.return_value |
3071 | + tmpfile.__enter__.return_value = tmpfile |
3072 | + tmpfile.name = tmp.name |
3073 | + with patch('subprocess.check_call') as _subp: |
3074 | + fetch.import_key(PGP_KEY_ASCII_ARMOR) |
3075 | + cmd = ['apt-key', 'add', tmp.name] |
3076 | + _subp.assert_called_with(cmd) |
3077 | + with open(tmp.name, 'r') as f: |
3078 | + self.assertEqual(PGP_KEY_ASCII_ARMOR, f.read()) |
3079 | + |
3080 | + @patch.object(fetch, 'log', lambda *args, **kwargs: None) |
3081 | + def test_import_bad_apt_key(self): |
3082 | + """Ensure error when importing apt key fails""" |
3083 | + with patch('subprocess.check_call') as _subp: |
3084 | + cmd = ['apt-key', 'adv', '--keyserver', |
3085 | + 'hkp://keyserver.ubuntu.com:80', '--recv-keys', 'foo'] |
3086 | + _subp.side_effect = subprocess.CalledProcessError(1, cmd, '') |
3087 | + try: |
3088 | + fetch.import_key('foo') |
3089 | + assert False |
3090 | + except fetch.GPGKeyError as e: |
3091 | + self.assertEqual(str(e), "Error importing PGP key 'foo'") |
3092 | + |
3093 | + @patch('charmhelpers.fetch.ubuntu.log') |
3094 | + def test_add_source_none_ubuntu(self, log): |
3095 | + fetch.add_source(source=None) |
3096 | + self.assertTrue(log.called) |
3097 | + |
3098 | + @patch('subprocess.check_call') |
3099 | + def test_add_source_ppa(self, check_call): |
3100 | + source = "ppa:test-ppa" |
3101 | + fetch.add_source(source=source) |
3102 | + check_call.assert_called_with( |
3103 | + ['add-apt-repository', '--yes', source]) |
3104 | + |
3105 | + @patch("charmhelpers.fetch.ubuntu.log") |
3106 | + @patch('subprocess.check_call') |
3107 | + @patch('time.sleep') |
3108 | + def test_add_source_ppa_retries_30_times(self, sleep, check_call, log): |
3109 | + self.call_count = 0 |
3110 | + |
3111 | + def side_effect(*args, **kwargs): |
3112 | + """Raise an 3 times, then return 0 """ |
3113 | + self.call_count += 1 |
3114 | + if self.call_count <= fetch.CMD_RETRY_COUNT: |
3115 | + raise subprocess.CalledProcessError( |
3116 | + returncode=1, cmd="some add-apt-repository command") |
3117 | + else: |
3118 | + return 0 |
3119 | + check_call.side_effect = side_effect |
3120 | + |
3121 | + source = "ppa:test-ppa" |
3122 | + fetch.add_source(source=source) |
3123 | + check_call.assert_called_with( |
3124 | + ['add-apt-repository', '--yes', source]) |
3125 | + sleep.assert_called_with(10) |
3126 | + self.assertTrue(fetch.CMD_RETRY_COUNT, sleep.call_count) |
3127 | + |
3128 | + @patch('charmhelpers.fetch.ubuntu.log') |
3129 | + @patch('subprocess.check_call') |
3130 | + def test_add_source_http_ubuntu(self, check_call, log): |
3131 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
3132 | + fetch.add_source(source=source) |
3133 | + check_call.assert_called_with( |
3134 | + ['add-apt-repository', '--yes', source]) |
3135 | + |
3136 | + @patch('charmhelpers.fetch.ubuntu.log') |
3137 | + @patch('subprocess.check_call') |
3138 | + def test_add_source_https(self, check_call, log): |
3139 | + source = "https://example.com" |
3140 | + fetch.add_source(source=source) |
3141 | + check_call.assert_called_with( |
3142 | + ['add-apt-repository', '--yes', source]) |
3143 | + |
3144 | + @patch('charmhelpers.fetch.ubuntu.log') |
3145 | + @patch('subprocess.check_call') |
3146 | + def test_add_source_deb(self, check_call, log): |
3147 | + """add-apt-repository behaves differently when using the deb prefix. |
3148 | + |
3149 | + $ add-apt-repository --yes \ |
3150 | + "http://special.example.com/ubuntu precise-special main" |
3151 | + $ grep special /etc/apt/sources.list |
3152 | + deb http://special.example.com/ubuntu precise precise-special main |
3153 | + deb-src http://special.example.com/ubuntu precise precise-special main |
3154 | + |
3155 | + $ add-apt-repository --yes \ |
3156 | + "deb http://special.example.com/ubuntu precise-special main" |
3157 | + $ grep special /etc/apt/sources.list |
3158 | + deb http://special.example.com/ubuntu precise precise-special main |
3159 | + deb-src http://special.example.com/ubuntu precise precise-special main |
3160 | + deb http://special.example.com/ubuntu precise-special main |
3161 | + deb-src http://special.example.com/ubuntu precise-special main |
3162 | + """ |
3163 | + source = "deb http://archive.ubuntu.com/ubuntu raring-backports main" |
3164 | + fetch.add_source(source=source) |
3165 | + check_call.assert_called_with( |
3166 | + ['add-apt-repository', '--yes', source]) |
3167 | + |
3168 | + @patch('charmhelpers.fetch.ubuntu.log') |
3169 | + @patch('subprocess.check_call') |
3170 | + def test_add_source_http_and_key_id(self, check_call, log): |
3171 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
3172 | + key_id = "akey" |
3173 | + check_call.return_value = 0 # Successful exit code |
3174 | + fetch.add_source(source=source, key=key_id) |
3175 | + check_call.assert_has_calls([ |
3176 | + call(['add-apt-repository', '--yes', source]), |
3177 | + call(['apt-key', 'adv', '--keyserver', |
3178 | + 'hkp://keyserver.ubuntu.com:80', '--recv-keys', key_id]) |
3179 | + ]) |
3180 | + |
3181 | + @patch('charmhelpers.fetch.ubuntu.log') |
3182 | + @patch('subprocess.check_call') |
3183 | + def test_add_source_https_and_key_id(self, check_call, log): |
3184 | + source = "https://USER:PASS@private-ppa.launchpad.net/project/awesome" |
3185 | + key_id = "GPGPGP" |
3186 | + check_call.return_value = 0 # Success from both calls |
3187 | + fetch.add_source(source=source, key=key_id) |
3188 | + check_call.assert_has_calls([ |
3189 | + call(['add-apt-repository', '--yes', source]), |
3190 | + call(['apt-key', 'adv', '--keyserver', |
3191 | + 'hkp://keyserver.ubuntu.com:80', '--recv-keys', key_id]) |
3192 | + ]) |
3193 | + |
3194 | + @patch('charmhelpers.fetch.ubuntu.log') |
3195 | + @patch('subprocess.check_call') |
3196 | + def test_add_source_http_and_key(self, check_call, log): |
3197 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
3198 | + key = ''' |
3199 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
3200 | + [...] |
3201 | + -----END PGP PUBLIC KEY BLOCK----- |
3202 | + ''' |
3203 | + with patch('subprocess.check_call') as check_call: |
3204 | + check_call.return_value = 0 |
3205 | + fetch.add_source(source=source, key=key) |
3206 | + check_call.assert_any_call(['add-apt-repository', '--yes', source]) |
3207 | + check_call.assert_any_call(['apt-key', 'add', ANY]) |
3208 | + |
3209 | + def test_add_source_cloud_invalid_pocket(self): |
3210 | + source = "cloud:havana-updates" |
3211 | + self.assertRaises(fetch.SourceConfigError, |
3212 | + fetch.add_source, source) |
3213 | + |
3214 | + @patch('charmhelpers.fetch.ubuntu.log') |
3215 | + @patch.object(fetch, 'filter_installed_packages') |
3216 | + @patch.object(fetch, 'apt_install') |
3217 | + @patch.object(fetch, 'lsb_release') |
3218 | + def test_add_source_cloud_pocket_style(self, lsb_release, apt_install, |
3219 | + filter_pkg, log): |
3220 | + source = "cloud:precise-updates/havana" |
3221 | + lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'} |
3222 | + result = ('# Ubuntu Cloud Archive\n' |
3223 | + 'deb http://ubuntu-cloud.archive.canonical.com/ubuntu' |
3224 | + ' precise-updates/havana main\n') |
3225 | + |
3226 | + with patch_open() as (mock_open, mock_file): |
3227 | + fetch.add_source(source=source) |
3228 | + mock_file.write.assert_called_with(result) |
3229 | + filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
3230 | + |
3231 | + @patch('charmhelpers.fetch.ubuntu.log') |
3232 | + @patch.object(fetch, 'filter_installed_packages') |
3233 | + @patch.object(fetch, 'apt_install') |
3234 | + @patch.object(fetch, 'lsb_release') |
3235 | + def test_add_source_cloud_os_style(self, lsb_release, apt_install, |
3236 | + filter_pkg, log): |
3237 | + source = "cloud:precise-havana" |
3238 | + lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'} |
3239 | + result = ('# Ubuntu Cloud Archive\n' |
3240 | + 'deb http://ubuntu-cloud.archive.canonical.com/ubuntu' |
3241 | + ' precise-updates/havana main\n') |
3242 | + with patch_open() as (mock_open, mock_file): |
3243 | + fetch.add_source(source=source) |
3244 | + mock_file.write.assert_called_with(result) |
3245 | + filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
3246 | + |
3247 | + @patch('charmhelpers.fetch.ubuntu.log') |
3248 | + @patch.object(fetch, 'filter_installed_packages') |
3249 | + @patch.object(fetch, 'apt_install') |
3250 | + def test_add_source_cloud_distroless_style(self, apt_install, |
3251 | + filter_pkg, log): |
3252 | + source = "cloud:havana" |
3253 | + result = ('# Ubuntu Cloud Archive\n' |
3254 | + 'deb http://ubuntu-cloud.archive.canonical.com/ubuntu' |
3255 | + ' precise-updates/havana main\n') |
3256 | + with patch_open() as (mock_open, mock_file): |
3257 | + fetch.add_source(source=source) |
3258 | + mock_file.write.assert_called_with(result) |
3259 | + filter_pkg.assert_called_with(['ubuntu-cloud-keyring']) |
3260 | + |
3261 | + @patch('charmhelpers.fetch.ubuntu.log') |
3262 | + @patch.object(fetch, 'lsb_release') |
3263 | + @patch('platform.machine') |
3264 | + def test_add_source_proposed_x86_64(self, _machine, lsb_release, log): |
3265 | + source = "proposed" |
3266 | + result = ('# Proposed\n' |
3267 | + 'deb http://archive.ubuntu.com/ubuntu precise-proposed' |
3268 | + ' main universe multiverse restricted\n') |
3269 | + lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'} |
3270 | + _machine.return_value = 'x86_64' |
3271 | + with patch_open() as (mock_open, mock_file): |
3272 | + fetch.add_source(source=source) |
3273 | + mock_file.write.assert_called_with(result) |
3274 | + |
3275 | + @patch('charmhelpers.fetch.ubuntu.log') |
3276 | + @patch.object(fetch, 'lsb_release') |
3277 | + @patch('platform.machine') |
3278 | + def test_add_source_proposed_ppc64le(self, _machine, lsb_release, log): |
3279 | + source = "proposed" |
3280 | + result = ( |
3281 | + "# Proposed\n" |
3282 | + "deb http://ports.ubuntu.com/ubuntu-ports precise-proposed main " |
3283 | + "universe multiverse restricted\n") |
3284 | + lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'} |
3285 | + _machine.return_value = 'ppc64le' |
3286 | + with patch_open() as (mock_open, mock_file): |
3287 | + fetch.add_source(source=source) |
3288 | + mock_file.write.assert_called_with(result) |
3289 | + |
3290 | + @patch('charmhelpers.fetch.ubuntu.log') |
3291 | + @patch('subprocess.check_call') |
3292 | + def test_add_source_http_and_key_id_ubuntu(self, check_call, log): |
3293 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
3294 | + key_id = "akey" |
3295 | + fetch.add_source(source=source, key=key_id) |
3296 | + check_call.assert_any_call(['add-apt-repository', '--yes', source]), |
3297 | + check_call.assert_any_call([ |
3298 | + 'apt-key', 'adv', '--keyserver', |
3299 | + 'hkp://keyserver.ubuntu.com:80', '--recv-keys', key_id]) |
3300 | + |
3301 | + @patch('charmhelpers.fetch.ubuntu.log') |
3302 | + @patch('subprocess.check_call') |
3303 | + def test_add_source_https_and_key_id_ubuntu(self, check_call, log): |
3304 | + source = "https://USER:PASS@private-ppa.launchpad.net/project/awesome" |
3305 | + key_id = "GPGPGP" |
3306 | + fetch.add_source(source=source, key=key_id) |
3307 | + check_call.assert_any_call(['add-apt-repository', '--yes', source]), |
3308 | + check_call.assert_any_call([ |
3309 | + 'apt-key', 'adv', '--keyserver', |
3310 | + 'hkp://keyserver.ubuntu.com:80', '--recv-keys', key_id]) |
3311 | + |
3312 | + @patch('charmhelpers.fetch.ubuntu.log') |
3313 | + @patch('subprocess.check_call') |
3314 | + def test_add_source_http_and_key_ubuntu(self, check_call, log): |
3315 | + source = "http://archive.ubuntu.com/ubuntu raring-backports main" |
3316 | + key = ''' |
3317 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
3318 | + [...] |
3319 | + -----END PGP PUBLIC KEY BLOCK----- |
3320 | + ''' |
3321 | + fetch.add_source(source=source, key=key) |
3322 | + check_call.assert_any_call(['add-apt-repository', '--yes', source]) |
3323 | + check_call.assert_any_call(['apt-key', 'add', ANY]) |
3324 | + |
3325 | + @patch('charmhelpers.fetch.ubuntu.log') |
3326 | + def test_configure_bad_install_source(self, log): |
3327 | + try: |
3328 | + fetch.add_source('foo', fail_invalid=True) |
3329 | + self.fail("Calling add_source('foo') should fail") |
3330 | + except fetch.SourceConfigError as e: |
3331 | + self.assertEqual(str(e), "Unknown source: 'foo'") |
3332 | + |
3333 | + @patch('charmhelpers.fetch.ubuntu.lsb_release') |
3334 | + def test_configure_install_source_uca_staging(self, _lsb): |
3335 | + """Test configuring installation source from UCA staging sources""" |
3336 | + _lsb.return_value = FAKE_RELEASE |
3337 | + # staging pockets are configured as PPAs |
3338 | + with patch('subprocess.check_call') as _subp: |
3339 | + src = 'cloud:precise-folsom/staging' |
3340 | + fetch.add_source(src) |
3341 | + cmd = ['add-apt-repository', '-y', |
3342 | + 'ppa:ubuntu-cloud-archive/folsom-staging'] |
3343 | + _subp.assert_called_with(cmd) |
3344 | + |
3345 | + @patch(builtin_open) |
3346 | + @patch('charmhelpers.fetch.ubuntu.apt_install') |
3347 | + @patch('charmhelpers.fetch.ubuntu.lsb_release') |
3348 | + @patch('charmhelpers.fetch.ubuntu.filter_installed_packages') |
3349 | + def test_configure_install_source_uca_repos( |
3350 | + self, _fip, _lsb, _install, _open): |
3351 | + """Test configuring installation source from UCA sources""" |
3352 | + _lsb.return_value = FAKE_RELEASE |
3353 | + _file = MagicMock(spec=io.FileIO) |
3354 | + _open.return_value = _file |
3355 | + _fip.side_effect = lambda x: x |
3356 | + for src, url in UCA_SOURCES: |
3357 | + actual_url = "# Ubuntu Cloud Archive\n{}\n".format(url) |
3358 | + fetch.add_source(src) |
3359 | + _install.assert_called_with(['ubuntu-cloud-keyring'], |
3360 | + fatal=True) |
3361 | + _open.assert_called_with( |
3362 | + '/etc/apt/sources.list.d/cloud-archive.list', |
3363 | + 'w' |
3364 | + ) |
3365 | + _file.__enter__().write.assert_called_with(actual_url) |
3366 | + |
3367 | + def test_configure_install_source_bad_uca(self): |
3368 | + """Test configuring installation source from bad UCA source""" |
3369 | + try: |
3370 | + fetch.add_source('cloud:foo-bar', fail_invalid=True) |
3371 | + self.fail("add_source('cloud:foo-bar') should fail") |
3372 | + except fetch.SourceConfigError as e: |
3373 | + _e = ('Invalid Cloud Archive release specified: foo-bar' |
3374 | + ' on this Ubuntuversion') |
3375 | + self.assertTrue(str(e).startswith(_e)) |
3376 | + |
3377 | + @patch('charmhelpers.fetch.ubuntu.log') |
3378 | + def test_add_unparsable_source(self, log_): |
3379 | + source = "propsed" # Minor typo |
3380 | + fetch.add_source(source=source) |
3381 | + self.assertEqual(1, log_.call_count) |
3382 | + |
3383 | + @patch('charmhelpers.fetch.ubuntu.log') |
3384 | + def test_add_distro_source(self, log): |
3385 | + source = "distro" |
3386 | + # distro is a noop but test validate no exception is thrown |
3387 | + fetch.add_source(source=source) |
3388 | + |
3389 | + |
3390 | +class AptTests(TestCase): |
3391 | + |
3392 | + @patch('subprocess.call') |
3393 | + @patch('charmhelpers.fetch.ubuntu.log') |
3394 | + def test_apt_upgrade_non_fatal(self, log, mock_call): |
3395 | + options = ['--foo', '--bar'] |
3396 | + fetch.apt_upgrade(options) |
3397 | + |
3398 | + mock_call.assert_called_with( |
3399 | + ['apt-get', '--assume-yes', |
3400 | + '--foo', '--bar', 'upgrade'], |
3401 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3402 | + |
3403 | + @patch('subprocess.check_call') |
3404 | + @patch('charmhelpers.fetch.ubuntu.log') |
3405 | + def test_apt_upgrade_fatal(self, log, mock_call): |
3406 | + options = ['--foo', '--bar'] |
3407 | + fetch.apt_upgrade(options, fatal=True) |
3408 | + |
3409 | + mock_call.assert_called_with( |
3410 | + ['apt-get', '--assume-yes', |
3411 | + '--foo', '--bar', 'upgrade'], |
3412 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3413 | + |
3414 | + @patch('subprocess.check_call') |
3415 | + @patch('charmhelpers.fetch.ubuntu.log') |
3416 | + def test_apt_dist_upgrade_fatal(self, log, mock_call): |
3417 | + options = ['--foo', '--bar'] |
3418 | + fetch.apt_upgrade(options, fatal=True, dist=True) |
3419 | + |
3420 | + mock_call.assert_called_with( |
3421 | + ['apt-get', '--assume-yes', |
3422 | + '--foo', '--bar', 'dist-upgrade'], |
3423 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3424 | + |
3425 | + @patch('subprocess.call') |
3426 | + @patch('charmhelpers.fetch.ubuntu.log') |
3427 | + def test_installs_apt_packages(self, log, mock_call): |
3428 | + packages = ['foo', 'bar'] |
3429 | + options = ['--foo', '--bar'] |
3430 | + |
3431 | + fetch.apt_install(packages, options) |
3432 | + |
3433 | + mock_call.assert_called_with( |
3434 | + ['apt-get', '--assume-yes', |
3435 | + '--foo', '--bar', 'install', 'foo', 'bar'], |
3436 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3437 | + |
3438 | + @patch('subprocess.call') |
3439 | + @patch('charmhelpers.fetch.ubuntu.log') |
3440 | + def test_installs_apt_packages_without_options(self, log, mock_call): |
3441 | + packages = ['foo', 'bar'] |
3442 | + |
3443 | + fetch.apt_install(packages) |
3444 | + |
3445 | + mock_call.assert_called_with( |
3446 | + ['apt-get', '--assume-yes', |
3447 | + '--option=Dpkg::Options::=--force-confold', |
3448 | + 'install', 'foo', 'bar'], |
3449 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3450 | + |
3451 | + @patch('subprocess.call') |
3452 | + @patch('charmhelpers.fetch.ubuntu.log') |
3453 | + def test_installs_apt_packages_as_string(self, log, mock_call): |
3454 | + packages = 'foo bar' |
3455 | + options = ['--foo', '--bar'] |
3456 | + |
3457 | + fetch.apt_install(packages, options) |
3458 | + |
3459 | + mock_call.assert_called_with( |
3460 | + ['apt-get', '--assume-yes', |
3461 | + '--foo', '--bar', 'install', 'foo bar'], |
3462 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3463 | + |
3464 | + @patch('subprocess.check_call') |
3465 | + @patch('charmhelpers.fetch.ubuntu.log') |
3466 | + def test_installs_apt_packages_with_possible_errors(self, log, |
3467 | + check_call): |
3468 | + packages = ['foo', 'bar'] |
3469 | + options = ['--foo', '--bar'] |
3470 | + |
3471 | + fetch.apt_install(packages, options, fatal=True) |
3472 | + |
3473 | + check_call.assert_called_with( |
3474 | + ['apt-get', '--assume-yes', |
3475 | + '--foo', '--bar', 'install', 'foo', 'bar'], |
3476 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3477 | + |
3478 | + @patch('subprocess.check_call') |
3479 | + @patch('charmhelpers.fetch.ubuntu.log') |
3480 | + def test_purges_apt_packages_as_string_fatal(self, log, mock_call): |
3481 | + packages = 'irrelevant names' |
3482 | + mock_call.side_effect = OSError('fail') |
3483 | + |
3484 | + self.assertRaises(OSError, fetch.apt_purge, packages, fatal=True) |
3485 | + self.assertTrue(log.called) |
3486 | + |
3487 | + @patch('subprocess.check_call') |
3488 | + @patch('charmhelpers.fetch.ubuntu.log') |
3489 | + def test_purges_apt_packages_fatal(self, log, mock_call): |
3490 | + packages = ['irrelevant', 'names'] |
3491 | + mock_call.side_effect = OSError('fail') |
3492 | + |
3493 | + self.assertRaises(OSError, fetch.apt_purge, packages, fatal=True) |
3494 | + self.assertTrue(log.called) |
3495 | + |
3496 | + @patch('subprocess.call') |
3497 | + @patch('charmhelpers.fetch.ubuntu.log') |
3498 | + def test_purges_apt_packages_as_string_nofatal(self, log, mock_call): |
3499 | + packages = 'foo bar' |
3500 | + |
3501 | + fetch.apt_purge(packages) |
3502 | + |
3503 | + self.assertTrue(log.called) |
3504 | + mock_call.assert_called_with( |
3505 | + ['apt-get', '--assume-yes', 'purge', 'foo bar'], |
3506 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3507 | + |
3508 | + @patch('subprocess.call') |
3509 | + @patch('charmhelpers.fetch.ubuntu.log') |
3510 | + def test_purges_apt_packages_nofatal(self, log, mock_call): |
3511 | + packages = ['foo', 'bar'] |
3512 | + |
3513 | + fetch.apt_purge(packages) |
3514 | + |
3515 | + self.assertTrue(log.called) |
3516 | + mock_call.assert_called_with( |
3517 | + ['apt-get', '--assume-yes', 'purge', 'foo', 'bar'], |
3518 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3519 | + |
3520 | + @patch('subprocess.check_call') |
3521 | + @patch('charmhelpers.fetch.ubuntu.log') |
3522 | + def test_mark_apt_packages_as_string_fatal(self, log, mock_call): |
3523 | + packages = 'irrelevant names' |
3524 | + mock_call.side_effect = OSError('fail') |
3525 | + |
3526 | + self.assertRaises(OSError, fetch.apt_mark, packages, sentinel.mark, |
3527 | + fatal=True) |
3528 | + self.assertTrue(log.called) |
3529 | + |
3530 | + @patch('subprocess.check_call') |
3531 | + @patch('charmhelpers.fetch.ubuntu.log') |
3532 | + def test_mark_apt_packages_fatal(self, log, mock_call): |
3533 | + packages = ['irrelevant', 'names'] |
3534 | + mock_call.side_effect = OSError('fail') |
3535 | + |
3536 | + self.assertRaises(OSError, fetch.apt_mark, packages, sentinel.mark, |
3537 | + fatal=True) |
3538 | + self.assertTrue(log.called) |
3539 | + |
3540 | + @patch('subprocess.call') |
3541 | + @patch('charmhelpers.fetch.ubuntu.log') |
3542 | + def test_mark_apt_packages_as_string_nofatal(self, log, mock_call): |
3543 | + packages = 'foo bar' |
3544 | + |
3545 | + fetch.apt_mark(packages, sentinel.mark) |
3546 | + |
3547 | + self.assertTrue(log.called) |
3548 | + mock_call.assert_called_with( |
3549 | + ['apt-mark', sentinel.mark, 'foo bar'], |
3550 | + universal_newlines=True) |
3551 | + |
3552 | + @patch('subprocess.call') |
3553 | + @patch('charmhelpers.fetch.ubuntu.log') |
3554 | + def test_mark_apt_packages_nofatal(self, log, mock_call): |
3555 | + packages = ['foo', 'bar'] |
3556 | + |
3557 | + fetch.apt_mark(packages, sentinel.mark) |
3558 | + |
3559 | + self.assertTrue(log.called) |
3560 | + mock_call.assert_called_with( |
3561 | + ['apt-mark', sentinel.mark, 'foo', 'bar'], |
3562 | + universal_newlines=True) |
3563 | + |
3564 | + @patch('subprocess.check_call') |
3565 | + @patch('charmhelpers.fetch.ubuntu.log') |
3566 | + def test_mark_apt_packages_nofatal_abortonfatal(self, log, mock_call): |
3567 | + packages = ['foo', 'bar'] |
3568 | + |
3569 | + fetch.apt_mark(packages, sentinel.mark, fatal=True) |
3570 | + |
3571 | + self.assertTrue(log.called) |
3572 | + mock_call.assert_called_with( |
3573 | + ['apt-mark', sentinel.mark, 'foo', 'bar'], |
3574 | + universal_newlines=True) |
3575 | + |
3576 | + @patch('charmhelpers.fetch.ubuntu.apt_mark') |
3577 | + def test_apt_hold(self, apt_mark): |
3578 | + fetch.apt_hold(sentinel.packages) |
3579 | + apt_mark.assert_called_once_with(sentinel.packages, 'hold', |
3580 | + fatal=False) |
3581 | + |
3582 | + @patch('charmhelpers.fetch.ubuntu.apt_mark') |
3583 | + def test_apt_hold_fatal(self, apt_mark): |
3584 | + fetch.apt_hold(sentinel.packages, fatal=sentinel.fatal) |
3585 | + apt_mark.assert_called_once_with(sentinel.packages, 'hold', |
3586 | + fatal=sentinel.fatal) |
3587 | + |
3588 | + @patch('charmhelpers.fetch.ubuntu.apt_mark') |
3589 | + def test_apt_unhold(self, apt_mark): |
3590 | + fetch.apt_unhold(sentinel.packages) |
3591 | + apt_mark.assert_called_once_with(sentinel.packages, 'unhold', |
3592 | + fatal=False) |
3593 | + |
3594 | + @patch('charmhelpers.fetch.ubuntu.apt_mark') |
3595 | + def test_apt_unhold_fatal(self, apt_mark): |
3596 | + fetch.apt_unhold(sentinel.packages, fatal=sentinel.fatal) |
3597 | + apt_mark.assert_called_once_with(sentinel.packages, 'unhold', |
3598 | + fatal=sentinel.fatal) |
3599 | + |
3600 | + @patch('subprocess.check_call') |
3601 | + def test_apt_update_fatal(self, check_call): |
3602 | + fetch.apt_update(fatal=True) |
3603 | + check_call.assert_called_with( |
3604 | + ['apt-get', 'update'], |
3605 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3606 | + |
3607 | + @patch('subprocess.call') |
3608 | + def test_apt_update_nonfatal(self, call): |
3609 | + fetch.apt_update() |
3610 | + call.assert_called_with( |
3611 | + ['apt-get', 'update'], |
3612 | + env=getenv({'DEBIAN_FRONTEND': 'noninteractive'})) |
3613 | + |
3614 | + @patch('subprocess.check_call') |
3615 | + @patch('time.sleep') |
3616 | + def test_run_apt_command_retries_if_fatal(self, check_call, sleep): |
3617 | + """The _run_apt_command function retries the command if it can't get |
3618 | + the APT lock.""" |
3619 | + self.called = False |
3620 | + |
3621 | + def side_effect(*args, **kwargs): |
3622 | + """ |
3623 | + First, raise an exception (can't acquire lock), then return 0 |
3624 | + (the lock is grabbed). |
3625 | + """ |
3626 | + if not self.called: |
3627 | + self.called = True |
3628 | + raise subprocess.CalledProcessError( |
3629 | + returncode=100, cmd="some command") |
3630 | + else: |
3631 | + return 0 |
3632 | + |
3633 | + check_call.side_effect = side_effect |
3634 | + check_call.return_value = 0 |
3635 | + |
3636 | + from charmhelpers.fetch.ubuntu import _run_apt_command |
3637 | + _run_apt_command(["some", "command"], fatal=True) |
3638 | + self.assertTrue(sleep.called) |
3639 | + |
3640 | + @patch('apt_pkg.Cache') |
3641 | + def test_get_upstream_version(self, cache): |
3642 | + cache.side_effect = fake_apt_cache |
3643 | + self.assertEqual(fetch.get_upstream_version('vim'), '7.3.547') |
3644 | + self.assertEqual(fetch.get_upstream_version('emacs'), None) |
3645 | + self.assertEqual(fetch.get_upstream_version('unknown'), None) |
As you might expect, there are a number of (what seem to be small) merge conflicts with Trunk by now. But I merged this into a local copy of Trunk, took a stab at resolving those conflicts, and found the following issues:
1. Some of the added tests appear to assume that the tests will be executed from a Xenial machine, which will not always be the case. It might be best to base the tests on Xenial (LTS) regardless of the release of the developer's machine which is executing the unit tests.
2. There are a number of "local variable 'env' referenced before assignment" errors in the tests which cover _run_apt_command. This may have been missed with the rebase against that big centos change. This is causing unit and lint check failures. fetch/ubuntu. py:642: 57: F821 undefined name 'env'
| charmhelpers/
3. There has been a recent change in the apt retry logic which needs to be reconciled on a rebase. I didn't fully (or carefully) resolve those conflicts in my WIP as I needed to plow on for a separate test case.
http:// bazaar. launchpad. net/~charm- helpers/ charm-helpers/ devel/revision/ 705
This is causing: fetch/ubuntu. py:645: 34: F821 undefined name 'APT_NO_ LOCK_RETRY_ COUNT' fetch/ubuntu. py:649: 29: F821 undefined name 'APT_NO_ LOCK_RETRY_ DELAY' fetch/ubuntu. py:650: 28: F821 undefined name 'APT_NO_ LOCK_RETRY_ DELAY'
| charmhelpers/
| charmhelpers/
| charmhelpers/
See nosetests results: paste.ubuntu. com/24278293/
- http://
My temporary trunk merge branch is here (NOT intended to land or to be proposed as my merge conflict resolution approach was quite fast and furious): /code.launchpad .net/~1chb1n/ charm-helpers/ lp1611134
- https:/
I synced it into the nova-compute charm as a WIP TEST here (also NOT for reals): /github. com/ryan- beisner/ charm-nova- compute- wip
- https:/
And that is what I'm using on arm64 at the moment (!), because I need to consume arm64 ports. I'll chime back in here with how that goes. :-)