Merge lp:~rharding/python-jujuclient/adjust_constraint_handling into lp:~hazmat/python-jujuclient/trunk

Proposed by Richard Harding
Status: Needs review
Proposed branch: lp:~rharding/python-jujuclient/adjust_constraint_handling
Merge into: lp:~hazmat/python-jujuclient/trunk
Diff against target: 211 lines (+146/-7)
5 files modified
.bzrignore (+16/-0)
README.rst (+21/-1)
jujuclient.py (+29/-4)
setup.py (+4/-2)
tests/test_environment.py (+76/-0)
To merge this branch: bzr merge lp:~rharding/python-jujuclient/adjust_constraint_handling
Reviewer Review Type Date Requested Status
Kapil Thangavelu Disapprove
Review via email: mp+194176@code.launchpad.net

Commit message

Adjust constraint handling to drop unknown ones.

- Adjust tests and test runner along with README updates.

Description of the change

Adjust constraint handling to drop unknown ones.

- Setup a whitelist for constraints for now until we get better messaging from the client through the deployer setup.
- Update the parse_constraints helper to drop constraints that aren't valid and perform the type casting as it was.
- Update with a new tests directory and update setup.py to be able to run python setup.py test
- Update README with some beginner hacking information
- Move the function tests to a different filename and +x it so that nose would skip it during normal test runs. It's meant to be run manually with an environment
- add a .bzrignore file

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

Line 49 of the diff has the last two characters on the line transposed ("test.s").

Since we don't really use the type of the constraints unless it is "int", perhaps a boolean instead of "int"/"str" would be more direct.

On line 178 and line 212 of the diff you could use mock.assert_called_once_with(...) instead.

20. By Richard Harding

Update per review, remember to +x the test_functional file

21. By Richard Harding

Update per review correctly this time

Revision history for this message
Richard Harding (rharding) wrote :

> Line 49 of the diff has the last two characters on the line transposed
> ("test.s").
>
> Since we don't really use the type of the constraints unless it is "int",
> perhaps a boolean instead of "int"/"str" would be more direct.
>
> On line 178 and line 212 of the diff you could use
> mock.assert_called_once_with(...) instead.

Thanks for the review and comments. Updated.

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

Whitelisting and handling exceptions should be done at the application level not at the client level which is policy free. Ie this implementation already misses several supported constraints, and new ones come out all the time. It also silently strips invalid constraints with no reporting back.

review: Disapprove
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

talked with rick a bit more about the reason for this on irc which is transient fix for gui. capturing for posterity.

<hazmat> rick_h_, is there any expectation or error handling for deploys in gui?
<rick_h_> hazmat: no, because it's async we fire the deploy request off and errors don't get back right now. At least in our debugging why come bundles were failing to work yesterday
<hazmat> rick_h_, that sounds like a separate / additional issue with the guiserver deployer middleware.
<rick_h_> hazmat: +1 the guiserver bit int he deployer needs to communicate errors back to the gui server in the charm
<rick_h_> hazmat: right now, in env/gui.py it calls deploy and walks away
<hazmat> rick_h_, and a common way to communicate errors is exceptions.. so three separate issues rich exception passthrough in deployer, and handle exceptions at gui server, and ... implement error handling in the gui for bundles
<rick_h_> hazmat: yes, that's the fix we want to do. To move forward today the band-aid fix came up. If it's not ok, then we'll do something else. However, trying to get to release today/tomorrow.
<hazmat> rick_h_, the closest place to gui that's reasonable to do a hack is the guiserver deployer middleware
<rick_h_> hazmat: rgr, will move the band-aid there for now then and we'll be fixing coms/deployer as a library as follow ups post-release

Unmerged revisions

21. By Richard Harding

Update per review correctly this time

20. By Richard Harding

Update per review, remember to +x the test_functional file

19. By Richard Harding

