diff --git a/Longhelp.md b/Longhelp.md new file mode 100644 index 00000000..4df6aab5 --- /dev/null +++ b/Longhelp.md @@ -0,0 +1,800 @@ +# marathon-lb +### Overview +The marathon-lb is a service discovery and load balancing tool +for Marathon based on HAProxy. It reads the Marathon task information +and dynamically generates HAProxy configuration details. + +To gather the task information, marathon-lb needs to know where +to find Marathon. The service configuration details are stored in labels. + +Every service port in Marathon can be configured independently. + +### Configuration +Service configuration lives in Marathon via labels. +Marathon-lb just needs to know where to find Marathon. +To run in listening mode you must also specify the address + port at +which marathon-lb can be reached by Marathon. + +### Command Line Usage + +``` +usage: marathon_lb.py [-h] [--longhelp] [--marathon MARATHON [MARATHON ...]] + [--listening LISTENING] [--callback-url CALLBACK_URL] + [--haproxy-config HAPROXY_CONFIG] [--group GROUP] + [--command COMMAND] [--sse] [--health-check] + [--dont-bind-http-https] [--ssl-certs SSL_CERTS] + [--skip-validation] [--dry] + [--syslog-socket SYSLOG_SOCKET] + [--log-format LOG_FORMAT] + [--marathon-auth-credential-file MARATHON_AUTH_CREDENTIAL_FILE] + [--auth-credentials AUTH_CREDENTIALS] + +Marathon HAProxy Load Balancer + +optional arguments: + -h, --help show this help message and exit + --longhelp Print out configuration details (default: False) + --marathon MARATHON [MARATHON ...], -m MARATHON [MARATHON ...] + [required] Marathon endpoint, eg. -m + http://marathon1:8080 -m http://marathon2:8080 + (default: None) + --listening LISTENING, -l LISTENING + The address this script listens on for marathon events + (e.g., http://0.0.0.0:8080) (default: None) + --callback-url CALLBACK_URL, -u CALLBACK_URL + The HTTP address that Marathon can call this script + back at (http://lb1:8080) (default: None) + --haproxy-config HAPROXY_CONFIG + 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: []) + --command COMMAND, -c COMMAND + If set, run this command to reload haproxy. (default: + None) + --sse, -s Use Server Sent Events instead of HTTP Callbacks + (default: False) + --health-check, -H If set, respect Marathon's health check statuses + before adding the app instance into the backend pool. + (default: False) + --dont-bind-http-https + Don't bind to HTTP and HTTPS frontends. (default: + False) + --ssl-certs SSL_CERTS + List of SSL certificates separated by commafor + frontend marathon_https_inEx: + /etc/ssl/site1.co.pem,/etc/ssl/site2.co.pem (default: + /etc/ssl/mesosphere.com.pem) + --skip-validation Skip haproxy config file validation (default: False) + --dry, -d Only print configuration to console (default: False) + --syslog-socket SYSLOG_SOCKET + Socket to write syslog messages to. Use '/dev/null' to + disable logging to syslog (default: /var/run/syslog) + --log-format LOG_FORMAT + Set log message format (default: %(name)s: + %(message)s) + --marathon-auth-credential-file MARATHON_AUTH_CREDENTIAL_FILE + Path to file containing a user/pass for the Marathon + HTTP API in the format of 'user:pass'. (default: None) + --auth-credentials AUTH_CREDENTIALS + user/pass for the Marathon HTTP API in the format of + 'user:pass'. (default: None) +``` +## Templates + +The following is a list of the available HAProxy templates. +Some templates are global-only (such as `HAPROXY_HEAD`), but most may +be overridden on a per service port basis using the +`HAPROXY_{n}_...` syntax. + +## `BACKEND_HEAD` + *Overridable* + +May be specified as `HAPROXY_BACKEND_HEAD` template or with label `HAPROXY_{n}_BACKEND_HEAD`. + +Defines the type of load balancing, roundrobin by default, +and connection mode, TCP or HTTP. + + +**Default template for `BACKEND_HEAD`:** +``` + +backend {backend} + balance {balance} + mode {mode} + +``` +## `BACKEND_HSTS_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_HSTS_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_HSTS_OPTIONS`. + +This template is used for the backend where the +`HAPROXY_{n}_USE_HSTS` label is set to true. + + +**Default template for `BACKEND_HSTS_OPTIONS`:** +``` + rspadd Strict-Transport-Security:\ max-age=15768000 + +``` +## `BACKEND_HTTP_HEALTHCHECK_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_HTTP_HEALTHCHECK_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_HTTP_HEALTHCHECK_OPTIONS`. + +Sets HTTP health check options, for example timeout check and httpchk GET. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckPath + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckIgnoreHttp1xx + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to " port {healthCheckPort}" + +Defaults to empty string. + +Example: +``` + option httpchk GET {healthCheckPath} + timeout check {healthCheckTimeoutSeconds}s +``` + + +**Default template for `BACKEND_HTTP_HEALTHCHECK_OPTIONS`:** +``` + option httpchk GET {healthCheckPath} + timeout check {healthCheckTimeoutSeconds}s + +``` +## `BACKEND_HTTP_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_HTTP_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_HTTP_OPTIONS`. + +Sets HTTP headers, for example X-Forwarded-For and X-Forwarded-Proto. + + +**Default template for `BACKEND_HTTP_OPTIONS`:** +``` + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + +``` +## `BACKEND_REDIRECT_HTTP_TO_HTTPS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS` template or with label `HAPROXY_{n}_BACKEND_REDIRECT_HTTP_TO_HTTPS`. + +This template is used with backends where the +`HAPROXY_{n}_REDIRECT_TO_HTTPS` label is set to true + + +**Default template for `BACKEND_REDIRECT_HTTP_TO_HTTPS`:** +``` + redirect scheme https code 301 if !{{ ssl_fc }} host_{cleanedUpHostname} + +``` +## `BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH` + *Overridable* + +May be specified as `HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH` template or with label `HAPROXY_{n}_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH`. + +Same as `HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS`, +but includes a path. + + +**Default template for `BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH`:** +``` + redirect scheme https code 301 if !{{ ssl_fc }} host_{cleanedUpHostname} path_{backend} + +``` +## `BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS`. + +Sets HTTP health check options for a single server, e.g. check inter. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckPath + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckIgnoreHttp1xx + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to " port {healthCheckPort}" + +Defaults to empty string. + +Example: +``` + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls} +``` + + +**Default template for `BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS`:** +``` + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls}{healthCheckPortOptions} + +``` +## `BACKEND_SERVER_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_SERVER_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_SERVER_OPTIONS`. + +The options for each server added to a backend. + + +**Default template for `BACKEND_SERVER_OPTIONS`:** +``` + server {serverName} {host_ipv4}:{port}{cookieOptions}{healthCheckOptions}{otherOptions} + +``` +## `BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS`. + +Sets TCP health check options for a single server, e.g. check inter. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to " port {healthCheckPort}" + +Defaults to empty string. + +Example: +``` + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls} +``` + + +**Default template for `BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS`:** +``` + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls}{healthCheckPortOptions} + +``` +## `BACKEND_STICKY_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_STICKY_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_STICKY_OPTIONS`. + +Sets a cookie for services where `HAPROXY_{n}_STICKY` is true. + + +**Default template for `BACKEND_STICKY_OPTIONS`:** +``` + cookie mesosphere_server_id insert indirect nocache + +``` +## `BACKEND_TCP_HEALTHCHECK_OPTIONS` + *Overridable* + +May be specified as `HAPROXY_BACKEND_TCP_HEALTHCHECK_OPTIONS` template or with label `HAPROXY_{n}_BACKEND_TCP_HEALTHCHECK_OPTIONS`. + +Sets TCP health check options, for example timeout check. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to " port {healthCheckPort}" + +Defaults to empty string. + +Example: +``` + timeout check {healthCheckTimeoutSeconds}s +``` + + +**Default template for `BACKEND_TCP_HEALTHCHECK_OPTIONS`:** +``` + +``` +## `FRONTEND_BACKEND_GLUE` + *Overridable* + +May be specified as `HAPROXY_FRONTEND_BACKEND_GLUE` template or with label `HAPROXY_{n}_FRONTEND_BACKEND_GLUE`. + +This option glues the backend to the frontend. + + +**Default template for `FRONTEND_BACKEND_GLUE`:** +``` + use_backend {backend} + +``` +## `FRONTEND_HEAD` + *Overridable* + +May be specified as `HAPROXY_FRONTEND_HEAD` template or with label `HAPROXY_{n}_FRONTEND_HEAD`. + +Defines the address and port to bind to for this frontend. + + +**Default template for `FRONTEND_HEAD`:** +``` + +frontend {backend} + bind {bindAddr}:{servicePort}{sslCert}{bindOptions} + mode {mode} + +``` +## `HEAD` + *Global* + +May be specified as `HAPROXY_HEAD` template. + +The head of the HAProxy config. This contains global settings +and defaults. + + +**Default template for `HEAD`:** +``` +global + daemon + log /dev/log local0 + log /dev/log local1 notice + 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-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-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 +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 + option redispatch + option http-server-close + option dontlognull +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 getconfig path /_haproxy_getconfig + http-request use-service lua.getconfig if getconfig + +``` +## `HTTPS_FRONTEND_ACL` + *Overridable* + +May be specified as `HAPROXY_HTTPS_FRONTEND_ACL` template or with label `HAPROXY_{n}_HTTPS_FRONTEND_ACL`. + +The ACL that performs the SNI based hostname matching +for the `HAPROXY_HTTPS_FRONTEND_HEAD` template. + + +**Default template for `HTTPS_FRONTEND_ACL`:** +``` + use_backend {backend} if {{ ssl_fc_sni {hostname} }} + +``` +## `HTTPS_FRONTEND_ACL_ONLY_WITH_PATH` + *Overridable* + +May be specified as `HAPROXY_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH` template or with label `HAPROXY_{n}_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH`. + +Same as HTTP_FRONTEND_ACL_ONLY_WITH_PATH, but for HTTPS. + + +**Default template for `HTTPS_FRONTEND_ACL_ONLY_WITH_PATH`:** +``` + acl path_{backend} path_beg {path} + +``` +## `HTTPS_FRONTEND_ACL_WITH_PATH` + *Overridable* + +May be specified as `HAPROXY_HTTPS_FRONTEND_ACL_WITH_PATH` template or with label `HAPROXY_{n}_HTTPS_FRONTEND_ACL_WITH_PATH`. + +The ACL that performs the SNI based hostname matching with path +for the `HAPROXY_HTTPS_FRONTEND_HEAD` template. + + +**Default template for `HTTPS_FRONTEND_ACL_WITH_PATH`:** +``` + use_backend {backend} if {{ ssl_fc_sni {hostname} }} path_{backend} + +``` +## `HTTPS_FRONTEND_HEAD` + *Global* + +May be specified as `HAPROXY_HTTPS_FRONTEND_HEAD` template. + +An HTTPS frontend for encrypted connections that binds to port *:443 by +default and gathers all virtual hosts as defined by the +`HAPROXY_{n}_VHOST` label. You must modify this file to +include your certificate. + + +**Default template for `HTTPS_FRONTEND_HEAD`:** +``` + +frontend marathon_https_in + bind *:443 ssl {sslCerts} + mode http + +``` +## `HTTP_FRONTEND_ACL` + *Overridable* + +May be specified as `HAPROXY_HTTP_FRONTEND_ACL` template or with label `HAPROXY_{n}_HTTP_FRONTEND_ACL`. + +The ACL that glues a backend to the corresponding virtual host +of the `HAPROXY_HTTP_FRONTEND_HEAD` + + +**Default template for `HTTP_FRONTEND_ACL`:** +``` + acl host_{cleanedUpHostname} hdr(host) -i {hostname} + use_backend {backend} if host_{cleanedUpHostname} + +``` +## `HTTP_FRONTEND_ACL_ONLY` + *Overridable* + +May be specified as `HAPROXY_HTTP_FRONTEND_ACL_ONLY` template or with label `HAPROXY_{n}_HTTP_FRONTEND_ACL_ONLY`. + +Define the ACL matching a particular hostname, but unlike +`HAPROXY_HTTP_FRONTEND_ACL`, only do the ACL portion. Does not glue +the ACL to the backend. This is useful only in the case of multiple +vhosts routing to the same backend. + + +**Default template for `HTTP_FRONTEND_ACL_ONLY`:** +``` + acl host_{cleanedUpHostname} hdr(host) -i {hostname} + +``` +## `HTTP_FRONTEND_ACL_ONLY_WITH_PATH` + *Overridable* + +May be specified as `HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH` template or with label `HAPROXY_{n}_HTTP_FRONTEND_ACL_ONLY_WITH_PATH`. + +Define the ACL matching a particular hostname with path, but unlike +`HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH`, only do the ACL portion. Does not glue +the ACL to the backend. This is useful only in the case of multiple +vhosts routing to the same backend + + +**Default template for `HTTP_FRONTEND_ACL_ONLY_WITH_PATH`:** +``` + acl path_{backend} path_beg {path} + +``` +## `HTTP_FRONTEND_ACL_WITH_PATH` + *Overridable* + +May be specified as `HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH` template or with label `HAPROXY_{n}_HTTP_FRONTEND_ACL_WITH_PATH`. + +The ACL that glues a backend to the corresponding virtual host with path +of the `HAPROXY_HTTP_FRONTEND_HEAD`. + + +**Default template for `HTTP_FRONTEND_ACL_WITH_PATH`:** +``` + acl host_{cleanedUpHostname} hdr(host) -i {hostname} + acl path_{backend} path_beg {path} + use_backend {backend} if host_{cleanedUpHostname} path_{backend} + +``` +## `HTTP_FRONTEND_APPID_ACL` + *Overridable* + +May be specified as `HAPROXY_HTTP_FRONTEND_APPID_ACL` template or with label `HAPROXY_{n}_HTTP_FRONTEND_APPID_ACL`. + +The ACL that glues a backend to the corresponding app +of the `HAPROXY_HTTP_FRONTEND_APPID_HEAD`. + + +**Default template for `HTTP_FRONTEND_APPID_ACL`:** +``` + acl app_{cleanedUpAppId} hdr(x-marathon-app-id) -i {appId} + use_backend {backend} if app_{cleanedUpAppId} + +``` +## `HTTP_FRONTEND_APPID_HEAD` + *Global* + +May be specified as `HAPROXY_HTTP_FRONTEND_APPID_HEAD` template. + +An HTTP frontend that binds to port *:9091 by default and gathers +all apps in HTTP mode. +To use this frontend to forward to your app, configure the app with +`HAPROXY_0_MODE=http` then you can access it via a call to the :9091 +with the header "X-Marathon-App-Id" set to the Marathon AppId. +Note multiple HTTP ports being exposed by the same marathon app are not +supported. Only the first HTTP port is available via this frontend. + + +**Default template for `HTTP_FRONTEND_APPID_HEAD`:** +``` + +frontend marathon_http_appid_in + bind *:9091 + mode http + +``` +## `HTTP_FRONTEND_HEAD` + *Global* + +May be specified as `HAPROXY_HTTP_FRONTEND_HEAD` template. + +An HTTP frontend that binds to port *:80 by default and gathers +all virtual hosts as defined by the `HAPROXY_{n}_VHOST` label. + + +**Default template for `HTTP_FRONTEND_HEAD`:** +``` + +frontend marathon_http_in + bind *:80 + mode http + +``` +## `HTTP_FRONTEND_ROUTING_ONLY` + *Overridable* + +May be specified as `HAPROXY_HTTP_FRONTEND_ROUTING_ONLY` template or with label `HAPROXY_{n}_HTTP_FRONTEND_ROUTING_ONLY`. + +This is the counterpart to `HAPROXY_HTTP_FRONTEND_ACL_ONLY` which +glues the acl name to the appropriate backend. + + +**Default template for `HTTP_FRONTEND_ROUTING_ONLY`:** +``` + use_backend {backend} if host_{cleanedUpHostname} + +``` +## `HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH` + *Overridable* + +May be specified as `HAPROXY_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH` template or with label `HAPROXY_{n}_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH`. + +This is the counterpart to `HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH` which +glues the acl names to the appropriate backend + + +**Default template for `HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH`:** +``` + use_backend {backend} if host_{cleanedUpHostname} path_{backend} + +``` +## Other Labels +These labels may be used to configure other app settings. + +## `VHOST` + *per service port* + +May be specified as `HAPROXY_{n}_VHOST`. + +The Marathon HTTP Virtual Host proxy hostname(s) to gather. + +Ex: `HAPROXY_0_VHOST = 'marathon.mesosphere.com'` + +Ex: `HAPROXY_0_VHOST = 'marathon.mesosphere.com,marathon'` + + +## `GROUP` + *per service port* + +May be specified as `HAPROXY_{n}_GROUP` or `HAPROXY_GROUP`. + +HAProxy group per service. This helps us have different HAProxy groups +per service port. This overrides `HAPROXY_GROUP` for the particular service. +If you have both external and internal services running on same set of +instances on different ports, you can use this feature to add them to +different haproxy configs. + +Ex: `HAPROXY_0_GROUP = 'external'` + +Ex: `HAPROXY_1_GROUP = 'internal'` + +Now if you run marathon_lb with --group external, it just adds the +service on `HAPROXY_0_PORT` (or first service port incase `HAPROXY_0_HOST` +is not configured) to haproxy config and similarly if you run it with +--group internal, it adds service on `HAPROXY_1_PORT` to haproxy config. +If the configuration is a combination of `HAPROXY_GROUP` and +`HAPROXY_{n}_GROUP`, the more specific definition takes precedence. + +Ex: `HAPROXY_0_GROUP = 'external'` + +Ex: `HAPROXY_GROUP = 'internal'` + +Considering the above example where the configuration is hybrid, +a service running on `HAPROXY_0_PORT` is associated with just 'external' +HAProxy group and not 'internal' group. And since there is no HAProxy +group mentioned for second service (`HAPROXY_1_GROUP` not defined) +it falls back to default `HAPROXY_GROUP` and gets associated with +'internal' group. + +Load balancers with the group '*' will collect all groups. + + +## `DEPLOYMENT_GROUP` + *per app* + +May be specified as `HAPROXY_DEPLOYMENT_GROUP`. + +Deployment group to which this app belongs. + + +## `DEPLOYMENT_ALT_PORT` + *per app* + +May be specified as `HAPROXY_DEPLOYMENT_ALT_PORT`. + +Alternate service port to be used during a blue/green deployment. + + +## `DEPLOYMENT_COLOUR` + *per app* + +May be specified as `HAPROXY_DEPLOYMENT_COLOUR`. + +Blue/green deployment colour. Used by the bluegreen_deploy.py script +to determine the state of a deploy. You generally do not need to modify +this unless you implement your own deployment orchestrator. + + +## `DEPLOYMENT_STARTED_AT` + *per app* + +May be specified as `HAPROXY_DEPLOYMENT_STARTED_AT`. + +The time at which a deployment started. You generally do not need +to modify this unless you implement your own deployment orchestrator. + + +## `DEPLOYMENT_TARGET_INSTANCES` + *per app* + +May be specified as `HAPROXY_DEPLOYMENT_TARGET_INSTANCES`. + +The target number of app instances to seek during deployment. You +generally do not need to modify this unless you implement your +own deployment orchestrator. + + +## `PATH` + *per service port* + +May be specified as `HAPROXY_{n}_PATH`. + + + +## `STICKY` + *per service port* + +May be specified as `HAPROXY_{n}_STICKY`. + +Enable sticky request routing for the service. + +Ex: `HAPROXY_0_STICKY = true` + + +## `REDIRECT_TO_HTTPS` + *per service port* + +May be specified as `HAPROXY_{n}_REDIRECT_TO_HTTPS`. + +Redirect HTTP traffic to HTTPS. Requires at least a VHost be set. + +Ex: `HAPROXY_0_REDIRECT_TO_HTTPS = true` + + +## `USE_HSTS` + *per service port* + +May be specified as `HAPROXY_{n}_USE_HSTS`. + +Enable the HSTS response header for HTTP clients which support it. + +Ex: `HAPROXY_0_USE_HSTS = true` + + +## `SSL_CERT` + *per service port* + +May be specified as `HAPROXY_{n}_SSL_CERT`. + +Enable the given SSL certificate for TLS/SSL traffic. + +Ex: `HAPROXY_0_SSL_CERT = '/etc/ssl/certs/marathon.mesosphere.com'` + + +## `BIND_OPTIONS` + *per service port* + +May be specified as `HAPROXY_{n}_BIND_OPTIONS`. + +Set additional bind options + +Ex: `HAPROXY_0_BIND_OPTIONS = 'ciphers AES128+EECDH:AES128+EDH force-tlsv12 no-sslv3'` + + +## `BIND_ADDR` + *per service port* + +May be specified as `HAPROXY_{n}_BIND_ADDR`. + +Bind to the specific address for the service. + +Ex: `HAPROXY_0_BIND_ADDR = '10.0.0.42'` + + +## `PORT` + *per service port* + +May be specified as `HAPROXY_{n}_PORT`. + +Bind to the specific port for the service. +This overrides the servicePort which has to be unique. + +Ex: `HAPROXY_0_PORT = 80` + + +## `MODE` + *per service port* + +May be specified as `HAPROXY_{n}_MODE`. + +Set the connection mode to either TCP or HTTP. The default is TCP. + +Ex: `HAPROXY_0_MODE = 'http'` + + +## `BALANCE` + *per service port* + +May be specified as `HAPROXY_{n}_BALANCE`. + +Set the load balancing algorithm to be used in a backend. The default is +roundrobin. + +Ex: `HAPROXY_0_BALANCE = 'leastconn'` + + + diff --git a/README.md b/README.md index b121d5bf..8b84e542 100644 --- a/README.md +++ b/README.md @@ -152,100 +152,8 @@ App labels are specified in the Marathon app definition. These can be used to ov Some labels are specified _per service port_. These are denoted with the `{n}` parameter in the label key, where `{n}` corresponds to the service port index, beginning at `0`. -The full list of labels which can be specified are: -``` - HAPROXY_GROUP - The group of marathon-lb instances that point to the service. - This is a global group for all services running on different vhosts in a application - This can be overrided for each service port. If it is not overriden this will be default group. - Load balancers with the group '*' will collect all groups. - - HAPROXY_DEPLOYMENT_GROUP - Deployment group to which this app belongs. - - HAPROXY_DEPLOYMENT_ALT_PORT - Alternate service port to be used during a blue/green deployment. - - HAPROXY_DEPLOYMENT_COLOUR - Blue/green deployment colour. Used by the bluegreen_deploy.py script to determine the state of a deploy. You generally do not need to modify this unless you implement your own deployment orchestrator. - - HAPROXY_DEPLOYMENT_STARTED_AT - The time at which a deployment started. You generally do not need to modify this unless you implement your own deployment orchestrator. - - HAPROXY_DEPLOYMENT_TARGET_INSTANCES - The target number of app instances to seek during deployment. You generally do not need to modify this unless you implement your own deployment orchestrator. - - HAPROXY_{n}_VHOST - The Marathon HTTP Virtual Host proxy hostname(s) to gather. - Ex: HAPROXY_0_VHOST = 'marathon.mesosphere.com' - Ex: HAPROXY_0_VHOST = 'marathon.mesosphere.com,marathon' - - HAPROXY_{n}_PATH - The HTTP path to match, starting at the beginning. To specify multiple paths, - pass a space separated list. The syntax matches that of the `path_beg` config - option in HAProxy. To use the path routing, you must also define a VHost. - Ex: HAPROXY_0_PATH = '/v2/api/derp' - Ex: HAPROXY_0_PATH = '-i /multiple /paths' - - HAPROXY_{n}_STICKY - Enable sticky request routing for the service. - Ex: HAPROXY_0_STICKY = true - - HAPROXY_{n}_REDIRECT_TO_HTTPS - Redirect HTTP traffic to HTTPS. Requires at least a VHost be set. - Ex: HAPROXY_0_REDIRECT_TO_HTTPS = true - - HAPROXY_{n}_USE_HSTS - Enable the HSTS response header for HTTP clients which support it. - Ex: HAPROXY_0_USE_HSTS = true - - HAPROXY_{n}_SSL_CERT - Enable the given SSL certificate for TLS/SSL traffic. - Ex: HAPROXY_0_SSL_CERT = '/etc/ssl/certs/marathon.mesosphere.com' - - HAPROXY_{n}_BIND_OPTIONS - Set additional bind options - Ex: HAPROXY_0_BIND_OPTIONS = 'ciphers AES128+EECDH:AES128+EDH force-tlsv12 no-sslv3' - - HAPROXY_{n}_BIND_ADDR - Bind to the specific address for the service. - Ex: HAPROXY_0_BIND_ADDR = '10.0.0.42' - - HAPROXY_{n}_PORT - Bind to the specific port for the service. - This overrides the servicePort which has to be unique. - Ex: HAPROXY_0_PORT = 80 - - HAPROXY_{n}_GROUP - Haproxy group per service. This helps us have different HA Proxy groups per service port. - This overrides HAPROXY_GROUP for the particular service. - If you have both external and internal services running on same set of instances on - different ports, you can use this feature to add them to different haproxy configs. - Ex: HAPROXY_0_GROUP = 'external' - HAPROXY_1_GROUP = 'internal' - - Now if you run marathon_lb with --group external, it just adds the service on HAPROXY_0_PORT - (or first service port incase HAPROXY_0_HOST is not configured) to haproxy config and similarly - if you run it with --group internal, it adds service on HAPROXY_1_PORT to haproxy config. - If the configuration is a combination of HAPROXY_GROUP and HAPROXY_{n}_GROUP, - the more specific definition takes precedence. - Ex: HAPROXY_0_GROUP = 'external' - HAPROXY_GROUP = 'internal' - - Considering the above example where the configuration is hybrid, Service running on - HAPROXY_0_PORT is associated with just 'external' haproxy group and not 'internal' group. - And since there is no Haproxy group mentioned for second service (HAPROXY_1_GROUP not defined) - it falls back to default HAPROXY_GROUP and gets associated with 'internal' group. - - - HAPROXY_{n}_MODE - Set the connection mode to either TCP or HTTP. The default is TCP. - Ex: HAPROXY_0_MODE = 'http' - - HAPROXY_{n}_BALANCE - Set the load balancing algorithm to be used in a backend. The default is roundrobin. - Ex: HAPROXY_0_BALANCE = 'leastconn' -``` +See [the configuration doc for the full list](Longhelp.md#other-labels) +of labels. ### Templates @@ -255,176 +163,10 @@ path from where the script is run. Some templates can also be [overridden _per app service port_](#overridable-templates). You may add your own templates to the Docker image, or provide them at startup. -``` - HAPROXY_HEAD - The head of the HAProxy config. This contains global settings - and defaults. - - HAPROXY_HTTP_FRONTEND_HEAD - An HTTP frontend that binds to port *:80 by default and gathers - all virtual hosts as defined by the HAPROXY_{n}_VHOST variable. - - HAPROXY_HTTP_FRONTEND_APPID_HEAD - An HTTP frontend that binds to port *:9091 by default and gathers - all apps in HTTP mode. - To use this frontend to forward to your app, configure the app with - "HAPROXY_0_MODE=http" then you can access it via a call to the :9091 with - the header "X-Marathon-App-Id" set to the Marathon AppId. - Note multiple HTTP ports being exposed by the same marathon app are not - supported. Only the first HTTP port is available via this frontend. - - HAPROXY_HTTPS_FRONTEND_HEAD - An HTTPS frontend for encrypted connections that binds to port *:443 by - default and gathers all virtual hosts as defined by the - HAPROXY_{n}_VHOST variable. You must modify this file to - include your certificate. - - HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS - This template is used with backends where the - HAPROXY_{n}_REDIRECT_TO_HTTPS label is set to true. - - HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH - Same as HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS, - but includes a path. - - HAPROXY_BACKEND_HSTS_OPTIONS - This template is used for the backend where the - HAPROXY_{n}_USE_HSTS label is set to true. - - HAPROXY_BACKEND_HTTP_OPTIONS - Sets HTTP headers, for example X-Forwarded-For and X-Forwarded-Proto. - - HAPROXY_BACKEND_HTTP_HEALTHCHECK_OPTIONS - Sets HTTP health check options, for example timeout check and httpchk GET. - Parameters of the first health check for this service are exposed as: - * healthCheckPortIndex - * healthCheckPort - * healthCheckProtocol - * healthCheckPath - * healthCheckTimeoutSeconds - * healthCheckIntervalSeconds - * healthCheckIgnoreHttp1xx - * healthCheckGracePeriodSeconds - * healthCheckMaxConsecutiveFailures - * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 - * healthCheckPortOptions is set to " port {healthCheckPort}" - Defaults to empty string. - Example: - option httpchk GET {healthCheckPath} - timeout check {healthCheckTimeoutSeconds}s - - - HAPROXY_BACKEND_TCP_HEALTHCHECK_OPTIONS - Sets TCP health check options, for example timeout check. - Parameters of the first health check for this service are exposed as: - * healthCheckPortIndex - * healthCheckPort - * healthCheckProtocol - * healthCheckTimeoutSeconds - * healthCheckIntervalSeconds - * healthCheckGracePeriodSeconds - * healthCheckMaxConsecutiveFailures - * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 - * healthCheckPortOptions is set to " port {healthCheckPort}" - Defaults to empty string. - Example: - timeout check {healthCheckTimeoutSeconds}s - - HAPROXY_BACKEND_STICKY_OPTIONS - Sets a cookie for services where HAPROXY_{n}_STICKY is true. - - HAPROXY_FRONTEND_HEAD - Defines the address and port to bind to. - - HAPROXY_BACKEND_HEAD - Defines the type of load balancing, roundrobin by default, - and connection mode, TCP or HTTP. - - HAPROXY_HTTP_FRONTEND_ACL - The ACL that glues a backend to the corresponding virtual host - of the HAPROXY_HTTP_FRONTEND_HEAD. - - HAPROXY_HTTP_FRONTEND_ACL_ONLY - Define the ACL matching a particular hostname, but unlike - HAPROXY_HTTP_FRONTEND_ACL, only do the ACL portion. Does not glue - the ACL to the backend. This is useful only in the case of multiple - vhosts routing to the same backend - - HAPROXY_HTTP_FRONTEND_ROUTING_ONLY - This is the counterpart to HAPROXY_HTTP_FRONTEND_ACL_ONLY which - glues the acl name to the appropriate backend. - - HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH - The ACL that glues a backend to the corresponding virtual host with path - of the HAPROXY_HTTP_FRONTEND_HEAD. - - HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH - Define the ACL matching a particular hostname with path, but unlike - HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH, only do the ACL portion. Does not glue - the ACL to the backend. This is useful only in the case of multiple - vhosts routing to the same backend - - HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH - Same as HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH except for HTTPS. - - HAPROXY_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH - This is the counterpart to HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH which - glues the acl names to the appropriate backend. - - HAPROXY_HTTP_FRONTEND_APPID_ACL - The ACL that glues a backend to the corresponding app - of the HAPROXY_HTTP_FRONTEND_APPID_HEAD. - - HAPROXY_HTTPS_FRONTEND_ACL - The ACL that performs the SNI based hostname matching - for the HAPROXY_HTTPS_FRONTEND_HEAD. - - HAPROXY_HTTPS_FRONTEND_ACL_WITH_PATH - The ACL that performs the SNI based hostname matching with path - for the HAPROXY_HTTPS_FRONTEND_HEAD. - - HAPROXY_BACKEND_SERVER_OPTIONS - The options for each physical server added to a backend. - - - HAPROXY_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS - Sets HTTP health check options for a single server, e.g. check inter. - Parameters of the first health check for this service are exposed as: - * healthCheckPortIndex - * healthCheckPort - * healthCheckProtocol - * healthCheckPath - * healthCheckTimeoutSeconds - * healthCheckIntervalSeconds - * healthCheckIgnoreHttp1xx - * healthCheckGracePeriodSeconds - * healthCheckMaxConsecutiveFailures - * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 - * healthCheckPortOptions is set to " port {healthCheckPort}" - Defaults to empty string. - Example: - check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls} - - HAPROXY_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS - Sets TCP health check options for a single server, e.g. check inter. - Parameters of the first health check for this service are exposed as: - * healthCheckPortIndex - * healthCheckPort - * healthCheckProtocol - * healthCheckTimeoutSeconds - * healthCheckIntervalSeconds - * healthCheckGracePeriodSeconds - * healthCheckMaxConsecutiveFailures - * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 - * healthCheckPortOptions is set to " port {healthCheckPort}" - - Defaults to empty string. - Example: - check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls} - - HAPROXY_FRONTEND_BACKEND_GLUE - This option glues the backend to the frontend. -``` + +See [the configuration doc for the full list](Longhelp.md#templates) +of templates. + #### Overridable templates Some templates may be overridden using app labels, @@ -449,31 +191,8 @@ 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: -``` -HAPROXY_{n}_FRONTEND_HEAD -HAPROXY_{n}_BACKEND_REDIRECT_HTTP_TO_HTTPS -HAPROXY_{n}_BACKEND_HEAD -HAPROXY_{n}_HTTP_FRONTEND_ACL -HAPROXY_{n}_HTTP_FRONTEND_ACL_ONLY -HAPROXY_{n}_HTTP_FRONTEND_ROUTING_ONLY -HAPROXY_{n}_HTTP_FRONTEND_ACL_WITH_PATH -HAPROXY_{n}_HTTP_FRONTEND_ACL_ONLY_WITH_PATH -HAPROXY_{n}_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH -HAPROXY_{n}_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH -HAPROXY_{n}_HTTP_FRONTEND_APPID_ACL -HAPROXY_{n}_HTTPS_FRONTEND_ACL -HAPROXY_{n}_HTTPS_FRONTEND_ACL_WITH_PATH -HAPROXY_{n}_BACKEND_HTTP_OPTIONS -HAPROXY_{n}_BACKEND_HSTS_OPTIONS -HAPROXY_{n}_BACKEND_HTTP_HEALTHCHECK_OPTIONS -HAPROXY_{n}_BACKEND_TCP_HEALTHCHECK_OPTIONS -HAPROXY_{n}_BACKEND_STICKY_OPTIONS -HAPROXY_{n}_BACKEND_SERVER_OPTIONS -HAPROXY_{n}_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS -HAPROXY_{n}_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS -HAPROXY_{n}_FRONTEND_BACKEND_GLUE -``` +The full list of per service port templates which can be specified +are [documented here](Longhelp.md#templates). ## Zero downtime deployments diff --git a/config.py b/config.py new file mode 100644 index 00000000..7d90c9d4 --- /dev/null +++ b/config.py @@ -0,0 +1,1000 @@ +#!/usr/bin/env python3 + +import os +import logging + +logger = logging.getLogger('marathon_lb') + + +class ConfigTemplate: + def __init__(self, name, value, overridable, description): + self.name = name + self.full_name = 'HAPROXY_' + name + self.value = value + self.default_value = value + self.overridable = overridable + self.description = description + + +class ConfigTemplater(object): + def add_template(self, template): + self.t[template.name] = template + + def load(self): + self.add_template( + ConfigTemplate(name='HEAD', + value='''\ +global + daemon + log /dev/log local0 + log /dev/log local1 notice + 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-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-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 +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 + option redispatch + option http-server-close + option dontlognull +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 getconfig path /_haproxy_getconfig + http-request use-service lua.getconfig if getconfig +''', + overridable=False, + description='''\ +The head of the HAProxy config. This contains global settings +and defaults. +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_HEAD', + value=''' +frontend marathon_http_in + bind *:80 + mode http +''', + overridable=False, + description='''\ +An HTTP frontend that binds to port *:80 by default and gathers +all virtual hosts as defined by the `HAPROXY_{n}_VHOST` label. +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_APPID_HEAD', + value=''' +frontend marathon_http_appid_in + bind *:9091 + mode http +''', + overridable=False, + description='''\ +An HTTP frontend that binds to port *:9091 by default and gathers +all apps in HTTP mode. +To use this frontend to forward to your app, configure the app with +`HAPROXY_0_MODE=http` then you can access it via a call to the :9091 +with the header "X-Marathon-App-Id" set to the Marathon AppId. +Note multiple HTTP ports being exposed by the same marathon app are not +supported. Only the first HTTP port is available via this frontend. +''')) + + # TODO(lloesche): make certificate path dynamic and allow multi-certs + self.add_template( + ConfigTemplate(name='HTTPS_FRONTEND_HEAD', + value=''' +frontend marathon_https_in + bind *:443 ssl {sslCerts} + mode http +''', + overridable=False, + description='''\ +An HTTPS frontend for encrypted connections that binds to port *:443 by +default and gathers all virtual hosts as defined by the +`HAPROXY_{n}_VHOST` label. You must modify this file to +include your certificate. +''')) + + self.add_template( + ConfigTemplate(name='FRONTEND_HEAD', + value=''' +frontend {backend} + bind {bindAddr}:{servicePort}{sslCert}{bindOptions} + mode {mode} +''', + overridable=True, + description='''\ +Defines the address and port to bind to for this frontend. +''')) + + self.add_template( + ConfigTemplate(name='BACKEND_HEAD', + value=''' +backend {backend} + balance {balance} + mode {mode} +''', + overridable=True, + description='''\ +Defines the type of load balancing, roundrobin by default, +and connection mode, TCP or HTTP. +''')) + + self.add_template( + ConfigTemplate(name='BACKEND_REDIRECT_HTTP_TO_HTTPS', + value='''\ + redirect scheme https code 301 if !{{ ssl_fc }} host_{cleanedUpHostname} +''', + overridable=True, + description='''\ +This template is used with backends where the +`HAPROXY_{n}_REDIRECT_TO_HTTPS` label is set to true +''')) + + self.add_template( + ConfigTemplate(name='BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH', + value='''\ + redirect scheme https code 301 if !{{ ssl_fc }} host_{cleanedUpHostname}\ + path_{backend} +''', + overridable=True, + description='''\ +Same as `HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS`, +but includes a path. +''')) + + self.add_template( + ConfigTemplate(name='BACKEND_HSTS_OPTIONS', + value='''\ + rspadd Strict-Transport-Security:\ max-age=15768000 +''', + overridable=True, + description='''\ +This template is used for the backend where the +`HAPROXY_{n}_USE_HSTS` label is set to true. +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_ACL', + value='''\ + acl host_{cleanedUpHostname} hdr(host) -i {hostname} + use_backend {backend} if host_{cleanedUpHostname} +''', + overridable=True, + description='''\ +The ACL that glues a backend to the corresponding virtual host +of the `HAPROXY_HTTP_FRONTEND_HEAD` +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_ACL_ONLY', + value='''\ + acl host_{cleanedUpHostname} hdr(host) -i {hostname} +''', + overridable=True, + description='''\ +Define the ACL matching a particular hostname, but unlike +`HAPROXY_HTTP_FRONTEND_ACL`, only do the ACL portion. Does not glue +the ACL to the backend. This is useful only in the case of multiple +vhosts routing to the same backend. +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_ROUTING_ONLY', + value='''\ + use_backend {backend} if host_{cleanedUpHostname} +''', + overridable=True, + description='''\ +This is the counterpart to `HAPROXY_HTTP_FRONTEND_ACL_ONLY` which +glues the acl name to the appropriate backend. +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_ACL_WITH_PATH', + value='''\ + acl host_{cleanedUpHostname} hdr(host) -i {hostname} + acl path_{backend} path_beg {path} + use_backend {backend} if host_{cleanedUpHostname} path_{backend} +''', + overridable=True, + description='''\ +The ACL that glues a backend to the corresponding virtual host with path +of the `HAPROXY_HTTP_FRONTEND_HEAD`. +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_ACL_ONLY_WITH_PATH', + value='''\ + acl path_{backend} path_beg {path} +''', + overridable=True, + description='''\ +Define the ACL matching a particular hostname with path, but unlike +`HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH`, only do the ACL portion. Does not glue +the ACL to the backend. This is useful only in the case of multiple +vhosts routing to the same backend +''')) + + self.add_template( + ConfigTemplate(name='HTTPS_FRONTEND_ACL_ONLY_WITH_PATH', + value='''\ + acl path_{backend} path_beg {path} +''', + overridable=True, + description='''\ +Same as HTTP_FRONTEND_ACL_ONLY_WITH_PATH, but for HTTPS. +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH', + value='''\ + use_backend {backend} if host_{cleanedUpHostname} path_{backend} +''', + overridable=True, + description='''\ +This is the counterpart to `HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH` which +glues the acl names to the appropriate backend +''')) + + self.add_template( + ConfigTemplate(name='HTTP_FRONTEND_APPID_ACL', + value='''\ + acl app_{cleanedUpAppId} hdr(x-marathon-app-id) -i {appId} + use_backend {backend} if app_{cleanedUpAppId} +''', + overridable=True, + description='''\ +The ACL that glues a backend to the corresponding app +of the `HAPROXY_HTTP_FRONTEND_APPID_HEAD`. +''')) + + self.add_template( + ConfigTemplate(name='HTTPS_FRONTEND_ACL', + value='''\ + use_backend {backend} if {{ ssl_fc_sni {hostname} }} +''', + overridable=True, + description='''\ +The ACL that performs the SNI based hostname matching +for the `HAPROXY_HTTPS_FRONTEND_HEAD` template. +''')) + + self.add_template( + ConfigTemplate(name='HTTPS_FRONTEND_ACL_WITH_PATH', + value='''\ + use_backend {backend} if {{ ssl_fc_sni {hostname} }} path_{backend} +''', + overridable=True, + description='''\ +The ACL that performs the SNI based hostname matching with path +for the `HAPROXY_HTTPS_FRONTEND_HEAD` template. +''')) + + self.add_template( + ConfigTemplate(name='BACKEND_HTTP_OPTIONS', + value='''\ + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } +''', + overridable=True, + description='''\ +Sets HTTP headers, for example X-Forwarded-For and X-Forwarded-Proto. +''')) + + self.add_template( + ConfigTemplate(name='BACKEND_HTTP_HEALTHCHECK_OPTIONS', + value='''\ + option httpchk GET {healthCheckPath} + timeout check {healthCheckTimeoutSeconds}s +''', + overridable=True, + description='''\ +Sets HTTP health check options, for example timeout check and httpchk GET. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckPath + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckIgnoreHttp1xx + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to ` port {healthCheckPort}` + +Defaults to empty string. + +Example: +``` + option httpchk GET {healthCheckPath} + timeout check {healthCheckTimeoutSeconds}s +``` + ''')) + + self.add_template( + ConfigTemplate(name='BACKEND_TCP_HEALTHCHECK_OPTIONS', + value='', + overridable=True, + description='''\ +Sets TCP health check options, for example timeout check. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to ` port {healthCheckPort}` + +Defaults to empty string. + +Example: +``` + timeout check {healthCheckTimeoutSeconds}s +``` + ''')) + + self.add_template( + ConfigTemplate(name='BACKEND_STICKY_OPTIONS', + value='''\ + cookie mesosphere_server_id insert indirect nocache +''', + overridable=True, + description='''\ +Sets a cookie for services where `HAPROXY_{n}_STICKY` is true. + ''')) + + self.add_template( + ConfigTemplate(name='BACKEND_SERVER_OPTIONS', + value='''\ + server {serverName} {host_ipv4}:{port}{cookieOptions}\ +{healthCheckOptions}{otherOptions} +''', + overridable=True, + description='''\ +The options for each server added to a backend. + ''')) + + self.add_template( + ConfigTemplate(name='BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS', + value='''\ + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls}\ +{healthCheckPortOptions} +''', + overridable=True, + description='''\ +Sets HTTP health check options for a single server, e.g. check inter. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckPath + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckIgnoreHttp1xx + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to ` port {healthCheckPort}` + +Defaults to empty string. + +Example: +``` + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls} +``` + ''')) + + self.add_template( + ConfigTemplate(name='BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS', + value='''\ + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls}\ +{healthCheckPortOptions} +''', + overridable=True, + description='''\ +Sets TCP health check options for a single server, e.g. check inter. +Parameters of the first health check for this service are exposed as: + * healthCheckPortIndex + * healthCheckPort + * healthCheckProtocol + * healthCheckTimeoutSeconds + * healthCheckIntervalSeconds + * healthCheckGracePeriodSeconds + * healthCheckMaxConsecutiveFailures + * healthCheckFalls is set to healthCheckMaxConsecutiveFailures + 1 + * healthCheckPortOptions is set to ` port {healthCheckPort}` + +Defaults to empty string. + +Example: +``` + check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls} +``` + ''')) + + self.add_template( + ConfigTemplate(name='FRONTEND_BACKEND_GLUE', + value='''\ + use_backend {backend} +''', + overridable=True, + description='''\ +This option glues the backend to the frontend. + ''')) + + def __init__(self, directory='templates'): + self.__template_directory = directory + self.t = dict() + self.load() + self.__load_templates() + + def __load_templates(self): + '''Loads template files if they exist, othwerwise it sets defaults''' + + for template in self.t: + name = self.t[template].full_name + try: + filename = os.path.join(self.__template_directory, name) + with open(filename) as f: + logger.info('overriding %s from %s', name, filename) + self.t[template].value = f.read() + except IOError: + logger.debug("setting default value for %s", name) + + def get_descriptions(self): + descriptions = '''\ +## Templates + +The following is a list of the available HAProxy templates. +Some templates are global-only (such as `HAPROXY_HEAD`), but most may +be overridden on a per service port basis using the +`HAPROXY_{n}_...` syntax. + +''' + desc_template = '''\ +## `{name}` + *{overridable}* + +May be specified as {specifiedAs}. + +{description} + +#### Default template for `{name}`: +``` +{default} +``` +''' + for tname in sorted(self.t.keys()): + t = self.t[tname] + spec = "`HAPROXY_" + t.name + "` template" + if t.overridable: + spec += " or with label `HAPROXY_{n}_" + t.name + "`" + descriptions += desc_template.format( + name=t.name, + specifiedAs=spec, + overridable="Overridable" if t.overridable else "Global", + description=t.description, + default=t.default_value + ) + + descriptions += '''\ +## Other Labels +These labels may be used to configure other app settings. + +''' + desc_template = '''\ +## `{name}` + *{perServicePort}* + +May be specified as {specifiedAs}. + +{description} + +''' + for label in labels: + if label.name not in self.t: + if label.name == 'GROUP': + # this one is a special snowflake + spec = "`HAPROXY_{n}_" + label.name + "`" + " or " + \ + "`HAPROXY_" + label.name + "`" + elif label.perServicePort: + spec = "`HAPROXY_{n}_" + label.name + "`" + else: + spec = "`HAPROXY_" + label.name + "`" + descriptions += desc_template.format( + name=label.name, + specifiedAs=spec, + perServicePort="per service port" if label.perServicePort + else "per app", + description=label.description + ) + return descriptions + + @property + def haproxy_head(self): + return self.t['HEAD'].value + + @property + def haproxy_http_frontend_head(self): + return self.t['HTTP_FRONTEND_HEAD'].value + + @property + def haproxy_http_frontend_appid_head(self): + return self.t['HTTP_FRONTEND_APPID_HEAD'].value + + @property + def haproxy_https_frontend_head(self): + return self.t['HTTPS_FRONTEND_HEAD'].value + + def haproxy_frontend_head(self, app): + if 'FRONTEND_HEAD' in app.labels: + return app.labels['HAPROXY_{0}_FRONTEND_HEAD'] + return self.t['FRONTEND_HEAD'].value + + def haproxy_backend_redirect_http_to_https(self, app): + if 'HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS'] + return self.t['BACKEND_REDIRECT_HTTP_TO_HTTPS'].value + + def haproxy_backend_redirect_http_to_https_with_path(self, app): + if 'HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH' in\ + app.labels: + return app.\ + labels['HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH'] + return self.t['BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH'].value + + def haproxy_backend_hsts_options(self, app): + if 'HAPROXY_{0}_BACKEND_HSTS_OPTIONS' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_HSTS_OPTIONS'] + return self.t['BACKEND_HSTS_OPTIONS'].value + + def haproxy_backend_head(self, app): + if 'HAPROXY_{0}_BACKEND_HEAD' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_HEAD'] + return self.t['BACKEND_HEAD'].value + + def haproxy_http_frontend_acl(self, app): + if 'HAPROXY_{0}_HTTP_FRONTEND_ACL' in app.labels: + return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL'] + return self.t['HTTP_FRONTEND_ACL'].value + + def haproxy_http_frontend_acl_only(self, app): + if 'HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY' in app.labels: + return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY'] + return self.t['HTTP_FRONTEND_ACL_ONLY'].value + + def haproxy_http_frontend_routing_only(self, app): + if 'HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY' in app.labels: + return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY'] + return self.t['HTTP_FRONTEND_ROUTING_ONLY'].value + + def haproxy_http_frontend_acl_with_path(self, app): + if 'HAPROXY_{0}_HTTP_FRONTEND_ACL_WITH_PATH' in app.labels: + return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL_WITH_PATH'] + return self.t['HTTP_FRONTEND_ACL_WITH_PATH'].value + + def haproxy_http_frontend_acl_only_with_path(self, app): + if 'HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY_WITH_PATH' in app.labels: + return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY_WITH_PATH'] + return self.t['HTTP_FRONTEND_ACL_ONLY_WITH_PATH'].value + + def haproxy_https_frontend_acl_only_with_path(self, app): + if 'HAPROXY_{0}_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH' in app.labels: + return app.labels['HAPROXY_{0}_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH'] + return self.t['HTTPS_FRONTEND_ACL_ONLY_WITH_PATH'].value + + def haproxy_http_frontend_routing_only_with_path(self, app): + if 'HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH' in app.labels: + return \ + app.labels['HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH'] + return self.t['HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH'].value + + def haproxy_http_frontend_appid_acl(self, app): + if 'HAPROXY_{0}_HTTP_FRONTEND_APPID_ACL' in app.labels: + return app.labels['HAPROXY_{0}_HTTP_FRONTEND_APPID_ACL'] + return self.t['HTTP_FRONTEND_APPID_ACL'].value + + def haproxy_https_frontend_acl(self, app): + if 'HAPROXY_{0}_HTTPS_FRONTEND_ACL' in app.labels: + return app.labels['HAPROXY_{0}_HTTPS_FRONTEND_ACL'] + return self.t['HTTPS_FRONTEND_ACL'].value + + def haproxy_https_frontend_acl_with_path(self, app): + if 'HAPROXY_{0}_HTTPS_FRONTEND_ACL_WITH_PATH' in app.labels: + return app.labels['HAPROXY_{0}_HTTPS_FRONTEND_ACL_WITH_PATH'] + return self.t['HTTPS_FRONTEND_ACL_WITH_PATH'].value + + def haproxy_backend_http_options(self, app): + if 'HAPROXY_{0}_BACKEND_HTTP_OPTIONS' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_HTTP_OPTIONS'] + return self.t['BACKEND_HTTP_OPTIONS'].value + + def haproxy_backend_http_healthcheck_options(self, app): + if 'HAPROXY_{0}_BACKEND_HTTP_HEALTHCHECK_OPTIONS' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_HTTP_HEALTHCHECK_OPTIONS'] + return self.t['BACKEND_HTTP_HEALTHCHECK_OPTIONS'].value + + def haproxy_backend_tcp_healthcheck_options(self, app): + if 'HAPROXY_{0}_BACKEND_TCP_HEALTHCHECK_OPTIONS' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_TCP_HEALTHCHECK_OPTIONS'] + return self.t['BACKEND_TCP_HEALTHCHECK_OPTIONS'].value + + def haproxy_backend_sticky_options(self, app): + if 'HAPROXY_{0}_BACKEND_STICKY_OPTIONS' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_STICKY_OPTIONS'] + return self.t['BACKEND_STICKY_OPTIONS'].value + + def haproxy_backend_server_options(self, app): + if 'HAPROXY_{0}_BACKEND_SERVER_OPTIONS' in app.labels: + return app.labels['HAPROXY_{0}_BACKEND_SERVER_OPTIONS'] + return self.t['BACKEND_SERVER_OPTIONS'].value + + def haproxy_backend_server_http_healthcheck_options(self, app): + if 'HAPROXY_{0}_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS' in \ + app.labels: + return self.__blank_prefix_or_empty( + app.labels['HAPROXY_{0}_BACKEND' + + '_SERVER_HTTP_HEALTHCHECK_OPTIONS'] + .strip()) + return self.__blank_prefix_or_empty( + self.t['BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS'].value.strip()) + + def haproxy_backend_server_tcp_healthcheck_options(self, app): + if 'HAPROXY_{0}_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS' in app.labels: + return self.__blank_prefix_or_empty( + app.labels['HAPROXY_{0}_BACKEND_' + 'SERVER_TCP_HEALTHCHECK_OPTIONS'] + .strip()) + return self.__blank_prefix_or_empty( + self.t['BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS'].value.strip()) + + def haproxy_frontend_backend_glue(self, app): + if 'HAPROXY_{0}_FRONTEND_BACKEND_GLUE' in app.labels: + return app.labels['HAPROXY_{0}_FRONTEND_BACKEND_GLUE'] + return self.t['FRONTEND_BACKEND_GLUE'].value + + def __blank_prefix_or_empty(self, s): + if s: + return ' ' + s + else: + return s + + +def string_to_bool(s): + return s.lower() in ["true", "t", "yes", "y"] + + +def set_hostname(x, k, v): + x.hostname = v + + +def set_path(x, k, v): + x.path = v + + +def set_sticky(x, k, v): + x.sticky = string_to_bool(v) + + +def set_redirect_http_to_https(x, k, v): + x.redirectHttpToHttps = string_to_bool(v) + + +def set_use_hsts(x, k, v): + x.useHsts = string_to_bool(v) + + +def set_sslCert(x, k, v): + x.sslCert = v + + +def set_bindOptions(x, k, v): + x.bindOptions = v + + +def set_bindAddr(x, k, v): + x.bindAddr = v + + +def set_port(x, k, v): + x.servicePort = int(v) + + +def set_mode(x, k, v): + x.mode = v + + +def set_balance(x, k, v): + x.balance = v + + +def set_label(x, k, v): + x.labels[k] = v + + +def set_group(x, k, v): + x.haproxy_groups = v.split(',') + + +class Label: + def __init__(self, name, func, description, perServicePort=True): + self.name = name + self.perServicePort = perServicePort + if perServicePort: + self.full_name = 'HAPROXY_{0}_' + name + else: + self.full_name = 'HAPROXY_' + name + self.func = func + self.description = description + +labels = [] +labels.append(Label(name='VHOST', + func=set_hostname, + description='''\ +The Marathon HTTP Virtual Host proxy hostname(s) to gather. + +Ex: `HAPROXY_0_VHOST = 'marathon.mesosphere.com'` + +Ex: `HAPROXY_0_VHOST = 'marathon.mesosphere.com,marathon'` + ''')) +labels.append(Label(name='GROUP', + func=set_group, + description='''\ +HAProxy group per service. This helps us have different HAProxy groups +per service port. This overrides `HAPROXY_GROUP` for the particular service. +If you have both external and internal services running on same set of +instances on different ports, you can use this feature to add them to +different haproxy configs. + +Ex: `HAPROXY_0_GROUP = 'external'` + +Ex: `HAPROXY_1_GROUP = 'internal'` + +Now if you run marathon_lb with --group external, it just adds the +service on `HAPROXY_0_PORT` (or first service port incase `HAPROXY_0_HOST` +is not configured) to haproxy config and similarly if you run it with +--group internal, it adds service on `HAPROXY_1_PORT` to haproxy config. +If the configuration is a combination of `HAPROXY_GROUP` and +`HAPROXY_{n}_GROUP`, the more specific definition takes precedence. + +Ex: `HAPROXY_0_GROUP = 'external'` + +Ex: `HAPROXY_GROUP = 'internal'` + +Considering the above example where the configuration is hybrid, +a service running on `HAPROXY_0_PORT` is associated with just 'external' +HAProxy group and not 'internal' group. And since there is no HAProxy +group mentioned for second service (`HAPROXY_1_GROUP` not defined) +it falls back to default `HAPROXY_GROUP` and gets associated with +'internal' group. + +Load balancers with the group '*' will collect all groups. + ''')) +labels.append(Label(name='DEPLOYMENT_GROUP', + func=None, + description='''\ +Deployment group to which this app belongs. + ''', + perServicePort=False)) +labels.append(Label(name='DEPLOYMENT_ALT_PORT', + func=None, + description='''\ +Alternate service port to be used during a blue/green deployment. + ''', + perServicePort=False)) +labels.append(Label(name='DEPLOYMENT_COLOUR', + func=None, + description='''\ +Blue/green deployment colour. Used by the bluegreen_deploy.py script +to determine the state of a deploy. You generally do not need to modify +this unless you implement your own deployment orchestrator. + ''', + perServicePort=False)) +labels.append(Label(name='DEPLOYMENT_STARTED_AT', + func=None, + description='''\ +The time at which a deployment started. You generally do not need +to modify this unless you implement your own deployment orchestrator. + ''', + perServicePort=False)) +labels.append(Label(name='DEPLOYMENT_TARGET_INSTANCES', + func=None, + description='''\ +The target number of app instances to seek during deployment. You +generally do not need to modify this unless you implement your +own deployment orchestrator. + ''', + perServicePort=False)) +labels.append(Label(name='PATH', + func=set_path, + description='''\ + ''')) +labels.append(Label(name='STICKY', + func=set_sticky, + description='''\ +Enable sticky request routing for the service. + +Ex: `HAPROXY_0_STICKY = true` + ''')) +labels.append(Label(name='REDIRECT_TO_HTTPS', + func=set_redirect_http_to_https, + description='''\ +Redirect HTTP traffic to HTTPS. Requires at least a VHost be set. + +Ex: `HAPROXY_0_REDIRECT_TO_HTTPS = true` + ''')) +labels.append(Label(name='USE_HSTS', + func=set_use_hsts, + description='''\ +Enable the HSTS response header for HTTP clients which support it. + +Ex: `HAPROXY_0_USE_HSTS = true` + ''')) +labels.append(Label(name='SSL_CERT', + func=set_sslCert, + description='''\ +Enable the given SSL certificate for TLS/SSL traffic. + +Ex: `HAPROXY_0_SSL_CERT = '/etc/ssl/certs/marathon.mesosphere.com'` + ''')) +labels.append(Label(name='BIND_OPTIONS', + func=set_bindOptions, + description='''\ +Set additional bind options + +Ex: `HAPROXY_0_BIND_OPTIONS = 'ciphers AES128+EECDH:AES128+EDH force-tlsv12\ + no-sslv3'` + ''')) +labels.append(Label(name='BIND_ADDR', + func=set_bindAddr, + description='''\ +Bind to the specific address for the service. + +Ex: `HAPROXY_0_BIND_ADDR = '10.0.0.42'` + ''')) +labels.append(Label(name='PORT', + func=set_port, + description='''\ +Bind to the specific port for the service. +This overrides the servicePort which has to be unique. + +Ex: `HAPROXY_0_PORT = 80` + ''')) +labels.append(Label(name='MODE', + func=set_mode, + description='''\ +Set the connection mode to either TCP or HTTP. The default is TCP. + +Ex: `HAPROXY_0_MODE = 'http'` + ''')) +labels.append(Label(name='BALANCE', + func=set_balance, + description='''\ +Set the load balancing algorithm to be used in a backend. The default is +roundrobin. + +Ex: `HAPROXY_0_BALANCE = 'leastconn'` + ''')) +labels.append(Label(name='FRONTEND_HEAD', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_REDIRECT_HTTP_TO_HTTPS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_HEAD', + func=set_label, + description='')) +labels.append(Label(name='HTTP_FRONTEND_ACL', + func=set_label, + description='')) +labels.append(Label(name='HTTP_FRONTEND_ACL_ONLY', + func=set_label, + description='')) +labels.append(Label(name='HTTP_FRONTEND_ROUTING_ONLY', + func=set_label, + description='')) +labels.append(Label(name='HTTP_FRONTEND_ACL_WITH_PATH', + func=set_label, + description='')) +labels.append(Label(name='HTTP_FRONTEND_ACL_ONLY_WITH_PATH', + func=set_label, + description='')) +labels.append(Label(name='HTTPS_FRONTEND_ACL_ONLY_WITH_PATH', + func=set_label, + description='')) +labels.append(Label(name='HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH', + func=set_label, + description='')) +labels.append(Label(name='HTTP_FRONTEND_APPID_ACL', + func=set_label, + description='')) +labels.append(Label(name='HTTPS_FRONTEND_ACL', + func=set_label, + description='')) +labels.append(Label(name='HTTPS_FRONTEND_ACL_WITH_PATH', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_HTTP_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_HSTS_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_HTTP_HEALTHCHECK_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_TCP_HEALTHCHECK_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_STICKY_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_SERVER_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS', + func=set_label, + description='')) +labels.append(Label(name='FRONTEND_BACKEND_GLUE', + func=set_label, + description='')) + +label_keys = {} +for label in labels: + if not label.func: + continue + label_keys[label.full_name] = label.func diff --git a/marathon_lb.py b/marathon_lb.py index eb337009..6bbb3376 100755 --- a/marathon_lb.py +++ b/marathon_lb.py @@ -1,56 +1,34 @@ #!/usr/bin/env python3 -"""Overview: - The marathon-lb is a replacement for the haproxy-marathon-bridge. - It reads the Marathon task information and dynamically generates - haproxy configuration details. +"""# marathon-lb +### Overview +The marathon-lb is a service discovery and load balancing tool +for Marathon based on HAProxy. It reads the Marathon task information +and dynamically generates HAProxy configuration details. - To gather the task information, the marathon-lb needs to know where - to find Marathon. The service configuration details are stored in labels. +To gather the task information, marathon-lb needs to know where +to find Marathon. The service configuration details are stored in labels. - Every service port in Marathon can be configured independently. +Every service port in Marathon can be configured independently. +### Configuration +Service configuration lives in Marathon via labels. +Marathon-lb just needs to know where to find Marathon. +To run in listening mode you must also specify the address + port at +which marathon-lb can be reached by Marathon. -Features: - - Virtual host aliases for services - - Soft restart of haproxy - - SSL Termination - - (Optional): real-time update from Marathon events - - -Configuration: - Service configuration lives in Marathon via labels. - The marathon-lb just needs to know where to find marathon. - To run in listening mode you must also specify the address + port at - which the marathon-lb can be reached by marathon. - - -Usage: - $ marathon-lb.py --marathon http://marathon1:8080 \ - --haproxy-config /etc/haproxy/haproxy.cfg - - The user that executes marathon-lb must have the permission to reload - haproxy. - - -Operational Notes: - - When a node in listening mode fails, remove the callback url for that - node in marathon. - - If run in listening mode, DNS isn't re-resolved. Restart the process - periodically to force re-resolution if desired. - - To avoid configuring itself as a backend when run via Marathon, - services with appID matching FRAMEWORK_NAME env var will be skipped. +### Command Line Usage """ from logging.handlers import SysLogHandler from operator import attrgetter from shutil import move from tempfile import mkstemp -from textwrap import dedent from wsgiref.simple_server import make_server from six.moves.urllib import parse from itertools import cycle from common import * +from config import * import argparse import json @@ -70,488 +48,6 @@ import threading import random - -class ConfigTemplater(object): - HAPROXY_HEAD = dedent('''\ - global - daemon - log /dev/log local0 - log /dev/log local1 notice - 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-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-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 - 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 - option redispatch - option http-server-close - option dontlognull - 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 getconfig path /_haproxy_getconfig - http-request use-service lua.getconfig if getconfig - ''') - - HAPROXY_HTTP_FRONTEND_HEAD = dedent(''' - frontend marathon_http_in - bind *:80 - mode http - ''') - - HAPROXY_HTTP_FRONTEND_APPID_HEAD = dedent(''' - frontend marathon_http_appid_in - bind *:9091 - mode http - ''') - - # TODO(lloesche): make certificate path dynamic and allow multiple certs - HAPROXY_HTTPS_FRONTEND_HEAD = dedent(''' - frontend marathon_https_in - bind *:443 ssl {sslCerts} - mode http - ''') - - HAPROXY_FRONTEND_HEAD = dedent(''' - frontend {backend} - bind {bindAddr}:{servicePort}{sslCert}{bindOptions} - mode {mode} - ''') - - HAPROXY_BACKEND_HEAD = dedent(''' - backend {backend} - balance {balance} - mode {mode} - ''') - - HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS = '''\ - redirect scheme https code 301 if !{{ ssl_fc }} host_{cleanedUpHostname} -''' - - HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH = '''\ - redirect scheme https code 301 if !{{ ssl_fc }} host_{cleanedUpHostname}\ - path_{backend} -''' - - HAPROXY_BACKEND_HSTS_OPTIONS = '''\ - rspadd Strict-Transport-Security:\ max-age=15768000 -''' - - HAPROXY_HTTP_FRONTEND_ACL = '''\ - acl host_{cleanedUpHostname} hdr(host) -i {hostname} - use_backend {backend} if host_{cleanedUpHostname} -''' - - HAPROXY_HTTP_FRONTEND_ACL_ONLY = '''\ - acl host_{cleanedUpHostname} hdr(host) -i {hostname} -''' - - HAPROXY_HTTP_FRONTEND_ROUTING_ONLY = '''\ - use_backend {backend} if host_{cleanedUpHostname} -''' - - HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH = '''\ - acl host_{cleanedUpHostname} hdr(host) -i {hostname} - acl path_{backend} path_beg {path} - use_backend {backend} if host_{cleanedUpHostname} path_{backend} -''' - - HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH = '''\ - acl path_{backend} path_beg {path} -''' - - HAPROXY_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH = '''\ - acl path_{backend} path_beg {path} -''' - - HAPROXY_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH = '''\ - use_backend {backend} if host_{cleanedUpHostname} path_{backend} -''' - - HAPROXY_HTTP_FRONTEND_APPID_ACL = '''\ - acl app_{cleanedUpAppId} hdr(x-marathon-app-id) -i {appId} - use_backend {backend} if app_{cleanedUpAppId} -''' - - HAPROXY_HTTPS_FRONTEND_ACL = '''\ - use_backend {backend} if {{ ssl_fc_sni {hostname} }} -''' - - HAPROXY_HTTPS_FRONTEND_ACL_WITH_PATH = '''\ - use_backend {backend} if {{ ssl_fc_sni {hostname} }} path_{backend} -''' - - HAPROXY_BACKEND_HTTP_OPTIONS = '''\ - option forwardfor - http-request set-header X-Forwarded-Port %[dst_port] - http-request add-header X-Forwarded-Proto https if { ssl_fc } -''' - - HAPROXY_BACKEND_HTTP_HEALTHCHECK_OPTIONS = '''\ - option httpchk GET {healthCheckPath} - timeout check {healthCheckTimeoutSeconds}s -''' - - HAPROXY_BACKEND_TCP_HEALTHCHECK_OPTIONS = '' - - HAPROXY_BACKEND_STICKY_OPTIONS = '''\ - cookie mesosphere_server_id insert indirect nocache -''' - - HAPROXY_BACKEND_SERVER_OPTIONS = '''\ - server {serverName} {host_ipv4}:{port}{cookieOptions}{healthCheckOptions}\ -{otherOptions} -''' - - HAPROXY_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS = '''\ - check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls}\ -{healthCheckPortOptions} -''' - HAPROXY_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS = '''\ - check inter {healthCheckIntervalSeconds}s fall {healthCheckFalls}\ -{healthCheckPortOptions} -''' - - HAPROXY_FRONTEND_BACKEND_GLUE = '''\ - use_backend {backend} -''' - - def __init__(self, directory='templates'): - self.__template_directory = directory - self.__load_templates() - - def __load_templates(self): - '''Loads template files if they exist, othwerwise it sets defaults''' - variables = [ - 'HAPROXY_HEAD', - 'HAPROXY_HTTP_FRONTEND_HEAD', - 'HAPROXY_HTTP_FRONTEND_APPID_HEAD', - 'HAPROXY_HTTPS_FRONTEND_HEAD', - 'HAPROXY_FRONTEND_HEAD', - 'HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS', - 'HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH', - 'HAPROXY_BACKEND_HSTS_OPTIONS', - 'HAPROXY_BACKEND_HEAD', - 'HAPROXY_HTTP_FRONTEND_ACL', - 'HAPROXY_HTTP_FRONTEND_ACL_ONLY', - 'HAPROXY_HTTP_FRONTEND_ROUTING_ONLY', - 'HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH', - 'HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH', - 'HAPROXY_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH', - 'HAPROXY_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH', - 'HAPROXY_HTTP_FRONTEND_APPID_ACL', - 'HAPROXY_HTTPS_FRONTEND_ACL', - 'HAPROXY_BACKEND_HTTP_OPTIONS', - 'HAPROXY_BACKEND_HTTP_HEALTHCHECK_OPTIONS', - 'HAPROXY_BACKEND_TCP_HEALTHCHECK_OPTIONS', - 'HAPROXY_BACKEND_STICKY_OPTIONS', - 'HAPROXY_BACKEND_SERVER_OPTIONS', - 'HAPROXY_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS', - 'HAPROXY_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS', - 'HAPROXY_FRONTEND_BACKEND_GLUE', - ] - - for variable in variables: - try: - filename = os.path.join(self.__template_directory, variable) - with open(filename) as f: - logger.info('overriding %s from %s', variable, filename) - setattr(self, variable, f.read()) - except IOError: - logger.debug("setting default value for %s", variable) - try: - setattr(self, variable, getattr(self.__class__, variable)) - except AttributeError: - logger.exception('default not found, aborting.') - raise - - @property - def haproxy_head(self): - return self.HAPROXY_HEAD - - @property - def haproxy_http_frontend_head(self): - return self.HAPROXY_HTTP_FRONTEND_HEAD - - @property - def haproxy_http_frontend_appid_head(self): - return self.HAPROXY_HTTP_FRONTEND_APPID_HEAD - - @property - def haproxy_https_frontend_head(self): - return self.HAPROXY_HTTPS_FRONTEND_HEAD - - def haproxy_frontend_head(self, app): - if 'HAPROXY_{0}_FRONTEND_HEAD' in app.labels: - return app.labels['HAPROXY_{0}_FRONTEND_HEAD'] - return self.HAPROXY_FRONTEND_HEAD - - def haproxy_backend_redirect_http_to_https(self, app): - if 'HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS'] - return self.HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS - - def haproxy_backend_redirect_http_to_https_with_path(self, app): - if 'HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH' in\ - app.labels: - return app.\ - labels['HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH'] - return self.HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS_WITH_PATH - - def haproxy_backend_hsts_options(self, app): - if 'HAPROXY_{0}_BACKEND_HSTS_OPTIONS' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_HSTS_OPTIONS'] - return self.HAPROXY_BACKEND_HSTS_OPTIONS - - def haproxy_backend_head(self, app): - if 'HAPROXY_{0}_BACKEND_HEAD' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_HEAD'] - return self.HAPROXY_BACKEND_HEAD - - def haproxy_http_frontend_acl(self, app): - if 'HAPROXY_{0}_HTTP_FRONTEND_ACL' in app.labels: - return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL'] - return self.HAPROXY_HTTP_FRONTEND_ACL - - def haproxy_http_frontend_acl_only(self, app): - if 'HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY' in app.labels: - return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY'] - return self.HAPROXY_HTTP_FRONTEND_ACL_ONLY - - def haproxy_http_frontend_routing_only(self, app): - if 'HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY' in app.labels: - return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY'] - return self.HAPROXY_HTTP_FRONTEND_ROUTING_ONLY - - def haproxy_http_frontend_acl_with_path(self, app): - if 'HAPROXY_{0}_HTTP_FRONTEND_ACL_WITH_PATH' in app.labels: - return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL_WITH_PATH'] - return self.HAPROXY_HTTP_FRONTEND_ACL_WITH_PATH - - def haproxy_http_frontend_acl_only_with_path(self, app): - if 'HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY_WITH_PATH' in app.labels: - return app.labels['HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY_WITH_PATH'] - return self.HAPROXY_HTTP_FRONTEND_ACL_ONLY_WITH_PATH - - def haproxy_https_frontend_acl_only_with_path(self, app): - if 'HAPROXY_{0}_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH' in app.labels: - return app.labels['HAPROXY_{0}_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH'] - return self.HAPROXY_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH - - def haproxy_http_frontend_routing_only_with_path(self, app): - if 'HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH' in app.labels: - return \ - app.labels['HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH'] - return self.HAPROXY_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH - - def haproxy_http_frontend_appid_acl(self, app): - if 'HAPROXY_{0}_HTTP_FRONTEND_APPID_ACL' in app.labels: - return app.labels['HAPROXY_{0}_HTTP_FRONTEND_APPID_ACL'] - return self.HAPROXY_HTTP_FRONTEND_APPID_ACL - - def haproxy_https_frontend_acl(self, app): - if 'HAPROXY_{0}_HTTPS_FRONTEND_ACL' in app.labels: - return app.labels['HAPROXY_{0}_HTTPS_FRONTEND_ACL'] - return self.HAPROXY_HTTPS_FRONTEND_ACL - - def haproxy_https_frontend_acl_with_path(self, app): - if 'HAPROXY_{0}_HTTPS_FRONTEND_ACL_WITH_PATH' in app.labels: - return app.labels['HAPROXY_{0}_HTTPS_FRONTEND_ACL_WITH_PATH'] - return self.HAPROXY_HTTPS_FRONTEND_ACL_WITH_PATH - - def haproxy_backend_http_options(self, app): - if 'HAPROXY_{0}_BACKEND_HTTP_OPTIONS' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_HTTP_OPTIONS'] - return self.HAPROXY_BACKEND_HTTP_OPTIONS - - def haproxy_backend_http_healthcheck_options(self, app): - if 'HAPROXY_{0}_BACKEND_HTTP_HEALTHCHECK_OPTIONS' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_HTTP_HEALTHCHECK_OPTIONS'] - return self.HAPROXY_BACKEND_HTTP_HEALTHCHECK_OPTIONS - - def haproxy_backend_tcp_healthcheck_options(self, app): - if 'HAPROXY_{0}_BACKEND_TCP_HEALTHCHECK_OPTIONS' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_TCP_HEALTHCHECK_OPTIONS'] - return self.HAPROXY_BACKEND_TCP_HEALTHCHECK_OPTIONS - - def haproxy_backend_sticky_options(self, app): - if 'HAPROXY_{0}_BACKEND_STICKY_OPTIONS' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_STICKY_OPTIONS'] - return self.HAPROXY_BACKEND_STICKY_OPTIONS - - def haproxy_backend_server_options(self, app): - if 'HAPROXY_{0}_BACKEND_SERVER_OPTIONS' in app.labels: - return app.labels['HAPROXY_{0}_BACKEND_SERVER_OPTIONS'] - return self.HAPROXY_BACKEND_SERVER_OPTIONS - - def haproxy_backend_server_http_healthcheck_options(self, app): - if 'HAPROXY_{0}_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS' in \ - app.labels: - return self.__blank_prefix_or_empty( - app.labels['HAPROXY_{0}_BACKEND' + - '_SERVER_HTTP_HEALTHCHECK_OPTIONS'] - .strip()) - return self.__blank_prefix_or_empty( - self.HAPROXY_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS.strip()) - - def haproxy_backend_server_tcp_healthcheck_options(self, app): - if 'HAPROXY_{0}_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS' in app.labels: - return self.__blank_prefix_or_empty( - app.labels['HAPROXY_{0}_BACKEND_' - 'SERVER_TCP_HEALTHCHECK_OPTIONS'] - .strip()) - return self.__blank_prefix_or_empty( - self.HAPROXY_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS.strip()) - - def haproxy_frontend_backend_glue(self, app): - if 'HAPROXY_{0}_FRONTEND_BACKEND_GLUE' in app.labels: - return app.labels['HAPROXY_{0}_FRONTEND_BACKEND_GLUE'] - return self.HAPROXY_FRONTEND_BACKEND_GLUE - - def __blank_prefix_or_empty(self, s): - if s: - return ' ' + s - else: - return s - - -def string_to_bool(s): - return s.lower() in ["true", "t", "yes", "y"] - - -def set_hostname(x, k, v): - x.hostname = v - - -def set_path(x, k, v): - x.path = v - - -def set_sticky(x, k, v): - x.sticky = string_to_bool(v) - - -def set_redirect_http_to_https(x, k, v): - x.redirectHttpToHttps = string_to_bool(v) - - -def set_use_hsts(x, k, v): - x.useHsts = string_to_bool(v) - - -def set_sslCert(x, k, v): - x.sslCert = v - - -def set_bindOptions(x, k, v): - x.bindOptions = v - - -def set_bindAddr(x, k, v): - x.bindAddr = v - - -def set_port(x, k, v): - x.servicePort = int(v) - - -def set_mode(x, k, v): - x.mode = v - - -def set_balance(x, k, v): - x.balance = v - - -def set_label(x, k, v): - x.labels[k] = v - - -def set_group(x, k, v): - x.haproxy_groups = v.split(',') - - -label_keys = { - 'HAPROXY_{0}_VHOST': set_hostname, - 'HAPROXY_{0}_GROUP': set_group, - 'HAPROXY_{0}_PATH': set_path, - 'HAPROXY_{0}_STICKY': set_sticky, - 'HAPROXY_{0}_REDIRECT_TO_HTTPS': set_redirect_http_to_https, - 'HAPROXY_{0}_USE_HSTS': set_use_hsts, - 'HAPROXY_{0}_SSL_CERT': set_sslCert, - 'HAPROXY_{0}_BIND_OPTIONS': set_bindOptions, - 'HAPROXY_{0}_BIND_ADDR': set_bindAddr, - 'HAPROXY_{0}_PORT': set_port, - 'HAPROXY_{0}_MODE': set_mode, - 'HAPROXY_{0}_BALANCE': set_balance, - 'HAPROXY_{0}_FRONTEND_HEAD': set_label, - 'HAPROXY_{0}_BACKEND_REDIRECT_HTTP_TO_HTTPS': set_label, - 'HAPROXY_{0}_BACKEND_HEAD': set_label, - 'HAPROXY_{0}_HTTP_FRONTEND_ACL': set_label, - 'HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY': set_label, - 'HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY': set_label, - 'HAPROXY_{0}_HTTP_FRONTEND_ACL_WITH_PATH': set_label, - 'HAPROXY_{0}_HTTP_FRONTEND_ACL_ONLY_WITH_PATH': set_label, - 'HAPROXY_{0}_HTTPS_FRONTEND_ACL_ONLY_WITH_PATH': set_label, - 'HAPROXY_{0}_HTTP_FRONTEND_ROUTING_ONLY_WITH_PATH': set_label, - 'HAPROXY_{0}_HTTP_FRONTEND_APPID_ACL': set_label, - 'HAPROXY_{0}_HTTPS_FRONTEND_ACL': set_label, - 'HAPROXY_{0}_HTTPS_FRONTEND_ACL_WITH_PATH': set_label, - 'HAPROXY_{0}_BACKEND_HTTP_OPTIONS': set_label, - 'HAPROXY_{0}_BACKEND_HSTS_OPTIONS': set_label, - 'HAPROXY_{0}_BACKEND_HTTP_HEALTHCHECK_OPTIONS': set_label, - 'HAPROXY_{0}_BACKEND_TCP_HEALTHCHECK_OPTIONS': set_label, - 'HAPROXY_{0}_BACKEND_STICKY_OPTIONS': set_label, - 'HAPROXY_{0}_BACKEND_SERVER_OPTIONS': set_label, - 'HAPROXY_{0}_BACKEND_SERVER_TCP_HEALTHCHECK_OPTIONS': set_label, - 'HAPROXY_{0}_BACKEND_SERVER_HTTP_HEALTHCHECK_OPTIONS': set_label, - 'HAPROXY_{0}_FRONTEND_BACKEND_GLUE': set_label, -} - logger = logging.getLogger('marathon_lb') @@ -1646,6 +1142,10 @@ def process_sse_events(marathon, config_file, groups, # Print the long help text if flag is set if args.longhelp: print(__doc__) + print('```') + arg_parser.print_help() + print('```') + print(ConfigTemplater().get_descriptions()) sys.exit() # otherwise make sure that a Marathon URL was specified else: