autoconf - various bug fixes with DockerController

This commit is contained in:
bunkerity
2021-07-29 15:43:51 +02:00
parent 7180378d0c
commit 1a32e7c02c
16 changed files with 134 additions and 82 deletions

View File

@@ -1,19 +1,18 @@
#!/usr/bin/python3
import utils
import subprocess, shutil, os, traceback, requests, time
from Controller import ControllerType
import Controller
from logger import log
class Config :
def __init__(type, api_uri) :
def __init__(self, type, api_uri) :
self.__type = type
self.__api_uri = api_uri
def gen(env) :
def gen(self, env) :
try :
# Write environment variables to a file
with open("/tmp/variables.env", "w") as f :
@@ -27,32 +26,32 @@ class Config :
stdout = proc.stdout.decode("ascii")
stderr = proc.stderr.decode("ascii")
if len(stdout) > 1 :
log("CONFIG", "INFO", "generator output : " + stdout)
log("config", "INFO", "generator output : " + stdout)
if stderr != "" :
log("CONFIG", "ERROR", "generator error : " + stderr)
log("config", "ERROR", "generator error : " + stderr)
# We're done
if proc.returncode == 0 :
if self.__type == ControllerType.SWARM or self.__type == ControllerType.KUBERNETES :
if self.__type == Controller.Type.SWARM or self.__type == Controller.Type.KUBERNETES :
return self.__jobs()
return True
log("CONFIG", "ERROR", "error while generating config (return code = " + str(proc.returncode) + ")")
log("config", "ERROR", "error while generating config (return code = " + str(proc.returncode) + ")")
except Exception as e :
log("CONFIG", "ERROR", "exception while generating site config : " + traceback.format_exc())
log("config", "ERROR", "exception while generating site config : " + traceback.format_exc())
return False
def reload(self, instances) :
ret = True
if self.__type == ControllerType.DOCKER :
if self.__type == Controller.Type.DOCKER :
for instance in instances :
try :
instance.kill("SIGHUP")
except :
ret = False
elif self.__type == ControllerType.SWARM :
elif self.__type == Controller.Type.SWARM :
ret = self.__api_call(instances, "/reload")
elif self.__type == ControllerType.KUBERNETES :
elif self.__type == Controller.Type.KUBERNETES :
ret = self.__api_call(instances, "/reload")
return ret
@@ -61,9 +60,9 @@ class Config :
def wait(self, instances) :
ret = True
if self.__type == ControllerType.DOCKER :
if self.__type == Controller.Type.DOCKER :
ret = self.__wait_docker()
elif self.__type == ControllerType.SWARM or self.__type == ControllerType.KUBERNETES :
elif self.__type == Controller.Type.SWARM or self.__type == Controller.Type.KUBERNETES :
ret = self.__wait_api()
return ret
@@ -96,20 +95,20 @@ class Config :
started = True
break
i = i + 1
log("CONFIG", "INFO" "waiting " + str(i) + " seconds before retrying to contact bunkerized-nginx instances")
log("config", "INFO" "waiting " + str(i) + " seconds before retrying to contact bunkerized-nginx instances")
if started :
log("CONFIG", "INFO", "bunkerized-nginx instances started")
log("config", "INFO", "bunkerized-nginx instances started")
return True
else :
log("CONFIG", "ERROR", "bunkerized-nginx instances are not started")
log("config", "ERROR", "bunkerized-nginx instances are not started")
except Exception as e :
log("CONFIG", "ERROR", "exception while waiting for bunkerized-nginx instances : " + traceback.format_exc())
log("config", "ERROR", "exception while waiting for bunkerized-nginx instances : " + traceback.format_exc())
return False
def __api_call(self, instances, path) :
ret = True
urls = []
if self.__type == ControllerType.SWARM :
if self.__type == Controller.Type.SWARM :
for instance in instances :
name = instance.name
for task in instance.tasks() :
@@ -117,8 +116,8 @@ class Config :
taskID = task["ID"]
url = "http://" + name + "." + nodeID + "." + taskID + ":8080" + self.__api_uri + path
urls.append(url)
elif self.__type == ControllerType.KUBERNETES :
log("CONFIG", "ERROR", "TODO get urls for k8s")
elif self.__type == Controller.Type.KUBERNETES :
log("config", "ERROR", "TODO get urls for k8s")
for url in urls :
try :
@@ -126,8 +125,8 @@ class Config :
except :
pass
if req and req.status_code == 200 and req.text == "ok" :
log("CONFIG", "INFO", "successfully sent API order to " + url)
log("config", "INFO", "successfully sent API order to " + url)
else :
log("CONFIG", "INFO", "failed API order to " + url)
log("config", "INFO", "failed API order to " + url)
ret = False
return ret