Adjust test running, add constraint checking

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2013-11-06 16:53:14 +0000
4@@ -0,0 +1,16 @@
5+./bin/
6+./build/
7+./data/
8+./develop-eggs/
9+./download-cache/
10+./docs/_build
11+./eggs/
12+./include/
13+./lib/
14+./local/
15+./node_modules/
16+./parts/
17+.coverage
18+.noseids
19+*.egg
20+*.egg-info/
21
22=== modified file 'README.rst'
23--- README.rst 2013-07-12 13:50:05 +0000
24+++ README.rst 2013-11-06 16:53:14 +0000
25@@ -1,5 +1,5 @@
26 Juju Client
27------------
28+============
29
30 A simple synchronous python client for the juju-core/gojuju websocket api.
31
32@@ -31,3 +31,23 @@
33 for change_set in watcher:
34 print change_set
35
36+
37+
38+Hacking
39+---------
40+
41+There are two sets of tests. The first are the main software tests in the
42+``tests`` directory. These are run via the test command.
43+
44+::
45+
46+ python setup.py test
47+
48+Note: The above command will install test dependencies. Be aware that it's ok
49+to get an initial download of packages the first time you run test.s
50+
51+
52+There is also a functional set of tests in the ``test_functional.py`` file.
53+These are run manually and require a working environment setup. They are
54+executable so that the test runner will ignore then during normal test runs.
55+Please check both sets of tests when verifying your changes are good to go.
56
57=== modified file 'jujuclient.py'
58--- jujuclient.py 2013-10-29 01:00:08 +0000
59+++ jujuclient.py 2013-11-06 16:53:14 +0000
60@@ -228,6 +228,13 @@
61
62 class Environment(RPC):
63
64+ constraint_whitelist = {
65+ 'arch': {'type': 'str'},
66+ 'cpu-cores': {'type': 'int'},
67+ 'cpu-power': {'type': 'int'},
68+ 'mem': {'type': 'int'},
69+ }
70+
71 def __init__(self, endpoint, conn=None):
72 self.endpoint = endpoint
73 self._watches = []
74@@ -326,10 +333,28 @@
75 return r
76
77 def _prepare_constraints(self, constraints):
78- for k in ['cpu-cores', 'cpu-power', 'mem']:
79- if constraints.get(k):
80- constraints[k] = int(constraints[k])
81- return constraints
82+ """Help format constraints per the juju-core expectations
83+
84+ - Cast int values for constraints that must be of type int
85+
86+ XXX: Rick Harding 11/6/13
87+ Remove constraints we don't expect. Follow ups should throw a proper
88+ exception or return a proper error that users of the client can use
89+ to provide feedback to end users.
90+ """
91+ clean_constraints = {}
92+ for name, value in constraints.iteritems():
93+ if name not in self.constraint_whitelist:
94+ continue
95+
96+ # If it exists, check what type it needs to be so that it's
97+ # accepted by the Go backend.
98+ if self.constraint_whitelist[name]['type'] == 'int':
99+ clean_constraints[name] = int(constraints[name])
100+ else:
101+ clean_constraints[name] = constraints[name]
102+
103+ return clean_constraints
104
105 # Relations
106 def add_relation(self, endpoint_a, endpoint_b):
107
108=== modified file 'setup.py'
109--- setup.py 2013-10-29 01:00:08 +0000
110+++ setup.py 2013-11-06 16:53:14 +0000
111@@ -6,12 +6,14 @@
112
113 setup(
114 name="jujuclient",
115- version="0.13",
116+ version="0.14",
117 description="A juju-core/gojuju simple synchronous python api client.",
118 author="Kapil Thangavelu",
119 author_email="kapil.foss@gmail.com",
120- url="http://juju.ubuntu.com",
121+ url="https://launchpad.net/python-jujuclient",
122 install_requires=["websocket-client"],
123+ tests_require=["nose==1.3", "mock==1.0.1"],
124+ test_suite="nose.collector",
125 classifiers=[
126 "Development Status :: 2 - Pre-Alpha",
127 "Programming Language :: Python",
128
129=== renamed file 'test_jujuclient.py' => 'test_functional.py' (properties changed: -x to +x)
130=== added directory 'tests'
131=== added file 'tests/__init__.py'
132=== added file 'tests/test_environment.py'
133--- tests/test_environment.py 1970-01-01 00:00:00 +0000
134+++ tests/test_environment.py 2013-11-06 16:53:14 +0000
135@@ -0,0 +1,76 @@
136+from mock import patch
137+from unittest import TestCase
138+
139+from jujuclient import Environment
140+
141+
142+class TestEnvironment(TestCase):
143+
144+ def test_non_whitelist_constraints_drop(self):
145+ """Any constraint not in the whitelist should be dropped."""
146+ # The endpoint and connection are required for the Environment, but
147+ # the constraints code doesn't deal with it. Just fake it till you
148+ # make it.
149+ endpoint = None
150+ connection = {}
151+ test_constraints = {
152+ 'cpu': '3',
153+ 'power': 10,
154+ 'cpu-power': 1000,
155+ 'mem': '8000',
156+ 'arch': 'i386',
157+ }
158+
159+ called_with = {
160+ "Type": "Client",
161+ "Request": "ServiceDeploy",
162+ "Params": {
163+ "ServiceName": 'test-service',
164+ "CharmURL": 'url',
165+ "NumUnits": 1,
166+ "Config": {},
167+ "Constraints": {
168+ 'arch': 'i386',
169+ 'cpu-power': 1000,
170+ 'mem': 8000
171+ }
172+ }
173+ }
174+
175+ with patch('jujuclient.Environment._rpc') as mock_rpc:
176+ env = Environment(endpoint, connection)
177+ env.deploy('test-service', 'url', constraints=test_constraints)
178+ mock_rpc.assert_called_once_with(called_with)
179+
180+ def test_constraints_cast_to_proper_type(self):
181+ """Some constraints must be an int value for Go to accept them."""
182+ endpoint = None
183+ connection = {}
184+ test_constraints = {
185+ 'cpu-cores': '4',
186+ 'cpu-power': '1000',
187+ 'mem': '8000',
188+ 'arch': 'i386',
189+ }
190+
191+ called_with = {
192+ "Type": "Client",
193+ "Request": "ServiceDeploy",
194+ "Params": {
195+ "ServiceName": 'test-service',
196+ "CharmURL": 'url',
197+ "NumUnits": 1,
198+ "Config": {},
199+ "Constraints": {
200+ 'arch': 'i386',
201+ 'cpu-cores': 4,
202+ 'cpu-power': 1000,
203+ 'mem': 8000
204+ }
205+ }
206+ }
207+
208+ with patch('jujuclient.Environment._rpc') as mock_rpc:
209+ env = Environment(endpoint, connection)
210+ env.deploy('test-service', 'url', constraints=test_constraints)
211+ mock_rpc.assert_called_once_with(called_with)

Subscribers

People subscribed via source and target branches