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 | 1 | from unittest import TestCase | ||
7 | 2 | from utils import load_deployment, relations_json_to_tuples | ||
8 | 3 | |||
9 | 4 | |||
10 | 5 | class TestLoadDeployment(TestCase): | ||
11 | 6 | |||
12 | 7 | def test_inherits_one_level(self): | ||
13 | 8 | """ | ||
14 | 9 | One level of inheritance is supported just fine. | ||
15 | 10 | """ | ||
16 | 11 | config = { | ||
17 | 12 | 'foo': { | ||
18 | 13 | 'series': 'precise', | ||
19 | 14 | 'services': { | ||
20 | 15 | 'foo-1': { | ||
21 | 16 | 'options': { | ||
22 | 17 | 'foo': True, | ||
23 | 18 | }, | ||
24 | 19 | }, | ||
25 | 20 | 'foo-2': {}, | ||
26 | 21 | }, | ||
27 | 22 | }, | ||
28 | 23 | 'bar': { | ||
29 | 24 | 'series': 'lucid', | ||
30 | 25 | 'inherits': 'foo', | ||
31 | 26 | 'services': { | ||
32 | 27 | 'foo-1': { | ||
33 | 28 | 'options': { | ||
34 | 29 | 'foo': False, | ||
35 | 30 | 'bar': True, | ||
36 | 31 | }, | ||
37 | 32 | }, | ||
38 | 33 | 'bar-1': {} | ||
39 | 34 | }, | ||
40 | 35 | } | ||
41 | 36 | } | ||
42 | 37 | |||
43 | 38 | (series, charms, | ||
44 | 39 | relations, overrides) = load_deployment(config, 'bar') | ||
45 | 40 | self.assertEqual('lucid', series) | ||
46 | 41 | self.assertEqual({ | ||
47 | 42 | 'foo-1': { | ||
48 | 43 | 'options': { | ||
49 | 44 | 'foo': False, | ||
50 | 45 | 'bar': True, | ||
51 | 46 | }, | ||
52 | 47 | }, | ||
53 | 48 | 'foo-2': {}, | ||
54 | 49 | 'bar-1': {}, | ||
55 | 50 | }, charms) | ||
56 | 51 | self.assertEqual({}, relations) | ||
57 | 52 | self.assertEqual({}, overrides) | ||
58 | 53 | |||
59 | 54 | def test_inherits_two_levels(self): | ||
60 | 55 | """ | ||
61 | 56 | Muliple recursive levels of inheritance are also supported. | ||
62 | 57 | """ | ||
63 | 58 | config = { | ||
64 | 59 | 'foo': { | ||
65 | 60 | 'series': 'precise', | ||
66 | 61 | 'services': { | ||
67 | 62 | 'foo-1': { | ||
68 | 63 | 'options': { | ||
69 | 64 | 'foo': True, | ||
70 | 65 | }, | ||
71 | 66 | }, | ||
72 | 67 | 'foo-2': {}, | ||
73 | 68 | }, | ||
74 | 69 | }, | ||
75 | 70 | 'bar': { | ||
76 | 71 | 'series': 'lucid', | ||
77 | 72 | 'inherits': 'foo', | ||
78 | 73 | 'services': { | ||
79 | 74 | 'foo-1': { | ||
80 | 75 | 'options': { | ||
81 | 76 | 'foo': False, | ||
82 | 77 | 'bar': True, | ||
83 | 78 | }, | ||
84 | 79 | }, | ||
85 | 80 | }, | ||
86 | 81 | }, | ||
87 | 82 | 'baz': { | ||
88 | 83 | 'series': 'precise', | ||
89 | 84 | 'inherits': 'bar', | ||
90 | 85 | 'services': { | ||
91 | 86 | 'foo-1': { | ||
92 | 87 | 'options': { | ||
93 | 88 | 'baz': True, | ||
94 | 89 | }, | ||
95 | 90 | }, | ||
96 | 91 | 'baz-1': {}, | ||
97 | 92 | }, | ||
98 | 93 | }, | ||
99 | 94 | } | ||
100 | 95 | |||
101 | 96 | (series, charms, | ||
102 | 97 | relations, overrides) = load_deployment(config, 'baz') | ||
103 | 98 | self.assertEqual('precise', series) | ||
104 | 99 | self.assertEqual({ | ||
105 | 100 | 'foo-1': { | ||
106 | 101 | 'options': { | ||
107 | 102 | 'foo': False, | ||
108 | 103 | 'bar': True, | ||
109 | 104 | 'baz': True, | ||
110 | 105 | }, | ||
111 | 106 | }, | ||
112 | 107 | 'foo-2': {}, | ||
113 | 108 | 'baz-1': {}, | ||
114 | 109 | }, charms) | ||
115 | 110 | self.assertEqual({}, relations) | ||
116 | 111 | self.assertEqual({}, overrides) | ||
117 | 112 | |||
118 | 113 | def test_inherits_multiple(self): | ||
119 | 114 | """ | ||
120 | 115 | It is also possible to inherit from multiple deployments at a single | ||
121 | 116 | level, which may or may not recurse. | ||
122 | 117 | """ | ||
123 | 118 | config = { | ||
124 | 119 | 'foo': { | ||
125 | 120 | 'series': 'precise', | ||
126 | 121 | 'services': { | ||
127 | 122 | 'foo-1': { | ||
128 | 123 | 'options': { | ||
129 | 124 | 'foo': True, | ||
130 | 125 | }, | ||
131 | 126 | }, | ||
132 | 127 | 'foo-2': {}, | ||
133 | 128 | }, | ||
134 | 129 | }, | ||
135 | 130 | 'bar': { | ||
136 | 131 | 'series': 'lucid', | ||
137 | 132 | 'services': { | ||
138 | 133 | 'foo-1': { | ||
139 | 134 | 'options': { | ||
140 | 135 | 'foo': False, | ||
141 | 136 | 'bar': True, | ||
142 | 137 | }, | ||
143 | 138 | }, | ||
144 | 139 | 'bar-1': {}, | ||
145 | 140 | }, | ||
146 | 141 | }, | ||
147 | 142 | 'baz': { | ||
148 | 143 | 'series': 'quantal', | ||
149 | 144 | 'inherits': ['foo', 'bar'], | ||
150 | 145 | 'services': { | ||
151 | 146 | 'foo-1': { | ||
152 | 147 | 'options': { | ||
153 | 148 | 'baz': True, | ||
154 | 149 | }, | ||
155 | 150 | }, | ||
156 | 151 | 'baz-1': {}, | ||
157 | 152 | }, | ||
158 | 153 | }, | ||
159 | 154 | } | ||
160 | 155 | |||
161 | 156 | (series, charms, | ||
162 | 157 | relations, overrides) = load_deployment(config, 'baz') | ||
163 | 158 | self.assertEqual('quantal', series) | ||
164 | 159 | self.assertEqual({ | ||
165 | 160 | 'foo-1': { | ||
166 | 161 | 'options': { | ||
167 | 162 | 'foo': False, | ||
168 | 163 | 'bar': True, | ||
169 | 164 | 'baz': True, | ||
170 | 165 | }, | ||
171 | 166 | }, | ||
172 | 167 | 'foo-2': {}, | ||
173 | 168 | 'bar-1': {}, | ||
174 | 169 | 'baz-1': {}, | ||
175 | 170 | }, charms) | ||
176 | 171 | self.assertEqual({}, relations) | ||
177 | 172 | self.assertEqual({}, overrides) | ||
178 | 173 | |||
179 | 174 | def test_inherits_multiple_recurse(self): | ||
180 | 175 | """ | ||
181 | 176 | It is also possible to inherit from multiple deployments at a single | ||
182 | 177 | level, one of which inherits from another deployment. | ||
183 | 178 | """ | ||
184 | 179 | config = { | ||
185 | 180 | 'qux': { | ||
186 | 181 | 'series': 'precise', | ||
187 | 182 | 'services': { | ||
188 | 183 | 'foo-1': { | ||
189 | 184 | 'options': { | ||
190 | 185 | 'qux': True, | ||
191 | 186 | }, | ||
192 | 187 | }, | ||
193 | 188 | 'foo-2': {}, | ||
194 | 189 | }, | ||
195 | 190 | }, | ||
196 | 191 | 'foo': { | ||
197 | 192 | 'inherits': 'qux', | ||
198 | 193 | 'series': 'precise', | ||
199 | 194 | 'services': { | ||
200 | 195 | 'foo-1': { | ||
201 | 196 | 'options': { | ||
202 | 197 | 'foo': True, | ||
203 | 198 | }, | ||
204 | 199 | }, | ||
205 | 200 | 'foo-2': {}, | ||
206 | 201 | }, | ||
207 | 202 | }, | ||
208 | 203 | 'bar': { | ||
209 | 204 | 'series': 'lucid', | ||
210 | 205 | 'services': { | ||
211 | 206 | 'foo-1': { | ||
212 | 207 | 'options': { | ||
213 | 208 | 'foo': False, | ||
214 | 209 | 'bar': True, | ||
215 | 210 | }, | ||
216 | 211 | }, | ||
217 | 212 | 'bar-1': {}, | ||
218 | 213 | }, | ||
219 | 214 | }, | ||
220 | 215 | 'baz': { | ||
221 | 216 | 'series': 'quantal', | ||
222 | 217 | 'inherits': ['foo', 'bar'], | ||
223 | 218 | 'services': { | ||
224 | 219 | 'foo-1': { | ||
225 | 220 | 'options': { | ||
226 | 221 | 'baz': True, | ||
227 | 222 | }, | ||
228 | 223 | }, | ||
229 | 224 | 'baz-1': {}, | ||
230 | 225 | }, | ||
231 | 226 | }, | ||
232 | 227 | } | ||
233 | 228 | |||
234 | 229 | (series, charms, | ||
235 | 230 | relations, overrides) = load_deployment(config, 'baz') | ||
236 | 231 | self.assertEqual('quantal', series) | ||
237 | 232 | self.assertEqual({ | ||
238 | 233 | 'foo-1': { | ||
239 | 234 | 'options': { | ||
240 | 235 | 'qux': True, | ||
241 | 236 | 'foo': False, | ||
242 | 237 | 'bar': True, | ||
243 | 238 | 'baz': True, | ||
244 | 239 | }, | ||
245 | 240 | }, | ||
246 | 241 | 'foo-2': {}, | ||
247 | 242 | 'bar-1': {}, | ||
248 | 243 | 'baz-1': {}, | ||
249 | 244 | }, charms) | ||
250 | 245 | self.assertEqual({}, relations) | ||
251 | 246 | self.assertEqual({}, overrides) | ||
252 | 247 | |||
253 | 248 | |||
254 | 249 | class TestRelationsJSONToTuples(TestCase): | ||
255 | 250 | |||
256 | 251 | def test_key_by_weight(self): | ||
257 | 252 | """ | ||
258 | 253 | Relations are grouped by weight. | ||
259 | 254 | """ | ||
260 | 255 | relations = { | ||
261 | 256 | 'foo': { | ||
262 | 257 | 'weight': 42, | ||
263 | 258 | 'consumes': ['bar'], | ||
264 | 259 | }, | ||
265 | 260 | 'baz': { | ||
266 | 261 | 'weight': 60, | ||
267 | 262 | 'consumes': ['qux'], | ||
268 | 263 | } | ||
269 | 264 | } | ||
270 | 265 | self.assertEqual({42: [('foo', 'bar')], | ||
271 | 266 | 60: [('baz', 'qux')]}, | ||
272 | 267 | relations_json_to_tuples(relations)) | ||
273 | 268 | |||
274 | 269 | def test_same_weight_overrides_deterministic(self): | ||
275 | 270 | """ | ||
276 | 271 | If a weight is specified and two relations have the same weight, the | ||
277 | 272 | last one in alphabetical consumer order wins. | ||
278 | 273 | """ | ||
279 | 274 | relations = { | ||
280 | 275 | 'foo': { | ||
281 | 276 | 'weight': 42, | ||
282 | 277 | 'consumes': ['bar'], | ||
283 | 278 | }, | ||
284 | 279 | 'baz': { | ||
285 | 280 | 'weight': 42, | ||
286 | 281 | 'consumes': ['qux'], | ||
287 | 282 | } | ||
288 | 283 | } | ||
289 | 284 | self.assertEqual({42: [('foo', 'bar')]}, | ||
290 | 285 | relations_json_to_tuples(relations)) | ||
291 | 286 | |||
292 | 287 | def test_weight_optional(self): | ||
293 | 288 | """ | ||
294 | 289 | If a weight is not provided, all relations are taken into account with | ||
295 | 290 | no overrides. | ||
296 | 291 | """ | ||
297 | 292 | relations = { | ||
298 | 293 | 'foo': { | ||
299 | 294 | 'consumes': ['bar'], | ||
300 | 295 | }, | ||
301 | 296 | 'baz': { | ||
302 | 297 | 'consumes': ['qux'], | ||
303 | 298 | } | ||
304 | 299 | } | ||
305 | 300 | self.assertEqual({0: [('baz', 'qux'), ('foo', 'bar')]}, | ||
306 | 301 | relations_json_to_tuples(relations)) | ||
307 | 0 | 302 | ||
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 | 465 | to a list of tuples that describe a relation as (consumer, provider) | 465 | to a list of tuples that describe a relation as (consumer, provider) |
313 | 466 | """ | 466 | """ |
314 | 467 | t = {} | 467 | t = {} |
317 | 468 | for consumer in relations.keys(): | 468 | for consumer in sorted(relations.keys()): |
318 | 469 | t[relations[consumer]["weight"]] = [] | 469 | weight = relations[consumer].get("weight", 0) |
319 | 470 | if not weight: | ||
320 | 471 | relation_list = t.setdefault(weight, []) | ||
321 | 472 | else: | ||
322 | 473 | relation_list = t[weight] = [] | ||
323 | 470 | for provider in relations[consumer]["consumes"]: | 474 | for provider in relations[consumer]["consumes"]: |
325 | 471 | t[relations[consumer]["weight"]].append((consumer, provider)) | 475 | relation_list.append((consumer, provider)) |
326 | 476 | relation_list.sort() | ||
327 | 472 | return t | 477 | return t |
328 | 473 | 478 | ||
329 | 474 | 479 | ||
330 | @@ -546,26 +551,34 @@ | |||
331 | 546 | display_deploys(config) | 551 | display_deploys(config) |
332 | 547 | exit(1) | 552 | exit(1) |
333 | 548 | 553 | ||
354 | 549 | deploy_config = config[deployment] | 554 | cur = config[deployment] |
355 | 550 | if 'inherits' in deploy_config: | 555 | configs = [cur] |
356 | 551 | cur = config[deployment] | 556 | |
357 | 552 | configs = [] | 557 | idx = 0 |
358 | 553 | 558 | inherits = [] | |
359 | 554 | while 'inherits' in cur: | 559 | while True: |
360 | 555 | configs.insert(0, cur) | 560 | parents = cur.get('inherits', ()) |
361 | 556 | parent = cur['inherits'] | 561 | if isinstance(parents, basestring): |
362 | 557 | try: | 562 | parents = [parents] |
363 | 558 | cur = config[parent] | 563 | |
364 | 559 | except KeyError: | 564 | if parents: |
365 | 560 | log.error("Could not find parent deployment in config: %s" % | 565 | inherits[idx:idx] = parents[::-1] |
366 | 561 | parent) | 566 | |
367 | 562 | exit(1) | 567 | if not inherits or idx > len(inherits) - 1: |
368 | 563 | 568 | break | |
369 | 564 | base = cur | 569 | |
370 | 565 | configs.insert(0, base) | 570 | parent = inherits[idx] |
371 | 566 | configs.append(config[deployment]) | 571 | idx += 1 |
372 | 567 | 572 | try: | |
373 | 568 | deploy_config = reduce(dict_merge, configs) | 573 | cur = config[parent] |
374 | 574 | configs.append(cur) | ||
375 | 575 | except KeyError: | ||
376 | 576 | log.error("Could not find parent deployment in config: %s" % | ||
377 | 577 | parent) | ||
378 | 578 | exit(1) | ||
379 | 579 | |||
380 | 580 | configs = configs[::-1] | ||
381 | 581 | deploy_config = reduce(dict_merge, configs) | ||
382 | 569 | 582 | ||
383 | 570 | series = deploy_config.get('series') | 583 | series = deploy_config.get('series') |
384 | 571 | charms = deploy_config.get('services', {}) | 584 | charms = deploy_config.get('services', {}) |