Merge lp:~niedbalski/python-jujuclient/lp-1456332 into lp:python-jujuclient

Proposed by Jorge Niedbalski
Status: Merged
Merged at revision: 57
Proposed branch: lp:~niedbalski/python-jujuclient/lp-1456332
Merge into: lp:python-jujuclient
Diff against target: 138 lines (+95/-5)
2 files modified
jujuclient.py (+31/-5)
test_jujuclient.py (+64/-0)
To merge this branch: bzr merge lp:~niedbalski/python-jujuclient/lp-1456332
Reviewer Review Type Date Requested Status
Tim Van Steenburgh (community) Approve
Felipe Reyes (community) Approve
Review via email: mp+259450@code.launchpad.net

This proposal supersedes a proposal from 2015-05-18.

Description of the change

This patch fixes LP: #1456332

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

One minor quibble: it's not clear what happens if you call Run without providing any machines, services, or units. Is that an error from Juju, or is it the equivalent of calling RunOnAllMachines? It would be great to clarify this in the run() docstring.

Otherwise looks good, pending a successful test run.

review: Needs Fixing
Revision history for this message
Felipe Reyes (freyes) wrote : Posted in a previous version of this proposal

Hi @niedbalski, I was reviewing your patch and looks fine to me, but I would like to ask you to add two more tests, to test when units are passed and another one for service, at the moment you're only testing with machines.

Best,

review: Needs Fixing
Revision history for this message
Jorge Niedbalski (niedbalski) wrote : Posted in a previous version of this proposal

Thanks guys for taking the time for reviewing.

@Tim,

I think that this is indeed a Juju-core validation error.

I submitted a PR for handling this on the server side: https://github.com/juju/juju/pull/2355

I don't think we should threat this as a special case, since this is a library and
the RPC call should warn about that. But, if you have any other idea, please let me know.

@Felipe,

I re-submitted addressing your comments adding 2 new tests.

Revision history for this message
Jorge Niedbalski (niedbalski) wrote : Posted in a previous version of this proposal

@Tim

I added an assertion if no target is passed, just in case.

Revision history for this message
Kapil Thangavelu (hazmat) wrote :
Download full text (5.4 KiB)

thats some api breakage with this merge proposal... and seems like breakage
in core if its needed.. all that said looks reasonable

On Mon, May 18, 2015 at 9:19 PM, Jorge Niedbalski <
<email address hidden>> wrote:

> Jorge Niedbalski has proposed merging
> lp:~niedbalski/python-jujuclient/lp-1456332 into lp:python-jujuclient.
>
> Requested reviews:
> Felipe Reyes (freyes)
> Tim Van Steenburgh (tvansteenburgh)
> Related bugs:
> Bug #1456332 in python-jujuclient: "Run command doesn't supports
> specific targets"
> https://bugs.launchpad.net/python-jujuclient/+bug/1456332
>
> For more details, see:
>
> https://code.launchpad.net/~niedbalski/python-jujuclient/lp-1456332/+merge/259450
>
> This patch fixes LP: #1456332
> --
> Your team juju-deployers is subscribed to branch lp:python-jujuclient.
>
> === modified file 'jujuclient.py'
> --- jujuclient.py 2015-05-11 14:33:56 +0000
> +++ jujuclient.py 2015-05-19 01:18:19 +0000
> @@ -273,7 +273,7 @@
> def _rpc(self, op):
> if not self._auth and not op.get("Request") == "Login":
> raise LoginRequired()
> - if not 'Params' in op:
> + if 'Params' not in op:
> op['Params'] = {}
> op['RequestId'] = self._request_id
> self._request_id += 1
> @@ -813,14 +813,40 @@
> "Params": {"Commands": command,
> "Timeout": timeout}})
>
> - def run(self, targets, command, timeout=None):
> + def run(self, command, timeout=None, machines=None,
> + services=None, units=None):
> """Run a shell command on the targets (services, units, or
> machines).
> + At least one target must be specified machines || services ||
> units
> """
> - return self._rpc({
> +
> + assert not (not machines and not services and not units), \
> + "You must specify a target"
> +
> + rpc_dict = {
> "Type": "Client",
> "Request": "Run",
> - "Params": {"Commands": command,
> - "Timeout": timeout}})
> + "Params": {
> + "Commands": command,
> + "Timeout": timeout,
> + }
> + }
> +
> + if machines:
> + if not isinstance(machines, (list, tuple)):
> + machines = [machines]
> + rpc_dict.update({'Machines': machines})
> +
> + if services:
> + if not isinstance(services, (list, tuple)):
> + services = [services]
> + rpc_dict.update({'Services': services})
> +
> + if units:
> + if not isinstance(units, (list, tuple)):
> + units = [units]
> + rpc_dict.update({'Units': units})
> +
> + return self._rpc(rpc_dict)
>
> # Machine ops
> def add_machine(self, series="", constraints=None,
>
> === modified file 'test_jujuclient.py'
> --- test_jujuclient.py 2015-05-11 14:33:56 +0000
> +++ test_jujuclient.py 2015-05-19 01:18:19 +0000
> @@ -83,6 +83,7 @@
> ValueError, Connector.split_host_port, 'I am not an ip/port
> combo'
> )
>
> +
> class KeyManagerTest(unittest.TestCase):
> def...

