Merge lp:~sidnei/charms/precise/squid-forwardproxy/trunk into lp:charms/squid-forwardproxy

Proposed by Sidnei da Silva
Status: Work in progress
Proposed branch: lp:~sidnei/charms/precise/squid-forwardproxy/trunk
Merge into: lp:charms/squid-forwardproxy
Diff against target: 897 lines (+422/-231)
13 files modified
.bzrignore (+8/-1)
Makefile (+26/-0)
config-manager.txt (+6/-0)
hooks/hooks.py (+71/-49)
hooks/shelltoolbox/__init__.py (+2/-0)
hooks/tests/test_hooks.py (+184/-0)
hooks/tests/test_utils.py (+76/-0)
hooks/tests/utils_for_tests.py (+21/-0)
hooks/utils.py (+15/-178)
metadata.yaml (+1/-1)
setup.cfg (+4/-0)
tarmac_tests.sh (+6/-0)
templates/main_config.template (+2/-2)
To merge this branch: bzr merge lp:~sidnei/charms/precise/squid-forwardproxy/trunk
Reviewer Review Type Date Requested Status
Mark Mims (community) Needs Resubmitting
Review via email: mp+150930@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Mark Mims (mark-mims) wrote :

same as apache... please reserve /tests for charm tests

review: Needs Resubmitting
27. By Sidnei da Silva

- Move tests dir to hooks/tests

Unmerged revisions

27. By Sidnei da Silva

- Move tests dir to hooks/tests

26. By Diogo Baeder

[r=sidnei] Preparing to run charm proof

25. By Diogo Baeder

[r=sidnei] Increasing code coverage; more migration to charmsupport

24. By Diogo Baeder

[r=sidnei] Improving code coverage

23. By Diogo Baeder

[r=sidnei] Preparing for Continuous Integration

22. By Alexander List

[alexlist,r=hloeung] merge upstreamed charm: refactored docs, fixed .bzrignore, fixed maintainer, fixed location of nrpe plugin

21. By Haw Loeung

