Merge lp:~johnsca/charms/trusty/cf-loggregator/port-conflicts into lp:~cf-charmers/charms/trusty/cf-loggregator/trunk
- Trusty Tahr (14.04)
- port-conflicts
- Merge into trunk
Proposed by
Cory Johns
Status: | Merged |
---|---|
Merged at revision: | 34 |
Proposed branch: | lp:~johnsca/charms/trusty/cf-loggregator/port-conflicts |
Merge into: | lp:~cf-charmers/charms/trusty/cf-loggregator/trunk |
Diff against target: |
348 lines (+128/-87) 7 files modified
config.yaml (+1/-7) hooks/charmhelpers/contrib/cloudfoundry/contexts.py (+17/-6) hooks/charmhelpers/core/services.py (+81/-55) hooks/config.py (+11/-2) hooks/loggregator-relation-changed (+5/-4) metadata.yaml (+2/-2) templates/loggregator.json (+11/-11) |
To merge this branch: | bzr merge lp:~johnsca/charms/trusty/cf-loggregator/port-conflicts |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cloud Foundry Charmers | Pending | ||
Review via email: mp+222675@code.launchpad.net |
Commit message
Description of the change
Resolve port conflicts for cf-loggregator
To post a comment you must log in.
Revision history for this message
Cory Johns (johnsca) wrote : | # |
- 40. By Cory Johns
-
Removed port options as they don't add much value to change
Revision history for this message
Cory Johns (johnsca) wrote : | # |
Please take a look.
Revision history for this message
Benjamin Saller (bcsaller) wrote : | # |
LGTM, thanks
https:/
File config.yaml (left):
https:/
config.yaml:11: The shared-secret for log clients to use when publishing
log
thanks :)
- 41. By Cory Johns
-
Fixed missed config-to-static port changes
Revision history for this message
Cory Johns (johnsca) wrote : | # |
Please take a look.
Revision history for this message
Benjamin Saller (bcsaller) wrote : | # |
LGTM pending port confirmation mentioned in the other log charm.
Looks like they still conflict
- 42. By Cory Johns
-
Fixed conflicting ports
Revision history for this message
Cory Johns (johnsca) wrote : | # |
Please take a look.
Revision history for this message
Cory Johns (johnsca) wrote : | # |
*** Submitted:
Resolve port conflicts for cf-loggregator
R=benjamin.saller
CC=
https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'config.yaml' |
2 | --- config.yaml 2014-06-03 04:16:58 +0000 |
3 | +++ config.yaml 2014-06-12 16:51:24 +0000 |
4 | @@ -1,16 +1,10 @@ |
5 | options: |
6 | - max-retained-logs: |
7 | + max_retained_logs: |
8 | type: int |
9 | default: 20 |
10 | description: | |
11 | Max lines of log output to buffer. If you wish to keep logs in a scalable |
12 | way sync them to a real logging backend. |
13 | - client_secret: |
14 | - type: string |
15 | - description: | |
16 | - The shared-secret for log clients to use when publishing log |
17 | - messages. |
18 | - default: 17b6cc14-eea8-433e-b8b7-a65ff8941e3b |
19 | source: |
20 | type: string |
21 | default: 'ppa:cf-charm/ppa' |
22 | |
23 | === modified file 'hooks/charmhelpers/contrib/cloudfoundry/contexts.py' |
24 | --- hooks/charmhelpers/contrib/cloudfoundry/contexts.py 2014-05-27 22:03:37 +0000 |
25 | +++ hooks/charmhelpers/contrib/cloudfoundry/contexts.py 2014-06-12 16:51:24 +0000 |
26 | @@ -33,7 +33,7 @@ |
27 | |
28 | class NatsRelation(RelationContext): |
29 | interface = 'nats' |
30 | - required_keys = ['nats_port', 'nats_address', 'nats_user', 'nats_password'] |
31 | + required_keys = ['address', 'port', 'user', 'password'] |
32 | |
33 | |
34 | class MysqlRelation(RelationContext): |
35 | @@ -44,9 +44,10 @@ |
36 | def get_data(self): |
37 | RelationContext.get_data(self) |
38 | if self.is_ready(): |
39 | - if 'port' not in self['db']: |
40 | - self['db']['port'] = '3306' |
41 | - self['db']['dsn'] = self.dsn_template.format(**self['db']) |
42 | + for unit in self['db']: |
43 | + if 'port' not in unit: |
44 | + unit['port'] = '3306' |
45 | + unit['dsn'] = self.dsn_template.format(**unit) |
46 | |
47 | |
48 | class RouterRelation(RelationContext): |
49 | @@ -56,9 +57,19 @@ |
50 | |
51 | class LogRouterRelation(RelationContext): |
52 | interface = 'logrouter' |
53 | - required_keys = ['shared-secret', 'logrouter-address'] |
54 | + required_keys = ['shared_secret', 'address', 'incoming_port', 'outgoing_port'] |
55 | |
56 | |
57 | class LoggregatorRelation(RelationContext): |
58 | interface = 'loggregator' |
59 | - required_keys = ['shared_secret', 'loggregator_address'] |
60 | + required_keys = ['address', 'incoming_port', 'outgoing_port'] |
61 | + |
62 | + |
63 | +class EtcdRelation(RelationContext): |
64 | + interface = 'etcd' |
65 | + required_keys = ['hostname', 'port'] |
66 | + |
67 | + |
68 | +class CloudControllerRelation(RelationContext): |
69 | + interface = 'cc' |
70 | + required_keys = ['hostname', 'port', 'user', 'password'] |
71 | |
72 | === modified file 'hooks/charmhelpers/core/services.py' |
73 | --- hooks/charmhelpers/core/services.py 2014-05-29 17:28:42 +0000 |
74 | +++ hooks/charmhelpers/core/services.py 2014-06-12 16:51:24 +0000 |
75 | @@ -11,6 +11,14 @@ |
76 | """ |
77 | Register a list of services, given their definitions. |
78 | |
79 | + Traditional charm authoring is focused on implementing hooks. That is, |
80 | + the charm author is thinking in terms of "What hook am I handling; what |
81 | + does this hook need to do?" However, in most cases, the real question |
82 | + should be "Do I have the information I need to configure and start this |
83 | + piece of software and, if so, what are the steps for doing so." The |
84 | + ServiceManager framework tries to bring the focus to the data and the |
85 | + setup tasks, in the most declarative way possible. |
86 | + |
87 | Service definitions are dicts in the following formats (all keys except |
88 | 'service' are optional): |
89 | |
90 | @@ -67,28 +75,28 @@ |
91 | a mongodb relation and which runs a custom `db_migrate` function prior to |
92 | restarting the service, and a Runit serivce called spadesd. |
93 | |
94 | - >>> manager = services.ServiceManager([ |
95 | - ... { |
96 | - ... 'service': 'bingod', |
97 | - ... 'ports': [80, 443], |
98 | - ... 'required_data': [MongoRelation(), config()], |
99 | - ... 'data_ready': [ |
100 | - ... services.template(source='bingod.conf'), |
101 | - ... services.template(source='bingod.ini', |
102 | - ... target='/etc/bingod.ini', |
103 | - ... owner='bingo', perms=0400), |
104 | - ... ], |
105 | - ... }, |
106 | - ... { |
107 | - ... 'service': 'spadesd', |
108 | - ... 'data_ready': services.template(source='spadesd_run.j2', |
109 | - ... target='/etc/sv/spadesd/run', |
110 | - ... perms=0555), |
111 | - ... 'start': runit_start, |
112 | - ... 'stop': runit_stop, |
113 | - ... }, |
114 | - ... ]) |
115 | - ... manager.manage() |
116 | + manager = services.ServiceManager([ |
117 | + { |
118 | + 'service': 'bingod', |
119 | + 'ports': [80, 443], |
120 | + 'required_data': [MongoRelation(), config(), {'my': 'data'}], |
121 | + 'data_ready': [ |
122 | + services.template(source='bingod.conf'), |
123 | + services.template(source='bingod.ini', |
124 | + target='/etc/bingod.ini', |
125 | + owner='bingo', perms=0400), |
126 | + ], |
127 | + }, |
128 | + { |
129 | + 'service': 'spadesd', |
130 | + 'data_ready': services.template(source='spadesd_run.j2', |
131 | + target='/etc/sv/spadesd/run', |
132 | + perms=0555), |
133 | + 'start': runit_start, |
134 | + 'stop': runit_stop, |
135 | + }, |
136 | + ]) |
137 | + manager.manage() |
138 | """ |
139 | self.services = {} |
140 | for service in services or []: |
141 | @@ -213,55 +221,73 @@ |
142 | interface = None |
143 | required_keys = [] |
144 | |
145 | + def __init__(self, *args, **kwargs): |
146 | + super(RelationContext, self).__init__(*args, **kwargs) |
147 | + self.get_data() |
148 | + |
149 | def __bool__(self): |
150 | """ |
151 | - Updates the data and returns True if all of the required_keys are available. |
152 | + Returns True if all of the required_keys are available. |
153 | """ |
154 | - self.get_data() |
155 | return self.is_ready() |
156 | |
157 | __nonzero__ = __bool__ |
158 | |
159 | + def __repr__(self): |
160 | + return super(RelationContext, self).__repr__() |
161 | + |
162 | def is_ready(self): |
163 | """ |
164 | - Returns True if all of the required_keys are available. |
165 | - """ |
166 | - return set(self.get(self.interface, {}).keys()).issuperset(set(self.required_keys)) |
167 | + Returns True if all of the `required_keys` are available from any units. |
168 | + """ |
169 | + ready = len(self.get(self.interface, [])) > 0 |
170 | + if not ready: |
171 | + hookenv.log('Incomplete relation: {}'.format(self.__class__.__name__), hookenv.DEBUG) |
172 | + return ready |
173 | + |
174 | + def _is_ready(self, unit_data): |
175 | + """ |
176 | + Helper method that tests a set of relation data and returns True if |
177 | + all of the `required_keys` are present. |
178 | + """ |
179 | + return set(unit_data.keys()).issuperset(set(self.required_keys)) |
180 | |
181 | def get_data(self): |
182 | """ |
183 | - Retrieve the relation data and store it under `self[self.interface]`. |
184 | - |
185 | - If there are more than one units related on the desired interface, |
186 | - then each unit will have its data stored under `self[self.interface][unit_id]` |
187 | - and one of the units with complete information will chosen at random |
188 | - to fill the values at `self[self.interface]`. |
189 | - |
190 | - |
191 | - For example: |
192 | - |
193 | - { |
194 | - 'foo': 'bar', |
195 | - 'unit/0': { |
196 | - 'foo': 'bar', |
197 | - }, |
198 | - 'unit/1': { |
199 | - 'foo': 'baz', |
200 | - }, |
201 | - } |
202 | + Retrieve the relation data for each unit involved in a realtion and, |
203 | + if complete, store it in a list under `self[self.interface]`. This |
204 | + is automatically called when the RelationContext is instantiated. |
205 | + |
206 | + The units are sorted lexographically first by the service ID, then by |
207 | + the unit ID. Thus, if an interface has two other services, 'db:1' |
208 | + and 'db:2', with 'db:1' having two units, 'wordpress/0' and 'wordpress/1', |
209 | + and 'db:2' having one unit, 'mediawiki/0', all of which have a complete |
210 | + set of data, the relation data for the units will be stored in the |
211 | + order: 'wordpress/0', 'wordpress/1', 'mediawiki/0'. |
212 | + |
213 | + If you only care about a single unit on the relation, you can just |
214 | + access it as `{{ interface[0]['key'] }}`. However, if you can at all |
215 | + support multiple units on a relation, you should iterate over the list, |
216 | + like: |
217 | + |
218 | + {% for unit in interface -%} |
219 | + {{ unit['key'] }}{% if not loop.last %},{% endif %} |
220 | + {%- endfor %} |
221 | + |
222 | + Note that since all sets of relation data from all related services and |
223 | + units are in a single list, if you need to know which service or unit a |
224 | + set of data came from, you'll need to extend this class to preserve |
225 | + that information. |
226 | """ |
227 | if not hookenv.relation_ids(self.interface): |
228 | return |
229 | |
230 | - ns = self.setdefault(self.interface, {}) |
231 | - required = set(self.required_keys) |
232 | - for rid in hookenv.relation_ids(self.interface): |
233 | - for unit in hookenv.related_units(rid): |
234 | + ns = self.setdefault(self.interface, []) |
235 | + for rid in sorted(hookenv.relation_ids(self.interface)): |
236 | + for unit in sorted(hookenv.related_units(rid)): |
237 | reldata = hookenv.relation_get(rid=rid, unit=unit) |
238 | - unit_ns = ns.setdefault(unit, {}) |
239 | - unit_ns.update(reldata) |
240 | - if set(reldata.keys()).issuperset(required): |
241 | - ns.update(reldata) |
242 | + if self._is_ready(reldata): |
243 | + ns.append(reldata) |
244 | |
245 | |
246 | class ManagerCallback(object): |
247 | @@ -316,6 +342,6 @@ |
248 | |
249 | |
250 | # Convenience aliases |
251 | -template = TemplateCallback |
252 | +render_template = template = TemplateCallback |
253 | open_ports = PortManagerCallback() |
254 | close_ports = PortManagerCallback() |
255 | |
256 | === modified file 'hooks/config.py' |
257 | --- hooks/config.py 2014-05-29 17:28:42 +0000 |
258 | +++ hooks/config.py 2014-06-12 16:51:24 +0000 |
259 | @@ -14,14 +14,23 @@ |
260 | LOGGREGATOR_CONFIG_DIR = os.path.join(LOGGREGATOR_DIR, 'config') |
261 | LOGGREGATOR_CONFIG_PATH = os.path.join(LOGGREGATOR_CONFIG_DIR, |
262 | 'loggregator.json') |
263 | +INCOMING_PORT = 3457 |
264 | +OUTGOING_PORT = 8082 |
265 | +VARZ_PORT = 8883 |
266 | |
267 | SERVICES = [ |
268 | { |
269 | 'service': LOGGREGATOR_JOB_NAME, |
270 | 'required_data': [ |
271 | - {'service_name': os.environ['JUJU_UNIT_NAME'].split('/')[0]}, |
272 | - hookenv.config(), |
273 | contexts.NatsRelation(), |
274 | + contexts.LogRouterRelation(), |
275 | + { |
276 | + 'service_name': os.environ['JUJU_UNIT_NAME'].split('/')[0], |
277 | + 'config': hookenv.config(), |
278 | + 'incoming_port': INCOMING_PORT, |
279 | + 'outgoing_port': OUTGOING_PORT, |
280 | + 'varz_port': VARZ_PORT, |
281 | + }, |
282 | ], |
283 | 'data_ready': [ |
284 | services.template(source='loggregator.conf', |
285 | |
286 | === modified file 'hooks/loggregator-relation-changed' |
287 | --- hooks/loggregator-relation-changed 2014-05-27 22:03:37 +0000 |
288 | +++ hooks/loggregator-relation-changed 2014-06-12 16:51:24 +0000 |
289 | @@ -1,8 +1,9 @@ |
290 | #!/usr/bin/env python |
291 | from charmhelpers.core import hookenv |
292 | +import config |
293 | |
294 | -config = hookenv.config() |
295 | -loggregator_address = hookenv.unit_get('private-address').encode('utf-8') |
296 | hookenv.relation_set(None, { |
297 | - 'shared_secret': config['client_secret'], |
298 | - 'loggregator_address': loggregator_address}) |
299 | + 'address': hookenv.unit_get('private-address').encode('utf-8'), |
300 | + 'incoming_port': config.INCOMING_PORT, |
301 | + 'outgoing_port': config.OUTGOING_PORT, |
302 | +}) |
303 | |
304 | === modified file 'metadata.yaml' |
305 | --- metadata.yaml 2014-05-12 11:12:06 +0000 |
306 | +++ metadata.yaml 2014-06-12 16:51:24 +0000 |
307 | @@ -12,5 +12,5 @@ |
308 | requires: |
309 | nats: |
310 | interface: nats |
311 | - # logrouter: |
312 | - # interface: logrouter |
313 | \ No newline at end of file |
314 | + logrouter: |
315 | + interface: logrouter |
316 | |
317 | === modified file 'templates/loggregator.json' |
318 | --- templates/loggregator.json 2014-04-28 12:57:31 +0000 |
319 | +++ templates/loggregator.json 2014-06-12 16:51:24 +0000 |
320 | @@ -1,16 +1,16 @@ |
321 | { |
322 | + "IncomingPort": {{ incoming_port }}, |
323 | + "OutgoingPort": {{ outgoing_port }}, |
324 | "Index": 0, |
325 | - "NatsHost": "{{ nats['nats_address'] }}", |
326 | - "VarzPort": 8888, |
327 | "SkipCertVerify": false, |
328 | + "MaxRetainedLogMessages": {{ config['max_retained_logs'] }}, |
329 | + "Syslog": "", |
330 | + "SharedSecret": "{{ logrouter[0]['shared_secret'] }}", |
331 | "VarzUser": "{{ service_name }}", |
332 | - "MaxRetainedLogMessages": 20, |
333 | - "OutgoingPort": 8080, |
334 | - "Syslog": "", |
335 | "VarzPass": "{{ service_name }}", |
336 | - "NatsUser": "{{ nats['nats_user'] }}", |
337 | - "NatsPass": "{{ nats['nats_password'] }}", |
338 | - "IncomingPort": 3456, |
339 | - "NatsPort": {{ nats['nats_port'] }}, |
340 | - "SharedSecret": "{{ client_secret }}" |
341 | -} |
342 | \ No newline at end of file |
343 | + "VarzPort": {{ varz_port }}, |
344 | + "NatsHost": "{{ nats[0]['address'] }}", |
345 | + "NatsUser": "{{ nats[0]['user'] }}", |
346 | + "NatsPass": "{{ nats[0]['password'] }}", |
347 | + "NatsPort": {{ nats[0]['port'] }} |
348 | +} |
Reviewers: mp+222675_ code.launchpad. net,
Message:
Please take a look.
Description:
Resolve port conflicts for cf-loggregator
https:/ /code.launchpad .net/~johnsca/ charms/ trusty/ cf-loggregator/ port-conflicts/ +merge/ 222675
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/103250045/
Affected files (+135, -86 lines): ers/contrib/ cloudfoundry/ contexts. py ers/core/ services. py or-relation- changed loggregator. json
A [revision details]
M config.yaml
M hooks/charmhelp
M hooks/charmhelp
M hooks/config.py
M hooks/loggregat
M metadata.yaml
M templates/