Merge lp:~free.ekanayaka/landscape-charm/new-data-provider-flow into lp:~landscape/landscape-charm/trunk

Proposed by Free Ekanayaka
Status: Merged
Approved by: Free Ekanayaka
Approved revision: 299
Merged at revision: 299
Proposed branch: lp:~free.ekanayaka/landscape-charm/new-data-provider-flow
Merge into: lp:~landscape/landscape-charm/trunk
Diff against target: 248 lines (+103/-37)
5 files modified
charmhelpers/core/hookenv.py (+62/-1)
charmhelpers/core/services/base.py (+30/-9)
charmhelpers/fetch/giturl.py (+7/-5)
lib/services.py (+2/-16)
lib/tests/test_services.py (+2/-6)
To merge this branch: bzr merge lp:~free.ekanayaka/landscape-charm/new-data-provider-flow
Reviewer Review Type Date Requested Status
🤖 Landscape Builder test results Approve
Данило Шеган (community) Approve
Björn Tillenius (community) Approve
Review via email: mp+260936@code.launchpad.net

Commit message

This branch syncs the charmhelper copy in the charm and takes advantage of some recent changes in the services framework, which now allow data-providers to be triggered outside relation hooks. We As a workaround we happened to have exactly the same behavior, which can now be dropped.

Description of the change

This branch syncs the charmhelper copy in the charm and takes advantage of some recent changes in the services framework, which now allow data-providers to be triggered outside relation hooks. We As a workaround we happened to have exactly the same behavior, which can now be dropped.

To post a comment you must log in.
Revision history for this message
Björn Tillenius (bjornt) wrote :

+1

review: Approve
Revision history for this message
Данило Шеган (danilo) :
review: Approve
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: make ci-test
Result: Success
Revno: 299
Branch: lp:~free.ekanayaka/landscape-charm/new-data-provider-flow
Jenkins: https://ci.lscape.net/job/latch-test/1172/

