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
=== modified file 'config.yaml'
--- config.yaml 2014-06-03 04:16:58 +0000
+++ config.yaml 2014-06-12 16:51:24 +0000
@@ -1,16 +1,10 @@
1options:1options:
2 max-retained-logs:2 max_retained_logs:
3 type: int3 type: int
4 default: 204 default: 20
5 description: |5 description: |
6 Max lines of log output to buffer. If you wish to keep logs in a scalable6 Max lines of log output to buffer. If you wish to keep logs in a scalable
7 way sync them to a real logging backend.7 way sync them to a real logging backend.
8 client_secret:
9 type: string
10 description: |
11 The shared-secret for log clients to use when publishing log
12 messages.
13 default: 17b6cc14-eea8-433e-b8b7-a65ff8941e3b
14 source:8 source:
15 type: string9 type: string
16 default: 'ppa:cf-charm/ppa'10 default: 'ppa:cf-charm/ppa'
1711
=== modified file 'hooks/charmhelpers/contrib/cloudfoundry/contexts.py'
--- hooks/charmhelpers/contrib/cloudfoundry/contexts.py 2014-05-27 22:03:37 +0000
+++ hooks/charmhelpers/contrib/cloudfoundry/contexts.py 2014-06-12 16:51:24 +0000
@@ -33,7 +33,7 @@
3333
34class NatsRelation(RelationContext):34class NatsRelation(RelationContext):
35 interface = 'nats'35 interface = 'nats'
36 required_keys = ['nats_port', 'nats_address', 'nats_user', 'nats_password']36 required_keys = ['address', 'port', 'user', 'password']
3737
3838
39class MysqlRelation(RelationContext):39class MysqlRelation(RelationContext):
@@ -44,9 +44,10 @@
44 def get_data(self):44 def get_data(self):
45 RelationContext.get_data(self)45 RelationContext.get_data(self)
46 if self.is_ready():46 if self.is_ready():
47 if 'port' not in self['db']:47 for unit in self['db']:
48 self['db']['port'] = '3306'48 if 'port' not in unit:
49 self['db']['dsn'] = self.dsn_template.format(**self['db'])49 unit['port'] = '3306'
50 unit['dsn'] = self.dsn_template.format(**unit)
5051
5152
52class RouterRelation(RelationContext):53class RouterRelation(RelationContext):
@@ -56,9 +57,19 @@
5657
57class LogRouterRelation(RelationContext):58class LogRouterRelation(RelationContext):
58 interface = 'logrouter'59 interface = 'logrouter'
59 required_keys = ['shared-secret', 'logrouter-address']60 required_keys = ['shared_secret', 'address', 'incoming_port', 'outgoing_port']
6061
6162
62class LoggregatorRelation(RelationContext):63class LoggregatorRelation(RelationContext):
63 interface = 'loggregator'64 interface = 'loggregator'
64 required_keys = ['shared_secret', 'loggregator_address']65 required_keys = ['address', 'incoming_port', 'outgoing_port']
66
67
68class EtcdRelation(RelationContext):
69 interface = 'etcd'
70 required_keys = ['hostname', 'port']
71
72
73class CloudControllerRelation(RelationContext):
74 interface = 'cc'
75 required_keys = ['hostname', 'port', 'user', 'password']
6576
=== modified file 'hooks/charmhelpers/core/services.py'
--- hooks/charmhelpers/core/services.py 2014-05-29 17:28:42 +0000
+++ hooks/charmhelpers/core/services.py 2014-06-12 16:51:24 +0000
@@ -11,6 +11,14 @@
11 """11 """
12 Register a list of services, given their definitions.12 Register a list of services, given their definitions.
1313
14 Traditional charm authoring is focused on implementing hooks. That is,
15 the charm author is thinking in terms of "What hook am I handling; what
16 does this hook need to do?" However, in most cases, the real question
17 should be "Do I have the information I need to configure and start this
18 piece of software and, if so, what are the steps for doing so." The
19 ServiceManager framework tries to bring the focus to the data and the
20 setup tasks, in the most declarative way possible.
21
14 Service definitions are dicts in the following formats (all keys except22 Service definitions are dicts in the following formats (all keys except
15 'service' are optional):23 'service' are optional):
1624
@@ -67,28 +75,28 @@
67 a mongodb relation and which runs a custom `db_migrate` function prior to75 a mongodb relation and which runs a custom `db_migrate` function prior to
68 restarting the service, and a Runit serivce called spadesd.76 restarting the service, and a Runit serivce called spadesd.
6977
70 >>> manager = services.ServiceManager([78 manager = services.ServiceManager([
71 ... {79 {
72 ... 'service': 'bingod',80 'service': 'bingod',
73 ... 'ports': [80, 443],81 'ports': [80, 443],
74 ... 'required_data': [MongoRelation(), config()],82 'required_data': [MongoRelation(), config(), {'my': 'data'}],
75 ... 'data_ready': [83 'data_ready': [
76 ... services.template(source='bingod.conf'),84 services.template(source='bingod.conf'),
77 ... services.template(source='bingod.ini',85 services.template(source='bingod.ini',
78 ... target='/etc/bingod.ini',86 target='/etc/bingod.ini',
79 ... owner='bingo', perms=0400),87 owner='bingo', perms=0400),
80 ... ],88 ],
81 ... },89 },
82 ... {90 {
83 ... 'service': 'spadesd',91 'service': 'spadesd',
84 ... 'data_ready': services.template(source='spadesd_run.j2',92 'data_ready': services.template(source='spadesd_run.j2',
85 ... target='/etc/sv/spadesd/run',93 target='/etc/sv/spadesd/run',
86 ... perms=0555),94 perms=0555),
87 ... 'start': runit_start,95 'start': runit_start,
88 ... 'stop': runit_stop,96 'stop': runit_stop,
89 ... },97 },
90 ... ])98 ])
91 ... manager.manage()99 manager.manage()
92 """100 """
93 self.services = {}101 self.services = {}
94 for service in services or []:102 for service in services or []:
@@ -213,55 +221,73 @@
213 interface = None221 interface = None
214 required_keys = []222 required_keys = []
215223
224 def __init__(self, *args, **kwargs):
225 super(RelationContext, self).__init__(*args, **kwargs)
226 self.get_data()
227
216 def __bool__(self):228 def __bool__(self):
217 """229 """
218 Updates the data and returns True if all of the required_keys are available.230 Returns True if all of the required_keys are available.
219 """231 """
220 self.get_data()
221 return self.is_ready()232 return self.is_ready()
222233
223 __nonzero__ = __bool__234 __nonzero__ = __bool__
224235
236 def __repr__(self):
237 return super(RelationContext, self).__repr__()
238
225 def is_ready(self):239 def is_ready(self):
226 """240 """
227 Returns True if all of the required_keys are available.241 Returns True if all of the `required_keys` are available from any units.
228 """242 """
229 return set(self.get(self.interface, {}).keys()).issuperset(set(self.required_keys))243 ready = len(self.get(self.interface, [])) > 0
244 if not ready:
245 hookenv.log('Incomplete relation: {}'.format(self.__class__.__name__), hookenv.DEBUG)
246 return ready
247
248 def _is_ready(self, unit_data):
249 """
250 Helper method that tests a set of relation data and returns True if
251 all of the `required_keys` are present.
252 """
253 return set(unit_data.keys()).issuperset(set(self.required_keys))
230254
231 def get_data(self):255 def get_data(self):
232 """256 """
233 Retrieve the relation data and store it under `self[self.interface]`.257 Retrieve the relation data for each unit involved in a realtion and,
234258 if complete, store it in a list under `self[self.interface]`. This
235 If there are more than one units related on the desired interface,259 is automatically called when the RelationContext is instantiated.
236 then each unit will have its data stored under `self[self.interface][unit_id]`260
237 and one of the units with complete information will chosen at random261 The units are sorted lexographically first by the service ID, then by
238 to fill the values at `self[self.interface]`.262 the unit ID. Thus, if an interface has two other services, 'db:1'
239263 and 'db:2', with 'db:1' having two units, 'wordpress/0' and 'wordpress/1',
240264 and 'db:2' having one unit, 'mediawiki/0', all of which have a complete
241 For example:265 set of data, the relation data for the units will be stored in the
242266 order: 'wordpress/0', 'wordpress/1', 'mediawiki/0'.
243 {267
244 'foo': 'bar',268 If you only care about a single unit on the relation, you can just
245 'unit/0': {269 access it as `{{ interface[0]['key'] }}`. However, if you can at all
246 'foo': 'bar',270 support multiple units on a relation, you should iterate over the list,
247 },271 like:
248 'unit/1': {272
249 'foo': 'baz',273 {% for unit in interface -%}
250 },274 {{ unit['key'] }}{% if not loop.last %},{% endif %}
251 }275 {%- endfor %}
276
277 Note that since all sets of relation data from all related services and
278 units are in a single list, if you need to know which service or unit a
279 set of data came from, you'll need to extend this class to preserve
280 that information.
252 """281 """
253 if not hookenv.relation_ids(self.interface):282 if not hookenv.relation_ids(self.interface):
254 return283 return
255284
256 ns = self.setdefault(self.interface, {})285 ns = self.setdefault(self.interface, [])
257 required = set(self.required_keys)286 for rid in sorted(hookenv.relation_ids(self.interface)):
258 for rid in hookenv.relation_ids(self.interface):287 for unit in sorted(hookenv.related_units(rid)):
259 for unit in hookenv.related_units(rid):
260 reldata = hookenv.relation_get(rid=rid, unit=unit)288 reldata = hookenv.relation_get(rid=rid, unit=unit)
261 unit_ns = ns.setdefault(unit, {})289 if self._is_ready(reldata):
262 unit_ns.update(reldata)290 ns.append(reldata)
263 if set(reldata.keys()).issuperset(required):
264 ns.update(reldata)
265291
266292
267class ManagerCallback(object):293class ManagerCallback(object):
@@ -316,6 +342,6 @@
316342
317343
318# Convenience aliases344# Convenience aliases
319template = TemplateCallback345render_template = template = TemplateCallback
320open_ports = PortManagerCallback()346open_ports = PortManagerCallback()
321close_ports = PortManagerCallback()347close_ports = PortManagerCallback()
322348
=== modified file 'hooks/config.py'
--- hooks/config.py 2014-05-29 17:28:42 +0000
+++ hooks/config.py 2014-06-12 16:51:24 +0000
@@ -14,14 +14,23 @@
14LOGGREGATOR_CONFIG_DIR = os.path.join(LOGGREGATOR_DIR, 'config')14LOGGREGATOR_CONFIG_DIR = os.path.join(LOGGREGATOR_DIR, 'config')
15LOGGREGATOR_CONFIG_PATH = os.path.join(LOGGREGATOR_CONFIG_DIR,15LOGGREGATOR_CONFIG_PATH = os.path.join(LOGGREGATOR_CONFIG_DIR,
16 'loggregator.json')16 'loggregator.json')
17INCOMING_PORT = 3457
18OUTGOING_PORT = 8082
19VARZ_PORT = 8883
1720
18SERVICES = [21SERVICES = [
19 {22 {
20 'service': LOGGREGATOR_JOB_NAME,23 'service': LOGGREGATOR_JOB_NAME,
21 'required_data': [24 'required_data': [
22 {'service_name': os.environ['JUJU_UNIT_NAME'].split('/')[0]},
23 hookenv.config(),
24 contexts.NatsRelation(),25 contexts.NatsRelation(),
26 contexts.LogRouterRelation(),
27 {
28 'service_name': os.environ['JUJU_UNIT_NAME'].split('/')[0],
29 'config': hookenv.config(),
30 'incoming_port': INCOMING_PORT,
31 'outgoing_port': OUTGOING_PORT,
32 'varz_port': VARZ_PORT,
33 },
25 ],34 ],
26 'data_ready': [35 'data_ready': [
27 services.template(source='loggregator.conf',36 services.template(source='loggregator.conf',
2837
=== modified file 'hooks/loggregator-relation-changed'
--- hooks/loggregator-relation-changed 2014-05-27 22:03:37 +0000
+++ hooks/loggregator-relation-changed 2014-06-12 16:51:24 +0000
@@ -1,8 +1,9 @@
1#!/usr/bin/env python1#!/usr/bin/env python
2from charmhelpers.core import hookenv2from charmhelpers.core import hookenv
3import config
34
4config = hookenv.config()
5loggregator_address = hookenv.unit_get('private-address').encode('utf-8')
6hookenv.relation_set(None, {5hookenv.relation_set(None, {
7 'shared_secret': config['client_secret'],6 'address': hookenv.unit_get('private-address').encode('utf-8'),
8 'loggregator_address': loggregator_address})7 'incoming_port': config.INCOMING_PORT,
8 'outgoing_port': config.OUTGOING_PORT,
9})
910
=== modified file 'metadata.yaml'
--- metadata.yaml 2014-05-12 11:12:06 +0000
+++ metadata.yaml 2014-06-12 16:51:24 +0000
@@ -12,5 +12,5 @@
12requires:12requires:
13 nats:13 nats:
14 interface: nats14 interface: nats
15 # logrouter:
16 # interface: logrouter
17\ No newline at end of file15\ No newline at end of file
16 logrouter:
17 interface: logrouter
1818
=== modified file 'templates/loggregator.json'
--- templates/loggregator.json 2014-04-28 12:57:31 +0000
+++ templates/loggregator.json 2014-06-12 16:51:24 +0000
@@ -1,16 +1,16 @@
1{1{
2 "IncomingPort": {{ incoming_port }},
3 "OutgoingPort": {{ outgoing_port }},
2 "Index": 0,4 "Index": 0,
3 "NatsHost": "{{ nats['nats_address'] }}",
4 "VarzPort": 8888,
5 "SkipCertVerify": false,5 "SkipCertVerify": false,
6 "MaxRetainedLogMessages": {{ config['max_retained_logs'] }},
7 "Syslog": "",
8 "SharedSecret": "{{ logrouter[0]['shared_secret'] }}",
6 "VarzUser": "{{ service_name }}",9 "VarzUser": "{{ service_name }}",
7 "MaxRetainedLogMessages": 20,
8 "OutgoingPort": 8080,
9 "Syslog": "",
10 "VarzPass": "{{ service_name }}",10 "VarzPass": "{{ service_name }}",
11 "NatsUser": "{{ nats['nats_user'] }}",
12 "NatsPass": "{{ nats['nats_password'] }}",
13 "IncomingPort": 3456,
14 "NatsPort": {{ nats['nats_port'] }},
15 "SharedSecret": "{{ client_secret }}"
16}
17\ No newline at end of file11\ No newline at end of file
12 "VarzPort": {{ varz_port }},
13 "NatsHost": "{{ nats[0]['address'] }}",
14 "NatsUser": "{{ nats[0]['user'] }}",
15 "NatsPass": "{{ nats[0]['password'] }}",
16 "NatsPort": {{ nats[0]['port'] }}
17}

Subscribers

People subscribed via source and target branches