Merge lp:~sidnei/juju-deployer/inherit-multiple into lp:~gandelman-a/juju-deployer/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
Reviewer Review Type Date Requested Status
Adam Gandelman Pending
Review via email: mp+160236@code.launchpad.net

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', {})

Subscribers

People subscribed via source and target branches