Merge lp:~axwalk/charm-helpers/hookenv-storage into lp:charm-helpers

Proposed by Andrew Wilkins
Status: Merged
Merged at revision: 455
Proposed branch: lp:~axwalk/charm-helpers/hookenv-storage
Merge into: lp:charm-helpers
Diff against target: 126 lines (+108/-0)
2 files modified
charmhelpers/core/hookenv.py (+32/-0)
tests/core/test_hookenv.py (+76/-0)
To merge this branch: bzr merge lp:~axwalk/charm-helpers/hookenv-storage
Reviewer Review Type Date Requested Status
Tim Van Steenburgh Approve
Review via email: mp+272076@code.launchpad.net

Description of the change

This MP adds two new functions to hookenv: storage_list and storage_get. These will, respectively, list and get details of the unit's storage attachments.

storage_list will return [] of the storage-list hook tool cannot be found in $PATH. storage_get does not filter exceptions, because there's no reason to use it unless you're in a storage hook, or you've called it with the results of storage_list. As such, the following sort of code:
    for s in storage_list():
        storage_get(storage_id=s)
will work regardless of the version of Juju.

To post a comment you must log in.
Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

The last three tests all do:

    self.assertEqual(result, result)

...which will always be true. Presumably an oversight, but one of those 'result' vars needs to be renamed in each test.

review: Needs Fixing
456. By Andrew Wilkins

Fix tautological tests

Revision history for this message
Andrew Wilkins (axwalk) wrote :

> The last three tests all do:
>
> self.assertEqual(result, result)
>
> ...which will always be true. Presumably an oversight, but one of those
> 'result' vars needs to be renamed in each test.

Wow, sorry about that :/
Fixed.

Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

+1 LGTM. Bonus point for using "tautological" in a commit message.

review: Approve

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-08-26 17:32:36 +0000
3+++ charmhelpers/core/hookenv.py 2015-09-24 01:29:26 +0000
4@@ -623,6 +623,38 @@
5 return unit_get('private-address')
6
7
8+@cached
9+def storage_get(attribute="", storage_id=""):
10+ """Get storage attributes"""
11+ _args = ['storage-get', '--format=json']
12+ if storage_id:
13+ _args.extend(('-s', storage_id))
14+ if attribute:
15+ _args.append(attribute)
16+ try:
17+ return json.loads(subprocess.check_output(_args).decode('UTF-8'))
18+ except ValueError:
19+ return None
20+
21+
22+@cached
23+def storage_list(storage_name=""):
24+ """List the storage IDs for the unit"""
25+ _args = ['storage-list', '--format=json']
26+ if storage_name:
27+ _args.append(storage_name)
28+ try:
29+ return json.loads(subprocess.check_output(_args).decode('UTF-8'))
30+ except ValueError:
31+ return None
32+ except OSError as e:
33+ import errno
34+ if e.errno == errno.ENOENT:
35+ # storage-list does not exist
36+ return []
37+ raise
38+
39+
40 class UnregisteredHookError(Exception):
41 """Raised when an undefined hook is called"""
42 pass
43
44=== modified file 'tests/core/test_hookenv.py'
45--- tests/core/test_hookenv.py 2015-08-26 17:32:36 +0000
46+++ tests/core/test_hookenv.py 2015-09-24 01:29:26 +0000
47@@ -1500,3 +1500,79 @@
48 def test_action_tag(self):
49 with patch.dict('os.environ', JUJU_ACTION_TAG='action-jack'):
50 self.assertEqual(hookenv.action_tag(), 'action-jack')
51+
52+ @patch('subprocess.check_output')
53+ def test_storage_list(self, check_output):
54+ ids = ['data/0', 'data/1', 'data/2']
55+ check_output.return_value = json.dumps(ids).encode('UTF-8')
56+
57+ storage_name = 'arbitrary'
58+ result = hookenv.storage_list(storage_name)
59+
60+ self.assertEqual(result, ids)
61+ check_output.assert_called_with(['storage-list', '--format=json',
62+ storage_name])
63+
64+ @patch('subprocess.check_output')
65+ def test_storage_list_notexist(self, check_output):
66+ import errno
67+ e = OSError()
68+ e.errno = errno.ENOENT
69+ check_output.side_effect = e
70+
71+ result = hookenv.storage_list()
72+
73+ self.assertEqual(result, [])
74+ check_output.assert_called_with(['storage-list', '--format=json'])
75+
76+ @patch('subprocess.check_output')
77+ def test_storage_get_notexist(self, check_output):
78+ # storage_get does not catch ENOENT, because there's no reason why you
79+ # should be calling storage_get except from a storage hook, or with
80+ # the result of storage_list (which will return [] as shown above).
81+
82+ import errno
83+ e = OSError()
84+ e.errno = errno.ENOENT
85+ check_output.side_effect = e
86+ self.assertRaises(OSError, hookenv.storage_get)
87+
88+ @patch('subprocess.check_output')
89+ def test_storage_get(self, check_output):
90+ expect = {
91+ 'location': '/dev/sda',
92+ 'kind': 'block',
93+ }
94+ check_output.return_value = json.dumps(expect).encode('UTF-8')
95+
96+ result = hookenv.storage_get()
97+
98+ self.assertEqual(result, expect)
99+ check_output.assert_called_with(['storage-get', '--format=json'])
100+
101+ @patch('subprocess.check_output')
102+ def test_storage_get_attr(self, check_output):
103+ expect = '/dev/sda'
104+ check_output.return_value = json.dumps(expect).encode('UTF-8')
105+
106+ attribute = 'location'
107+ result = hookenv.storage_get(attribute)
108+
109+ self.assertEqual(result, expect)
110+ check_output.assert_called_with(['storage-get', '--format=json',
111+ attribute])
112+
113+ @patch('subprocess.check_output')
114+ def test_storage_get_with_id(self, check_output):
115+ expect = {
116+ 'location': '/dev/sda',
117+ 'kind': 'block',
118+ }
119+ check_output.return_value = json.dumps(expect).encode('UTF-8')
120+
121+ storage_id = 'data/0'
122+ result = hookenv.storage_get(storage_id=storage_id)
123+
124+ self.assertEqual(result, expect)
125+ check_output.assert_called_with(['storage-get', '--format=json',
126+ '-s', storage_id])

Subscribers

People subscribed via source and target branches