Merge lp:~jimbaker/pyjuju/rel-mgmt-endpoint-join into lp:pyjuju

Proposed by Jim Baker
Status: Merged
Approved by: Gustavo Niemeyer
Approved revision: 119
Merged at revision: 112
Proposed branch: lp:~jimbaker/pyjuju/rel-mgmt-endpoint-join
Merge into: lp:pyjuju
Diff against target: 355 lines (+212/-51)
5 files modified
ensemble/formula/tests/repository/varnish/metadata.yaml (+7/-0)
ensemble/formula/tests/repository/wordpress/metadata.yaml (+1/-1)
ensemble/formula/tests/test_metadata.py (+3/-3)
ensemble/state/service.py (+16/-4)
ensemble/state/tests/test_service.py (+185/-43)
To merge this branch: bzr merge lp:~jimbaker/pyjuju/rel-mgmt-endpoint-join
Reviewer Review Type Date Requested Status
Gustavo Niemeyer Approve
Review via email: mp+42301@code.launchpad.net

Description of the change

Adds support for ServiceStateManager.join_descriptors. The actual heavy lifting is done by an iterator algebra implementation of the nested loops join, part of relational algebra recipe, which I authored some years ago (http://code.activestate.com/recipes/492216-iterator-algebra-implementations-of-relational-joi/), and making a staticmethod version may_relate so that it can be used as a join operator (for a theta join, see http://en.wikipedia.org/wiki/Relational_algebra#.CE.B8-join_and_equijoin). The relation algebra code is slightly modified by adding a pair function plus some doc updates.

It's good to see that contrary to my original feeling about the possible utility, this recipe actually implements useful functionality and is not just for demoing these ideas in executable pseudocode.

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Thanks Jim. A couple of comments:

[1]

Given the specific context we're handling, what's the advantage of the
approach used when compared to the straightforward option:

    result = []
    relation_set1 = yield self.get_relation_endpoints(descriptor1)
    relation_set2 = yield self.get_rleation_endpoitns(descriptor2)
    for relation1 in relation_set1:
        for relation2 in relation_set2:
            if relation1.may_relate_to(relation2):
                result.append((relation1, relation2))
    return result

?

[2]

Individual tests which verify a specific intended aspect, including a nice
docstring which explain its intended outcome, and perhaps some additional detail
about why it might be wrong (in some situations), are generally more
understandable and easier to maintain in the future.

Please check out a few other test classes, such as the topology tests, to get an
idea about the way we've generally approached the problem.

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

One question i had looking at this api is if it would pick up peer relations, or if those where intended to be handled separately.

Revision history for this message
Jim Baker (jimbaker) wrote :

On Wed, Dec 1, 2010 at 5:48 PM, Kapil Thangavelu <
<email address hidden>> wrote:

> One question i had looking at this api is if it would pick up peer
> relations, or if those where intended to be handled separately.
>
>
Yes, it should handle peer relations, based on the fundamental method,
RelationEndpoint.may_relate_to supports that. I don't have any tests yet for
this case, however, so I will fix that.

>
> --
>
> https://code.launchpad.net/~jimbaker/ensemble/rel-mgmt-endpoint-join/+merge/42301
> You are the owner of lp:~jimbaker/ensemble/rel-mgmt-endpoint-join.
>

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Jim, can you please provide some feedback on the above review as well? We generally try to
answer reviews on a point-by-point basis, both to personally track that something was done
about them, and also to provide some feedback to the reviewer and to the rest of the team
which is following the reviews.

review: Needs Information
Revision history for this message
Jim Baker (jimbaker) wrote :

On Wed, Dec 1, 2010 at 8:56 AM, Gustavo Niemeyer <email address hidden>wrote:

> Review: Needs Information
> Thanks Jim. A couple of comments:
>
> [1]
>
> Given the specific context we're handling, what's the advantage of the
> approach used when compared to the straightforward option:
>
> result = []
> relation_set1 = yield self.get_relation_endpoints(descriptor1)
> relation_set2 = yield self.get_rleation_endpoitns(descriptor2)
> for relation1 in relation_set1:
> for relation2 in relation_set2:
> if relation1.may_relate_to(relation2):
> result.append((relation1, relation2))
> return result
>
>
Both implement the nested loops join. The advantage of the recipe I wrote is
that it's designed for composability. Think about the idea of linq. At this
time, that's not in the picture, and usually isn't until we have more such
queries, or their variants (only care if at least one result exists, for
example).

So it probably makes more sense to just write it as you describe.

>
> [2]
>
> Individual tests which verify a specific intended aspect, including a nice
> docstring which explain its intended outcome, and perhaps some additional
> detail
> about why it might be wrong (in some situations), are generally more
> understandable and easier to maintain in the future.
>

Agreed, I've changed to adopt this practice in my latest branch on batch
topology changes.

Revision history for this message
Jim Baker (jimbaker) wrote :

On Thu, Dec 2, 2010 at 4:41 AM, Gustavo Niemeyer <email address hidden>wrote:

> Review: Needs Information
> Jim, can you please provide some feedback on the above review as well? We
> generally try to
> answer reviews on a point-by-point basis, both to personally track that
> something was done
> about them, and also to provide some feedback to the reviewer and to the
> rest of the team
> which is following the reviews.
>

Sorry, I had gotten in the habit of making these responses when I had the
branch pushed in response to the feedback. But in this case certainly, and
probably others, it would be better to immediately respond.

110. By Jim Baker

Removed my relationalgebra library (sniff)

111. By Jim Baker

Removed no longer used RelationEndpoint.may_relate (used as a theta op)

112. By Jim Baker

Removed no longer used RelationEndpoint.may_relate (used as a theta op)

113. By Jim Baker

Split tests

114. By Jim Baker

Some docstring edits

Revision history for this message
Jim Baker (jimbaker) wrote :

On Wed, Dec 1, 2010 at 8:47 PM, Jim Baker <email address hidden> wrote:

> On Wed, Dec 1, 2010 at 5:48 PM, Kapil Thangavelu <
> <email address hidden>> wrote:
>
> > One question i had looking at this api is if it would pick up peer
> > relations, or if those where intended to be handled separately.
> >
> >
> Yes, it should handle peer relations, based on the fundamental method,
> RelationEndpoint.may_relate_to supports that. I don't have any tests yet
> for
> this case, however, so I will fix that.
>
>
Added tests for peer relations.

>
>

Revision history for this message
Jim Baker (jimbaker) wrote :

On Thu, Dec 2, 2010 at 9:42 AM, Jim Baker <email address hidden> wrote:

> On Wed, Dec 1, 2010 at 8:56 AM, Gustavo Niemeyer <email address hidden>wrote:
>
>> Review: Needs Information
>> Thanks Jim. A couple of comments:
>>
>> [1]
>>
>> Given the specific context we're handling, what's the advantage of the
>> approach used when compared to the straightforward option:
>>
>> result = []
>> relation_set1 = yield self.get_relation_endpoints(descriptor1)
>> relation_set2 = yield self.get_rleation_endpoitns(descriptor2)
>> for relation1 in relation_set1:
>> for relation2 in relation_set2:
>> if relation1.may_relate_to(relation2):
>> result.append((relation1, relation2))
>> return result
>>
>>
> Both implement the nested loops join. The advantage of the recipe I wrote
> is that it's designed for composability. Think about the idea of linq. At
> this time, that's not in the picture, and usually isn't until we have more
> such queries, or their variants (only care if at least one result exists,
> for example).
>
> So it probably makes more sense to just write it as you describe.
>

In the latest version of the branch I pushed.

>
>
>>
>> [2]
>>
>> Individual tests which verify a specific intended aspect, including a nice
>> docstring which explain its intended outcome, and perhaps some additional
>> detail
>> about why it might be wrong (in some situations), are generally more
>> understandable and easier to maintain in the future.
>>
>
> Agreed, I've changed to adopt this practice in my latest branch on batch
> topology changes.
>
>

I have split these tests in the latest version of the branch.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Thanks for the refactoring Jim. A few additional points:

[3]

177 + """Test descriptor of the form ``<service name:relation name>``"""
178 + yield self.add_service_from_formula("wordpress")
179 + yield self.add_service_from_formula("mysql")

The testing there looks a bit fragile, and not really covering the intended semantics
of matching the descriptor. Note that all of the results return exactly the same
thing, and that same thing is composed by the only possible relation which can match
for the given formulas.

[4]

Along the same lines, it doesn't look like any test returns more than a single pair,
with or without descriptors, so being able to see more than a single potential relation
isn't being covered.

[5]

10 +class BadRelation(StateError):
11 + """Descriptors describe an invalid relation.

That's unused and untested.

review: Needs Fixing
115. By Jim Baker

Merge trunk

116. By Jim Baker

Merge trunk

117. By Jim Baker

Rework tests for joining descriptors

118. By Jim Baker

Removed obsolete BadRelation exception

119. By Jim Baker

PEP8

Revision history for this message
Jim Baker (jimbaker) wrote :

Ready for review. Hopefully this is getting close!

On Tue, Dec 7, 2010 at 1:10 PM, Gustavo Niemeyer <email address hidden>wrote:

> Review: Needs Fixing
> Thanks for the refactoring Jim. A few additional points:
>
> [3]
>
> 177 + """Test descriptor of the form ``<service name:relation
> name>``"""
> 178 + yield self.add_service_from_formula("wordpress")
> 179 + yield self.add_service_from_formula("mysql")
>
> The testing there looks a bit fragile, and not really covering the intended
> semantics
> of matching the descriptor. Note that all of the results return exactly
> the same
> thing, and that same thing is composed by the only possible relation which
> can match
> for the given formulas.
>

I reworked the testing to show more possible scenarios.

>
> [4]
>
> Along the same lines, it doesn't look like any test returns more than a
> single pair,
> with or without descriptors, so being able to see more than a single
> potential relation
> isn't being covered.
>

The revised tests demonstrate that If the relation name if not specified,
multiple endpoint pairs may be returned.

>
> [5]
>
> 10 +class BadRelation(StateError):
> 11 + """Descriptors describe an invalid relation.
>
> That's unused and untested.
>

This should have not been put in this branch, so I have removed it. It's
from an earlier design where we are not returning a set of endpoint pairs,
possibly empty, possibly more than one, but instead returned this exception.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Thanks for the fixes. Looks good, +1!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'ensemble/formula/tests/repository/varnish'
2=== added file 'ensemble/formula/tests/repository/varnish/metadata.yaml'
3--- ensemble/formula/tests/repository/varnish/metadata.yaml 1970-01-01 00:00:00 +0000
4+++ ensemble/formula/tests/repository/varnish/metadata.yaml 2010-12-10 05:02:12 +0000
5@@ -0,0 +1,7 @@
6+ensemble: formula
7+name: varnish
8+revision: 1
9+summary: "Database engine"
10+description: "Another popular database"
11+provides:
12+ webcache: varnish
13
14=== modified file 'ensemble/formula/tests/repository/wordpress/metadata.yaml'
15--- ensemble/formula/tests/repository/wordpress/metadata.yaml 2010-11-18 23:54:56 +0000
16+++ ensemble/formula/tests/repository/wordpress/metadata.yaml 2010-12-10 05:02:12 +0000
17@@ -14,7 +14,7 @@
18 limit: 1
19 optional: false
20 cache:
21- interface: somecache
22+ interface: varnish
23 limit: 2
24 optional: true
25
26
27=== modified file 'ensemble/formula/tests/test_metadata.py'
28--- ensemble/formula/tests/test_metadata.py 2010-11-29 17:22:27 +0000
29+++ ensemble/formula/tests/test_metadata.py 2010-12-10 05:02:12 +0000
30@@ -202,7 +202,7 @@
31 """Test the parsing of some well-known sample files"""
32
33 def get_metadata(self, formula_name):
34- """Get the associated metadata for a given formula, like ``wordpress``"""
35+ """Get the associated metadata for a given formula, eg ``wordpress``"""
36 metadata = MetaData(os.path.join(
37 test_repository_path, formula_name, "metadata.yaml"))
38 self.assertEqual(metadata.name, formula_name)
39@@ -214,7 +214,7 @@
40 Such relations are defined as follows::
41 provides:
42 server: mysql
43- """
44+ """
45 metadata = self.get_metadata("mysql")
46 self.assertEqual(metadata.peers, None)
47 self.assertEqual(
48@@ -248,7 +248,7 @@
49 {"interface": "mysql", "limit": 1, "optional": False})
50 self.assertEqual(
51 metadata.requires["cache"],
52- {"interface": "somecache", "limit": 2, "optional": True})
53+ {"interface": "varnish", "limit": 2, "optional": True})
54
55 def test_reshape_interface_spec(self):
56 """Test rewriting of a given interface specification into long form.
57
58=== modified file 'ensemble/state/service.py'
59--- ensemble/state/service.py 2010-11-29 19:21:18 +0000
60+++ ensemble/state/service.py 2010-12-10 05:02:12 +0000
61@@ -66,15 +66,16 @@
62 Returns the following:
63
64 - Returns a set of matching endpoints, drawn from the peers,
65- provides, and requires interfaces. The empty set if there
66- are no endpoints matching the `descriptor`.
67+ provides, and requires interfaces. The empty set is
68+ returned if there are no endpoints matching the
69+ `descriptor`.
70
71- - Raises a BadDescriptor exception if the descriptor cannot
72+ - Raises a `BadDescriptor` exception if `descriptor` cannot
73 be parsed.
74 """
75 tokens = descriptor.split(":")
76 if len(tokens) == 1 and bool(tokens[0]):
77- query_service_name, query_relation_name = descriptor, None
78+ query_service_name, query_relation_name = descriptor, None
79 elif len(tokens) == 2 and bool(tokens[0]) and bool(tokens[1]):
80 query_service_name, query_relation_name = tokens
81 else:
82@@ -100,6 +101,17 @@
83 relation_role=relation_role))
84 returnValue(endpoints)
85
86+ @inlineCallbacks
87+ def join_descriptors(self, descriptor1, descriptor2):
88+ """Return a list of pairs of RelationEndpoints joining descriptors."""
89+ result = []
90+ relation_set1 = yield self.get_relation_endpoints(descriptor1)
91+ relation_set2 = yield self.get_relation_endpoints(descriptor2)
92+ for relation1 in relation_set1:
93+ for relation2 in relation_set2:
94+ if relation1.may_relate_to(relation2):
95+ result.append((relation1, relation2))
96+ returnValue(result)
97
98 class ServiceState(StateBase):
99 """State of a service registered in an environment.
100
101=== modified file 'ensemble/state/tests/test_service.py'
102--- ensemble/state/tests/test_service.py 2010-11-29 19:21:18 +0000
103+++ ensemble/state/tests/test_service.py 2010-12-10 05:02:12 +0000
104@@ -29,7 +29,7 @@
105 self.machine_state_manager = MachineStateManager(self.client)
106 self.formula_state = yield self.formula_state_manager.add_formula_state(
107 "namespace", self.formula)
108- self.relation_state_manager = None
109+ self.relation_state_manager = RelationStateManager(self.client)
110
111 @inlineCallbacks
112 def add_service(self, name):
113@@ -39,9 +39,6 @@
114
115 @inlineCallbacks
116 def add_relation(self, relation_type, *services):
117- if not self.relation_state_manager:
118- self.relation_state_manager = RelationStateManager(self.client)
119-
120 relation_state = yield self.relation_state_manager.add_relation_state(
121 relation_type)
122 for service_meta in services:
123@@ -641,48 +638,193 @@
124 returnValue(service_state)
125
126 @inlineCallbacks
127- def test_get_relation_endpoints(self):
128- """Test getting endpoints from their descriptors."""
129-
130- # descriptor of the form ``<service name>``
131+ def test_get_relation_endpoints_service_name(self):
132+ """Test getting endpoints with descriptor ``<service name>``"""
133 yield self.add_service_from_formula("wordpress")
134- wp_endpoints = yield self.service_state_manager.get_relation_endpoints(
135- "wordpress")
136 self.assertEqual(
137- wp_endpoints,
138+ (yield self.service_state_manager.get_relation_endpoints(
139+ "wordpress")),
140 set([RelationEndpoint("wordpress", "http", "url", "provides"),
141 RelationEndpoint("wordpress", "mysql", "db", "requires"),
142- RelationEndpoint("wordpress", "somecache", "cache",
143- "requires")]))
144-
145- # descriptor of the form ``<service name:relation name>``
146- yield self.add_service_from_formula("riak")
147- riak_endpoints = yield self.service_state_manager.get_relation_endpoints(
148- "riak:ring")
149- self.assertEqual(
150- riak_endpoints,
151+ RelationEndpoint(
152+ "wordpress", "varnish", "cache", "requires")]))
153+ yield self.add_service_from_formula("riak")
154+ self.assertEqual(
155+ (yield self.service_state_manager.get_relation_endpoints(
156+ "riak")),
157+ set([RelationEndpoint("riak", "http", "admin", "provides"),
158+ RelationEndpoint("riak", "http", "endpoint", "provides"),
159+ RelationEndpoint("riak", "riak", "ring", "peers")]))
160+
161+ @inlineCallbacks
162+ def test_get_relation_endpoints_service_name_relation_name(self):
163+ """Test getting endpoints with ``<service name:relation name>``"""
164+ yield self.add_service_from_formula("wordpress")
165+ self.assertEqual(
166+ (yield self.service_state_manager.get_relation_endpoints(
167+ "wordpress:url")),
168+ set([RelationEndpoint("wordpress", "http", "url", "provides")]))
169+ self.assertEqual(
170+ (yield self.service_state_manager.get_relation_endpoints(
171+ "wordpress:db")),
172+ set([RelationEndpoint("wordpress", "mysql", "db", "requires")]))
173+ self.assertEqual(
174+ (yield self.service_state_manager.get_relation_endpoints(
175+ "wordpress:cache")),
176+ set([RelationEndpoint(
177+ "wordpress", "varnish", "cache", "requires")]))
178+ yield self.add_service_from_formula("riak")
179+ self.assertEqual(
180+ (yield self.service_state_manager.get_relation_endpoints(
181+ "riak:ring")),
182 set([RelationEndpoint("riak", "riak", "ring", "peers")]))
183
184- # descriptor for a nonexistent formula
185- yield self.add_service("nosuch")
186- nosuch_endpoints = yield self.service_state_manager.get_relation_endpoints(
187- "nosuch")
188- self.assertEqual(nosuch_endpoints, set())
189- nosuch_endpoints2 = yield self.service_state_manager.get_relation_endpoints(
190- "nosuch:nonsense")
191- self.assertEqual(nosuch_endpoints2, set())
192-
193- # bad descriptors
194- try:
195- _ = yield self.service_state_manager.get_relation_endpoints("a:b:c")
196- except BadDescriptor:
197- pass
198- else:
199- self.fail("Did not raise BadDescriptor")
200- try:
201- _ = yield self.service_state_manager.get_relation_endpoints("")
202- except BadDescriptor:
203- pass
204- else:
205- self.fail("Did not raise BadDescriptor")
206-
207+ @inlineCallbacks
208+ def test_descriptor_for_services_without_formulas(self):
209+ """Test with services that have no corresponding formulas defined"""
210+ yield self.add_service("noformula")
211+ self.assertEqual(
212+ (yield self.service_state_manager.get_relation_endpoints(
213+ "noformula")),
214+ set())
215+ self.assertEqual(
216+ (yield self.service_state_manager.get_relation_endpoints(
217+ "noformula:nonsense")),
218+ set())
219+
220+ @inlineCallbacks
221+ def test_descriptor_for_missing_service(self):
222+ """Test with a service that is not in the topology"""
223+ yield self.assertFailure(
224+ self.service_state_manager.get_relation_endpoints(
225+ "notadded"),
226+ ServiceStateNotFound)
227+
228+ @inlineCallbacks
229+ def test_bad_descriptors(self):
230+ """Test that the descriptors meet the minimum naming standards"""
231+ yield self.assertFailure(
232+ self.service_state_manager.get_relation_endpoints("a:b:c"),
233+ BadDescriptor)
234+ yield self.assertFailure(
235+ self.service_state_manager.get_relation_endpoints(""),
236+ BadDescriptor)
237+
238+ @inlineCallbacks
239+ def test_join_descriptors_service_name(self):
240+ """Test descriptor of the form ``<service name>`"""
241+ yield self.add_service_from_formula("wordpress")
242+ yield self.add_service_from_formula("mysql")
243+ self.assertEqual(
244+ (yield self.service_state_manager.join_descriptors(
245+ "wordpress", "mysql")),
246+ [(RelationEndpoint("wordpress", "mysql", "db", "requires"),
247+ RelationEndpoint("mysql", "mysql", "server", "provides"))])
248+ # symmetric - note the pair has rotated
249+ self.assertEqual(
250+ (yield self.service_state_manager.join_descriptors(
251+ "mysql", "wordpress")),
252+ [(RelationEndpoint("mysql", "mysql", "server", "provides"),
253+ RelationEndpoint("wordpress", "mysql", "db", "requires"))])
254+ yield self.add_service_from_formula("varnish")
255+ self.assertEqual(
256+ (yield self.service_state_manager.join_descriptors(
257+ "wordpress", "varnish")),
258+ [(RelationEndpoint("wordpress", "varnish", "cache", "requires"),
259+ RelationEndpoint("varnish", "varnish", "webcache", "provides"))])
260+
261+ @inlineCallbacks
262+ def test_join_descriptors_service_name_relation_name(self):
263+ """Test joining descriptors ``<service name:relation name>``"""
264+ yield self.add_service_from_formula("wordpress")
265+ yield self.add_service_from_formula("mysql")
266+ self.assertEqual(
267+ (yield self.service_state_manager.join_descriptors(
268+ "wordpress:db", "mysql")),
269+ [(RelationEndpoint("wordpress", "mysql", "db", "requires"),
270+ RelationEndpoint("mysql", "mysql", "server", "provides"))])
271+ self.assertEqual(
272+ (yield self.service_state_manager.join_descriptors(
273+ "mysql:server", "wordpress")),
274+ [(RelationEndpoint("mysql", "mysql", "server", "provides"),
275+ RelationEndpoint("wordpress", "mysql", "db", "requires"))])
276+ self.assertEqual(
277+ (yield self.service_state_manager.join_descriptors(
278+ "mysql:server", "wordpress:db")),
279+ [(RelationEndpoint("mysql", "mysql", "server", "provides"),
280+ RelationEndpoint("wordpress", "mysql", "db", "requires"))])
281+
282+ yield self.add_service_from_formula("varnish")
283+ self.assertEqual(
284+ (yield self.service_state_manager.join_descriptors(
285+ "wordpress:cache", "varnish")),
286+ [(RelationEndpoint("wordpress", "varnish", "cache", "requires"),
287+ RelationEndpoint("varnish", "varnish", "webcache", "provides"))])
288+ self.assertEqual(
289+ (yield self.service_state_manager.join_descriptors(
290+ "wordpress:cache", "varnish:webcache")),
291+ [(RelationEndpoint("wordpress", "varnish", "cache", "requires"),
292+ RelationEndpoint("varnish", "varnish", "webcache", "provides"))])
293+
294+ @inlineCallbacks
295+ def test_join_peer_descriptors(self):
296+ """Test joining of peer relation descriptors"""
297+ yield self.add_service_from_formula("riak")
298+ self.assertEqual(
299+ (yield self.service_state_manager.join_descriptors(
300+ "riak", "riak")),
301+ [(RelationEndpoint("riak", "riak", "ring", "peers"),
302+ RelationEndpoint("riak", "riak", "ring", "peers"))])
303+ self.assertEqual(
304+ (yield self.service_state_manager.join_descriptors(
305+ "riak:ring", "riak")),
306+ [(RelationEndpoint("riak", "riak", "ring", "peers"),
307+ RelationEndpoint("riak", "riak", "ring", "peers"))])
308+ self.assertEqual(
309+ (yield self.service_state_manager.join_descriptors(
310+ "riak:ring", "riak:ring")),
311+ [(RelationEndpoint("riak", "riak", "ring", "peers"),
312+ RelationEndpoint("riak", "riak", "ring", "peers"))])
313+ self.assertEqual(
314+ (yield self.service_state_manager.join_descriptors(
315+ "riak:no-ring", "riak:ring")),
316+ [])
317+
318+ @inlineCallbacks
319+ def test_join_descriptors_no_common_relation(self):
320+ """Test joining of descriptors that do not share a relation"""
321+ yield self.add_service_from_formula("mysql")
322+ yield self.add_service_from_formula("riak")
323+ yield self.add_service_from_formula("wordpress")
324+ yield self.add_service_from_formula("varnish")
325+ self.assertEqual(
326+ (yield self.service_state_manager.join_descriptors(
327+ "mysql", "riak")),
328+ [])
329+ self.assertEqual(
330+ (yield self.service_state_manager.join_descriptors(
331+ "mysql:server", "riak:ring")),
332+ [])
333+ self.assertEqual(
334+ (yield self.service_state_manager.join_descriptors(
335+ "varnish", "mysql")),
336+ [])
337+ self.assertEqual(
338+ (yield self.service_state_manager.join_descriptors(
339+ "riak:ring", "riak:admin")),
340+ [])
341+ self.assertEqual(
342+ (yield self.service_state_manager.join_descriptors(
343+ "riak", "wordpress")),
344+ [])
345+
346+ @inlineCallbacks
347+ def test_join_descriptors_no_service_state(self):
348+ """Test joining of nonexistent services"""
349+ yield self.add_service_from_formula("wordpress")
350+ yield self.assertFailure(
351+ self.service_state_manager.join_descriptors("wordpress", "nosuch"),
352+ ServiceStateNotFound)
353+ yield self.assertFailure(
354+ self.service_state_manager.join_descriptors("notyet", "nosuch"),
355+ ServiceStateNotFound)

Subscribers

People subscribed via source and target branches

to status/vote changes: