Merge lp:~johnsca/charms/trusty/cf-uaa/refactor into lp:~cf-charmers/charms/trusty/cf-uaa/trunk
- Trusty Tahr (14.04)
- refactor
- Merge into trunk
Proposed by
Cory Johns
Status: | Merged |
---|---|
Merged at revision: | 30 |
Proposed branch: | lp:~johnsca/charms/trusty/cf-uaa/refactor |
Merge into: | lp:~cf-charmers/charms/trusty/cf-uaa/trunk |
Diff against target: |
1692 lines (+749/-448) 19 files modified
hooks/charmhelpers/contrib/cloudfoundry/common.py (+3/-62) hooks/charmhelpers/contrib/cloudfoundry/config_helper.py (+0/-11) hooks/charmhelpers/contrib/cloudfoundry/contexts.py (+26/-54) hooks/charmhelpers/contrib/cloudfoundry/install.py (+0/-35) hooks/charmhelpers/contrib/cloudfoundry/services.py (+0/-118) hooks/charmhelpers/contrib/cloudfoundry/upstart_helper.py (+0/-14) hooks/charmhelpers/contrib/hahelpers/apache.py (+9/-8) hooks/charmhelpers/contrib/openstack/context.py (+107/-26) hooks/charmhelpers/contrib/openstack/neutron.py (+31/-5) hooks/charmhelpers/contrib/openstack/utils.py (+9/-1) hooks/charmhelpers/contrib/storage/linux/lvm.py (+1/-1) hooks/charmhelpers/contrib/storage/linux/utils.py (+28/-5) hooks/charmhelpers/core/hookenv.py (+98/-1) hooks/charmhelpers/core/host.py (+47/-0) hooks/charmhelpers/core/services.py (+84/-0) hooks/charmhelpers/core/templating.py (+158/-0) hooks/charmhelpers/fetch/__init__.py (+97/-65) hooks/hooks.py (+24/-19) hooks/install (+27/-23) |
To merge this branch: | bzr merge lp:~johnsca/charms/trusty/cf-uaa/refactor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cloud Foundry Charmers | Pending | ||
Review via email: mp+219918@code.launchpad.net |
Commit message
Description of the change
Refactored to use refactored charm-helpers
To post a comment you must log in.
Revision history for this message
Cory Johns (johnsca) wrote : | # |
Revision history for this message
Benjamin Saller (bcsaller) wrote : | # |
LGTM
Thanks, I would consider just using config. or common. for the import
namespace of constants
https:/
File hooks/install (right):
https:/
hooks/install:13: import config as uaa_config
just config. is fine again
https:/
hooks/install:43: 'sqlite-
Hopefully we can drop this once we move to mysql
- 31. By Cory Johns
-
Merged :parent
- 32. By Cory Johns
-
Resynced charm-helpers
- 33. By Cory Johns
-
Removed unnecessary prefix from config var
Revision history for this message
Cory Johns (johnsca) wrote : | # |
*** Submitted:
Refactored to use refactored charm-helpers
R=benjamin.saller
CC=
https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed directory 'files/upstart' |
2 | === modified file 'hooks/charmhelpers/contrib/cloudfoundry/common.py' |
3 | --- hooks/charmhelpers/contrib/cloudfoundry/common.py 2014-05-07 16:31:39 +0000 |
4 | +++ hooks/charmhelpers/contrib/cloudfoundry/common.py 2014-05-20 19:42:42 +0000 |
5 | @@ -1,11 +1,3 @@ |
6 | -import sys |
7 | -import os |
8 | -import pwd |
9 | -import grp |
10 | -import subprocess |
11 | - |
12 | -from contextlib import contextmanager |
13 | -from charmhelpers.core.hookenv import log, ERROR, DEBUG |
14 | from charmhelpers.core import host |
15 | |
16 | from charmhelpers.fetch import ( |
17 | @@ -13,59 +5,8 @@ |
18 | ) |
19 | |
20 | |
21 | -def run(command, exit_on_error=True, quiet=False): |
22 | - '''Run a command and return the output.''' |
23 | - if not quiet: |
24 | - log("Running {!r}".format(command), DEBUG) |
25 | - p = subprocess.Popen( |
26 | - command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
27 | - shell=isinstance(command, basestring)) |
28 | - p.stdin.close() |
29 | - lines = [] |
30 | - for line in p.stdout: |
31 | - if line: |
32 | - if not quiet: |
33 | - print line |
34 | - lines.append(line) |
35 | - elif p.poll() is not None: |
36 | - break |
37 | - |
38 | - p.wait() |
39 | - |
40 | - if p.returncode == 0: |
41 | - return '\n'.join(lines) |
42 | - |
43 | - if p.returncode != 0 and exit_on_error: |
44 | - log("ERROR: {}".format(p.returncode), ERROR) |
45 | - sys.exit(p.returncode) |
46 | - |
47 | - raise subprocess.CalledProcessError( |
48 | - p.returncode, command, '\n'.join(lines)) |
49 | - |
50 | - |
51 | -def chownr(path, owner, group): |
52 | - uid = pwd.getpwnam(owner).pw_uid |
53 | - gid = grp.getgrnam(group).gr_gid |
54 | - for root, dirs, files in os.walk(path): |
55 | - for momo in dirs: |
56 | - os.chown(os.path.join(root, momo), uid, gid) |
57 | - for momo in files: |
58 | - os.chown(os.path.join(root, momo), uid, gid) |
59 | - |
60 | - |
61 | -@contextmanager |
62 | -def chdir(d): |
63 | - cur = os.getcwd() |
64 | - try: |
65 | - yield os.chdir(d) |
66 | - finally: |
67 | - os.chdir(cur) |
68 | - |
69 | - |
70 | def prepare_cloudfoundry_environment(config_data, packages): |
71 | - if 'source' in config_data: |
72 | - add_source(config_data['source'], config_data.get('key')) |
73 | - apt_update(fatal=True) |
74 | - if packages: |
75 | - apt_install(packages=filter_installed_packages(packages), fatal=True) |
76 | + add_source(config_data['source'], config_data.get('key')) |
77 | + apt_update(fatal=True) |
78 | + apt_install(packages=filter_installed_packages(packages), fatal=True) |
79 | host.adduser('vcap') |
80 | |
81 | === removed file 'hooks/charmhelpers/contrib/cloudfoundry/config_helper.py' |
82 | --- hooks/charmhelpers/contrib/cloudfoundry/config_helper.py 2014-03-31 22:38:23 +0000 |
83 | +++ hooks/charmhelpers/contrib/cloudfoundry/config_helper.py 1970-01-01 00:00:00 +0000 |
84 | @@ -1,11 +0,0 @@ |
85 | -import jinja2 |
86 | - |
87 | -TEMPLATES_DIR = 'templates' |
88 | - |
89 | -def render_template(template_name, context, template_dir=TEMPLATES_DIR): |
90 | - templates = jinja2.Environment( |
91 | - loader=jinja2.FileSystemLoader(template_dir)) |
92 | - template = templates.get_template(template_name) |
93 | - return template.render(context) |
94 | - |
95 | - |
96 | |
97 | === modified file 'hooks/charmhelpers/contrib/cloudfoundry/contexts.py' |
98 | --- hooks/charmhelpers/contrib/cloudfoundry/contexts.py 2014-05-07 16:31:39 +0000 |
99 | +++ hooks/charmhelpers/contrib/cloudfoundry/contexts.py 2014-05-20 19:42:42 +0000 |
100 | @@ -1,54 +1,16 @@ |
101 | import os |
102 | -import yaml |
103 | - |
104 | -from charmhelpers.core import hookenv |
105 | -from charmhelpers.contrib.openstack.context import OSContextGenerator |
106 | - |
107 | - |
108 | -class RelationContext(OSContextGenerator): |
109 | - def __call__(self): |
110 | - if not hookenv.relation_ids(self.interface): |
111 | - return {} |
112 | - |
113 | - ctx = {} |
114 | - for rid in hookenv.relation_ids(self.interface): |
115 | - for unit in hookenv.related_units(rid): |
116 | - reldata = hookenv.relation_get(rid=rid, unit=unit) |
117 | - required = set(self.required_keys) |
118 | - if set(reldata.keys()).issuperset(required): |
119 | - ns = ctx.setdefault(self.interface, {}) |
120 | - for k, v in reldata.items(): |
121 | - ns[k] = v |
122 | - return ctx |
123 | - |
124 | - return {} |
125 | - |
126 | - |
127 | -class ConfigContext(OSContextGenerator): |
128 | - def __call__(self): |
129 | - return hookenv.config() |
130 | - |
131 | - |
132 | -class StorableContext(object): |
133 | - |
134 | - def store_context(self, file_name, config_data): |
135 | - with open(file_name, 'w') as file_stream: |
136 | - yaml.dump(config_data, file_stream) |
137 | - |
138 | - def read_context(self, file_name): |
139 | - with open(file_name, 'r') as file_stream: |
140 | - data = yaml.load(file_stream) |
141 | - if not data: |
142 | - raise OSError("%s is empty" % file_name) |
143 | - return data |
144 | + |
145 | +from charmhelpers.core.templating import ( |
146 | + ContextGenerator, |
147 | + RelationContext, |
148 | + StorableContext, |
149 | +) |
150 | |
151 | |
152 | # Stores `config_data` hash into yaml file with `file_name` as a name |
153 | # if `file_name` already exists, then it loads data from `file_name`. |
154 | -class StoredContext(OSContextGenerator, StorableContext): |
155 | - |
156 | +class StoredContext(ContextGenerator, StorableContext): |
157 | def __init__(self, file_name, config_data): |
158 | - self.data = config_data |
159 | if os.path.exists(file_name): |
160 | self.data = self.read_context(file_name) |
161 | else: |
162 | @@ -59,25 +21,35 @@ |
163 | return self.data |
164 | |
165 | |
166 | -class StaticContext(OSContextGenerator): |
167 | - def __init__(self, data): |
168 | - self.data = data |
169 | - |
170 | - def __call__(self): |
171 | - return self.data |
172 | - |
173 | - |
174 | class NatsContext(RelationContext): |
175 | interface = 'nats' |
176 | required_keys = ['nats_port', 'nats_address', 'nats_user', 'nats_password'] |
177 | |
178 | |
179 | +class MysqlDSNContext(RelationContext): |
180 | + interface = 'db' |
181 | + required_keys = ['user', 'password', 'host', 'database'] |
182 | + dsn_template = "mysql2://{user}:{password}@{host}:{port}/{database}" |
183 | + |
184 | + def __call__(self): |
185 | + ctx = RelationContext.__call__(self) |
186 | + if ctx: |
187 | + if 'port' not in ctx: |
188 | + ctx['db']['port'] = '3306' |
189 | + ctx['db']['dsn'] = self.dsn_template.format(**ctx['db']) |
190 | + return ctx |
191 | + |
192 | + |
193 | class RouterContext(RelationContext): |
194 | interface = 'router' |
195 | required_keys = ['domain'] |
196 | |
197 | |
198 | +class LogRouterContext(RelationContext): |
199 | + interface = 'logrouter' |
200 | + required_keys = ['shared-secret', 'logrouter-address'] |
201 | + |
202 | + |
203 | class LoggregatorContext(RelationContext): |
204 | interface = 'loggregator' |
205 | required_keys = ['shared_secret', 'loggregator_address'] |
206 | - |
207 | |
208 | === removed file 'hooks/charmhelpers/contrib/cloudfoundry/install.py' |
209 | --- hooks/charmhelpers/contrib/cloudfoundry/install.py 2014-03-31 22:38:23 +0000 |
210 | +++ hooks/charmhelpers/contrib/cloudfoundry/install.py 1970-01-01 00:00:00 +0000 |
211 | @@ -1,35 +0,0 @@ |
212 | -import os |
213 | -import subprocess |
214 | - |
215 | - |
216 | -def install(src, dest, fileprops=None, sudo=False): |
217 | - """Install a file from src to dest. Dest can be a complete filename |
218 | - or a target directory. fileprops is a dict with 'owner' (username of owner) |
219 | - and mode (octal string) as keys, the defaults are 'ubuntu' and '400' |
220 | - |
221 | - When owner is passed or when access requires it sudo can be set to True and |
222 | - sudo will be used to install the file. |
223 | - """ |
224 | - if not fileprops: |
225 | - fileprops = {} |
226 | - mode = fileprops.get('mode', '400') |
227 | - owner = fileprops.get('owner') |
228 | - cmd = ['install'] |
229 | - |
230 | - if not os.path.exists(src): |
231 | - raise OSError(src) |
232 | - |
233 | - if not os.path.exists(dest) and not os.path.exists(os.path.dirname(dest)): |
234 | - # create all but the last component as path |
235 | - cmd.append('-D') |
236 | - |
237 | - if mode: |
238 | - cmd.extend(['-m', mode]) |
239 | - |
240 | - if owner: |
241 | - cmd.extend(['-o', owner]) |
242 | - |
243 | - if sudo: |
244 | - cmd.insert(0, 'sudo') |
245 | - cmd.extend([src, dest]) |
246 | - subprocess.check_call(cmd) |
247 | |
248 | === removed file 'hooks/charmhelpers/contrib/cloudfoundry/services.py' |
249 | --- hooks/charmhelpers/contrib/cloudfoundry/services.py 2014-04-05 16:55:32 +0000 |
250 | +++ hooks/charmhelpers/contrib/cloudfoundry/services.py 1970-01-01 00:00:00 +0000 |
251 | @@ -1,118 +0,0 @@ |
252 | -import os |
253 | -import tempfile |
254 | -from charmhelpers.core import host |
255 | - |
256 | -from charmhelpers.contrib.cloudfoundry.install import install |
257 | -from charmhelpers.core.hookenv import log |
258 | -from jinja2 import Environment, FileSystemLoader |
259 | - |
260 | -SERVICE_CONFIG = [] |
261 | -TEMPLATE_LOADER = None |
262 | - |
263 | - |
264 | -def render_template(template_name, context): |
265 | - """Render template to a tempfile returning the name""" |
266 | - _, fn = tempfile.mkstemp() |
267 | - template = load_template(template_name) |
268 | - output = template.render(context) |
269 | - with open(fn, "w") as fp: |
270 | - fp.write(output) |
271 | - return fn |
272 | - |
273 | - |
274 | -def collect_contexts(context_providers): |
275 | - ctx = {} |
276 | - for provider in context_providers: |
277 | - c = provider() |
278 | - if not c: |
279 | - return {} |
280 | - ctx.update(c) |
281 | - return ctx |
282 | - |
283 | - |
284 | -def load_template(name): |
285 | - return TEMPLATE_LOADER.get_template(name) |
286 | - |
287 | - |
288 | -def configure_templates(template_dir): |
289 | - global TEMPLATE_LOADER |
290 | - TEMPLATE_LOADER = Environment(loader=FileSystemLoader(template_dir)) |
291 | - |
292 | - |
293 | -def register(service_configs, template_dir): |
294 | - """Register a list of service configs. |
295 | - |
296 | - Service Configs are dicts in the following formats: |
297 | - |
298 | - { |
299 | - "service": <service name>, |
300 | - "templates": [ { |
301 | - 'target': <render target of template>, |
302 | - 'source': <optional name of template in passed in template_dir> |
303 | - 'file_properties': <optional dict taking owner and octal mode> |
304 | - 'contexts': [ context generators, see contexts.py ] |
305 | - } |
306 | - ] } |
307 | - |
308 | - If 'source' is not provided for a template the template_dir will |
309 | - be consulted for ``basename(target).j2``. |
310 | - """ |
311 | - global SERVICE_CONFIG |
312 | - if template_dir: |
313 | - configure_templates(template_dir) |
314 | - SERVICE_CONFIG.extend(service_configs) |
315 | - |
316 | - |
317 | -def reset(): |
318 | - global SERVICE_CONFIG |
319 | - SERVICE_CONFIG = [] |
320 | - |
321 | - |
322 | -# def service_context(name): |
323 | -# contexts = collect_contexts(template['contexts']) |
324 | - |
325 | -def reconfigure_service(service_name, restart=True): |
326 | - global SERVICE_CONFIG |
327 | - service = None |
328 | - for service in SERVICE_CONFIG: |
329 | - if service['service'] == service_name: |
330 | - break |
331 | - if not service or service['service'] != service_name: |
332 | - raise KeyError('Service not registered: %s' % service_name) |
333 | - |
334 | - templates = service['templates'] |
335 | - for template in templates: |
336 | - contexts = collect_contexts(template['contexts']) |
337 | - if contexts: |
338 | - template_target = template['target'] |
339 | - default_template = "%s.j2" % os.path.basename(template_target) |
340 | - template_name = template.get('source', default_template) |
341 | - output_file = render_template(template_name, contexts) |
342 | - file_properties = template.get('file_properties') |
343 | - install(output_file, template_target, file_properties) |
344 | - os.unlink(output_file) |
345 | - else: |
346 | - restart = False |
347 | - |
348 | - if restart: |
349 | - host.service_restart(service_name) |
350 | - |
351 | - |
352 | -def stop_services(): |
353 | - global SERVICE_CONFIG |
354 | - for service in SERVICE_CONFIG: |
355 | - if host.service_running(service['service']): |
356 | - host.service_stop(service['service']) |
357 | - |
358 | - |
359 | -def get_service(service_name): |
360 | - global SERVICE_CONFIG |
361 | - for service in SERVICE_CONFIG: |
362 | - if service_name == service['service']: |
363 | - return service |
364 | - return None |
365 | - |
366 | - |
367 | -def reconfigure_services(restart=True): |
368 | - for service in SERVICE_CONFIG: |
369 | - reconfigure_service(service['service'], restart=restart) |
370 | |
371 | === removed file 'hooks/charmhelpers/contrib/cloudfoundry/upstart_helper.py' |
372 | --- hooks/charmhelpers/contrib/cloudfoundry/upstart_helper.py 2014-05-07 11:50:56 +0000 |
373 | +++ hooks/charmhelpers/contrib/cloudfoundry/upstart_helper.py 1970-01-01 00:00:00 +0000 |
374 | @@ -1,14 +0,0 @@ |
375 | -import os |
376 | -import glob |
377 | -from charmhelpers.core import hookenv |
378 | -from charmhelpers.core.hookenv import charm_dir |
379 | -from charmhelpers.contrib.cloudfoundry.install import install |
380 | - |
381 | - |
382 | -def install_upstart_scripts(dirname=os.path.join(hookenv.charm_dir(), |
383 | - 'files/upstart'), |
384 | - pattern='*.conf'): |
385 | - for script in glob.glob("%s/%s" % (dirname, pattern)): |
386 | - filename = os.path.join(dirname, script) |
387 | - hookenv.log('Installing upstart job:' + filename, hookenv.DEBUG) |
388 | - install(filename, '/etc/init') |
389 | |
390 | === modified file 'hooks/charmhelpers/contrib/hahelpers/apache.py' |
391 | --- hooks/charmhelpers/contrib/hahelpers/apache.py 2014-04-01 08:57:07 +0000 |
392 | +++ hooks/charmhelpers/contrib/hahelpers/apache.py 2014-05-20 19:42:42 +0000 |
393 | @@ -39,14 +39,15 @@ |
394 | |
395 | |
396 | def get_ca_cert(): |
397 | - ca_cert = None |
398 | - log("Inspecting identity-service relations for CA SSL certificate.", |
399 | - level=INFO) |
400 | - for r_id in relation_ids('identity-service'): |
401 | - for unit in relation_list(r_id): |
402 | - if not ca_cert: |
403 | - ca_cert = relation_get('ca_cert', |
404 | - rid=r_id, unit=unit) |
405 | + ca_cert = config_get('ssl_ca') |
406 | + if ca_cert is None: |
407 | + log("Inspecting identity-service relations for CA SSL certificate.", |
408 | + level=INFO) |
409 | + for r_id in relation_ids('identity-service'): |
410 | + for unit in relation_list(r_id): |
411 | + if ca_cert is None: |
412 | + ca_cert = relation_get('ca_cert', |
413 | + rid=r_id, unit=unit) |
414 | return ca_cert |
415 | |
416 | |
417 | |
418 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' |
419 | --- hooks/charmhelpers/contrib/openstack/context.py 2014-03-31 22:38:23 +0000 |
420 | +++ hooks/charmhelpers/contrib/openstack/context.py 2014-05-20 19:42:42 +0000 |
421 | @@ -1,5 +1,6 @@ |
422 | import json |
423 | import os |
424 | +import time |
425 | |
426 | from base64 import b64decode |
427 | |
428 | @@ -113,7 +114,8 @@ |
429 | class SharedDBContext(OSContextGenerator): |
430 | interfaces = ['shared-db'] |
431 | |
432 | - def __init__(self, database=None, user=None, relation_prefix=None): |
433 | + def __init__(self, |
434 | + database=None, user=None, relation_prefix=None, ssl_dir=None): |
435 | ''' |
436 | Allows inspecting relation for settings prefixed with relation_prefix. |
437 | This is useful for parsing access for multiple databases returned via |
438 | @@ -122,6 +124,7 @@ |
439 | self.relation_prefix = relation_prefix |
440 | self.database = database |
441 | self.user = user |
442 | + self.ssl_dir = ssl_dir |
443 | |
444 | def __call__(self): |
445 | self.database = self.database or config('database') |
446 | @@ -139,17 +142,72 @@ |
447 | |
448 | for rid in relation_ids('shared-db'): |
449 | for unit in related_units(rid): |
450 | - passwd = relation_get(password_setting, rid=rid, unit=unit) |
451 | + rdata = relation_get(rid=rid, unit=unit) |
452 | ctxt = { |
453 | - 'database_host': relation_get('db_host', rid=rid, |
454 | - unit=unit), |
455 | + 'database_host': rdata.get('db_host'), |
456 | 'database': self.database, |
457 | 'database_user': self.user, |
458 | - 'database_password': passwd, |
459 | - } |
460 | - if context_complete(ctxt): |
461 | - return ctxt |
462 | - return {} |
463 | + 'database_password': rdata.get(password_setting), |
464 | + 'database_type': 'mysql' |
465 | + } |
466 | + if context_complete(ctxt): |
467 | + db_ssl(rdata, ctxt, self.ssl_dir) |
468 | + return ctxt |
469 | + return {} |
470 | + |
471 | + |
472 | +class PostgresqlDBContext(OSContextGenerator): |
473 | + interfaces = ['pgsql-db'] |
474 | + |
475 | + def __init__(self, database=None): |
476 | + self.database = database |
477 | + |
478 | + def __call__(self): |
479 | + self.database = self.database or config('database') |
480 | + if self.database is None: |
481 | + log('Could not generate postgresql_db context. ' |
482 | + 'Missing required charm config options. ' |
483 | + '(database name)') |
484 | + raise OSContextError |
485 | + ctxt = {} |
486 | + |
487 | + for rid in relation_ids(self.interfaces[0]): |
488 | + for unit in related_units(rid): |
489 | + ctxt = { |
490 | + 'database_host': relation_get('host', rid=rid, unit=unit), |
491 | + 'database': self.database, |
492 | + 'database_user': relation_get('user', rid=rid, unit=unit), |
493 | + 'database_password': relation_get('password', rid=rid, unit=unit), |
494 | + 'database_type': 'postgresql', |
495 | + } |
496 | + if context_complete(ctxt): |
497 | + return ctxt |
498 | + return {} |
499 | + |
500 | + |
501 | +def db_ssl(rdata, ctxt, ssl_dir): |
502 | + if 'ssl_ca' in rdata and ssl_dir: |
503 | + ca_path = os.path.join(ssl_dir, 'db-client.ca') |
504 | + with open(ca_path, 'w') as fh: |
505 | + fh.write(b64decode(rdata['ssl_ca'])) |
506 | + ctxt['database_ssl_ca'] = ca_path |
507 | + elif 'ssl_ca' in rdata: |
508 | + log("Charm not setup for ssl support but ssl ca found") |
509 | + return ctxt |
510 | + if 'ssl_cert' in rdata: |
511 | + cert_path = os.path.join( |
512 | + ssl_dir, 'db-client.cert') |
513 | + if not os.path.exists(cert_path): |
514 | + log("Waiting 1m for ssl client cert validity") |
515 | + time.sleep(60) |
516 | + with open(cert_path, 'w') as fh: |
517 | + fh.write(b64decode(rdata['ssl_cert'])) |
518 | + ctxt['database_ssl_cert'] = cert_path |
519 | + key_path = os.path.join(ssl_dir, 'db-client.key') |
520 | + with open(key_path, 'w') as fh: |
521 | + fh.write(b64decode(rdata['ssl_key'])) |
522 | + ctxt['database_ssl_key'] = key_path |
523 | + return ctxt |
524 | |
525 | |
526 | class IdentityServiceContext(OSContextGenerator): |
527 | @@ -161,24 +219,25 @@ |
528 | |
529 | for rid in relation_ids('identity-service'): |
530 | for unit in related_units(rid): |
531 | + rdata = relation_get(rid=rid, unit=unit) |
532 | ctxt = { |
533 | - 'service_port': relation_get('service_port', rid=rid, |
534 | - unit=unit), |
535 | - 'service_host': relation_get('service_host', rid=rid, |
536 | - unit=unit), |
537 | - 'auth_host': relation_get('auth_host', rid=rid, unit=unit), |
538 | - 'auth_port': relation_get('auth_port', rid=rid, unit=unit), |
539 | - 'admin_tenant_name': relation_get('service_tenant', |
540 | - rid=rid, unit=unit), |
541 | - 'admin_user': relation_get('service_username', rid=rid, |
542 | - unit=unit), |
543 | - 'admin_password': relation_get('service_password', rid=rid, |
544 | - unit=unit), |
545 | - # XXX: Hard-coded http. |
546 | - 'service_protocol': 'http', |
547 | - 'auth_protocol': 'http', |
548 | + 'service_port': rdata.get('service_port'), |
549 | + 'service_host': rdata.get('service_host'), |
550 | + 'auth_host': rdata.get('auth_host'), |
551 | + 'auth_port': rdata.get('auth_port'), |
552 | + 'admin_tenant_name': rdata.get('service_tenant'), |
553 | + 'admin_user': rdata.get('service_username'), |
554 | + 'admin_password': rdata.get('service_password'), |
555 | + 'service_protocol': |
556 | + rdata.get('service_protocol') or 'http', |
557 | + 'auth_protocol': |
558 | + rdata.get('auth_protocol') or 'http', |
559 | } |
560 | if context_complete(ctxt): |
561 | + # NOTE(jamespage) this is required for >= icehouse |
562 | + # so a missing value just indicates keystone needs |
563 | + # upgrading |
564 | + ctxt['admin_tenant_id'] = rdata.get('service_tenant_id') |
565 | return ctxt |
566 | return {} |
567 | |
568 | @@ -186,6 +245,9 @@ |
569 | class AMQPContext(OSContextGenerator): |
570 | interfaces = ['amqp'] |
571 | |
572 | + def __init__(self, ssl_dir=None): |
573 | + self.ssl_dir = ssl_dir |
574 | + |
575 | def __call__(self): |
576 | log('Generating template context for amqp') |
577 | conf = config() |
578 | @@ -196,7 +258,6 @@ |
579 | log('Could not generate shared_db context. ' |
580 | 'Missing required charm config options: %s.' % e) |
581 | raise OSContextError |
582 | - |
583 | ctxt = {} |
584 | for rid in relation_ids('amqp'): |
585 | ha_vip_only = False |
586 | @@ -214,6 +275,14 @@ |
587 | unit=unit), |
588 | 'rabbitmq_virtual_host': vhost, |
589 | }) |
590 | + |
591 | + ssl_port = relation_get('ssl_port', rid=rid, unit=unit) |
592 | + if ssl_port: |
593 | + ctxt['rabbit_ssl_port'] = ssl_port |
594 | + ssl_ca = relation_get('ssl_ca', rid=rid, unit=unit) |
595 | + if ssl_ca: |
596 | + ctxt['rabbit_ssl_ca'] = ssl_ca |
597 | + |
598 | if relation_get('ha_queues', rid=rid, unit=unit) is not None: |
599 | ctxt['rabbitmq_ha_queues'] = True |
600 | |
601 | @@ -221,6 +290,16 @@ |
602 | rid=rid, unit=unit) is not None |
603 | |
604 | if context_complete(ctxt): |
605 | + if 'rabbit_ssl_ca' in ctxt: |
606 | + if not self.ssl_dir: |
607 | + log(("Charm not setup for ssl support " |
608 | + "but ssl ca found")) |
609 | + break |
610 | + ca_path = os.path.join( |
611 | + self.ssl_dir, 'rabbit-client-ca.pem') |
612 | + with open(ca_path, 'w') as fh: |
613 | + fh.write(b64decode(ctxt['rabbit_ssl_ca'])) |
614 | + ctxt['rabbit_ssl_ca'] = ca_path |
615 | # Sufficient information found = break out! |
616 | break |
617 | # Used for active/active rabbitmq >= grizzly |
618 | @@ -391,6 +470,8 @@ |
619 | 'private_address': unit_get('private-address'), |
620 | 'endpoints': [] |
621 | } |
622 | + if is_clustered(): |
623 | + ctxt['private_address'] = config('vip') |
624 | for api_port in self.external_ports: |
625 | ext_port = determine_apache_port(api_port) |
626 | int_port = determine_api_port(api_port) |
627 | @@ -489,7 +570,7 @@ |
628 | |
629 | if self.plugin == 'ovs': |
630 | ctxt.update(self.ovs_ctxt()) |
631 | - elif self.plugin == 'nvp': |
632 | + elif self.plugin in ['nvp', 'nsx']: |
633 | ctxt.update(self.nvp_ctxt()) |
634 | |
635 | alchemy_flags = config('neutron-alchemy-flags') |
636 | |
637 | === modified file 'hooks/charmhelpers/contrib/openstack/neutron.py' |
638 | --- hooks/charmhelpers/contrib/openstack/neutron.py 2014-03-31 22:38:23 +0000 |
639 | +++ hooks/charmhelpers/contrib/openstack/neutron.py 2014-05-20 19:42:42 +0000 |
640 | @@ -17,6 +17,8 @@ |
641 | kver = check_output(['uname', '-r']).strip() |
642 | return 'linux-headers-%s' % kver |
643 | |
644 | +QUANTUM_CONF_DIR = '/etc/quantum' |
645 | + |
646 | |
647 | def kernel_version(): |
648 | """ Retrieve the current major kernel version as a tuple e.g. (3, 13) """ |
649 | @@ -35,6 +37,8 @@ |
650 | |
651 | |
652 | # legacy |
653 | + |
654 | + |
655 | def quantum_plugins(): |
656 | from charmhelpers.contrib.openstack import context |
657 | return { |
658 | @@ -46,7 +50,8 @@ |
659 | 'contexts': [ |
660 | context.SharedDBContext(user=config('neutron-database-user'), |
661 | database=config('neutron-database'), |
662 | - relation_prefix='neutron')], |
663 | + relation_prefix='neutron', |
664 | + ssl_dir=QUANTUM_CONF_DIR)], |
665 | 'services': ['quantum-plugin-openvswitch-agent'], |
666 | 'packages': [[headers_package()] + determine_dkms_package(), |
667 | ['quantum-plugin-openvswitch-agent']], |
668 | @@ -61,7 +66,8 @@ |
669 | 'contexts': [ |
670 | context.SharedDBContext(user=config('neutron-database-user'), |
671 | database=config('neutron-database'), |
672 | - relation_prefix='neutron')], |
673 | + relation_prefix='neutron', |
674 | + ssl_dir=QUANTUM_CONF_DIR)], |
675 | 'services': [], |
676 | 'packages': [], |
677 | 'server_packages': ['quantum-server', |
678 | @@ -70,6 +76,8 @@ |
679 | } |
680 | } |
681 | |
682 | +NEUTRON_CONF_DIR = '/etc/neutron' |
683 | + |
684 | |
685 | def neutron_plugins(): |
686 | from charmhelpers.contrib.openstack import context |
687 | @@ -83,7 +91,8 @@ |
688 | 'contexts': [ |
689 | context.SharedDBContext(user=config('neutron-database-user'), |
690 | database=config('neutron-database'), |
691 | - relation_prefix='neutron')], |
692 | + relation_prefix='neutron', |
693 | + ssl_dir=NEUTRON_CONF_DIR)], |
694 | 'services': ['neutron-plugin-openvswitch-agent'], |
695 | 'packages': [[headers_package()] + determine_dkms_package(), |
696 | ['neutron-plugin-openvswitch-agent']], |
697 | @@ -98,20 +107,37 @@ |
698 | 'contexts': [ |
699 | context.SharedDBContext(user=config('neutron-database-user'), |
700 | database=config('neutron-database'), |
701 | - relation_prefix='neutron')], |
702 | + relation_prefix='neutron', |
703 | + ssl_dir=NEUTRON_CONF_DIR)], |
704 | 'services': [], |
705 | 'packages': [], |
706 | 'server_packages': ['neutron-server', |
707 | 'neutron-plugin-nicira'], |
708 | 'server_services': ['neutron-server'] |
709 | + }, |
710 | + 'nsx': { |
711 | + 'config': '/etc/neutron/plugins/vmware/nsx.ini', |
712 | + 'driver': 'vmware', |
713 | + 'contexts': [ |
714 | + context.SharedDBContext(user=config('neutron-database-user'), |
715 | + database=config('neutron-database'), |
716 | + relation_prefix='neutron', |
717 | + ssl_dir=NEUTRON_CONF_DIR)], |
718 | + 'services': [], |
719 | + 'packages': [], |
720 | + 'server_packages': ['neutron-server', |
721 | + 'neutron-plugin-vmware'], |
722 | + 'server_services': ['neutron-server'] |
723 | } |
724 | } |
725 | - # NOTE: patch in ml2 plugin for icehouse onwards |
726 | if release >= 'icehouse': |
727 | + # NOTE: patch in ml2 plugin for icehouse onwards |
728 | plugins['ovs']['config'] = '/etc/neutron/plugins/ml2/ml2_conf.ini' |
729 | plugins['ovs']['driver'] = 'neutron.plugins.ml2.plugin.Ml2Plugin' |
730 | plugins['ovs']['server_packages'] = ['neutron-server', |
731 | 'neutron-plugin-ml2'] |
732 | + # NOTE: patch in vmware renames nvp->nsx for icehouse onwards |
733 | + plugins['nvp'] = plugins['nsx'] |
734 | return plugins |
735 | |
736 | |
737 | |
738 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' |
739 | --- hooks/charmhelpers/contrib/openstack/utils.py 2014-03-31 22:38:23 +0000 |
740 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2014-05-20 19:42:42 +0000 |
741 | @@ -65,6 +65,7 @@ |
742 | ('1.10.0', 'havana'), |
743 | ('1.9.1', 'havana'), |
744 | ('1.9.0', 'havana'), |
745 | + ('1.13.1', 'icehouse'), |
746 | ('1.13.0', 'icehouse'), |
747 | ('1.12.0', 'icehouse'), |
748 | ('1.11.0', 'icehouse'), |
749 | @@ -130,6 +131,11 @@ |
750 | def get_os_codename_package(package, fatal=True): |
751 | '''Derive OpenStack release codename from an installed package.''' |
752 | apt.init() |
753 | + |
754 | + # Tell apt to build an in-memory cache to prevent race conditions (if |
755 | + # another process is already building the cache). |
756 | + apt.config.set("Dir::Cache::pkgcache", "") |
757 | + |
758 | cache = apt.Cache() |
759 | |
760 | try: |
761 | @@ -182,7 +188,7 @@ |
762 | if cname == codename: |
763 | return version |
764 | #e = "Could not determine OpenStack version for package: %s" % pkg |
765 | - #error_out(e) |
766 | + # error_out(e) |
767 | |
768 | |
769 | os_rel = None |
770 | @@ -400,6 +406,8 @@ |
771 | rtype = 'PTR' |
772 | elif isinstance(address, basestring): |
773 | rtype = 'A' |
774 | + else: |
775 | + return None |
776 | |
777 | answers = dns.resolver.query(address, rtype) |
778 | if answers: |
779 | |
780 | === modified file 'hooks/charmhelpers/contrib/storage/linux/lvm.py' |
781 | --- hooks/charmhelpers/contrib/storage/linux/lvm.py 2014-04-01 08:57:07 +0000 |
782 | +++ hooks/charmhelpers/contrib/storage/linux/lvm.py 2014-05-20 19:42:42 +0000 |
783 | @@ -62,7 +62,7 @@ |
784 | pvd = check_output(['pvdisplay', block_device]).splitlines() |
785 | for l in pvd: |
786 | if l.strip().startswith('VG Name'): |
787 | - vg = ' '.join(l.split()).split(' ').pop() |
788 | + vg = ' '.join(l.strip().split()[2:]) |
789 | return vg |
790 | |
791 | |
792 | |
793 | === modified file 'hooks/charmhelpers/contrib/storage/linux/utils.py' |
794 | --- hooks/charmhelpers/contrib/storage/linux/utils.py 2014-04-01 08:57:07 +0000 |
795 | +++ hooks/charmhelpers/contrib/storage/linux/utils.py 2014-05-20 19:42:42 +0000 |
796 | @@ -1,8 +1,11 @@ |
797 | -from os import stat |
798 | +import os |
799 | +import re |
800 | from stat import S_ISBLK |
801 | |
802 | from subprocess import ( |
803 | - check_call |
804 | + check_call, |
805 | + check_output, |
806 | + call |
807 | ) |
808 | |
809 | |
810 | @@ -12,7 +15,9 @@ |
811 | |
812 | :returns: boolean: True if path is a block device, False if not. |
813 | ''' |
814 | - return S_ISBLK(stat(path).st_mode) |
815 | + if not os.path.exists(path): |
816 | + return False |
817 | + return S_ISBLK(os.stat(path).st_mode) |
818 | |
819 | |
820 | def zap_disk(block_device): |
821 | @@ -22,5 +27,23 @@ |
822 | |
823 | :param block_device: str: Full path of block device to clean. |
824 | ''' |
825 | - check_call(['sgdisk', '--zap-all', '--clear', |
826 | - '--mbrtogpt', block_device]) |
827 | + # sometimes sgdisk exits non-zero; this is OK, dd will clean up |
828 | + call(['sgdisk', '--zap-all', '--mbrtogpt', |
829 | + '--clear', block_device]) |
830 | + dev_end = check_output(['blockdev', '--getsz', block_device]) |
831 | + gpt_end = int(dev_end.split()[0]) - 100 |
832 | + check_call(['dd', 'if=/dev/zero', 'of=%s' % (block_device), |
833 | + 'bs=1M', 'count=1']) |
834 | + check_call(['dd', 'if=/dev/zero', 'of=%s' % (block_device), |
835 | + 'bs=512', 'count=100', 'seek=%s' % (gpt_end)]) |
836 | + |
837 | +def is_device_mounted(device): |
838 | + '''Given a device path, return True if that device is mounted, and False |
839 | + if it isn't. |
840 | + |
841 | + :param device: str: Full path of the device to check. |
842 | + :returns: boolean: True if the path represents a mounted device, False if |
843 | + it doesn't. |
844 | + ''' |
845 | + out = check_output(['mount']) |
846 | + return bool(re.search(device + r"[0-9]+\b", out)) |
847 | |
848 | === modified file 'hooks/charmhelpers/core/hookenv.py' |
849 | --- hooks/charmhelpers/core/hookenv.py 2014-03-26 17:52:44 +0000 |
850 | +++ hooks/charmhelpers/core/hookenv.py 2014-05-20 19:42:42 +0000 |
851 | @@ -155,6 +155,100 @@ |
852 | return os.path.basename(sys.argv[0]) |
853 | |
854 | |
855 | +class Config(dict): |
856 | + """A Juju charm config dictionary that can write itself to |
857 | + disk (as json) and track which values have changed since |
858 | + the previous hook invocation. |
859 | + |
860 | + Do not instantiate this object directly - instead call |
861 | + ``hookenv.config()`` |
862 | + |
863 | + Example usage:: |
864 | + |
865 | + >>> # inside a hook |
866 | + >>> from charmhelpers.core import hookenv |
867 | + >>> config = hookenv.config() |
868 | + >>> config['foo'] |
869 | + 'bar' |
870 | + >>> config['mykey'] = 'myval' |
871 | + >>> config.save() |
872 | + |
873 | + |
874 | + >>> # user runs `juju set mycharm foo=baz` |
875 | + >>> # now we're inside subsequent config-changed hook |
876 | + >>> config = hookenv.config() |
877 | + >>> config['foo'] |
878 | + 'baz' |
879 | + >>> # test to see if this val has changed since last hook |
880 | + >>> config.changed('foo') |
881 | + True |
882 | + >>> # what was the previous value? |
883 | + >>> config.previous('foo') |
884 | + 'bar' |
885 | + >>> # keys/values that we add are preserved across hooks |
886 | + >>> config['mykey'] |
887 | + 'myval' |
888 | + >>> # don't forget to save at the end of hook! |
889 | + >>> config.save() |
890 | + |
891 | + """ |
892 | + CONFIG_FILE_NAME = '.juju-persistent-config' |
893 | + |
894 | + def __init__(self, *args, **kw): |
895 | + super(Config, self).__init__(*args, **kw) |
896 | + self._prev_dict = None |
897 | + self.path = os.path.join(charm_dir(), Config.CONFIG_FILE_NAME) |
898 | + if os.path.exists(self.path): |
899 | + self.load_previous() |
900 | + |
901 | + def load_previous(self, path=None): |
902 | + """Load previous copy of config from disk so that current values |
903 | + can be compared to previous values. |
904 | + |
905 | + :param path: |
906 | + |
907 | + File path from which to load the previous config. If `None`, |
908 | + config is loaded from the default location. If `path` is |
909 | + specified, subsequent `save()` calls will write to the same |
910 | + path. |
911 | + |
912 | + """ |
913 | + self.path = path or self.path |
914 | + with open(self.path) as f: |
915 | + self._prev_dict = json.load(f) |
916 | + |
917 | + def changed(self, key): |
918 | + """Return true if the value for this key has changed since |
919 | + the last save. |
920 | + |
921 | + """ |
922 | + if self._prev_dict is None: |
923 | + return True |
924 | + return self.previous(key) != self.get(key) |
925 | + |
926 | + def previous(self, key): |
927 | + """Return previous value for this key, or None if there |
928 | + is no "previous" value. |
929 | + |
930 | + """ |
931 | + if self._prev_dict: |
932 | + return self._prev_dict.get(key) |
933 | + return None |
934 | + |
935 | + def save(self): |
936 | + """Save this config to disk. |
937 | + |
938 | + Preserves items in _prev_dict that do not exist in self. |
939 | + |
940 | + """ |
941 | + if self._prev_dict: |
942 | + for k, v in self._prev_dict.iteritems(): |
943 | + if k not in self: |
944 | + self[k] = v |
945 | + with open(self.path, 'w') as f: |
946 | + json.dump(self, f) |
947 | + |
948 | + |
949 | @cached |
950 | def config(scope=None): |
951 | """Juju charm configuration""" |
952 | @@ -163,7 +257,10 @@ |
953 | config_cmd_line.append(scope) |
954 | config_cmd_line.append('--format=json') |
955 | try: |
956 | - return json.loads(subprocess.check_output(config_cmd_line)) |
957 | + config_data = json.loads(subprocess.check_output(config_cmd_line)) |
958 | + if scope is not None: |
959 | + return config_data |
960 | + return Config(config_data) |
961 | except ValueError: |
962 | return None |
963 | |
964 | |
965 | === modified file 'hooks/charmhelpers/core/host.py' |
966 | --- hooks/charmhelpers/core/host.py 2014-03-26 17:52:44 +0000 |
967 | +++ hooks/charmhelpers/core/host.py 2014-05-20 19:42:42 +0000 |
968 | @@ -12,6 +12,9 @@ |
969 | import string |
970 | import subprocess |
971 | import hashlib |
972 | +import shutil |
973 | +import apt_pkg |
974 | +from contextlib import contextmanager |
975 | |
976 | from collections import OrderedDict |
977 | |
978 | @@ -143,6 +146,16 @@ |
979 | target.write(content) |
980 | |
981 | |
982 | +def copy_file(src, dst, owner='root', group='root', perms=0444): |
983 | + """Create or overwrite a file with the contents of another file""" |
984 | + log("Writing file {} {}:{} {:o} from {}".format(dst, owner, group, perms, src)) |
985 | + uid = pwd.getpwnam(owner).pw_uid |
986 | + gid = grp.getgrnam(group).gr_gid |
987 | + shutil.copyfile(src, dst) |
988 | + os.chown(dst, uid, gid) |
989 | + os.chmod(dst, perms) |
990 | + |
991 | + |
992 | def mount(device, mountpoint, options=None, persist=False): |
993 | """Mount a filesystem at a particular mountpoint""" |
994 | cmd_args = ['mount'] |
995 | @@ -295,3 +308,37 @@ |
996 | if 'link/ether' in words: |
997 | hwaddr = words[words.index('link/ether') + 1] |
998 | return hwaddr |
999 | + |
1000 | + |
1001 | +def cmp_pkgrevno(package, revno, pkgcache=None): |
1002 | + '''Compare supplied revno with the revno of the installed package |
1003 | + 1 => Installed revno is greater than supplied arg |
1004 | + 0 => Installed revno is the same as supplied arg |
1005 | + -1 => Installed revno is less than supplied arg |
1006 | + ''' |
1007 | + if not pkgcache: |
1008 | + apt_pkg.init() |
1009 | + pkgcache = apt_pkg.Cache() |
1010 | + pkg = pkgcache[package] |
1011 | + return apt_pkg.version_compare(pkg.current_ver.ver_str, revno) |
1012 | + |
1013 | + |
1014 | +@contextmanager |
1015 | +def chdir(d): |
1016 | + cur = os.getcwd() |
1017 | + try: |
1018 | + yield os.chdir(d) |
1019 | + finally: |
1020 | + os.chdir(cur) |
1021 | + |
1022 | + |
1023 | +def chownr(path, owner, group): |
1024 | + uid = pwd.getpwnam(owner).pw_uid |
1025 | + gid = grp.getgrnam(group).gr_gid |
1026 | + |
1027 | + for root, dirs, files in os.walk(path): |
1028 | + for name in dirs + files: |
1029 | + full = os.path.join(root, name) |
1030 | + broken_symlink = os.path.lexists(full) and not os.path.exists(full) |
1031 | + if not broken_symlink: |
1032 | + os.chown(full, uid, gid) |
1033 | |
1034 | === added file 'hooks/charmhelpers/core/services.py' |
1035 | --- hooks/charmhelpers/core/services.py 1970-01-01 00:00:00 +0000 |
1036 | +++ hooks/charmhelpers/core/services.py 2014-05-20 19:42:42 +0000 |
1037 | @@ -0,0 +1,84 @@ |
1038 | +from charmhelpers.core import templating |
1039 | +from charmhelpers.core import host |
1040 | + |
1041 | + |
1042 | +SERVICES = {} |
1043 | + |
1044 | + |
1045 | +def register(services, templates_dir=None): |
1046 | + """ |
1047 | + Register a list of service configs. |
1048 | + |
1049 | + Service Configs are dicts in the following formats: |
1050 | + |
1051 | + { |
1052 | + "service": <service name>, |
1053 | + "templates": [ { |
1054 | + 'target': <render target of template>, |
1055 | + 'source': <optional name of template in passed in templates_dir> |
1056 | + 'file_properties': <optional dict taking owner and octal mode> |
1057 | + 'contexts': [ context generators, see contexts.py ] |
1058 | + } |
1059 | + ] } |
1060 | + |
1061 | + Either `source` or `target` must be provided. |
1062 | + |
1063 | + If 'source' is not provided for a template the templates_dir will |
1064 | + be consulted for ``basename(target).j2``. |
1065 | + |
1066 | + If `target` is not provided, it will be assumed to be |
1067 | + ``/etc/init/<service name>.conf``. |
1068 | + """ |
1069 | + for service in services: |
1070 | + service.setdefault('templates_dir', templates_dir) |
1071 | + SERVICES[service['service']] = service |
1072 | + |
1073 | + |
1074 | +def reconfigure_services(restart=True): |
1075 | + """ |
1076 | + Update all files for all services and optionally restart them, if ready. |
1077 | + """ |
1078 | + for service_name in SERVICES.keys(): |
1079 | + reconfigure_service(service_name, restart=restart) |
1080 | + |
1081 | + |
1082 | +def reconfigure_service(service_name, restart=True): |
1083 | + """ |
1084 | + Update all files for a single service and optionally restart it, if ready. |
1085 | + """ |
1086 | + service = SERVICES.get(service_name) |
1087 | + if not service or service['service'] != service_name: |
1088 | + raise KeyError('Service not registered: %s' % service_name) |
1089 | + |
1090 | + manager_type = service.get('type', UpstartService) |
1091 | + manager_type(service).reconfigure(restart) |
1092 | + |
1093 | + |
1094 | +def stop_services(): |
1095 | + for service_name in SERVICES.keys(): |
1096 | + if host.service_running(service_name): |
1097 | + host.service_stop(service_name) |
1098 | + |
1099 | + |
1100 | +class ServiceTypeManager(object): |
1101 | + def __init__(self, service_definition): |
1102 | + self.service_name = service_definition['service'] |
1103 | + self.templates = service_definition['templates'] |
1104 | + self.templates_dir = service_definition['templates_dir'] |
1105 | + |
1106 | + def reconfigure(self, restart=True): |
1107 | + raise NotImplementedError() |
1108 | + |
1109 | + |
1110 | +class UpstartService(ServiceTypeManager): |
1111 | + def __init__(self, service_definition): |
1112 | + super(UpstartService, self).__init__(service_definition) |
1113 | + for tmpl in self.templates: |
1114 | + if 'target' not in tmpl: |
1115 | + tmpl['target'] = '/etc/init/%s.conf' % self.service_name |
1116 | + |
1117 | + def reconfigure(self, restart): |
1118 | + complete = templating.render(self.templates, self.templates_dir) |
1119 | + |
1120 | + if restart and complete: |
1121 | + host.service_restart(self.service_name) |
1122 | |
1123 | === added file 'hooks/charmhelpers/core/templating.py' |
1124 | --- hooks/charmhelpers/core/templating.py 1970-01-01 00:00:00 +0000 |
1125 | +++ hooks/charmhelpers/core/templating.py 2014-05-20 19:42:42 +0000 |
1126 | @@ -0,0 +1,158 @@ |
1127 | +import os |
1128 | +import yaml |
1129 | + |
1130 | +from charmhelpers.core import host |
1131 | +from charmhelpers.core import hookenv |
1132 | + |
1133 | + |
1134 | +class ContextGenerator(object): |
1135 | + """ |
1136 | + Base interface for template context container generators. |
1137 | + |
1138 | + A template context is a dictionary that contains data needed to populate |
1139 | + the template. The generator instance should produce the context when |
1140 | + called (without arguments) by collecting information from juju (config-get, |
1141 | + relation-get, etc), the system, or whatever other sources are appropriate. |
1142 | + |
1143 | + A context generator should only return any values if it has enough information |
1144 | + to provide all of its values. Any context that is missing data is considered |
1145 | + incomplete and will cause that template to not render until it has all of its |
1146 | + necessary data. |
1147 | + |
1148 | + The template may receive several contexts, which will be merged together, |
1149 | + so care should be taken in the key names. |
1150 | + """ |
1151 | + def __call__(self): |
1152 | + raise NotImplementedError |
1153 | + |
1154 | + |
1155 | +class StorableContext(object): |
1156 | + """ |
1157 | + A mixin for persisting a context to disk. |
1158 | + """ |
1159 | + def store_context(self, file_name, config_data): |
1160 | + with open(file_name, 'w') as file_stream: |
1161 | + yaml.dump(config_data, file_stream) |
1162 | + |
1163 | + def read_context(self, file_name): |
1164 | + with open(file_name, 'r') as file_stream: |
1165 | + data = yaml.load(file_stream) |
1166 | + if not data: |
1167 | + raise OSError("%s is empty" % file_name) |
1168 | + return data |
1169 | + |
1170 | + |
1171 | +class ConfigContext(ContextGenerator): |
1172 | + """ |
1173 | + A context generator that generates a context containing all of the |
1174 | + juju config values. |
1175 | + """ |
1176 | + def __call__(self): |
1177 | + return hookenv.config() |
1178 | + |
1179 | + |
1180 | +class RelationContext(ContextGenerator): |
1181 | + """ |
1182 | + Base class for a context generator that gets relation data from juju. |
1183 | + |
1184 | + Subclasses must provide `interface`, which is the interface type of interest, |
1185 | + and `required_keys`, which is the set of keys required for the relation to |
1186 | + be considered complete. The first relation for the interface that is complete |
1187 | + will be used to populate the data for template. |
1188 | + |
1189 | + The generated context will be namespaced under the interface type, to prevent |
1190 | + potential naming conflicts. |
1191 | + """ |
1192 | + interface = None |
1193 | + required_keys = [] |
1194 | + |
1195 | + def __call__(self): |
1196 | + if not hookenv.relation_ids(self.interface): |
1197 | + return {} |
1198 | + |
1199 | + ctx = {} |
1200 | + for rid in hookenv.relation_ids(self.interface): |
1201 | + for unit in hookenv.related_units(rid): |
1202 | + reldata = hookenv.relation_get(rid=rid, unit=unit) |
1203 | + required = set(self.required_keys) |
1204 | + if set(reldata.keys()).issuperset(required): |
1205 | + ns = ctx.setdefault(self.interface, {}) |
1206 | + for k, v in reldata.items(): |
1207 | + ns[k] = v |
1208 | + return ctx |
1209 | + |
1210 | + return {} |
1211 | + |
1212 | + |
1213 | +class StaticContext(ContextGenerator): |
1214 | + def __init__(self, data): |
1215 | + self.data = data |
1216 | + |
1217 | + def __call__(self): |
1218 | + return self.data |
1219 | + |
1220 | + |
1221 | +def _collect_contexts(context_providers): |
1222 | + """ |
1223 | + Helper function to collect and merge contexts from a list of providers. |
1224 | + |
1225 | + If any of the contexts are incomplete (i.e., they return an empty dict), |
1226 | + the template is considered incomplete and will not render. |
1227 | + """ |
1228 | + ctx = {} |
1229 | + for provider in context_providers: |
1230 | + c = provider() |
1231 | + if not c: |
1232 | + return False |
1233 | + ctx.update(c) |
1234 | + return ctx |
1235 | + |
1236 | + |
1237 | +def render(template_definitions, templates_dir=None): |
1238 | + """ |
1239 | + Render one or more templates, given a list of template definitions. |
1240 | + |
1241 | + The template definitions should be dicts with the keys: `source`, `target`, |
1242 | + `file_properties`, and `contexts`. |
1243 | + |
1244 | + The `source` path, if not absolute, is relative to the `templates_dir` |
1245 | + given when the rendered was created. If `source` is not provided |
1246 | + for a template the `template_dir` will be consulted for |
1247 | + ``basename(target).j2``. |
1248 | + |
1249 | + The `target` path should be absolute. |
1250 | + |
1251 | + The `file_properties` should be a dict optionally containing |
1252 | + `owner`, `group`, or `perms` options, to be passed to `write_file`. |
1253 | + |
1254 | + The `contexts` should be a list containing zero or more ContextGenerators. |
1255 | + |
1256 | + The `template_dir` defaults to `$CHARM_DIR/templates` |
1257 | + |
1258 | + Returns True if all of the templates were "complete" (i.e., the context |
1259 | + generators were able to collect the information needed to render the |
1260 | + template) and were rendered. |
1261 | + """ |
1262 | + # lazy import jinja2 in case templating is needed in install hook |
1263 | + from jinja2 import FileSystemLoader, Environment, exceptions |
1264 | + all_complete = True |
1265 | + if templates_dir is None: |
1266 | + templates_dir = os.path.join(hookenv.charm_dir(), 'templates') |
1267 | + loader = Environment(loader=FileSystemLoader(templates_dir)) |
1268 | + for tmpl in template_definitions: |
1269 | + ctx = _collect_contexts(tmpl.get('contexts', [])) |
1270 | + if ctx is False: |
1271 | + all_complete = False |
1272 | + continue |
1273 | + try: |
1274 | + source = tmpl.get('source', os.path.basename(tmpl['target'])+'.j2') |
1275 | + template = loader.get_template(source) |
1276 | + except exceptions.TemplateNotFound as e: |
1277 | + hookenv.log('Could not load template %s from %s.' % |
1278 | + (tmpl['source'], templates_dir), |
1279 | + level=hookenv.ERROR) |
1280 | + raise e |
1281 | + content = template.render(ctx) |
1282 | + host.mkdir(os.path.dirname(tmpl['target'])) |
1283 | + host.write_file(tmpl['target'], content, **tmpl.get('file_properties', {})) |
1284 | + return all_complete |
1285 | |
1286 | === modified file 'hooks/charmhelpers/fetch/__init__.py' |
1287 | --- hooks/charmhelpers/fetch/__init__.py 2014-03-31 22:27:22 +0000 |
1288 | +++ hooks/charmhelpers/fetch/__init__.py 2014-05-20 19:42:42 +0000 |
1289 | @@ -1,4 +1,5 @@ |
1290 | import importlib |
1291 | +import time |
1292 | from yaml import safe_load |
1293 | from charmhelpers.core.host import ( |
1294 | lsb_release |
1295 | @@ -15,6 +16,7 @@ |
1296 | import apt_pkg |
1297 | import os |
1298 | |
1299 | + |
1300 | CLOUD_ARCHIVE = """# Ubuntu Cloud Archive |
1301 | deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main |
1302 | """ |
1303 | @@ -56,10 +58,62 @@ |
1304 | 'precise-proposed/icehouse': 'precise-proposed/icehouse', |
1305 | } |
1306 | |
1307 | +# The order of this list is very important. Handlers should be listed in from |
1308 | +# least- to most-specific URL matching. |
1309 | +FETCH_HANDLERS = ( |
1310 | + 'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler', |
1311 | + 'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler', |
1312 | +) |
1313 | + |
1314 | +APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT. |
1315 | +APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks. |
1316 | +APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times. |
1317 | + |
1318 | + |
1319 | +class SourceConfigError(Exception): |
1320 | + pass |
1321 | + |
1322 | + |
1323 | +class UnhandledSource(Exception): |
1324 | + pass |
1325 | + |
1326 | + |
1327 | +class AptLockError(Exception): |
1328 | + pass |
1329 | + |
1330 | + |
1331 | +class BaseFetchHandler(object): |
1332 | + |
1333 | + """Base class for FetchHandler implementations in fetch plugins""" |
1334 | + |
1335 | + def can_handle(self, source): |
1336 | + """Returns True if the source can be handled. Otherwise returns |
1337 | + a string explaining why it cannot""" |
1338 | + return "Wrong source type" |
1339 | + |
1340 | + def install(self, source): |
1341 | + """Try to download and unpack the source. Return the path to the |
1342 | + unpacked files or raise UnhandledSource.""" |
1343 | + raise UnhandledSource("Wrong source type {}".format(source)) |
1344 | + |
1345 | + def parse_url(self, url): |
1346 | + return urlparse(url) |
1347 | + |
1348 | + def base_url(self, url): |
1349 | + """Return url without querystring or fragment""" |
1350 | + parts = list(self.parse_url(url)) |
1351 | + parts[4:] = ['' for i in parts[4:]] |
1352 | + return urlunparse(parts) |
1353 | + |
1354 | |
1355 | def filter_installed_packages(packages): |
1356 | """Returns a list of packages that require installation""" |
1357 | apt_pkg.init() |
1358 | + |
1359 | + # Tell apt to build an in-memory cache to prevent race conditions (if |
1360 | + # another process is already building the cache). |
1361 | + apt_pkg.config.set("Dir::Cache::pkgcache", "") |
1362 | + |
1363 | cache = apt_pkg.Cache() |
1364 | _pkgs = [] |
1365 | for package in packages: |
1366 | @@ -87,14 +141,7 @@ |
1367 | cmd.extend(packages) |
1368 | log("Installing {} with options: {}".format(packages, |
1369 | options)) |
1370 | - env = os.environ.copy() |
1371 | - if 'DEBIAN_FRONTEND' not in env: |
1372 | - env['DEBIAN_FRONTEND'] = 'noninteractive' |
1373 | - |
1374 | - if fatal: |
1375 | - subprocess.check_call(cmd, env=env) |
1376 | - else: |
1377 | - subprocess.call(cmd, env=env) |
1378 | + _run_apt_command(cmd, fatal) |
1379 | |
1380 | |
1381 | def apt_upgrade(options=None, fatal=False, dist=False): |
1382 | @@ -109,24 +156,13 @@ |
1383 | else: |
1384 | cmd.append('upgrade') |
1385 | log("Upgrading with options: {}".format(options)) |
1386 | - |
1387 | - env = os.environ.copy() |
1388 | - if 'DEBIAN_FRONTEND' not in env: |
1389 | - env['DEBIAN_FRONTEND'] = 'noninteractive' |
1390 | - |
1391 | - if fatal: |
1392 | - subprocess.check_call(cmd, env=env) |
1393 | - else: |
1394 | - subprocess.call(cmd, env=env) |
1395 | + _run_apt_command(cmd, fatal) |
1396 | |
1397 | |
1398 | def apt_update(fatal=False): |
1399 | """Update local apt cache""" |
1400 | cmd = ['apt-get', 'update'] |
1401 | - if fatal: |
1402 | - subprocess.check_call(cmd) |
1403 | - else: |
1404 | - subprocess.call(cmd) |
1405 | + _run_apt_command(cmd, fatal) |
1406 | |
1407 | |
1408 | def apt_purge(packages, fatal=False): |
1409 | @@ -137,10 +173,7 @@ |
1410 | else: |
1411 | cmd.extend(packages) |
1412 | log("Purging {}".format(packages)) |
1413 | - if fatal: |
1414 | - subprocess.check_call(cmd) |
1415 | - else: |
1416 | - subprocess.call(cmd) |
1417 | + _run_apt_command(cmd, fatal) |
1418 | |
1419 | |
1420 | def apt_hold(packages, fatal=False): |
1421 | @@ -151,6 +184,7 @@ |
1422 | else: |
1423 | cmd.extend(packages) |
1424 | log("Holding {}".format(packages)) |
1425 | + |
1426 | if fatal: |
1427 | subprocess.check_call(cmd) |
1428 | else: |
1429 | @@ -184,14 +218,10 @@ |
1430 | apt.write(PROPOSED_POCKET.format(release)) |
1431 | if key: |
1432 | subprocess.check_call(['apt-key', 'adv', '--keyserver', |
1433 | - 'keyserver.ubuntu.com', '--recv', |
1434 | + 'hkp://keyserver.ubuntu.com:80', '--recv', |
1435 | key]) |
1436 | |
1437 | |
1438 | -class SourceConfigError(Exception): |
1439 | - pass |
1440 | - |
1441 | - |
1442 | def configure_sources(update=False, |
1443 | sources_var='install_sources', |
1444 | keys_var='install_keys'): |
1445 | @@ -224,17 +254,6 @@ |
1446 | if update: |
1447 | apt_update(fatal=True) |
1448 | |
1449 | -# The order of this list is very important. Handlers should be listed in from |
1450 | -# least- to most-specific URL matching. |
1451 | -FETCH_HANDLERS = ( |
1452 | - 'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler', |
1453 | - 'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler', |
1454 | -) |
1455 | - |
1456 | - |
1457 | -class UnhandledSource(Exception): |
1458 | - pass |
1459 | - |
1460 | |
1461 | def install_remote(source): |
1462 | """ |
1463 | @@ -265,30 +284,6 @@ |
1464 | return install_remote(source) |
1465 | |
1466 | |
1467 | -class BaseFetchHandler(object): |
1468 | - |
1469 | - """Base class for FetchHandler implementations in fetch plugins""" |
1470 | - |
1471 | - def can_handle(self, source): |
1472 | - """Returns True if the source can be handled. Otherwise returns |
1473 | - a string explaining why it cannot""" |
1474 | - return "Wrong source type" |
1475 | - |
1476 | - def install(self, source): |
1477 | - """Try to download and unpack the source. Return the path to the |
1478 | - unpacked files or raise UnhandledSource.""" |
1479 | - raise UnhandledSource("Wrong source type {}".format(source)) |
1480 | - |
1481 | - def parse_url(self, url): |
1482 | - return urlparse(url) |
1483 | - |
1484 | - def base_url(self, url): |
1485 | - """Return url without querystring or fragment""" |
1486 | - parts = list(self.parse_url(url)) |
1487 | - parts[4:] = ['' for i in parts[4:]] |
1488 | - return urlunparse(parts) |
1489 | - |
1490 | - |
1491 | def plugins(fetch_handlers=None): |
1492 | if not fetch_handlers: |
1493 | fetch_handlers = FETCH_HANDLERS |
1494 | @@ -306,3 +301,40 @@ |
1495 | log("FetchHandler {} not found, skipping plugin".format( |
1496 | handler_name)) |
1497 | return plugin_list |
1498 | + |
1499 | + |
1500 | +def _run_apt_command(cmd, fatal=False): |
1501 | + """ |
1502 | + Run an APT command, checking output and retrying if the fatal flag is set |
1503 | + to True. |
1504 | + |
1505 | + :param: cmd: str: The apt command to run. |
1506 | + :param: fatal: bool: Whether the command's output should be checked and |
1507 | + retried. |
1508 | + """ |
1509 | + env = os.environ.copy() |
1510 | + |
1511 | + if 'DEBIAN_FRONTEND' not in env: |
1512 | + env['DEBIAN_FRONTEND'] = 'noninteractive' |
1513 | + |
1514 | + if fatal: |
1515 | + retry_count = 0 |
1516 | + result = None |
1517 | + |
1518 | + # If the command is considered "fatal", we need to retry if the apt |
1519 | + # lock was not acquired. |
1520 | + |
1521 | + while result is None or result == APT_NO_LOCK: |
1522 | + try: |
1523 | + result = subprocess.check_call(cmd, env=env) |
1524 | + except subprocess.CalledProcessError, e: |
1525 | + retry_count = retry_count + 1 |
1526 | + if retry_count > APT_NO_LOCK_RETRY_COUNT: |
1527 | + raise |
1528 | + result = e.returncode |
1529 | + log("Couldn't acquire DPKG lock. Will retry in {} seconds." |
1530 | + "".format(APT_NO_LOCK_RETRY_DELAY)) |
1531 | + time.sleep(APT_NO_LOCK_RETRY_DELAY) |
1532 | + |
1533 | + else: |
1534 | + subprocess.call(cmd, env=env) |
1535 | |
1536 | === modified file 'hooks/hooks.py' |
1537 | --- hooks/hooks.py 2014-04-07 02:47:19 +0000 |
1538 | +++ hooks/hooks.py 2014-05-20 19:42:42 +0000 |
1539 | @@ -3,13 +3,12 @@ |
1540 | import os |
1541 | import sys |
1542 | |
1543 | -from charmhelpers.core import hookenv, host |
1544 | -from charmhelpers.core.host import service_start |
1545 | +from charmhelpers.core import hookenv |
1546 | from charmhelpers.core.hookenv import log, unit_get |
1547 | +from charmhelpers.core import services |
1548 | +from charmhelpers.core import templating |
1549 | from charmhelpers.contrib.cloudfoundry import contexts |
1550 | -from charmhelpers.contrib.cloudfoundry.install import install |
1551 | -from charmhelpers.contrib.cloudfoundry import services |
1552 | -from config import * |
1553 | +import config |
1554 | |
1555 | |
1556 | hooks = hookenv.Hooks() |
1557 | @@ -17,21 +16,29 @@ |
1558 | |
1559 | uaa_address = unit_get('private-address').encode('utf-8') |
1560 | |
1561 | -uaa_service_templates = [('uaa.yml', UAA_CONFIG_FILE), |
1562 | - ('varz.yml', VARZ_CONFIG_FILE)] |
1563 | - |
1564 | services.register([ |
1565 | { |
1566 | + 'service': 'cf-uaa', |
1567 | + 'templates': [ |
1568 | + {'source': 'cf-uaa.conf'}, |
1569 | + {'source': 'uaa.yml', |
1570 | + 'target': config.config_FILE}, |
1571 | + {'source': 'varz.yml', |
1572 | + 'target': config.VARZ_CONFIG_FILE}, |
1573 | + ], |
1574 | + }, |
1575 | + { |
1576 | 'service': 'cf-registrar', |
1577 | - 'templates': [{ |
1578 | - 'source': 'registrar.yml', |
1579 | - 'target': REGISTRAR_CONFIG_FILE, |
1580 | - 'contexts': [contexts.NatsContext(), |
1581 | - contexts.RouterContext(), |
1582 | - contexts.StaticContext({'uaa_address': uaa_address})] |
1583 | - }] |
1584 | + 'templates': [ |
1585 | + {'source': 'cf-registrar.conf'}, |
1586 | + {'source': 'registrar.yml', |
1587 | + 'target': config.REGISTRAR_CONFIG_FILE, |
1588 | + 'contexts': [contexts.NatsContext(), |
1589 | + contexts.RouterContext(), |
1590 | + templating.StaticContext({'uaa_address': uaa_address})]}, |
1591 | + ], |
1592 | } |
1593 | -], TEMPLATE_DIR) |
1594 | +]) |
1595 | |
1596 | |
1597 | @hooks.hook("config-changed") |
1598 | @@ -41,9 +48,7 @@ |
1599 | |
1600 | @hooks.hook() |
1601 | def start(): |
1602 | - for src, dest in uaa_service_templates: |
1603 | - install(os.path.join(TEMPLATE_DIR, src), dest) |
1604 | - host.service_start('cf-uaa') |
1605 | + pass |
1606 | |
1607 | |
1608 | @hooks.hook() |
1609 | |
1610 | === modified file 'hooks/install' |
1611 | --- hooks/install 2014-04-08 07:41:32 +0000 |
1612 | +++ hooks/install 2014-05-20 19:42:42 +0000 |
1613 | @@ -2,49 +2,53 @@ |
1614 | # vim: et ai ts=4 sw=4: |
1615 | import os |
1616 | import shutil |
1617 | +from subprocess import check_call |
1618 | |
1619 | from charmhelpers.core import hookenv, host |
1620 | from charmhelpers.core.hookenv import log, DEBUG |
1621 | from charmhelpers.contrib.cloudfoundry.common import ( |
1622 | - chownr, run, prepare_cloudfoundry_environment |
1623 | -) |
1624 | -from charmhelpers.contrib.cloudfoundry.upstart_helper import ( |
1625 | - install_upstart_scripts |
1626 | + prepare_cloudfoundry_environment |
1627 | ) |
1628 | |
1629 | -from config import * |
1630 | +import config |
1631 | |
1632 | |
1633 | def install(): |
1634 | - prepare_cloudfoundry_environment(hookenv.config(), UAA_PACKAGES) |
1635 | - install_upstart_scripts() |
1636 | + prepare_cloudfoundry_environment(hookenv.config(), config.UAA_PACKAGES) |
1637 | |
1638 | if os.path.isfile('/etc/init.d/tomcat7'): |
1639 | - run(['update-rc.d', '-f', 'tomcat7', 'remove']) |
1640 | + check_call(['update-rc.d', '-f', 'tomcat7', 'remove']) |
1641 | log("Stopping Tomcat ...", DEBUG) |
1642 | host.service_stop('tomcat7') |
1643 | os.remove('/etc/init.d/tomcat7') |
1644 | - dirs = [CF_DIR, RUN_DIR, LOG_DIR, TOMCAT_HOME, |
1645 | - UAA_DIR, UAA_CONFIG_DIR, VARZ_CONFIG_DIR, |
1646 | - REGISTRAR_DIR, REGISTRAR_CONFIG_DIR] |
1647 | + dirs = [config.CF_DIR, |
1648 | + config.RUN_DIR, |
1649 | + config.LOG_DIR, |
1650 | + config.TOMCAT_HOME, |
1651 | + config.UAA_DIR, |
1652 | + config.config_DIR, |
1653 | + config.VARZ_CONFIG_DIR, |
1654 | + config.REGISTRAR_DIR, |
1655 | + config.REGISTRAR_CONFIG_DIR] |
1656 | for item in dirs: |
1657 | host.mkdir(item, owner='vcap', group='vcap', perms=0775) |
1658 | - if not os.path.isfile(os.path.join(TOMCAT_HOME, |
1659 | + if not os.path.isfile(os.path.join(config.TOMCAT_HOME, |
1660 | 'lib', 'sqlite-jdbc-3.7.2.jar')): |
1661 | - os.chdir(os.path.join(TOMCAT_HOME, 'lib')) |
1662 | - log('Installing SQLite jdbc driver jar into Tomcat lib directory', |
1663 | - DEBUG) |
1664 | - # TODO consider installing from charm |
1665 | - run(['wget', 'https://bitbucket.org/xerial/sqlite-jdbc/downloads/' |
1666 | - 'sqlite-jdbc-3.7.2.jar']) |
1667 | + with host.chdir(os.path.join(config.TOMCAT_HOME, 'lib')): |
1668 | + log('Installing SQLite jdbc driver jar into Tomcat lib directory', |
1669 | + DEBUG) |
1670 | + # TODO consider installing from charm |
1671 | + check_call(['wget', |
1672 | + 'https://bitbucket.org/xerial/sqlite-jdbc/downloads/' |
1673 | + 'sqlite-jdbc-3.7.2.jar']) |
1674 | log("Cleaning up old config files", DEBUG) |
1675 | - shutil.rmtree(UAA_CONFIG_DIR) |
1676 | - shutil.copytree(os.path.join(hookenv.charm_dir(), |
1677 | - 'files/config'), UAA_CONFIG_DIR) |
1678 | + shutil.rmtree(config.config_DIR) |
1679 | + shutil.copytree(os.path.join(hookenv.charm_dir(), 'files/config'), |
1680 | + config.config_DIR) |
1681 | |
1682 | # TODO do it in package: create vcap user and dir structures |
1683 | - chownr('/var/vcap', owner='vcap', group='vcap') |
1684 | - chownr(CF_DIR, owner='vcap', group='vcap') |
1685 | + host.chownr('/var/vcap', owner='vcap', group='vcap') |
1686 | + host.chownr(config.CF_DIR, owner='vcap', group='vcap') |
1687 | |
1688 | |
1689 | if __name__ == '__main__': |
1690 | |
1691 | === renamed file 'files/upstart/cf-registrar.conf' => 'templates/cf-registrar.conf' |
1692 | === renamed file 'files/upstart/cf-uaa.conf' => 'templates/cf-uaa.conf' |
Reviewers: mp+219918_ code.launchpad. net,
Message:
Please take a look.
Description:
Refactored to use refactored charm-helpers
https:/ /code.launchpad .net/~johnsca/ charms/ trusty/ cf-uaa/ refactor/ +merge/ 219918
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/91450049/
Affected files (+491, -375 lines): ers/contrib/ cloudfoundry/ common. py ers/contrib/ cloudfoundry/ config_ helper. py ers/contrib/ cloudfoundry/ contexts. py ers/contrib/ cloudfoundry/ install. py ers/contrib/ cloudfoundry/ services. py ers/contrib/ cloudfoundry/ upstart_ helper. py ers/contrib/ hahelpers/ apache. py ers/contrib/ openstack/ context. py ers/contrib/ openstack/ neutron. py ers/contrib/ openstack/ utils.py ers/contrib/ storage/ linux/utils. py ers/core/ host.py ers/core/ services. py ers/core/ templating. py ers/fetch/ __init_ _.py cf-registrar. conf cf-uaa. conf
A [revision details]
M hooks/charmhelp
D hooks/charmhelp
M hooks/charmhelp
D hooks/charmhelp
D hooks/charmhelp
D hooks/charmhelp
M hooks/charmhelp
M hooks/charmhelp
M hooks/charmhelp
M hooks/charmhelp
M hooks/charmhelp
M hooks/charmhelp
A hooks/charmhelp
A hooks/charmhelp
M hooks/charmhelp
M hooks/hooks.py
M hooks/install
M templates/
M templates/