Merge lp:~james-page/charm-helpers/configure_source_redux into lp:charm-helpers

Proposed by James Page
Status: Merged
Merged at revision: 36
Proposed branch: lp:~james-page/charm-helpers/configure_source_redux
Merge into: lp:charm-helpers
Diff against target: 168 lines (+126/-9)
2 files modified
charmhelpers/fetch/__init__.py (+21/-9)
tests/fetch/test_fetch.py (+105/-0)
To merge this branch: bzr merge lp:~james-page/charm-helpers/configure_source_redux
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+171079@code.launchpad.net

Description of the change

Redux of configure_source code in fetch helper

1) Fixed up a number of bugs and issues in the original code

2) Implement correct cloud: handling

3) Added tests for everything.

To post a comment you must log in.
38. By James Page

Rebase against trunk

39. By James Page

Fixup failing test for cloud: type

40. By James Page

Tidy lint

Revision history for this message
Liam Young (gnuoy) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmhelpers/fetch/__init__.py'
--- charmhelpers/fetch/__init__.py 2013-05-19 18:17:25 +0000
+++ charmhelpers/fetch/__init__.py 2013-06-26 13:21:26 +0000
@@ -1,15 +1,27 @@
1from yaml import safe_load1from yaml import safe_load
2from core.hookenv import config_get2from charmhelpers.core.hookenv import config
3from subprocess import check_call3from charmhelpers.core.host import (
4 apt_install, apt_update, filter_installed_packages
5)
6import subprocess
7
8CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
9deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
10"""
411
512
6def add_source(source, key=None):13def add_source(source, key=None):
7 if ((source.startswith('ppa:') or14 if ((source.startswith('ppa:') or
8 source.startswith('cloud:') or
9 source.startswith('http:'))):15 source.startswith('http:'))):
10 check_call('add-apt-repository', source)16 subprocess.check_call(['add-apt-repository', source])
17 elif source.startswith('cloud:'):
18 apt_install(filter_installed_packages(['ubuntu-cloud-keyring']),
19 fatal=True)
20 pocket = source.split(':')[-1]
21 with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt:
22 apt.write(CLOUD_ARCHIVE.format(pocket))
11 if key:23 if key:
12 check_call('apt-key', 'import', key)24 subprocess.check_call(['apt-key', 'import', key])
1325
1426
15class SourceConfigError(Exception):27class SourceConfigError(Exception):
@@ -32,8 +44,8 @@
3244
33 Note that 'null' (a.k.a. None) should not be quoted.45 Note that 'null' (a.k.a. None) should not be quoted.
34 """46 """
35 sources = safe_load(config_get(sources_var))47 sources = safe_load(config(sources_var))
36 keys = safe_load(config_get(keys_var))48 keys = safe_load(config(keys_var))
37 if isinstance(sources, basestring) and isinstance(keys, basestring):49 if isinstance(sources, basestring) and isinstance(keys, basestring):
38 add_source(sources, keys)50 add_source(sources, keys)
39 else:51 else:
@@ -41,6 +53,6 @@
41 msg = 'Install sources and keys lists are different lengths'53 msg = 'Install sources and keys lists are different lengths'
42 raise SourceConfigError(msg)54 raise SourceConfigError(msg)
43 for src_num in range(len(sources)):55 for src_num in range(len(sources)):
44 add_source(sources[src_num], sources[src_num])56 add_source(sources[src_num], keys[src_num])
45 if update:57 if update:
46 check_call(('apt-get', 'update'))58 apt_update(fatal=True)
4759
=== added directory 'tests/fetch'
=== added file 'tests/fetch/__init__.py'
=== added file 'tests/fetch/test_fetch.py'
--- tests/fetch/test_fetch.py 1970-01-01 00:00:00 +0000
+++ tests/fetch/test_fetch.py 2013-06-26 13:21:26 +0000
@@ -0,0 +1,105 @@
1from contextlib import contextmanager
2from mock import patch, call, MagicMock
3from testtools import TestCase
4import yaml
5
6import charmhelpers.fetch as fetch
7
8
9@contextmanager
10def patch_open():
11 '''Patch open() to allow mocking both open() itself and the file that is
12 yielded.
13
14 Yields the mock for "open" and "file", respectively.'''
15 mock_open = MagicMock(spec=open)
16 mock_file = MagicMock(spec=file)
17
18 @contextmanager
19 def stub_open(*args, **kwargs):
20 mock_open(*args, **kwargs)
21 yield mock_file
22
23 with patch('__builtin__.open', stub_open):
24 yield mock_open, mock_file
25
26
27class FetchTest(TestCase):
28 @patch('subprocess.check_call')
29 def test_add_source_ppa(self, check_call):
30 source = "ppa:test-ppa"
31 fetch.add_source(source=source)
32 check_call.assert_called_with(['add-apt-repository',
33 source])
34
35 @patch('subprocess.check_call')
36 def test_add_source_http(self, check_call):
37 source = "http://archive.ubuntu.com/ubuntu raring-backports main"
38 fetch.add_source(source=source)
39 check_call.assert_called_with(['add-apt-repository',
40 source])
41
42 @patch.object(fetch, 'filter_installed_packages')
43 @patch.object(fetch, 'apt_install')
44 def test_add_source_cloud(self, apt_install, filter_pkg):
45 source = "cloud:havana-updates"
46 result = '''# Ubuntu Cloud Archive
47deb http://ubuntu-cloud.archive.canonical.com/ubuntu havana-updates main
48'''
49 with patch_open() as (mock_open, mock_file):
50 fetch.add_source(source=source)
51 mock_file.write.assert_called_with(result)
52 filter_pkg.assert_called_with(['ubuntu-cloud-keyring'])
53
54 @patch('subprocess.check_call')
55 def test_add_source_http_and_key(self, check_call):
56 source = "http://archive.ubuntu.com/ubuntu raring-backports main"
57 key = "akey"
58 fetch.add_source(source=source, key=key)
59 check_call.assert_has_calls([
60 call(['add-apt-repository', source]),
61 call(['apt-key', 'import', key])
62 ])
63
64 @patch.object(fetch, 'config')
65 @patch.object(fetch, 'add_source')
66 def test_configure_sources_single_source(self, add_source, config):
67 config.side_effect = ['source', 'key']
68 fetch.configure_sources()
69 add_source.assert_called_with('source', 'key')
70
71 @patch.object(fetch, 'config')
72 @patch.object(fetch, 'add_source')
73 def test_configure_sources_multiple_sources(self, add_source, config):
74 sources = ["sourcea", "sourceb"]
75 keys = ["keya", None]
76 config.side_effect = [
77 yaml.dump(sources),
78 yaml.dump(keys)
79 ]
80 fetch.configure_sources()
81 add_source.assert_has_calls([
82 call('sourcea', 'keya'),
83 call('sourceb', None)
84 ])
85
86 @patch.object(fetch, 'config')
87 @patch.object(fetch, 'add_source')
88 def test_configure_sources_missing_keys(self, add_source, config):
89 sources = ["sourcea", "sourceb"]
90 keys = ["keya"] # Second key is missing
91 config.side_effect = [
92 yaml.dump(sources),
93 yaml.dump(keys)
94 ]
95 self.assertRaises(fetch.SourceConfigError, fetch.configure_sources)
96
97 @patch.object(fetch, 'apt_update')
98 @patch.object(fetch, 'config')
99 @patch.object(fetch, 'add_source')
100 def test_configure_sources_apt_update_called(self, add_source, config,
101 apt_update):
102 config.side_effect = ['source', 'key']
103 fetch.configure_sources(update=True)
104 add_source.assert_called_with('source', 'key')
105 apt_update.assertCalled()

Subscribers

People subscribed via source and target branches