187 lines
6.7 KiB
Python
187 lines
6.7 KiB
Python
from easymapping import HaproxyConfigGenerator
|
|
from functions import Functions
|
|
import yaml
|
|
import sys
|
|
import os
|
|
import json
|
|
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):
|
|
# Abstract
|
|
pass
|
|
|
|
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 inspect_network(self):
|
|
docker = "/usr/bin/docker"
|
|
|
|
containers_list = Functions.run_bash('DOCKER_PROCESSOR', docker + " ps -q", log_output=False)
|
|
containers_list.sort()
|
|
containers = list(set(containers_list))
|
|
self.parsed_object = {}
|
|
for container in containers:
|
|
self.parsed_object[container] = json.loads(''.join(Functions.run_bash('DOCKER_PROCESSOR', docker + ' inspect --format "{{ json .Config.Labels }}" ' + container, log_output=False)))
|
|
|
|
def parse(self):
|
|
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
|
|
|
|
|
|
class Swarm(ProcessorInterface):
|
|
def inspect_network(self):
|
|
docker = "/usr/bin/docker"
|
|
|
|
node_list = ' '.join(Functions.run_bash('SWARM_PROCESSOR', docker + ' node ls -q', False))
|
|
containers_list_raw = Functions.run_bash('SWARM_PROCESSOR', docker + ' node ps ' + node_list + ' --format "{{ .Name }}" --filter desired-state=running', log_output=False)
|
|
containers_list = []
|
|
for container in containers_list_raw:
|
|
containers_list.append(container.split('.')[0])
|
|
containers_list.sort()
|
|
containers = list(set(containers_list))
|
|
self.parsed_object = {}
|
|
for container in containers:
|
|
self.parsed_object[container] = json.loads(''.join(Functions.run_bash('SWARM_PROCESSOR', docker + ' service inspect --format "{{ json .Spec.Labels }}" ' + container, log_output=False)))
|
|
|
|
def parse(self):
|
|
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
|
|
|
|
|
|
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)
|
|
|
|
def parse(self):
|
|
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
|
|
|
|
|
|
|