From fa5ec0fb44d2061ae3cb784ba64ef30a37e518a3 Mon Sep 17 00:00:00 2001 From: Matt DeBoer Date: Wed, 9 Nov 2016 14:09:56 -0800 Subject: [PATCH] add HAPROXY_GLOBAL_DEFAULT_OPTIONS via environment variable added 'TCPLOG' option more tests, and 'is_option_enabled' update tests implemented HAPROXY_DEFAULT_OPTIONS update docs applied latest suggestions rename to global_default_options code & doc cleanup --- README.md | 8 ++ config.py | 12 ++- tests/test_marathon_lb_haproxy_options.py | 115 ++++++++++++++++++++++ 3 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 tests/test_marathon_lb_haproxy_options.py diff --git a/README.md b/README.md index 79eff35b..63d1c75e 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,14 @@ Here is an example for a service called `http-service` which requires that The full list of per service port templates which can be specified are [documented here](Longhelp.md#templates). +#### HAProxy Global Default Options + +As a shortcut to add haproxy global default options (without overriding the global template) a comma-separated +list of options may be specified via the `HAPROXY_GLOBAL_DEFAULT_OPTIONS` environment variable. +The default value when not specified is `redispatch,http-server-close,dontlognull`; as an example, to add the +`httplog` option (and keep the existing defaults), one should specify `HAPROXY_GLOBAL_DEFAULT_OPTIONS=redispatch,http-server-close,dontlognull,httplog`. + - _Note that this setting has no effect when the `HAPROXY_HEAD` template has been overridden._ + ## Operational Best Practices * Use service ports within the reserved range (which is 10000 to 10100 by default). This will prevent port conflicts, and ensure reloads don't result in connection errors. diff --git a/config.py b/config.py index 1937af40..de370618 100644 --- a/config.py +++ b/config.py @@ -20,6 +20,14 @@ class ConfigTemplater(object): def add_template(self, template): self.t[template.name] = template + def global_default_options(self): + default = 'redispatch,http-server-close,dontlognull' + options = os.getenv('HAPROXY_GLOBAL_DEFAULT_OPTIONS', default) + template = ' option {opt}\n' + lines = set(template.format(opt=opt.strip()) + for opt in options.split(',')) + return ''.join(lines) + def load(self): self.add_template( ConfigTemplate(name='HEAD', @@ -77,9 +85,7 @@ def load(self): timeout http-request 15s timeout queue 30s timeout tarpit 60s - option redispatch - option http-server-close - option dontlognull +''' + self.additional_default_options() + '''\ listen stats bind 0.0.0.0:9090 balance diff --git a/tests/test_marathon_lb_haproxy_options.py b/tests/test_marathon_lb_haproxy_options.py new file mode 100644 index 00000000..75d64252 --- /dev/null +++ b/tests/test_marathon_lb_haproxy_options.py @@ -0,0 +1,115 @@ +import copy +import json +import unittest +import os + +import marathon_lb +from tests.test_marathon_lb import TestMarathonUpdateHaproxy + + +def template_option(opt): + return ' option {opt}\n'.format(opt=opt) + + +base_config_prefix = '''global + daemon + log /dev/log local0 + log /dev/log local1 notice + spread-checks 5 + max-spread-checks 15000 + maxconn 50000 + tune.ssl.default-dh-param 2048 + ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:\ +ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\ +ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ +ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:\ +DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ +ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:\ +ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:\ +DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:\ +DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\ +EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:\ +AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS + ssl-default-bind-options no-sslv3 no-tlsv10 no-tls-tickets + ssl-default-server-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:\ +ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\ +ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ +ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:\ +DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ +ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:\ +ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:\ +DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:\ +DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\ +EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:\ +AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS + ssl-default-server-options no-sslv3 no-tlsv10 no-tls-tickets + stats socket /var/run/haproxy/socket + server-state-file global + server-state-base /var/state/haproxy/ + lua-load /marathon-lb/getpids.lua + lua-load /marathon-lb/getconfig.lua + lua-load /marathon-lb/getmaps.lua + lua-load /marathon-lb/signalmlb.lua +defaults + load-server-state-from-file global + log global + retries 3 + backlog 10000 + maxconn 10000 + timeout connect 3s + timeout client 30s + timeout server 30s + timeout tunnel 3600s + timeout http-keep-alive 1s + timeout http-request 15s + timeout queue 30s + timeout tarpit 60s +''' + +base_config_suffix = '''\ +listen stats + bind 0.0.0.0:9090 + balance + mode http + stats enable + monitor-uri /_haproxy_health_check + acl getpid path /_haproxy_getpids + http-request use-service lua.getpids if getpid + acl getvhostmap path /_haproxy_getvhostmap + http-request use-service lua.getvhostmap if getvhostmap + acl getappmap path /_haproxy_getappmap + http-request use-service lua.getappmap if getappmap + acl getconfig path /_haproxy_getconfig + http-request use-service lua.getconfig if getconfig + + acl signalmlbhup path /_mlb_signal/hup + http-request use-service lua.signalmlbhup if signalmlbhup + acl signalmlbusr1 path /_mlb_signal/usr1 + http-request use-service lua.signalmlbusr1 if signalmlbusr1 +''' + + +class TestAdditionalOptions(TestMarathonUpdateHaproxy): + + def setUp(self): + self.maxDiff = None + os.environ['HAPROXY_GLOBAL_DEFAULT_OPTIONS'] = 'httplog,tcplog' + base_config = base_config_prefix + base_config += template_option('httplog') + base_config += template_option('tcplog') + base_config += base_config_suffix + self.base_config = base_config + + +class TestDuplicatedOptions(TestMarathonUpdateHaproxy): + + def setUp(self): + self.maxDiff = None + os.environ['HAPROXY_GLOBAL_DEFAULT_OPTIONS'] = \ + 'httplog,tcplog,dontlognull,tcplog' + base_config = base_config_prefix + base_config += template_option('httplog') + base_config += template_option('dontlognull') + base_config += template_option('tcplog') + base_config += base_config_suffix + self.base_config = base_config