Refactor E2E tests and configuration format
- Replaced `easymapping` configuration with `containers` for better maintainability and clarity. - Introduced `DockerComposeFixture` class in `utils.py` to manage Docker Compose lifecycle and smart build strategy. - Enhanced YAML-to-environment variable conversion in `ContainerEnv` for dynamic configuration support. - Updated HAProxy configurations and test fixtures to reflect the new format. - Improved test coverage for YAML parsing, environment variable handling, and HAProxy config generation.
This commit is contained in:
parent
c125985150
commit
97845b8a52
22 changed files with 1042 additions and 442 deletions
|
|
@ -45,6 +45,22 @@ backend srv_stats
|
|||
mode http
|
||||
server Local 127.0.0.1:1936
|
||||
|
||||
frontend http_in_443
|
||||
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||
mode http
|
||||
|
||||
acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br
|
||||
acl is_rule_host1_com_br_443_2 hdr(host) -i host1.com.br:443
|
||||
use_backend srv_host1_com_br_443 if is_rule_host1_com_br_443_1 OR is_rule_host1_com_br_443_2
|
||||
|
||||
backend srv_host1_com_br_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 container:5000 check weight 1
|
||||
|
||||
frontend http_in_80
|
||||
bind *:80
|
||||
mode http
|
||||
|
|
@ -75,22 +91,6 @@ backend srv_host2_com_br_80
|
|||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 other:3000 check weight 1
|
||||
|
||||
frontend http_in_443
|
||||
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||
mode http
|
||||
|
||||
acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br
|
||||
acl is_rule_host1_com_br_443_2 hdr(host) -i host1.com.br:443
|
||||
use_backend srv_host1_com_br_443 if is_rule_host1_com_br_443_1 OR is_rule_host1_com_br_443_2
|
||||
|
||||
backend srv_host1_com_br_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 container:80 check weight 1
|
||||
|
||||
frontend http_in_8080
|
||||
bind *:8080
|
||||
mode http
|
||||
|
|
|
|||
42
tests/fixtures/static.yml
vendored
42
tests/fixtures/static.yml
vendored
|
|
@ -5,27 +5,23 @@ stats:
|
|||
|
||||
customerrors: true # Optional (default false)
|
||||
|
||||
easymapping:
|
||||
- port: 80
|
||||
hosts:
|
||||
host1.com.br:
|
||||
containers:
|
||||
- container:5000
|
||||
certbot: true
|
||||
host2.com.br:
|
||||
containers:
|
||||
- other:3000
|
||||
redirect:
|
||||
www.host1.com.br: http://host1.com.br
|
||||
|
||||
- port: 443
|
||||
ssl: True
|
||||
hosts:
|
||||
host1.com.br:
|
||||
containers:
|
||||
- container:80
|
||||
certbot:
|
||||
email: test@example.com
|
||||
|
||||
- port: 8080
|
||||
hosts:
|
||||
host3.com.br:
|
||||
containers: [ "domain:8181" ]
|
||||
containers:
|
||||
"host1.com.br:80":
|
||||
ip: ["container:5000"]
|
||||
certbot: true
|
||||
|
||||
"host2.com.br:80":
|
||||
ip: ["other:3000"]
|
||||
|
||||
"www.host1.com.br:80":
|
||||
redirect: "http://host1.com.br"
|
||||
|
||||
"host1.com.br:443":
|
||||
ip: ["container:80"]
|
||||
ssl: true
|
||||
|
||||
"host3.com.br:8080":
|
||||
ip: ["domain:8181"]
|
||||
|
|
|
|||
13
tests/fixtures/static_multi_domain.yml
vendored
Normal file
13
tests/fixtures/static_multi_domain.yml
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
stats:
|
||||
username: admin
|
||||
password: test123
|
||||
port: 1936
|
||||
|
||||
customerrors: true
|
||||
|
||||
containers:
|
||||
"host1.com:80":
|
||||
ip: ["webapp:8080"]
|
||||
|
||||
"host2.com:80":
|
||||
ip: ["webapp:8080"] # Same container as host1
|
||||
|
|
@ -344,3 +344,110 @@ def test_container_log_level():
|
|||
del os.environ['CERTBOT_LOG_LEVEL']
|
||||
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||
del os.environ['HAPROXY_LOG_LEVEL']
|
||||
|
||||
|
||||
def test_yaml_to_env_loglevel():
|
||||
"""Test that YAML logLevel config is properly converted to environment variables"""
|
||||
yaml_config = {
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.ERROR,
|
||||
"haproxy": Functions.FATAL,
|
||||
"certbot": Functions.TRACE,
|
||||
}
|
||||
}
|
||||
try:
|
||||
result = ContainerEnv.read(yaml_config)
|
||||
assert result["logLevel"]["easyhaproxy"] == Functions.ERROR
|
||||
assert result["logLevel"]["haproxy"] == Functions.FATAL
|
||||
assert result["logLevel"]["certbot"] == Functions.TRACE
|
||||
# Verify environment variables were set
|
||||
assert os.environ.get('EASYHAPROXY_LOG_LEVEL') == Functions.ERROR
|
||||
assert os.environ.get('HAPROXY_LOG_LEVEL') == Functions.FATAL
|
||||
assert os.environ.get('CERTBOT_LOG_LEVEL') == Functions.TRACE
|
||||
finally:
|
||||
# Cleanup
|
||||
for key in ['EASYHAPROXY_LOG_LEVEL', 'HAPROXY_LOG_LEVEL', 'CERTBOT_LOG_LEVEL']:
|
||||
if key in os.environ:
|
||||
del os.environ[key]
|
||||
|
||||
|
||||
def test_yaml_to_env_certbot():
|
||||
"""Test that YAML certbot config is properly converted to environment variables"""
|
||||
yaml_config = {
|
||||
"certbot": {
|
||||
"email": "test@example.com",
|
||||
"autoconfig": "letsencrypt",
|
||||
"server": "https://acme-v02.api.letsencrypt.org/directory",
|
||||
"eab_kid": "test_kid",
|
||||
"eab_hmac_key": "test_hmac",
|
||||
"retry_count": 10,
|
||||
"preferred_challenges": "dns",
|
||||
"manual_auth_hook": "test_hook"
|
||||
}
|
||||
}
|
||||
try:
|
||||
result = ContainerEnv.read(yaml_config)
|
||||
assert result["certbot"]["email"] == "test@example.com"
|
||||
assert result["certbot"]["autoconfig"] == "letsencrypt"
|
||||
assert result["certbot"]["server"] == "https://acme-v02.api.letsencrypt.org/directory"
|
||||
assert result["certbot"]["eab_kid"] == "test_kid"
|
||||
assert result["certbot"]["eab_hmac_key"] == "test_hmac"
|
||||
assert result["certbot"]["retry_count"] == 10
|
||||
assert result["certbot"]["preferred_challenges"] == "dns"
|
||||
assert result["certbot"]["manual_auth_hook"] == "test_hook"
|
||||
# Verify environment variables were set
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_EMAIL') == "test@example.com"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_AUTOCONFIG') == "letsencrypt"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_SERVER') == "https://acme-v02.api.letsencrypt.org/directory"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_EAB_KID') == "test_kid"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_EAB_HMAC_KEY') == "test_hmac"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_RETRY_COUNT') == "10"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_PREFERRED_CHALLENGES') == "dns"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_MANUAL_AUTH_HOOK') == "test_hook"
|
||||
finally:
|
||||
# Cleanup
|
||||
for key in ['EASYHAPROXY_CERTBOT_EMAIL', 'EASYHAPROXY_CERTBOT_AUTOCONFIG',
|
||||
'EASYHAPROXY_CERTBOT_SERVER', 'EASYHAPROXY_CERTBOT_EAB_KID',
|
||||
'EASYHAPROXY_CERTBOT_EAB_HMAC_KEY', 'EASYHAPROXY_CERTBOT_RETRY_COUNT',
|
||||
'EASYHAPROXY_CERTBOT_PREFERRED_CHALLENGES', 'EASYHAPROXY_CERTBOT_MANUAL_AUTH_HOOK']:
|
||||
if key in os.environ:
|
||||
del os.environ[key]
|
||||
|
||||
|
||||
def test_yaml_to_env_combined():
|
||||
"""Test that combined YAML config (logLevel + certbot) works correctly"""
|
||||
yaml_config = {
|
||||
"customerrors": True,
|
||||
"ssl_mode": "strict",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.WARN,
|
||||
"haproxy": Functions.ERROR,
|
||||
},
|
||||
"certbot": {
|
||||
"email": "combined@example.com",
|
||||
"retry_count": 5
|
||||
}
|
||||
}
|
||||
try:
|
||||
result = ContainerEnv.read(yaml_config)
|
||||
# Check the result
|
||||
assert result["customerrors"] == True
|
||||
assert result["ssl_mode"] == "strict"
|
||||
assert result["logLevel"]["easyhaproxy"] == Functions.WARN
|
||||
assert result["logLevel"]["haproxy"] == Functions.ERROR
|
||||
assert result["certbot"]["email"] == "combined@example.com"
|
||||
assert result["certbot"]["retry_count"] == 5
|
||||
# Verify environment variables
|
||||
assert os.environ.get('HAPROXY_CUSTOMERRORS') == "true"
|
||||
assert os.environ.get('EASYHAPROXY_SSL_MODE') == "strict"
|
||||
assert os.environ.get('EASYHAPROXY_LOG_LEVEL') == Functions.WARN
|
||||
assert os.environ.get('HAPROXY_LOG_LEVEL') == Functions.ERROR
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_EMAIL') == "combined@example.com"
|
||||
assert os.environ.get('EASYHAPROXY_CERTBOT_RETRY_COUNT') == "5"
|
||||
finally:
|
||||
# Cleanup
|
||||
for key in ['HAPROXY_CUSTOMERRORS', 'EASYHAPROXY_SSL_MODE',
|
||||
'EASYHAPROXY_LOG_LEVEL', 'HAPROXY_LOG_LEVEL',
|
||||
'EASYHAPROXY_CERTBOT_EMAIL', 'EASYHAPROXY_CERTBOT_RETRY_COUNT']:
|
||||
if key in os.environ:
|
||||
del os.environ[key]
|
||||
|
|
|
|||
|
|
@ -233,15 +233,25 @@ def test_parser_finds_services_raw():
|
|||
def test_parser_static():
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(path + "/fixtures/static.yml") as content_file:
|
||||
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
|
||||
parsed_yaml = yaml.load(content_file.read(), Loader=yaml.FullLoader)
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(parsed)
|
||||
haproxy_config = cfg.generate()
|
||||
# Use ContainerEnv.read() to convert containers format to env vars
|
||||
from functions import ContainerEnv
|
||||
env_config = ContainerEnv.read(parsed_yaml)
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(env_config)
|
||||
|
||||
# Simulate static processor's conversion of containers to labels
|
||||
from processor import Static
|
||||
static = Static(path + "/fixtures/static.yml")
|
||||
parsed_labels = static.parsed_object
|
||||
|
||||
haproxy_config = cfg.generate(parsed_labels)
|
||||
assert len(haproxy_config) > 0
|
||||
|
||||
with open(path + "/expected/static.txt") as expected_file:
|
||||
assert expected_file.read() == haproxy_config
|
||||
assert [] == cfg.certbot_hosts
|
||||
assert ['host1.com.br'] == cfg.certbot_hosts
|
||||
|
||||
|
||||
def test_parser_static_raw():
|
||||
|
|
@ -249,6 +259,7 @@ def test_parser_static_raw():
|
|||
with open(path + "/fixtures/static.yml") as content_file:
|
||||
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
|
||||
|
||||
# Updated to new containers format
|
||||
expected = {
|
||||
"stats": {
|
||||
"username": "admin",
|
||||
|
|
@ -256,48 +267,36 @@ def test_parser_static_raw():
|
|||
"port": 1936
|
||||
},
|
||||
"customerrors": True,
|
||||
"easymapping": [
|
||||
{
|
||||
"port": 80,
|
||||
"hosts": {
|
||||
"host1.com.br": {
|
||||
"containers": [
|
||||
"container:5000"
|
||||
],
|
||||
"certbot": True
|
||||
},
|
||||
"host2.com.br": {
|
||||
"containers": [
|
||||
"other:3000"
|
||||
]
|
||||
}
|
||||
},
|
||||
"redirect": {
|
||||
"www.host1.com.br": "http://host1.com.br"
|
||||
}
|
||||
"certbot": {
|
||||
"email": "test@example.com"
|
||||
},
|
||||
"containers": {
|
||||
"host1.com.br:80": {
|
||||
"ip": [
|
||||
"container:5000"
|
||||
],
|
||||
"certbot": True
|
||||
},
|
||||
{
|
||||
"port": 443,
|
||||
"ssl": True,
|
||||
"hosts": {
|
||||
"host1.com.br": {
|
||||
"containers": [
|
||||
"container:80"
|
||||
]
|
||||
}
|
||||
}
|
||||
"host2.com.br:80": {
|
||||
"ip": [
|
||||
"other:3000"
|
||||
]
|
||||
},
|
||||
{
|
||||
"port": 8080,
|
||||
"hosts": {
|
||||
"host3.com.br": {
|
||||
"containers": [
|
||||
"domain:8181"
|
||||
]
|
||||
}
|
||||
}
|
||||
"www.host1.com.br:80": {
|
||||
"redirect": "http://host1.com.br"
|
||||
},
|
||||
"host1.com.br:443": {
|
||||
"ip": [
|
||||
"container:80"
|
||||
],
|
||||
"ssl": True
|
||||
},
|
||||
"host3.com.br:8080": {
|
||||
"ip": [
|
||||
"domain:8181"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
assert expected == parsed
|
||||
|
|
|
|||
|
|
@ -8,58 +8,46 @@ def test_processor_static():
|
|||
ProcessorInterface.static_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./fixtures/static.yml")
|
||||
static = ProcessorInterface.factory(ProcessorInterface.STATIC)
|
||||
|
||||
parsed_object = [
|
||||
{
|
||||
"hosts": {
|
||||
"host1.com.br": {
|
||||
"containers": [
|
||||
"container:5000"
|
||||
],
|
||||
"certbot": True
|
||||
},
|
||||
"host2.com.br": {
|
||||
"containers": [
|
||||
"other:3000"
|
||||
]
|
||||
}
|
||||
},
|
||||
"port": 80,
|
||||
"redirect": {
|
||||
"www.host1.com.br": "http://host1.com.br"
|
||||
}
|
||||
# New format: parsed_object is a dict mapping container IPs to their labels
|
||||
# Note: 'container' now has labels for BOTH host1.com.br:80 and host1.com.br:443
|
||||
parsed_object = {
|
||||
'container': {
|
||||
'easyhaproxy.host1_com_br_80.host': 'host1.com.br',
|
||||
'easyhaproxy.host1_com_br_80.port': '80',
|
||||
'easyhaproxy.host1_com_br_80.localport': '5000',
|
||||
'easyhaproxy.host1_com_br_80.certbot': 'true',
|
||||
'easyhaproxy.host1_com_br_443.host': 'host1.com.br',
|
||||
'easyhaproxy.host1_com_br_443.port': '443',
|
||||
'easyhaproxy.host1_com_br_443.localport': '80',
|
||||
'easyhaproxy.host1_com_br_443.ssl': 'true',
|
||||
},
|
||||
{
|
||||
"hosts": {
|
||||
"host1.com.br": {
|
||||
"containers": [
|
||||
"container:80"
|
||||
]
|
||||
}
|
||||
},
|
||||
"port": 443,
|
||||
"ssl": True
|
||||
'other': {
|
||||
'easyhaproxy.host2_com_br_80.host': 'host2.com.br',
|
||||
'easyhaproxy.host2_com_br_80.port': '80',
|
||||
'easyhaproxy.host2_com_br_80.localport': '3000',
|
||||
},
|
||||
{
|
||||
"hosts": {
|
||||
"host3.com.br": {
|
||||
"containers": [
|
||||
"domain:8181"
|
||||
]
|
||||
}
|
||||
},
|
||||
"port": 8080
|
||||
}
|
||||
]
|
||||
'redirect-www.host1.com.br-80': {
|
||||
'easyhaproxy.www_host1_com_br_80.host': 'www.host1.com.br',
|
||||
'easyhaproxy.www_host1_com_br_80.port': '80',
|
||||
'easyhaproxy.www_host1_com_br_80.redirect': '{"www.host1.com.br": "http://host1.com.br"}',
|
||||
'easyhaproxy.www_host1_com_br_80.redirect_only': 'true',
|
||||
},
|
||||
'domain': {
|
||||
'easyhaproxy.host3_com_br_8080.host': 'host3.com.br',
|
||||
'easyhaproxy.host3_com_br_8080.port': '8080',
|
||||
'easyhaproxy.host3_com_br_8080.localport': '8181',
|
||||
},
|
||||
}
|
||||
hosts = [
|
||||
'host1.com.br:443',
|
||||
'host1.com.br:80',
|
||||
'host2.com.br:80',
|
||||
'host1.com.br:443',
|
||||
'host3.com.br:8080'
|
||||
]
|
||||
|
||||
assert static.get_certbot_hosts() is None
|
||||
assert static.get_parsed_object() == parsed_object
|
||||
assert static.get_hosts() == hosts
|
||||
assert static.get_hosts() is None
|
||||
|
||||
haproxy_cfg = static.get_haproxy_conf()
|
||||
|
||||
|
|
@ -67,8 +55,39 @@ def test_processor_static():
|
|||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/static.txt"))
|
||||
|
||||
# @todo: Static doesnt populate this fields
|
||||
assert static.get_certbot_hosts() == []
|
||||
assert static.get_certbot_hosts() == ['host1.com.br']
|
||||
assert static.get_parsed_object() == parsed_object
|
||||
assert static.get_hosts() == hosts
|
||||
|
||||
|
||||
def test_processor_static_multiple_domains_same_container():
|
||||
"""Test that multiple domains can point to the same backend container"""
|
||||
ProcessorInterface.static_file = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
"./fixtures/static_multi_domain.yml"
|
||||
)
|
||||
static = ProcessorInterface.factory(ProcessorInterface.STATIC)
|
||||
|
||||
parsed_object = static.get_parsed_object()
|
||||
|
||||
# Should have labels for both host1 and host2 on the same container
|
||||
assert 'webapp' in parsed_object
|
||||
webapp_labels = parsed_object['webapp']
|
||||
|
||||
# Check both host definitions are present (this is the key test - both should exist!)
|
||||
assert 'easyhaproxy.host1_com_80.host' in webapp_labels
|
||||
assert 'easyhaproxy.host2_com_80.host' in webapp_labels
|
||||
assert webapp_labels['easyhaproxy.host1_com_80.host'] == 'host1.com'
|
||||
assert webapp_labels['easyhaproxy.host2_com_80.host'] == 'host2.com'
|
||||
|
||||
# Generate HAProxy config
|
||||
haproxy_cfg = static.get_haproxy_conf()
|
||||
|
||||
# Verify both backends are created
|
||||
assert 'backend srv_host1_com_80' in haproxy_cfg
|
||||
assert 'backend srv_host2_com_80' in haproxy_cfg
|
||||
|
||||
# Both should point to the same container
|
||||
assert haproxy_cfg.count('server srv-0 webapp:8080') == 2
|
||||
|
||||
# test_processor_static()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue