Merge lp:~james-page/charm-helpers/refactor-service-control into lp:charm-helpers

Proposed by James Page
Status: Merged
Merged at revision: 32
Proposed branch: lp:~james-page/charm-helpers/refactor-service-control
Merge into: lp:charm-helpers
Diff against target: 196 lines (+62/-71)
3 files modified
charmhelpers/core/host.py (+11/-9)
tests/contrib/charmsupport/test_nrpe.py (+4/-4)
tests/core/test_host.py (+47/-58)
To merge this branch: bzr merge lp:~james-page/charm-helpers/refactor-service-control
Reviewer Review Type Date Requested Status
Matthew Wedgwood (community) Approve
James Page Needs Resubmitting
Review via email: mp+170383@code.launchpad.net

Description of the change

Refactoring of service control code in host helper

1) Use 'service' command for all service control

Detecting upstart and init.d configuration files is overkill; this is
exactly what the 'service' command is design todo and it also deals with
saucy onwards where init.d and upstart configuration with the same
name might be installed.

'service' will always do the right thing

2) Added restart and reload helpers

reload detects an error (say the service is not running) and will fallback
to restart if so.

This is inline with the openstack charm helpers code.

To post a comment you must log in.
Revision history for this message
Matthew Wedgwood (mew) wrote :

For service_reload, I don't think it's safe to assume that a service should be restarted if reload fails. I'm at a loss to find an example, but services whose "reload" commands handle invalid configuration gracefully might be brought down on a "restart."

Revision history for this message
James Page (james-page) wrote :

Maybe that is a little use case focussed - we used this method to either reload or (re)start haproxy for the HA openstack charms. In the instance where haproxy was not running, the restart call would start it correctly.

I'll add an option to the restart function to optionally enable this so that by default, reload won't restart.

28. By James Page

Make restart on failed reload optional, add/update tests to cover this