View File

@@ -1,7 +1,9 @@
from abc import ABC, abstractmethod
from enum import Enum
from Config import Config
class ControllerType(Enum) :
class Type(Enum) :
DOCKER = 1
SWARM = 2
KUBERNETES = 3
@@ -28,7 +30,7 @@ class Controller(ABC) :
return self.__config.gen(env)
@abstractmethod
def process_events(self) :
def process_events(self, current_env) :
pass
@abstractmethod

View File

@@ -1,11 +1,12 @@
import docker
from Controller import Controller, ControllerType
import utils
import Controller
class DockerController(Controller) :
from logger import log
class DockerController(Controller.Controller) :
def __init__(self) :
super().__init__(ControllerType.DOCKER)
super().__init__(Controller.Type.DOCKER)
# TODO : honor env vars like DOCKER_HOST
self.__client = docker.DockerClient(base_url='unix:///var/run/docker.sock')
@@ -21,22 +22,38 @@ class DockerController(Controller) :
for variable in instance.attrs["Config"]["Env"] :
env[variable.split("=")[0]] = variable.replace(variable.split("=")[0] + "=", "", 1)
first_servers = []
if "SERVER_NAME" in env :
if "SERVER_NAME" in env and env["SERVER_NAME"] != "" :
first_servers = env["SERVER_NAME"].split(" ")
for container in self.__get_containers() :
first_server = container.labels["bunkerized-nginx.SERVER_NAME"].split(" ")[0]
first_servers.append(first_server)
for variable, value in instance.labels.items() :
if variable.startswith("bunkerized-nginx.") :
for variable, value in container.labels.items() :
if variable.startswith("bunkerized-nginx.") and variable != "bunkerized-nginx.AUTOCONF" :
env[first_server + "_" + variable.replace("bunkerized-nginx.", "", 1)] = value
env["SERVER_NAME"] = " ".join(first_servers)
if len(first_servers) == 0 :
env["SERVER_NAME"] = ""
else :
env["SERVER_NAME"] = " ".join(first_servers)
return self._fix_env(env)
def process_events(self, current_env) :
old_env = current_env
for event in client.events(decode=True, filter={"type": "container", "label": ["bunkerized-nginx.AUTOCONF", "bunkerized-nginx.SERVER_NAME"]}) :
# TODO : check why filter isn't working as expected
#for event in self.__client.events(decode=True, filters={"type": "container", "label": ["bunkerized-nginx.AUTOCONF", "bunkerized-nginx.SERVER_NAME"]}) :
for event in self.__client.events(decode=True, filters={"type": "container"}) :
new_env = self.get_env()
if new_env != old_env :
log("controller", "INFO", "generating new configuration")
if self.gen_conf(new_env) :
old_env.copy(new_env)
log("CONTROLLER", "INFO", "successfully generated new configuration")
old_env = new_env.copy()
log("controller", "INFO", "successfully generated new configuration")
if self.reload() :
log("controller", "INFO", "successful reload")
else :
log("controller", "ERROR", "failed reload")
else :
log("controller", "ERROR", "can't generate new configuration")
def reload(self) :
return self._reload(self.__get_instances())

View File

@@ -1,14 +1,14 @@
from kubernetes import client, config, watch
from threading import Thread
from Controller import Controller, ControllerType
import Controller
from logger import log
class IngressController :
class IngressController(Controller.Controller) :
def __init__(self, api_uri) :
super().__init__(ControllerType.KUBERNETES, api_uri=api_uri, lock=Lock())
super().__init__(Controller.Type.KUBERNETES, api_uri=api_uri, lock=Lock())
config.load_incluster_config()
self.__api = client.CoreV1Api()
self.__extensions_api = client.ExtensionsV1beta1Api()
@@ -79,7 +79,7 @@ class IngressController :
new_env = self.get_env()
if new_env != self.__old_env() :
if self.gen_conf(new_env, lock=False) :
self.__old_env.copy(new_env)
self.__old_env = new_env.copy()
log("CONTROLLER", "INFO", "successfully generated new configuration")
self.lock.release()
@@ -90,6 +90,9 @@ class IngressController :
new_env = self.get_env()
if new_env != self.__old_env() :
if self.gen_conf(new_env, lock=False) :
self.__old_env.copy(new_env)
self.__old_env = new_env.copy()
log("CONTROLLER", "INFO", "successfully generated new configuration")
self.lock.release()
def reload(self) :
return self._reload(self.__get_ingresses())

