diff --git a/README.md b/README.md index ac4d522..92c70fb 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ EasyHAProxy can detect and configure HAProxy automatically on the following plat - Docker Swarm - Kubernetes +## Who is using? + +EasyHAProxy is part of some projects: +- Dokku +- MicroK8s + +See detailed instructions on how to install below. + ## Features EasyHAProxy will discover the services based on the Docker Tags of the containers running on a Docker host or Docker Swarm cluster and dynamically set up the `haproxy.cfg`. Below, EasyHAProxy main features: @@ -34,7 +42,7 @@ EasyHAProxy will discover the services based on the Docker Tags of the container Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file. -## How Does It Works? +## How Does It Work? You don't need to change your current infrastructure and don't need to learn the HAProxy configuration. diff --git a/docs/docker-environment.md b/docs/docker-environment.md index 0b9ec11..6e86477 100644 --- a/docs/docker-environment.md +++ b/docs/docker-environment.md @@ -1,20 +1,20 @@ # Docker environment variables -| Environment Variable | Description | Default | -|---------------------------------|-------------------------------------------------------------------------------------------------|------------------| -| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** | -| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` | -| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt | *empty* | -| EASYHAPROXY_LETSENCRYPT_SERVER | (Optional) Can be `staging` or 'schema://domain.tld'. If set, will try to connect to the Letsencrypt test server | *empty* | -| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default`| -| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. | 10 | -| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` | -| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* | -| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` | -| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` | +| Environment Variable | Description | Default | +|---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| +| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** | +| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` | +| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt | *empty* | +| EASYHAPROXY_LETSENCRYPT_SERVER | (Optional) Can be `staging` or 'schema://domain.tld'. If set, will try to connect to the Letsencrypt test server | *empty* | +| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default` | +| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. | 10 | +| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | +| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | +| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | INFO | +| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` | +| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* | +| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` | +| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` | diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 8b65f6f..f35020d 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -79,14 +79,17 @@ Caveats: ## Kubernetes annotations -| annotation | Description | Default | Example | -|-----------------------------|-----------------------------------------------------------------------------------------|--------------|--------------| -| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress -| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false -| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencrypt certificates for the ingresses domains. | false | true or false -| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} -| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp -| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp +| annotation | Description | Default | Example | +|----------------------------------|-----------------------------------------------------------------------------------------|--------------|---------------------------------------| +| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress | +| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false | +| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencrypt certificates for the ingresses domains. | false | true or false | +| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} | +| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp | +| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp | +| easyhaproxt.logLevel.certbot | (optional) Certbot log level | DEBUG | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | +| easyhaproxt.logLevel.eashhaproxy | (optional) EasyHAProxy log level | DEBUG | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | +| easyhaproxt.logLevel.haproxy | (optional) HAProxy log level | INFO | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | **Important**: The annotations are per ingress and applied to all hosts in that ingress configuration. diff --git a/docs/static.md b/docs/static.md index 6a35bda..47de056 100644 --- a/docs/static.md +++ b/docs/static.md @@ -20,9 +20,11 @@ customerrors: true # Optional (default false) ssl_mode: default -letsencrypt: { - "email": "acme@example.org" -} +logLevel: + haproxy: INFO + +letsencrypt: + email: "acme@example.org" easymapping: - port: 80 @@ -83,10 +85,14 @@ customerrors: true # Optional (default false) ssl_mode: default # Optional -letsencrypt: { # Optional. If you enable `letsencrypt` will need to setu0p this, - # otherwise the certificate will be issued - "email": "acme@example.org" -} +logLevel: + certbot: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL + easyhaproxy: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL + haproxy: INFO # Optional (default: INFO). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL + +# Optional. If you enable `letsencrypt` will need to set up this, otherwise the certificate will be issued +letsencrypt: + email": "acme@example.org" easymapping: - port: 80 # Listen port diff --git a/src/easymapping/__init__.py b/src/easymapping/__init__.py index c2626a6..a08bddc 100644 --- a/src/easymapping/__init__.py +++ b/src/easymapping/__init__.py @@ -56,7 +56,7 @@ class HaproxyConfigGenerator: self.serving_hosts = [] self.certs = {} - def generate(self, container_metadata = {}): + def generate(self, container_metadata={}): self.mapping.setdefault("easymapping", []) if container_metadata != {}: diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 748ad1e..e296a1c 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -31,7 +31,13 @@ class ContainerEnv: "email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), "server": os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "false").lower() in ["true", "1", "yes"] } - + + env_vars["logLevel"] = { + "easyhaproxy": os.getenv("EASYHAPROXY_LOG_LEVEL") if os.getenv("EASYHAPROXY_LOG_LEVEL") else Functions.DEBUG, + "haproxy": os.getenv("HAPROXY_LOG_LEVEL") if os.getenv("HAPROXY_LOG_LEVEL") else Functions.INFO, + "certbot": os.getenv("CERTBOT_LOG_LEVEL") if os.getenv("CERTBOT_LOG_LEVEL") else Functions.DEBUG, + } + return env_vars diff --git a/src/templates/haproxy.cfg.j2 b/src/templates/haproxy.cfg.j2 index a1842b1..9264ade 100644 --- a/src/templates/haproxy.cfg.j2 +++ b/src/templates/haproxy.cfg.j2 @@ -1,5 +1,18 @@ +{% set log_definition = data["logLevel"] | default({}) %} +{% set log_level = log_definition["haproxy"] | default("INFO") | upper %} +{% if log_level == "TRACE" or log_level == "DEBUG" %} +{% set haproxy_log_level = "debug" %} +{% elif log_level == "INFO" %} +{% set haproxy_log_level = "info" %} +{% elif log_level == "WARN" %} +{% set haproxy_log_level = "warning" %} +{% elif log_level == "ERROR" %} +{% set haproxy_log_level = "err" %} +{% elif log_level == "FATAL" %} +{% set haproxy_log_level = "crit" %} +{% endif %} global - log stdout format raw local0 info + log stdout format raw local0 {{ haproxy_log_level }} maxconn 2000 {% if data["ssl_mode"] == "strict" %} {% include "ssl_strict.j2" %} diff --git a/src/tests/test_containerenv.py b/src/tests/test_containerenv.py index c338871..5145251 100644 --- a/src/tests/test_containerenv.py +++ b/src/tests/test_containerenv.py @@ -1,12 +1,20 @@ import pytest import os + +from functions import Functions from processor import ContainerEnv + def test_container_env_empty(): assert { "customerrors": False, "ssl_mode": "default", - "lookup_label": "easyhaproxy" + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() # os.environ['CERTBOT_LOG_LEVEL'] = 'warn' @@ -17,7 +25,12 @@ def test_container_env_customerrors(): assert { "customerrors": True, "ssl_mode": "default", - "lookup_label": "easyhaproxy" + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_CUSTOMERRORS'] = '' @@ -28,7 +41,12 @@ def test_container_env_sslmode(): assert { "customerrors": False, "ssl_mode": "strict", - "lookup_label": "easyhaproxy" + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['EASYHAPROXY_SSL_MODE'] = '' @@ -41,6 +59,11 @@ def test_container_env_stats(): "customerrors": False, "ssl_mode": "default", "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_USERNAME'] = '' @@ -58,7 +81,12 @@ def test_container_env_stats_password(): "password": "xyz", "port": "1936" - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_PASSWORD'] = '' @@ -78,7 +106,12 @@ def test_container_env_stats_password(): "password": "xyz", "port": "2101" - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_USERNAME'] = '' @@ -96,7 +129,12 @@ def test_container_env_stats_password(): "letsencrypt": { "email": "acme@example.org", "server": False - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' @@ -112,7 +150,32 @@ def test_container_env_letsencrypt(): "letsencrypt": { "email": "acme@example.org", "server": True - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: - os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' \ No newline at end of file + os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' + +def test_container_log_level(): + os.environ['CERTBOT_LOG_LEVEL'] = Functions.TRACE + os.environ['EASYHAPROXY_LOG_LEVEL'] = Functions.ERROR + os.environ['HAPROXY_LOG_LEVEL'] = Functions.FATAL + try: + assert { + "customerrors": False, + "ssl_mode": "default", + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.ERROR, + "haproxy": Functions.FATAL, + "certbot": Functions.TRACE, + }, + } == ContainerEnv.read() + finally: + os.environ['CERTBOT_LOG_LEVEL'] = '' + os.environ['EASYHAPROXY_LOG_LEVEL'] = '' + os.environ['HAPROXY_LOG_LEVEL'] = ''