Merge pull request #44 from byjg/issue/43
Issue #43 - Load Balance + Custom HAProxy config
This commit is contained in:
commit
ab0452388b
33 changed files with 503 additions and 150 deletions
10
README.md
10
README.md
|
|
@ -19,6 +19,14 @@ EasyHAProxy can detect and configure HAProxy automatically on the following plat
|
||||||
- Docker Swarm
|
- Docker Swarm
|
||||||
- Kubernetes
|
- Kubernetes
|
||||||
|
|
||||||
|
## Who is using?
|
||||||
|
|
||||||
|
EasyHAProxy is part of some projects:
|
||||||
|
- Dokku
|
||||||
|
- MicroK8s
|
||||||
|
|
||||||
|
See detailed instructions on how to install below.
|
||||||
|
|
||||||
## EasyHAProxy Mission
|
## EasyHAProxy Mission
|
||||||
|
|
||||||
Easy to set up and low configuration to numerous features.
|
Easy to set up and low configuration to numerous features.
|
||||||
|
|
@ -38,7 +46,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.
|
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.
|
You don't need to change your current infrastructure and don't need to learn the HAProxy configuration.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:3.16
|
FROM alpine:3.18
|
||||||
|
|
||||||
ARG RELEASE_VERSION_ARG
|
ARG RELEASE_VERSION_ARG
|
||||||
|
|
||||||
|
|
@ -11,7 +11,6 @@ COPY src/ /scripts/
|
||||||
COPY build/assets /
|
COPY build/assets /
|
||||||
|
|
||||||
RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml certbot openssl \
|
RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml certbot openssl \
|
||||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
|
||||||
&& pip3 install --upgrade pip \
|
&& pip3 install --upgrade pip \
|
||||||
&& pip install -r requirements.txt \
|
&& pip install -r requirements.txt \
|
||||||
&& pytest -s -vv tests/ \
|
&& pytest -s -vv tests/ \
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@
|
||||||
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
||||||
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key |
|
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key |
|
||||||
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
||||||
| easyhaproxy.[definition].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
||||||
| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false |
|
| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false |
|
||||||
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
||||||
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use* this with `ssl` or `certbot` parameters | false | true OR false |
|
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use* this with `ssl` or `certbot` parameters | false | true OR false |
|
||||||
|
| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based |
|
||||||
|
|
||||||
The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations.
|
The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,5 @@
|
||||||
| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` |
|
| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,12 @@
|
||||||
|
|
||||||
This method will use a docker standalone installation to discover the containers and configure the HAProxy.
|
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.
|
You cannot mix docker containers with swarm containers.
|
||||||
If not, EasyHAProxy will connect the container with the EasyHAProxy network.
|
|
||||||
|
The only request is that containers and EasyHAProxy must be in the same docker network.
|
||||||
|
If you don't add to your services the same network EasyHAProxy is connected to, EasyHAProxy will attach it network to your container.
|
||||||
|
|
||||||
|
Also, it is highly recommended you create a network external to EasyHAProxy.
|
||||||
|
|
||||||
e.g.:
|
e.g.:
|
||||||
|
|
||||||
|
|
@ -47,13 +51,12 @@ Once the container is running, EasyHAProxy will detect automatically and start t
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
You don't need to expose any port in your container.
|
||||||
|
|
||||||
Please follow the [docker label configuration](container-labels.md) to see other configurations available.
|
Please follow the [docker label configuration](container-labels.md) to see other configurations available.
|
||||||
|
|
||||||
## Setup the EasyHAProxy container
|
## Setup the EasyHAProxy container
|
||||||
|
|
||||||
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [docker container environment](docker-environment.md)
|
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [docker container environment](docker-environment.md)
|
||||||
|
|
||||||
|
|
||||||
## Setup certificates with ACME (e.g. Letsencrypt)
|
## Setup certificates with ACME (e.g. Letsencrypt)
|
||||||
|
|
||||||
Follow [this link](acme.md)
|
Follow [this link](acme.md)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ Once the container is running, EasyHAProxy will detect automatically and start t
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
You don't need to expose any port in your container.
|
||||||
|
|
||||||
Caveats:
|
Notes:
|
||||||
|
|
||||||
- At this point, the implementation doesn't support all ingress properties or wildcard domains.
|
- At this point, the implementation doesn't support all ingress properties or wildcard domains.
|
||||||
- The ingress will publish the ports 80 and 443, plus 1936 if stats are enabled.
|
- The ingress will publish the ports 80 and 443, plus 1936 if stats are enabled.
|
||||||
|
|
@ -79,14 +79,14 @@ Caveats:
|
||||||
|
|
||||||
## Kubernetes annotations
|
## Kubernetes annotations
|
||||||
|
|
||||||
| annotation | Description | Default | Example |
|
| annotation | Description | Default | Example |
|
||||||
|-----------------------------|-------------------------------------------------------------------------------------|--------------|---------------------------|
|
|----------------------------------|-------------------------------------------------------------------------------------|--------------|---------------------------------------|
|
||||||
| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress |
|
| 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.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false |
|
||||||
| easyhaproxy.certbot | (optional) Boolean. It will request certbot certificates for the ingresses domains. | false | true or false |
|
| easyhaproxy.certbot | (optional) Boolean. It will request certbot 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.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.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 |
|
| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp |
|
||||||
|
|
||||||
**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration.
|
**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ customerrors: true # Optional (default false)
|
||||||
|
|
||||||
ssl_mode: default
|
ssl_mode: default
|
||||||
|
|
||||||
|
logLevel:
|
||||||
|
haproxy: INFO
|
||||||
|
|
||||||
certbot: {
|
certbot: {
|
||||||
"email": "acme@example.org"
|
"email": "acme@example.org"
|
||||||
}
|
}
|
||||||
|
|
@ -83,8 +86,13 @@ customerrors: true # Optional (default false)
|
||||||
|
|
||||||
ssl_mode: default # Optional
|
ssl_mode: default # Optional
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
certbot: { # Optional. If you enable `certbot` will need to setu0p this,
|
certbot: { # Optional. If you enable `certbot` will need to setu0p this,
|
||||||
# otherwise the certificate will be issued
|
# otherwise the certificate will be issued
|
||||||
"email": "acme@example.org"
|
"email": "acme@example.org"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,17 @@
|
||||||
This method will use a docker swarm installation to discover the containers and configure the HAProxy.
|
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.
|
You cannot mix docker containers with swarm containers.
|
||||||
If not, EasyHAProxy will connect the service with the EasyHAProxy service network.
|
|
||||||
|
The only request is that containers and EasyHAProxy must be in the same docker swarm network.
|
||||||
|
If you don't add to your services the same network EasyHAProxy is connected to, EasyHAProxy will attach it network to your container.
|
||||||
|
|
||||||
|
Also, it is highly recommended you create a network external to EasyHAProxy.
|
||||||
|
|
||||||
e.g.:
|
e.g.:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker network create -d overlay easyhaproxy
|
docker network create -d overlay --attachable easyhaproxy
|
||||||
```
|
```
|
||||||
|
|
||||||
And then deploy the EasyHAProxy stack:
|
And then deploy the EasyHAProxy stack:
|
||||||
|
|
@ -79,13 +83,11 @@ networks:
|
||||||
external: true
|
external: true
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The services to be discovered **don't need** to be in the same network as EasyHAProxy is.
|
|
||||||
|
|
||||||
Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container.
|
Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container.
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
You don't need to expose any port in your container.
|
||||||
|
|
||||||
Please follow the [docker label configuration](container-labels.md) to see other configurations available.
|
Please follow the [docker label configuration](container-labels.md) to see other configurations available.
|
||||||
|
|
||||||
## Setup the EasyHAProxy container
|
## Setup the EasyHAProxy container
|
||||||
|
|
||||||
|
|
|
||||||
14
docs/volumes.md
Normal file
14
docs/volumes.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Volumes
|
||||||
|
|
||||||
|
You can map the following volumes:
|
||||||
|
|
||||||
|
| Volume | Description |
|
||||||
|
|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| /etc/haproxy/static/ | The folder that will contain the [config.yml](static.md) file for static configuration |
|
||||||
|
| /certs/haproxy/ | The folder that will contain the certificates (`PEM`) for the [SSL](ssl.md) |
|
||||||
|
| /certs/certbot/ | The folder that will contain the certificates (`PEM`) processed by Certbot (e.g. Let's Encrypt). More info: [acme](acme.md). |
|
||||||
|
| /etc/haproxy/conf.d/ | The folder that will contain the [custom configuration](other.md) files. |
|
||||||
|
| /etc/haproxy/errors-custom/ | The folder that will contain the [custom error](other.md) html files. |
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
82
examples/swarm/certs/host1.local.pem
Normal file
82
examples/swarm/certs/host1.local.pem
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
aA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
YkU14WzQKleFkiBJI9lVvnfgGnOlgg0=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
40
examples/swarm/easyhaproxy.yml
Normal file
40
examples/swarm/easyhaproxy.yml
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# To Install
|
||||||
|
# docker network create --driver overlay --attachable easyhaproxy
|
||||||
|
# docker stack deploy -c easyhaproxy.yml easyhaproxy
|
||||||
|
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
haproxy:
|
||||||
|
image: byjg/easy-haproxy:4.3.1-rc2
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./certs:/certs/haproxy
|
||||||
|
- certs_certbot:/certs/certbot
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
environment:
|
||||||
|
EASYHAPROXY_DISCOVER: swarm
|
||||||
|
EASYHAPROXY_SSL_MODE: "loose"
|
||||||
|
EASYHAPROXY_LETSENCRYPT_EMAIL: changeme@example.org
|
||||||
|
HAPROXY_CUSTOMERRORS: "true"
|
||||||
|
HAPROXY_USERNAME: admin
|
||||||
|
HAPROXY_PASSWORD: password
|
||||||
|
HAPROXY_STATS_PORT: 1936
|
||||||
|
ports:
|
||||||
|
- "80:80/tcp"
|
||||||
|
- "443:443/tcp"
|
||||||
|
- "1936:1936/tcp"
|
||||||
|
networks:
|
||||||
|
- easyhaproxy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
easyhaproxy:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
certs_certbot:
|
||||||
|
# external: true
|
||||||
|
# certs_haproxy:
|
||||||
|
# external: true
|
||||||
28
examples/swarm/portainer.yml
Normal file
28
examples/swarm/portainer.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# To install:
|
||||||
|
# docker stack deploy -c portainer.yml portainer
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
portainer:
|
||||||
|
image: portainer/portainer-ce:latest
|
||||||
|
volumes:
|
||||||
|
- portainer_data:/data portainer
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
labels:
|
||||||
|
# easyhaproxy.http.redirect_ssl: true
|
||||||
|
# easyhaproxy.http.certbot: true
|
||||||
|
easyhaproxy.http.host: portainer.local
|
||||||
|
easyhaproxy.http.port: 80
|
||||||
|
easyhaproxy.http.localport: 9000
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
certs_certbot:
|
||||||
|
external: true
|
||||||
|
# certs_haproxy:
|
||||||
|
# external: true
|
||||||
|
portainer_data:
|
||||||
|
# external: true
|
||||||
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
# To install:
|
||||||
|
# docker stack deploy -c services.yml services
|
||||||
|
#
|
||||||
# To test:
|
# To test:
|
||||||
# curl -k -H "Host: host1.local" https://127.0.0.1/
|
# curl -k -H "Host: host1.local" https://127.0.0.1/
|
||||||
# curl -k -H "Host: host2.local" https://127.0.0.1/
|
# curl -k -H "Host: host2.local" https://127.0.0.1/
|
||||||
|
|
@ -18,25 +21,6 @@
|
||||||
version: "3"
|
version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
haproxy:
|
|
||||||
image: byjg/easy-haproxy
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
- ./host2.local.pem:/certs/haproxy/host2.local.pem
|
|
||||||
deploy:
|
|
||||||
replicas: 1
|
|
||||||
environment:
|
|
||||||
EASYHAPROXY_DISCOVER: swarm
|
|
||||||
EASYHAPROXY_SSL_MODE: "loose"
|
|
||||||
HAPROXY_CUSTOMERRORS: "true"
|
|
||||||
HAPROXY_USERNAME: admin
|
|
||||||
HAPROXY_PASSWORD: password
|
|
||||||
HAPROXY_STATS_PORT: 1936
|
|
||||||
ports:
|
|
||||||
- "80:80/tcp"
|
|
||||||
- "443:443/tcp"
|
|
||||||
- "1936:1936/tcp"
|
|
||||||
|
|
||||||
container:
|
container:
|
||||||
image: byjg/static-httpserver
|
image: byjg/static-httpserver
|
||||||
deploy:
|
deploy:
|
||||||
|
|
@ -114,7 +114,7 @@ class HaproxyConfigGenerator:
|
||||||
if port not in easymapping:
|
if port not in easymapping:
|
||||||
easymapping[port] = {
|
easymapping[port] = {
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
"health-check": "",
|
"ssl-check": "",
|
||||||
"port": port,
|
"port": port,
|
||||||
"hosts": dict(),
|
"hosts": dict(),
|
||||||
"redirect": dict(),
|
"redirect": dict(),
|
||||||
|
|
@ -126,8 +126,8 @@ class HaproxyConfigGenerator:
|
||||||
"80"
|
"80"
|
||||||
)
|
)
|
||||||
|
|
||||||
easymapping[port]["health-check"] = self.label.get(
|
easymapping[port]["ssl-check"] = self.label.get(
|
||||||
self.label.create([definition, "health-check"]),
|
self.label.create([definition, "ssl-check"]),
|
||||||
""
|
""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -142,6 +142,10 @@ class HaproxyConfigGenerator:
|
||||||
easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool(
|
easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool(
|
||||||
self.label.create([definition, "redirect_ssl"])
|
self.label.create([definition, "redirect_ssl"])
|
||||||
)
|
)
|
||||||
|
easymapping[port]["hosts"][hostname]["balance"] = self.label.get(
|
||||||
|
self.label.create([definition, "balance"]),
|
||||||
|
"roundrobin"
|
||||||
|
)
|
||||||
|
|
||||||
easymapping[port]["redirect"] = self.label.get_json(
|
easymapping[port]["redirect"] = self.label.get_json(
|
||||||
self.label.create([definition, "redirect"])
|
self.label.create([definition, "redirect"])
|
||||||
|
|
@ -151,7 +155,7 @@ class HaproxyConfigGenerator:
|
||||||
if "443" not in easymapping:
|
if "443" not in easymapping:
|
||||||
easymapping["443"] = {
|
easymapping["443"] = {
|
||||||
"mode": "http",
|
"mode": "http",
|
||||||
"health-check": "ssl",
|
"ssl-check": "ssl",
|
||||||
"port": "443",
|
"port": "443",
|
||||||
"hosts": dict(),
|
"hosts": dict(),
|
||||||
"redirect": dict(),
|
"redirect": dict(),
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,13 @@ class ContainerEnv:
|
||||||
env_vars["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv(
|
env_vars["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv(
|
||||||
"EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
|
"EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
env_vars["certbot"] = {
|
env_vars["certbot"] = {
|
||||||
"autoconfig": os.getenv("EASYHAPROXY_CERTBOT_AUTOCONFIG", ""),
|
"autoconfig": os.getenv("EASYHAPROXY_CERTBOT_AUTOCONFIG", ""),
|
||||||
"email": os.getenv("EASYHAPROXY_CERTBOT_EMAIL", ""),
|
"email": os.getenv("EASYHAPROXY_CERTBOT_EMAIL", ""),
|
||||||
|
|
@ -72,7 +79,7 @@ class ContainerEnv:
|
||||||
env_vars["certbot"]["eab_kid"] = os.environ['EASYHAPROXY_CERTBOT_EAB_KID'] = resp["eab_kid"]
|
env_vars["certbot"]["eab_kid"] = os.environ['EASYHAPROXY_CERTBOT_EAB_KID'] = resp["eab_kid"]
|
||||||
env_vars["certbot"]["eab_hmac_key"] = os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] = resp["eab_hmac_key"]
|
env_vars["certbot"]["eab_hmac_key"] = os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] = resp["eab_hmac_key"]
|
||||||
else:
|
else:
|
||||||
os.environ["EASYHAPROXY_CERTBOT_EMAIL"] = ""
|
del os.environ["EASYHAPROXY_CERTBOT_EMAIL"]
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "Could not obtain ZeroSSL credentials " + resp["error"]["type"])
|
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "Could not obtain ZeroSSL credentials " + resp["error"]["type"])
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_CERTBOT_SERVER'] = env_vars["certbot"]["server"]
|
os.environ['EASYHAPROXY_CERTBOT_SERVER'] = env_vars["certbot"]["server"]
|
||||||
|
|
@ -174,26 +181,20 @@ class Functions:
|
||||||
class Consts:
|
class Consts:
|
||||||
easyhaproxy_config = "/etc/haproxy/static/config.yml"
|
easyhaproxy_config = "/etc/haproxy/static/config.yml"
|
||||||
haproxy_config = "/etc/haproxy/haproxy.cfg"
|
haproxy_config = "/etc/haproxy/haproxy.cfg"
|
||||||
|
custom_config_folder = "/etc/haproxy/conf.d"
|
||||||
certs_certbot = "/certs/certbot"
|
certs_certbot = "/certs/certbot"
|
||||||
certs_haproxy = "/certs/haproxy"
|
certs_haproxy = "/certs/haproxy"
|
||||||
|
|
||||||
|
|
||||||
class DaemonizeHAProxy:
|
class DaemonizeHAProxy:
|
||||||
def __init__(self):
|
def __init__(self, custom_config_folder = None):
|
||||||
self.process = None
|
self.process = None
|
||||||
self.thread = None
|
self.thread = None
|
||||||
self.sleep_secs = None
|
self.sleep_secs = None
|
||||||
|
self.custom_config_folder = custom_config_folder if custom_config_folder is not None else Consts.custom_config_folder
|
||||||
|
|
||||||
def haproxy(self, action):
|
def haproxy(self, action):
|
||||||
if action == "start":
|
self.__prepare(self.get_haproxy_command(action))
|
||||||
self.__prepare(
|
|
||||||
"/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock")
|
|
||||||
else:
|
|
||||||
return_code, pid_tmp = Functions().run_bash(Functions.HAPROXY_LOG, "cat /run/haproxy.pid", log_output=False)
|
|
||||||
pid = "".join(pid_tmp)
|
|
||||||
self.__prepare(
|
|
||||||
"/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf %s" %
|
|
||||||
pid)
|
|
||||||
|
|
||||||
if self.process is None:
|
if self.process is None:
|
||||||
return
|
return
|
||||||
|
|
@ -201,6 +202,18 @@ class DaemonizeHAProxy:
|
||||||
self.thread = Process(target=self.__start, args=())
|
self.thread = Process(target=self.__start, args=())
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
|
def get_haproxy_command(self, action, pid_file="/run/haproxy.pid"):
|
||||||
|
custom_config_files = ""
|
||||||
|
if len(list(self.get_custom_config_files().keys())) != 0:
|
||||||
|
custom_config_files = "-f %s" % (self.custom_config_folder)
|
||||||
|
|
||||||
|
if action == "start":
|
||||||
|
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -S /var/run/haproxy.sock" % (custom_config_files, pid_file)
|
||||||
|
else:
|
||||||
|
return_code, output = Functions().run_bash(Functions.HAPROXY_LOG, "cat %s" % pid_file, log_output=False)
|
||||||
|
pid = "".join(output)
|
||||||
|
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -x /var/run/haproxy.sock -sf %s" % (custom_config_files, pid_file, pid)
|
||||||
|
|
||||||
def __prepare(self, command):
|
def __prepare(self, command):
|
||||||
source = Functions.HAPROXY_LOG
|
source = Functions.HAPROXY_LOG
|
||||||
if not isinstance(command, (list, tuple)):
|
if not isinstance(command, (list, tuple)):
|
||||||
|
|
@ -250,6 +263,16 @@ class DaemonizeHAProxy:
|
||||||
|
|
||||||
time.sleep(self.sleep_secs)
|
time.sleep(self.sleep_secs)
|
||||||
|
|
||||||
|
def get_custom_config_files(self):
|
||||||
|
if not os.path.exists(self.custom_config_folder):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
files = {}
|
||||||
|
for file in os.listdir(self.custom_config_folder):
|
||||||
|
if file.endswith(".cfg"):
|
||||||
|
files[os.path.join(self.custom_config_folder, file)] = os.path.getmtime(os.path.join(self.custom_config_folder, file))
|
||||||
|
return dict(sorted(files.items(), key=lambda t: t[0]))
|
||||||
|
|
||||||
|
|
||||||
class Certbot:
|
class Certbot:
|
||||||
def __init__(self, certs):
|
def __init__(self, certs):
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ def start():
|
||||||
|
|
||||||
old_haproxy = None
|
old_haproxy = None
|
||||||
haproxy = DaemonizeHAProxy()
|
haproxy = DaemonizeHAProxy()
|
||||||
|
current_custom_config_files = haproxy.get_custom_config_files()
|
||||||
haproxy.haproxy("start")
|
haproxy.haproxy("start")
|
||||||
haproxy.sleep()
|
haproxy.sleep()
|
||||||
|
|
||||||
|
|
@ -35,8 +36,7 @@ def start():
|
||||||
try:
|
try:
|
||||||
old_parsed = processor_obj.get_parsed_object()
|
old_parsed = processor_obj.get_parsed_object()
|
||||||
processor_obj.refresh()
|
processor_obj.refresh()
|
||||||
if certbot.check_certificates(certbot_certs_found) or DeepDiff(old_parsed,
|
if certbot.check_certificates(certbot_certs_found) or DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive() or DeepDiff(current_custom_config_files, haproxy.get_custom_config_files()) != {}:
|
||||||
processor_obj.get_parsed_object()) != {} or not haproxy.is_alive():
|
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'New configuration found. Reloading...')
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'New configuration found. Reloading...')
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE,
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE,
|
||||||
'Object Found: %s' % (processor_obj.get_parsed_object()))
|
'Object Found: %s' % (processor_obj.get_parsed_object()))
|
||||||
|
|
@ -47,6 +47,7 @@ def start():
|
||||||
'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
||||||
old_haproxy = haproxy
|
old_haproxy = haproxy
|
||||||
haproxy = DaemonizeHAProxy()
|
haproxy = DaemonizeHAProxy()
|
||||||
|
current_custom_config_files = haproxy.get_custom_config_files()
|
||||||
haproxy.haproxy("reload")
|
haproxy.haproxy("reload")
|
||||||
old_haproxy.terminate()
|
old_haproxy.terminate()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ class ProcessorInterface:
|
||||||
self.certbot_hosts = None
|
self.certbot_hosts = None
|
||||||
self.hosts = None
|
self.hosts = None
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
self.label = ContainerEnv.read()['lookup_label']
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -151,28 +152,38 @@ class Swarm(ProcessorInterface):
|
||||||
def inspect_network(self):
|
def inspect_network(self):
|
||||||
ha_proxy_service_name = self.client.containers.get(socket.gethostname()).name.split('.')[0]
|
ha_proxy_service_name = self.client.containers.get(socket.gethostname()).name.split('.')[0]
|
||||||
ha_proxy_network_id = None
|
ha_proxy_network_id = None
|
||||||
|
swarm_ingress_id = None
|
||||||
|
|
||||||
|
# Get the HAProxy network and the ingress network
|
||||||
for endpoint in self.client.services.get(ha_proxy_service_name).attrs['Endpoint']["VirtualIPs"]:
|
for endpoint in self.client.services.get(ha_proxy_service_name).attrs['Endpoint']["VirtualIPs"]:
|
||||||
ha_proxy_network_id = endpoint["NetworkID"]
|
network_name = self.client.networks.get(endpoint["NetworkID"]).name
|
||||||
if self.client.networks.get(ha_proxy_network_id).name != 'ingress':
|
if swarm_ingress_id is None and network_name == 'ingress':
|
||||||
|
swarm_ingress_id = endpoint["NetworkID"]
|
||||||
|
if ha_proxy_network_id is None and network_name != 'ingress':
|
||||||
|
ha_proxy_network_id = endpoint["NetworkID"]
|
||||||
|
if ha_proxy_network_id is not None and swarm_ingress_id is not None:
|
||||||
break
|
break
|
||||||
|
|
||||||
if ha_proxy_network_id is None:
|
# Check if the service is attached to the HAProxy network
|
||||||
raise "Could not find ingress network"
|
|
||||||
|
|
||||||
self.parsed_object = {}
|
self.parsed_object = {}
|
||||||
for service in self.client.services.list():
|
for service in self.client.services.list():
|
||||||
|
if not any(self.label in key for key in service.attrs["Spec"]["Labels"]):
|
||||||
|
continue
|
||||||
|
|
||||||
ip_address = None
|
ip_address = None
|
||||||
network_list = []
|
network_list = []
|
||||||
for endpoint in service.attrs["Endpoint"]["VirtualIPs"]:
|
for endpoint in service.attrs["Endpoint"]["VirtualIPs"]:
|
||||||
if ha_proxy_network_id == endpoint["NetworkID"]:
|
if ha_proxy_network_id == endpoint["NetworkID"]:
|
||||||
ip_address = endpoint["Addr"].split("/")[0]
|
ip_address = endpoint["Addr"].split("/")[0]
|
||||||
break
|
break
|
||||||
network_list.append(endpoint["NetworkID"])
|
elif swarm_ingress_id != endpoint["NetworkID"]:
|
||||||
|
network_list.append(endpoint["NetworkID"])
|
||||||
|
|
||||||
|
# Attach the service to the HAProxy network
|
||||||
if ip_address is None:
|
if ip_address is None:
|
||||||
network_list.append(ha_proxy_network_id)
|
network_list.append(ha_proxy_network_id)
|
||||||
service.update(networks=network_list)
|
service.update(networks = network_list)
|
||||||
continue # skip to the next service to give time to update the network
|
continue # skip to the next service to give time to update the network
|
||||||
|
|
||||||
self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"]
|
self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"]
|
||||||
|
|
||||||
|
|
@ -187,10 +198,9 @@ class Kubernetes(ProcessorInterface):
|
||||||
self.cert_cache = {}
|
self.cert_cache = {}
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@staticmethod
|
def _check_annotation(self, annotations, key, default=None):
|
||||||
def _check_annotation(annotations, key):
|
|
||||||
if key not in annotations:
|
if key not in annotations:
|
||||||
return None
|
return default
|
||||||
return annotations[key]
|
return annotations[key]
|
||||||
|
|
||||||
def inspect_network(self):
|
def inspect_network(self):
|
||||||
|
|
@ -210,9 +220,7 @@ class Kubernetes(ProcessorInterface):
|
||||||
redirect_ssl = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect_ssl")
|
redirect_ssl = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect_ssl")
|
||||||
redirect = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect")
|
redirect = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect")
|
||||||
mode = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.mode")
|
mode = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.mode")
|
||||||
listen_port = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.listen_port")
|
listen_port = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.listen_port", 80)
|
||||||
if listen_port is None:
|
|
||||||
listen_port = 80
|
|
||||||
|
|
||||||
data = {"creation_timestamp": ingress.metadata.creation_timestamp.strftime("%x %X"),
|
data = {"creation_timestamp": ingress.metadata.creation_timestamp.strftime("%x %X"),
|
||||||
"resource_version": ingress.metadata.resource_version, "namespace": ingress.metadata.namespace}
|
"resource_version": ingress.metadata.resource_version, "namespace": ingress.metadata.namespace}
|
||||||
|
|
@ -259,6 +267,7 @@ class Kubernetes(ProcessorInterface):
|
||||||
rule_data["%s.redirect" % definition] = redirect
|
rule_data["%s.redirect" % definition] = redirect
|
||||||
if mode is not None:
|
if mode is not None:
|
||||||
rule_data["%s.mode" % definition] = mode
|
rule_data["%s.mode" % definition] = mode
|
||||||
|
rule_data["%s.balance" % definition] = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.balance", "roundrobin")
|
||||||
|
|
||||||
service_name = rule.http.paths[0].backend.service.name
|
service_name = rule.http.paths[0].backend.service.name
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{% if "ssl" in o %}
|
{% if "ssl" in o %}
|
||||||
bind *:{{ o["port"] }} ssl crt /certs/certbot/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:{{ o["port"] }} ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
{% elif "h2" in o and o["h2"] %}
|
{% elif "h2" in o and o["h2"] %}
|
||||||
bind *:{{ o["port"] }} proto h2
|
bind *:{{ o["port"] }} proto h2
|
||||||
option http-use-htx
|
option http-use-htx
|
||||||
|
|
|
||||||
|
|
@ -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
|
global
|
||||||
log stdout format raw local0 info
|
log stdout format raw local0 {{ haproxy_log_level }}
|
||||||
maxconn 2000
|
maxconn 2000
|
||||||
{% if data["ssl_mode"] == "strict" %}
|
{% if data["ssl_mode"] == "strict" %}
|
||||||
{% include "ssl_strict.j2" %}
|
{% include "ssl_strict.j2" %}
|
||||||
|
|
@ -59,7 +72,7 @@ frontend {{ mode }}_in_{{ o["port"] }}
|
||||||
{% for k in o["hosts"] -%}
|
{% for k in o["hosts"] -%}
|
||||||
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
|
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
|
||||||
backend srv_{{ host }}
|
backend srv_{{ host }}
|
||||||
balance roundrobin
|
balance {{ o["balance"] | default("roundrobin") }}
|
||||||
mode {{ mode }}
|
mode {{ mode }}
|
||||||
{% if mode == "http" %}
|
{% if mode == "http" %}
|
||||||
option forwardfor
|
option forwardfor
|
||||||
|
|
@ -67,10 +80,10 @@ backend srv_{{ host }}
|
||||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||||
{% elif mode == "tcp" %}
|
{% elif mode == "tcp" %}
|
||||||
option tcp-check
|
option tcp-check
|
||||||
tcp-check connect{{ " ssl" if o["health-check"] == "ssl" }}
|
tcp-check connect{{ " ssl" if o["ssl-check"] == "ssl" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for c in o["hosts"][k]["containers"] %}
|
{% for c in o["hosts"][k]["containers"] %}
|
||||||
server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["health-check"] == "ssl" }}
|
server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["ssl-check"] == "ssl" }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ backend srv_stats
|
||||||
server Local 127.0.0.1:1936
|
server Local 127.0.0.1:1936
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/certbot/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local
|
acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ backend srv_test2_example_org_80
|
||||||
server srv-0 83d57d592e26:8080 check weight 1
|
server srv-0 83d57d592e26:8080 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/certbot/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_test_example_org_443_1 hdr(host) -i test.example.org
|
acl is_rule_test_example_org_443_1 hdr(host) -i test.example.org
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ backend srv_host1_local_80
|
||||||
server srv-0 5b69bc7fea1b:80 check weight 1
|
server srv-0 5b69bc7fea1b:80 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/certbot/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_host2_local_443_1 hdr(host) -i host2.local
|
acl is_rule_host2_local_443_1 hdr(host) -i host2.local
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ backend srv_node-exporter_quantum_example_org_31337
|
||||||
server srv-0 my-stack_node-exporter:9100 check weight 1
|
server srv-0 my-stack_node-exporter:9100 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/certbot/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com.br }
|
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com.br }
|
||||||
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com }
|
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com }
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ backend srv_host2_com_br_80
|
||||||
server srv-0 other:3000 check weight 1
|
server srv-0 other:3000 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/certbot/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br
|
acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br
|
||||||
|
|
|
||||||
2
src/tests/fixtures/00_haproxy.cfg
vendored
Normal file
2
src/tests/fixtures/00_haproxy.cfg
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
global
|
||||||
|
maxconn 4000
|
||||||
2
src/tests/fixtures/10_haproxy.cfg
vendored
Normal file
2
src/tests/fixtures/10_haproxy.cfg
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
global
|
||||||
|
maxconn 5000
|
||||||
2
src/tests/fixtures/services-tcp
vendored
2
src/tests/fixtures/services-tcp
vendored
|
|
@ -1,2 +1,2 @@
|
||||||
{"test_agent": {"easyhaproxy.agent.host":"agent.quantum.local","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"test", "easyhaproxy.agent.health-check":"ssl"},
|
{"test_agent": {"easyhaproxy.agent.host":"agent.quantum.local","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"test", "easyhaproxy.agent.ssl-check":"ssl"},
|
||||||
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}}
|
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from functions import ContainerEnv
|
from functions import Functions, ContainerEnv
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_empty():
|
def test_container_env_empty():
|
||||||
|
|
@ -8,6 +8,11 @@ def test_container_env_empty():
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
"certbot": {"autoconfig": "",
|
"certbot": {"autoconfig": "",
|
||||||
"eab_hmac_key": "",
|
"eab_hmac_key": "",
|
||||||
"eab_kid": "",
|
"eab_kid": "",
|
||||||
|
|
@ -26,6 +31,11 @@ def test_container_env_customerrors():
|
||||||
"customerrors": True,
|
"customerrors": True,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
"certbot": {"autoconfig": "",
|
"certbot": {"autoconfig": "",
|
||||||
"eab_hmac_key": "",
|
"eab_hmac_key": "",
|
||||||
"eab_kid": "",
|
"eab_kid": "",
|
||||||
|
|
@ -34,7 +44,7 @@ def test_container_env_customerrors():
|
||||||
"retry_count": 60}
|
"retry_count": 60}
|
||||||
} == ContainerEnv.read()
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_CUSTOMERRORS'] = ''
|
del os.environ['HAPROXY_CUSTOMERRORS']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_sslmode():
|
def test_container_env_sslmode():
|
||||||
|
|
@ -44,6 +54,11 @@ def test_container_env_sslmode():
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "strict",
|
"ssl_mode": "strict",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
"certbot": {"autoconfig": "",
|
"certbot": {"autoconfig": "",
|
||||||
"eab_hmac_key": "",
|
"eab_hmac_key": "",
|
||||||
"eab_kid": "",
|
"eab_kid": "",
|
||||||
|
|
@ -52,7 +67,7 @@ def test_container_env_sslmode():
|
||||||
"retry_count": 60}
|
"retry_count": 60}
|
||||||
} == ContainerEnv.read()
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_SSL_MODE'] = ''
|
del os.environ['EASYHAPROXY_SSL_MODE']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_stats():
|
def test_container_env_stats():
|
||||||
|
|
@ -63,6 +78,11 @@ def test_container_env_stats():
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
"certbot": {"autoconfig": "",
|
"certbot": {"autoconfig": "",
|
||||||
"eab_hmac_key": "",
|
"eab_hmac_key": "",
|
||||||
"eab_kid": "",
|
"eab_kid": "",
|
||||||
|
|
@ -71,8 +91,8 @@ def test_container_env_stats():
|
||||||
"retry_count": 60}
|
"retry_count": 60}
|
||||||
} == ContainerEnv.read()
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_USERNAME'] = ''
|
del os.environ['HAPROXY_USERNAME']
|
||||||
os.environ['HAPROXY_STATS_PORT'] = ''
|
del os.environ['HAPROXY_STATS_PORT']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_stats_password():
|
def test_container_env_stats_password():
|
||||||
|
|
@ -88,6 +108,11 @@ def test_container_env_stats_password():
|
||||||
"port": "1936"
|
"port": "1936"
|
||||||
|
|
||||||
},
|
},
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
"certbot": {"autoconfig": "",
|
"certbot": {"autoconfig": "",
|
||||||
"eab_hmac_key": "",
|
"eab_hmac_key": "",
|
||||||
"eab_kid": "",
|
"eab_kid": "",
|
||||||
|
|
@ -96,7 +121,7 @@ def test_container_env_stats_password():
|
||||||
"retry_count": 60}
|
"retry_count": 60}
|
||||||
} == ContainerEnv.read()
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_PASSWORD'] = ''
|
del os.environ['HAPROXY_PASSWORD']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_stats_password_2():
|
def test_container_env_stats_password_2():
|
||||||
|
|
@ -113,6 +138,11 @@ def test_container_env_stats_password_2():
|
||||||
"password": "xyz",
|
"password": "xyz",
|
||||||
"port": "2101"
|
"port": "2101"
|
||||||
},
|
},
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
"certbot": {"autoconfig": "",
|
"certbot": {"autoconfig": "",
|
||||||
"eab_hmac_key": "",
|
"eab_hmac_key": "",
|
||||||
"eab_kid": "",
|
"eab_kid": "",
|
||||||
|
|
@ -121,9 +151,9 @@ def test_container_env_stats_password_2():
|
||||||
"retry_count": 60}
|
"retry_count": 60}
|
||||||
} == ContainerEnv.read()
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_USERNAME'] = ''
|
del os.environ['HAPROXY_USERNAME']
|
||||||
os.environ['HAPROXY_STATS_PORT'] = ''
|
del os.environ['HAPROXY_STATS_PORT']
|
||||||
os.environ['HAPROXY_PASSWORD'] = ''
|
del os.environ['HAPROXY_PASSWORD']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_certbot_email():
|
def test_container_env_certbot_email():
|
||||||
|
|
@ -133,6 +163,11 @@ def test_container_env_certbot_email():
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
"certbot": {
|
"certbot": {
|
||||||
"autoconfig": "",
|
"autoconfig": "",
|
||||||
'eab_hmac_key': "",
|
'eab_hmac_key': "",
|
||||||
|
|
@ -143,7 +178,7 @@ def test_container_env_certbot_email():
|
||||||
}
|
}
|
||||||
} == ContainerEnv.read()
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = ''
|
del os.environ['EASYHAPROXY_CERTBOT_EMAIL']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_certbot_full():
|
def test_container_env_certbot_full():
|
||||||
|
|
@ -154,17 +189,55 @@ def test_container_env_certbot_full():
|
||||||
os.environ['EASYHAPROXY_CERTBOT_RETRY_COUNT'] = "10"
|
os.environ['EASYHAPROXY_CERTBOT_RETRY_COUNT'] = "10"
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
"certbot": {
|
"logLevel": {
|
||||||
"autoconfig": "",
|
"easyhaproxy": Functions.DEBUG,
|
||||||
"email": "acme@example.org",
|
"haproxy": Functions.INFO,
|
||||||
"server": "schema://url/a",
|
"certbot": Functions.DEBUG,
|
||||||
'eab_hmac_key': 'eab_hmac_key',
|
},
|
||||||
'eab_kid': 'eab_kid',
|
"certbot": {
|
||||||
'retry_count': 10
|
"autoconfig": "",
|
||||||
}
|
"email": "acme@example.org",
|
||||||
} == ContainerEnv.read()
|
"server": "schema://url/a",
|
||||||
|
'eab_hmac_key': 'eab_hmac_key',
|
||||||
|
'eab_kid': 'eab_kid',
|
||||||
|
'retry_count': 10
|
||||||
|
}
|
||||||
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = ''
|
del os.environ['EASYHAPROXY_CERTBOT_EMAIL']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_SERVER']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_EAB_KID']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_RETRY_COUNT']
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
"certbot": {
|
||||||
|
"autoconfig": "",
|
||||||
|
'eab_hmac_key': "",
|
||||||
|
'eab_kid': "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60
|
||||||
|
}
|
||||||
|
} == ContainerEnv.read()
|
||||||
|
finally:
|
||||||
|
del os.environ['CERTBOT_LOG_LEVEL']
|
||||||
|
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||||
|
del os.environ['HAPROXY_LOG_LEVEL']
|
||||||
|
|
|
||||||
48
src/tests/test_daemonize.py
Normal file
48
src/tests/test_daemonize.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from functions import DaemonizeHAProxy, Functions
|
||||||
|
|
||||||
|
|
||||||
|
def test_daemonize_haproxy():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
assert daemon is not None
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_check_config():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
filed = daemon.get_custom_config_files()
|
||||||
|
assert filed == {}
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_start():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
command = daemon.get_haproxy_command("start")
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock"
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_reload():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
command = daemon.get_haproxy_command("reload")
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf "
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_check_config():
|
||||||
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
|
filed = daemon.get_custom_config_files()
|
||||||
|
assert filed == {
|
||||||
|
os.path.dirname(__file__) + "/fixtures/00_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/00_haproxy.cfg"),
|
||||||
|
os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg")
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_start():
|
||||||
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
|
command = daemon.get_haproxy_command("start")
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (os.path.dirname(__file__) + "/fixtures")
|
||||||
|
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_reload():
|
||||||
|
tmp_pid_file = "/tmp/tmp_pid.txt"
|
||||||
|
Functions.save(tmp_pid_file, "10")
|
||||||
|
|
||||||
|
try:
|
||||||
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
|
command = daemon.get_haproxy_command("reload", tmp_pid_file)
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p %s -x /var/run/haproxy.sock -sf %s" % (os.path.dirname(__file__) + "/fixtures", tmp_pid_file, 10)
|
||||||
|
finally:
|
||||||
|
os.remove(tmp_pid_file)
|
||||||
|
|
@ -100,7 +100,7 @@ def test_processor_docker():
|
||||||
'hostssl.local.pem': 'Some PEM Certificate'
|
'hostssl.local.pem': 'Some PEM Certificate'
|
||||||
}
|
}
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = ''
|
del os.environ['EASYHAPROXY_CERTBOT_EMAIL']
|
||||||
container.stop()
|
container.stop()
|
||||||
container2.stop()
|
container2.stop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,15 @@ def test_functions_check_local_level():
|
||||||
|
|
||||||
os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('CERTBOT', Functions.INFO) == True
|
assert Functions.skip_log('CERTBOT', Functions.INFO) == True
|
||||||
os.environ['CERTBOT_LOG_LEVEL'] = ''
|
del os.environ['CERTBOT_LOG_LEVEL']
|
||||||
|
|
||||||
os.environ['HAPROXY_LOG_LEVEL'] = 'warn'
|
os.environ['HAPROXY_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('HAPROXY', Functions.INFO) == True
|
assert Functions.skip_log('HAPROXY', Functions.INFO) == True
|
||||||
os.environ['HAPROXY_LOG_LEVEL'] = ''
|
del os.environ['HAPROXY_LOG_LEVEL']
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('EASYHAPROXY', Functions.INFO) == True
|
assert Functions.skip_log('EASYHAPROXY', Functions.INFO) == True
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = ''
|
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||||
|
|
||||||
|
|
||||||
def test_function_load_and_save():
|
def test_function_load_and_save():
|
||||||
|
|
@ -57,7 +57,7 @@ def test_functions_check_log_sanity():
|
||||||
assert len(Functions.debug_log) == 2
|
assert len(Functions.debug_log) == 2
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = ''
|
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||||
Functions.debug_log = None
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,11 +114,12 @@ def test_parser_finds_services_raw():
|
||||||
|
|
||||||
parsed_object = [
|
parsed_object = [
|
||||||
{
|
{
|
||||||
"mode": "tcp",
|
"mode":"tcp",
|
||||||
"health-check": "",
|
"ssl-check":"",
|
||||||
"port": "31339",
|
"port":"31339",
|
||||||
"hosts": {
|
"hosts":{
|
||||||
"agent.quantum.example.org": {
|
"agent.quantum.example.org": {
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_agent:9001"
|
"my-stack_agent:9001"
|
||||||
],
|
],
|
||||||
|
|
@ -131,18 +132,20 @@ def test_parser_finds_services_raw():
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mode": "http",
|
"mode":"http",
|
||||||
"health-check": "",
|
"ssl-check":"",
|
||||||
"port": "31337",
|
"port":"31337",
|
||||||
"hosts": {
|
"hosts":{
|
||||||
"cadvisor.quantum.example.org": {
|
"cadvisor.quantum.example.org":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_cadvisor:8080"
|
"my-stack_cadvisor:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"node-exporter.quantum.example.org": {
|
"node-exporter.quantum.example.org":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_node-exporter:9100"
|
"my-stack_node-exporter:9100"
|
||||||
],
|
],
|
||||||
|
|
@ -155,18 +158,20 @@ def test_parser_finds_services_raw():
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mode": "http",
|
"mode":"http",
|
||||||
"health-check": "",
|
"ssl-check":"",
|
||||||
"port": "443",
|
"port":"443",
|
||||||
"hosts": {
|
"hosts":{
|
||||||
"node-exporter.quantum.example.org": {
|
"node-exporter.quantum.example.org": {
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_node-exporter:9100"
|
"my-stack_node-exporter:9100"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"www.somehost.com.br": {
|
"www.somehost.com.br":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"some-service:80"
|
"some-service:80"
|
||||||
],
|
],
|
||||||
|
|
@ -184,11 +189,12 @@ def test_parser_finds_services_raw():
|
||||||
"ssl": True
|
"ssl": True
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mode": "http",
|
"mode":"http",
|
||||||
"health-check": "",
|
"ssl-check":"",
|
||||||
"port": "80",
|
"port":"80",
|
||||||
"hosts": {
|
"hosts":{
|
||||||
"www.somehost.com.br": {
|
"www.somehost.com.br":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"some-service:80"
|
"some-service:80"
|
||||||
],
|
],
|
||||||
|
|
@ -451,25 +457,28 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
|
|
||||||
parsed_object = [
|
parsed_object = [
|
||||||
{
|
{
|
||||||
"health-check": "",
|
"ssl-check":"",
|
||||||
"hosts": {
|
"hosts":{
|
||||||
"host2.local": {
|
"host2.local":{
|
||||||
"containers": [
|
"balance":"roundrobin",
|
||||||
"10.152.183.215:8080"
|
"containers":[
|
||||||
|
"10.152.183.215:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"valida.me": {
|
"valida.me":{
|
||||||
"containers": [
|
"balance":"roundrobin",
|
||||||
"10.152.183.62:8080"
|
"containers":[
|
||||||
|
"10.152.183.62:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"www.valida.me": {
|
"www.valida.me":{
|
||||||
"containers": [
|
"balance":"roundrobin",
|
||||||
"10.152.183.62:8080"
|
"containers":[
|
||||||
|
"10.152.183.62:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
|
|
@ -482,11 +491,12 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"health-check": "ssl",
|
"ssl-check":"ssl",
|
||||||
"hosts": {
|
"hosts":{
|
||||||
"host2.local": {
|
"host2.local":{
|
||||||
"containers": [
|
"balance":"roundrobin",
|
||||||
"10.152.183.215:8080"
|
"containers":[
|
||||||
|
"10.152.183.215:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue