Merge lp:~niedbalski/python-jujuclient/lp-1456332 into lp:python-jujuclient
- lp-1456332
- Merge into trunk
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 | ||||
Related bugs: |
|
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.
Commit message
Description of the change
This patch fixes LP: #1456332
Tim Van Steenburgh (tvansteenburgh) wrote : Posted in a previous version of this proposal | # |
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,
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:/
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.
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.
Kapil Thangavelu (hazmat) wrote : | # |
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:/
>
> For more details, see:
>
> https:/
>
> 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 = [machines]
> + rpc_dict.
> +
> + if services:
> + if not isinstance(
> + services = [services]
> + rpc_dict.
> +
> + if units:
> + if not isinstance(units, (list, tuple)):
> + units = [units]
> + rpc_dict.
> +
> + return self._rpc(rpc_dict)
>
> # Machine ops
> def add_machine(self, series="", constraints=None,
>
> === modified file 'test_jujuclien
> --- 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.
> combo'
> )
>
> +
> class KeyManagerTest(
> def...
Felipe Reyes (freyes) wrote : | # |
Thanks Jorge, LGTM.
Tim Van Steenburgh (tvansteenburgh) 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).
> 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:/
> >
> > For more details, see:
> >
> > https:/
> jujuclient/
> >
> > 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 = [machines]
> > + rpc_dict.
> > +
> > + if services:
> > + if not isinstance(
> > + services = [services]
> > + rpc_dict.
> > +
> > + if units:
> > + if not isinstance(units, (list, tuple)):
> > + units = [units]
> > + rpc_dict.
> > +
> > + return self._rpc(rpc_dict)
> >
> > # Machine ops
> > def add_machine(self, s...
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.
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
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() |
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.