1
0
Fork 0

Added more unit tests

This commit is contained in:
Joao Gilberto Magalhaes 2022-08-25 19:37:02 -05:00
parent 733e73ae58
commit f007f857df
6 changed files with 325 additions and 5 deletions

View file

@ -56,10 +56,10 @@ class HaproxyConfigGenerator:
self.serving_hosts = [] self.serving_hosts = []
self.certs = {} self.certs = {}
def generate(self, container_metadata = None): def generate(self, container_metadata = {}):
self.mapping.setdefault("easymapping", []) self.mapping.setdefault("easymapping", [])
if container_metadata is not None: if container_metadata != {}:
self.mapping["easymapping"] = self.parse(container_metadata) self.mapping["easymapping"] = self.parse(container_metadata)
file_loader = FileSystemLoader('templates') file_loader = FileSystemLoader('templates')

View file

@ -13,7 +13,7 @@ class ContainerEnv:
def read(): def read():
env_vars = { env_vars = {
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False, "customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default") "ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE") if os.getenv("EASYHAPROXY_SSL_MODE") else 'default'
} }
if os.getenv("HAPROXY_PASSWORD"): if os.getenv("HAPROXY_PASSWORD"):
@ -33,6 +33,8 @@ class ContainerEnv:
class ProcessorInterface: class ProcessorInterface:
static_file = "/etc/haproxy/easyconfig.yml"
def __init__(self, filename = None): def __init__(self, filename = None):
self.filename = filename self.filename = filename
self.refresh() self.refresh()
@ -40,7 +42,7 @@ class ProcessorInterface:
@staticmethod @staticmethod
def factory(mode): def factory(mode):
if mode == "static": if mode == "static":
return Static("/etc/haproxy/easyconfig.yml") return Static(ProcessorInterface.static_file)
elif mode == "docker": elif mode == "docker":
return Docker() return Docker()
elif mode == "swarm": elif mode == "swarm":
@ -95,7 +97,6 @@ class ProcessorInterface:
Functions.save("{0}/{1}".format(path, cert), self.get_certs(cert)) Functions.save("{0}/{1}".format(path, cert), self.get_certs(cert))
class Static(ProcessorInterface): class Static(ProcessorInterface):
def inspect_network(self): def inspect_network(self):
self.parsed_object = {} self.parsed_object = {}

View file

