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
=== modified file 'charmhelpers/core/host.py'
--- charmhelpers/core/host.py 2013-06-12 16:18:26 +0000
+++ charmhelpers/core/host.py 2013-06-20 21:23:22 +0000
@@ -23,16 +23,18 @@
23 service('stop', service_name)23 service('stop', service_name)
2424
2525
26def service_restart(service_name):
27 service('restart', service_name)
28
29
30def service_reload(service_name, restart_on_failure=False):
31 if not service('reload', service_name) and restart_on_failure:
32 service('restart', service_name)
33
34
26def service(action, service_name):35def service(action, service_name):
27 cmd = None36 cmd = ['service', service_name, action]
28 if os.path.exists(os.path.join('/etc/init', '%s.conf' % service_name)):37 return subprocess.call(cmd) == 0
29 cmd = ['initctl', action, service_name]
30 elif os.path.exists(os.path.join('/etc/init.d', service_name)):
31 cmd = [os.path.join('/etc/init.d', service_name), action]
32 if cmd:
33 return_value = subprocess.call(cmd)
34 return return_value == 0
35 return False
3638
3739
38def adduser(username, password=None, shell='/bin/bash', system_user=False):40def adduser(username, password=None, shell='/bin/bash', system_user=False):
3941
=== modified file 'tests/contrib/charmsupport/test_nrpe.py'
--- tests/contrib/charmsupport/test_nrpe.py 2013-05-11 20:24:29 +0000
+++ tests/contrib/charmsupport/test_nrpe.py 2013-06-20 21:23:22 +0000
@@ -77,7 +77,7 @@
7777
78 self.assertEqual(None, checker.write())78 self.assertEqual(None, checker.write())
7979
80 self.check_call_counts(config=1, getpwnam=1, getgrnam=1, exists=2)80 self.check_call_counts(config=1, getpwnam=1, getgrnam=1, exists=1)
8181
82 def test_write_restarts_service(self):82 def test_write_restarts_service(self):
83 self.patched['config'].return_value = {'nagios_context': 'test'}83 self.patched['config'].return_value = {'nagios_context': 'test'}
@@ -86,10 +86,10 @@
8686
87 self.assertEqual(None, checker.write())87 self.assertEqual(None, checker.write())
8888
89 expected = ['initctl', 'restart', 'nagios-nrpe-server']89 expected = ['service', 'nagios-nrpe-server', 'restart']
90 self.assertEqual(expected, self.patched['call'].call_args[0][0])90 self.assertEqual(expected, self.patched['call'].call_args[0][0])
91 self.check_call_counts(config=1, getpwnam=1, getgrnam=1,91 self.check_call_counts(config=1, getpwnam=1, getgrnam=1,
92 exists=2, call=1)92 exists=1, call=1)
9393
94 def test_update_nrpe(self):94 def test_update_nrpe(self):
95 self.patched['config'].return_value = {'nagios_context': 'a'}95 self.patched['config'].return_value = {'nagios_context': 'a'}
@@ -139,7 +139,7 @@
139 self.patched['relation_set'].assert_called_once_with(139 self.patched['relation_set'].assert_called_once_with(
140 relation_id="local-monitors:1", monitors=monitors)140 relation_id="local-monitors:1", monitors=monitors)
141 self.check_call_counts(config=1, getpwnam=1, getgrnam=1,141 self.check_call_counts(config=1, getpwnam=1, getgrnam=1,
142 exists=4, open=2, listdir=1,142 exists=3, open=2, listdir=1,
143 relation_ids=1, relation_set=1)143 relation_ids=1, relation_set=1)
144144
145145
146146
=== modified file 'tests/core/test_host.py'
--- tests/core/test_host.py 2013-06-12 16:18:26 +0000
+++ tests/core/test_host.py 2013-06-20 21:23:22 +0000
@@ -62,62 +62,18 @@
6262
63class HelpersTest(TestCase):63class HelpersTest(TestCase):
64 @patch('subprocess.call')64 @patch('subprocess.call')
65 @patch('os.path.exists')65 def test_runs_service_action(self, mock_call):
66 def test_runs_service_action(self, exists, mock_call):66 mock_call.return_value = 0
67 init_exists = True67 action = 'some-action'
68 init_d_exists = False68 service_name = 'foo-service'
69 exists.side_effect = [init_exists, init_d_exists]69
70 mock_call.return_value = 070 result = host.service(action, service_name)
71 action = 'some-action'71
72 service_name = 'foo-service'72 self.assertTrue(result)
7373 mock_call.assert_called_with(['service', service_name, action])
74 result = host.service(action, service_name)74
7575 @patch('subprocess.call')
76 self.assertTrue(result)76 def test_returns_false_when_service_fails(self, mock_call):
77 mock_call.assert_called_with(['initctl', action, service_name])
78 exists.assert_has_calls([
79 call(os.path.join('/etc/init', '%s.conf' % service_name)),
80 ])
81
82 @patch('subprocess.call')
83 @patch('os.path.exists')
84 def test_runs_service_action_on_init_d(self, exists, mock_call):
85 init_exists = False
86 init_d_exists = True
87 exists.side_effect = [init_exists, init_d_exists]
88 mock_call.return_value = 0
89 action = 'some-action'
90 service_name = 'foo-service'
91
92 result = host.service(action, service_name)
93
94 self.assertTrue(result)
95 mock_call.assert_called_with([os.path.join('/etc/init.d',
96 service_name), action])
97 exists.assert_has_calls([
98 call(os.path.join('/etc/init.d', service_name)),
99 ])
100
101 @patch('subprocess.call')
102 @patch('os.path.exists')
103 def test_doesnt_run_service_if_it_doesn_exist(self, exists, mock_call):
104 init_exists = False
105 init_d_exists = False
106 exists.side_effect = [init_exists, init_d_exists]
107 action = 'some-action'
108 service_name = 'foo-service'
109
110 result = host.service(action, service_name)
111
112 self.assertFalse(result)
113 self.assertFalse(mock_call.called)
114
115 @patch('subprocess.call')
116 @patch('os.path.exists')
117 def test_returns_false_when_service_fails(self, exists, mock_call):
118 init_exists = False
119 init_d_exists = True
120 exists.side_effect = [init_exists, init_d_exists]
121 mock_call.return_value = 177 mock_call.return_value = 1
122 action = 'some-action'78 action = 'some-action'
123 service_name = 'foo-service'79 service_name = 'foo-service'
@@ -125,8 +81,7 @@
125 result = host.service(action, service_name)81 result = host.service(action, service_name)
12682
127 self.assertFalse(result)83 self.assertFalse(result)
128 mock_call.assert_called_with([os.path.join('/etc/init.d',84 mock_call.assert_called_with(['service', service_name, action])
129 service_name), action])
13085
131 @patch.object(host, 'service')86 @patch.object(host, 'service')
132 def test_starts_a_service(self, service):87 def test_starts_a_service(self, service):
@@ -142,6 +97,40 @@
14297
143 service.assert_called_with('stop', service_name)98 service.assert_called_with('stop', service_name)
14499
100 @patch.object(host, 'service')
101 def test_restarts_a_service(self, service):
102 service_name = 'foo-service'
103 host.service_restart(service_name)
104
105 service.assert_called_with('restart', service_name)
106
107 @patch.object(host, 'service')
108 def test_reloads_a_service(self, service):
109 service_name = 'foo-service'
110 service.side_effect = [True]
111 host.service_reload(service_name)
112
113 service.assert_called_with('reload', service_name)
114
115 @patch.object(host, 'service')
116 def test_failed_reload_restarts_a_service(self, service):
117 service_name = 'foo-service'
118 service.side_effect = [False, True]
119 host.service_reload(service_name, restart_on_failure=True)
120
121 service.assert_has_calls([
122 call('reload', service_name),
123 call('restart', service_name)
124 ])
125
126 @patch.object(host, 'service')
127 def test_failed_reload_without_restart(self, service):
128 service_name = 'foo-service'
129 service.side_effect = [False]
130 host.service_reload(service_name)
131
132 service.assert_called_with('reload', service_name)
133
145 @patch('pwd.getpwnam')134 @patch('pwd.getpwnam')
146 @patch('subprocess.check_call')135 @patch('subprocess.check_call')
147 @patch.object(host, 'log')136 @patch.object(host, 'log')

Subscribers

People subscribed via source and target branches