Merge lp:~gnuoy/charm-helpers/status-set into lp:charm-helpers

Proposed by Liam Young
Status: Merged
Merged at revision: 370
Proposed branch: lp:~gnuoy/charm-helpers/status-set
Merge into: lp:charm-helpers
Diff against target: 104 lines (+89/-0)
2 files modified
charmhelpers/core/hookenv.py (+46/-0)
tests/core/test_hookenv.py (+43/-0)
To merge this branch: bzr merge lp:~gnuoy/charm-helpers/status-set
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Approve
Review via email: mp+258533@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

Looks good. Would have looked even better if I had seen it before repeating your work (see https://code.launchpad.net/~stub/charm-helpers/status-set/+merge/258618 ).

One minor issue - you need to add 'universal_newlines=True' to the subprocess.check_output command or you will get back a byte string under Python3.

I'm approving your work, pending the Py3 fix. However, before landing you might want to check out my branch and see if you prefer my approach. The major departure is that I chose to make status_set implicitly terminate on waiting and blocked, which also allowed me to put the unit into an error state as a fallback if the charm attempts to set blocked and status-set is unavailable. However, I just realized that this will not play well with Config's implicit saves so more work would be needed.

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

Thanks for the review, I'll get that py3 fix added. I took a look at your branch and I'm not keen on having the exits in status_set tbh, I was planning to run status_set multiple times in the same hook.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/core/hookenv.py'
2--- charmhelpers/core/hookenv.py 2015-04-07 16:12:51 +0000
3+++ charmhelpers/core/hookenv.py 2015-05-07 17:36:13 +0000
4@@ -605,3 +605,49 @@
5
6 The results set by action_set are preserved."""
7 subprocess.check_call(['action-fail', message])
8+
9+
10+def status_set(workload_state, message):
11+ """Set the workload state with a message
12+
13+ Use status-set to set the workload state with a message which is visible
14+ to the user via juju status. If the status-set command is not found then
15+ assume this is juju < 1.23 and juju-log the message unstead.
16+
17+ workload_state -- valid juju workload state.
18+ message -- status update message
19+ """
20+ valid_states = ['maintenance', 'blocked', 'waiting', 'active']
21+ if workload_state not in valid_states:
22+ raise ValueError(
23+ '{!r} is not a valid workload state'.format(workload_state)
24+ )
25+ cmd = ['status-set', workload_state, message]
26+ try:
27+ ret = subprocess.call(cmd)
28+ if ret == 0:
29+ return
30+ except OSError as e:
31+ if e.errno != errno.ENOENT:
32+ raise
33+ log_message = 'status-set failed: {} {}'.format(workload_state,
34+ message)
35+ log(log_message, level='INFO')
36+
37+
38+def status_get():
39+ """Retrieve the previously set juju workload state
40+
41+ If the status-set command is not found then assume this is juju < 1.23 and
42+ return 'unknown'
43+ """
44+ cmd = ['status-get']
45+ try:
46+ raw_status = subprocess.check_output(cmd)
47+ status = raw_status.rstrip()
48+ return status
49+ except OSError as e:
50+ if e.errno == errno.ENOENT:
51+ return 'unknown'
52+ else:
53+ raise
54
55=== modified file 'tests/core/test_hookenv.py'
56--- tests/core/test_hookenv.py 2015-03-12 10:31:22 +0000
57+++ tests/core/test_hookenv.py 2015-05-07 17:36:13 +0000
58@@ -1115,3 +1115,46 @@
59 message = "Ooops, the action failed"
60 hookenv.action_fail(message)
61 check_call.assert_called_with(['action-fail', message])
62+
63+ def test_status_set_invalid_state(self):
64+ self.assertRaises(ValueError, hookenv.status_set, 'random', 'message')
65+
66+ @patch('subprocess.call')
67+ def test_status(self, call):
68+ call.return_value = 0
69+ hookenv.status_set('active', 'Everything is Awesome!')
70+ call.assert_called_with(['status-set', 'active', 'Everything is Awesome!'])
71+
72+ @patch('subprocess.call')
73+ @patch.object(hookenv, 'log')
74+ def test_status_enoent(self, log, call):
75+ call.side_effect = OSError(2, 'fail')
76+ hookenv.status_set('active', 'Everything is Awesome!')
77+ log.assert_called_with('status-set failed: active Everything is Awesome!', level='INFO')
78+
79+ @patch('subprocess.call')
80+ @patch.object(hookenv, 'log')
81+ def test_status_statuscmd_fail(self, log, call):
82+ call.side_effect = OSError(3, 'fail')
83+ self.assertRaises(OSError, hookenv.status_set, 'active', 'msg')
84+ call.assert_called_with(['status-set', 'active', 'msg'])
85+
86+ @patch('subprocess.check_output')
87+ def test_status_get(self, check_output):
88+ check_output.return_value = 'active\n'
89+ result = hookenv.status_get()
90+ self.assertEqual(result, 'active')
91+ check_output.assert_called_with(['status-get'])
92+
93+ @patch('subprocess.check_output')
94+ def test_status_get_nostatus(self, check_output):
95+ check_output.side_effect = OSError(2, 'fail')
96+ result = hookenv.status_get()
97+ self.assertEqual(result, 'unknown')
98+ check_output.assert_called_with(['status-get'])
99+
100+ @patch('subprocess.check_output')
101+ def test_status_get_status_error(self, check_output):
102+ check_output.side_effect = OSError(3, 'fail')
103+ self.assertRaises(OSError, hookenv.status_get)
104+ check_output.assert_called_with(['status-get'])

Subscribers

People subscribed via source and target branches