Merge lp:~sidnei/juju-deployer/inherit-multiple into lp:~gandelman-a/juju-deployer/trunk
- inherit-multiple
- Merge into trunk
Proposed by
Sidnei da Silva
Status: | Merged |
---|---|
Merged at revision: | 70 |
Proposed branch: | lp:~sidnei/juju-deployer/inherit-multiple |
Merge into: | lp:~gandelman-a/juju-deployer/trunk |
Diff against target: |
384 lines (+337/-23) 2 files modified
tests/test_utils.py (+301/-0) utils.py (+36/-23) |
To merge this branch: | bzr merge lp:~sidnei/juju-deployer/inherit-multiple |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Gandelman | Pending | ||
Review via email: mp+160236@code.launchpad.net |
Commit message
Description of the change
- Allow 'inherits' to be a list of configs to inherit, to make it easy to create a full stack from multiple smaller ones without deep inheritance chain.
- Make 'weight' optional in relations.
To post a comment you must log in.
- 71. By Sidnei da Silva
-
- Also update test_inherit_
multiple - 72. By Sidnei da Silva
-
- Add another test, make 'weight' optional while keeping (perhaps unintentional) feature of overriding a relation if it has the same weight.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'tests' |
2 | === added file 'tests/test_utils.py' |
3 | --- tests/test_utils.py 1970-01-01 00:00:00 +0000 |
4 | +++ tests/test_utils.py 2013-04-23 01:41:25 +0000 |
5 | @@ -0,0 +1,301 @@ |
6 | +from unittest import TestCase |
7 | +from utils import load_deployment, relations_json_to_tuples |
8 | + |
9 | + |
10 | +class TestLoadDeployment(TestCase): |
11 | + |
12 | + def test_inherits_one_level(self): |
13 | + """ |
14 | + One level of inheritance is supported just fine. |
15 | + """ |
16 | + config = { |
17 | + 'foo': { |
18 | + 'series': 'precise', |
19 | + 'services': { |
20 | + 'foo-1': { |
21 | + 'options': { |
22 | + 'foo': True, |
23 | + }, |
24 | + }, |
25 | + 'foo-2': {}, |
26 | + }, |
27 | + }, |
28 | + 'bar': { |
29 | + 'series': 'lucid', |
30 | + 'inherits': 'foo', |
31 | + 'services': { |
32 | + 'foo-1': { |
33 | + 'options': { |
34 | + 'foo': False, |
35 | + 'bar': True, |
36 | + }, |
37 | + }, |
38 | + 'bar-1': {} |
39 | + }, |
40 | + } |
41 | + } |
42 | + |
43 | + (series, charms, |
44 | + relations, overrides) = load_deployment(config, 'bar') |
45 | + self.assertEqual('lucid', series) |
46 | + self.assertEqual({ |
47 | + 'foo-1': { |
48 | + 'options': { |
49 | + 'foo': False, |
50 | + 'bar': True, |
51 | + }, |
52 | + }, |
53 | + 'foo-2': {}, |
54 | + 'bar-1': {}, |
55 | + }, charms) |
56 | + self.assertEqual({}, relations) |
57 | + self.assertEqual({}, overrides) |
58 | + |
59 | + def test_inherits_two_levels(self): |
60 | + """ |
61 | + Muliple recursive levels of inheritance are also supported. |
62 | + """ |
63 | + config = { |
64 | + 'foo': { |
65 | + 'series': 'precise', |
66 | + 'services': { |
67 | + 'foo-1': { |
68 | + 'options': { |
69 | + 'foo': True, |
70 | + }, |
71 | + }, |
72 | + 'foo-2': {}, |
73 | + }, |
74 | + }, |
75 | + 'bar': { |
76 | + 'series': 'lucid', |
77 | + 'inherits': 'foo', |
78 | + 'services': { |
79 | + 'foo-1': { |
80 | + 'options': { |
81 | + 'foo': False, |
82 | + 'bar': True, |
83 | + }, |
84 | + }, |
85 | + }, |
86 | + }, |
87 | + 'baz': { |
88 | + 'series': 'precise', |
89 | + 'inherits': 'bar', |
90 | + 'services': { |
91 | + 'foo-1': { |
92 | + 'options': { |
93 | + 'baz': True, |
94 | + }, |
95 | + }, |
96 | + 'baz-1': {}, |
97 | + }, |
98 | + }, |
99 | + } |
100 | + |
101 | + (series, charms, |
102 | + relations, overrides) = load_deployment(config, 'baz') |
103 | + self.assertEqual('precise', series) |
104 | + self.assertEqual({ |
105 | + 'foo-1': { |
106 | + 'options': { |
107 | + 'foo': False, |
108 | + 'bar': True, |
109 | + 'baz': True, |
110 | + }, |
111 | + }, |
112 | + 'foo-2': {}, |
113 | + 'baz-1': {}, |
114 | + }, charms) |
115 | + self.assertEqual({}, relations) |
116 | + self.assertEqual({}, overrides) |
117 | + |
118 | + def test_inherits_multiple(self): |
119 | + """ |
120 | + It is also possible to inherit from multiple deployments at a single |
121 | + level, which may or may not recurse. |
122 | + """ |
123 | + config = { |
124 | + 'foo': { |
125 | + 'series': 'precise', |
126 | + 'services': { |
127 | + 'foo-1': { |
128 | + 'options': { |
129 | + 'foo': True, |
130 | + }, |
131 | + }, |
132 | + 'foo-2': {}, |
133 | + }, |
134 | + }, |
135 | + 'bar': { |
136 | + 'series': 'lucid', |
137 | + 'services': { |
138 | + 'foo-1': { |
139 | + 'options': { |
140 | + 'foo': False, |
141 | + 'bar': True, |
142 | + }, |
143 | + }, |
144 | + 'bar-1': {}, |
145 | + }, |
146 | + }, |
147 | + 'baz': { |
148 | + 'series': 'quantal', |
149 | + 'inherits': ['foo', 'bar'], |
150 | + 'services': { |
151 | + 'foo-1': { |
152 | + 'options': { |
153 | + 'baz': True, |
154 | + }, |
155 | + }, |
156 | + 'baz-1': {}, |
157 | + }, |
158 | + }, |
159 | + } |
160 | + |
161 | + (series, charms, |
162 | + relations, overrides) = load_deployment(config, 'baz') |
163 | + self.assertEqual('quantal', series) |
164 | + self.assertEqual({ |
165 | + 'foo-1': { |
166 | + 'options': { |
167 | + 'foo': False, |
168 | + 'bar': True, |
169 | + 'baz': True, |
170 | + }, |
171 | + }, |
172 | + 'foo-2': {}, |
173 | + 'bar-1': {}, |
174 | + 'baz-1': {}, |
175 | + }, charms) |
176 | + self.assertEqual({}, relations) |
177 | + self.assertEqual({}, overrides) |
178 | + |
179 | + def test_inherits_multiple_recurse(self): |
180 | + """ |
181 | + It is also possible to inherit from multiple deployments at a single |
182 | + level, one of which inherits from another deployment. |
183 | + """ |
184 | + config = { |
185 | + 'qux': { |
186 | + 'series': 'precise', |
187 | + 'services': { |
188 | + 'foo-1': { |
189 | + 'options': { |
190 | + 'qux': True, |
191 | + }, |
192 | + }, |
193 | + 'foo-2': {}, |
194 | + }, |
195 | + }, |
196 | + 'foo': { |
197 | + 'inherits': 'qux', |
198 | + 'series': 'precise', |
199 | + 'services': { |
200 | + 'foo-1': { |
201 | + 'options': { |
202 | + 'foo': True, |
203 | + }, |
204 | + }, |
205 | + 'foo-2': {}, |
206 | + }, |
207 | + }, |
208 | + 'bar': { |
209 | + 'series': 'lucid', |
210 | + 'services': { |
211 | + 'foo-1': { |
212 | + 'options': { |
213 | + 'foo': False, |
214 | + 'bar': True, |
215 | + }, |
216 | + }, |
217 | + 'bar-1': {}, |
218 | + }, |
219 | + }, |
220 | + 'baz': { |
221 | + 'series': 'quantal', |
222 | + 'inherits': ['foo', 'bar'], |
223 | + 'services': { |
224 | + 'foo-1': { |
225 | + 'options': { |
226 | + 'baz': True, |
227 | + }, |
228 | + }, |
229 | + 'baz-1': {}, |
230 | + }, |
231 | + }, |
232 | + } |
233 | + |
234 | + (series, charms, |
235 | + relations, overrides) = load_deployment(config, 'baz') |
236 | + self.assertEqual('quantal', series) |
237 | + self.assertEqual({ |
238 | + 'foo-1': { |
239 | + 'options': { |
240 | + 'qux': True, |
241 | + 'foo': False, |
242 | + 'bar': True, |
243 | + 'baz': True, |
244 | + }, |
245 | + }, |
246 | + 'foo-2': {}, |
247 | + 'bar-1': {}, |
248 | + 'baz-1': {}, |
249 | + }, charms) |
250 | + self.assertEqual({}, relations) |
251 | + self.assertEqual({}, overrides) |
252 | + |
253 | + |
254 | +class TestRelationsJSONToTuples(TestCase): |
255 | + |
256 | + def test_key_by_weight(self): |
257 | + """ |
258 | + Relations are grouped by weight. |
259 | + """ |
260 | + relations = { |
261 | + 'foo': { |
262 | + 'weight': 42, |
263 | + 'consumes': ['bar'], |
264 | + }, |
265 | + 'baz': { |
266 | + 'weight': 60, |
267 | + 'consumes': ['qux'], |
268 | + } |
269 | + } |
270 | + self.assertEqual({42: [('foo', 'bar')], |
271 | + 60: [('baz', 'qux')]}, |
272 | + relations_json_to_tuples(relations)) |
273 | + |
274 | + def test_same_weight_overrides_deterministic(self): |
275 | + """ |
276 | + If a weight is specified and two relations have the same weight, the |
277 | + last one in alphabetical consumer order wins. |
278 | + """ |
279 | + relations = { |
280 | + 'foo': { |
281 | + 'weight': 42, |
282 | + 'consumes': ['bar'], |
283 | + }, |
284 | + 'baz': { |
285 | + 'weight': 42, |
286 | + 'consumes': ['qux'], |
287 | + } |
288 | + } |
289 | + self.assertEqual({42: [('foo', 'bar')]}, |
290 | + relations_json_to_tuples(relations)) |
291 | + |
292 | + def test_weight_optional(self): |
293 | + """ |
294 | + If a weight is not provided, all relations are taken into account with |
295 | + no overrides. |
296 | + """ |
297 | + relations = { |
298 | + 'foo': { |
299 | + 'consumes': ['bar'], |
300 | + }, |
301 | + 'baz': { |
302 | + 'consumes': ['qux'], |
303 | + } |
304 | + } |
305 | + self.assertEqual({0: [('baz', 'qux'), ('foo', 'bar')]}, |
306 | + relations_json_to_tuples(relations)) |
307 | |
308 | === modified file 'utils.py' |
309 | --- utils.py 2013-03-12 03:30:18 +0000 |
310 | +++ utils.py 2013-04-23 01:41:25 +0000 |
311 | @@ -465,10 +465,15 @@ |
312 | to a list of tuples that describe a relation as (consumer, provider) |
313 | """ |
314 | t = {} |
315 | - for consumer in relations.keys(): |
316 | - t[relations[consumer]["weight"]] = [] |
317 | + for consumer in sorted(relations.keys()): |
318 | + weight = relations[consumer].get("weight", 0) |
319 | + if not weight: |
320 | + relation_list = t.setdefault(weight, []) |
321 | + else: |
322 | + relation_list = t[weight] = [] |
323 | for provider in relations[consumer]["consumes"]: |
324 | - t[relations[consumer]["weight"]].append((consumer, provider)) |
325 | + relation_list.append((consumer, provider)) |
326 | + relation_list.sort() |
327 | return t |
328 | |
329 | |
330 | @@ -546,26 +551,34 @@ |
331 | display_deploys(config) |
332 | exit(1) |
333 | |
334 | - deploy_config = config[deployment] |
335 | - if 'inherits' in deploy_config: |
336 | - cur = config[deployment] |
337 | - configs = [] |
338 | - |
339 | - while 'inherits' in cur: |
340 | - configs.insert(0, cur) |
341 | - parent = cur['inherits'] |
342 | - try: |
343 | - cur = config[parent] |
344 | - except KeyError: |
345 | - log.error("Could not find parent deployment in config: %s" % |
346 | - parent) |
347 | - exit(1) |
348 | - |
349 | - base = cur |
350 | - configs.insert(0, base) |
351 | - configs.append(config[deployment]) |
352 | - |
353 | - deploy_config = reduce(dict_merge, configs) |
354 | + cur = config[deployment] |
355 | + configs = [cur] |
356 | + |
357 | + idx = 0 |
358 | + inherits = [] |
359 | + while True: |
360 | + parents = cur.get('inherits', ()) |
361 | + if isinstance(parents, basestring): |
362 | + parents = [parents] |
363 | + |
364 | + if parents: |
365 | + inherits[idx:idx] = parents[::-1] |
366 | + |
367 | + if not inherits or idx > len(inherits) - 1: |
368 | + break |
369 | + |
370 | + parent = inherits[idx] |
371 | + idx += 1 |
372 | + try: |
373 | + cur = config[parent] |
374 | + configs.append(cur) |
375 | + except KeyError: |
376 | + log.error("Could not find parent deployment in config: %s" % |
377 | + parent) |
378 | + exit(1) |
379 | + |
380 | + configs = configs[::-1] |
381 | + deploy_config = reduce(dict_merge, configs) |
382 | |
383 | series = deploy_config.get('series') |
384 | charms = deploy_config.get('services', {}) |