[hloeung,r=alexlist] Allow overriding default refresh pattern.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2013-02-05 06:25:28 +0000
3+++ .bzrignore 2013-03-18 12:32:24 +0000
4@@ -1,1 +1,8 @@
5-basenode/
6+revision
7+_trial_temp
8+.coverage
9+coverage.xml
10+*.crt
11+*.key
12+lib/*
13+*.pyc
14
15=== added file 'Makefile'
16--- Makefile 1970-01-01 00:00:00 +0000
17+++ Makefile 2013-03-18 12:32:24 +0000
18@@ -0,0 +1,26 @@
19+PWD := $(shell pwd)
20+HOOKS_DIR := $(PWD)/hooks
21+TEST_PREFIX := PYTHONPATH=$(HOOKS_DIR)
22+TEST_DIR := $(PWD)/hooks/tests
23+CHARM_DIR := $(PWD)
24+EXCLUDED_LINT_DIRS := $(PWD)/lib
25+PYTHON := /usr/bin/env python
26+
27+
28+build: sourcedeps test lint proof
29+
30+proof:
31+ @echo Proofing charm...
32+ @charm proof $(PWD) && echo OK
33+
34+test:
35+ @echo Starting tests...
36+ @CHARM_DIR=$(CHARM_DIR) $(TEST_PREFIX) nosetests $(TEST_DIR)
37+
38+lint:
39+ @echo Checking for Python syntax...
40+ @flake8 $(PWD) --ignore=E123 --exclude=$(EXCLUDED_LINT_DIRS) && echo OK
41+
42+sourcedeps: $(PWD)/config-manager.txt
43+ @echo Updating source dependencies...
44+ @$(PYTHON) /usr/lib/config-manager/cm.py update $(PWD)/config-manager.txt
45
46=== added file 'config-manager.txt'
47--- config-manager.txt 1970-01-01 00:00:00 +0000
48+++ config-manager.txt 2013-03-18 12:32:24 +0000
49@@ -0,0 +1,6 @@
50+# After making changes to this file, to ensure that your sourcedeps are
51+# up-to-date do:
52+#
53+# make sourcedeps
54+
55+./lib/charmsupport bzr+ssh://bazaar.launchpad.net/+branch/charmsupport;revno=33
56
57=== modified file 'hooks/hooks.py'
58--- hooks/hooks.py 2013-02-05 06:18:50 +0000
59+++ hooks/hooks.py 2013-03-18 12:32:24 +0000
60@@ -13,6 +13,25 @@
61 import pwd
62 import shutil
63 import glob
64+
65+# Make sure that charmsupport is importable, or bail out.
66+try:
67+ import charmsupport
68+ _ = charmsupport
69+except ImportError:
70+ local_copy = os.path.join(
71+ os.path.dirname(os.path.dirname(__file__)),
72+ "lib", "charmsupport")
73+ if not os.path.exists(local_copy) or not os.path.isdir(local_copy):
74+ sys.exit("Could not find required 'charmsupport' library.")
75+ sys.path.insert(0, local_copy)
76+
77+from charmsupport.hookenv import (
78+ log,
79+ related_units,
80+ relation_ids,
81+)
82+
83 import utils
84
85 ###############################################################################
86@@ -42,10 +61,9 @@
87 config_cmd_line.append('--format=json')
88 config_data = json.loads(subprocess.check_output(config_cmd_line))
89 except Exception, e:
90- utils.juju_log('INFO', str(e))
91- config_data = None
92- finally:
93- return(config_data)
94+ log(str(e), level='INFO')
95+ else:
96+ return config_data
97
98
99 #------------------------------------------------------------------------------
100@@ -69,10 +87,9 @@
101 relation_cmd_line.append(unit_name)
102 relation_data = subprocess.check_output(relation_cmd_line)
103 except Exception, e:
104- utils.juju_log('INFO', str(e))
105- relation_data = None
106- finally:
107- return(relation_data)
108+ log(str(e), level='INFO')
109+ else:
110+ return relation_data
111
112
113 #------------------------------------------------------------------------------
114@@ -86,30 +103,13 @@
115 #------------------------------------------------------------------------------
116 def relation_get(scope=None, unit_name=None, relation_id=None):
117 try:
118- relation_data = json.loads(relation_json())
119+ json_data = relation_json(unit_name=unit_name, scope=scope,
120+ relation_id=relation_id)
121+ relation_data = json.loads(json_data)
122 except Exception, e:
123- utils.juju_log('WARNING', str(e))
124- relation_data = None
125- finally:
126- return(relation_data)
127-
128-
129-#------------------------------------------------------------------------------
130-# relation_ids: Returns a list of relation ids
131-# optional parameters: relation_type
132-# relation_type: return relations only of this type
133-#------------------------------------------------------------------------------
134-def relation_ids(relation_type='cached-website'):
135- # accept strings or iterators
136- if isinstance(relation_type, basestring):
137- reltypes = [relation_type]
138+ log(str(e), level='WARNING')
139 else:
140- reltypes = relation_type
141- relids = []
142- for reltype in reltypes:
143- relid_cmd_line = ['relation-ids', '--format=json', reltype]
144- relids.extend(json.loads(subprocess.check_output(relid_cmd_line)))
145- return relids
146+ return relation_data
147
148
149 #------------------------------------------------------------------------------
150@@ -120,13 +120,11 @@
151 #------------------------------------------------------------------------------
152 def relation_get_all():
153 reldata = {}
154- relids = relation_ids()
155+ relids = relation_ids('cached-website')
156 for relid in relids:
157- units_cmd_line = ['relation-list', '--format=json', '-r', relid]
158- units = json.loads(subprocess.check_output(units_cmd_line))
159+ units = related_units(relid)
160 for unit in units:
161- reldata[unit] = json.loads(relation_json(relation_id=relid,
162- unit_name=unit))
163+ reldata[unit] = relation_get(relation_id=relid, unit_name=unit)
164 if 'sitenames' in reldata[unit]:
165 reldata[unit]['sitenames'] = reldata[unit]['sitenames'].split()
166 reldata[unit]['relation-id'] = relid
167@@ -142,9 +140,8 @@
168 #------------------------------------------------------------------------------
169 def load_squid3_config(squid3_config_file="/etc/squid3/squid.conf"):
170 if os.path.isfile(squid3_config_file):
171- return(open(squid3_config_file).read())
172- else:
173- return(None)
174+ with open(squid3_config_file) as f:
175+ return f.read()
176
177
178 #------------------------------------------------------------------------------
179@@ -226,14 +223,25 @@
180 refresh_patterns = yaml.load(config_data['refresh_patterns'])
181 else:
182 refresh_patterns = {}
183+ # Use default refresh pattern if specified.
184+ if '.' in refresh_patterns:
185+ default_refresh_pattern = refresh_patterns.pop('.')
186+ else:
187+ default_refresh_pattern = {
188+ 'min': 30,
189+ 'percent': 20,
190+ 'max': 4320,
191+ }
192+
193 if config_data['auth_list']:
194 auth_list = yaml.load(config_data['auth_list'])
195 else:
196 auth_list = {}
197
198 config_data['cache_l1'] = int(math.ceil(math.sqrt(
199- int(config_data['cache_size_mb']) * 1024 / (16 *
200- int(config_data['target_objs_per_dir']) * int(config_data['avg_obj_size_kb'])))))
201+ int(config_data['cache_size_mb']) * 1024 /
202+ (16 * int(config_data['target_objs_per_dir']) *
203+ int(config_data['avg_obj_size_kb'])))))
204 config_data['cache_l2'] = config_data['cache_l1'] * 16
205
206 with open(default_squid3_config, 'w') as squid3_config:
207@@ -241,6 +249,7 @@
208 'config': config_data,
209 'relations': relations,
210 'refresh_patterns': refresh_patterns,
211+ 'default_refresh_pattern': default_refresh_pattern,
212 'auth_list': auth_list,
213 }
214 squid3_config.write(utils.render_template('main_config.template',
215@@ -255,7 +264,8 @@
216 if action is None or squid3_config is None:
217 return(None)
218 elif action == "check":
219- retVal = subprocess.call(['/usr/sbin/squid3', '-f', squid3_config, '-k', 'parse'])
220+ retVal = subprocess.call(['/usr/sbin/squid3', '-f', squid3_config,
221+ '-k', 'parse'])
222 if retVal == 1:
223 return(False)
224 elif retVal == 0:
225@@ -285,35 +295,46 @@
226 utils.juju_log('FATAL', "Nagios user not setup, exiting")
227 return
228 utils.install_unattended('libwww-perl')
229- shutil.copy2('%s/files/nrpe-external-master/check_squid' % (os.environ['CHARM_DIR']), '/usr/local/lib/nagios/plugins/')
230+ shutil.copy2('%s/files/nrpe-external-master/check_squid' % (
231+ os.environ['CHARM_DIR']), '/usr/local/lib/nagios/plugins/')
232 unit_name = os.environ['JUJU_UNIT_NAME'].replace('/', '-')
233 nrpe_check_file = '/etc/nagios/nrpe.d/check_squidfp.cfg'
234- nagios_hostname = "%s-%s-%s" % (config_data['nagios_context'], config_data['nagios_service_type'], unit_name)
235+ nagios_hostname = "%s-%s-%s" % (config_data['nagios_context'],
236+ config_data['nagios_service_type'],
237+ unit_name)
238 nagios_logdir = '/var/log/nagios'
239 nagios_exportdir = '/var/lib/nagios/export'
240- nrpe_service_file = '/var/lib/nagios/export/service__%s_check_squidfp.cfg' % (nagios_hostname)
241+ nrpe_service_file = ('/var/lib/nagios/export/service__%s_check_squidfp.cfg'
242+ % (nagios_hostname))
243 if not os.path.exists(nagios_logdir):
244 os.mkdir(nagios_logdir)
245 os.chown(nagios_logdir, nagios_uid, nagios_gid)
246 if not os.path.exists(nagios_exportdir):
247- utils.juju_log('FATAL', 'Exiting as %s is not accessible' % (nagios_exportdir))
248+ utils.juju_log('FATAL', 'Exiting as %s is not accessible' %
249+ (nagios_exportdir))
250 return
251 for f in os.listdir(nagios_exportdir):
252 if re.search('.*check_squidfp.cfg', f):
253 os.remove(os.path.join(nagios_exportdir, f))
254 from jinja2 import Environment, FileSystemLoader
255 template_env = Environment(
256- loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'], 'templates')))
257+ loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'],
258+ 'templates')))
259 templ_vars = {
260 'nagios_hostname': nagios_hostname,
261 'nagios_servicegroup': config_data['nagios_context'],
262 }
263- template = template_env.get_template('nrpe_service.template').render(templ_vars)
264+ template = template_env.get_template('nrpe_service.template').render(
265+ templ_vars)
266 with open(nrpe_service_file, 'w') as nrpe_service_config:
267 nrpe_service_config.write(str(template))
268 with open(nrpe_check_file, 'w') as nrpe_check_config:
269 nrpe_check_config.write("# check squidfp\n")
270- nrpe_check_config.write("command[check_squidfp]=/usr/local/lib/nagios/plugins/check_squid %s - - 127.0.0.1 %s - - 200\n" % (config_data['nagios_check_url'], get_service_ports()[0]))
271+ nrpe_check_config.write("command[check_squidfp]="
272+ "/usr/local/lib/nagios/plugins/check_squid %s "
273+ "- - 127.0.0.1 %s - - 200\n" % (
274+ config_data['nagios_check_url'],
275+ get_service_ports()[0]))
276 if os.path.isfile('/etc/init.d/nagios-nrpe-server'):
277 subprocess.call(['service', 'nagios-nrpe-server', 'reload'])
278
279@@ -329,7 +350,8 @@
280 os.mkdir(default_squid3_config_dir, 0600)
281 if not os.path.exists(default_squid3_config_cache_dir):
282 os.mkdir(default_squid3_config_cache_dir, 0600)
283- shutil.copy2('%s/files/default.squid3' % (os.environ['CHARM_DIR']), '/etc/default/squid3')
284+ shutil.copy2('%s/files/default.squid3' % (os.environ['CHARM_DIR']),
285+ '/etc/default/squid3')
286 return (utils.install_unattended('squid3', 'python-jinja2'))
287
288
289
290=== modified file 'hooks/shelltoolbox/__init__.py'
291--- hooks/shelltoolbox/__init__.py 2012-10-03 11:59:29 +0000
292+++ hooks/shelltoolbox/__init__.py 2013-03-18 12:32:24 +0000
293@@ -603,10 +603,12 @@
294 def changed(self):
295 return set(key for key in self.intersect
296 if self.past_dict[key] != self.current_dict[key])
297+
298 @property
299 def unchanged(self):
300 return set(key for key in self.intersect
301 if self.past_dict[key] == self.current_dict[key])
302+
303 @property
304 def modified(self):
305 return self.current_dict != self.past_dict
306
307=== added directory 'hooks/tests'
308=== added file 'hooks/tests/__init__.py'
309=== added file 'hooks/tests/test_hooks.py'
310--- hooks/tests/test_hooks.py 1970-01-01 00:00:00 +0000
311+++ hooks/tests/test_hooks.py 2013-03-18 12:32:24 +0000
312@@ -0,0 +1,184 @@
313+import json
314+
315+from mock import patch, call
316+from testtools import TestCase
317+
318+import hooks
319+from utils_for_tests import patch_open
320+
321+
322+class HelpersTest(TestCase):
323+ @patch('subprocess.check_output')
324+ def test_gets_general_config(self, check_output):
325+ config = {'foo': 'bar'}
326+ check_output.return_value = json.dumps(config)
327+
328+ result = hooks.config_get()
329+
330+ self.assertEqual(result, config)
331+ check_output.assert_called_with(['config-get', '--format=json'])
332+
333+ @patch('subprocess.check_output')
334+ def test_gets_specific_config(self, check_output):
335+ config = {'foo': 'bar'}
336+ check_output.return_value = json.dumps(config)
337+
338+ result = hooks.config_get('foo')
339+
340+ self.assertEqual(result, config)
341+ check_output.assert_called_with(['config-get', 'foo', '--format=json'])
342+
343+ @patch('subprocess.check_output')
344+ @patch.object(hooks, 'log')
345+ def test_logs_config_get_errors(self, log, check_output):
346+ error = Exception('some error')
347+ check_output.side_effect = error
348+
349+ result = hooks.config_get()
350+
351+ self.assertIsNone(result)
352+ log.assert_called_with(str(error), level='INFO')
353+
354+ @patch('subprocess.check_output')
355+ def test_gets_relation_json(self, check_output):
356+ data = {'foo': 'bar'}
357+ check_output.return_value = json.dumps(data)
358+ unit = 'some-unit'
359+
360+ result = hooks.relation_json(unit_name=unit)
361+
362+ self.assertEqual(result, json.dumps(data))
363+ check_output.assert_called_with(['relation-get', '--format=json', '-',
364+ unit])
365+
366+ @patch('subprocess.check_output')
367+ def test_gets_relation_json_with_additional_data(self, check_output):
368+ data = {'foo': 'bar'}
369+ check_output.return_value = json.dumps(data)
370+ unit = 'some-unit'
371+ scope = 'some-scope'
372+ relation_id = 'some-relation_id'
373+
374+ result = hooks.relation_json(unit_name=unit, scope=scope,
375+ relation_id=relation_id)
376+
377+ self.assertEqual(result, json.dumps(data))
378+ check_output.assert_called_with(['relation-get', '--format=json', '-r',
379+ relation_id, scope, unit])
380+
381+ @patch('subprocess.check_output')
382+ @patch.object(hooks, 'log')
383+ def test_logs_relation_json_errors(self, log, check_output):
384+ error = Exception('some error')
385+ check_output.side_effect = error
386+
387+ result = hooks.relation_json(unit_name='some-unit')
388+
389+ self.assertIsNone(result)
390+ log.assert_called_with(str(error), level='INFO')
391+
392+ @patch.object(hooks, 'relation_json')
393+ def test_gets_relation(self, relation_json):
394+ data = {'foo': 'bar'}
395+ relation_json.return_value = json.dumps(data)
396+ unit = 'some-unit'
397+ scope = 'some-scope'
398+ relation_id = 'some-relation_id'
399+
400+ result = hooks.relation_get(unit_name=unit, scope=scope,
401+ relation_id=relation_id)
402+
403+ self.assertEqual(result, data)
404+ relation_json.assert_called_with(unit_name=unit, scope=scope,
405+ relation_id=relation_id)
406+
407+ @patch.object(hooks, 'relation_json')
408+ @patch.object(hooks, 'log')
409+ def test_logs_relation_get_errors(self, log, relation_json):
410+ error = Exception('some error')
411+ relation_json.side_effect = error
412+
413+ result = hooks.relation_get()
414+
415+ self.assertIsNone(result)
416+ log.assert_called_with(str(error), level='WARNING')
417+
418+ @patch.object(hooks, 'relation_ids')
419+ @patch.object(hooks, 'relation_get')
420+ @patch.object(hooks, 'related_units')
421+ def test_gets_all_relations(self, related_units, relation_get,
422+ relation_ids):
423+ ids = ['id1', 'id2']
424+ units = [
425+ ['foo/1', 'bar/1'],
426+ ['foo/2', 'bar/2'],
427+ ]
428+ relation_data = [
429+ {
430+ 'sitenames': 'foo bar',
431+ 'some-other-data': 'some-other-value',
432+ },
433+ {
434+ 'sitenames': 'FOO BAR',
435+ },
436+ {},
437+ {},
438+ ]
439+ relation_ids.return_value = ids
440+ related_units.side_effect = units
441+ relation_get.side_effect = relation_data
442+
443+ result = hooks.relation_get_all()
444+
445+ self.assertEqual(
446+ result, {
447+ 'foo/1': {'name': 'foo_1',
448+ 'relation-id': 'id1',
449+ 'sitenames': ['foo', 'bar'],
450+ 'some-other-data': 'some-other-value'},
451+ 'bar/1': {'name': 'bar_1',
452+ 'relation-id': 'id1',
453+ 'sitenames': ['FOO', 'BAR']},
454+ 'foo/2': {'name': 'foo_2',
455+ 'relation-id': 'id2'},
456+ 'bar/2': {'name': 'bar_2',
457+ 'relation-id': 'id2'},
458+ }
459+ )
460+
461+ relation_ids.assert_called_with('cached-website')
462+ self.assertEqual(related_units.mock_calls, [
463+ call('id1'),
464+ call('id2'),
465+ ])
466+ self.assertEqual(relation_get.mock_calls, [
467+ call(unit_name='foo/1', relation_id='id1'),
468+ call(unit_name='bar/1', relation_id='id1'),
469+ call(unit_name='foo/2', relation_id='id2'),
470+ call(unit_name='bar/2', relation_id='id2'),
471+ ])
472+
473+ @patch('os.path.isfile')
474+ def test_loads_squid3_config(self, isfile):
475+ isfile.return_value = True
476+ path = '/etc/squid3/squid.conf'
477+
478+ with patch_open() as (mock_open, mock_file):
479+ content = 'some content'
480+ mock_file.read.return_value = content
481+
482+ result = hooks.load_squid3_config()
483+
484+ self.assertEqual(result, content)
485+ isfile.assert_called_with(path)
486+ mock_open.assert_called_with(path)
487+
488+ @patch('os.path.isfile')
489+ def test_loads_none_if_squid3_config_unexistant(self, isfile):
490+ isfile.return_value = False
491+ path = '/etc/squid3/squid.conf'
492+
493+ result = hooks.load_squid3_config()
494+
495+ self.assertIsNone(result)
496+ isfile.assert_called_with(path)
497
498=== added file 'hooks/tests/test_utils.py'
499--- hooks/tests/test_utils.py 1970-01-01 00:00:00 +0000
500+++ hooks/tests/test_utils.py 2013-03-18 12:32:24 +0000
501@@ -0,0 +1,76 @@
502+from testtools import TestCase
503+from mock import patch, call
504+
505+import utils
506+
507+
508+class AptLockIsNoMore(Exception):
509+ pass
510+
511+
512+class HelpersTest(TestCase):
513+ @patch('subprocess.check_call')
514+ def test_installs_packages(self, check_call):
515+ check_call.side_effect = [AptLockIsNoMore(), None, None]
516+
517+ utils.install('foo', 'bar')
518+
519+ self.assertEqual(check_call.mock_calls, [
520+ call(['lsof', '/var/lib/dpkg/lock']),
521+ call(['apt-get', '-y', 'install', 'foo', 'bar']),
522+ ])
523+
524+ @patch('subprocess.check_call')
525+ def test_doesnt_install_package_while_apt_is_locked(self, check_call):
526+ check_call.side_effect = [None, None, AptLockIsNoMore(), None, None]
527+
528+ utils.install('foo', 'bar')
529+
530+ self.assertEqual(check_call.mock_calls, [
531+ call(['lsof', '/var/lib/dpkg/lock']),
532+ call(['lsof', '/var/lib/dpkg/lock']),
533+ call(['lsof', '/var/lib/dpkg/lock']),
534+ call(['apt-get', '-y', 'install', 'foo', 'bar']),
535+ ])
536+
537+ @patch.object(utils, 'jinja2')
538+ def test_renders_a_template(self, jinja2):
539+ templates = jinja2.Environment.return_value
540+ template = templates.get_template.return_value
541+
542+ utils.render_template('some template', 'some context')
543+
544+ jinja2.FileSystemLoader.assert_called_with(utils.TEMPLATES_DIR)
545+ jinja2.Environment.assert_called_with(
546+ loader=jinja2.FileSystemLoader.return_value)
547+ templates.get_template.assert_called_with('some template')
548+ template.render.assert_called_with('some context')
549+
550+ @patch.object(utils, 'jinja2')
551+ def test_renders_a_template_from_a_different_directory(self, jinja2):
552+ templates = jinja2.Environment.return_value
553+ template = templates.get_template.return_value
554+
555+ utils.render_template('some template', 'some context', 'some dir')
556+
557+ jinja2.FileSystemLoader.assert_called_with('some dir')
558+ jinja2.Environment.assert_called_with(
559+ loader=jinja2.FileSystemLoader.return_value)
560+ templates.get_template.assert_called_with('some template')
561+ template.render.assert_called_with('some context')
562+
563+ @patch('subprocess.check_call')
564+ def test_installs_unattended_packages(self, check_call):
565+ utils.install_unattended('foo', 'bar')
566+ check_call.assert_called_with(['env', 'DEBIAN_FRONTEND=noninteractive',
567+ 'apt-get', '-y',
568+ '-odpkg::options::=--force-confdef',
569+ '-odpkg::options::=--force-confold',
570+ 'install', 'foo', 'bar'])
571+
572+ @patch('subprocess.check_call')
573+ def test_logs_something_with_juju(self, check_call):
574+ utils.juju_log('not critical', 'some message')
575+
576+ check_call.assert_called_with(['juju-log', '--log-level',
577+ 'not critical', 'some message'])
578
579=== added file 'hooks/tests/utils_for_tests.py'
580--- hooks/tests/utils_for_tests.py 1970-01-01 00:00:00 +0000
581+++ hooks/tests/utils_for_tests.py 2013-03-18 12:32:24 +0000
582@@ -0,0 +1,21 @@
583+from contextlib import contextmanager
584+
585+from mock import patch, MagicMock
586+
587+
588+@contextmanager
589+def patch_open():
590+ '''Patch open() to allow mocking both open() itself and the file that is
591+ yielded.
592+
593+ Yields the mock for "open" and "file", respectively.'''
594+ mock_open = MagicMock(spec=open)
595+ mock_file = MagicMock(spec=file)
596+
597+ @contextmanager
598+ def stub_open(*args, **kwargs):
599+ mock_open(*args, **kwargs)
600+ yield mock_file
601+
602+ with patch('__builtin__.open', stub_open):
603+ yield mock_open, mock_file
604
605=== modified file 'hooks/utils.py'
606--- hooks/utils.py 2013-01-22 12:45:52 +0000
607+++ hooks/utils.py 2013-03-18 12:32:24 +0000
608@@ -7,55 +7,38 @@
609 # Paul Collins <paul.collins@canonical.com>
610 #
611
612-import os
613 import subprocess
614-import socket
615-import sys
616-
617-
618-def do_hooks(hooks):
619- hook = os.path.basename(sys.argv[0])
620-
621- try:
622- hooks[hook]()
623- except KeyError:
624- juju_log('INFO',
625- "This charm doesn't know how to handle '{}'.".format(hook))
626
627
628 def install(*pkgs):
629- apt_locked = True
630- while apt_locked:
631- cmd = [ 'lsof', '/var/lib/dpkg/lock' ]
632+ while True:
633+ cmd = ['lsof', '/var/lib/dpkg/lock']
634 try:
635 subprocess.check_call(cmd)
636- apt_locked = True
637 except:
638- apt_locked = False
639- if not apt_locked:
640- cmd = [
641- 'apt-get',
642- '-y',
643- 'install'
644- ]
645- for pkg in pkgs:
646- cmd.append(pkg)
647- subprocess.check_call(cmd)
648+ break
649+ cmd = [
650+ 'apt-get',
651+ '-y',
652+ 'install'
653+ ]
654+ cmd.extend(pkgs)
655+ subprocess.check_call(cmd)
656
657
658 TEMPLATES_DIR = 'templates'
659
660 try:
661 import jinja2
662-except ImportError:
663+except ImportError: # pragma: no cover
664 install('python-jinja2')
665- import jinja2
666+ import jinja2 # NOQA
667
668
669 def render_template(template_name, context, template_dir=TEMPLATES_DIR):
670 templates = jinja2.Environment(
671- loader=jinja2.FileSystemLoader(template_dir)
672- )
673+ loader=jinja2.FileSystemLoader(template_dir)
674+ )
675 template = templates.get_template(template_name)
676 return template.render(context)
677
678@@ -74,51 +57,16 @@
679 '-odpkg::options::=--force-confdef', # always take the default
680 '-odpkg::options::=--force-confold', # set the default to "N"
681 'install'
682- ]
683+ ]
684 for pkg in pkgs:
685 cmd.append(pkg)
686 subprocess.check_call(cmd)
687
688-
689-def configure_source():
690- source = config_get('source')
691- if (source.startswith('ppa:') or
692- source.startswith('cloud:')):
693- cmd = [
694- 'add-apt-repository',
695- source
696- ]
697- subprocess.check_call(cmd)
698- if source.startswith('http:'):
699- with open('/etc/apt/sources.list.d/ceph.list', 'w') as apt:
700- apt.write("deb " + source + "\n")
701- key = config_get('key')
702- if key != "":
703- cmd = [
704- 'apt-key',
705- 'import',
706- key
707- ]
708- subprocess.check_call(cmd)
709- cmd = [
710- 'apt-get',
711- 'update'
712- ]
713- subprocess.check_call(cmd)
714-
715 # Protocols
716 TCP = 'TCP'
717 UDP = 'UDP'
718
719
720-def expose(port, protocol='TCP'):
721- cmd = [
722- 'open-port',
723- '{}/{}'.format(port, protocol)
724- ]
725- subprocess.check_call(cmd)
726-
727-
728 def juju_log(severity, message):
729 cmd = [
730 'juju-log',
731@@ -126,114 +74,3 @@
732 message
733 ]
734 subprocess.check_call(cmd)
735-
736-
737-def relation_ids(relation):
738- cmd = [
739- 'relation-ids',
740- relation
741- ]
742- return subprocess.check_output(cmd).split() # IGNORE:E1103
743-
744-
745-def relation_list(rid):
746- cmd = [
747- 'relation-list',
748- '-r', rid,
749- ]
750- return subprocess.check_output(cmd).split() # IGNORE:E1103
751-
752-
753-def relation_get(attribute, unit=None, rid=None):
754- cmd = [
755- 'relation-get',
756- ]
757- if rid:
758- cmd.append('-r')
759- cmd.append(rid)
760- cmd.append(attribute)
761- if unit:
762- cmd.append(unit)
763- return subprocess.check_output(cmd).strip() # IGNORE:E1103
764-
765-
766-def relation_set(**kwargs):
767- cmd = [
768- 'relation-set'
769- ]
770- args = []
771- for k, v in kwargs.items():
772- if k == 'rid':
773- cmd.append('-r')
774- cmd.append(v)
775- else:
776- args.append('{}={}'.format(k, v))
777- cmd += args
778- subprocess.check_call(cmd)
779-
780-
781-def unit_get(attribute):
782- cmd = [
783- 'unit-get',
784- attribute
785- ]
786- return subprocess.check_output(cmd).strip() # IGNORE:E1103
787-
788-
789-def config_get(attribute):
790- cmd = [
791- 'config-get',
792- attribute
793- ]
794- return subprocess.check_output(cmd).strip() # IGNORE:E1103
795-
796-
797-def get_unit_hostname():
798- return socket.gethostname()
799-
800-
801-def get_host_ip(hostname):
802- if not hostname:
803- hostname=unit_get('private-address')
804- cmd = [
805- 'dig',
806- '+short',
807- hostname
808- ]
809- return subprocess.check_output(cmd).strip() # IGNORE:E1103
810-
811-
812-def do_basenode():
813- juju_log('INFO', 'Let there be basenode.')
814- try:
815- subprocess.check_call(['sh', '-c',
816- 'cd basenode && python setup.py install'])
817- except subprocess.CalledProcessError:
818- juju_log('ERROR', 'Installation of basenode failed!')
819- raise
820- from basenode import basenode_init
821- basenode_init()
822- juju_log('INFO', 'Done with basenode.')
823-
824-
825-def try_basenode():
826- try:
827- shall_we_dance = int(config_get('basenode'))
828- if shall_we_dance:
829- do_basenode()
830- except ValueError:
831- juju_log('ERROR',
832- 'Config setting "basenode" is not an integer, forging ahead.')
833-
834-
835-def debconf_set_selections(selections):
836- command = ['debconf-set-selections']
837- p = subprocess.Popen(command, close_fds=True, stdin=subprocess.PIPE)
838-
839- for selection in selections:
840- p.stdin.write(selection + "\n")
841- p.stdin.close()
842-
843- if p.wait() != 0:
844- raise subprocess.CalledProcessError(
845- cmd=command, returncode=p.returncode)
846
847=== added directory 'lib'
848=== modified file 'metadata.yaml'
849--- metadata.yaml 2013-02-05 06:18:50 +0000
850+++ metadata.yaml 2013-03-18 12:32:24 +0000
851@@ -1,6 +1,6 @@
852 name: squid-forwardproxy
853 summary: Full featured Web Proxy cache (HTTP proxy)
854-maintainer: "Alexander List <alexander.list@canonical.com>"
855+maintainer: [Matthew Wedgwood <matthew.wedgwood@canonical.com>, Alexander List <alexander.list@canonical.com>]
856 description: >
857 Squid is a high-performance proxy caching server for web clients,
858 supporting FTP, gopher, and HTTP data objects. Squid version 3 is a
859
860=== added file 'setup.cfg'
861--- setup.cfg 1970-01-01 00:00:00 +0000
862+++ setup.cfg 2013-03-18 12:32:24 +0000
863@@ -0,0 +1,4 @@
864+[nosetests]
865+with-coverage=1
866+cover-erase=1
867+cover-package=hooks,utils
868\ No newline at end of file
869
870=== added file 'tarmac_tests.sh'
871--- tarmac_tests.sh 1970-01-01 00:00:00 +0000
872+++ tarmac_tests.sh 2013-03-18 12:32:24 +0000
873@@ -0,0 +1,6 @@
874+#!/bin/sh
875+# How the tests are run in Jenkins by Tarmac
876+
877+set -e
878+
879+make build
880
881=== modified file 'templates/main_config.template'
882--- templates/main_config.template 2013-01-22 12:45:52 +0000
883+++ templates/main_config.template 2013-03-18 12:32:24 +0000
884@@ -35,11 +35,11 @@
885 {% for rp in refresh_patterns.keys() -%}
886 refresh_pattern {{ rp }} {{ refresh_patterns[rp]['min'] }} {{ refresh_patterns[rp]['percent'] }}% {{ refresh_patterns[rp]['max'] }}
887 {% endfor -%}
888-refresh_pattern . 30 20% 4320
889+refresh_pattern . {{default_refresh_pattern.min}} {{default_refresh_pattern.percent}}% {{default_refresh_pattern.max}}
890
891 {% for auth in auth_list -%}
892 {% set idx = loop.index -%}
893-{% for auth_name in auth.keys() -%}
894+{% for auth_name in auth.keys() -%}
895 {% for auth_val in auth[auth_name] -%}
896 acl l{{idx}}_{{ auth_name }} {{auth_name}} {{ auth_val }}
897 {% endfor -%}

Subscribers

People subscribed via source and target branches

to all changes: