~tvansteenburgh/charms/precise/haproxy/sticky-sessions

« back to all changes in this revision

Viewing changes to hooks/hooks.py

[sidnei] This restores some functionality that got removed by accident during my refactoring and was even documented in README.md, namely, that a service can specify a piece of yaml via relation set services=<> to be used when generating the haproxy stanzas.

Show diffs side-by-side

added added

removed removed

Lines of Context:
79
79
# Supporting functions
80
80
###############################################################################
81
81
 
 
82
def comma_split(value):
 
83
    values = value.split(",")
 
84
    return filter(None, (v.strip() for v in values))
 
85
 
82
86
 
83
87
def ensure_package_status(packages, status):
84
88
    if status in ['install', 'hold']:
105
109
#------------------------------------------------------------------------------
106
110
def create_haproxy_globals():
107
111
    config_data = config_get()
108
 
    global_log = config_data['global_log'].split(',')
 
112
    global_log = comma_split(config_data['global_log'])
109
113
    haproxy_globals = []
110
114
    haproxy_globals.append('global')
111
115
    for global_log_item in global_log:
127
131
#------------------------------------------------------------------------------
128
132
def create_haproxy_defaults():
129
133
    config_data = config_get()
130
 
    default_options = config_data['default_options'].split(',')
131
 
    default_timeouts = config_data['default_timeouts'].split(',')
 
134
    default_options = comma_split(config_data['default_options'])
 
135
    default_timeouts = comma_split(config_data['default_timeouts'])
132
136
    haproxy_defaults = []
133
137
    haproxy_defaults.append("defaults")
134
138
    haproxy_defaults.append("    log %s" % config_data['default_log'])
286
290
            server_line = "    server %s %s:%s" % \
287
291
                (server_name, server_ip, server_port)
288
292
            if server_options is not None:
289
 
                server_line += " %s" % " ".join(server_options)
 
293
                if isinstance(server_options, basestring):
 
294
                    server_line += " " + server_options
 
295
                else:
 
296
                    server_line += " " + " ".join(server_options)
290
297
            service_config.append(server_line)
291
298
    return '\n'.join(service_config)
292
299
 
331
338
def get_config_services():
332
339
    config_data = config_get()
333
340
    services = {}
334
 
    for service in yaml.safe_load(config_data['services']):
 
341
    return parse_services_yaml(services, config_data['services'])
 
342
 
 
343
 
 
344
def parse_services_yaml(services, yaml_data):
 
345
    for service in yaml.safe_load(yaml_data):
335
346
        service_name = service["service_name"]
336
347
        if not services:
337
348
            # 'None' is used as a marker for the first service defined, which
338
349
            # is used as the default service if a proxied server doesn't
339
350
            # specify which service it is bound to.
340
351
            services[None] = {"service_name": service_name}
341
 
        if is_proxy(service_name) and ("option forwardfor" not in
342
 
                                       service["service_options"]):
343
 
            service["service_options"].append("option forwardfor")
344
 
 
345
 
        if isinstance(service["server_options"], basestring):
346
 
            service["server_options"] = service["server_options"].split()
347
 
 
348
 
        services[service_name] = service
 
352
 
 
353
        if "service_options" in service:
 
354
            if isinstance(service["service_options"], basestring):
 
355
                service["service_options"] = comma_split(
 
356
                    service["service_options"])
 
357
 
 
358
            if is_proxy(service_name) and ("option forwardfor" not in
 
359
                                           service["service_options"]):
 
360
                service["service_options"].append("option forwardfor")
 
361
 
 
362
        if (("server_options" in service and
 
363
             isinstance(service["server_options"], basestring))):
 
364
            service["server_options"] = comma_split(service["server_options"])
 
365
 
 
366
        services[service_name] = merge_service(
 
367
            services.get(service_name, {}), service)
 
368
    return services
 
369
 
 
370
 
 
371
def merge_service(old_service, new_service):
 
372
    # Stomp over all but servers
 
373
    for key in new_service:
 
374
        if key not in ("services",):
 
375
            old_service[key] = new_service[key]
 
376
 
 
377
    # Stomp over duplicate server definitions.
 
378
    if old_service.get("servers") and new_service.get("servers"):
 
379
        servers = {}
 
380
        for service in (old_service, new_service):
 
381
            for server_name, host, port, options in service.get("servers", ()):
 
382
                servers[(host, port)] = (server_name, options)
 
383
 
 
384
        old_service["servers"] = [
 
385
            (server_name, host, port, options)
 
386
            for (host, port), (server_name, options) in sorted(
 
387
                servers.iteritems())]
 
388
 
 
389
    return old_service
 
390
 
 
391
 
 
392
def ensure_service_host_port(services):
 
393
    config_data = config_get()
 
394
    seen = []
 
395
    missing = []
 
396
    for service, options in sorted(services.iteritems()):
 
397
        if not "service_host" in options:
 
398
            missing.append(options)
 
399
            continue
 
400
        if not "service_port" in options:
 
401
            missing.append(options)
 
402
            continue
 
403
        seen.append((options["service_host"], int(options["service_port"])))
 
404
 
 
405
    seen.sort()
 
406
    last_port = seen and seen[-1][1] or int(config_data["monitoring_port"])
 
407
    for options in missing:
 
408
        last_port += 2
 
409
        options["service_host"] = "0.0.0.0"
 
410
        options["service_port"] = last_port
349
411
 
350
412
    return services
351
413
 
370
432
#------------------------------------------------------------------------------
371
433
def create_services():
372
434
    services_dict = get_config_services()
 
435
 
 
436
    # Augment services_dict with service definitions from relation data.
 
437
    relation_data = relations_of_type("reverseproxy")
 
438
    for relation_info in relation_data:
 
439
        if "services" in relation_info:
 
440
            services_dict = parse_services_yaml(services_dict,
 
441
                                                relation_info['services'])
 
442
 
373
443
    if len(services_dict) == 0:
374
444
        log("No services configured, exiting.")
375
445
        return
378
448
 
379
449
    for relation_info in relation_data:
380
450
        unit = relation_info['__unit__']
 
451
 
 
452
        if "services" in relation_info:
 
453
            log("Unit '%s' overrides 'services', "
 
454
                "skipping further processing." % unit)
 
455
            continue
 
456
 
381
457
        juju_service_name = unit.rpartition('/')[0]
382
458
 
383
459
        relation_ok = True
384
 
        for required in ("port", "private-address", "hostname"):
 
460
        for required in ("port", "private-address"):
385
461
            if not required in relation_info:
386
462
                log("No %s in relation data for '%s', skipping." %
387
463
                    (required, unit))
440
516
        return
441
517
 
442
518
    del services_dict[None]
 
519
    services_dict = ensure_service_host_port(services_dict)
443
520
    services_dict = apply_peer_config(services_dict)
444
521
    write_service_config(services_dict)
445
522
    return services_dict