Merge lp:~jimbaker/pyjuju/rel-mgmt-endpoint-factory into lp:pyjuju
- rel-mgmt-endpoint-factory
- Merge into trunk
Proposed by
Jim Baker
Status: | Merged |
---|---|
Approved by: | Gustavo Niemeyer |
Approved revision: | 110 |
Merged at revision: | 107 |
Proposed branch: | lp:~jimbaker/pyjuju/rel-mgmt-endpoint-factory |
Merge into: | lp:pyjuju |
Diff against target: |
523 lines (+332/-26) 11 files modified
ensemble/formula/metadata.py (+108/-2) ensemble/formula/tests/repository/mysql/metadata.yaml (+7/-0) ensemble/formula/tests/repository/riak/metadata.yaml (+13/-0) ensemble/formula/tests/repository/wordpress/metadata.yaml (+21/-0) ensemble/formula/tests/test_metadata.py (+86/-4) ensemble/state/endpoint.py (+10/-11) ensemble/state/relation.py (+1/-0) ensemble/state/service.py (+37/-1) ensemble/state/tests/test_endpoint.py (+8/-7) ensemble/state/tests/test_relation.py (+1/-1) ensemble/state/tests/test_service.py (+40/-0) |
To merge this branch: | bzr merge lp:~jimbaker/pyjuju/rel-mgmt-endpoint-factory |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gustavo Niemeyer | Approve | ||
Review via email: mp+41823@code.launchpad.net |
Commit message
Description of the change
Adds ensemble.
This branch is stacked on metadata-relations, which it relies on for testing with respect to formulas.
To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote : | # |
Oh, please note that the other branch this is based on has a minor comment as well.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'ensemble/formula/metadata.py' |
2 | --- ensemble/formula/metadata.py 2010-10-29 19:55:50 +0000 |
3 | +++ ensemble/formula/metadata.py 2010-11-25 05:51:59 +0000 |
4 | @@ -5,20 +5,111 @@ |
5 | from ensemble.errors import ( |
6 | EnsembleError, InvalidEnsembleHeaderValue, FileNotFound) |
7 | from ensemble.lib.schema import ( |
8 | - SchemaError, KeyDict, Constant, Int, UnicodeOrString) |
9 | + SchemaError, Bool, Constant, Dict, Int, |
10 | + KeyDict, List, OneOf, UnicodeOrString) |
11 | |
12 | |
13 | class MetaDataError(EnsembleError): |
14 | """Raised when an error in the info file of a formula is found.""" |
15 | |
16 | |
17 | +INTERFACE_SCHEMA = KeyDict({ |
18 | + "interface": UnicodeOrString("utf-8"), |
19 | + "limit": OneOf(Constant(None), Int()), |
20 | + "optional": Bool()}) |
21 | + |
22 | + |
23 | +class ReshapeInterfaceSpec(object): |
24 | + """Support reshaping of a YAML interface spec to list of interfaces. |
25 | + |
26 | + We need this class because our formula shorthand is difficult to |
27 | + work with (unfortunately). So we coerce shorthand and then store |
28 | + the desired format in ZK. |
29 | + |
30 | + Supports the following variants:: |
31 | + |
32 | + provides: |
33 | + server: riak |
34 | + admin: http |
35 | + foobar: |
36 | + interface: blah |
37 | + |
38 | + provides: |
39 | + server: |
40 | + interface: mysql |
41 | + limit: |
42 | + optional: false |
43 | + |
44 | + In all cases, this is converted to a fully specified |
45 | + representation, as seen in the mysql interface description above |
46 | + (in YAML format) |
47 | + """ |
48 | + |
49 | + value_converter = UnicodeOrString("utf-8") |
50 | + |
51 | + def __init__(self, limit): |
52 | + """Create relation interface reshaper. |
53 | + |
54 | + @limit: the limit for this relation. Used to provide defaults |
55 | + for a given kind of relation role (peer, provider, consumer) |
56 | + """ |
57 | + self.limit = limit |
58 | + |
59 | + def coerce_interface(self, interface_spec, path): |
60 | + """Coerce `interface_spec` from shorthand form. |
61 | + |
62 | + Helper method to support each of the variants, either the |
63 | + formula does not specify limit and optional, such as foobar in |
64 | + the above example; or the interface spec is just a string, |
65 | + such as the ``server: riak`` example. |
66 | + """ |
67 | + if not isinstance(interface_spec, dict): |
68 | + return { |
69 | + "interface": |
70 | + self.value_converter.coerce(interface_spec, path), |
71 | + "limit": self.limit, |
72 | + "optional": False} |
73 | + else: |
74 | + # Optional values are context-sensitive and/or have |
75 | + # defaults, which is different than what KeyDict can |
76 | + # readily support. So just do it here first, then |
77 | + # coerce. |
78 | + if "limit" not in interface_spec: |
79 | + interface_spec["limit"] = self.limit |
80 | + if "optional" not in interface_spec: |
81 | + interface_spec["optional"] = False |
82 | + return INTERFACE_SCHEMA.coerce(interface_spec, path) |
83 | + |
84 | + def coerce(self, value, path): |
85 | + """Converts an interface specification in shorthand form. |
86 | + |
87 | + @value: the value to be coerced |
88 | + @path: used in exception reporting""" |
89 | + if not isinstance(value, dict): |
90 | + raise SchemaError(path, "Invalid relation specification") |
91 | + |
92 | + return dict( |
93 | + (self.value_converter.coerce(K, path), |
94 | + self.coerce_interface(V, path)) |
95 | + for K, V in value.iteritems()) |
96 | + |
97 | + |
98 | +RELATION_SCHEMA = Dict( |
99 | + UnicodeOrString("utf-8"), INTERFACE_SCHEMA) |
100 | + |
101 | SCHEMA = KeyDict({ |
102 | "ensemble": Constant("formula"), |
103 | "name": UnicodeOrString("utf-8"), |
104 | "revision": Int(), |
105 | "summary": UnicodeOrString("utf-8"), |
106 | "description": UnicodeOrString("utf-8"), |
107 | - }) |
108 | + "peers": OneOf(RELATION_SCHEMA, |
109 | + ReshapeInterfaceSpec(limit=1)), |
110 | + "provides": OneOf(RELATION_SCHEMA, |
111 | + ReshapeInterfaceSpec(limit=None)), |
112 | + "requires": OneOf(RELATION_SCHEMA, |
113 | + ReshapeInterfaceSpec(limit=1)), |
114 | + }, optional=set(["provides", "requires", "peers"])) |
115 | |
116 | |
117 | class MetaData(object): |
118 | @@ -59,6 +150,21 @@ |
119 | """The formula description.""" |
120 | return self._data.get("description") |
121 | |
122 | + @property |
123 | + def provides(self): |
124 | + """The formula provides relations.""" |
125 | + return self._data.get("provides") |
126 | + |
127 | + @property |
128 | + def requires(self): |
129 | + """The formula requires relations.""" |
130 | + return self._data.get("requires") |
131 | + |
132 | + @property |
133 | + def peers(self): |
134 | + """The formula peers relations.""" |
135 | + return self._data.get("peers") |
136 | + |
137 | def get_serialization_data(self): |
138 | """Get internal dictionary representing the state of this instance. |
139 | |
140 | |
141 | === added directory 'ensemble/formula/tests/repository/mysql' |
142 | === added file 'ensemble/formula/tests/repository/mysql/metadata.yaml' |
143 | --- ensemble/formula/tests/repository/mysql/metadata.yaml 1970-01-01 00:00:00 +0000 |
144 | +++ ensemble/formula/tests/repository/mysql/metadata.yaml 2010-11-25 05:51:59 +0000 |
145 | @@ -0,0 +1,7 @@ |
146 | +ensemble: formula |
147 | +name: mysql |
148 | +revision: 1 |
149 | +summary: "Database engine" |
150 | +description: "A pretty popular database" |
151 | +provides: |
152 | + server: mysql |
153 | |
154 | === added directory 'ensemble/formula/tests/repository/riak' |
155 | === added file 'ensemble/formula/tests/repository/riak/metadata.yaml' |
156 | --- ensemble/formula/tests/repository/riak/metadata.yaml 1970-01-01 00:00:00 +0000 |
157 | +++ ensemble/formula/tests/repository/riak/metadata.yaml 2010-11-25 05:51:59 +0000 |
158 | @@ -0,0 +1,13 @@ |
159 | +ensemble: formula |
160 | +name: riak |
161 | +revision: 7 |
162 | +summary: "K/V storage engine" |
163 | +description: "Scalable K/V Store in Erlang with Clocks :-)" |
164 | +provides: |
165 | + endpoint: |
166 | + interface: http |
167 | + admin: |
168 | + interface: http |
169 | +peers: |
170 | + ring: |
171 | + interface: riak |
172 | |
173 | === added directory 'ensemble/formula/tests/repository/wordpress' |
174 | === added file 'ensemble/formula/tests/repository/wordpress/metadata.yaml' |
175 | --- ensemble/formula/tests/repository/wordpress/metadata.yaml 1970-01-01 00:00:00 +0000 |
176 | +++ ensemble/formula/tests/repository/wordpress/metadata.yaml 2010-11-25 05:51:59 +0000 |
177 | @@ -0,0 +1,21 @@ |
178 | +ensemble: formula |
179 | +name: wordpress |
180 | +revision: 3 |
181 | +summary: "Blog engine" |
182 | +description: "A pretty popular blog engine" |
183 | +provides: |
184 | + url: |
185 | + interface: http |
186 | + limit: |
187 | + optional: false |
188 | +requires: |
189 | + db: |
190 | + interface: mysql |
191 | + limit: 1 |
192 | + optional: false |
193 | + cache: |
194 | + interface: somecache |
195 | + limit: 2 |
196 | + optional: true |
197 | + |
198 | + |
199 | |
200 | === modified file 'ensemble/formula/tests/test_metadata.py' |
201 | --- ensemble/formula/tests/test_metadata.py 2010-10-29 19:55:50 +0000 |
202 | +++ ensemble/formula/tests/test_metadata.py 2010-11-25 05:51:59 +0000 |
203 | @@ -5,14 +5,15 @@ |
204 | import inspect |
205 | |
206 | from ensemble.lib.testing import TestCase |
207 | -from ensemble.formula.metadata import MetaData, MetaDataError |
208 | +from ensemble.formula.metadata import ( |
209 | + MetaData, MetaDataError, ReshapeInterfaceSpec, SchemaError) |
210 | from ensemble.errors import InvalidEnsembleHeaderValue, FileNotFound |
211 | from ensemble.formula import tests |
212 | |
213 | -sample_path = os.path.join( |
214 | +test_repository_path = os.path.join( |
215 | os.path.dirname(inspect.getabsfile(tests)), |
216 | - "repository", "dummy", "metadata.yaml") |
217 | - |
218 | + "repository") |
219 | +sample_path = os.path.join(test_repository_path, "dummy", "metadata.yaml") |
220 | sample_configuration = open(sample_path).read() |
221 | |
222 | |
223 | @@ -195,3 +196,84 @@ |
224 | self.assertRaises(AssertionError, self.metadata.get_formula_id, None) |
225 | self.assertRaises(AssertionError, self.metadata.get_formula_id, 1) |
226 | self.assertRaises(AssertionError, self.metadata.get_formula_id, "z:x") |
227 | + |
228 | + |
229 | +class ParseTest(TestCase): |
230 | + """Test the parsing of some well-known sample files""" |
231 | + |
232 | + def get_metadata(self, formula_name): |
233 | + metadata = MetaData(os.path.join( |
234 | + test_repository_path, formula_name, "metadata.yaml")) |
235 | + self.assertEqual(metadata.name, formula_name) |
236 | + return metadata |
237 | + |
238 | + def test_mysql_sample(self): |
239 | + metadata = self.get_metadata("mysql") |
240 | + self.assertEqual(metadata.peers, None) |
241 | + self.assertEqual( |
242 | + metadata.provides["server"], |
243 | + {"interface": "mysql", "limit": None, "optional": False}) |
244 | + self.assertEqual(metadata.requires, None) |
245 | + |
246 | + def test_riak_sample(self): |
247 | + metadata = self.get_metadata("riak") |
248 | + self.assertEqual( |
249 | + metadata.peers["ring"], |
250 | + {"interface": "riak", "limit": 1, "optional": False}) |
251 | + self.assertEqual( |
252 | + metadata.provides["endpoint"], |
253 | + {"interface": "http", "limit": None, "optional": False}) |
254 | + self.assertEqual( |
255 | + metadata.provides["admin"], |
256 | + {"interface": "http", "limit": None, "optional": False}) |
257 | + self.assertEqual(metadata.requires, None) |
258 | + |
259 | + def test_wordpress_sample(self): |
260 | + metadata = self.get_metadata("wordpress") |
261 | + self.assertEqual(metadata.peers, None) |
262 | + self.assertEqual( |
263 | + metadata.provides["url"], |
264 | + {"interface": "http", "limit": None, "optional": False}) |
265 | + self.assertEqual( |
266 | + metadata.requires["db"], |
267 | + {"interface": "mysql", "limit": 1, "optional": False}) |
268 | + self.assertEqual( |
269 | + metadata.requires["cache"], |
270 | + {"interface": "somecache", "limit": 2, "optional": True}) |
271 | + |
272 | + def test_reshape_interface_spec(self): |
273 | + provides_reshaper = ReshapeInterfaceSpec(limit=None) |
274 | + self.assertEqual( |
275 | + provides_reshaper.coerce({"admin": "http"}, ["provides"]), |
276 | + {"admin": {"interface": "http", "limit": None, "optional": False}}) |
277 | + self.assertEqual( |
278 | + provides_reshaper.coerce( |
279 | + {"admin": {"interface": "http"}}, ["provides"]), |
280 | + {"admin": {"interface": "http", "limit": None, "optional": False}}) |
281 | + self.assertEqual( |
282 | + provides_reshaper.coerce( |
283 | + {"foobar": {"interface": "http", "limit": 2}}, ["provides"]), |
284 | + {"foobar": {"interface": "http", "limit": 2, "optional": False}}) |
285 | + self.assertEqual( |
286 | + provides_reshaper.coerce( |
287 | + {"foobar": {"interface": "http", "optional": True}}, |
288 | + ["provides"]), |
289 | + {"foobar": {"interface": "http", "limit": None, "optional": True}}) |
290 | + self.assertRaises( |
291 | + SchemaError, |
292 | + provides_reshaper.coerce, {"admin": 42}, ["provides"]) |
293 | + self.assertRaises( |
294 | + SchemaError, |
295 | + provides_reshaper.coerce, |
296 | + {"admin": {"interface": "http", "optional": None}}, |
297 | + ["provides"]) |
298 | + self.assertRaises( |
299 | + SchemaError, |
300 | + provides_reshaper.coerce, |
301 | + {"admin": {"interface": "http", "limit": "none, really"}}, |
302 | + ["provides"]) |
303 | + |
304 | + consumer_reshaper = ReshapeInterfaceSpec(limit=1) # just test defaults |
305 | + self.assertEqual( |
306 | + consumer_reshaper.coerce({"admin": "http"}, ["consumes"]), |
307 | + {"admin": {"interface": "http", "limit": 1, "optional": False}}) |
308 | |
309 | === modified file 'ensemble/state/endpoint.py' |
310 | --- ensemble/state/endpoint.py 2010-11-24 21:16:25 +0000 |
311 | +++ ensemble/state/endpoint.py 2010-11-25 05:51:59 +0000 |
312 | @@ -23,20 +23,19 @@ |
313 | """Test whether the `other` endpoint may be used in a common relation. |
314 | |
315 | RelationEndpoints may be related if they share the same relation_type |
316 | - (which is called an "interface" in formulas) and one is a ``provider`` |
317 | - and the other is a ``consumer``; or if both endpoints have a |
318 | - relation_role of ``peer``. |
319 | - |
320 | + (which is called an "interface" in formulas) and one is a ``provides`` |
321 | + and the other is a ``requires``; or if both endpoints have a |
322 | + relation_role of ``peers``. |
323 | + |
324 | Raises a `TypeError` is `other` is not a `RelationEndpoint`. |
325 | """ |
326 | |
327 | if not isinstance(other, RelationEndpoint): |
328 | raise TypeError("Not a RelationEndpoint", other) |
329 | return (self.relation_type == other.relation_type and |
330 | - ((self.relation_role == "provider" and |
331 | - other.relation_role == "consumer") or |
332 | - (self.relation_role == "consumer" and |
333 | - other.relation_role == "provider") or |
334 | - (self.relation_role == "peer" and |
335 | - other.relation_role == "peer"))) |
336 | - |
337 | + ((self.relation_role == "provides" and |
338 | + other.relation_role == "requires") or |
339 | + (self.relation_role == "requires" and |
340 | + other.relation_role == "provides") or |
341 | + (self.relation_role == "peers" and |
342 | + other.relation_role == "peers"))) |
343 | |
344 | === modified file 'ensemble/state/relation.py' |
345 | --- ensemble/state/relation.py 2010-11-15 21:08:50 +0000 |
346 | +++ ensemble/state/relation.py 2010-11-25 05:51:59 +0000 |
347 | @@ -667,3 +667,4 @@ |
348 | """ |
349 | return [unit_id for unit_id in units \ |
350 | if unit_id != self._watcher_unit.internal_unit_id] |
351 | + |
352 | |
353 | === modified file 'ensemble/state/service.py' |
354 | --- ensemble/state/service.py 2010-11-18 23:54:49 +0000 |
355 | +++ ensemble/state/service.py 2010-11-25 05:51:59 +0000 |
356 | @@ -5,11 +5,13 @@ |
357 | from twisted.internet.defer import inlineCallbacks, returnValue |
358 | |
359 | from ensemble.state.agent import AgentStateMixin |
360 | +from ensemble.state.base import StateBase |
361 | +from ensemble.state.endpoint import RelationEndpoint |
362 | from ensemble.state.errors import ( |
363 | StateChanged, ServiceStateNotFound, ServiceUnitStateNotFound, |
364 | ServiceUnitStateMachineAlreadyAssigned, ServiceStateNameInUse, |
365 | BadServiceStateName) |
366 | -from ensemble.state.base import StateBase |
367 | +from ensemble.state.formula import FormulaStateManager |
368 | from ensemble.state.relation import ServiceRelationState |
369 | |
370 | |
371 | @@ -56,6 +58,40 @@ |
372 | raise ServiceStateNotFound(service_name) |
373 | returnValue(ServiceState(self._client, internal_id, service_name)) |
374 | |
375 | + @inlineCallbacks |
376 | + def get_relation_endpoints(self, descriptor): |
377 | + """Get all relation endpoints for `descriptor`. |
378 | + |
379 | + A `descriptor` is of the form <service name>[:<relation name>]. |
380 | + Returns an empty set if there are no endpoints matching |
381 | + the `descriptor`. |
382 | + """ |
383 | + try: |
384 | + query_service_name, query_relation_name = descriptor.split(":") |
385 | + except: |
386 | + query_service_name, query_relation_name = descriptor, None |
387 | + formula_state_manager = FormulaStateManager(self._client) |
388 | + service_state = yield self.get_service_state( |
389 | + query_service_name) |
390 | + formula_id = yield service_state.get_formula_id() |
391 | + formula_state = yield formula_state_manager.get_formula_state( |
392 | + formula_id) |
393 | + formula_metadata = yield formula_state.get_metadata() |
394 | + endpoints = set() |
395 | + for relation_role in ("peers", "provides", "requires"): |
396 | + relations = getattr(formula_metadata, relation_role) |
397 | + if relations: |
398 | + for relation_name, spec in relations.iteritems(): |
399 | + if (query_relation_name is None or |
400 | + query_relation_name == relation_name): |
401 | + endpoints.add(RelationEndpoint( |
402 | + service_name=query_service_name, |
403 | + relation_type=spec["interface"], |
404 | + relation_name=relation_name, |
405 | + relation_role=relation_role)) |
406 | + returnValue(endpoints) |
407 | + |
408 | + |
409 | |
410 | class ServiceState(StateBase): |
411 | """State of a service registered in an environment. |
412 | |
413 | === modified file 'ensemble/state/tests/test_endpoint.py' |
414 | --- ensemble/state/tests/test_endpoint.py 2010-11-24 21:25:58 +0000 |
415 | +++ ensemble/state/tests/test_endpoint.py 2010-11-25 05:51:59 +0000 |
416 | @@ -1,11 +1,12 @@ |
417 | from ensemble.lib.testing import TestCase |
418 | from ensemble.state.endpoint import RelationEndpoint |
419 | |
420 | + |
421 | class RelationEndpointTest(TestCase): |
422 | def test_may_relate_to(self): |
423 | - mysql_ep = RelationEndpoint("mysqldb", "mysql", "db", "provider") |
424 | - blog_ep = RelationEndpoint("blog", "mysql", "mysql", "consumer") |
425 | - pg_ep = RelationEndpoint("postgres", "postgres", "db", "provider") |
426 | + mysql_ep = RelationEndpoint("mysqldb", "mysql", "db", "provides") |
427 | + blog_ep = RelationEndpoint("blog", "mysql", "mysql", "requires") |
428 | + pg_ep = RelationEndpoint("postgres", "postgres", "db", "provides") |
429 | |
430 | self.assertRaises(TypeError, mysql_ep.may_relate_to, 42) |
431 | |
432 | @@ -20,15 +21,15 @@ |
433 | # antireflexive om relation_role - |
434 | # must be consumer AND provider or vice versa |
435 | self.assertFalse(blog_ep.may_relate_to( |
436 | - RelationEndpoint("foo", "mysql", "db", "consumer"))) |
437 | + RelationEndpoint("foo", "mysql", "db", "requires"))) |
438 | self.assertFalse(mysql_ep.may_relate_to( |
439 | - RelationEndpoint("foo", "mysql", "db", "provider"))) |
440 | + RelationEndpoint("foo", "mysql", "db", "provides"))) |
441 | |
442 | - # irreflexive for provider/consumer |
443 | + # irreflexive for provides/requires |
444 | self.assertFalse(mysql_ep.may_relate_to(mysql_ep)) |
445 | self.assertFalse(blog_ep.may_relate_to(blog_ep)) |
446 | self.assertFalse(pg_ep.may_relate_to(pg_ep)) |
447 | |
448 | # but reflexive for peers |
449 | - riak_ep = RelationEndpoint("riak", "riak", "riak", "peer") |
450 | + riak_ep = RelationEndpoint("riak", "riak", "riak", "peers") |
451 | self.assert_(riak_ep.may_relate_to(riak_ep)) |
452 | |
453 | === modified file 'ensemble/state/tests/test_relation.py' |
454 | --- ensemble/state/tests/test_relation.py 2010-11-16 22:37:44 +0000 |
455 | +++ ensemble/state/tests/test_relation.py 2010-11-25 05:51:59 +0000 |
456 | @@ -1,10 +1,10 @@ |
457 | import functools |
458 | + |
459 | import yaml |
460 | import zookeeper |
461 | |
462 | from twisted.internet.defer import ( |
463 | inlineCallbacks, returnValue, Deferred, fail, succeed) |
464 | - |
465 | from txzookeeper import ZookeeperClient |
466 | |
467 | from ensemble.state.formula import FormulaStateManager |
468 | |
469 | === modified file 'ensemble/state/tests/test_service.py' |
470 | --- ensemble/state/tests/test_service.py 2010-11-18 23:54:49 +0000 |
471 | +++ ensemble/state/tests/test_service.py 2010-11-25 05:51:59 +0000 |
472 | @@ -1,7 +1,12 @@ |
473 | +import os.path |
474 | + |
475 | import yaml |
476 | |
477 | from twisted.internet.defer import inlineCallbacks, Deferred, returnValue |
478 | |
479 | +from ensemble.formula.directory import FormulaDirectory |
480 | +from ensemble.formula.tests.test_metadata import test_repository_path |
481 | +from ensemble.state.endpoint import RelationEndpoint |
482 | from ensemble.state.formula import FormulaStateManager |
483 | from ensemble.state.service import ServiceStateManager |
484 | from ensemble.state.machine import MachineStateManager |
485 | @@ -624,3 +629,38 @@ |
486 | |
487 | self.assertEqual([r.internal_relation_id for r in new_relations], |
488 | [relation_state2.internal_id]) |
489 | + |
490 | + @inlineCallbacks |
491 | + def add_service_from_formula(self, service_name): |
492 | + formula_dir = FormulaDirectory(os.path.join( |
493 | + test_repository_path, service_name)) |
494 | + formula_state = yield self.formula_state_manager.add_formula_state( |
495 | + "namespace", formula_dir) |
496 | + service_state = yield self.service_state_manager.add_service_state( |
497 | + service_name, formula_state) |
498 | + returnValue(service_state) |
499 | + |
500 | + @inlineCallbacks |
501 | + def test_get_relation_endpoints(self): |
502 | + yield self.add_service_from_formula("wordpress") |
503 | + wp_endpoints = yield self.service_state_manager.get_relation_endpoints( |
504 | + "wordpress") |
505 | + self.assertEqual( |
506 | + wp_endpoints, |
507 | + set([RelationEndpoint("wordpress", "http", "url", "provides"), |
508 | + RelationEndpoint("wordpress", "mysql", "db", "requires"), |
509 | + RelationEndpoint("wordpress", "somecache", "cache", |
510 | + "requires")])) |
511 | + yield self.add_service_from_formula("riak") |
512 | + riak_endpoints = yield self.service_state_manager.get_relation_endpoints( |
513 | + "riak:ring") |
514 | + self.assertEqual( |
515 | + riak_endpoints, |
516 | + set([RelationEndpoint("riak", "riak", "ring", "peers")])) |
517 | + yield self.add_service("nosuch") # blank formula! |
518 | + nosuch_endpoints = yield self.service_state_manager.get_relation_endpoints( |
519 | + "nosuch") |
520 | + self.assertEqual(nosuch_endpoints, set()) |
521 | + nosuch_endpoints2 = yield self.service_state_manager.get_relation_endpoints( |
522 | + "nosuch:nonsense") |
523 | + self.assertEqual(nosuch_endpoints2, set()) |
Looks good overall, thanks Jim. +1, considering the following comments:
[1]
383 + try: split(" :")
384 + query_service_name, query_relation_name = descriptor.
385 + except:
386 + query_service_name, query_relation_name = descriptor, None
It's generally a bad idea to use bare excepts unless the intention is really to
catch *all* exceptions, which isn't the case in most situations (e.g. this would
get even NameErrors, for instance).
This alternative approach will avoid any of those issues:
tokens = descriptor. split(" :", 1)
if len(tokens) == 2: / else:
[2]
There are two terminology conflicts within Ensemble which would be good to get solved:
a) provides/requires vs. server/client as a role
b) interface vs. relation type
Can you please raise that conversation and fix the conflict using the consensus in
that conversation (or at least agree on how and when it should be fixed as part of
the discussion).