View File

@@ -21,7 +21,7 @@ class ReloadServerHandler(socketserver.StreamRequestHandler):
locked = False
self.request.sendall(b"ok")
elif data == b"reload" :
ret = self.server.controller.reload() :
ret = self.server.controller.reload()
if ret :
self.request.sendall(b"ok")
else :

View File

@@ -1,11 +1,13 @@
import docker
from Controller import Controller, ControllerType
import utils
class SwarmController(Controller) :
from logger import log
import Controller
class SwarmController(Controller.Controller) :
def __init__(self, api_uri) :
super().__init__(ControllerType.SWARM, api_uri=api_uri, lock=Lock())
super().__init__(Controller.Type.SWARM, api_uri=api_uri, lock=Lock())
# TODO : honor env vars like DOCKER_HOST
self.__client = docker.DockerClient(base_url='unix:///var/run/docker.sock')
@@ -21,24 +23,39 @@ class SwarmController(Controller) :
for variable in instance.attrs["Spec"]["TaskTemplate"]["ContainerSpec"]["Env"] :
env[variable.split("=")[0]] = variable.replace(variable.split("=")[0] + "=", "", 1)
first_servers = []
if "SERVER_NAME" in env :
if "SERVER_NAME" in env and env["SERVER_NAME"] != "" :
first_servers = env["SERVER_NAME"].split(" ")
for service in self.__get_services() :
first_server = service.attrs["Spec"]["Labels"]["bunkerized-nginx.SERVER_NAME"].split(" ")[0]
first_servers.append(first_server)
for variable, value in service.attrs["Spec"]["Labels"].items() :
if variable.startswith("bunkerized-nginx.") :
if variable.startswith("bunkerized-nginx.") and variable != "bunkerized-nginx.AUTOCONF" :
env[first_server + "_" + variable.replace("bunkerized-nginx.", "", 1)] = value
env["SERVER_NAME"] = " ".join(first_servers)
if len(first_servers) == 0 :
env["SERVER_NAME"] = ""
else :
env["SERVER_NAME"] = " ".join(first_servers)
return self._fix_env(env)
def process_events(self, current_env) :
old_env = current_env
for event in client.events(decode=True, filter={"type": "service", "label": ["bunkerized-nginx.AUTOCONF", "bunkerized-nginx.SERVER_NAME"]}) :
# TODO : check why filter isn't working as expected
#for event in self.__client.events(decode=True, filters={"type": "service", "label": ["bunkerized-nginx.AUTOCONF", "bunkerized-nginx.SERVER_NAME"]}) :
for event in self.__client.events(decode=True, filters={"type": "service"}) :
new_env = self.get_env()
if new_env != old_env :
self.lock.acquire()
if self.gen_conf(new_env, lock=False) :
old_env.copy(new_env)
log("CONTROLLER", "INFO", "successfully generated new configuration")
log("controller", "INFO", "generating new configuration")
if self.gen_conf(new_env) :
old_env = new_env.copy()
log("controller", "INFO", "successfully generated new configuration")
if self.reload() :
log("controller", "INFO", "successful reload")
else :
log("controller", "ERROR", "failed reload")
else :
log("controller", "ERROR", "can't generate new configuration")
self.lock.release()
def reload(self) :
return self._reload(self.__get_instances())

View File

@@ -6,7 +6,7 @@ import docker, os, stat, sys, select, threading
from DockerController import DockerController
from SwarmController import SwarmController
from KubernetesController import KubernetesController
from IngressController import IngressController
from logger import log
@@ -21,7 +21,7 @@ if swarm :
controller = SwarmController(api_uri)
elif kubernetes :
log("autoconf", "INFO", "kubernetes mode detected")
controller = KubernetesController(api_uri)
controller = IngressController(api_uri)
else :
log("autoconf", "INFO", "docker mode detected")
controller = DockerController()
@@ -32,8 +32,8 @@ if swarm or kubernetes :
# Apply the first config for existing services
current_env = controller.get_env()
if env != {} :
if current_env != {} :
controller.gen_conf(current_env)
# Process events
controller.process_events()
controller.process_events(current_env)