Revision history for this message
James Page (james-page) :
review: Needs Resubmitting
Revision history for this message
Matthew Wedgwood (mew) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/core/host.py'
2--- charmhelpers/core/host.py 2013-06-12 16:18:26 +0000
3+++ charmhelpers/core/host.py 2013-06-20 21:23:22 +0000
4@@ -23,16 +23,18 @@
5 service('stop', service_name)
6
7
8+def service_restart(service_name):
9+ service('restart', service_name)
10+
11+
12+def service_reload(service_name, restart_on_failure=False):
13+ if not service('reload', service_name) and restart_on_failure:
14+ service('restart', service_name)
15+
16+
17 def service(action, service_name):
18- cmd = None
19- if os.path.exists(os.path.join('/etc/init', '%s.conf' % service_name)):
20- cmd = ['initctl', action, service_name]
21- elif os.path.exists(os.path.join('/etc/init.d', service_name)):
22- cmd = [os.path.join('/etc/init.d', service_name), action]
23- if cmd:
24- return_value = subprocess.call(cmd)
25- return return_value == 0
26- return False
27+ cmd = ['service', service_name, action]
28+ return subprocess.call(cmd) == 0
29
30
31 def adduser(username, password=None, shell='/bin/bash', system_user=False):
32
33=== modified file 'tests/contrib/charmsupport/test_nrpe.py'
34--- tests/contrib/charmsupport/test_nrpe.py 2013-05-11 20:24:29 +0000
35+++ tests/contrib/charmsupport/test_nrpe.py 2013-06-20 21:23:22 +0000
36@@ -77,7 +77,7 @@
37
38 self.assertEqual(None, checker.write())
39
40- self.check_call_counts(config=1, getpwnam=1, getgrnam=1, exists=2)
41+ self.check_call_counts(config=1, getpwnam=1, getgrnam=1, exists=1)
42
43 def test_write_restarts_service(self):
44 self.patched['config'].return_value = {'nagios_context': 'test'}
45@@ -86,10 +86,10 @@
46
47 self.assertEqual(None, checker.write())
48
49- expected = ['initctl', 'restart', 'nagios-nrpe-server']
50+ expected = ['service', 'nagios-nrpe-server', 'restart']
51 self.assertEqual(expected, self.patched['call'].call_args[0][0])
52 self.check_call_counts(config=1, getpwnam=1, getgrnam=1,
53- exists=2, call=1)
54+ exists=1, call=1)
55
56 def test_update_nrpe(self):
57 self.patched['config'].return_value = {'nagios_context': 'a'}
58@@ -139,7 +139,7 @@
59 self.patched['relation_set'].assert_called_once_with(
60 relation_id="local-monitors:1", monitors=monitors)
61 self.check_call_counts(config=1, getpwnam=1, getgrnam=1,
62- exists=4, open=2, listdir=1,
63+ exists=3, open=2, listdir=1,
64 relation_ids=1, relation_set=1)
65
66
67
68=== modified file 'tests/core/test_host.py'
69--- tests/core/test_host.py 2013-06-12 16:18:26 +0000
70+++ tests/core/test_host.py 2013-06-20 21:23:22 +0000
71@@ -62,62 +62,18 @@
72
73 class HelpersTest(TestCase):
74 @patch('subprocess.call')
75- @patch('os.path.exists')
76- def test_runs_service_action(self, exists, mock_call):
77- init_exists = True
78- init_d_exists = False
79- exists.side_effect = [init_exists, init_d_exists]
80- mock_call.return_value = 0
81- action = 'some-action'
82- service_name = 'foo-service'
83-
84- result = host.service(action, service_name)
85-
86- self.assertTrue(result)
87- mock_call.assert_called_with(['initctl', action, service_name])
88- exists.assert_has_calls([
89- call(os.path.join('/etc/init', '%s.conf' % service_name)),
90- ])
91-
92- @patch('subprocess.call')
93- @patch('os.path.exists')
94- def test_runs_service_action_on_init_d(self, exists, mock_call):
95- init_exists = False
96- init_d_exists = True
97- exists.side_effect = [init_exists, init_d_exists]
98- mock_call.return_value = 0
99- action = 'some-action'
100- service_name = 'foo-service'
101-
102- result = host.service(action, service_name)
103-
104- self.assertTrue(result)
105- mock_call.assert_called_with([os.path.join('/etc/init.d',
106- service_name), action])
107- exists.assert_has_calls([
108- call(os.path.join('/etc/init.d', service_name)),
109- ])
110-
111- @patch('subprocess.call')
112- @patch('os.path.exists')
113- def test_doesnt_run_service_if_it_doesn_exist(self, exists, mock_call):
114- init_exists = False
115- init_d_exists = False
116- exists.side_effect = [init_exists, init_d_exists]
117- action = 'some-action'
118- service_name = 'foo-service'
119-
120- result = host.service(action, service_name)
121-
122- self.assertFalse(result)
123- self.assertFalse(mock_call.called)
124-
125- @patch('subprocess.call')
126- @patch('os.path.exists')
127- def test_returns_false_when_service_fails(self, exists, mock_call):
128- init_exists = False
129- init_d_exists = True
130- exists.side_effect = [init_exists, init_d_exists]
131+ def test_runs_service_action(self, mock_call):
132+ mock_call.return_value = 0
133+ action = 'some-action'
134+ service_name = 'foo-service'
135+
136+ result = host.service(action, service_name)
137+
138+ self.assertTrue(result)
139+ mock_call.assert_called_with(['service', service_name, action])
140+
141+ @patch('subprocess.call')
142+ def test_returns_false_when_service_fails(self, mock_call):
143 mock_call.return_value = 1
144 action = 'some-action'
145 service_name = 'foo-service'
146@@ -125,8 +81,7 @@
147 result = host.service(action, service_name)
148
149 self.assertFalse(result)
150- mock_call.assert_called_with([os.path.join('/etc/init.d',
151- service_name), action])
152+ mock_call.assert_called_with(['service', service_name, action])
153
154 @patch.object(host, 'service')
155 def test_starts_a_service(self, service):
156@@ -142,6 +97,40 @@
157
158 service.assert_called_with('stop', service_name)
159
160+ @patch.object(host, 'service')
161+ def test_restarts_a_service(self, service):
162+ service_name = 'foo-service'
163+ host.service_restart(service_name)
164+
165+ service.assert_called_with('restart', service_name)
166+
167+ @patch.object(host, 'service')
168+ def test_reloads_a_service(self, service):
169+ service_name = 'foo-service'
170+ service.side_effect = [True]
171+ host.service_reload(service_name)
172+
173+ service.assert_called_with('reload', service_name)
174+
175+ @patch.object(host, 'service')
176+ def test_failed_reload_restarts_a_service(self, service):
177+ service_name = 'foo-service'
178+ service.side_effect = [False, True]
179+ host.service_reload(service_name, restart_on_failure=True)
180+
181+ service.assert_has_calls([
182+ call('reload', service_name),
183+ call('restart', service_name)
184+ ])
185+
186+ @patch.object(host, 'service')
187+ def test_failed_reload_without_restart(self, service):
188+ service_name = 'foo-service'
189+ service.side_effect = [False]
190+ host.service_reload(service_name)
191+
192+ service.assert_called_with('reload', service_name)
193+
194 @patch('pwd.getpwnam')
195 @patch('subprocess.check_call')
196 @patch.object(host, 'log')

Subscribers

People subscribed via source and target branches