Read more...

Revision history for this message
Felipe Reyes (freyes) wrote :

Thanks Jorge, LGTM.

review: Approve
Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :
Download full text (5.9 KiB)

> thats some api breakage with this merge proposal... and seems like breakage
> in core if its needed.. all that said looks reasonable
>

Kapil can you elaborate on what the api breakage is? The existing version of run() doesn't even pass the intended targets through to the rpc call (i.e., it appears to be broken).

> On Mon, May 18, 2015 at 9:19 PM, Jorge Niedbalski <
> <email address hidden>> wrote:
>
> > Jorge Niedbalski has proposed merging
> > lp:~niedbalski/python-jujuclient/lp-1456332 into lp:python-jujuclient.
> >
> > Requested reviews:
> > Felipe Reyes (freyes)
> > Tim Van Steenburgh (tvansteenburgh)
> > Related bugs:
> > Bug #1456332 in python-jujuclient: "Run command doesn't supports
> > specific targets"
> > https://bugs.launchpad.net/python-jujuclient/+bug/1456332
> >
> > For more details, see:
> >
> > https://code.launchpad.net/~niedbalski/python-
> jujuclient/lp-1456332/+merge/259450
> >
> > This patch fixes LP: #1456332
> > --
> > Your team juju-deployers is subscribed to branch lp:python-jujuclient.
> >
> > === modified file 'jujuclient.py'
> > --- jujuclient.py 2015-05-11 14:33:56 +0000
> > +++ jujuclient.py 2015-05-19 01:18:19 +0000
> > @@ -273,7 +273,7 @@
> > def _rpc(self, op):
> > if not self._auth and not op.get("Request") == "Login":
> > raise LoginRequired()
> > - if not 'Params' in op:
> > + if 'Params' not in op:
> > op['Params'] = {}
> > op['RequestId'] = self._request_id
> > self._request_id += 1
> > @@ -813,14 +813,40 @@
> > "Params": {"Commands": command,
> > "Timeout": timeout}})
> >
> > - def run(self, targets, command, timeout=None):
> > + def run(self, command, timeout=None, machines=None,
> > + services=None, units=None):
> > """Run a shell command on the targets (services, units, or
> > machines).
> > + At least one target must be specified machines || services ||
> > units
> > """
> > - return self._rpc({
> > +
> > + assert not (not machines and not services and not units), \
> > + "You must specify a target"
> > +
> > + rpc_dict = {
> > "Type": "Client",
> > "Request": "Run",
> > - "Params": {"Commands": command,
> > - "Timeout": timeout}})
> > + "Params": {
> > + "Commands": command,
> > + "Timeout": timeout,
> > + }
> > + }
> > +
> > + if machines:
> > + if not isinstance(machines, (list, tuple)):
> > + machines = [machines]
> > + rpc_dict.update({'Machines': machines})
> > +
> > + if services:
> > + if not isinstance(services, (list, tuple)):
> > + services = [services]
> > + rpc_dict.update({'Services': services})
> > +
> > + if units:
> > + if not isinstance(units, (list, tuple)):
> > + units = [units]
> > + rpc_dict.update({'Units': units})
> > +
> > + return self._rpc(rpc_dict)
> >
> > # Machine ops
> > def add_machine(self, s...

Read more...

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

LGTM Jorge, thanks. There are failures in the test suite (unrelated to your changes), but that's a preexisting condition and there is already a bug open for them.

review: Approve
Revision history for this message
Jorge Niedbalski (niedbalski) wrote :

On Tue, May 19, 2015 at 10:20 AM, Tim Van Steenburgh
<email address hidden> wrote:
>> thats some api breakage with this merge proposal... and seems like breakage
>> in core if its needed.. all that said looks reasonable
>>
>
> Kapil can you elaborate on what the api breakage is? The existing version of run() doesn't even pass the intended targets through to the rpc call (i.e., it appears to be broken).
>
Tim,

I think he refers to the method signature change, but AFAIK there is
no internal calls to this method, and since targets were never
used on the rpc call, seems to me that this code is broken since it
was introduced.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'jujuclient.py'
2--- jujuclient.py 2015-05-11 14:33:56 +0000
3+++ jujuclient.py 2015-05-19 01:18:19 +0000
4@@ -273,7 +273,7 @@
5 def _rpc(self, op):
6 if not self._auth and not op.get("Request") == "Login":
7 raise LoginRequired()
8- if not 'Params' in op:
9+ if 'Params' not in op:
10 op['Params'] = {}
11 op['RequestId'] = self._request_id
12 self._request_id += 1
13@@ -813,14 +813,40 @@
14 "Params": {"Commands": command,
15 "Timeout": timeout}})
16
17- def run(self, targets, command, timeout=None):
18+ def run(self, command, timeout=None, machines=None,
19+ services=None, units=None):
20 """Run a shell command on the targets (services, units, or machines).
21+ At least one target must be specified machines || services || units
22 """
23- return self._rpc({
24+
25+ assert not (not machines and not services and not units), \
26+ "You must specify a target"
27+
28+ rpc_dict = {
29 "Type": "Client",
30 "Request": "Run",
31- "Params": {"Commands": command,
32- "Timeout": timeout}})
33+ "Params": {
34+ "Commands": command,
35+ "Timeout": timeout,
36+ }
37+ }
38+
39+ if machines:
40+ if not isinstance(machines, (list, tuple)):
41+ machines = [machines]
42+ rpc_dict.update({'Machines': machines})
43+
44+ if services:
45+ if not isinstance(services, (list, tuple)):
46+ services = [services]
47+ rpc_dict.update({'Services': services})
48+
49+ if units:
50+ if not isinstance(units, (list, tuple)):
51+ units = [units]
52+ rpc_dict.update({'Units': units})
53+
54+ return self._rpc(rpc_dict)
55
56 # Machine ops
57 def add_machine(self, series="", constraints=None,
58
59=== modified file 'test_jujuclient.py'
60--- test_jujuclient.py 2015-05-11 14:33:56 +0000
61+++ test_jujuclient.py 2015-05-19 01:18:19 +0000
62@@ -83,6 +83,7 @@
63 ValueError, Connector.split_host_port, 'I am not an ip/port combo'
64 )
65
66+
67 class KeyManagerTest(unittest.TestCase):
68 def setUp(self):
69 self.env = Environment.connect(ENV_NAME)
70@@ -382,5 +383,68 @@
71 pass
72
73
74+class TestEnvironment(unittest.TestCase):
75+
76+ def setUp(self):
77+ self.env = Environment.connect(ENV_NAME)
78+
79+ def test_run_no_target(self):
80+ self.assertRaises(AssertionError, self.env.run, "sudo test")
81+
82+ def test_run_target_machines(self):
83+ with mock.patch.object(self.env, '_rpc',
84+ return_value=None) as rpc:
85+ self.env.run("sudo test", machines=["0", "1"])
86+
87+ rpc.assert_called_once_with({
88+ "Type": "Client",
89+ "Request": "Run",
90+ "Params": {
91+ "Commands": "sudo test",
92+ "Timeout": None,
93+ },
94+ "Machines": [
95+ '0',
96+ '1',
97+ ]
98+ })
99+
100+ def test_run_target_services(self):
101+ with mock.patch.object(self.env, '_rpc',
102+ return_value=None) as rpc:
103+ self.env.run("sudo test", services=["cinder", "glance"])
104+
105+ rpc.assert_called_once_with({
106+ "Type": "Client",
107+ "Request": "Run",
108+ "Params": {
109+ "Commands": "sudo test",
110+ "Timeout": None,
111+ },
112+ "Services": [
113+ 'cinder',
114+ 'glance',
115+ ]
116+ })
117+
118+ def test_run_target_units(self):
119+ with mock.patch.object(self.env, '_rpc',
120+ return_value=None) as rpc:
121+ self.env.run("sudo test", units=["mysql/0", "mysql/1"])
122+
123+ rpc.assert_called_once_with({
124+ "Type": "Client",
125+ "Request": "Run",
126+ "Params": {
127+ "Commands": "sudo test",
128+ "Timeout": None,
129+ },
130+ "Units": [
131+ 'mysql/0',
132+ 'mysql/1',
133+ ]
134+ })
135+
136+
137 if __name__ == '__main__':
138 unittest.main()

Subscribers

People subscribed via source and target branches