From 79819aeacb9a609273cff0db7000df4da7d61040 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Date: Wed, 9 Nov 2022 18:27:22 -0300 Subject: [PATCH 1/7] issue #32 --- docs/docker-environment.md | 29 +++++++++++++++-------------- src/easymapping/__init__.py | 2 +- src/functions/__init__.py | 9 ++++++--- src/main.py | 2 +- src/processor/__init__.py | 3 ++- src/tests/test_containerenv.py | 16 ++++++++++++++++ 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/docs/docker-environment.md b/docs/docker-environment.md index 1ed6288..fd41020 100644 --- a/docs/docker-environment.md +++ b/docs/docker-environment.md @@ -1,19 +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_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_STAGING | (Optional) If true, will try to connect to the Letsencrypt test server | false | +| 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` | diff --git a/src/easymapping/__init__.py b/src/easymapping/__init__.py index 6ad8af7..c2626a6 100644 --- a/src/easymapping/__init__.py +++ b/src/easymapping/__init__.py @@ -49,7 +49,7 @@ class HaproxyConfigGenerator: def __init__(self, mapping): self.mapping = mapping self.mapping.setdefault("ssl_mode", 'default') - self.mapping.setdefault("letsencrypt", {"email": ""}) + self.mapping.setdefault("letsencrypt", {"email": "", "staging": False}) self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower() self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy") self.letsencrypt_hosts = [] diff --git a/src/functions/__init__.py b/src/functions/__init__.py index 244b454..3438086 100644 --- a/src/functions/__init__.py +++ b/src/functions/__init__.py @@ -174,9 +174,10 @@ def sleep(self): class Certbot: - def __init__(self, certs, email): + def __init__(self, certs, email, staging): self.certs = certs self.email = email + self.staging = staging def check_certificates(self, hosts): if self.email == "" or len(hosts) == 0: @@ -201,7 +202,7 @@ def check_certificates(self, hosts): Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Renew certificate for %s" % (host)) renew_certs.append(host_arg) - certbot_certonly = ('/usr/bin/certbot certonly ' + certbot_certonly = ('/usr/bin/certbot certonly {staging}' ' --standalone' ' --preferred-challenges http' ' --http-01-port 2080' @@ -210,7 +211,9 @@ def check_certificates(self, hosts): ' --no-eff-email' ' --non-interactive' ' --max-log-backups=0' - ' %s --email %s' % (' '.join(request_certs), self.email) + ' {certs} --email {email}'.format(certs = ' '.join(request_certs), + email = self.email, + staging = '--staging' if self.staging else '') ) ret_reload = False diff --git a/src/main.py b/src/main.py index da5f413..b9017a5 100644 --- a/src/main.py +++ b/src/main.py @@ -22,7 +22,7 @@ def start(): haproxy.haproxy("start") haproxy.sleep() - certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")) + certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), os.getenv("EASYHAPROXY_LETSENCRYPT_STAGING", "false").lower() in ["true", "1", "yes"]) while True: if old_haproxy is not None: diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 3c17c6c..6fe66e8 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -27,7 +27,8 @@ def read(): env_vars["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv("EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy" if (os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")): env_vars["letsencrypt"] = { - "email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL") + "email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), + "staging": os.getenv("EASYHAPROXY_LETSENCRYPT_STAGING", "false").lower() in ["true", "1", "yes"] } return env_vars diff --git a/src/tests/test_containerenv.py b/src/tests/test_containerenv.py index 5e64b0f..afc5be0 100644 --- a/src/tests/test_containerenv.py +++ b/src/tests/test_containerenv.py @@ -95,8 +95,24 @@ def test_container_env_stats_password(): "lookup_label": "easyhaproxy", "letsencrypt": { "email": "acme@example.org", + "staging": False } } == ContainerEnv.read() finally: os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' +def test_container_env_letsencrypt(): + os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = 'acme@example.org' + os.environ['EASYHAPROXY_LETSENCRYPT_STAGING'] = 'true' + try: + assert { + "customerrors": False, + "ssl_mode": "default", + "lookup_label": "easyhaproxy", + "letsencrypt": { + "email": "acme@example.org", + "staging": True + } + } == ContainerEnv.read() + finally: + os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' \ No newline at end of file From 9535413a10aabb89e164f81ab2a3bbc1be36fe1c Mon Sep 17 00:00:00 2001 From: Joao Gilberto Date: Wed, 9 Nov 2022 23:57:12 -0300 Subject: [PATCH 2/7] Issue #33 --- examples/docker/docker-compose.yml | 2 +- examples/swarm/docker-compose.yml | 2 +- src/processor/__init__.py | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/docker/docker-compose.yml b/examples/docker/docker-compose.yml index ef60248..7587ae8 100644 --- a/examples/docker/docker-compose.yml +++ b/examples/docker/docker-compose.yml @@ -13,7 +13,7 @@ # location: https://host1.local/ # # Test SSL: -# openssl s_client -showcerts -connect 127.0.0.1:443 --servername host1.local +# openssl s_client -showcerts -connect 127.0.0.1:443 -servername host1.local version: "3" diff --git a/examples/swarm/docker-compose.yml b/examples/swarm/docker-compose.yml index d9000bf..d5fa394 100644 --- a/examples/swarm/docker-compose.yml +++ b/examples/swarm/docker-compose.yml @@ -13,7 +13,7 @@ # location: https://host1.local/ # # Test SSL: -# openssl s_client -showcerts -connect 127.0.0.1:443 --servername host1.local +# openssl s_client -showcerts -connect 127.0.0.1:443 -servername host1.local version: "3" diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 6fe66e8..90c5972 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -129,7 +129,10 @@ def __init__(self, filename = None): def inspect_network(self): self.parsed_object = {} for container in self.client.containers.list(): - self.parsed_object[container.name] = container.labels + ip_address = container.attrs["NetworkSettings"]["IPAddress"] + if not ip_address: + ip_address = list(container.attrs["NetworkSettings"]["Networks"].values())[0]["IPAddress"] + self.parsed_object[ip_address] = container.labels class Swarm(ProcessorInterface): @@ -140,7 +143,7 @@ def __init__(self, filename = None): def inspect_network(self): self.parsed_object = {} for container in self.client.services.list(): - self.parsed_object[container.attrs["Spec"]["Name"]] = container.attrs["Spec"]["Labels"] + self.parsed_object[container.attrs["Endpoint"]["VirtualIPs"][0]["Addr"].split("/")[0]] = container.attrs["Spec"]["Labels"] class Kubernetes(ProcessorInterface): From 607470a122428dc69861dfd3ca39066aac707a94 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Date: Thu, 10 Nov 2022 17:43:59 -0300 Subject: [PATCH 3/7] Issue #33 - handle networks --- docs/docker.md | 1 + docs/swarm.md | 3 ++- src/processor/__init__.py | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index c777b35..3ffdc7f 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -5,6 +5,7 @@ This method will use a docker standalone installation to discover the containers and configure the HAProxy. The only requirement is that containers and EasyHAProxy must be in the same docker network. +If not, EasyHAProxy will connect the container with the EasyHAProxy network. e.g.: diff --git a/docs/swarm.md b/docs/swarm.md index b6844a5..15f2457 100644 --- a/docs/swarm.md +++ b/docs/swarm.md @@ -3,9 +3,10 @@ ## Setup Docker EasyHAProxy This method will use a docker swarm installation to discover the containers and configure the HAProxy. -The advantage of this method is that you can discover containers in other nodes from the cluster. +The advantage of this method is that you can discover containers in other nodes from the cluster. The only requirement is that containers and EasyHAProxy must be in the same docker swarm network. +If not, EasyHAProxy will connect the service with the EasyHAProxy service network. e.g.: diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 90c5972..c58e608 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -6,6 +6,7 @@ import json import base64 import docker +import socket from kubernetes import client, config from kubernetes.client.rest import ApiException @@ -127,11 +128,17 @@ def __init__(self, filename = None): super().__init__() def inspect_network(self): + ha_proxy_network_name = next(iter(self.client.containers.get(socket.gethostname()).attrs["NetworkSettings"]["Networks"])) + ha_proxy_network = self.client.networks.get(ha_proxy_network_name) + self.parsed_object = {} for container in self.client.containers.list(): - ip_address = container.attrs["NetworkSettings"]["IPAddress"] - if not ip_address: - ip_address = list(container.attrs["NetworkSettings"]["Networks"].values())[0]["IPAddress"] + # Issue 32 - Docker container cannot connect to containers in different network. + if ha_proxy_network_name not in container.attrs["NetworkSettings"]["Networks"].keys(): + ha_proxy_network.connect(container.name) + container = self.client.containers.get(container.name) # refresh object + + ip_address = container.attrs["NetworkSettings"]["Networks"][ha_proxy_network_name]["IPAddress"] self.parsed_object[ip_address] = container.labels @@ -141,9 +148,28 @@ def __init__(self, filename = None): super().__init__() def inspect_network(self): + ha_proxy_service_name = self.client.containers.get(socket.gethostname()).name.split('.')[0] + for endpoint in self.client.services.get(ha_proxy_service_name).attrs['Endpoint']["VirtualIPs"]: + ha_proxy_network_id = endpoint["NetworkID"] + if self.client.networks.get(ha_proxy_network_id).name != 'ingress': + break + self.parsed_object = {} - for container in self.client.services.list(): - self.parsed_object[container.attrs["Endpoint"]["VirtualIPs"][0]["Addr"].split("/")[0]] = container.attrs["Spec"]["Labels"] + for service in self.client.services.list(): + ip_address = None + network_list = [] + for endpoint in service.attrs["Endpoint"]["VirtualIPs"]: + if ha_proxy_network_id == endpoint["NetworkID"]: + ip_address = endpoint["Addr"].split("/")[0] + break + network_list.append(endpoint["NetworkID"]) + + if ip_address is None: + network_list.append(ha_proxy_network_id) + service.update(networks = network_list) + continue # skip to the next service to give time to update the network + + self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"] class Kubernetes(ProcessorInterface): From f7969c2deba60408d2ea0c081f8ab10a0e5c881b Mon Sep 17 00:00:00 2001 From: Joao Gilberto Date: Thu, 10 Nov 2022 18:43:50 -0300 Subject: [PATCH 4/7] Issue #32 renaming letsencrypt test server env var --- .vscode/settings.json | 6 ++++++ docs/docker-environment.md | 2 +- src/functions/__init__.py | 17 +++++++++++++---- src/main.py | 2 +- src/processor/__init__.py | 2 +- src/tests/test_containerenv.py | 6 +++--- 6 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..74ab854 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cSpell.words": [ + "certonly", + "letsencrypt" + ] +} \ No newline at end of file diff --git a/docs/docker-environment.md b/docs/docker-environment.md index fd41020..a8f8393 100644 --- a/docs/docker-environment.md +++ b/docs/docker-environment.md @@ -5,7 +5,7 @@ | 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_STAGING | (Optional) If true, will try to connect to the Letsencrypt test server | false | +| EASYHAPROXY_LETSENCRYPT_SERVER | (Optional) Can be true or 'schema://domain.tld'. If set, will try to connect to the Letsencrypt test server | false | | 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 | diff --git a/src/functions/__init__.py b/src/functions/__init__.py index 3438086..387a870 100644 --- a/src/functions/__init__.py +++ b/src/functions/__init__.py @@ -174,10 +174,19 @@ def sleep(self): class Certbot: - def __init__(self, certs, email, staging): + def __init__(self, certs, email, test_server): self.certs = certs self.email = email - self.staging = staging + self.test_server = self.set_test_server(test_server) + + def set_test_server(self, test_server): + if not test_server: + return "" + + if test_server.lower() in ["true", "1", "yes"]: + return "--staging" + else: + return "--server " + test_server def check_certificates(self, hosts): if self.email == "" or len(hosts) == 0: @@ -202,7 +211,7 @@ def check_certificates(self, hosts): Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Renew certificate for %s" % (host)) renew_certs.append(host_arg) - certbot_certonly = ('/usr/bin/certbot certonly {staging}' + certbot_certonly = ('/usr/bin/certbot certonly {test_server}' ' --standalone' ' --preferred-challenges http' ' --http-01-port 2080' @@ -213,7 +222,7 @@ def check_certificates(self, hosts): ' --max-log-backups=0' ' {certs} --email {email}'.format(certs = ' '.join(request_certs), email = self.email, - staging = '--staging' if self.staging else '') + test_server = self.test_server) ) ret_reload = False diff --git a/src/main.py b/src/main.py index b9017a5..0631ef6 100644 --- a/src/main.py +++ b/src/main.py @@ -22,7 +22,7 @@ def start(): haproxy.haproxy("start") haproxy.sleep() - certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), os.getenv("EASYHAPROXY_LETSENCRYPT_STAGING", "false").lower() in ["true", "1", "yes"]) + certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "false").lower()) while True: if old_haproxy is not None: diff --git a/src/processor/__init__.py b/src/processor/__init__.py index c58e608..1ad8db7 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -29,7 +29,7 @@ def read(): if (os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")): env_vars["letsencrypt"] = { "email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), - "staging": os.getenv("EASYHAPROXY_LETSENCRYPT_STAGING", "false").lower() in ["true", "1", "yes"] + "server": os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "false").lower() in ["true", "1", "yes"] } return env_vars diff --git a/src/tests/test_containerenv.py b/src/tests/test_containerenv.py index afc5be0..c338871 100644 --- a/src/tests/test_containerenv.py +++ b/src/tests/test_containerenv.py @@ -95,7 +95,7 @@ def test_container_env_stats_password(): "lookup_label": "easyhaproxy", "letsencrypt": { "email": "acme@example.org", - "staging": False + "server": False } } == ContainerEnv.read() finally: @@ -103,7 +103,7 @@ def test_container_env_stats_password(): def test_container_env_letsencrypt(): os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = 'acme@example.org' - os.environ['EASYHAPROXY_LETSENCRYPT_STAGING'] = 'true' + os.environ['EASYHAPROXY_LETSENCRYPT_SERVER'] = 'true' try: assert { "customerrors": False, @@ -111,7 +111,7 @@ def test_container_env_letsencrypt(): "lookup_label": "easyhaproxy", "letsencrypt": { "email": "acme@example.org", - "staging": True + "server": True } } == ContainerEnv.read() finally: From f791897449e4ae6bfc3f9b07d4c413af3983ea01 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 8 Feb 2023 17:18:03 -0600 Subject: [PATCH 5/7] Fix errors in the test --- .vscode/launch.json | 16 +++++++ src/processor/__init__.py | 11 ++++- src/tests/test_docker.py | 92 +++++++++++++++++++++++---------------- 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9cb10c6..7dc1ae9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,22 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "PyTest Current File", + "type": "python", + "request": "launch", + "module": "pytest", + "justMyCode": true, + "console": "integratedTerminal", + "cwd": "${workspaceFolder}/src", + "args": [ + "-vv", + "${file}" + ], + "env": { + "PYTHONPATH": "${cwd}/src" + } + }, { "name": "Python: Current File", "type": "python", diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 1ad8db7..748ad1e 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -128,7 +128,14 @@ def __init__(self, filename = None): super().__init__() def inspect_network(self): - ha_proxy_network_name = next(iter(self.client.containers.get(socket.gethostname()).attrs["NetworkSettings"]["Networks"])) + try: + ha_proxy_network_name = next(iter(self.client.containers.get(socket.gethostname()).attrs["NetworkSettings"]["Networks"])) + except: + # HAProxy is not running in a container, get first container network + if len(self.client.containers.list()) == 0: + return + ha_proxy_network_name = next(iter(self.client.containers.get(self.client.containers.list()[0].name).attrs["NetworkSettings"]["Networks"])) + ha_proxy_network = self.client.networks.get(ha_proxy_network_name) self.parsed_object = {} @@ -192,6 +199,8 @@ def inspect_network(self): self.parsed_object = {} for ingress in ret.items: + if 'kubernetes.io/ingress.class' not in ingress.metadata.annotations: + continue if ingress.metadata.annotations['kubernetes.io/ingress.class'] != "easyhaproxy-ingress": continue diff --git a/src/tests/test_docker.py b/src/tests/test_docker.py index d65a08b..da0c60d 100644 --- a/src/tests/test_docker.py +++ b/src/tests/test_docker.py @@ -6,15 +6,24 @@ from processor import ProcessorInterface from processor import Docker -def _get_hydrated_object(parsed_objects, key): - assert key in parsed_objects.keys() + +def _get_hydrated_object(parsed_objects, lookup_key): hydrated_object = {} - for keys in parsed_objects[key]: - if "easyhaproxy" in keys: - hydrated_object[keys] = parsed_objects[key][keys] + for key in parsed_objects: + for keys in parsed_objects[key]: + if lookup_key in keys: + hydrated_object[keys] = parsed_objects[key][keys] return hydrated_object +def _get_ip_host(parsed_objects, lookup_key): + hydrated_object = {} + for key in parsed_objects: + for keys in parsed_objects[key]: + if lookup_key in keys: + return key + + def test_processor_docker(): try: client = docker.from_env() @@ -24,32 +33,32 @@ def test_processor_docker(): if len(client.containers.list()) > 0: pytest.skip("I cannot run this test with other containers running.") - container = client.containers.run("byjg/static-httpserver", - name="test_processor_docker", - detach=True, - auto_remove=True, - remove=True, - labels={ - "easyhaproxy.http.port": "80", - "easyhaproxy.http.localport": "8080", - "easyhaproxy.http.host": "host1.local", - - "easyhaproxy.http2.port": "90", - "easyhaproxy.http2.localport": "9000", - "easyhaproxy.http2.host": "host2.local", - "easyhaproxy.http2.letsencrypt": "true", - }) - container2 = client.containers.run("byjg/static-httpserver", - name="test2_processor_docker", - detach=True, - auto_remove=True, - remove=True, - labels={ - "easyhaproxy.ssl.port": "443", - "easyhaproxy.ssl.localport": "8080", - "easyhaproxy.ssl.host": "hostssl.local", - "easyhaproxy.ssl.sslcert": "U29tZSBQRU0gQ2VydGlmaWNhdGU=" - }) + container = client.containers.run("byjg/static-httpserver", + name="test_processor_docker", + detach=True, + auto_remove=True, + remove=True, + labels={ + "easyhaproxy.http.port": "80", + "easyhaproxy.http.localport": "8080", + "easyhaproxy.http.host": "host1.local", + + "easyhaproxy.http2.port": "90", + "easyhaproxy.http2.localport": "9000", + "easyhaproxy.http2.host": "host2.local", + "easyhaproxy.http2.letsencrypt": "true", + }) + container2 = client.containers.run("byjg/static-httpserver", + name="test2_processor_docker", + detach=True, + auto_remove=True, + remove=True, + labels={ + "easyhaproxy.ssl.port": "443", + "easyhaproxy.ssl.localport": "8080", + "easyhaproxy.ssl.host": "hostssl.local", + "easyhaproxy.ssl.sslcert": "U29tZSBQRU0gQ2VydGlmaWNhdGU=" + }) try: time.sleep(1) @@ -57,7 +66,7 @@ def test_processor_docker(): static = ProcessorInterface.factory("docker") assert static.get_letsencrypt_hosts() is None - + assert { 'easyhaproxy.http.host': 'host1.local', 'easyhaproxy.http.localport': '8080', @@ -66,27 +75,34 @@ def test_processor_docker(): 'easyhaproxy.http2.localport': '9000', 'easyhaproxy.http2.port': '90', 'easyhaproxy.http2.letsencrypt': 'true', - } == _get_hydrated_object(static.get_parsed_object(), "test_processor_docker") + } == _get_hydrated_object(static.get_parsed_object(), "easyhaproxy.http") assert { 'easyhaproxy.ssl.host': 'hostssl.local', 'easyhaproxy.ssl.localport': '8080', 'easyhaproxy.ssl.port': '443', 'easyhaproxy.ssl.sslcert': 'U29tZSBQRU0gQ2VydGlmaWNhdGU=' - } == _get_hydrated_object(static.get_parsed_object(), "test2_processor_docker") + } == _get_hydrated_object(static.get_parsed_object(), "easyhaproxy.ssl.") assert static.get_hosts() is None assert static.get_certs() == {} haproxy_cfg = static.get_haproxy_conf() - assert haproxy_cfg == Functions.load(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/docker.txt")) + assert haproxy_cfg == Functions.load(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/docker.txt")).replace("test_processor_docker", _get_ip_host( + static.get_parsed_object(), "easyhaproxy.http")).replace("test2_processor_docker", _get_ip_host(static.get_parsed_object(), "easyhaproxy.ssl")) assert static.get_letsencrypt_hosts() == ['host2.local'] - assert static.get_hosts() == ['hostssl.local:443', 'host1.local:80', 'host2.local:90'] - assert static.get_certs() == {'hostssl.local.pem': 'Some PEM Certificate'} + assert static.get_hosts() == [ + 'hostssl.local:443', + 'host1.local:80', + 'host2.local:90' + ] + assert static.get_certs() == { + 'hostssl.local.pem': 'Some PEM Certificate' + } finally: os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' container.stop() container2.stop() -#test_processor_docker() \ No newline at end of file +# test_processor_docker() From c3caa3af90f1bebb89726ed4914ca007d5fc757f Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 8 Feb 2023 20:08:53 -0600 Subject: [PATCH 6/7] Upgrade HELM package --- helm/easyhaproxy/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/easyhaproxy/Chart.yaml b/helm/easyhaproxy/Chart.yaml index 3bcc5b0..724ec79 100644 --- a/helm/easyhaproxy/Chart.yaml +++ b/helm/easyhaproxy/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.4 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "4.2.0" +appVersion: "4.3.0" From cd2a3c6b9c30a54a0a4a53a5829d779c731855a3 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 8 Feb 2023 20:29:40 -0600 Subject: [PATCH 7/7] Fix Runtime Errors --- docs/docker-environment.md | 2 +- helm/easyhaproxy/Chart.yaml | 2 +- src/functions/__init__.py | 11 ++++++----- src/main.py | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/docker-environment.md b/docs/docker-environment.md index a8f8393..0b9ec11 100644 --- a/docs/docker-environment.md +++ b/docs/docker-environment.md @@ -5,7 +5,7 @@ | 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 true or 'schema://domain.tld'. If set, will try to connect to the Letsencrypt test server | false | +| 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 | diff --git a/helm/easyhaproxy/Chart.yaml b/helm/easyhaproxy/Chart.yaml index 724ec79..3bcc5b0 100644 --- a/helm/easyhaproxy/Chart.yaml +++ b/helm/easyhaproxy/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.4 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "4.3.0" +appVersion: "4.2.0" diff --git a/src/functions/__init__.py b/src/functions/__init__.py index 387a870..d174dcf 100644 --- a/src/functions/__init__.py +++ b/src/functions/__init__.py @@ -180,13 +180,12 @@ def __init__(self, certs, email, test_server): self.test_server = self.set_test_server(test_server) def set_test_server(self, test_server): - if not test_server: - return "" - - if test_server.lower() in ["true", "1", "yes"]: + if test_server.lower() == "staging": return "--staging" - else: + elif test_server.lower().startswith("http"): return "--server " + test_server + else: + return "" def check_certificates(self, hosts): if self.email == "" or len(hosts) == 0: @@ -247,6 +246,8 @@ def merge_certificate(self, cert, key, filename): def find_live_certificates(self): letsencrypt_certs = "/etc/letsencrypt/live/" + if not os.path.exists(letsencrypt_certs): + return for item in os.listdir(letsencrypt_certs): path = os.path.join(letsencrypt_certs, item) if os.path.isdir(path): diff --git a/src/main.py b/src/main.py index 0631ef6..34e29d8 100644 --- a/src/main.py +++ b/src/main.py @@ -22,7 +22,7 @@ def start(): haproxy.haproxy("start") haproxy.sleep() - certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "false").lower()) + certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "").lower()) while True: if old_haproxy is not None: @@ -70,4 +70,4 @@ def main(): start() if __name__ == '__main__': - main() \ No newline at end of file + main()