1
0
Fork 0

Merge pull request #27 from byjg/kubernetes-refactory

Kubernetes refactory
This commit is contained in:
Joao M 2022-08-24 21:34:13 -05:00 committed by GitHub
commit edee20c5bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 725 additions and 498 deletions

View file

@ -6,4 +6,4 @@ tasks:
- command: | - command: |
virtualenv -p /usr/bin/python3 venv virtualenv -p /usr/bin/python3 venv
source venv/bin/activate source venv/bin/activate
pip install -r requirements.txt pip install -r src/requirements.txt

View file

@ -1,26 +0,0 @@
language: python
services:
- docker
jobs:
include:
- stage: test
if: (type IN (pull_request))
install:
- pip install -r requirements.txt
script:
- pytest -s tests/
- stage: build docker
if: (branch = master) AND (NOT (type IN (pull_request)))
install:
- docker pull byjg/k8s-ci
script:
- docker run --privileged -v /tmp/z:/var/lib/containers -it --rm -v $PWD:/work -w /work -e DOCKER_USERNAME=$DOCKER_USERNAME -e DOCKER_PASSWORD=$DOCKER_PASSWORD -e DOCKER_REGISTRY=$DOCKER_REGISTRY byjg/k8s-ci /work/build-multiarch.sh
- stage: documentation
if: (branch = master) AND (NOT (type IN (pull_request)))
install: skip
script: "curl https://opensource.byjg.com/add-doc.sh | bash /dev/stdin devops docker-easy-haproxy"

4
.vscode/launch.json vendored
View file

@ -10,9 +10,9 @@
"request": "launch", "request": "launch",
"program": "${file}", "program": "${file}",
"console": "integratedTerminal", "console": "integratedTerminal",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}/src",
"env": { "env": {
"PYTHONPATH": "${cwd}" "PYTHONPATH": "${cwd}/src"
} }
} }
] ]

View file

@ -6,18 +6,15 @@ ENV RELEASE_VERSION=$RELEASE_VERSION_ARG
WORKDIR /scripts WORKDIR /scripts
COPY requirements.txt /scripts COPY src/ /scripts/
COPY templates /scripts/templates/
COPY easymapping /scripts/easymapping/
COPY tests/ /scripts/tests/
COPY assets / COPY assets /
RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml docker certbot openssl \ RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml certbot openssl \
&& ln -s /usr/bin/python3 /usr/bin/python \ && 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 tests/ \ && pytest -s -vv tests/ \
&& openssl dhparam -out /etc/haproxy/dhparam 2048 \ && openssl dhparam -out /etc/haproxy/dhparam 2048 \
&& openssl dhparam -out /etc/haproxy/dhparam-1024 1024 && openssl dhparam -out /etc/haproxy/dhparam-1024 1024
CMD ["/bin/bash", "-c", "/scripts/haproxy.sh" ] CMD ["/usr/bin/python", "-u", "/scripts/main.py" ]

View file

@ -170,6 +170,10 @@ services:
... ...
``` ```
```bash
docker stack deploy --compose-file docker-compose.yml mystack
```
### Single Definition ### Single Definition
```bash ```bash

View file

@ -1,66 +0,0 @@
name: docker-easy-haproxy
project:
version: 1.0.0
download_url: https://github.com/byjg/docker-easy-haproxy/releases
license:
software: MIT
software_url: https://opensource.org/licenses/MIT
docs: MIT
docs_url: https://opensource.org/licenses/MIT
git_edit_address: https://github.com/byjg/docker-easy-haproxy/blob/master/
links:
header:
- title: GitHub
url: https://github.com/byjg/docker-easy-haproxy
- title: ByJG
url: https://opensource.byjg.com/
footer:
- title: GitHub
url: https://github.com/byjg/docker-easy-haproxy
- title: Issues
url: https://github.com/byjg/docker-easy-haproxy/issues
ui:
header:
color1: "#080331"
color2: "#0033cc"
trianglify: true
social:
github:
user: byjg
repo: docker-easy-haproxy
twitter:
enabled: false
via:
hash: opensourcebyjg
account:
facebook:
enabled: true
url: https://opensource.byjg.com/
profileUrl:
author:
twitter: byjg
twitter:
card: summary
username: byjg
logo: https://opensource.byjg.com/images/logo_byjg.png
analytics:
google: UA-130014324-1
plugins:
- jekyll-seo-tag
# Build settings
markdown: kramdown
remote_theme: byjg/jekyll-docs-theme

View file

@ -1,5 +0,0 @@
_
___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _
/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |
\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |
|__/ |_| |__/

View file

@ -1,55 +0,0 @@
#!/usr/bin/env bash
source /scripts/functions.sh
if [ ! -f /scripts/letsencrypt_hosts.txt ]; then
exit 0
fi
# Semaphore
if [ -f /tmp/certbot-lock ]; then
log "notice" "CERTBOT_JOB" "Another process is running"
exit 0
fi
touch /tmp/certbot-lock
mkdir -p /var/log/letsencrypt
ln -sf /dev/stdout /var/log/letsencrypt/letsencrypt.log
REQUEST_CERTS=""
RENEW_CERTS=""
for domain in $(cat /scripts/letsencrypt_hosts.txt); do
if [ ! -f "/certs/letsencrypt/$domain.pem" ]; then
REQUEST_CERTS="$REQUES_CERTS -d $domain"
continue
fi
if [[ $(find "/certs/letsencrypt/$domain.pem" -mtime +30 -print) ]]; then
RENEW_CERTS="$RENEW_CERTS -d $domain"
fi
done
if [ -n "$REQUEST_CERTS" ]; then
log "info" "CERTBOT_JOB" "Requesting certificates for $REQUEST_CERTS"
certbot certonly \
--standalone \
--preferred-challenges http \
--http-01-port 2080 \
--agree-tos \
--issuance-timeout 90 \
--no-eff-email \
--non-interactive \
--max-log-backups=0 \
--post-hook "/scripts/certbot_to_haproxy.sh" \
$REQUEST_CERTS --email $EASYHAPROXY_LETSENCRYPT_EMAIL
fi
if [ -n "$RENEW_CERTS" ]; then
log "info" "CERTBOT_JOB" "Resquesting renew certificated fort $RENEW_CERTS"
certbot renew --post-hook "/scripts/certbot_to_haproxy.sh"
fi
# Release semaphore
rm /tmp/certbot-lock