review: Approve (test results)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/core/hookenv.py'
2--- charmhelpers/core/hookenv.py 2015-05-19 09:33:01 +0000
3+++ charmhelpers/core/hookenv.py 2015-06-03 12:34:04 +0000
4@@ -364,11 +364,16 @@
5 relation_settings = relation_settings if relation_settings else {}
6 relation_cmd_line = ['relation-set']
7 accepts_file = "--file" in subprocess.check_output(
8- relation_cmd_line + ["--help"])
9+ relation_cmd_line + ["--help"], universal_newlines=True)
10 if relation_id is not None:
11 relation_cmd_line.extend(('-r', relation_id))
12 settings = relation_settings.copy()
13 settings.update(kwargs)
14+ for key, value in settings.items():
15+ # Force value to be a string: it always should, but some call
16+ # sites pass in things like dicts or numbers.
17+ if value is not None:
18+ settings[key] = "{}".format(value)
19 if accepts_file:
20 # --file was introduced in Juju 1.23.2. Use it by default if
21 # available, since otherwise we'll break if the relation data is
22@@ -390,6 +395,17 @@
23 flush(local_unit())
24
25
26+def relation_clear(r_id=None):
27+ ''' Clears any relation data already set on relation r_id '''
28+ settings = relation_get(rid=r_id,
29+ unit=local_unit())
30+ for setting in settings:
31+ if setting not in ['public-address', 'private-address']:
32+ settings[setting] = None
33+ relation_set(relation_id=r_id,
34+ **settings)
35+
36+
37 @cached
38 def relation_ids(reltype=None):
39 """A list of relation_ids"""
40@@ -681,3 +697,48 @@
41 return 'unknown'
42 else:
43 raise
44+
45+
46+def translate_exc(from_exc, to_exc):
47+ def inner_translate_exc1(f):
48+ def inner_translate_exc2(*args, **kwargs):
49+ try:
50+ return f(*args, **kwargs)
51+ except from_exc:
52+ raise to_exc
53+
54+ return inner_translate_exc2
55+
56+ return inner_translate_exc1
57+
58+
59+@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
60+def is_leader():
61+ """Does the current unit hold the juju leadership
62+
63+ Uses juju to determine whether the current unit is the leader of its peers
64+ """
65+ cmd = ['is-leader', '--format=json']
66+ return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
67+
68+
69+@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
70+def leader_get(attribute=None):
71+ """Juju leader get value(s)"""
72+ cmd = ['leader-get', '--format=json'] + [attribute or '-']
73+ return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
74+
75+
76+@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
77+def leader_set(settings=None, **kwargs):
78+ """Juju leader set value(s)"""
79+ log("Juju leader-set '%s'" % (settings), level=DEBUG)
80+ cmd = ['leader-set']
81+ settings = settings or {}
82+ settings.update(kwargs)
83+ for k, v in settings.iteritems():
84+ if v is None:
85+ cmd.append('{}='.format(k))
86+ else:
87+ cmd.append('{}={}'.format(k, v))
88+ subprocess.check_call(cmd)
89
90=== modified file 'charmhelpers/core/services/base.py'
91--- charmhelpers/core/services/base.py 2015-05-12 14:31:50 +0000
92+++ charmhelpers/core/services/base.py 2015-06-03 12:34:04 +0000
93@@ -15,8 +15,8 @@
94 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
95
96 import os
97-import re
98 import json
99+from inspect import getargspec
100 from collections import Iterable, OrderedDict
101
102 from charmhelpers.core import host
103@@ -132,8 +132,8 @@
104 if hook_name == 'stop':
105 self.stop_services()
106 else:
107+ self.reconfigure_services()
108 self.provide_data()
109- self.reconfigure_services()
110 cfg = hookenv.config()
111 if cfg.implicit_save:
112 cfg.save()
113@@ -145,15 +145,36 @@
114 A provider must have a `name` attribute, which indicates which relation
115 to set data on, and a `provide_data()` method, which returns a dict of
116 data to set.
117+
118+ The `provide_data()` method can optionally accept two parameters:
119+
120+ * ``remote_service`` The name of the remote service that the data will
121+ be provided to. The `provide_data()` method will be called once
122+ for each connected service (not unit). This allows the method to
123+ tailor its data to the given service.
124+ * ``service_ready`` Whether or not the service definition had all of
125+ its requirements met, and thus the ``data_ready`` callbacks run.
126+
127+ Note that the ``provided_data`` methods are now called **after** the
128+ ``data_ready`` callbacks are run. This gives the ``data_ready`` callbacks
129+ a chance to generate any data necessary for the providing to the remote
130+ services.
131 """
132- hook_name = hookenv.hook_name()
133- for service in self.services.values():
134+ for service_name, service in self.services.items():
135+ service_ready = self.is_ready(service_name)
136 for provider in service.get('provided_data', []):
137- if re.match(r'{}-relation-(joined|changed)'.format(provider.name), hook_name):
138- data = provider.provide_data()
139- _ready = provider._is_ready(data) if hasattr(provider, '_is_ready') else data
140- if _ready:
141- hookenv.relation_set(None, data)
142+ for relid in hookenv.relation_ids(provider.name):
143+ units = hookenv.related_units(relid)
144+ if not units:
145+ continue
146+ remote_service = units[0].split('/')[0]
147+ argspec = getargspec(provider.provide_data)
148+ if len(argspec.args) > 1:
149+ data = provider.provide_data(remote_service, service_ready)
150+ else:
151+ data = provider.provide_data()
152+ if data:
153+ hookenv.relation_set(relid, data)
154
155 def reconfigure_services(self, *service_names):
156 """
157
158=== modified file 'charmhelpers/fetch/giturl.py'
159--- charmhelpers/fetch/giturl.py 2015-03-12 11:42:26 +0000
160+++ charmhelpers/fetch/giturl.py 2015-06-03 12:34:04 +0000
161@@ -45,14 +45,16 @@
162 else:
163 return True
164
165- def clone(self, source, dest, branch):
166+ def clone(self, source, dest, branch, depth=None):
167 if not self.can_handle(source):
168 raise UnhandledSource("Cannot handle {}".format(source))
169
170- repo = Repo.clone_from(source, dest)
171- repo.git.checkout(branch)
172+ if depth:
173+ Repo.clone_from(source, dest, branch=branch, depth=depth)
174+ else:
175+ Repo.clone_from(source, dest, branch=branch)
176
177- def install(self, source, branch="master", dest=None):
178+ def install(self, source, branch="master", dest=None, depth=None):
179 url_parts = self.parse_url(source)
180 branch_name = url_parts.path.strip("/").split("/")[-1]
181 if dest:
182@@ -63,7 +65,7 @@
183 if not os.path.exists(dest_dir):
184 mkdir(dest_dir, perms=0o755)
185 try:
186- self.clone(source, dest_dir, branch)
187+ self.clone(source, dest_dir, branch, depth)
188 except GitCommandError as e:
189 raise UnhandledSource(e.message)
190 except OSError as e:
191
192=== modified file 'lib/services.py'
193--- lib/services.py 2015-06-02 09:21:44 +0000
194+++ lib/services.py 2015-06-03 12:34:04 +0000
195@@ -53,15 +53,13 @@
196 leader_context = LandscapeLeaderContext(
197 host=self._host, hookenv=self._hookenv)
198
199- haproxy_provider = HAProxyProvider(
200- SERVICE_COUNTS, paths=self._paths, is_leader=is_leader)
201-
202 manager = ServiceManager(services=[{
203 "service": "landscape",
204 "ports": [],
205 "provided_data": [
206 LandscapeProvider(leader_context),
207- haproxy_provider,
208+ HAProxyProvider(
209+ SERVICE_COUNTS, paths=self._paths, is_leader=is_leader),
210 RabbitMQProvider(),
211 ],
212 # Required data is available to the render_template calls below.
213@@ -94,16 +92,4 @@
214 "start": LSCtl(subprocess=self._subprocess, hookenv=self._hookenv),
215 }])
216
217- # XXX The services framework only triggers data providers within the
218- # context of relation joined/changed hooks, however we also
219- # want to trigger the haproxy provider if the SSL certificate
220- # has changed.
221- if self._hookenv.hook_name() == "config-changed":
222- config = self._hookenv.config()
223- if config.changed("ssl-cert") or config.changed("ssl-key"):
224- relation_ids = self._hookenv.relation_ids(HAProxyProvider.name)
225- data = haproxy_provider.provide_data()
226- for relation_id in relation_ids:
227- self._hookenv.relation_set(relation_id, data)
228-
229 manager.manage()
230
231=== modified file 'lib/tests/test_services.py'
232--- lib/tests/test_services.py 2015-05-26 12:05:02 +0000
233+++ lib/tests/test_services.py 2015-06-03 12:34:04 +0000
234@@ -77,12 +77,8 @@
235 self.hook()
236 # Assert that the HAProxyProvider has run by checking that it set the
237 # relation with the dict returned by HAProxyProvider.provide_data (the
238- # only key of that dict is 'services'). The ID of relation being set
239- # is None because we're running in the website-relation-joined hook
240- # and are using the default relation ID (which in a real-world
241- # relation-set run will resolve to the relation for the http
242- # interface).
243- self.assertIn("services", self.hookenv.relations[None])
244+ # only key of that dict is 'services').
245+ self.assertIn("services", self.hookenv.relations["website:1"])
246
247 def test_amqp_relation_not_ready(self):
248 """

Subscribers

People subscribed via source and target branches