@ -0,0 +1,101 @@
global
log stdout format raw local0 info
maxconn 2000
tune.ssl.default-dh-param 2048
# intermediate configuration
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-dh-param-file /etc/haproxy/dhparam
defaults
log global
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
frontend stats
bind *:1936
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
default_backend srv_stats
backend srv_stats
mode http
server Local 127.0.0.1:1936
frontend http_in_443
bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
mode http
acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local
acl is_rule_hostssl_local_443_2 hdr(host) -i hostssl.local:443
use_backend srv_hostssl_local_443 if is_rule_hostssl_local_443_1 OR is_rule_hostssl_local_443_2
acl is_rule_host2_local_443_1 hdr(host) -i host2.local
acl is_rule_host2_local_443_2 hdr(host) -i host2.local:443
use_backend srv_host2_local_443 if is_rule_host2_local_443_1 OR is_rule_host2_local_443_2
backend srv_hostssl_local_443
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
server srv-0 test2_processor_docker:8080 check weight 1
backend srv_host2_local_443
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
server srv-0 test_processor_docker:9000 check weight 1
frontend http_in_80
bind *:80
mode http
acl is_rule_host1_local_80_1 hdr(host) -i host1.local
acl is_rule_host1_local_80_2 hdr(host) -i host1.local:80
use_backend srv_host1_local_80 if is_rule_host1_local_80_1 OR is_rule_host1_local_80_2
backend srv_host1_local_80
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
server srv-0 test_processor_docker:8080 check weight 1
frontend http_in_90
bind *:90
mode http
acl is_rule_host2_local_90_1 hdr(host) -i host2.local
acl is_rule_host2_local_90_2 hdr(host) -i host2.local:90
acl is_letsencrypt_host2_local_90 path_beg /.well-known/acme-challenge/
use_backend letsencrypt_backend if is_letsencrypt_host2_local_90 is_rule_host2_local_90_1 OR is_letsencrypt_host2_local_90 is_rule_host2_local_90_2
use_backend srv_host2_local_90 if is_rule_host2_local_90_1 OR is_rule_host2_local_90_2
backend srv_host2_local_90
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
server srv-0 test_processor_docker:9000 check weight 1
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,102 @@
import pytest
import os
from processor import ContainerEnv
def test_container_env_empty():
assert {
"customerrors": False,
"ssl_mode": "default",
"lookup_label": "easyhaproxy"
} == ContainerEnv.read()
# os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
def test_container_env_customerrors():
os.environ['HAPROXY_CUSTOMERRORS'] = 'true'
try:
assert {
"customerrors": True,
"ssl_mode": "default",
"lookup_label": "easyhaproxy"
} == ContainerEnv.read()
finally:
os.environ['HAPROXY_CUSTOMERRORS'] = ''
def test_container_env_sslmode():
os.environ['EASYHAPROXY_SSL_MODE'] = 'strict'
try:
assert {
"customerrors": False,
"ssl_mode": "strict",
"lookup_label": "easyhaproxy"
} == ContainerEnv.read()
finally:
os.environ['EASYHAPROXY_SSL_MODE'] = ''
def test_container_env_stats():
os.environ['HAPROXY_USERNAME'] = 'abc'
os.environ['HAPROXY_STATS_PORT'] = '2101'
try:
assert {
"customerrors": False,
"ssl_mode": "default",
"lookup_label": "easyhaproxy",
} == ContainerEnv.read()
finally:
os.environ['HAPROXY_USERNAME'] = ''
os.environ['HAPROXY_STATS_PORT'] = ''
def test_container_env_stats_password():
os.environ['HAPROXY_PASSWORD'] = 'xyz'
try:
assert {
"customerrors": False,
"ssl_mode": "default",
"lookup_label": "easyhaproxy",
"stats": {
"username": "admin",
"password": "xyz",
"port": "1936"
}
} == ContainerEnv.read()
finally:
os.environ['HAPROXY_PASSWORD'] = ''
def test_container_env_stats_password():
os.environ['HAPROXY_USERNAME'] = 'abc'
os.environ['HAPROXY_STATS_PORT'] = '2101'
os.environ['HAPROXY_PASSWORD'] = 'xyz'
try:
assert {
"customerrors": False,
"ssl_mode": "default",
"lookup_label": "easyhaproxy",
"stats": {
"username": "abc",
"password": "xyz",
"port": "2101"
}
} == ContainerEnv.read()
finally:
os.environ['HAPROXY_USERNAME'] = ''
os.environ['HAPROXY_STATS_PORT'] = ''
os.environ['HAPROXY_PASSWORD'] = ''
def test_container_env_stats_password():
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = 'acme@example.org'
try:
assert {
"customerrors": False,
"ssl_mode": "default",
"lookup_label": "easyhaproxy",
"letsencrypt": {
"email": "acme@example.org",
}
} == ContainerEnv.read()
finally:
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = ''

92
src/tests/test_docker.py Normal file
View file

@ -0,0 +1,92 @@
import pytest
import os
import time
import docker
from functions import Functions
from processor import ProcessorInterface
from processor import Docker
def _get_hydrated_object(parsed_objects, key):
assert key in parsed_objects.keys()
hydrated_object = {}
for keys in parsed_objects[key]:
if "easyhaproxy" in keys:
hydrated_object[keys] = parsed_objects[key][keys]
return hydrated_object
def test_processor_docker():
try:
client = docker.from_env()
except docker.errors.DockerException:
pytest.skip("There is no docker environment")
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="
})
try:
time.sleep(1)
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = 'docker@example.org'
static = ProcessorInterface.factory("docker")
assert static.get_letsencrypt_hosts() is None
assert {
'easyhaproxy.http.host': 'host1.local',
'easyhaproxy.http.localport': '8080',
'easyhaproxy.http.port': '80',
'easyhaproxy.http2.host': 'host2.local',
'easyhaproxy.http2.localport': '9000',
'easyhaproxy.http2.port': '90',
'easyhaproxy.http2.letsencrypt': 'true',
} == _get_hydrated_object(static.get_parsed_object(), "test_processor_docker")
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")
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 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'}
finally:
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = ''
container.stop()
container2.stop()
#test_processor_docker()

24
src/tests/test_static.py Normal file
View file

@ -0,0 +1,24 @@
import pytest
import os
from functions import Functions
from processor import ProcessorInterface
from processor import Static
def test_processor_static():
ProcessorInterface.static_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./fixtures/static.yml")
static = ProcessorInterface.factory("static")
assert static.get_letsencrypt_hosts() is None
assert static.get_parsed_object() == {}
assert static.get_hosts() is None
haproxy_cfg = static.get_haproxy_conf()
assert haproxy_cfg == Functions.load(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/static.txt"))
# @todo: Static doesnt populate this fields
assert static.get_letsencrypt_hosts() == []
assert static.get_parsed_object() == {}
assert static.get_hosts() == []
# test_processor_static()