View file

@ -1,14 +0,0 @@
#!/bin/bash
source /scripts/functions.sh
# Loop through all Let's Encrypt certificates
for CERTIFICATE in `find /etc/letsencrypt/live/* -type d`; do
CERTIFICATE=`basename $CERTIFICATE`
# Combine certificate and private key to single file
cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /certs/letsencrypt/$CERTIFICATE.pem
done
# It will be checked on haproxy-reload.sh
touch /tmp/force-reload

View file

@ -1,9 +0,0 @@
#!/bin/bash
function log() {
# ARGS:
# - loglevel
# - app
# - message
echo [$2] $(date +"$EASYHAPROXY_DATEFORMAT") [$1]: $3
}

View file

@ -1,81 +0,0 @@
#!/usr/bin/env bash
source /scripts/functions.sh
cd /scripts
RELOAD="true"
if [[ "$EASYHAPROXY_DISCOVER" == "static" ]]; then
CONTROL_FILE="/etc/haproxy/haproxy.cfg"
touch ${CONTROL_FILE}
cp ${CONTROL_FILE} ${CONTROL_FILE}.old
python3 static.py /etc/haproxy/easyconfig.yml > ${CONTROL_FILE}
else
CONTROL_FILE="/tmp/.docker_data"
touch ${CONTROL_FILE}
mv ${CONTROL_FILE} ${CONTROL_FILE}.old
touch ${CONTROL_FILE}
case "$EASYHAPROXY_DISCOVER" in
docker)
CONTAINERS=$(docker ps -q | sort | uniq)
LABEL_PATH=".Config.Labels"
for container in ${CONTAINERS}; do
docker inspect --format "{{ json $LABEL_PATH }}" ${container} | xargs -I % echo ${container}=% >> ${CONTROL_FILE}
done
;;
swarm)
CONTAINERS=$(docker node ps $(docker node ls -q) --format "{{ .Name }}" --filter desired-state=running | cut -d. -f1 | sort | uniq)
LABEL_PATH=".Spec.Labels"
for container in ${CONTAINERS}; do
docker service inspect --format "{{ json $LABEL_PATH }}" ${container} | xargs -I % echo ${container}=% >> ${CONTROL_FILE}
done
;;
kubernetes)
python3 /scripts/k8s.py > ${CONTROL_FILE}
;;
*)
log "error" "CONF_CHECK" "EASYHAPROXY_DISCOVER should be 'static', 'docker', 'swarm' or kubernetes. I got '$EASYHAPROXY_DISCOVER' instead."
exit 1
;;
esac
if cmp -s ${CONTROL_FILE} ${CONTROL_FILE}.old ; then
RELOAD="false"
else
python3 swarm.py > /etc/haproxy/haproxy.cfg
log "info" "CONF_CHECK" "New configuration found"
fi
fi
if cmp -s ${CONTROL_FILE} ${CONTROL_FILE}.old ; then
RELOAD="false"
fi
if [[ ! -z "$1" ]]; then
log "info" "CONF_CHECK" "Initial configuration. Skip certbot."
else
/scripts/certbot.sh
fi
# If Certbot reloads successfully will create the file /tmp/force-reload
if [ -f /tmp/force-reload ]; then
log "info" "CONF_CHECK" "New certificates found..."
RELOAD="true"
rm /tmp/force-reload
fi
if [[ ! -z "$1" ]]; then
log "info" "CONF_CHECK" "Starting haproxy..."
/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg $(ls /etc/haproxy/conf.d/*.cfg 2>/dev/null | xargs -I{} echo -f {}) -p /run/haproxy.pid -S /var/run/haproxy.sock &
elif [[ "$RELOAD" == "true" ]]; then
log "info" "CONF_CHECK" "Reloading..."
/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg $(ls /etc/haproxy/conf.d/*.cfg 2>/dev/null | xargs -I{} echo -f {}) -p /run/haproxy.pid -x /var/run/haproxy.sock -sf $(cat /run/haproxy.pid) &
fi

View file

@ -1,29 +0,0 @@
#!/usr/bin/env bash
source /scripts/functions.sh
/usr/sbin/haproxy -v
if [ -z "$EASYHAPROXY_DATEFORMAT" ]; then
export EASYHAPROXY_DATEFORMAT="%Y-%m-%d %H:%M:%S %Z"
fi
if [ -z "$EASYHAPROXY_REFRESH_CONF" ]; then
export EASYHAPROXY_REFRESH_CONF=10
fi
cat banner.txt
echo Release: $RELEASE_VERSION
echo
echo "Environment"
env | sort | grep 'HAPROXY' | xargs -I{} echo " - {} "
echo
/scripts/haproxy-reload.sh initial
while true; do
sleep $EASYHAPROXY_REFRESH_CONF
log "info" "CONF_CHECK" "Heartbeat."
/scripts/haproxy-reload.sh
done

View file

@ -1,50 +0,0 @@
from kubernetes import client, config
from kubernetes.client.rest import ApiException
import json
# https://github.com/kubernetes-client/python/tree/master/kubernetes/docs
def main():
config.load_incluster_config()
api_instance = client.CoreV1Api()
v1 = client.NetworkingV1Api()
ret = v1.list_ingress_for_all_namespaces(watch=False)
discover = {}
for i in ret.items:
if i.metadata.annotations['kubernetes.io/ingress.class'] != "easyhaproxy-ingress":
continue
data = {}
#ingress_name = i.metadata.name
data["creation_timestamp"] = i.metadata.creation_timestamp.strftime("%x %X")
data["resource_version"] = i.metadata.resource_version
data["namespace"] = i.metadata.namespace
for rule in i.spec.rules:
rule_data = {}
port_number = rule.http.paths[0].backend.service.port.number
definition = rule.host.replace(".", "-")
rule_data["easyhaproxy.%s_%s.host" % (definition, port_number)] = rule.host
rule_data["easyhaproxy.%s_%s.port" % (definition, port_number)] = "80"
rule_data["easyhaproxy.%s_%s.localport" % (definition, port_number)] = port_number
service_name = rule.http.paths[0].backend.service.name
try:
api_response = api_instance.read_namespaced_service(service_name, i.metadata.namespace)
cluster_ip = api_response.spec.cluster_ip
except ApiException as e:
cluster_ip = None
# print("Exception when calling CoreV1Api->read_namespaced_service: %s\n" % e)
if cluster_ip is not None:
if cluster_ip not in discover.keys():
discover[cluster_ip] = data
discover[cluster_ip].update(rule_data)
for k in discover.keys():
print("%s=%s" % (k, json.dumps(discover[k])))
if __name__ == '__main__':
main()

View file

@ -1,22 +0,0 @@
import yaml
import sys
import os
from easymapping import HaproxyConfigGenerator
if len(sys.argv) != 2:
print("You need to pass the easyconfig.yml path")
exit(1)
with open(sys.argv[1], 'r') as content_file:
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
cfg = HaproxyConfigGenerator(parsed)
print(cfg.generate())
path = os.path.dirname(os.path.realpath(__file__))
with open(path + "/letsencrypt_hosts.txt", 'w') as fp:
fp.write('\n'.join(cfg.letsencrypt_hosts))
exit(0)

View file

@ -1,34 +0,0 @@
import os
from easymapping import HaproxyConfigGenerator
with open("/tmp/.docker_data", 'r') as content_file:
line_list = content_file.readlines()
result = {
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default")
}
if os.getenv("HAPROXY_PASSWORD"):
result["stats"] = {
"username": os.getenv("HAPROXY_USERNAME") if os.getenv("HAPROXY_USERNAME") else "admin",
"password": os.getenv("HAPROXY_PASSWORD"),
"port": os.getenv("HAPROXY_STATS_PORT") if os.getenv("HAPROXY_STATS_PORT") else "1936",
}
result["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv("EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
if (os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")):
result["letsencrypt"] = {
"email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")
}
cfg = HaproxyConfigGenerator(result)
print(cfg.generate(line_list))
path = os.path.dirname(os.path.realpath(__file__))
with open(path + "/letsencrypt_hosts.txt", 'w') as fp:
fp.write('\n'.join(cfg.letsencrypt_hosts))
# print(jsonStr)

View file

@ -38,17 +38,7 @@ spec:
requests: requests:
cpu: "100m" cpu: "100m"
memory: "128Mi" memory: "128Mi"
#limits:
# cpu: "1"
# memory: "1Gi"
#securityContext:
# allowPrivilegeEscalation: true
# #runAsUser: 101 #nginx
# capabilities:
# drop:
# - ALL
# add:
# - NET_BIND_SERVICE
env: env:
- name: EASYHAPROXY_DISCOVER - name: EASYHAPROXY_DISCOVER
value: kubernetes value: kubernetes

View file

@ -0,0 +1,80 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: easyhaproxy-ingress
name: container-example
namespace: parking
spec:
rules:
- host: example.org
http:
paths:
- backend:
service:
name: container-example
port:
number: 8080
pathType: ImplementationSpecific
- host: www.example.org
http:
paths:
- backend:
service:
name: container-example
port:
number: 8080
pathType: ImplementationSpecific
---
apiVersion: v1
kind: Service
metadata:
name: container-example
namespace: parking
spec:
ports:
- name: http
port: 8080
selector:
app: container-example
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: container-example
namespace: parking
spec:
replicas: 1
revisionHistoryLimit: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: container-example
template:
metadata:
labels:
app: container-example
spec:
containers:
- name: container-example
image: byjg/static-httpserver
ports:
- containerPort: 8080
resources:
limits:
cpu: '0.05'
memory: '20Mi'
requests:
cpu: '0.05'
memory: '20Mi'
env:
- name: TITLE
value: "My Host Example"

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,50 @@
-----BEGIN CERTIFICATE-----
MIIDqTCCApGgAwIBAgIUId5b9t9uqH78g02EzbWF6FKVw3gwDQYJKoZIhvcNAQEL
BQAwZDELMAkGA1UEBhMCQlIxFzAVBgNVBAgMDlJpbyBkZSBKYW5laXJvMRcwFQYD
VQQHDA5SaW8gZGUgSmFuZWlybzENMAsGA1UECgwEQUNNRTEUMBIGA1UEAwwLaG9z
dDIubG9jYWwwHhcNMjIwODE1MDQyNzA1WhcNMjMwODE1MDQyNzA1WjBkMQswCQYD
VQQGEwJCUjEXMBUGA1UECAwOUmlvIGRlIEphbmVpcm8xFzAVBgNVBAcMDlJpbyBk
ZSBKYW5laXJvMQ0wCwYDVQQKDARBQ01FMRQwEgYDVQQDDAtob3N0Mi5sb2NhbDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMSKvrOahavCXnvSF5131hpo
6k65C57jgRQ84FaDj5MbJOVlYQVFtMG0XOk7a+hh5v1fe4wH0R7I6FDo0V9sS+ss
ko5bsElc1xYlg5HbuKq89vRSKg6EDlztx3BKbi912Pmt5vFGNJ16zcw77DUrQIXo
4I/b4a3pmBiWj43NoTIrmSWHtsGwwOj3iDvSweqdYXJIr3hpHH5u6pohjDoQvqDz
K6Mu8p6mhCUKNs7KFJnNInNG25oQT6O0n4OGtmgRjLWopdEnOhMkKsfIoI1XtlXB
LBDv7huICk3t5ywtfCQyO09kX7lFIgd5rn7+MjwH5WNeqbQJxuaqjoXQnNZUgUsC
AwEAAaNTMFEwHQYDVR0OBBYEFNhMBG8q6a+iK2nECwVTn6B9EXZOMB8GA1UdIwQY
MBaAFNhMBG8q6a+iK2nECwVTn6B9EXZOMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAJmudvx8+p5iIUsT8fm/fbVM0DA6qWALDYUJnTn3j6Lq4vpf
PFC+q1LmuWfBQMyqKrHrP3e493EctXoiSKZO6iN5dVJIur02OjGuiAEcsYuY1nLn
s9piiI+UEwxH6ux1NaHUnzsWauoBvRhzjXvO6SAVSZJYa9dY5mizXklDyDNuG5U0
lXv9egMGBsy0dG6eFXkU5CPdxWU540yI2sCtSAj7z+WRUD5k7gJ7tVoY3//jHQZG
5STTmm5t9kpIZTWkptyJos9oZJFYMIXqW2Fc6tyLZpRp31R78tDs6ETIkToDc0RR
jz66th6HI+ZlgIBQhw09+hYAhBDe9+Dmd/SzQZc=
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDEir6zmoWrwl57
0hedd9YaaOpOuQue44EUPOBWg4+TGyTlZWEFRbTBtFzpO2voYeb9X3uMB9EeyOhQ
6NFfbEvrLJKOW7BJXNcWJYOR27iqvPb0UioOhA5c7cdwSm4vddj5rebxRjSdes3M
O+w1K0CF6OCP2+Gt6ZgYlo+NzaEyK5klh7bBsMDo94g70sHqnWFySK94aRx+buqa
IYw6EL6g8yujLvKepoQlCjbOyhSZzSJzRtuaEE+jtJ+DhrZoEYy1qKXRJzoTJCrH
yKCNV7ZVwSwQ7+4biApN7ecsLXwkMjtPZF+5RSIHea5+/jI8B+VjXqm0Ccbmqo6F
0JzWVIFLAgMBAAECggEBAKceitVROQQ5e/mxRR9CfK1sNH/H3Ne3/1PkB6XIrFab
qB3evEatZOuon7A6NKEeTjl37Se+pdSVZOUXcqC/BzbraZre3+EhrkpIj72ApV+Y
2iwZiWVaaJQgI4uZ3mNAw8RaWJsj5S1a9I8LDOiQ5IZ45CmvABDPJeMScvJSvRRY
e5N0L6stqS7Z+IoyGVKUfp1iNO0YyywOUiSkIRXgscuRXZGYpiGPomsJ+Js1ejzW
jyStlZJEr4L1285rGPrmHqjTwFd+hG80Wc4179xL+WRE6HBEUZSiy95fe6kcPHXX
BgiVYtcFKmiBi2dTbxl4e94ut239i0HtlJ1ZJtLh+BECgYEA8a298K2zXkHosxhN
tRrH7XfMPTkHDDd3rxM21LT+fIXqinGUp9LYaDcbjuTPs8e33uKMd7R6Q40x89yW
IXNka/VL0PXUeV67aCVLLqgXDLGudluJinH0XvmI0CmBecFSMqIFmQlgqERoGGs3
UMac0p876T4XkGQQJdf62bFpE4UCgYEA0DBH0PDlOpwXccDgXrMfayr8HAhI+G5R
yWQ//9iirtU83chwIWwkh53eLLMzLgdqJnPiWyaUW5BqzmYuD23nhxcQ7PNdIqOO
H1sE6zqLNshv46t5QKlh1Q4qjd7UqtgrSrY63RXJCMWTwnNMeDLtj8gaKbjkrG3R
BM2ildt6Uo8CgYBX7NDUli1SloH1XlsvD047S8FHaM7yl994F3J0UmDfpszcj1P4
9pF64Mmq4/3Yt0li0mMuTb/Jgb3xrYgFJXkcecKahEVH3ropup+umsLAAIirUMQq
VSkFwJ0Qtnj/deDUwPNuaOX8cd65O5CFV6zIR9xBEDD8fBsP2ZLOzmefDQKBgQDF
m24vVthd/1cJdCgD+0VxNYXDHeIVXLFo1S0iLYCNLn3tjZlRQBKUXzZJe3ay0/rf
sNND7aSYHMYkTzydDJbc1PoNzxmyDUiTXpOWqyUExM/fbB1VUPE5h47AxqdZ2oGN
EtdgjpMZLmCIC2SkGsL+3NJok8UKHdpuErmmQIMk5QKBgQCgEWcYtLXC3YDYMFdI
UgcTebFqSs3mLYgub1xekW3IXR2yom4V5fQTLiF7Yfn2dpDW4IcMU0UJFYVsUlhK
aGtet4Vm5Nn8+Mghot5yAjqO9yAUaub7wgifKIe99tQKd8uZyCvJ0hhvmDDSfx4m
B/TEiFAO99yF49iSxEVSAS6pqQ==
-----END PRIVATE KEY-----

View file

@ -46,24 +46,21 @@ class DockerLabelHandler:
class HaproxyConfigGenerator: class HaproxyConfigGenerator:
def __init__(self, mapping, ssl_cert_folder="/certs"): def __init__(self, mapping):
self.mapping = mapping self.mapping = mapping
self.mapping.setdefault("ssl_mode", 'default') self.mapping.setdefault("ssl_mode", 'default')
self.mapping.setdefault("letsencrypt", {"email": ""}) self.mapping.setdefault("letsencrypt", {"email": ""})
self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower() self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower()
self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy") self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy")
self.ssl_cert_haproxy = ssl_cert_folder + "/haproxy"
self.ssl_cert_letsecncrypt = ssl_cert_folder + "/letsencrypt"
self.letsencrypt_hosts = [] self.letsencrypt_hosts = []
os.makedirs(self.ssl_cert_haproxy, exist_ok=True) self.serving_hosts = []
os.makedirs(self.ssl_cert_letsecncrypt, exist_ok=True) self.certs = {}
def generate(self, line_list = []): def generate(self, container_metadata = None):
self.mapping.setdefault("easymapping", []) self.mapping.setdefault("easymapping", [])
# static? if container_metadata is not None:
if len(line_list) > 0: self.mapping["easymapping"] = self.parse(container_metadata)
self.mapping["easymapping"] = self.parse(line_list)
file_loader = FileSystemLoader('templates') file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader) env = Environment(loader=file_loader)
@ -74,15 +71,11 @@ class HaproxyConfigGenerator:
return template.render(data=self.mapping) return template.render(data=self.mapping)
def parse(self, line_list): def parse(self, container_metadata):
easymapping = dict() easymapping = dict()
for line in line_list: for container in container_metadata:
line = line.strip() d = container_metadata[container]
i = line.find("=")
container = line[:i]
json_str = line[i+1:]
d = json.loads(json_str)
# Extract the definitions dynamically # Extract the definitions dynamically
definitions = {} definitions = {}
@ -140,6 +133,7 @@ class HaproxyConfigGenerator:
for hostname in d[host_label].split(","): for hostname in d[host_label].split(","):
hostname = hostname.strip() hostname = hostname.strip()
self.serving_hosts.append("%s:%s" % (hostname, port))
easymapping[port]["hosts"].setdefault(hostname, {}) easymapping[port]["hosts"].setdefault(hostname, {})
easymapping[port]["hosts"][hostname].setdefault("containers", []) easymapping[port]["hosts"][hostname].setdefault("containers", [])
easymapping[port]["hosts"][hostname].setdefault("letsencrypt", False) easymapping[port]["hosts"][hostname].setdefault("letsencrypt", False)
@ -172,14 +166,10 @@ class HaproxyConfigGenerator:
# handle SSL # handle SSL
ssl_label = self.label.create([definition, "sslcert"]) ssl_label = self.label.create([definition, "sslcert"])
if self.label.has_label(ssl_label): if self.label.has_label(ssl_label):
filename = "{}/{}.pem".format( filename = "{}.pem".format(d[host_label])
self.ssl_cert_haproxy, d[host_label]
)
easymapping[port]["ssl"] = True easymapping[port]["ssl"] = True
with open(filename, 'wb') as file: self.certs[filename] = base64.b64decode(d[ssl_label]).decode('ascii')
file.write(
base64.b64decode(d[ssl_label])
)
if self.label.get_bool(self.label.create([definition, "ssl"])): if self.label.get_bool(self.label.create([definition, "ssl"])):
easymapping[port]["ssl"] = True easymapping[port]["ssl"] = True

191
src/functions/__init__.py Normal file
View file

@ -0,0 +1,191 @@
from datetime import datetime
from multiprocessing import Process, Lock
import subprocess
import shlex
import time
import os
class Functions:
@staticmethod
def load(filename):
with open(filename, 'r') as content_file:
return content_file.read()
@staticmethod
def save(filename, contents):
with open(filename, 'w') as file:
file.write(contents)
@staticmethod
def log(source, level, message):
if message is None or message == "":
return
if not isinstance(message, (list, tuple)):
message = [message]
for line in message:
print("[%s] %s [%s]: %s" % (source, datetime.now().strftime("%x %X"), level, line.rstrip()))
@staticmethod
def run_bash(source, command, log_output=True, return_result=True):
if not isinstance(command, (list, tuple)):
command = shlex.split(command)
try:
process = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
output = []
while True:
line = process.stdout.readline().rstrip()
output.append(line) if return_result else None
Functions.log(source, "info", line) if log_output else None
Functions.log(source, "warning", process.stderr.readline())
return_code = process.poll()
if return_code is not None:
lines = []
for line in process.stdout.readlines():
output.append(line.rstrip()) if return_result else None
lines.append(line.rstrip())
Functions.log(source, "info", lines) if log_output else None
Functions.log(source, "warning", process.stderr.readlines())
break
return output
except Exception as e:
Functions.log(source, 'error', "%s" % (e))
class DaemonizeHAProxy:
def __init__(self):
self.process = None
self.thread = None
def haproxy(self, action):
if action == "start":
self.__prepare("/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock")
else:
pid = "".join(Functions().run_bash("HAPROXY", "cat /run/haproxy.pid", log_output=False))
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:
return
self.thread = Process(target=self.__start, args=())
self.thread.start()
def __prepare(self, command):
source = "HAPROXY"
if not isinstance(command, (list, tuple)):
command = shlex.split(command)
try:
self.process = subprocess.Popen(command,
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=-1,
universal_newlines=True)
except Exception as e:
Functions.log(source, 'error', "%s" % (e))
def __start(self):
source = "HAPROXY"
try:
with self.process.stdout:
for line in iter(self.process.stdout.readline, b''):
Functions.log(source, "info", line)
returncode = self.process.wait()
Functions.log(source, "info", "Return code %s" % (returncode))
except Exception as e:
Functions.log(source, 'error', "%s" % (e))
def is_alive(self):
return self.thread.is_alive()
def kill(self):
self.process.kill()
self.thread.kill()
def terminate(self):
self.process.terminate()
self.thread.terminate()
class Certbot:
def __init__(self, certs, email):
self.certs = certs
self.email = email
def check_certificates(self, hosts):
if self.email == "" or len(hosts) == 0:
return False
try:
request_certs = []
renew_certs = []
current_time = time.time()
for host in hosts:
filename = "%s/%s.pem" % (self.certs, host)
host_arg = '-d %s' % (host)
if not os.path.exists(filename):
Functions.log("CERTBOT", "info", "Request new certificate for %s" % (host))
request_certs.append(host_arg)
else:
creation_time = os.path.getctime(filename)
if (current_time - creation_time) // (24 * 3600) > 90:
Functions.log("CERTBOT", "info", "Request expired certificate for %s" % (host))
request_certs.append(host_arg)
if (current_time - creation_time) // (24 * 3600) >= 45:
Functions.log("CERTBOT", "info", "Renew certificate for %s" % (host))
renew_certs.append(host_arg)
certbot_certonly = ('/usr/bin/certbot certonly '
' --standalone'
' --preferred-challenges http'
' --http-01-port 2080'
' --agree-tos'
' --issuance-timeout 90'
' --no-eff-email'
' --non-interactive'
' --max-log-backups=0'
' %s --email %s' % (' '.join(request_certs), self.email)
)
ret_reload = False
if len(request_certs) > 0:
Functions.run_bash("CERTBOT", certbot_certonly, return_result=False)
ret_reload = True
if len(renew_certs) > 0:
Functions.run_bash("CERTBOT", "/usb/bin/certbot renew", return_result=False)
ret_reload = True
if ret_reload:
self.find_live_certificates()
return ret_reload
except Exception as e:
Functions.log("CERTBOT", "error", "%s" % (e))
return False
def merge_certificate(self, cert, key, filename):
Functions.save(filename, cert + key)
def find_live_certificates(self):
letsencrypt_certs = "/etc/letsencrypt/live/"
for item in os.listdir(letsencrypt_certs):
path = os.path.join(letsencrypt_certs, item)
if os.path.isdir(path):
cert = Functions.load(os.path.join(path, "fullchain.pem"))
key = Functions.load(os.path.join(path, "privkey.pem"))
filename = "%s/%s.pem" % (self.certs, item)
self.merge_certificate(cert, key, filename)

76
src/main.py Normal file
View file

@ -0,0 +1,76 @@
from functions import Functions, DaemonizeHAProxy, Certbot
from processor import ProcessorInterface
import os
import time
from deepdiff import DeepDiff
easyhaproxy_config = "/etc/haproxy/easyconfig.yml"
haproxy_config = "/etc/haproxy/haproxy.cfg"
certs_letsencrypt = "/certs/letsencrypt"
certs_haproxy = "/certs/haproxy"
def start():
processor_obj = ProcessorInterface.factory(os.getenv("EASYHAPROXY_DISCOVER"))
if processor_obj is None:
exit(1)
os.makedirs(certs_letsencrypt, exist_ok=True)
os.makedirs(certs_haproxy, exist_ok=True)
processor_obj.save_config(haproxy_config)
processor_obj.save_certs(certs_haproxy)
letsencrypt_certs_found = processor_obj.get_letsencrypt_hosts()
Functions.log('EASYHAPROXY', 'info', 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
old_haproxy = None
haproxy = DaemonizeHAProxy()
haproxy.haproxy("start")
certbot = Certbot(certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"))
while True:
time.sleep(10)
if old_haproxy is not None:
old_haproxy.kill()
old_haproxy = None
try:
old_parsed = processor_obj.get_parsed_object()
processor_obj.refresh()
if DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive():
Functions.log('EASYHAPROXY', 'info', 'New configuration found. Reloading...')
processor_obj.save_config(haproxy_config)
processor_obj.save_certs(certs_haproxy)
letsencrypt_certs_found = processor_obj.get_letsencrypt_hosts()
Functions.log('EASYHAPROXY', 'info', 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
old_haproxy = haproxy
haproxy = DaemonizeHAProxy()
haproxy.haproxy("reload")
old_haproxy.terminate()
certbot.check_certificates(letsencrypt_certs_found)
except Exception as e:
Functions.log('EASYHAPROXY', 'error', "Err: %s" % (e))
Functions.log('EASYHAPROXY', 'info', 'Heartbeat')
def main():
Functions.run_bash('INIT', '/usr/sbin/haproxy -v')
Functions.log('INIT', 'info', " _ ")
Functions.log('INIT', 'info', " ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ ")
Functions.log('INIT', 'info', "/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |")
Functions.log('INIT', 'info', "\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |")
Functions.log('INIT', 'info', " |__/ |_| |__/ ")
Functions.log('INIT', 'info', "Release: %s" % (os.getenv("RELEASE_VERSION")))
Functions.log('INIT', 'info', 'Environment:')
for name, value in os.environ.items():
if "HAPROXY" in name:
Functions.log('INIT', 'info', "- {0}: {1}".format(name, value))
start()
if __name__ == '__main__':
main()

173
src/processor/__init__.py Normal file
View file

@ -0,0 +1,173 @@
from easymapping import HaproxyConfigGenerator
from functions import Functions
import yaml
import sys
import os
import json
import docker
from kubernetes import client, config
from kubernetes.client.rest import ApiException
class ContainerEnv:
@staticmethod
def read():
env_vars = {
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default")
}
if os.getenv("HAPROXY_PASSWORD"):
env_vars["stats"] = {
"username": os.getenv("HAPROXY_USERNAME") if os.getenv("HAPROXY_USERNAME") else "admin",
"password": os.getenv("HAPROXY_PASSWORD"),
"port": os.getenv("HAPROXY_STATS_PORT") if os.getenv("HAPROXY_STATS_PORT") else "1936",
}
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")
}
return env_vars
class ProcessorInterface:
def __init__(self, filename = None):
self.filename = filename
self.refresh()
@staticmethod
def factory(mode):
if mode == "static":
return Static("/etc/haproxy/easyconfig.yml")
elif mode == "docker":
return Docker()
elif mode == "swarm":
return Swarm()
elif mode == "kubernetes":
return Kubernetes()
else:
Functions.log("FACTORY", "error", "Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got '%s'" % (mode))
return None
def refresh(self):
self.letsencrypt_hosts = None
self.parsed_object = None
self.cfg = None
self.hosts = None
self.inspect_network()
self.parse()
def inspect_network(self):
#Abstract
pass
def parse(self):
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
def get_letsencrypt_hosts(self):
return self.letsencrypt_hosts
def get_hosts(self):
return self.hosts
def get_parsed_object(self):
return self.parsed_object
def get_certs(self, key = None):
if key is None:
return self.cfg.certs
else:
return None if key not in self.cfg.certs else self.cfg.certs[key]
def get_haproxy_conf(self):
conf = self.cfg.generate(self.parsed_object)
self.letsencrypt_hosts = self.cfg.letsencrypt_hosts
self.hosts = self.cfg.serving_hosts
return conf
def save_config(self, filename):
Functions.save(filename, self.get_haproxy_conf())
def save_certs(self, path):
for cert in self.get_certs():
Functions.save("{0}/{1}".format(path, cert), self.get_certs(cert))
class Static(ProcessorInterface):
def inspect_network(self):
self.parsed_object = {}
def parse(self):
static_content = yaml.load(Functions.load(self.filename), Loader=yaml.FullLoader)
self.cfg = HaproxyConfigGenerator(static_content)
class Docker(ProcessorInterface):
def __init__(self, filename = None):
self.client = docker.from_env()
super().__init__()
def inspect_network(self):
self.parsed_object = {}
for container in self.client.containers.list():
self.parsed_object[container.name] = container.labels
class Swarm(ProcessorInterface):
def __init__(self, filename = None):
self.client = docker.from_env()
super().__init__()
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"]
class Kubernetes(ProcessorInterface):
def __init__(self, filename = None):
config.load_incluster_config()
self.api_instance = client.CoreV1Api()
self.v1 = client.NetworkingV1Api()
super().__init__()
def inspect_network(self):
ret = self.v1.list_ingress_for_all_namespaces(watch=False)
self.parsed_object = {}
for i in ret.items:
if i.metadata.annotations['kubernetes.io/ingress.class'] != "easyhaproxy-ingress":
continue
data = {}
#ingress_name = i.metadata.name
data["creation_timestamp"] = i.metadata.creation_timestamp.strftime("%x %X")
data["resource_version"] = i.metadata.resource_version
data["namespace"] = i.metadata.namespace
for rule in i.spec.rules:
rule_data = {}
port_number = rule.http.paths[0].backend.service.port.number
definition = rule.host.replace(".", "-")
rule_data["easyhaproxy.%s_%s.host" % (definition, port_number)] = rule.host
rule_data["easyhaproxy.%s_%s.port" % (definition, port_number)] = "80"
rule_data["easyhaproxy.%s_%s.localport" % (definition, port_number)] = port_number
service_name = rule.http.paths[0].backend.service.name
try:
api_response = self.api_instance.read_namespaced_service(service_name, i.metadata.namespace)
cluster_ip = api_response.spec.cluster_ip
except ApiException as e:
cluster_ip = None
# print("Exception when calling CoreV1Api->read_namespaced_service: %s\n" % e)
if cluster_ip is not None:
if cluster_ip not in self.parsed_object.keys():
self.parsed_object[cluster_ip] = data
self.parsed_object[cluster_ip].update(rule_data)

View file

@ -2,4 +2,6 @@ pyyaml
docker docker
jinja2 jinja2
pytest pytest
docker
kubernetes kubernetes
deepdiff

View file

@ -78,3 +78,4 @@ backend srv_{{ host }}
backend letsencrypt_backend backend letsencrypt_backend
mode http mode http
server certbot 127.0.0.1:2080 server certbot 127.0.0.1:2080

5
src/tests/fixtures/no-services vendored Normal file
View file

@ -0,0 +1,5 @@
{"swarm-prom_caddy": {"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"},
"swarm-prom_cadvisor": {"com.docker.stack.image":"google/cadvisor","com.docker.stack.namespace":"swarm-prom"},
"swarm-prom_dockerd-exporter": {"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"},
"swarm-prom_unsee": {"com.docker.stack.image":"cloudflare/unsee:v0.8.0","com.docker.stack.namespace":"swarm-prom"},
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy","com.docker.stack.namespace":"test"}}

6
src/tests/fixtures/services vendored Normal file
View file

@ -0,0 +1,6 @@
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
"my-stack_agent": {"easyhaproxy.agent.host":"agent.quantum.example.org","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":"my-stack","com.planetary-quantum":"monitoring"},
"my-stack_cadvisor": {"easyhaproxy.cadvisor.host":"cadvisor.quantum.example.org","easyhaproxy.cadvisor.localport":"8080","easyhaproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
"my-stack_node-exporter": {"easyhaproxy.exp.host":"node-exporter.quantum.example.org","easyhaproxy.exp.localport":"9100","easyhaproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","easyhaproxy.exp.letsencrypt":"true"},
"my-stack_reverse-proxy": {"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
"some-service": {"easyhaproxy.http.port":"80","easyhaproxy.http.host":"www.somehost.com.br","easyhaproxy.http.localport":"80","easyhaproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.port":"443","easyhaproxy.https.host":"www.somehost.com.br","easyhaproxy.https.localport":"80","easyhaproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}}

View file

@ -0,0 +1,6 @@
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
"my-stack_agent": {"haproxy.agent.host":"agent.quantum.example.org","haproxy.agent.localport":"9001","haproxy.agent.mode":"tcp","haproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
"my-stack_cadvisor": {"haproxy.cadvisor.host":"cadvisor.quantum.example.org","haproxy.cadvisor.localport":"8080","haproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
"my-stack_node-exporter": {"haproxy.exp.host":"node-exporter.quantum.example.org","haproxy.exp.localport":"9100","haproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","haproxy.exp.letsencrypt":"yes"},
"my-stack_reverse-proxy": {"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
"some-service": {"haproxy.http.port":"80","haproxy.http.host":"www.somehost.com.br","haproxy.http.localport":"80","haproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.port":"443","haproxy.https.host":"www.somehost.com.br","haproxy.https.localport":"80","haproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}}

View file

@ -0,0 +1,4 @@
{"f5c645a0dfc6": {"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"},
"bbd4d1854155": {"com.docker.compose.config-hash":"3dc790bf2bea944359c75a40c45655bd868f1d85beb599d1ca797e8ea2c95ee4","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:0fd95b1512c207048ab3fcc74032354f38143fbb8235ac2a47da903c98a58205","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"},
"b63438410b6a": {"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"},
"83d57d592e26": {"com.docker.compose.config-hash":"8c5871144f1e8a3aeca037207c02f011ab2c6e6c311a3773602b63541762dab5","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:c4232396c715f3d568816c666e6d9b4a68ef6c36f6243b4007c4ee1d8335fd65","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"static","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test2.example.org","easyhaproxy.http.localport":"8080","easyhaproxy.http.port":"80","io.buildah.version":"1.21.0"}}

View file

@ -0,0 +1,2 @@
{"test_nginx.2.t5r94mjlced7m3t5orfjbowmm": {"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"},
"test_nginx.1.p552hqxkdx88narjrp5kouwb2": {"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"}}

View file

@ -0,0 +1,3 @@
{"db79d3a910f4": {"com.docker.compose.config-hash":"5bde40f52451521ad201e70de1291397376a0498a7c955624a609da3b60e7e8e","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:ea39067705590557dd0cd951664a10970ceefcb725a3c1f43690d6d6d4ed5fce","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"},
"3e63154954b0": {"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"},
"eb294c110eb1": {"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"}}

File diff suppressed because one or more lines are too long

2
src/tests/fixtures/services-tcp vendored Normal file
View file

@ -0,0 +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_proxy": {"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}}

View file

@ -2,6 +2,7 @@ import easymapping
import pytest import pytest
import os import os
import yaml import yaml
import json
CERTS_FOLDER="/tmp/certs" CERTS_FOLDER="/tmp/certs"
CERT_FILE="/tmp/certs/haproxy/www.somehost.com.br.pem" CERT_FILE="/tmp/certs/haproxy/www.somehost.com.br.pem"
@ -10,7 +11,7 @@ LETSENCRYPT_EMAIL="some@email.com"
def load_fixture(file): def load_fixture(file):
path = os.path.dirname(os.path.realpath(__file__)) path = os.path.dirname(os.path.realpath(__file__))
with open(path + "/fixtures/" + file, 'r') as content_file: with open(path + "/fixtures/" + file, 'r') as content_file:
line_list = content_file.readlines() line_list = json.loads("".join(content_file.readlines()))
return line_list return line_list
@ -25,7 +26,7 @@ def test_parser_doesnt_crash():
} }
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -47,10 +48,7 @@ def test_parser_finds_services():
} }
} }
if os.path.exists(CERT_FILE): cfg = easymapping.HaproxyConfigGenerator(result)
os.remove(CERT_FILE)
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -58,8 +56,7 @@ def test_parser_finds_services():
with open(path + "/expected/services.txt", 'r') as expected_file: with open(path + "/expected/services.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config assert expected_file.read() == haproxy_config
with open(CERT_FILE, 'r') as expected_file: assert {"www.somehost.com.br.pem":"Some PEM Certificate"} == cfg.certs
assert expected_file.read() == "Some PEM Certificate"
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
@ -80,7 +77,7 @@ def test_parser_finds_services_changed_label():
if os.path.exists(CERT_FILE): if os.path.exists(CERT_FILE):
os.remove(CERT_FILE) os.remove(CERT_FILE)
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -88,8 +85,7 @@ def test_parser_finds_services_changed_label():
with open(path + "/expected/services.txt", 'r') as expected_file: with open(path + "/expected/services.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config assert expected_file.read() == haproxy_config
with open(CERT_FILE, 'r') as expected_file: assert {"www.somehost.com.br.pem":"Some PEM Certificate"} == cfg.certs
assert expected_file.read() == "Some PEM Certificate"
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
@ -109,7 +105,7 @@ def test_parser_finds_services_raw():
if os.path.exists(CERT_FILE): if os.path.exists(CERT_FILE):
os.remove(CERT_FILE) os.remove(CERT_FILE)
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
parsed_object = [ parsed_object = [
{ {
@ -217,7 +213,7 @@ def test_parser_static():
with open(path + "/fixtures/static.yml", 'r') as content_file: with open(path + "/fixtures/static.yml", 'r') as content_file:
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader) parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
cfg = easymapping.HaproxyConfigGenerator(parsed, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(parsed)
haproxy_config = cfg.generate() haproxy_config = cfg.generate()
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -295,7 +291,7 @@ def test_parser_tcp():
} }
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
# print(haproxy_config) # print(haproxy_config)
@ -315,7 +311,7 @@ def test_parser_multi_containers():
} }
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -337,7 +333,7 @@ def test_parser_multiple_hosts():
} }
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -358,7 +354,7 @@ def test_parser_redirect_ssl():
} }
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -379,7 +375,7 @@ def test_parser_ssl_strict():
} }
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -396,7 +392,7 @@ def test_parser_ssl_loose():
"ssl_mode": "loose", "ssl_mode": "loose",
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -418,7 +414,7 @@ def test_parser_ssl_letsencrypt():
} }
} }
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) cfg = easymapping.HaproxyConfigGenerator(result)
haproxy_config = cfg.generate(line_list) haproxy_config = cfg.generate(line_list)
assert len(haproxy_config) > 0 assert len(haproxy_config) > 0
@ -431,3 +427,4 @@ def test_parser_ssl_letsencrypt():
#test_parser_tcp() #test_parser_tcp()
#test_parser_multiple_hosts() #test_parser_multiple_hosts()
#test_parser_ssl_letsencrypt() #test_parser_ssl_letsencrypt()
#test_parser_finds_services()

View file

@ -1,5 +0,0 @@
swarm-prom_caddy={"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"}
swarm-prom_cadvisor={"com.docker.stack.image":"google/cadvisor","com.docker.stack.namespace":"swarm-prom"}
swarm-prom_dockerd-exporter={"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"}
swarm-prom_unsee={"com.docker.stack.image":"cloudflare/unsee:v0.8.0","com.docker.stack.namespace":"swarm-prom"}
test_proxy={"com.docker.stack.image":"byjg/easy-haproxy","com.docker.stack.namespace":"test"}

View file

@ -1,6 +0,0 @@
portainer-agent_agent={"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"}
my-stack_agent={"easyhaproxy.agent.host":"agent.quantum.example.org","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":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_cadvisor={"easyhaproxy.cadvisor.host":"cadvisor.quantum.example.org","easyhaproxy.cadvisor.localport":"8080","easyhaproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_node-exporter={"easyhaproxy.exp.host":"node-exporter.quantum.example.org","easyhaproxy.exp.localport":"9100","easyhaproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","easyhaproxy.exp.letsencrypt":"true"}
my-stack_reverse-proxy={"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
some-service={"easyhaproxy.http.port":"80","easyhaproxy.http.host":"www.somehost.com.br","easyhaproxy.http.localport":"80","easyhaproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.port":"443","easyhaproxy.https.host":"www.somehost.com.br","easyhaproxy.https.localport":"80","easyhaproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}

View file

@ -1,6 +0,0 @@
portainer-agent_agent={"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"}
my-stack_agent={"haproxy.agent.host":"agent.quantum.example.org","haproxy.agent.localport":"9001","haproxy.agent.mode":"tcp","haproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_cadvisor={"haproxy.cadvisor.host":"cadvisor.quantum.example.org","haproxy.cadvisor.localport":"8080","haproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_node-exporter={"haproxy.exp.host":"node-exporter.quantum.example.org","haproxy.exp.localport":"9100","haproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","haproxy.exp.letsencrypt":"yes"}
my-stack_reverse-proxy={"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
some-service={"haproxy.http.port":"80","haproxy.http.host":"www.somehost.com.br","haproxy.http.localport":"80","haproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.port":"443","haproxy.https.host":"www.somehost.com.br","haproxy.https.localport":"80","haproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}

View file

@ -1,4 +0,0 @@
f5c645a0dfc6={"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"}
bbd4d1854155={"com.docker.compose.config-hash":"3dc790bf2bea944359c75a40c45655bd868f1d85beb599d1ca797e8ea2c95ee4","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:0fd95b1512c207048ab3fcc74032354f38143fbb8235ac2a47da903c98a58205","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"}
b63438410b6a={"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"}
83d57d592e26={"com.docker.compose.config-hash":"8c5871144f1e8a3aeca037207c02f011ab2c6e6c311a3773602b63541762dab5","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:c4232396c715f3d568816c666e6d9b4a68ef6c36f6243b4007c4ee1d8335fd65","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"static","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test2.example.org","easyhaproxy.http.localport":"8080","easyhaproxy.http.port":"80","io.buildah.version":"1.21.0"}

View file

@ -1,2 +0,0 @@
test_nginx.2.t5r94mjlced7m3t5orfjbowmm={"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"}
test_nginx.1.p552hqxkdx88narjrp5kouwb2={"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"}

View file

@ -1,3 +0,0 @@
db79d3a910f4={"com.docker.compose.config-hash":"5bde40f52451521ad201e70de1291397376a0498a7c955624a609da3b60e7e8e","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:ea39067705590557dd0cd951664a10970ceefcb725a3c1f43690d6d6d4ed5fce","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"}
3e63154954b0={"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"}
eb294c110eb1={"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"}

File diff suppressed because one or more lines are too long

View file

@ -1,2 +0,0 @@
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_proxy={"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}