diff --git a/.gitpod.yml b/.gitpod.yml index 2b37d88..b763650 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -6,4 +6,4 @@ tasks: - command: | virtualenv -p /usr/bin/python3 venv source venv/bin/activate - pip install -r requirements.txt + pip install -r src/requirements.txt diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ed1f6e6..0000000 --- a/.travis.yml +++ /dev/null @@ -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" diff --git a/.vscode/launch.json b/.vscode/launch.json index 428608e..9cb10c6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,9 +10,9 @@ "request": "launch", "program": "${file}", "console": "integratedTerminal", - "cwd": "${workspaceFolder}", + "cwd": "${workspaceFolder}/src", "env": { - "PYTHONPATH": "${cwd}" + "PYTHONPATH": "${cwd}/src" } } ] diff --git a/Dockerfile b/Dockerfile index 9cb2339..fb29e07 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,18 +6,15 @@ ENV RELEASE_VERSION=$RELEASE_VERSION_ARG WORKDIR /scripts -COPY requirements.txt /scripts -COPY templates /scripts/templates/ -COPY easymapping /scripts/easymapping/ -COPY tests/ /scripts/tests/ +COPY src/ /scripts/ 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 \ && pip3 install --upgrade pip \ && 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-1024 1024 -CMD ["/bin/bash", "-c", "/scripts/haproxy.sh" ] +CMD ["/usr/bin/python", "-u", "/scripts/main.py" ] diff --git a/README.md b/README.md index ec14cbf..0f457b4 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,10 @@ services: ... ``` +```bash +docker stack deploy --compose-file docker-compose.yml mystack +``` + ### Single Definition ```bash diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 3935400..0000000 --- a/_config.yml +++ /dev/null @@ -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 - diff --git a/assets/scripts/banner.txt b/assets/scripts/banner.txt deleted file mode 100644 index f897aab..0000000 --- a/assets/scripts/banner.txt +++ /dev/null @@ -1,5 +0,0 @@ - _ - ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ -/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || | -\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, | - |__/ |_| |__/ diff --git a/assets/scripts/certbot.sh b/assets/scripts/certbot.sh deleted file mode 100755 index 8678231..0000000 --- a/assets/scripts/certbot.sh +++ /dev/null @@ -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 diff --git a/assets/scripts/certbot_to_haproxy.sh b/assets/scripts/certbot_to_haproxy.sh deleted file mode 100755 index 532a265..0000000 --- a/assets/scripts/certbot_to_haproxy.sh +++ /dev/null @@ -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 \ No newline at end of file diff --git a/assets/scripts/functions.sh b/assets/scripts/functions.sh deleted file mode 100644 index e90e730..0000000 --- a/assets/scripts/functions.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -function log() { - # ARGS: - # - loglevel - # - app - # - message - echo [$2] $(date +"$EASYHAPROXY_DATEFORMAT") [$1]: $3 -} diff --git a/assets/scripts/haproxy-reload.sh b/assets/scripts/haproxy-reload.sh deleted file mode 100755 index 7cd3822..0000000 --- a/assets/scripts/haproxy-reload.sh +++ /dev/null @@ -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 diff --git a/assets/scripts/haproxy.sh b/assets/scripts/haproxy.sh deleted file mode 100755 index 92c15e5..0000000 --- a/assets/scripts/haproxy.sh +++ /dev/null @@ -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 diff --git a/assets/scripts/k8s.py b/assets/scripts/k8s.py deleted file mode 100644 index 8ebc19e..0000000 --- a/assets/scripts/k8s.py +++ /dev/null @@ -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() - diff --git a/assets/scripts/static.py b/assets/scripts/static.py deleted file mode 100644 index 2e36cc6..0000000 --- a/assets/scripts/static.py +++ /dev/null @@ -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) diff --git a/assets/scripts/swarm.py b/assets/scripts/swarm.py deleted file mode 100644 index 52c1a73..0000000 --- a/assets/scripts/swarm.py +++ /dev/null @@ -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) - - diff --git a/kubernetes/easyhaproxy.yml b/examples/kubernetes/easyhaproxy.yml similarity index 80% rename from kubernetes/easyhaproxy.yml rename to examples/kubernetes/easyhaproxy.yml index 000c238..2aba608 100644 --- a/kubernetes/easyhaproxy.yml +++ b/examples/kubernetes/easyhaproxy.yml @@ -38,17 +38,7 @@ spec: requests: cpu: "100m" memory: "128Mi" - #limits: - # cpu: "1" - # memory: "1Gi" - #securityContext: - # allowPrivilegeEscalation: true - # #runAsUser: 101 #nginx - # capabilities: - # drop: - # - ALL - # add: - # - NET_BIND_SERVICE + env: - name: EASYHAPROXY_DISCOVER value: kubernetes diff --git a/examples/kubernetes/service.yml b/examples/kubernetes/service.yml new file mode 100644 index 0000000..d98399f --- /dev/null +++ b/examples/kubernetes/service.yml @@ -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" diff --git a/examples/swarm/docker-compose.yml b/examples/swarm/docker-compose.yml new file mode 100644 index 0000000..d9000bf --- /dev/null +++ b/examples/swarm/docker-compose.yml @@ -0,0 +1,67 @@ +# To test: +# curl -k -H "Host: host1.local" https://127.0.0.1/ +# curl -k -H "Host: host2.local" https://127.0.0.1/ +# +# curl -I -H Host:host1.local http://127.0.0.1 +# HTTP/1.1 301 Moved Permanently +# content-length: 0 +# location: https://host1.local/ +# +# curl -I -H Host:host2.local http://127.0.0.1 +# HTTP/1.1 301 Moved Permanently +# content-length: 0 +# location: https://host1.local/ +# +# Test SSL: +# openssl s_client -showcerts -connect 127.0.0.1:443 --servername host1.local + +version: "3" + +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: + image: byjg/static-httpserver + deploy: + replicas: 1 + labels: + easyhaproxy.http.redirect_ssl: "true" + easyhaproxy.http.host: host1.local + easyhaproxy.http.port: 80 + + easyhaproxy.https.port: 443 + easyhaproxy.https.localport: 8080 + easyhaproxy.https.host: host1.local + easyhaproxy.https.sslcert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZEVENDQXZXZ0F3SUJBZ0lVUmkrdzFaVmdlZWRUbE5JQXdxUUJNSnY2ZFhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xhRzl6ZERFdWJHOWpZV3d3SGhjTk1qRXdPREV3TVRnME9UQTJXaGNOTXpFdwpPREE0TVRnME9UQTJXakFXTVJRd0VnWURWUVFEREF0b2IzTjBNUzVzYjJOaGJEQ0NBaUl3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFNQkRBaExBeWdKdWFXNnc2ZmZpZ3pUQUFHWHBtRXowdEl4bjFrNFoKeDV3TjVycHYvcXUwUU1ZeitBdjJ1MWVPS0VLWmVhRlJWcFQwcjkzZFg3SXZiRVpIdDI1R1BpQnZsTEdxaGpLUgpQblNrLzdVOFhtc250dFVBVjdyVkVLMVVyZEZ3OC9Jd3JpUUMrZGhyMG1uWWZTRE1rdkJvTUZwZGhWTlRyYkFaCjFUQjZyUWpFN0FyME10OG15OTZYSm13cmNqSzJUaitFMnJnUElVejFlNWNla0ZZSURTQmF0bXcrMyt2citUNXgKRk5Ga0oybzMwVzVvOFpmbENKSnpyVmFpaHFRaWNzNlpLRGdwZjdpcVhNRml3V0lsaGRRcEd2eDVHZi9LRlRLOQpVYU9uUlp6L1grMkNlYkFGYVRIUjNrL1BZcHBXVGdCQkJ1UnZscEN3K3dkbmttdGVDMFNRUkY5MVFXVnI3ZWpvCjdLYU9sR0k1VnR2TVVzV3ZUZUFabXBheW1JYUFURVR1T0phWTBKVTExT21MZUQ5RE9qNUUyU1E3cUlYL3BGY3AKeHB6RzVqNGMrTWxndnhQMlZBa05UZUFYQ2FZaVBCUUg1WlpnMEhFMlduQjFLaExSRmxIZDRpSFFEMkdKNXlOLwo2ZkNGQmZaZktTZUs4SmF1d3hnV2tyYTUzT2NEcS9tS2QrREEvZEsrL3J1Rzd0cXdWZ0lhMDRIT3Bsek03TFlSCkdCMElyczkrbHI1L1BKYlFabVUwNzNNZG42Y1hBZzNwKzZ3dndGbERrUzV2MTNnQkRZTkh0RjYyYmM1NTFlZEYKWjZrR3pKN3dtR1JvODRhQlA3TXVSWmVSZUxPclNTNjdhMXdMZHpac01uUDFUSjd4OUxmcjlNS2wydURuUWRuWQpleDhEQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QWZCZ05WCkhTTUVHREFXZ0JTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUNoUVlOdWFoMyttVHBJQkRZeEdyalRKTnVPVElNYVd6TXlpMXRrZitMMApzRUd3cGJtQU8ybVdXUVlGN1dWTHNpOThQVUxoM2FkanQyaml1ZDlWbGFhQzZnbnduNVpvMStQaWxvOXNOTExXCjZpajArck40a3dJbS9wTnFpK2pEdXUyY3ZBdUhJd1pXZWg4YkVlLzVVQ3hvNGlobVdGUU44ZUo2VFVLQ3BoUkMKNkVvci9TU1paQlFIZ1BsMEJjaHpIT2t3dTdSM0xDbmRScXhqaEFvVmI5eVFPVitac21UZUpYdWx3TnpKMXVMdApUOE9JZ0lpRHBtQm83SFNOMkgwazNjaHgwMEFzalV5SjltbUFXUGVqRmUvS1hMUlBjVlpSMTdqaHpnZklCRXpzCk01V3RXRm0xYUhEalZ2Nk02aXRlVm02MUU5VCtrL00xMXJ1MWUyWXdzeFREdmI2eDA0bWNyTnU5c29xZGRCYnIKVmZwbHV1b1EvaEVBYlh0Rk5Qb1R5U3B6MGN3T3djSENvd1ZPTG1kS2d2SW1zelppTXlISEc4VkdHbVBoODhuNwp3VnhiMGdWMFA0Uk1yY01MZGVUZG41NVlRcjFDcUJyMzRlQjZvbDZBc2JUbTNWekJIUlZtRk5rc2wxbzVKQjV0CnRYTGdGL0c4L3J6Si80bTFQYVZ1eHJCN0R4VW1JazhFUGJTSVZrdlp2ZDdMQnpLd1E2SWZWYXVjZXdIZkVhalEKVklpZXhTTWlGYzdsdzNLbnhqT0haamY2Rk05VllnM05vKytHZEM5OXM3TGtJdUp3QU1MTnFUUTdIdmhuN1l2UAo0RmxTSWdjNnhqMFlrR1pFUWxiNW8vNW5hdUVxUVUwQUJndzZqdEk0TnhyTkxUNmNwN0NPNE0weElERWcvM1lECmFBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0KTUlJSlF3SUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NDUzB3Z2drcEFnRUFBb0lDQVFEQVF3SVN3TW9DYm1sdQpzT24zNG9NMHdBQmw2WmhNOUxTTVo5Wk9HY2VjRGVhNmIvNnJ0RURHTS9nTDlydFhqaWhDbVhtaFVWYVU5Sy9kCjNWK3lMMnhHUjdkdVJqNGdiNVN4cW9ZeWtUNTBwUCsxUEY1cko3YlZBRmU2MVJDdFZLM1JjUFB5TUs0a0F2blkKYTlKcDJIMGd6Skx3YURCYVhZVlRVNjJ3R2RVd2VxMEl4T3dLOURMZkpzdmVseVpzSzNJeXRrNC9oTnE0RHlGTQo5WHVYSHBCV0NBMGdXclpzUHQvcjYvaytjUlRSWkNkcU45RnVhUEdYNVFpU2M2MVdvb2FrSW5MT21TZzRLWCs0CnFsekJZc0ZpSllYVUtScjhlUm4veWhVeXZWR2pwMFdjLzEvdGdubXdCV2t4MGQ1UHoyS2FWazRBUVFia2I1YVEKc1BzSFo1SnJYZ3RFa0VSZmRVRmxhKzNvNk95bWpwUmlPVmJiekZMRnIwM2dHWnFXc3BpR2dFeEU3amlXbU5DVgpOZFRwaTNnL1F6bytSTmtrTzZpRi82UlhLY2FjeHVZK0hQakpZTDhUOWxRSkRVM2dGd21tSWp3VUIrV1dZTkJ4Ck5scHdkU29TMFJaUjNlSWgwQTloaWVjamYrbndoUVgyWHlrbml2Q1dyc01ZRnBLMnVkem5BNnY1aW5mZ3dQM1MKdnY2N2h1N2FzRllDR3RPQnpxWmN6T3kyRVJnZENLN1BmcGErZnp5VzBHWmxOTzl6SForbkZ3SU42ZnVzTDhCWgpRNUV1YjlkNEFRMkRSN1JldG0zT2VkWG5SV2VwQnN5ZThKaGthUE9HZ1QrekxrV1hrWGl6cTBrdXUydGNDM2MyCmJESno5VXllOGZTMzYvVENwZHJnNTBIWjJIc2ZBd0lEQVFBQkFvSUNBUUMveFpiWjBjY3RxYWdzcXZhVk5URWUKZXExcStoZmFHdlBFWVFhWUhJcklFKzJpNVhjbkdjTEtjS2ZvZHhEakFuOFIvemdkT3A2Y01YMENWbi9Qb2hIawpBRUR0RTgrQVZ3d0FNMUZzT3dnTEhWR2FHejhxcnhCbFlkUWdIY3BtdWVJdTJQWGJDOGVIVUJpYVVPSXVoYXc1Ci9SUk1EQUMvQWkyc3NmaTdnT2p2VkU0b1F4UVcwUUcxS0dPT0FVSm4vdVlIdzJSRlkyVXUxcGlteE8ya0RPNTMKZ2N4bUMxV09ueUNIbUhhaVcvVWg3ejZKYW1mU000ZFh0VEpac2x5aDM3ZGhIS05iZzlWa1A3Q1FLQTRoTHpvcApoYmY1cVk2cmFyZ09OaW55MUhnTVB4cm13S3VVb3VKeU90TjB5QnR4akRDVU5hWFVCd2l5N3NOR1MrSDR2c3lCCjVQOUhoSUhTdHUrRlp0M0hHN0VJcUNuZGlhU0tEUzRqV2FWUUFiYm80bloyWnMyQkQreERlUFJDUlVxWDdyTTQKNFh6UElSV1dYbW1XZi83SWcyOUhicnA0YTlMY09tUTJsZUNKdGJhVEZTTjk2T0xVSjVFK2hRMHVsQ1pnQlZtUQpSQ1VZa0pQNGxPemJhS2R6anhnSE1ySHptNDVlVUZmOExpck94aTJ1eXhYSFFtRE51NGIzWDE4a3QzUGdVbVVtCjNkWHBsM2ZxU3lKYTdTQ1Y4Wk5CcnNyRHExRSt0aFl0dTkxUWJWU0d4SGQ5SHJOVmUzWGRMYk9DZFU5Q3VDNjkKTmdsem5hYTdzWkxxbXlLZWpUZkdzWTd4cldkTmNNUGw0cDRmY0lEL080RXBBU1pmb3JwVGVLTlQwWklmWlpldwpiMG1BUWVZWnFRTThpL3FNWU4vdUFRS0NBUUVBNXFnMXNSTk1jNlZkTS90UmdsYXNHWW94amdSQzJPcUFEWmdzCm1BWE1VSjNrRXJweXh0K2VDaW15OGlidVlwelJUSVE4ZkJUV1JrQ3RSWlhKNytLY0xWdGs5UVpJb0xiaHlOd2QKNEl4RVFaRnVVbGpEYnZTalRMU3ljc0h2bzY1aWJXSWZUTDdiZ1dsTEdnR3EvVU96ZkdzZ0g2Uzl3THA1RzMwRwo4RUx5akk1ZVRJWUlDcmZUbVZMK2M0NU1ScEVNS28rY3Z6OFB5c2lhT0ZUbjNjeXN3UFZkWWFlRUVxTVFqVTh3CklHTnNHWkx5dFk3QkFCQmNZMGxkcnRiYS9PK0Z2LytSSDd1VXR6UDd4cENJd0ZDeDgwWnpOK1dSeTlOdkk2M1UKenEzeUlCb1c5R3lBcEQyK1BMYVBOeGY3UUxUVUNoWTFaei9kWVJsdEtPeHYyQWE1Z1FLQ0FRRUExV0xXTnFwMApmaEIvWnRmU0VTaHhGTU04OWNqTjZBYXoxV0tMN3VUQm91OW9TSm54amtoa2FWNzZhY25UL2lxWHR4TU5nSGkxCmZJbURwVTNQdk0wWTRVZDJUNDdvSGM2UDFCclpQTi9HbVh5L3M2QkFFZFB3TGU3Sis0blRJU0hBZEdtcmgrYS8KNXBrdHUzMmc5bFdxZnR4ZWNGSVZTTFBXa3hUMFhLaU14cDFmZmtMK09hdnBNZ01GWks0MWlLczNkTlNoS1BvZwpMOEdTUGNQOXgveW43OFAyZUszTitQR2psQTZwUHpyQU55V1U3TjAvYm1IY0I5VEtQK3VkWVdjalZocnU3TVlOCndOckU0a0tkQzh2OGk3eDd0RGJ2Yjc5VCtGbzZQSWg1M3AwT3NuWnpBOFVSMFFOUit2RFF1ZlF1eWFqOFJFQysKWkc4WXlDS3N2azh5Z3dLQ0FRQS9mc1N4QjBmL2VlRXJZeDZ3QzUzNnRlRW9ZQ0hxeHJzVGd2V2JyOVRyeUZzMQprSi95QVRMblIwMWNmYjBYNW1WemM5K1dwTUhMdXhnMzFLRXZhU2xuRHdhK3NNa2pmTlN3ejI5bUZoYmdHZUhOCngyT2RVcmoxYjdURUJJRXNoTi9SanJaaEVSVXFEY3MvMEgrNmtuMkJYWmdOUGZPQ2I1TFJMMXpPblE5YUJBTVAKZThJUStVUEZyR1FoZVdXajgxL3ZBM081N2VreUFJRDd5dHU5WWcrWVdyTW5JODhtdGo3ak40NWZEQitBOXNQYgptUDJtUDlxKzlqNVUyQTZXbkhVc1FuVTMwQktEVUVzYUFVV3o4MExaWG1adlY4SUg0eDl3S2ZVd0pCQklLQVp6CnFMN005N1k3em1Ha1gvU3BmbDMwbk9KOGxzY2hhTGQxRVlsRVphMkJBb0lCQVFDeWUyNVQ0VFY1TUpGdjB6dVoKTUd1TmcxU2MvTzRGa24yZkVVT2NlV2pod1VCSDRjUGpUL2YxRHdXRHNOYUo5TlJieENyNTkzMU9BclBEYzVjOApBNDA0K1k0ak01UkJRa0tabGk5NHRIQW9kK2pjOVVCQjZUVXZKbGw1OVNsTXdDOTY3OXdTMjFaT0tuZlBLR0NYClNzWkdRRXNaeGY2Wmhoc0hnWEozZ2wvbHpVSlBtUGVPQTVZVlIrT2Q5LzA5S0lGRlRvalNmb3luaFZDdUt4NDkKeGI0dVZZbjJIT0o0eEowZlBUZ2hkQ0hNdnJtWGVlUVJqdmI4OGVhTm1xVlVFSEhGRnRnYjRma2xBNWZFN1JUeApCaGxpUkRCd1o3YlVrSU5LNnlWazluNkJUbnM1bU12UkxtZ2RuSnBZdkU3S0MwMkxUYlpiM0krajhDMFpVYStOCnF5N0RBb0lCQUFpZXJpYlM3V1VjbDJhQmxrbTUrVzdxTm0vSU5tNXp2bm9TUG82VjN3YTVoczZmOStDL2tiZEYKODdqUVBBL1lGZTN1UjJzQUo3c2xYNWV1Wks4V21mcEZtZ3psdTBzRXo4MU1MUS9XeXBadFp5dHlWdFd6QjJQdQpYQ1cxdGRTSDllSTJCbWhYZ29rSE5UTTQ4TmsveE9FTnJQL3NlWHJJeDVMSzBobkRIWm90dS96NitZU2tCOWhGCmNtMmZaeWdEMWRNTFg2bGlSaW14eUZZK2RJQ0pOQjk1SmlmVExXWW5XZUdkZGt3UHRYVWVHWEUxb2x6dk5rTEQKek16RTA5dWhreC9sUkpudGVPQkVaYWY4ME9CLzA5T2k5YjkvcnhZNTlkd3NINkdheExvVGZFS3VQbnZCVk1OUgpZa1UxNFd6UUtsZUZraUJKSTlsVnZuZmdHbk9sZ2cwPQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t + + container2: + image: byjg/static-httpserver + deploy: + replicas: 1 + labels: + easyhaproxy.http.host: host2.local + easyhaproxy.http.port: 80 + easyhaproxy.http.redirect_ssl: "true" + + easyhaproxy.https.port: 443 + easyhaproxy.https.localport: 8080 + easyhaproxy.https.host: host2.local + easyhaproxy.https.ssl: "true" + diff --git a/examples/swarm/host2.local.pem b/examples/swarm/host2.local.pem new file mode 100644 index 0000000..917062a --- /dev/null +++ b/examples/swarm/host2.local.pem @@ -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----- \ No newline at end of file diff --git a/easymapping/__init__.py b/src/easymapping/__init__.py similarity index 85% rename from easymapping/__init__.py rename to src/easymapping/__init__.py index a10fa2f..042ca61 100644 --- a/easymapping/__init__.py +++ b/src/easymapping/__init__.py @@ -46,24 +46,21 @@ class DockerLabelHandler: class HaproxyConfigGenerator: - def __init__(self, mapping, ssl_cert_folder="/certs"): + def __init__(self, mapping): self.mapping = mapping self.mapping.setdefault("ssl_mode", 'default') self.mapping.setdefault("letsencrypt", {"email": ""}) self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower() 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 = [] - os.makedirs(self.ssl_cert_haproxy, exist_ok=True) - os.makedirs(self.ssl_cert_letsecncrypt, exist_ok=True) + self.serving_hosts = [] + self.certs = {} - def generate(self, line_list = []): + def generate(self, container_metadata = None): self.mapping.setdefault("easymapping", []) - # static? - if len(line_list) > 0: - self.mapping["easymapping"] = self.parse(line_list) + if container_metadata is not None: + self.mapping["easymapping"] = self.parse(container_metadata) file_loader = FileSystemLoader('templates') env = Environment(loader=file_loader) @@ -74,15 +71,11 @@ class HaproxyConfigGenerator: return template.render(data=self.mapping) - def parse(self, line_list): + def parse(self, container_metadata): easymapping = dict() - for line in line_list: - line = line.strip() - i = line.find("=") - container = line[:i] - json_str = line[i+1:] - d = json.loads(json_str) + for container in container_metadata: + d = container_metadata[container] # Extract the definitions dynamically definitions = {} @@ -140,6 +133,7 @@ class HaproxyConfigGenerator: for hostname in d[host_label].split(","): hostname = hostname.strip() + self.serving_hosts.append("%s:%s" % (hostname, port)) easymapping[port]["hosts"].setdefault(hostname, {}) easymapping[port]["hosts"][hostname].setdefault("containers", []) easymapping[port]["hosts"][hostname].setdefault("letsencrypt", False) @@ -172,14 +166,10 @@ class HaproxyConfigGenerator: # handle SSL ssl_label = self.label.create([definition, "sslcert"]) if self.label.has_label(ssl_label): - filename = "{}/{}.pem".format( - self.ssl_cert_haproxy, d[host_label] - ) + filename = "{}.pem".format(d[host_label]) easymapping[port]["ssl"] = True - with open(filename, 'wb') as file: - file.write( - base64.b64decode(d[ssl_label]) - ) + self.certs[filename] = base64.b64decode(d[ssl_label]).decode('ascii') + if self.label.get_bool(self.label.create([definition, "ssl"])): easymapping[port]["ssl"] = True diff --git a/src/functions/__init__.py b/src/functions/__init__.py new file mode 100644 index 0000000..5d62154 --- /dev/null +++ b/src/functions/__init__.py @@ -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) diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..69bd1c3 --- /dev/null +++ b/src/main.py @@ -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() \ No newline at end of file diff --git a/src/processor/__init__.py b/src/processor/__init__.py new file mode 100644 index 0000000..726b14b --- /dev/null +++ b/src/processor/__init__.py @@ -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) + + + + diff --git a/pytest.ini b/src/pytest.ini similarity index 100% rename from pytest.ini rename to src/pytest.ini diff --git a/requirements.txt b/src/requirements.txt similarity index 51% rename from requirements.txt rename to src/requirements.txt index 7959d6f..5c4ba98 100644 --- a/requirements.txt +++ b/src/requirements.txt @@ -2,4 +2,6 @@ pyyaml docker jinja2 pytest -kubernetes \ No newline at end of file +docker +kubernetes +deepdiff \ No newline at end of file diff --git a/templates/bind.j2 b/src/templates/bind.j2 similarity index 100% rename from templates/bind.j2 rename to src/templates/bind.j2 diff --git a/templates/frontend-mode-http.j2 b/src/templates/frontend-mode-http.j2 similarity index 100% rename from templates/frontend-mode-http.j2 rename to src/templates/frontend-mode-http.j2 diff --git a/templates/frontend-mode-tcp.j2 b/src/templates/frontend-mode-tcp.j2 similarity index 100% rename from templates/frontend-mode-tcp.j2 rename to src/templates/frontend-mode-tcp.j2 diff --git a/templates/haproxy.cfg.j2 b/src/templates/haproxy.cfg.j2 similarity index 99% rename from templates/haproxy.cfg.j2 rename to src/templates/haproxy.cfg.j2 index eda4b8a..a1842b1 100644 --- a/templates/haproxy.cfg.j2 +++ b/src/templates/haproxy.cfg.j2 @@ -78,3 +78,4 @@ backend srv_{{ host }} backend letsencrypt_backend mode http server certbot 127.0.0.1:2080 + diff --git a/templates/ssl_default.j2 b/src/templates/ssl_default.j2 similarity index 100% rename from templates/ssl_default.j2 rename to src/templates/ssl_default.j2 diff --git a/templates/ssl_loose.j2 b/src/templates/ssl_loose.j2 similarity index 100% rename from templates/ssl_loose.j2 rename to src/templates/ssl_loose.j2 diff --git a/templates/ssl_strict.j2 b/src/templates/ssl_strict.j2 similarity index 100% rename from templates/ssl_strict.j2 rename to src/templates/ssl_strict.j2 diff --git a/tests/__init__.py b/src/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to src/tests/__init__.py diff --git a/tests/context.py b/src/tests/context.py similarity index 100% rename from tests/context.py rename to src/tests/context.py diff --git a/tests/expected/no-services.txt b/src/tests/expected/no-services.txt similarity index 97% rename from tests/expected/no-services.txt rename to src/tests/expected/no-services.txt index 428cbca..a95c434 100644 --- a/tests/expected/no-services.txt +++ b/src/tests/expected/no-services.txt @@ -25,4 +25,4 @@ defaults backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/services-letsencrypt.txt b/src/tests/expected/services-letsencrypt.txt similarity index 99% rename from tests/expected/services-letsencrypt.txt rename to src/tests/expected/services-letsencrypt.txt index e0093d1..3e2eafb 100644 --- a/tests/expected/services-letsencrypt.txt +++ b/src/tests/expected/services-letsencrypt.txt @@ -92,4 +92,4 @@ backend srv_test_example_org_443 backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/services-multi-containers.txt b/src/tests/expected/services-multi-containers.txt similarity index 98% rename from tests/expected/services-multi-containers.txt rename to src/tests/expected/services-multi-containers.txt index 00e9196..5910691 100644 --- a/tests/expected/services-multi-containers.txt +++ b/src/tests/expected/services-multi-containers.txt @@ -42,4 +42,4 @@ backend srv_www_helloworld_com_19901 backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/services-multiple-hosts.txt b/src/tests/expected/services-multiple-hosts.txt similarity index 98% rename from tests/expected/services-multiple-hosts.txt rename to src/tests/expected/services-multiple-hosts.txt index cc9c39e..8c356cd 100644 --- a/tests/expected/services-multiple-hosts.txt +++ b/src/tests/expected/services-multiple-hosts.txt @@ -75,4 +75,4 @@ backend srv_www_helloworld_com_19901 backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/services-redirect-ssl.txt b/src/tests/expected/services-redirect-ssl.txt similarity index 99% rename from tests/expected/services-redirect-ssl.txt rename to src/tests/expected/services-redirect-ssl.txt index d0dbb07..077123b 100644 --- a/tests/expected/services-redirect-ssl.txt +++ b/src/tests/expected/services-redirect-ssl.txt @@ -77,4 +77,4 @@ backend srv_host1_local_443 backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/services-tcp.txt b/src/tests/expected/services-tcp.txt similarity index 97% rename from tests/expected/services-tcp.txt rename to src/tests/expected/services-tcp.txt index 6df1a26..fa8fd7a 100644 --- a/tests/expected/services-tcp.txt +++ b/src/tests/expected/services-tcp.txt @@ -39,4 +39,4 @@ backend srv_agent_quantum_local_31339 backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/services.txt b/src/tests/expected/services.txt similarity index 99% rename from tests/expected/services.txt rename to src/tests/expected/services.txt index 43765e6..bf2cecd 100644 --- a/tests/expected/services.txt +++ b/src/tests/expected/services.txt @@ -121,4 +121,4 @@ backend srv_www_somehost_com_br_80 backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/ssl-loose.txt b/src/tests/expected/ssl-loose.txt similarity index 98% rename from tests/expected/ssl-loose.txt rename to src/tests/expected/ssl-loose.txt index 674c9c6..45ac853 100644 --- a/tests/expected/ssl-loose.txt +++ b/src/tests/expected/ssl-loose.txt @@ -35,4 +35,4 @@ backend srv_stats backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/ssl-strict.txt b/src/tests/expected/ssl-strict.txt similarity index 95% rename from tests/expected/ssl-strict.txt rename to src/tests/expected/ssl-strict.txt index 815fd2f..f4e00b4 100644 --- a/tests/expected/ssl-strict.txt +++ b/src/tests/expected/ssl-strict.txt @@ -20,4 +20,4 @@ defaults backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/tests/expected/static.txt b/src/tests/expected/static.txt similarity index 99% rename from tests/expected/static.txt rename to src/tests/expected/static.txt index f3cd653..ea0362f 100644 --- a/tests/expected/static.txt +++ b/src/tests/expected/static.txt @@ -107,4 +107,4 @@ backend srv_host3_com_br_8080 backend letsencrypt_backend mode http - server certbot 127.0.0.1:2080 \ No newline at end of file + server certbot 127.0.0.1:2080 diff --git a/src/tests/fixtures/no-services b/src/tests/fixtures/no-services new file mode 100644 index 0000000..9e1b3c9 --- /dev/null +++ b/src/tests/fixtures/no-services @@ -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"}} diff --git a/src/tests/fixtures/services b/src/tests/fixtures/services new file mode 100644 index 0000000..56ef60a --- /dev/null +++ b/src/tests/fixtures/services @@ -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="}} \ No newline at end of file diff --git a/src/tests/fixtures/services-changed-label b/src/tests/fixtures/services-changed-label new file mode 100644 index 0000000..2a23c24 --- /dev/null +++ b/src/tests/fixtures/services-changed-label @@ -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="}} \ No newline at end of file diff --git a/src/tests/fixtures/services-letsencrypt b/src/tests/fixtures/services-letsencrypt new file mode 100644 index 0000000..9c83781 --- /dev/null +++ b/src/tests/fixtures/services-letsencrypt @@ -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"}} \ No newline at end of file diff --git a/src/tests/fixtures/services-multi-containers b/src/tests/fixtures/services-multi-containers new file mode 100644 index 0000000..4257caa --- /dev/null +++ b/src/tests/fixtures/services-multi-containers @@ -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"}} \ No newline at end of file diff --git a/src/tests/fixtures/services-multiple-hosts b/src/tests/fixtures/services-multiple-hosts new file mode 100644 index 0000000..66db013 --- /dev/null +++ b/src/tests/fixtures/services-multiple-hosts @@ -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\"}"}} \ No newline at end of file diff --git a/src/tests/fixtures/services-redirect-ssl b/src/tests/fixtures/services-redirect-ssl new file mode 100644 index 0000000..1e4a539 --- /dev/null +++ b/src/tests/fixtures/services-redirect-ssl @@ -0,0 +1,3 @@ +{"3571640c480a": {"com.docker.compose.config-hash":"b3089bebef089144accf08bc189c2dbd527cb2ff2b44f0fd57f031b3258d9389","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.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"container2","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"host2.local","easyhaproxy.http.port":"80","easyhaproxy.http.redirect_ssl":"true","easyhaproxy.https.host":"host2.local","easyhaproxy.https.localport":"8080","easyhaproxy.https.port":"443","easyhaproxy.https.ssl":"true","io.buildah.version":"1.21.0"}, +"5b69bc7fea1b": {"com.docker.compose.config-hash":"e2e9424b6ef2adfa33f750e3894446c9200f24674f5cf601ad60a87430e14314","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.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"container","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"host1.local","easyhaproxy.http.port":"80","easyhaproxy.http.redirect_ssl":"true","easyhaproxy.https.host":"host1.local","easyhaproxy.https.localport":"8080","easyhaproxy.https.port":"443","easyhaproxy.https.sslcert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZEVENDQXZXZ0F3SUJBZ0lVUmkrdzFaVmdlZWRUbE5JQXdxUUJNSnY2ZFhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xhRzl6ZERFdWJHOWpZV3d3SGhjTk1qRXdPREV3TVRnME9UQTJXaGNOTXpFdwpPREE0TVRnME9UQTJXakFXTVJRd0VnWURWUVFEREF0b2IzTjBNUzVzYjJOaGJEQ0NBaUl3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFNQkRBaExBeWdKdWFXNnc2ZmZpZ3pUQUFHWHBtRXowdEl4bjFrNFoKeDV3TjVycHYvcXUwUU1ZeitBdjJ1MWVPS0VLWmVhRlJWcFQwcjkzZFg3SXZiRVpIdDI1R1BpQnZsTEdxaGpLUgpQblNrLzdVOFhtc250dFVBVjdyVkVLMVVyZEZ3OC9Jd3JpUUMrZGhyMG1uWWZTRE1rdkJvTUZwZGhWTlRyYkFaCjFUQjZyUWpFN0FyME10OG15OTZYSm13cmNqSzJUaitFMnJnUElVejFlNWNla0ZZSURTQmF0bXcrMyt2citUNXgKRk5Ga0oybzMwVzVvOFpmbENKSnpyVmFpaHFRaWNzNlpLRGdwZjdpcVhNRml3V0lsaGRRcEd2eDVHZi9LRlRLOQpVYU9uUlp6L1grMkNlYkFGYVRIUjNrL1BZcHBXVGdCQkJ1UnZscEN3K3dkbmttdGVDMFNRUkY5MVFXVnI3ZWpvCjdLYU9sR0k1VnR2TVVzV3ZUZUFabXBheW1JYUFURVR1T0phWTBKVTExT21MZUQ5RE9qNUUyU1E3cUlYL3BGY3AKeHB6RzVqNGMrTWxndnhQMlZBa05UZUFYQ2FZaVBCUUg1WlpnMEhFMlduQjFLaExSRmxIZDRpSFFEMkdKNXlOLwo2ZkNGQmZaZktTZUs4SmF1d3hnV2tyYTUzT2NEcS9tS2QrREEvZEsrL3J1Rzd0cXdWZ0lhMDRIT3Bsek03TFlSCkdCMElyczkrbHI1L1BKYlFabVUwNzNNZG42Y1hBZzNwKzZ3dndGbERrUzV2MTNnQkRZTkh0RjYyYmM1NTFlZEYKWjZrR3pKN3dtR1JvODRhQlA3TXVSWmVSZUxPclNTNjdhMXdMZHpac01uUDFUSjd4OUxmcjlNS2wydURuUWRuWQpleDhEQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QWZCZ05WCkhTTUVHREFXZ0JTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUNoUVlOdWFoMyttVHBJQkRZeEdyalRKTnVPVElNYVd6TXlpMXRrZitMMApzRUd3cGJtQU8ybVdXUVlGN1dWTHNpOThQVUxoM2FkanQyaml1ZDlWbGFhQzZnbnduNVpvMStQaWxvOXNOTExXCjZpajArck40a3dJbS9wTnFpK2pEdXUyY3ZBdUhJd1pXZWg4YkVlLzVVQ3hvNGlobVdGUU44ZUo2VFVLQ3BoUkMKNkVvci9TU1paQlFIZ1BsMEJjaHpIT2t3dTdSM0xDbmRScXhqaEFvVmI5eVFPVitac21UZUpYdWx3TnpKMXVMdApUOE9JZ0lpRHBtQm83SFNOMkgwazNjaHgwMEFzalV5SjltbUFXUGVqRmUvS1hMUlBjVlpSMTdqaHpnZklCRXpzCk01V3RXRm0xYUhEalZ2Nk02aXRlVm02MUU5VCtrL00xMXJ1MWUyWXdzeFREdmI2eDA0bWNyTnU5c29xZGRCYnIKVmZwbHV1b1EvaEVBYlh0Rk5Qb1R5U3B6MGN3T3djSENvd1ZPTG1kS2d2SW1zelppTXlISEc4VkdHbVBoODhuNwp3VnhiMGdWMFA0Uk1yY01MZGVUZG41NVlRcjFDcUJyMzRlQjZvbDZBc2JUbTNWekJIUlZtRk5rc2wxbzVKQjV0CnRYTGdGL0c4L3J6Si80bTFQYVZ1eHJCN0R4VW1JazhFUGJTSVZrdlp2ZDdMQnpLd1E2SWZWYXVjZXdIZkVhalEKVklpZXhTTWlGYzdsdzNLbnhqT0haamY2Rk05VllnM05vKytHZEM5OXM3TGtJdUp3QU1MTnFUUTdIdmhuN1l2UAo0RmxTSWdjNnhqMFlrR1pFUWxiNW8vNW5hdUVxUVUwQUJndzZqdEk0TnhyTkxUNmNwN0NPNE0weElERWcvM1lECmFBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0KTUlJSlF3SUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NDUzB3Z2drcEFnRUFBb0lDQVFEQVF3SVN3TW9DYm1sdQpzT24zNG9NMHdBQmw2WmhNOUxTTVo5Wk9HY2VjRGVhNmIvNnJ0RURHTS9nTDlydFhqaWhDbVhtaFVWYVU5Sy9kCjNWK3lMMnhHUjdkdVJqNGdiNVN4cW9ZeWtUNTBwUCsxUEY1cko3YlZBRmU2MVJDdFZLM1JjUFB5TUs0a0F2blkKYTlKcDJIMGd6Skx3YURCYVhZVlRVNjJ3R2RVd2VxMEl4T3dLOURMZkpzdmVseVpzSzNJeXRrNC9oTnE0RHlGTQo5WHVYSHBCV0NBMGdXclpzUHQvcjYvaytjUlRSWkNkcU45RnVhUEdYNVFpU2M2MVdvb2FrSW5MT21TZzRLWCs0CnFsekJZc0ZpSllYVUtScjhlUm4veWhVeXZWR2pwMFdjLzEvdGdubXdCV2t4MGQ1UHoyS2FWazRBUVFia2I1YVEKc1BzSFo1SnJYZ3RFa0VSZmRVRmxhKzNvNk95bWpwUmlPVmJiekZMRnIwM2dHWnFXc3BpR2dFeEU3amlXbU5DVgpOZFRwaTNnL1F6bytSTmtrTzZpRi82UlhLY2FjeHVZK0hQakpZTDhUOWxRSkRVM2dGd21tSWp3VUIrV1dZTkJ4Ck5scHdkU29TMFJaUjNlSWgwQTloaWVjamYrbndoUVgyWHlrbml2Q1dyc01ZRnBLMnVkem5BNnY1aW5mZ3dQM1MKdnY2N2h1N2FzRllDR3RPQnpxWmN6T3kyRVJnZENLN1BmcGErZnp5VzBHWmxOTzl6SForbkZ3SU42ZnVzTDhCWgpRNUV1YjlkNEFRMkRSN1JldG0zT2VkWG5SV2VwQnN5ZThKaGthUE9HZ1QrekxrV1hrWGl6cTBrdXUydGNDM2MyCmJESno5VXllOGZTMzYvVENwZHJnNTBIWjJIc2ZBd0lEQVFBQkFvSUNBUUMveFpiWjBjY3RxYWdzcXZhVk5URWUKZXExcStoZmFHdlBFWVFhWUhJcklFKzJpNVhjbkdjTEtjS2ZvZHhEakFuOFIvemdkT3A2Y01YMENWbi9Qb2hIawpBRUR0RTgrQVZ3d0FNMUZzT3dnTEhWR2FHejhxcnhCbFlkUWdIY3BtdWVJdTJQWGJDOGVIVUJpYVVPSXVoYXc1Ci9SUk1EQUMvQWkyc3NmaTdnT2p2VkU0b1F4UVcwUUcxS0dPT0FVSm4vdVlIdzJSRlkyVXUxcGlteE8ya0RPNTMKZ2N4bUMxV09ueUNIbUhhaVcvVWg3ejZKYW1mU000ZFh0VEpac2x5aDM3ZGhIS05iZzlWa1A3Q1FLQTRoTHpvcApoYmY1cVk2cmFyZ09OaW55MUhnTVB4cm13S3VVb3VKeU90TjB5QnR4akRDVU5hWFVCd2l5N3NOR1MrSDR2c3lCCjVQOUhoSUhTdHUrRlp0M0hHN0VJcUNuZGlhU0tEUzRqV2FWUUFiYm80bloyWnMyQkQreERlUFJDUlVxWDdyTTQKNFh6UElSV1dYbW1XZi83SWcyOUhicnA0YTlMY09tUTJsZUNKdGJhVEZTTjk2T0xVSjVFK2hRMHVsQ1pnQlZtUQpSQ1VZa0pQNGxPemJhS2R6anhnSE1ySHptNDVlVUZmOExpck94aTJ1eXhYSFFtRE51NGIzWDE4a3QzUGdVbVVtCjNkWHBsM2ZxU3lKYTdTQ1Y4Wk5CcnNyRHExRSt0aFl0dTkxUWJWU0d4SGQ5SHJOVmUzWGRMYk9DZFU5Q3VDNjkKTmdsem5hYTdzWkxxbXlLZWpUZkdzWTd4cldkTmNNUGw0cDRmY0lEL080RXBBU1pmb3JwVGVLTlQwWklmWlpldwpiMG1BUWVZWnFRTThpL3FNWU4vdUFRS0NBUUVBNXFnMXNSTk1jNlZkTS90UmdsYXNHWW94amdSQzJPcUFEWmdzCm1BWE1VSjNrRXJweXh0K2VDaW15OGlidVlwelJUSVE4ZkJUV1JrQ3RSWlhKNytLY0xWdGs5UVpJb0xiaHlOd2QKNEl4RVFaRnVVbGpEYnZTalRMU3ljc0h2bzY1aWJXSWZUTDdiZ1dsTEdnR3EvVU96ZkdzZ0g2Uzl3THA1RzMwRwo4RUx5akk1ZVRJWUlDcmZUbVZMK2M0NU1ScEVNS28rY3Z6OFB5c2lhT0ZUbjNjeXN3UFZkWWFlRUVxTVFqVTh3CklHTnNHWkx5dFk3QkFCQmNZMGxkcnRiYS9PK0Z2LytSSDd1VXR6UDd4cENJd0ZDeDgwWnpOK1dSeTlOdkk2M1UKenEzeUlCb1c5R3lBcEQyK1BMYVBOeGY3UUxUVUNoWTFaei9kWVJsdEtPeHYyQWE1Z1FLQ0FRRUExV0xXTnFwMApmaEIvWnRmU0VTaHhGTU04OWNqTjZBYXoxV0tMN3VUQm91OW9TSm54amtoa2FWNzZhY25UL2lxWHR4TU5nSGkxCmZJbURwVTNQdk0wWTRVZDJUNDdvSGM2UDFCclpQTi9HbVh5L3M2QkFFZFB3TGU3Sis0blRJU0hBZEdtcmgrYS8KNXBrdHUzMmc5bFdxZnR4ZWNGSVZTTFBXa3hUMFhLaU14cDFmZmtMK09hdnBNZ01GWks0MWlLczNkTlNoS1BvZwpMOEdTUGNQOXgveW43OFAyZUszTitQR2psQTZwUHpyQU55V1U3TjAvYm1IY0I5VEtQK3VkWVdjalZocnU3TVlOCndOckU0a0tkQzh2OGk3eDd0RGJ2Yjc5VCtGbzZQSWg1M3AwT3NuWnpBOFVSMFFOUit2RFF1ZlF1eWFqOFJFQysKWkc4WXlDS3N2azh5Z3dLQ0FRQS9mc1N4QjBmL2VlRXJZeDZ3QzUzNnRlRW9ZQ0hxeHJzVGd2V2JyOVRyeUZzMQprSi95QVRMblIwMWNmYjBYNW1WemM5K1dwTUhMdXhnMzFLRXZhU2xuRHdhK3NNa2pmTlN3ejI5bUZoYmdHZUhOCngyT2RVcmoxYjdURUJJRXNoTi9SanJaaEVSVXFEY3MvMEgrNmtuMkJYWmdOUGZPQ2I1TFJMMXpPblE5YUJBTVAKZThJUStVUEZyR1FoZVdXajgxL3ZBM081N2VreUFJRDd5dHU5WWcrWVdyTW5JODhtdGo3ak40NWZEQitBOXNQYgptUDJtUDlxKzlqNVUyQTZXbkhVc1FuVTMwQktEVUVzYUFVV3o4MExaWG1adlY4SUg0eDl3S2ZVd0pCQklLQVp6CnFMN005N1k3em1Ha1gvU3BmbDMwbk9KOGxzY2hhTGQxRVlsRVphMkJBb0lCQVFDeWUyNVQ0VFY1TUpGdjB6dVoKTUd1TmcxU2MvTzRGa24yZkVVT2NlV2pod1VCSDRjUGpUL2YxRHdXRHNOYUo5TlJieENyNTkzMU9BclBEYzVjOApBNDA0K1k0ak01UkJRa0tabGk5NHRIQW9kK2pjOVVCQjZUVXZKbGw1OVNsTXdDOTY3OXdTMjFaT0tuZlBLR0NYClNzWkdRRXNaeGY2Wmhoc0hnWEozZ2wvbHpVSlBtUGVPQTVZVlIrT2Q5LzA5S0lGRlRvalNmb3luaFZDdUt4NDkKeGI0dVZZbjJIT0o0eEowZlBUZ2hkQ0hNdnJtWGVlUVJqdmI4OGVhTm1xVlVFSEhGRnRnYjRma2xBNWZFN1JUeApCaGxpUkRCd1o3YlVrSU5LNnlWazluNkJUbnM1bU12UkxtZ2RuSnBZdkU3S0MwMkxUYlpiM0krajhDMFpVYStOCnF5N0RBb0lCQUFpZXJpYlM3V1VjbDJhQmxrbTUrVzdxTm0vSU5tNXp2bm9TUG82VjN3YTVoczZmOStDL2tiZEYKODdqUVBBL1lGZTN1UjJzQUo3c2xYNWV1Wks4V21mcEZtZ3psdTBzRXo4MU1MUS9XeXBadFp5dHlWdFd6QjJQdQpYQ1cxdGRTSDllSTJCbWhYZ29rSE5UTTQ4TmsveE9FTnJQL3NlWHJJeDVMSzBobkRIWm90dS96NitZU2tCOWhGCmNtMmZaeWdEMWRNTFg2bGlSaW14eUZZK2RJQ0pOQjk1SmlmVExXWW5XZUdkZGt3UHRYVWVHWEUxb2x6dk5rTEQKek16RTA5dWhreC9sUkpudGVPQkVaYWY4ME9CLzA5T2k5YjkvcnhZNTlkd3NINkdheExvVGZFS3VQbnZCVk1OUgpZa1UxNFd6UUtsZUZraUJKSTlsVnZuZmdHbk9sZ2cwPQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t","io.buildah.version":"1.21.0"}, +"77094604be33": {"com.docker.compose.config-hash":"b671dd783e367918c604400fe83cd2450c845a555bda6769bd1351bdcce3c565","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:4e24fb65b607dc3b105ab5ec5945707ccbf50ac32605e7f4935440b453b7c45c","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose.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"}} \ No newline at end of file diff --git a/src/tests/fixtures/services-tcp b/src/tests/fixtures/services-tcp new file mode 100644 index 0000000..5b5c2b0 --- /dev/null +++ b/src/tests/fixtures/services-tcp @@ -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"}} \ No newline at end of file diff --git a/tests/fixtures/static.yml b/src/tests/fixtures/static.yml similarity index 100% rename from tests/fixtures/static.yml rename to src/tests/fixtures/static.yml diff --git a/tests/test_labels.py b/src/tests/test_labels.py similarity index 100% rename from tests/test_labels.py rename to src/tests/test_labels.py diff --git a/tests/test_parser.py b/src/tests/test_parser.py similarity index 90% rename from tests/test_parser.py rename to src/tests/test_parser.py index 81b4dfc..f46e6bf 100644 --- a/tests/test_parser.py +++ b/src/tests/test_parser.py @@ -2,6 +2,7 @@ import easymapping import pytest import os import yaml +import json CERTS_FOLDER="/tmp/certs" CERT_FILE="/tmp/certs/haproxy/www.somehost.com.br.pem" @@ -10,7 +11,7 @@ LETSENCRYPT_EMAIL="some@email.com" def load_fixture(file): path = os.path.dirname(os.path.realpath(__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 @@ -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) assert len(haproxy_config) > 0 @@ -47,10 +48,7 @@ def test_parser_finds_services(): } } - if os.path.exists(CERT_FILE): - os.remove(CERT_FILE) - - cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) + cfg = easymapping.HaproxyConfigGenerator(result) haproxy_config = cfg.generate(line_list) assert len(haproxy_config) > 0 @@ -58,8 +56,7 @@ def test_parser_finds_services(): with open(path + "/expected/services.txt", 'r') as expected_file: assert expected_file.read() == haproxy_config - with open(CERT_FILE, 'r') as expected_file: - assert expected_file.read() == "Some PEM Certificate" + assert {"www.somehost.com.br.pem":"Some PEM Certificate"} == cfg.certs 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): os.remove(CERT_FILE) - cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) + cfg = easymapping.HaproxyConfigGenerator(result) haproxy_config = cfg.generate(line_list) 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: assert expected_file.read() == haproxy_config - with open(CERT_FILE, 'r') as expected_file: - assert expected_file.read() == "Some PEM Certificate" + assert {"www.somehost.com.br.pem":"Some PEM Certificate"} == cfg.certs 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): os.remove(CERT_FILE) - cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) + cfg = easymapping.HaproxyConfigGenerator(result) parsed_object = [ { @@ -217,7 +213,7 @@ def test_parser_static(): with open(path + "/fixtures/static.yml", 'r') as content_file: parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader) - cfg = easymapping.HaproxyConfigGenerator(parsed, CERTS_FOLDER) + cfg = easymapping.HaproxyConfigGenerator(parsed) haproxy_config = cfg.generate() 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) # 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) 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) 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) 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) assert len(haproxy_config) > 0 @@ -396,7 +392,7 @@ def test_parser_ssl_loose(): "ssl_mode": "loose", } - cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) + cfg = easymapping.HaproxyConfigGenerator(result) haproxy_config = cfg.generate(line_list) 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) assert len(haproxy_config) > 0 @@ -430,4 +426,5 @@ def test_parser_ssl_letsencrypt(): #test_parser_finds_services_raw() #test_parser_tcp() #test_parser_multiple_hosts() -#test_parser_ssl_letsencrypt() \ No newline at end of file +#test_parser_ssl_letsencrypt() +#test_parser_finds_services() \ No newline at end of file diff --git a/tests/fixtures/no-services b/tests/fixtures/no-services deleted file mode 100644 index a0f782d..0000000 --- a/tests/fixtures/no-services +++ /dev/null @@ -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"} diff --git a/tests/fixtures/services b/tests/fixtures/services deleted file mode 100644 index c77af21..0000000 --- a/tests/fixtures/services +++ /dev/null @@ -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="} \ No newline at end of file diff --git a/tests/fixtures/services-changed-label b/tests/fixtures/services-changed-label deleted file mode 100644 index f4b0b14..0000000 --- a/tests/fixtures/services-changed-label +++ /dev/null @@ -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="} \ No newline at end of file diff --git a/tests/fixtures/services-letsencrypt b/tests/fixtures/services-letsencrypt deleted file mode 100644 index 2d616ff..0000000 --- a/tests/fixtures/services-letsencrypt +++ /dev/null @@ -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"} diff --git a/tests/fixtures/services-multi-containers b/tests/fixtures/services-multi-containers deleted file mode 100644 index 7e58c70..0000000 --- a/tests/fixtures/services-multi-containers +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/tests/fixtures/services-multiple-hosts b/tests/fixtures/services-multiple-hosts deleted file mode 100644 index c04b29a..0000000 --- a/tests/fixtures/services-multiple-hosts +++ /dev/null @@ -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\"}"} \ No newline at end of file diff --git a/tests/fixtures/services-redirect-ssl b/tests/fixtures/services-redirect-ssl deleted file mode 100644 index 718450b..0000000 --- a/tests/fixtures/services-redirect-ssl +++ /dev/null @@ -1,3 +0,0 @@ -3571640c480a={"com.docker.compose.config-hash":"b3089bebef089144accf08bc189c2dbd527cb2ff2b44f0fd57f031b3258d9389","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.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"container2","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"host2.local","easyhaproxy.http.port":"80","easyhaproxy.http.redirect_ssl":"true","easyhaproxy.https.host":"host2.local","easyhaproxy.https.localport":"8080","easyhaproxy.https.port":"443","easyhaproxy.https.ssl":"true","io.buildah.version":"1.21.0"} -5b69bc7fea1b={"com.docker.compose.config-hash":"e2e9424b6ef2adfa33f750e3894446c9200f24674f5cf601ad60a87430e14314","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.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"container","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"host1.local","easyhaproxy.http.port":"80","easyhaproxy.http.redirect_ssl":"true","easyhaproxy.https.host":"host1.local","easyhaproxy.https.localport":"8080","easyhaproxy.https.port":"443","easyhaproxy.https.sslcert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZEVENDQXZXZ0F3SUJBZ0lVUmkrdzFaVmdlZWRUbE5JQXdxUUJNSnY2ZFhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xhRzl6ZERFdWJHOWpZV3d3SGhjTk1qRXdPREV3TVRnME9UQTJXaGNOTXpFdwpPREE0TVRnME9UQTJXakFXTVJRd0VnWURWUVFEREF0b2IzTjBNUzVzYjJOaGJEQ0NBaUl3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFNQkRBaExBeWdKdWFXNnc2ZmZpZ3pUQUFHWHBtRXowdEl4bjFrNFoKeDV3TjVycHYvcXUwUU1ZeitBdjJ1MWVPS0VLWmVhRlJWcFQwcjkzZFg3SXZiRVpIdDI1R1BpQnZsTEdxaGpLUgpQblNrLzdVOFhtc250dFVBVjdyVkVLMVVyZEZ3OC9Jd3JpUUMrZGhyMG1uWWZTRE1rdkJvTUZwZGhWTlRyYkFaCjFUQjZyUWpFN0FyME10OG15OTZYSm13cmNqSzJUaitFMnJnUElVejFlNWNla0ZZSURTQmF0bXcrMyt2citUNXgKRk5Ga0oybzMwVzVvOFpmbENKSnpyVmFpaHFRaWNzNlpLRGdwZjdpcVhNRml3V0lsaGRRcEd2eDVHZi9LRlRLOQpVYU9uUlp6L1grMkNlYkFGYVRIUjNrL1BZcHBXVGdCQkJ1UnZscEN3K3dkbmttdGVDMFNRUkY5MVFXVnI3ZWpvCjdLYU9sR0k1VnR2TVVzV3ZUZUFabXBheW1JYUFURVR1T0phWTBKVTExT21MZUQ5RE9qNUUyU1E3cUlYL3BGY3AKeHB6RzVqNGMrTWxndnhQMlZBa05UZUFYQ2FZaVBCUUg1WlpnMEhFMlduQjFLaExSRmxIZDRpSFFEMkdKNXlOLwo2ZkNGQmZaZktTZUs4SmF1d3hnV2tyYTUzT2NEcS9tS2QrREEvZEsrL3J1Rzd0cXdWZ0lhMDRIT3Bsek03TFlSCkdCMElyczkrbHI1L1BKYlFabVUwNzNNZG42Y1hBZzNwKzZ3dndGbERrUzV2MTNnQkRZTkh0RjYyYmM1NTFlZEYKWjZrR3pKN3dtR1JvODRhQlA3TXVSWmVSZUxPclNTNjdhMXdMZHpac01uUDFUSjd4OUxmcjlNS2wydURuUWRuWQpleDhEQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QWZCZ05WCkhTTUVHREFXZ0JTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUNoUVlOdWFoMyttVHBJQkRZeEdyalRKTnVPVElNYVd6TXlpMXRrZitMMApzRUd3cGJtQU8ybVdXUVlGN1dWTHNpOThQVUxoM2FkanQyaml1ZDlWbGFhQzZnbnduNVpvMStQaWxvOXNOTExXCjZpajArck40a3dJbS9wTnFpK2pEdXUyY3ZBdUhJd1pXZWg4YkVlLzVVQ3hvNGlobVdGUU44ZUo2VFVLQ3BoUkMKNkVvci9TU1paQlFIZ1BsMEJjaHpIT2t3dTdSM0xDbmRScXhqaEFvVmI5eVFPVitac21UZUpYdWx3TnpKMXVMdApUOE9JZ0lpRHBtQm83SFNOMkgwazNjaHgwMEFzalV5SjltbUFXUGVqRmUvS1hMUlBjVlpSMTdqaHpnZklCRXpzCk01V3RXRm0xYUhEalZ2Nk02aXRlVm02MUU5VCtrL00xMXJ1MWUyWXdzeFREdmI2eDA0bWNyTnU5c29xZGRCYnIKVmZwbHV1b1EvaEVBYlh0Rk5Qb1R5U3B6MGN3T3djSENvd1ZPTG1kS2d2SW1zelppTXlISEc4VkdHbVBoODhuNwp3VnhiMGdWMFA0Uk1yY01MZGVUZG41NVlRcjFDcUJyMzRlQjZvbDZBc2JUbTNWekJIUlZtRk5rc2wxbzVKQjV0CnRYTGdGL0c4L3J6Si80bTFQYVZ1eHJCN0R4VW1JazhFUGJTSVZrdlp2ZDdMQnpLd1E2SWZWYXVjZXdIZkVhalEKVklpZXhTTWlGYzdsdzNLbnhqT0haamY2Rk05VllnM05vKytHZEM5OXM3TGtJdUp3QU1MTnFUUTdIdmhuN1l2UAo0RmxTSWdjNnhqMFlrR1pFUWxiNW8vNW5hdUVxUVUwQUJndzZqdEk0TnhyTkxUNmNwN0NPNE0weElERWcvM1lECmFBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0KTUlJSlF3SUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NDUzB3Z2drcEFnRUFBb0lDQVFEQVF3SVN3TW9DYm1sdQpzT24zNG9NMHdBQmw2WmhNOUxTTVo5Wk9HY2VjRGVhNmIvNnJ0RURHTS9nTDlydFhqaWhDbVhtaFVWYVU5Sy9kCjNWK3lMMnhHUjdkdVJqNGdiNVN4cW9ZeWtUNTBwUCsxUEY1cko3YlZBRmU2MVJDdFZLM1JjUFB5TUs0a0F2blkKYTlKcDJIMGd6Skx3YURCYVhZVlRVNjJ3R2RVd2VxMEl4T3dLOURMZkpzdmVseVpzSzNJeXRrNC9oTnE0RHlGTQo5WHVYSHBCV0NBMGdXclpzUHQvcjYvaytjUlRSWkNkcU45RnVhUEdYNVFpU2M2MVdvb2FrSW5MT21TZzRLWCs0CnFsekJZc0ZpSllYVUtScjhlUm4veWhVeXZWR2pwMFdjLzEvdGdubXdCV2t4MGQ1UHoyS2FWazRBUVFia2I1YVEKc1BzSFo1SnJYZ3RFa0VSZmRVRmxhKzNvNk95bWpwUmlPVmJiekZMRnIwM2dHWnFXc3BpR2dFeEU3amlXbU5DVgpOZFRwaTNnL1F6bytSTmtrTzZpRi82UlhLY2FjeHVZK0hQakpZTDhUOWxRSkRVM2dGd21tSWp3VUIrV1dZTkJ4Ck5scHdkU29TMFJaUjNlSWgwQTloaWVjamYrbndoUVgyWHlrbml2Q1dyc01ZRnBLMnVkem5BNnY1aW5mZ3dQM1MKdnY2N2h1N2FzRllDR3RPQnpxWmN6T3kyRVJnZENLN1BmcGErZnp5VzBHWmxOTzl6SForbkZ3SU42ZnVzTDhCWgpRNUV1YjlkNEFRMkRSN1JldG0zT2VkWG5SV2VwQnN5ZThKaGthUE9HZ1QrekxrV1hrWGl6cTBrdXUydGNDM2MyCmJESno5VXllOGZTMzYvVENwZHJnNTBIWjJIc2ZBd0lEQVFBQkFvSUNBUUMveFpiWjBjY3RxYWdzcXZhVk5URWUKZXExcStoZmFHdlBFWVFhWUhJcklFKzJpNVhjbkdjTEtjS2ZvZHhEakFuOFIvemdkT3A2Y01YMENWbi9Qb2hIawpBRUR0RTgrQVZ3d0FNMUZzT3dnTEhWR2FHejhxcnhCbFlkUWdIY3BtdWVJdTJQWGJDOGVIVUJpYVVPSXVoYXc1Ci9SUk1EQUMvQWkyc3NmaTdnT2p2VkU0b1F4UVcwUUcxS0dPT0FVSm4vdVlIdzJSRlkyVXUxcGlteE8ya0RPNTMKZ2N4bUMxV09ueUNIbUhhaVcvVWg3ejZKYW1mU000ZFh0VEpac2x5aDM3ZGhIS05iZzlWa1A3Q1FLQTRoTHpvcApoYmY1cVk2cmFyZ09OaW55MUhnTVB4cm13S3VVb3VKeU90TjB5QnR4akRDVU5hWFVCd2l5N3NOR1MrSDR2c3lCCjVQOUhoSUhTdHUrRlp0M0hHN0VJcUNuZGlhU0tEUzRqV2FWUUFiYm80bloyWnMyQkQreERlUFJDUlVxWDdyTTQKNFh6UElSV1dYbW1XZi83SWcyOUhicnA0YTlMY09tUTJsZUNKdGJhVEZTTjk2T0xVSjVFK2hRMHVsQ1pnQlZtUQpSQ1VZa0pQNGxPemJhS2R6anhnSE1ySHptNDVlVUZmOExpck94aTJ1eXhYSFFtRE51NGIzWDE4a3QzUGdVbVVtCjNkWHBsM2ZxU3lKYTdTQ1Y4Wk5CcnNyRHExRSt0aFl0dTkxUWJWU0d4SGQ5SHJOVmUzWGRMYk9DZFU5Q3VDNjkKTmdsem5hYTdzWkxxbXlLZWpUZkdzWTd4cldkTmNNUGw0cDRmY0lEL080RXBBU1pmb3JwVGVLTlQwWklmWlpldwpiMG1BUWVZWnFRTThpL3FNWU4vdUFRS0NBUUVBNXFnMXNSTk1jNlZkTS90UmdsYXNHWW94amdSQzJPcUFEWmdzCm1BWE1VSjNrRXJweXh0K2VDaW15OGlidVlwelJUSVE4ZkJUV1JrQ3RSWlhKNytLY0xWdGs5UVpJb0xiaHlOd2QKNEl4RVFaRnVVbGpEYnZTalRMU3ljc0h2bzY1aWJXSWZUTDdiZ1dsTEdnR3EvVU96ZkdzZ0g2Uzl3THA1RzMwRwo4RUx5akk1ZVRJWUlDcmZUbVZMK2M0NU1ScEVNS28rY3Z6OFB5c2lhT0ZUbjNjeXN3UFZkWWFlRUVxTVFqVTh3CklHTnNHWkx5dFk3QkFCQmNZMGxkcnRiYS9PK0Z2LytSSDd1VXR6UDd4cENJd0ZDeDgwWnpOK1dSeTlOdkk2M1UKenEzeUlCb1c5R3lBcEQyK1BMYVBOeGY3UUxUVUNoWTFaei9kWVJsdEtPeHYyQWE1Z1FLQ0FRRUExV0xXTnFwMApmaEIvWnRmU0VTaHhGTU04OWNqTjZBYXoxV0tMN3VUQm91OW9TSm54amtoa2FWNzZhY25UL2lxWHR4TU5nSGkxCmZJbURwVTNQdk0wWTRVZDJUNDdvSGM2UDFCclpQTi9HbVh5L3M2QkFFZFB3TGU3Sis0blRJU0hBZEdtcmgrYS8KNXBrdHUzMmc5bFdxZnR4ZWNGSVZTTFBXa3hUMFhLaU14cDFmZmtMK09hdnBNZ01GWks0MWlLczNkTlNoS1BvZwpMOEdTUGNQOXgveW43OFAyZUszTitQR2psQTZwUHpyQU55V1U3TjAvYm1IY0I5VEtQK3VkWVdjalZocnU3TVlOCndOckU0a0tkQzh2OGk3eDd0RGJ2Yjc5VCtGbzZQSWg1M3AwT3NuWnpBOFVSMFFOUit2RFF1ZlF1eWFqOFJFQysKWkc4WXlDS3N2azh5Z3dLQ0FRQS9mc1N4QjBmL2VlRXJZeDZ3QzUzNnRlRW9ZQ0hxeHJzVGd2V2JyOVRyeUZzMQprSi95QVRMblIwMWNmYjBYNW1WemM5K1dwTUhMdXhnMzFLRXZhU2xuRHdhK3NNa2pmTlN3ejI5bUZoYmdHZUhOCngyT2RVcmoxYjdURUJJRXNoTi9SanJaaEVSVXFEY3MvMEgrNmtuMkJYWmdOUGZPQ2I1TFJMMXpPblE5YUJBTVAKZThJUStVUEZyR1FoZVdXajgxL3ZBM081N2VreUFJRDd5dHU5WWcrWVdyTW5JODhtdGo3ak40NWZEQitBOXNQYgptUDJtUDlxKzlqNVUyQTZXbkhVc1FuVTMwQktEVUVzYUFVV3o4MExaWG1adlY4SUg0eDl3S2ZVd0pCQklLQVp6CnFMN005N1k3em1Ha1gvU3BmbDMwbk9KOGxzY2hhTGQxRVlsRVphMkJBb0lCQVFDeWUyNVQ0VFY1TUpGdjB6dVoKTUd1TmcxU2MvTzRGa24yZkVVT2NlV2pod1VCSDRjUGpUL2YxRHdXRHNOYUo5TlJieENyNTkzMU9BclBEYzVjOApBNDA0K1k0ak01UkJRa0tabGk5NHRIQW9kK2pjOVVCQjZUVXZKbGw1OVNsTXdDOTY3OXdTMjFaT0tuZlBLR0NYClNzWkdRRXNaeGY2Wmhoc0hnWEozZ2wvbHpVSlBtUGVPQTVZVlIrT2Q5LzA5S0lGRlRvalNmb3luaFZDdUt4NDkKeGI0dVZZbjJIT0o0eEowZlBUZ2hkQ0hNdnJtWGVlUVJqdmI4OGVhTm1xVlVFSEhGRnRnYjRma2xBNWZFN1JUeApCaGxpUkRCd1o3YlVrSU5LNnlWazluNkJUbnM1bU12UkxtZ2RuSnBZdkU3S0MwMkxUYlpiM0krajhDMFpVYStOCnF5N0RBb0lCQUFpZXJpYlM3V1VjbDJhQmxrbTUrVzdxTm0vSU5tNXp2bm9TUG82VjN3YTVoczZmOStDL2tiZEYKODdqUVBBL1lGZTN1UjJzQUo3c2xYNWV1Wks4V21mcEZtZ3psdTBzRXo4MU1MUS9XeXBadFp5dHlWdFd6QjJQdQpYQ1cxdGRTSDllSTJCbWhYZ29rSE5UTTQ4TmsveE9FTnJQL3NlWHJJeDVMSzBobkRIWm90dS96NitZU2tCOWhGCmNtMmZaeWdEMWRNTFg2bGlSaW14eUZZK2RJQ0pOQjk1SmlmVExXWW5XZUdkZGt3UHRYVWVHWEUxb2x6dk5rTEQKek16RTA5dWhreC9sUkpudGVPQkVaYWY4ME9CLzA5T2k5YjkvcnhZNTlkd3NINkdheExvVGZFS3VQbnZCVk1OUgpZa1UxNFd6UUtsZUZraUJKSTlsVnZuZmdHbk9sZ2cwPQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t","io.buildah.version":"1.21.0"} -77094604be33={"com.docker.compose.config-hash":"b671dd783e367918c604400fe83cd2450c845a555bda6769bd1351bdcce3c565","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:4e24fb65b607dc3b105ab5ec5945707ccbf50ac32605e7f4935440b453b7c45c","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose.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"} diff --git a/tests/fixtures/services-tcp b/tests/fixtures/services-tcp deleted file mode 100644 index 41f7410..0000000 --- a/tests/fixtures/services-tcp +++ /dev/null @@ -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"}