From 00df6ceec59b87d1f615f696088e74dc1fe3c189 Mon Sep 17 00:00:00 2001 From: Brenden Matthews Date: Tue, 24 Jan 2017 11:26:15 -0800 Subject: [PATCH] Introduce new `--strict-mode` option. This also adds a new `HAPROXY_{n}_ENABLED` option, which lets you enabled/disable individual backends for an app. This resolves issues #402 and #403. --- Longhelp.md | 22 ++- README.md | 4 +- config.py | 12 ++ marathon_lb.py | 25 ++- tests/test_marathon_lb.py | 325 ++++++++++++++++++++++++++++++++------ 5 files changed, 331 insertions(+), 57 deletions(-) diff --git a/Longhelp.md b/Longhelp.md index e07a3568..bf8a7b57 100644 --- a/Longhelp.md +++ b/Longhelp.md @@ -18,7 +18,8 @@ Marathon-lb just needs to know where to find Marathon. ``` usage: marathon_lb.py [-h] [--longhelp] [--marathon MARATHON [MARATHON ...]] [--haproxy-config HAPROXY_CONFIG] [--group GROUP] - [--command COMMAND] [--sse] [--health-check] + [--command COMMAND] [--strict-mode] [--sse] + [--health-check] [--lru-cache-capacity LRU_CACHE_CAPACITY] [--haproxy-map] [--dont-bind-http-https] [--ssl-certs SSL_CERTS] [--skip-validation] [--dry] @@ -44,11 +45,15 @@ optional arguments: Location of haproxy configuration (default: /etc/haproxy/haproxy.cfg) --group GROUP [required] Only generate config for apps which list - the specified names. Use '*' to match all groups - (default: []) + the specified names. Use '*' to match all groups, + including those without a group specified. (default: + []) --command COMMAND, -c COMMAND If set, run this command to reload haproxy. (default: None) + --strict-mode If set, backends are only advertised if + HAPROXY_{n}_ENABLED=true. Strict mode will be enabled + by default in a future release. (default: False) --sse, -s Use Server Sent Events (default: False) --health-check, -H If set, respect Marathon's health check statuses before adding the app instance into the backend pool. @@ -1052,6 +1057,17 @@ generally do not need to modify this unless you implement your own deployment orchestrator. +## `HAPROXY_{n}_ENABLED` + *per service port* + +Specified as `HAPROXY_{n}_ENABLED`. + +Enable this backend. By default, all backends are enabled. To disable +backends by default, specify the `--strict-mode` flag. + +Ex: `HAPROXY_0_ENABLED = true` + + ## `HAPROXY_{n}_GROUP` *per service port* diff --git a/README.md b/README.md index 75a759e8..a4e23451 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ You can also run the update script directly. To generate an HAProxy configuration from Marathon running at `localhost:8080` with the `marathon_lb.py` script, run: ```console -$ ./marathon_lb.py --marathon http://localhost:8080 --group external +$ ./marathon_lb.py --marathon http://localhost:8080 --group external --strict-mode --health-check ``` It is possible to pass `--auth-credentials=` option if your Marathon requires authentication: @@ -123,7 +123,7 @@ $ ./marathon_lb.py --help You can provide your SSL certificate paths to be placed in frontend marathon_https_in section with `--ssl-certs`. ``` console -$ ./marathon_lb.py --marathon http://localhost:8080 --group external --ssl-certs /etc/ssl/site1.co,/etc/ssl/site2.co +$ ./marathon_lb.py --marathon http://localhost:8080 --group external --ssl-certs /etc/ssl/site1.co,/etc/ssl/site2.co --health-check --strict-mode ``` If you are using the script directly, you have two options: diff --git a/config.py b/config.py index a3e669d8..1b912648 100644 --- a/config.py +++ b/config.py @@ -1116,6 +1116,10 @@ def set_sticky(x, k, v): x.sticky = string_to_bool(v) +def set_enabled(x, k, v): + x.enabled = string_to_bool(v) + + def set_redirect_http_to_https(x, k, v): x.redirectHttpToHttps = string_to_bool(v) @@ -1309,6 +1313,14 @@ def __init__(self, name, func, description, perServicePort=True): Ex: `HAPROXY_0_STICKY = true` ''')) +labels.append(Label(name='ENABLED', + func=set_enabled, + description='''\ +Enable this backend. By default, all backends are enabled. To disable +backends by default, specify the `--strict-mode` flag. + +Ex: `HAPROXY_0_ENABLED = true` + ''')) labels.append(Label(name='REDIRECT_TO_HTTPS', func=set_redirect_http_to_https, description='''\ diff --git a/marathon_lb.py b/marathon_lb.py index fa24eb05..2de7bf3a 100755 --- a/marathon_lb.py +++ b/marathon_lb.py @@ -91,7 +91,7 @@ def __repr__(self): class MarathonService(object): - def __init__(self, appId, servicePort, healthCheck): + def __init__(self, appId, servicePort, healthCheck, strictMode): self.appId = appId self.servicePort = servicePort self.backends = set() @@ -105,6 +105,7 @@ def __init__(self, appId, servicePort, healthCheck): self.authUser = None self.authPasswd = None self.sticky = False + self.enabled = not strictMode self.redirectHttpToHttps = False self.useHsts = False self.sslCert = None @@ -153,10 +154,11 @@ def __eq__(self, other): class Marathon(object): - def __init__(self, hosts, health_check, auth, ca_cert=None): + def __init__(self, hosts, health_check, strict_mode, auth, ca_cert=None): # TODO(cmaloney): Support getting master list from zookeeper self.__hosts = hosts self.__health_check = health_check + self.__strict_mode = strict_mode self.__auth = auth self.__cycle_hosts = cycle(self.__hosts) self.__verify = False @@ -215,6 +217,9 @@ def list(self): def health_check(self): return self.__health_check + def strict_mode(self): + return self.__strict_mode + def tasks(self): logger.info('fetching tasks') return self.api_req('GET', ['tasks'])["tasks"] @@ -360,6 +365,9 @@ def config(apps, groups, bind_http_https, ssl_certs, templater, else: if not has_group(groups, app.groups): continue + # Skip if it's not actually enabled + if not app.enabled: + continue logger.debug("configuring app %s", app.appId) if len(app.backends) < 1: @@ -1333,8 +1341,9 @@ def get_apps(marathon): logger.warning("Skipping undefined service port") continue - service = MarathonService( - appId, servicePort, get_health_check(app, i)) + service = MarathonService(appId, servicePort, + get_health_check(app, i), + marathon.strict_mode()) for key_unformatted in label_keys: key = key_unformatted.format(i) @@ -1580,12 +1589,17 @@ def get_arg_parser(): parser.add_argument("--group", help="[required] Only generate config for apps which" " list the specified names. Use '*' to match all" - " groups", + " groups, including those without a group specified.", action="append", default=list()) parser.add_argument("--command", "-c", help="If set, run this command to reload haproxy.", default=None) + parser.add_argument("--strict-mode", + help="If set, backends are only advertised if" + " HAPROXY_{n}_ENABLED=true. Strict mode will be" + " enabled by default in a future release.", + action="store_true") parser.add_argument("--sse", "-s", help="Use Server Sent Events", action="store_true") @@ -1710,6 +1724,7 @@ def process_sse_events(marathon, processor): # Marathon API connector marathon = Marathon(args.marathon, args.health_check, + args.strict_mode, get_marathon_auth_params(args), args.marathon_ca_cert) diff --git a/tests/test_marathon_lb.py b/tests/test_marathon_lb.py index e79fb832..e0562c43 100644 --- a/tests/test_marathon_lb.py +++ b/tests/test_marathon_lb.py @@ -161,6 +161,7 @@ def test_config_simple_app(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -171,7 +172,8 @@ def test_config_simple_app(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) apps = [app] @@ -216,6 +218,7 @@ def test_config_healthcheck_command(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -229,7 +232,8 @@ def test_config_healthcheck_command(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) apps = [app] @@ -267,6 +271,7 @@ def test_config_simple_app_vhost(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -277,7 +282,8 @@ def test_config_simple_app_vhost(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com" app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) @@ -326,6 +332,7 @@ def test_config_simple_app_multiple_vhost(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -336,7 +343,8 @@ def test_config_simple_app_multiple_vhost(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.groups = ['external'] app.add_backend("agent1", "192.0.2.1", 1234, False) @@ -387,6 +395,7 @@ def test_config_simple_app_vhost_and_redirect(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -397,7 +406,8 @@ def test_config_simple_app_vhost_and_redirect(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com" app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) @@ -447,6 +457,7 @@ def test_config_simple_app_multiple_vhost_and_redirect(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -457,7 +468,8 @@ def test_config_simple_app_multiple_vhost_and_redirect(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.groups = ['external'] app.redirectHttpToHttps = True @@ -509,6 +521,7 @@ def test_config_simple_app_vhost_with_auth(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -519,7 +532,8 @@ def test_config_simple_app_vhost_with_auth(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com" app.groups = ['external'] app.authRealm = "realm" @@ -580,6 +594,7 @@ def test_config_simple_app_multiple_vhost_and_auth(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -590,7 +605,8 @@ def test_config_simple_app_multiple_vhost_and_auth(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.authRealm = "realm" app.authUser = "testuser" @@ -656,6 +672,7 @@ def test_config_simple_app_vhost_with_path_and_auth(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -666,7 +683,8 @@ def test_config_simple_app_vhost_with_path_and_auth(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com" app.path = '/some/path' app.groups = ['external'] @@ -730,6 +748,7 @@ def test_config_simple_app_multiple_vhost_with_path_and_auth(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -740,7 +759,8 @@ def test_config_simple_app_multiple_vhost_with_path_and_auth(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.path = '/some/path' app.groups = ['external'] @@ -810,6 +830,7 @@ def test_config_simple_app_vhost_with_path(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -820,7 +841,8 @@ def test_config_simple_app_vhost_with_path(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com" app.path = '/some/path' app.groups = ['external'] @@ -872,6 +894,7 @@ def test_config_simple_app_multiple_vhost_with_path(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -882,7 +905,8 @@ def test_config_simple_app_multiple_vhost_with_path(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.path = '/some/path' app.groups = ['external'] @@ -937,6 +961,7 @@ def test_config_simple_app_vhost_with_path_and_redirect(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -947,7 +972,8 @@ def test_config_simple_app_vhost_with_path_and_redirect(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com" app.path = '/some/path' app.groups = ['external'] @@ -1001,6 +1027,7 @@ def test_config_simple_app_multiple_vhost_with_path_and_redirect(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1011,7 +1038,8 @@ def test_config_simple_app_multiple_vhost_with_path_and_redirect(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.path = '/some/path' app.groups = ['external'] @@ -1068,6 +1096,7 @@ def test_config_simple_app_multiple_vhost_path_redirect_hsts(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1078,7 +1107,8 @@ def test_config_simple_app_multiple_vhost_path_redirect_hsts(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.path = '/some/path' app.groups = ['external'] @@ -1137,6 +1167,7 @@ def test_config_simple_app_balance(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1147,7 +1178,8 @@ def test_config_simple_app_balance(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.balance = "leastconn" app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) @@ -1201,6 +1233,9 @@ def list(self): def health_check(self): return True + def strict_mode(self): + return False + groups = ['external'] bind_http_https = True ssl_certs = "" @@ -1249,6 +1284,7 @@ def test_config_simple_app_healthcheck_port(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1259,7 +1295,8 @@ def test_config_simple_app_healthcheck_port(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) apps = [app] @@ -1304,6 +1341,7 @@ def test_config_simple_app_healthcheck_port_using_another_portindex(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1314,11 +1352,13 @@ def test_config_simple_app_healthcheck_port_using_another_portindex(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "192.0.2.1", 1024, False) app.healthcheck_port_index = 1 - admin_app = marathon_lb.MarathonService('/nginx', 10001, healthCheck) + admin_app = marathon_lb.MarathonService('/nginx', 10001, healthCheck, + strictMode) admin_app.groups = ['external'] admin_app.add_backend("agent1", "192.0.2.1", 1025, False) apps = [app, admin_app] @@ -1378,6 +1418,7 @@ def test_config_simple_app_healthcheck_port_diff_portindex_and_group(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1388,11 +1429,13 @@ def test_config_simple_app_healthcheck_port_diff_portindex_and_group(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "192.0.2.1", 1024, False) app.healthcheck_port_index = 1 - admin_app = marathon_lb.MarathonService('/nginx', 10001, healthCheck) + admin_app = marathon_lb.MarathonService('/nginx', 10001, healthCheck, + strictMode) admin_app.groups = ['internal'] admin_app.add_backend("agent1", "192.0.2.1", 1025, False) apps = [app, admin_app] @@ -1459,6 +1502,7 @@ def test_config_simple_app_healthcheck_port_portindex_out_of_range(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1469,11 +1513,13 @@ def test_config_simple_app_healthcheck_port_portindex_out_of_range(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "192.0.2.1", 1024, False) app.healthcheck_port_index = 3 - admin_app = marathon_lb.MarathonService('/nginx', 10001, healthCheck) + admin_app = marathon_lb.MarathonService('/nginx', 10001, healthCheck, + strictMode) admin_app.groups = ['external'] admin_app.add_backend("agent1", "192.0.2.1", 1025, False) apps = [app, admin_app] @@ -1533,6 +1579,7 @@ def test_config_simple_app_tcp_healthcheck(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "protocol": "TCP", @@ -1542,7 +1589,8 @@ def test_config_simple_app_tcp_healthcheck(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) app.hostname = "test.example.com" @@ -1589,12 +1637,15 @@ def test_config_haproxy_group_fallback(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} - app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app1.groups = ['external', 'internal'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/nginx', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/nginx', 10001, healthCheck, + strictMode) app2.groups = ['external', 'internal'] app2.add_backend("agent1", "1.1.1.1", 1025, False) apps = [app1, app2] @@ -1641,12 +1692,15 @@ def test_config_haproxy_group_per_service(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} - app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app1.haproxy_groups = ['external'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/nginx', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/nginx', 10001, healthCheck, + strictMode) app2.haproxy_groups = ['internal'] app2.add_backend("agent1", "1.1.1.1", 1025, False) apps = [app1, app2] @@ -1683,12 +1737,15 @@ def test_config_haproxy_group_hybrid(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} - app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app1.haproxy_groups = ['internal'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/nginx', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/nginx', 10001, healthCheck, + strictMode) app2.groups = ['external'] app2.add_backend("agent1", "1.1.1.1", 1025, False) apps = [app1, app2] @@ -1727,9 +1784,11 @@ def test_config_simple_app_proxypass(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.hostname = 'test.example.com' app.proxypath = '/test/' @@ -1779,9 +1838,11 @@ def test_config_simple_app_revproxy(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.hostname = 'test.example.com' app.revproxypath = '/test' @@ -1832,9 +1893,11 @@ def test_config_simple_app_redirect(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.hostname = 'test.example.com' app.redirpath = '/test' @@ -1885,9 +1948,11 @@ def test_config_simple_app_sticky(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) app.sticky = True @@ -1927,6 +1992,7 @@ def test_config_multi_app_multiple_vhost_with_path(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -1937,7 +2003,8 @@ def test_config_multi_app_multiple_vhost_with_path(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.hostname = "test.example.com,test" app.path = '/some/path' app.groups = ['external'] @@ -2075,6 +2142,7 @@ def test_config_haproxy_map(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} healthCheck = { @@ -2087,11 +2155,13 @@ def test_config_haproxy_map(self): "maxConsecutiveFailures": 10 } - app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app1.hostname = "server.nginx.net,server.nginx1.net" app1.haproxy_groups = ['external'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck, + strictMode) app2.hostname = "server.apache.net" app2.haproxy_groups = ['external'] app2.add_backend("agent2", "2.2.2.2", 1025, False) @@ -2180,6 +2250,7 @@ def test_config_haproxy_map_hybrid(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} healthCheck = { @@ -2192,11 +2263,13 @@ def test_config_haproxy_map_hybrid(self): "maxConsecutiveFailures": 10 } - app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app1.hostname = "server.nginx.net,server.nginx1.net" app1.haproxy_groups = ['external'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck, + strictMode) app2.hostname = "server.apache.net" app2.path = "/apache" app2.haproxy_groups = ['external'] @@ -2296,6 +2369,7 @@ def test_config_haproxy_map_auth_noauth(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} healthCheck = { @@ -2308,11 +2382,13 @@ def test_config_haproxy_map_auth_noauth(self): "maxConsecutiveFailures": 10 } - app1 = marathon_lb.MarathonService('/nginx1', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx1', 10000, healthCheck, + strictMode) app1.hostname = "server.nginx.net" app1.haproxy_groups = ['external'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/nginx2', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/nginx2', 10001, healthCheck, + strictMode) app2.hostname = "server.nginx.net" app2.authRealm = "realm" app2.authUser = "testuser" @@ -2414,6 +2490,7 @@ def test_config_haproxy_map_hybrid_with_vhost_path(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} healthCheck = { @@ -2426,11 +2503,13 @@ def test_config_haproxy_map_hybrid_with_vhost_path(self): "maxConsecutiveFailures": 10 } - app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app1.hostname = "server.nginx.net,server.nginx1.net" app1.haproxy_groups = ['external'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck, + strictMode) app2.hostname = "server.apache.net,server.apache1.net" app2.path = "/apache" app2.haproxy_groups = ['external'] @@ -2528,6 +2607,7 @@ def test_config_haproxy_map_hybrid_httptohttps_redirect(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = {} healthCheck = { @@ -2540,11 +2620,13 @@ def test_config_haproxy_map_hybrid_httptohttps_redirect(self): "maxConsecutiveFailures": 10 } - app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app1 = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app1.hostname = "server.nginx.net,server.nginx1.net" app1.haproxy_groups = ['external'] app1.add_backend("agent1", "1.1.1.1", 1024, False) - app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck) + app2 = marathon_lb.MarathonService('/apache', 10001, healthCheck, + strictMode) app2.hostname = "server.apache.net,server.apache1.net" app2.haproxy_groups = ['external'] app2.add_backend("agent2", "2.2.2.2", 1025, False) @@ -2638,6 +2720,7 @@ def test_config_simple_app_proxypass_health_check(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -2648,7 +2731,8 @@ def test_config_simple_app_proxypass_health_check(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.proxypath = "/proxy/path" app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) @@ -2696,6 +2780,7 @@ def test_config_simple_app_revproxy_health_check(self): bind_http_https = True ssl_certs = "" templater = marathon_lb.ConfigTemplater() + strictMode = False healthCheck = { "path": "/", @@ -2706,7 +2791,8 @@ def test_config_simple_app_revproxy_health_check(self): "timeoutSeconds": 10, "maxConsecutiveFailures": 10 } - app = marathon_lb.MarathonService('/nginx', 10000, healthCheck) + app = marathon_lb.MarathonService('/nginx', 10000, healthCheck, + strictMode) app.revproxypath = "/proxy/path" app.groups = ['external'] app.add_backend("agent1", "1.1.1.1", 1024, False) @@ -2746,5 +2832,150 @@ def test_config_simple_app_revproxy_health_check(self): option httpchk GET / timeout check 10s server agent1_1_1_1_1_1024 1.1.1.1:1024 check inter 2s fall 11 +''' + self.assertMultiLineEqual(config, expected) + + def test_strict_mode_on_and_off(self): + apps = dict() + groups = ['external'] + bind_http_https = True + ssl_certs = "" + templater = marathon_lb.ConfigTemplater() + + healthCheck = {} + healthCheck = { + "path": "/", + "protocol": "HTTP", + "portIndex": 0, + "gracePeriodSeconds": 15, + "intervalSeconds": 3, + "timeoutSeconds": 15, + "maxConsecutiveFailures": 10 + } + + app1 = marathon_lb.MarathonService('/nginx1', 10000, healthCheck, + True) + app1.hostname = "server.nginx.net" + app1.haproxy_groups = ['external'] + app1.add_backend("agent1", "1.1.1.1", 1024, False) + app2 = marathon_lb.MarathonService('/nginx2', 10001, healthCheck, + False) + app2.hostname = "server.nginx.net" + app2.haproxy_groups = ['external'] + app2.add_backend("agent2", "2.2.2.2", 1025, False) + apps = [app1, app2] + haproxy_map = True + domain_map_array = [] + app_map_array = [] + config_file = "/etc/haproxy/haproxy.cfg" + config = marathon_lb.config(apps, groups, bind_http_https, ssl_certs, + templater, haproxy_map, domain_map_array, + app_map_array, config_file) + expected = self.base_config + ''' +frontend marathon_http_in + bind *:80 + mode http + use_backend %[req.hdr(host),lower,regsub(:.*$,,),\ +map(/etc/haproxy/domain2backend.map)] + +frontend marathon_http_appid_in + bind *:9091 + mode http + use_backend %[req.hdr(x-marathon-app-id),lower,\ +map(/etc/haproxy/app2backend.map)] + +frontend marathon_https_in + bind *:443 ssl crt /etc/ssl/cert.pem + mode http + use_backend %[ssl_fc_sni,lower,map(/etc/haproxy/domain2backend.map)] + +frontend nginx2_10001 + bind *:10001 + mode http + use_backend nginx2_10001 + +backend nginx2_10001 + balance roundrobin + mode http + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + option httpchk GET / + timeout check 15s + server agent2_2_2_2_2_1025 2.2.2.2:1025 check inter 3s fall 11 +''' + self.assertMultiLineEqual(config, expected) + + def test_backend_disabled_and_enablede(self): + apps = dict() + groups = ['external'] + bind_http_https = True + ssl_certs = "" + templater = marathon_lb.ConfigTemplater() + strictMode = False + + healthCheck = {} + healthCheck = { + "path": "/", + "protocol": "HTTP", + "portIndex": 0, + "gracePeriodSeconds": 15, + "intervalSeconds": 3, + "timeoutSeconds": 15, + "maxConsecutiveFailures": 10 + } + + app1 = marathon_lb.MarathonService('/nginx1', 10000, healthCheck, + strictMode) + app1.hostname = "server.nginx.net" + app1.haproxy_groups = ['external'] + app1.enabled = False + app1.add_backend("agent1", "1.1.1.1", 1024, False) + app2 = marathon_lb.MarathonService('/nginx2', 10001, healthCheck, + strictMode) + app2.hostname = "server.nginx.net" + app2.haproxy_groups = ['external'] + app2.enabled = True + app2.add_backend("agent2", "2.2.2.2", 1025, False) + apps = [app1, app2] + haproxy_map = True + domain_map_array = [] + app_map_array = [] + config_file = "/etc/haproxy/haproxy.cfg" + config = marathon_lb.config(apps, groups, bind_http_https, ssl_certs, + templater, haproxy_map, domain_map_array, + app_map_array, config_file) + expected = self.base_config + ''' +frontend marathon_http_in + bind *:80 + mode http + use_backend %[req.hdr(host),lower,regsub(:.*$,,),\ +map(/etc/haproxy/domain2backend.map)] + +frontend marathon_http_appid_in + bind *:9091 + mode http + use_backend %[req.hdr(x-marathon-app-id),lower,\ +map(/etc/haproxy/app2backend.map)] + +frontend marathon_https_in + bind *:443 ssl crt /etc/ssl/cert.pem + mode http + use_backend %[ssl_fc_sni,lower,map(/etc/haproxy/domain2backend.map)] + +frontend nginx2_10001 + bind *:10001 + mode http + use_backend nginx2_10001 + +backend nginx2_10001 + balance roundrobin + mode http + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + option httpchk GET / + timeout check 15s + server agent2_2_2_2_2_1025 2.2.2.2:1025 check inter 3s fall 11 ''' self.assertMultiLineEqual(config, expected)