Merge pull request #27 from byjg/kubernetes-refactory
Kubernetes refactory
This commit is contained in:
commit
edee20c5bd
63 changed files with 725 additions and 498 deletions
|
|
@ -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
|
||||
|
|
|
|||
26
.travis.yml
26
.travis.yml
|
|
@ -1,26 +0,0 @@
|
|||
language: python
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
if: (type IN (pull_request))
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
script:
|
||||
- pytest -s tests/
|
||||
|
||||
- stage: build docker
|
||||
if: (branch = master) AND (NOT (type IN (pull_request)))
|
||||
install:
|
||||
- docker pull byjg/k8s-ci
|
||||
script:
|
||||
- docker run --privileged -v /tmp/z:/var/lib/containers -it --rm -v $PWD:/work -w /work -e DOCKER_USERNAME=$DOCKER_USERNAME -e DOCKER_PASSWORD=$DOCKER_PASSWORD -e DOCKER_REGISTRY=$DOCKER_REGISTRY byjg/k8s-ci /work/build-multiarch.sh
|
||||
|
||||
- stage: documentation
|
||||
if: (branch = master) AND (NOT (type IN (pull_request)))
|
||||
install: skip
|
||||
script: "curl https://opensource.byjg.com/add-doc.sh | bash /dev/stdin devops docker-easy-haproxy"
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
|
@ -10,9 +10,9 @@
|
|||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"cwd": "${workspaceFolder}/src",
|
||||
"env": {
|
||||
"PYTHONPATH": "${cwd}"
|
||||
"PYTHONPATH": "${cwd}/src"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
11
Dockerfile
11
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" ]
|
||||
|
|
|
|||
|
|
@ -170,6 +170,10 @@ services:
|
|||
...
|
||||
```
|
||||
|
||||
```bash
|
||||
docker stack deploy --compose-file docker-compose.yml mystack
|
||||
```
|
||||
|
||||
### Single Definition
|
||||
|
||||
```bash
|
||||
|
|
|
|||
66
_config.yml
66
_config.yml
|
|
@ -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
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
_
|
||||
___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _
|
||||
/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |
|
||||
\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |
|
||||
|__/ |_| |__/
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
function log() {
|
||||
# ARGS:
|
||||
# - loglevel
|
||||
# - app
|
||||
# - message
|
||||
echo [$2] $(date +"$EASYHAPROXY_DATEFORMAT") [$1]: $3
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
80
examples/kubernetes/service.yml
Normal file
80
examples/kubernetes/service.yml
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: easyhaproxy-ingress
|
||||
name: container-example
|
||||
namespace: parking
|
||||
spec:
|
||||
rules:
|
||||
- host: example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: container-example
|
||||
port:
|
||||
number: 8080
|
||||
pathType: ImplementationSpecific
|
||||
- host: www.example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: container-example
|
||||
port:
|
||||
number: 8080
|
||||
pathType: ImplementationSpecific
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: container-example
|
||||
namespace: parking
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
selector:
|
||||
app: container-example
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: container-example
|
||||
namespace: parking
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: container-example
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: container-example
|
||||
spec:
|
||||
containers:
|
||||
- name: container-example
|
||||
image: byjg/static-httpserver
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: '0.05'
|
||||
memory: '20Mi'
|
||||
requests:
|
||||
cpu: '0.05'
|
||||
memory: '20Mi'
|
||||
env:
|
||||
- name: TITLE
|
||||
value: "My Host Example"
|
||||
67
examples/swarm/docker-compose.yml
Normal file
67
examples/swarm/docker-compose.yml
Normal file
File diff suppressed because one or more lines are too long
50
examples/swarm/host2.local.pem
Normal file
50
examples/swarm/host2.local.pem
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDqTCCApGgAwIBAgIUId5b9t9uqH78g02EzbWF6FKVw3gwDQYJKoZIhvcNAQEL
|
||||
BQAwZDELMAkGA1UEBhMCQlIxFzAVBgNVBAgMDlJpbyBkZSBKYW5laXJvMRcwFQYD
|
||||
VQQHDA5SaW8gZGUgSmFuZWlybzENMAsGA1UECgwEQUNNRTEUMBIGA1UEAwwLaG9z
|
||||
dDIubG9jYWwwHhcNMjIwODE1MDQyNzA1WhcNMjMwODE1MDQyNzA1WjBkMQswCQYD
|
||||
VQQGEwJCUjEXMBUGA1UECAwOUmlvIGRlIEphbmVpcm8xFzAVBgNVBAcMDlJpbyBk
|
||||
ZSBKYW5laXJvMQ0wCwYDVQQKDARBQ01FMRQwEgYDVQQDDAtob3N0Mi5sb2NhbDCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMSKvrOahavCXnvSF5131hpo
|
||||
6k65C57jgRQ84FaDj5MbJOVlYQVFtMG0XOk7a+hh5v1fe4wH0R7I6FDo0V9sS+ss
|
||||
ko5bsElc1xYlg5HbuKq89vRSKg6EDlztx3BKbi912Pmt5vFGNJ16zcw77DUrQIXo
|
||||
4I/b4a3pmBiWj43NoTIrmSWHtsGwwOj3iDvSweqdYXJIr3hpHH5u6pohjDoQvqDz
|
||||
K6Mu8p6mhCUKNs7KFJnNInNG25oQT6O0n4OGtmgRjLWopdEnOhMkKsfIoI1XtlXB
|
||||
LBDv7huICk3t5ywtfCQyO09kX7lFIgd5rn7+MjwH5WNeqbQJxuaqjoXQnNZUgUsC
|
||||
AwEAAaNTMFEwHQYDVR0OBBYEFNhMBG8q6a+iK2nECwVTn6B9EXZOMB8GA1UdIwQY
|
||||
MBaAFNhMBG8q6a+iK2nECwVTn6B9EXZOMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
|
||||
hvcNAQELBQADggEBAJmudvx8+p5iIUsT8fm/fbVM0DA6qWALDYUJnTn3j6Lq4vpf
|
||||
PFC+q1LmuWfBQMyqKrHrP3e493EctXoiSKZO6iN5dVJIur02OjGuiAEcsYuY1nLn
|
||||
s9piiI+UEwxH6ux1NaHUnzsWauoBvRhzjXvO6SAVSZJYa9dY5mizXklDyDNuG5U0
|
||||
lXv9egMGBsy0dG6eFXkU5CPdxWU540yI2sCtSAj7z+WRUD5k7gJ7tVoY3//jHQZG
|
||||
5STTmm5t9kpIZTWkptyJos9oZJFYMIXqW2Fc6tyLZpRp31R78tDs6ETIkToDc0RR
|
||||
jz66th6HI+ZlgIBQhw09+hYAhBDe9+Dmd/SzQZc=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDEir6zmoWrwl57
|
||||
0hedd9YaaOpOuQue44EUPOBWg4+TGyTlZWEFRbTBtFzpO2voYeb9X3uMB9EeyOhQ
|
||||
6NFfbEvrLJKOW7BJXNcWJYOR27iqvPb0UioOhA5c7cdwSm4vddj5rebxRjSdes3M
|
||||
O+w1K0CF6OCP2+Gt6ZgYlo+NzaEyK5klh7bBsMDo94g70sHqnWFySK94aRx+buqa
|
||||
IYw6EL6g8yujLvKepoQlCjbOyhSZzSJzRtuaEE+jtJ+DhrZoEYy1qKXRJzoTJCrH
|
||||
yKCNV7ZVwSwQ7+4biApN7ecsLXwkMjtPZF+5RSIHea5+/jI8B+VjXqm0Ccbmqo6F
|
||||
0JzWVIFLAgMBAAECggEBAKceitVROQQ5e/mxRR9CfK1sNH/H3Ne3/1PkB6XIrFab
|
||||
qB3evEatZOuon7A6NKEeTjl37Se+pdSVZOUXcqC/BzbraZre3+EhrkpIj72ApV+Y
|
||||
2iwZiWVaaJQgI4uZ3mNAw8RaWJsj5S1a9I8LDOiQ5IZ45CmvABDPJeMScvJSvRRY
|
||||
e5N0L6stqS7Z+IoyGVKUfp1iNO0YyywOUiSkIRXgscuRXZGYpiGPomsJ+Js1ejzW
|
||||
jyStlZJEr4L1285rGPrmHqjTwFd+hG80Wc4179xL+WRE6HBEUZSiy95fe6kcPHXX
|
||||
BgiVYtcFKmiBi2dTbxl4e94ut239i0HtlJ1ZJtLh+BECgYEA8a298K2zXkHosxhN
|
||||
tRrH7XfMPTkHDDd3rxM21LT+fIXqinGUp9LYaDcbjuTPs8e33uKMd7R6Q40x89yW
|
||||
IXNka/VL0PXUeV67aCVLLqgXDLGudluJinH0XvmI0CmBecFSMqIFmQlgqERoGGs3
|
||||
UMac0p876T4XkGQQJdf62bFpE4UCgYEA0DBH0PDlOpwXccDgXrMfayr8HAhI+G5R
|
||||
yWQ//9iirtU83chwIWwkh53eLLMzLgdqJnPiWyaUW5BqzmYuD23nhxcQ7PNdIqOO
|
||||
H1sE6zqLNshv46t5QKlh1Q4qjd7UqtgrSrY63RXJCMWTwnNMeDLtj8gaKbjkrG3R
|
||||
BM2ildt6Uo8CgYBX7NDUli1SloH1XlsvD047S8FHaM7yl994F3J0UmDfpszcj1P4
|
||||
9pF64Mmq4/3Yt0li0mMuTb/Jgb3xrYgFJXkcecKahEVH3ropup+umsLAAIirUMQq
|
||||
VSkFwJ0Qtnj/deDUwPNuaOX8cd65O5CFV6zIR9xBEDD8fBsP2ZLOzmefDQKBgQDF
|
||||
m24vVthd/1cJdCgD+0VxNYXDHeIVXLFo1S0iLYCNLn3tjZlRQBKUXzZJe3ay0/rf
|
||||
sNND7aSYHMYkTzydDJbc1PoNzxmyDUiTXpOWqyUExM/fbB1VUPE5h47AxqdZ2oGN
|
||||
EtdgjpMZLmCIC2SkGsL+3NJok8UKHdpuErmmQIMk5QKBgQCgEWcYtLXC3YDYMFdI
|
||||
UgcTebFqSs3mLYgub1xekW3IXR2yom4V5fQTLiF7Yfn2dpDW4IcMU0UJFYVsUlhK
|
||||
aGtet4Vm5Nn8+Mghot5yAjqO9yAUaub7wgifKIe99tQKd8uZyCvJ0hhvmDDSfx4m
|
||||
B/TEiFAO99yF49iSxEVSAS6pqQ==
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -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
|
||||
|
||||
191
src/functions/__init__.py
Normal file
191
src/functions/__init__.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
from datetime import datetime
|
||||
from multiprocessing import Process, Lock
|
||||
import subprocess
|
||||
import shlex
|
||||
import time
|
||||
import os
|
||||
|
||||
class Functions:
|
||||
@staticmethod
|
||||
def load(filename):
|
||||
with open(filename, 'r') as content_file:
|
||||
return content_file.read()
|
||||
|
||||
@staticmethod
|
||||
def save(filename, contents):
|
||||
with open(filename, 'w') as file:
|
||||
file.write(contents)
|
||||
|
||||
@staticmethod
|
||||
def log(source, level, message):
|
||||
if message is None or message == "":
|
||||
return
|
||||
|
||||
if not isinstance(message, (list, tuple)):
|
||||
message = [message]
|
||||
|
||||
for line in message:
|
||||
print("[%s] %s [%s]: %s" % (source, datetime.now().strftime("%x %X"), level, line.rstrip()))
|
||||
|
||||
@staticmethod
|
||||
def run_bash(source, command, log_output=True, return_result=True):
|
||||
if not isinstance(command, (list, tuple)):
|
||||
command = shlex.split(command)
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
|
||||
output = []
|
||||
|
||||
while True:
|
||||
line = process.stdout.readline().rstrip()
|
||||
output.append(line) if return_result else None
|
||||
Functions.log(source, "info", line) if log_output else None
|
||||
Functions.log(source, "warning", process.stderr.readline())
|
||||
return_code = process.poll()
|
||||
if return_code is not None:
|
||||
lines = []
|
||||
for line in process.stdout.readlines():
|
||||
output.append(line.rstrip()) if return_result else None
|
||||
lines.append(line.rstrip())
|
||||
Functions.log(source, "info", lines) if log_output else None
|
||||
Functions.log(source, "warning", process.stderr.readlines())
|
||||
break
|
||||
|
||||
return output
|
||||
except Exception as e:
|
||||
Functions.log(source, 'error', "%s" % (e))
|
||||
|
||||
|
||||
class DaemonizeHAProxy:
|
||||
def __init__(self):
|
||||
self.process = None
|
||||
self.thread = None
|
||||
|
||||
def haproxy(self, action):
|
||||
if action == "start":
|
||||
self.__prepare("/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock")
|
||||
else:
|
||||
pid = "".join(Functions().run_bash("HAPROXY", "cat /run/haproxy.pid", log_output=False))
|
||||
self.__prepare("/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf %s" % (pid))
|
||||
|
||||
if self.process is None:
|
||||
return
|
||||
|
||||
self.thread = Process(target=self.__start, args=())
|
||||
self.thread.start()
|
||||
|
||||
def __prepare(self, command):
|
||||
source = "HAPROXY"
|
||||
if not isinstance(command, (list, tuple)):
|
||||
command = shlex.split(command)
|
||||
|
||||
try:
|
||||
self.process = subprocess.Popen(command,
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=-1,
|
||||
universal_newlines=True)
|
||||
|
||||
except Exception as e:
|
||||
Functions.log(source, 'error', "%s" % (e))
|
||||
|
||||
|
||||
def __start(self):
|
||||
source = "HAPROXY"
|
||||
try:
|
||||
with self.process.stdout:
|
||||
for line in iter(self.process.stdout.readline, b''):
|
||||
Functions.log(source, "info", line)
|
||||
|
||||
returncode = self.process.wait()
|
||||
Functions.log(source, "info", "Return code %s" % (returncode))
|
||||
|
||||
except Exception as e:
|
||||
Functions.log(source, 'error', "%s" % (e))
|
||||
|
||||
def is_alive(self):
|
||||
return self.thread.is_alive()
|
||||
|
||||
def kill(self):
|
||||
self.process.kill()
|
||||
self.thread.kill()
|
||||
|
||||
def terminate(self):
|
||||
self.process.terminate()
|
||||
self.thread.terminate()
|
||||
|
||||
|
||||
class Certbot:
|
||||
def __init__(self, certs, email):
|
||||
self.certs = certs
|
||||
self.email = email
|
||||
|
||||
def check_certificates(self, hosts):
|
||||
if self.email == "" or len(hosts) == 0:
|
||||
return False
|
||||
|
||||
try:
|
||||
request_certs = []
|
||||
renew_certs = []
|
||||
current_time = time.time()
|
||||
for host in hosts:
|
||||
filename = "%s/%s.pem" % (self.certs, host)
|
||||
host_arg = '-d %s' % (host)
|
||||
if not os.path.exists(filename):
|
||||
Functions.log("CERTBOT", "info", "Request new certificate for %s" % (host))
|
||||
request_certs.append(host_arg)
|
||||
else:
|
||||
creation_time = os.path.getctime(filename)
|
||||
if (current_time - creation_time) // (24 * 3600) > 90:
|
||||
Functions.log("CERTBOT", "info", "Request expired certificate for %s" % (host))
|
||||
request_certs.append(host_arg)
|
||||
if (current_time - creation_time) // (24 * 3600) >= 45:
|
||||
Functions.log("CERTBOT", "info", "Renew certificate for %s" % (host))
|
||||
renew_certs.append(host_arg)
|
||||
|
||||
certbot_certonly = ('/usr/bin/certbot certonly '
|
||||
' --standalone'
|
||||
' --preferred-challenges http'
|
||||
' --http-01-port 2080'
|
||||
' --agree-tos'
|
||||
' --issuance-timeout 90'
|
||||
' --no-eff-email'
|
||||
' --non-interactive'
|
||||
' --max-log-backups=0'
|
||||
' %s --email %s' % (' '.join(request_certs), self.email)
|
||||
)
|
||||
|
||||
ret_reload = False
|
||||
if len(request_certs) > 0:
|
||||
Functions.run_bash("CERTBOT", certbot_certonly, return_result=False)
|
||||
ret_reload = True
|
||||
|
||||
if len(renew_certs) > 0:
|
||||
Functions.run_bash("CERTBOT", "/usb/bin/certbot renew", return_result=False)
|
||||
ret_reload = True
|
||||
|
||||
if ret_reload:
|
||||
self.find_live_certificates()
|
||||
|
||||
return ret_reload
|
||||
except Exception as e:
|
||||
Functions.log("CERTBOT", "error", "%s" % (e))
|
||||
return False
|
||||
|
||||
def merge_certificate(self, cert, key, filename):
|
||||
Functions.save(filename, cert + key)
|
||||
|
||||
def find_live_certificates(self):
|
||||
letsencrypt_certs = "/etc/letsencrypt/live/"
|
||||
for item in os.listdir(letsencrypt_certs):
|
||||
path = os.path.join(letsencrypt_certs, item)
|
||||
if os.path.isdir(path):
|
||||
cert = Functions.load(os.path.join(path, "fullchain.pem"))
|
||||
key = Functions.load(os.path.join(path, "privkey.pem"))
|
||||
filename = "%s/%s.pem" % (self.certs, item)
|
||||
self.merge_certificate(cert, key, filename)
|
||||
76
src/main.py
Normal file
76
src/main.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
from functions import Functions, DaemonizeHAProxy, Certbot
|
||||
from processor import ProcessorInterface
|
||||
import os
|
||||
import time
|
||||
from deepdiff import DeepDiff
|
||||
|
||||
easyhaproxy_config = "/etc/haproxy/easyconfig.yml"
|
||||
haproxy_config = "/etc/haproxy/haproxy.cfg"
|
||||
certs_letsencrypt = "/certs/letsencrypt"
|
||||
certs_haproxy = "/certs/haproxy"
|
||||
|
||||
def start():
|
||||
processor_obj = ProcessorInterface.factory(os.getenv("EASYHAPROXY_DISCOVER"))
|
||||
if processor_obj is None:
|
||||
exit(1)
|
||||
|
||||
os.makedirs(certs_letsencrypt, exist_ok=True)
|
||||
os.makedirs(certs_haproxy, exist_ok=True)
|
||||
|
||||
processor_obj.save_config(haproxy_config)
|
||||
processor_obj.save_certs(certs_haproxy)
|
||||
letsencrypt_certs_found = processor_obj.get_letsencrypt_hosts()
|
||||
Functions.log('EASYHAPROXY', 'info', 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
||||
|
||||
old_haproxy = None
|
||||
haproxy = DaemonizeHAProxy()
|
||||
haproxy.haproxy("start")
|
||||
|
||||
certbot = Certbot(certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"))
|
||||
|
||||
while True:
|
||||
time.sleep(10)
|
||||
if old_haproxy is not None:
|
||||
old_haproxy.kill()
|
||||
old_haproxy = None
|
||||
try:
|
||||
old_parsed = processor_obj.get_parsed_object()
|
||||
processor_obj.refresh()
|
||||
if DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive():
|
||||
Functions.log('EASYHAPROXY', 'info', 'New configuration found. Reloading...')
|
||||
processor_obj.save_config(haproxy_config)
|
||||
processor_obj.save_certs(certs_haproxy)
|
||||
letsencrypt_certs_found = processor_obj.get_letsencrypt_hosts()
|
||||
Functions.log('EASYHAPROXY', 'info', 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
||||
old_haproxy = haproxy
|
||||
haproxy = DaemonizeHAProxy()
|
||||
haproxy.haproxy("reload")
|
||||
old_haproxy.terminate()
|
||||
|
||||
certbot.check_certificates(letsencrypt_certs_found)
|
||||
except Exception as e:
|
||||
Functions.log('EASYHAPROXY', 'error', "Err: %s" % (e))
|
||||
Functions.log('EASYHAPROXY', 'info', 'Heartbeat')
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
Functions.run_bash('INIT', '/usr/sbin/haproxy -v')
|
||||
|
||||
Functions.log('INIT', 'info', " _ ")
|
||||
Functions.log('INIT', 'info', " ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ ")
|
||||
Functions.log('INIT', 'info', "/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |")
|
||||
Functions.log('INIT', 'info', "\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |")
|
||||
Functions.log('INIT', 'info', " |__/ |_| |__/ ")
|
||||
|
||||
Functions.log('INIT', 'info', "Release: %s" % (os.getenv("RELEASE_VERSION")))
|
||||
Functions.log('INIT', 'info', 'Environment:')
|
||||
for name, value in os.environ.items():
|
||||
if "HAPROXY" in name:
|
||||
Functions.log('INIT', 'info', "- {0}: {1}".format(name, value))
|
||||
|
||||
start()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
173
src/processor/__init__.py
Normal file
173
src/processor/__init__.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
from easymapping import HaproxyConfigGenerator
|
||||
from functions import Functions
|
||||
import yaml
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import docker
|
||||
from kubernetes import client, config
|
||||
from kubernetes.client.rest import ApiException
|
||||
|
||||
class ContainerEnv:
|
||||
@staticmethod
|
||||
def read():
|
||||
env_vars = {
|
||||
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
|
||||
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default")
|
||||
}
|
||||
|
||||
if os.getenv("HAPROXY_PASSWORD"):
|
||||
env_vars["stats"] = {
|
||||
"username": os.getenv("HAPROXY_USERNAME") if os.getenv("HAPROXY_USERNAME") else "admin",
|
||||
"password": os.getenv("HAPROXY_PASSWORD"),
|
||||
"port": os.getenv("HAPROXY_STATS_PORT") if os.getenv("HAPROXY_STATS_PORT") else "1936",
|
||||
}
|
||||
|
||||
env_vars["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv("EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
|
||||
if (os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")):
|
||||
env_vars["letsencrypt"] = {
|
||||
"email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")
|
||||
}
|
||||
|
||||
return env_vars
|
||||
|
||||
|
||||
class ProcessorInterface:
|
||||
def __init__(self, filename = None):
|
||||
self.filename = filename
|
||||
self.refresh()
|
||||
|
||||
@staticmethod
|
||||
def factory(mode):
|
||||
if mode == "static":
|
||||
return Static("/etc/haproxy/easyconfig.yml")
|
||||
elif mode == "docker":
|
||||
return Docker()
|
||||
elif mode == "swarm":
|
||||
return Swarm()
|
||||
elif mode == "kubernetes":
|
||||
return Kubernetes()
|
||||
else:
|
||||
Functions.log("FACTORY", "error", "Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got '%s'" % (mode))
|
||||
return None
|
||||
|
||||
def refresh(self):
|
||||
self.letsencrypt_hosts = None
|
||||
self.parsed_object = None
|
||||
self.cfg = None
|
||||
self.hosts = None
|
||||
self.inspect_network()
|
||||
self.parse()
|
||||
|
||||
def inspect_network(self):
|
||||
#Abstract
|
||||
pass
|
||||
|
||||
def parse(self):
|
||||
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
|
||||
|
||||
def get_letsencrypt_hosts(self):
|
||||
return self.letsencrypt_hosts
|
||||
|
||||
def get_hosts(self):
|
||||
return self.hosts
|
||||
|
||||
def get_parsed_object(self):
|
||||
return self.parsed_object
|
||||
|
||||
def get_certs(self, key = None):
|
||||
if key is None:
|
||||
return self.cfg.certs
|
||||
else:
|
||||
return None if key not in self.cfg.certs else self.cfg.certs[key]
|
||||
|
||||
def get_haproxy_conf(self):
|
||||
conf = self.cfg.generate(self.parsed_object)
|
||||
self.letsencrypt_hosts = self.cfg.letsencrypt_hosts
|
||||
self.hosts = self.cfg.serving_hosts
|
||||
return conf
|
||||
|
||||
def save_config(self, filename):
|
||||
Functions.save(filename, self.get_haproxy_conf())
|
||||
|
||||
def save_certs(self, path):
|
||||
for cert in self.get_certs():
|
||||
Functions.save("{0}/{1}".format(path, cert), self.get_certs(cert))
|
||||
|
||||
|
||||
|
||||
class Static(ProcessorInterface):
|
||||
def inspect_network(self):
|
||||
self.parsed_object = {}
|
||||
|
||||
def parse(self):
|
||||
static_content = yaml.load(Functions.load(self.filename), Loader=yaml.FullLoader)
|
||||
self.cfg = HaproxyConfigGenerator(static_content)
|
||||
|
||||
|
||||
class Docker(ProcessorInterface):
|
||||
def __init__(self, filename = None):
|
||||
self.client = docker.from_env()
|
||||
super().__init__()
|
||||
|
||||
def inspect_network(self):
|
||||
self.parsed_object = {}
|
||||
for container in self.client.containers.list():
|
||||
self.parsed_object[container.name] = container.labels
|
||||
|
||||
|
||||
class Swarm(ProcessorInterface):
|
||||
def __init__(self, filename = None):
|
||||
self.client = docker.from_env()
|
||||
super().__init__()
|
||||
|
||||
def inspect_network(self):
|
||||
self.parsed_object = {}
|
||||
for container in self.client.services.list():
|
||||
self.parsed_object[container.attrs["Spec"]["Name"]] = container.attrs["Spec"]["Labels"]
|
||||
|
||||
|
||||
class Kubernetes(ProcessorInterface):
|
||||
def __init__(self, filename = None):
|
||||
config.load_incluster_config()
|
||||
self.api_instance = client.CoreV1Api()
|
||||
self.v1 = client.NetworkingV1Api()
|
||||
super().__init__()
|
||||
|
||||
def inspect_network(self):
|
||||
|
||||
ret = self.v1.list_ingress_for_all_namespaces(watch=False)
|
||||
|
||||
self.parsed_object = {}
|
||||
for i in ret.items:
|
||||
if i.metadata.annotations['kubernetes.io/ingress.class'] != "easyhaproxy-ingress":
|
||||
continue
|
||||
|
||||
data = {}
|
||||
#ingress_name = i.metadata.name
|
||||
data["creation_timestamp"] = i.metadata.creation_timestamp.strftime("%x %X")
|
||||
data["resource_version"] = i.metadata.resource_version
|
||||
data["namespace"] = i.metadata.namespace
|
||||
for rule in i.spec.rules:
|
||||
rule_data = {}
|
||||
port_number = rule.http.paths[0].backend.service.port.number
|
||||
definition = rule.host.replace(".", "-")
|
||||
rule_data["easyhaproxy.%s_%s.host" % (definition, port_number)] = rule.host
|
||||
rule_data["easyhaproxy.%s_%s.port" % (definition, port_number)] = "80"
|
||||
rule_data["easyhaproxy.%s_%s.localport" % (definition, port_number)] = port_number
|
||||
service_name = rule.http.paths[0].backend.service.name
|
||||
try:
|
||||
api_response = self.api_instance.read_namespaced_service(service_name, i.metadata.namespace)
|
||||
cluster_ip = api_response.spec.cluster_ip
|
||||
except ApiException as e:
|
||||
cluster_ip = None
|
||||
# print("Exception when calling CoreV1Api->read_namespaced_service: %s\n" % e)
|
||||
|
||||
if cluster_ip is not None:
|
||||
if cluster_ip not in self.parsed_object.keys():
|
||||
self.parsed_object[cluster_ip] = data
|
||||
self.parsed_object[cluster_ip].update(rule_data)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -2,4 +2,6 @@ pyyaml
|
|||
docker
|
||||
jinja2
|
||||
pytest
|
||||
kubernetes
|
||||
docker
|
||||
kubernetes
|
||||
deepdiff
|
||||
|
|
@ -78,3 +78,4 @@ backend srv_{{ host }}
|
|||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
|
||||
|
|
@ -25,4 +25,4 @@ defaults
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -92,4 +92,4 @@ backend srv_test_example_org_443
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -42,4 +42,4 @@ backend srv_www_helloworld_com_19901
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -75,4 +75,4 @@ backend srv_www_helloworld_com_19901
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -77,4 +77,4 @@ backend srv_host1_local_443
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -39,4 +39,4 @@ backend srv_agent_quantum_local_31339
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -121,4 +121,4 @@ backend srv_www_somehost_com_br_80
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -35,4 +35,4 @@ backend srv_stats
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -20,4 +20,4 @@ defaults
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -107,4 +107,4 @@ backend srv_host3_com_br_8080
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
5
src/tests/fixtures/no-services
vendored
Normal file
5
src/tests/fixtures/no-services
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{"swarm-prom_caddy": {"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"},
|
||||
"swarm-prom_cadvisor": {"com.docker.stack.image":"google/cadvisor","com.docker.stack.namespace":"swarm-prom"},
|
||||
"swarm-prom_dockerd-exporter": {"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"},
|
||||
"swarm-prom_unsee": {"com.docker.stack.image":"cloudflare/unsee:v0.8.0","com.docker.stack.namespace":"swarm-prom"},
|
||||
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy","com.docker.stack.namespace":"test"}}
|
||||
6
src/tests/fixtures/services
vendored
Normal file
6
src/tests/fixtures/services
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
|
||||
"my-stack_agent": {"easyhaproxy.agent.host":"agent.quantum.example.org","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_cadvisor": {"easyhaproxy.cadvisor.host":"cadvisor.quantum.example.org","easyhaproxy.cadvisor.localport":"8080","easyhaproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_node-exporter": {"easyhaproxy.exp.host":"node-exporter.quantum.example.org","easyhaproxy.exp.localport":"9100","easyhaproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","easyhaproxy.exp.letsencrypt":"true"},
|
||||
"my-stack_reverse-proxy": {"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"some-service": {"easyhaproxy.http.port":"80","easyhaproxy.http.host":"www.somehost.com.br","easyhaproxy.http.localport":"80","easyhaproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.port":"443","easyhaproxy.https.host":"www.somehost.com.br","easyhaproxy.https.localport":"80","easyhaproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}}
|
||||
6
src/tests/fixtures/services-changed-label
vendored
Normal file
6
src/tests/fixtures/services-changed-label
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
|
||||
"my-stack_agent": {"haproxy.agent.host":"agent.quantum.example.org","haproxy.agent.localport":"9001","haproxy.agent.mode":"tcp","haproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_cadvisor": {"haproxy.cadvisor.host":"cadvisor.quantum.example.org","haproxy.cadvisor.localport":"8080","haproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_node-exporter": {"haproxy.exp.host":"node-exporter.quantum.example.org","haproxy.exp.localport":"9100","haproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","haproxy.exp.letsencrypt":"yes"},
|
||||
"my-stack_reverse-proxy": {"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"some-service": {"haproxy.http.port":"80","haproxy.http.host":"www.somehost.com.br","haproxy.http.localport":"80","haproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.port":"443","haproxy.https.host":"www.somehost.com.br","haproxy.https.localport":"80","haproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}}
|
||||
4
src/tests/fixtures/services-letsencrypt
vendored
Normal file
4
src/tests/fixtures/services-letsencrypt
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{"f5c645a0dfc6": {"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"},
|
||||
"bbd4d1854155": {"com.docker.compose.config-hash":"3dc790bf2bea944359c75a40c45655bd868f1d85beb599d1ca797e8ea2c95ee4","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:0fd95b1512c207048ab3fcc74032354f38143fbb8235ac2a47da903c98a58205","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"},
|
||||
"b63438410b6a": {"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"},
|
||||
"83d57d592e26": {"com.docker.compose.config-hash":"8c5871144f1e8a3aeca037207c02f011ab2c6e6c311a3773602b63541762dab5","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:c4232396c715f3d568816c666e6d9b4a68ef6c36f6243b4007c4ee1d8335fd65","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"static","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test2.example.org","easyhaproxy.http.localport":"8080","easyhaproxy.http.port":"80","io.buildah.version":"1.21.0"}}
|
||||
2
src/tests/fixtures/services-multi-containers
vendored
Normal file
2
src/tests/fixtures/services-multi-containers
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{"test_nginx.2.t5r94mjlced7m3t5orfjbowmm": {"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"},
|
||||
"test_nginx.1.p552hqxkdx88narjrp5kouwb2": {"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"}}
|
||||
3
src/tests/fixtures/services-multiple-hosts
vendored
Normal file
3
src/tests/fixtures/services-multiple-hosts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{"db79d3a910f4": {"com.docker.compose.config-hash":"5bde40f52451521ad201e70de1291397376a0498a7c955624a609da3b60e7e8e","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:ea39067705590557dd0cd951664a10970ceefcb725a3c1f43690d6d6d4ed5fce","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"},
|
||||
"3e63154954b0": {"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"},
|
||||
"eb294c110eb1": {"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"}}
|
||||
3
src/tests/fixtures/services-redirect-ssl
vendored
Normal file
3
src/tests/fixtures/services-redirect-ssl
vendored
Normal file
File diff suppressed because one or more lines are too long
2
src/tests/fixtures/services-tcp
vendored
Normal file
2
src/tests/fixtures/services-tcp
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{"test_agent": {"easyhaproxy.agent.host":"agent.quantum.local","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"test", "easyhaproxy.agent.health-check":"ssl"},
|
||||
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}}
|
||||
|
|
@ -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()
|
||||
#test_parser_ssl_letsencrypt()
|
||||
#test_parser_finds_services()
|
||||
5
tests/fixtures/no-services
vendored
5
tests/fixtures/no-services
vendored
|
|
@ -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"}
|
||||
6
tests/fixtures/services
vendored
6
tests/fixtures/services
vendored
|
|
@ -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="}
|
||||
6
tests/fixtures/services-changed-label
vendored
6
tests/fixtures/services-changed-label
vendored
|
|
@ -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="}
|
||||
4
tests/fixtures/services-letsencrypt
vendored
4
tests/fixtures/services-letsencrypt
vendored
|
|
@ -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"}
|
||||
2
tests/fixtures/services-multi-containers
vendored
2
tests/fixtures/services-multi-containers
vendored
|
|
@ -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"}
|
||||
3
tests/fixtures/services-multiple-hosts
vendored
3
tests/fixtures/services-multiple-hosts
vendored
|
|
@ -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\"}"}
|
||||
3
tests/fixtures/services-redirect-ssl
vendored
3
tests/fixtures/services-redirect-ssl
vendored
File diff suppressed because one or more lines are too long
2
tests/fixtures/services-tcp
vendored
2
tests/fixtures/services-tcp
vendored
|
|
@ -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"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue