Merge lp:~johnsca/charms/trusty/cf-loggregator/port-conflicts into lp:~cf-charmers/charms/trusty/cf-loggregator/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
Reviewer Review Type Date Requested Status
Cloud Foundry Charmers Pending
Review via email: mp+222675@code.launchpad.net

Description of the change

Resolve port conflicts for cf-loggregator

https://codereview.appspot.com/103250045/

To post a comment you must log in.
Revision history for this message
Cory Johns (johnsca) wrote :

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):
   A [revision details]
   M config.yaml
   M hooks/charmhelpers/contrib/cloudfoundry/contexts.py
   M hooks/charmhelpers/core/services.py
   M hooks/config.py
   M hooks/loggregator-relation-changed
   M metadata.yaml
   M templates/loggregator.json

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 :
Revision history for this message
Benjamin Saller (bcsaller) wrote :

LGTM, thanks

https://codereview.appspot.com/103250045/diff/20001/config.yaml
File config.yaml (left):

https://codereview.appspot.com/103250045/diff/20001/config.yaml#oldcode11
config.yaml:11: The shared-secret for log clients to use when publishing
log
thanks :)

https://codereview.appspot.com/103250045/

41. By Cory Johns

Fixed missed config-to-static port changes

Revision history for this message
Cory Johns (johnsca) wrote :
Revision history for this message
Benjamin Saller (bcsaller) wrote :

LGTM pending port confirmation mentioned in the other log charm.

Looks like they still conflict

https://codereview.appspot.com/103250045/

42. By Cory Johns

Fixed conflicting ports

Revision history for this message
Cory Johns (johnsca) wrote :
Revision history for this message
Cory Johns (johnsca) wrote :

*** Submitted:

Resolve port conflicts for cf-loggregator

R=benjamin.saller
CC=
https://codereview.appspot.com/103250045

https://codereview.appspot.com/103250045/

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+}

Subscribers

People subscribed via source and target branches