autoconf - fixed infinite lock

This commit is contained in:
bunkerity 2021-08-13 10:02:14 +02:00
parent b199464a73
commit c9a6b6c27d
No known key found for this signature in database
GPG Key ID: 3D80806F12602A7C
7 changed files with 127 additions and 109 deletions

View File

@ -8,10 +8,9 @@ from logger import log
class Config : class Config :
def __init__(self, type, api_uri, lock=None, http_port="8080") : def __init__(self, type, api_uri, http_port="8080") :
self.__type = type self.__type = type
self.__api_uri = api_uri self.__api_uri = api_uri
self.__lock = lock
self.__http_port = http_port self.__http_port = http_port
def __jobs(self) : def __jobs(self) :
@ -29,14 +28,7 @@ class Config :
return True return True
def gen(self, env) : def gen(self, env) :
locked = False
try : try :
# Lock
if self.__lock :
log("config", "ERROR", "lock")
self.__lock.acquire()
locked = True
# Write environment variables to a file # Write environment variables to a file
with open("/tmp/variables.env", "w") as f : with open("/tmp/variables.env", "w") as f :
for k, v in env.items() : for k, v in env.items() :
@ -45,12 +37,6 @@ class Config :
# Call the generator # Call the generator
proc = subprocess.run(["/bin/su", "-c", "/opt/bunkerized-nginx/gen/main.py --settings /opt/bunkerized-nginx/settings.json --templates /opt/bunkerized-nginx/confs --output /etc/nginx --variables /tmp/variables.env", "nginx"], capture_output=True) proc = subprocess.run(["/bin/su", "-c", "/opt/bunkerized-nginx/gen/main.py --settings /opt/bunkerized-nginx/settings.json --templates /opt/bunkerized-nginx/confs --output /etc/nginx --variables /tmp/variables.env", "nginx"], capture_output=True)
# Unlock
if self.__lock :
log("config", "ERROR", "release")
self.__lock.release()
locked = False
# Print stdout/stderr # Print stdout/stderr
stdout = proc.stdout.decode("ascii") stdout = proc.stdout.decode("ascii")
stderr = proc.stderr.decode("ascii") stderr = proc.stderr.decode("ascii")
@ -68,8 +54,6 @@ class Config :
except Exception as e : 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())
if locked :
self.__lock.release()
return False return False
def reload(self, instances) : def reload(self, instances) :
@ -137,9 +121,6 @@ class Config :
return False return False
def __api_call(self, instances, path) : def __api_call(self, instances, path) :
if self.__lock :
log("config", "ERROR", "lock")
self.__lock.acquire()
ret = True ret = True
nb = 0 nb = 0
urls = [] urls = []
@ -174,7 +155,4 @@ class Config :
else : else :
log("config", "INFO", "failed API order to " + url) log("config", "INFO", "failed API order to " + url)
ret = False ret = False
if self.__lock :
log("config", "ERROR", "release")
self.__lock.release()
return ret and nb > 0 return ret and nb > 0

View File

@ -11,7 +11,7 @@ class Type(Enum) :
class Controller(ABC) : class Controller(ABC) :
def __init__(self, type, api_uri=None, lock=None, http_port="8080") : def __init__(self, type, api_uri=None, lock=None, http_port="8080") :
self._config = Config(type, api_uri, lock=lock, http_port=http_port) self._config = Config(type, api_uri, http_port=http_port)
self.lock = lock self.lock = lock
@abstractmethod @abstractmethod
@ -27,7 +27,11 @@ class Controller(ABC) :
return fixed_env return fixed_env
def gen_conf(self, env) : def gen_conf(self, env) :
return self._config.gen(env) try :
ret = self._config.gen(env)
except :
ret = False
return ret
@abstractmethod @abstractmethod
def wait(self) : def wait(self) :
@ -42,4 +46,8 @@ class Controller(ABC) :
pass pass
def _reload(self, instances) : def _reload(self, instances) :
return self._config.reload(instances) try :
ret = self._config.reload(instances)
except :
ret = False
return ret

View File

@ -42,30 +42,39 @@ class DockerController(Controller.Controller) :
for event in self.__client.events(decode=True, filters={"type": "container"}) : for event in self.__client.events(decode=True, filters={"type": "container"}) :
new_env = self.get_env() new_env = self.get_env()
if new_env != old_env : if new_env != old_env :
log("controller", "INFO", "generating new configuration") try :
if self.gen_conf(new_env) : log("controller", "INFO", "generating new configuration")
old_env = new_env.copy() if self.gen_conf(new_env) :
log("controller", "INFO", "successfully generated new configuration") old_env = new_env.copy()
if self.reload() : log("controller", "INFO", "successfully generated new configuration")
log("controller", "INFO", "successful reload") if self.reload() :
log("controller", "INFO", "successful reload")
else :
log("controller", "ERROR", "failed reload")
else : else :
log("controller", "ERROR", "failed reload") log("controller", "ERROR", "can't generate new configuration")
else : except :
log("controller", "ERROR", "can't generate new configuration") log("controller", "ERROR", "exception while receiving event")
def reload(self) : def reload(self) :
return self._reload(self.__get_instances()) return self._reload(self.__get_instances())
def wait(self) : def wait(self) :
# Wait for a container try :
instances = self.__get_instances() # Wait for a container
while len(instances) == 0 :
time.sleep(1)
instances = self.__get_instances() instances = self.__get_instances()
# Generate first config while len(instances) == 0 :
env = self.get_env() time.sleep(1)
if not self.gen_conf(env) : instances = self.__get_instances()
return False, env # Generate first config
# Wait for nginx env = self.get_env()
return self._config.wait(instances), env if not self.gen_conf(env) :
self.lock.release()
return False, env
# Wait for nginx
self.lock.release()
return self._config.wait(instances), env
except :
pass
return False, {}

View File

@ -14,7 +14,6 @@ class IngressController(Controller.Controller) :
self.__api = client.CoreV1Api() self.__api = client.CoreV1Api()
self.__extensions_api = client.ExtensionsV1beta1Api() self.__extensions_api = client.ExtensionsV1beta1Api()
self.__old_env = {} self.__old_env = {}
self.__internal_lock = Lock()
def __get_pods(self) : def __get_pods(self) :
return self.__api.list_pod_for_all_namespaces(watch=False, label_selector="bunkerized-nginx").items return self.__api.list_pod_for_all_namespaces(watch=False, label_selector="bunkerized-nginx").items
@ -110,68 +109,83 @@ class IngressController(Controller.Controller) :
def __watch_pod(self) : def __watch_pod(self) :
w = watch.Watch() w = watch.Watch()
for event in w.stream(self.__api.list_pod_for_all_namespaces, label_selector="bunkerized-nginx") : for event in w.stream(self.__api.list_pod_for_all_namespaces, label_selector="bunkerized-nginx") :
self.__internal_lock.acquire() self.lock.acquire()
new_env = self.get_env() new_env = self.get_env()
if new_env != self.__old_env : if new_env != self.__old_env :
if self.gen_conf(new_env) : try :
self.__old_env = new_env.copy() if self.gen_conf(new_env) :
log("CONTROLLER", "INFO", "successfully generated new configuration") self.__old_env = new_env.copy()
if self.reload() : log("CONTROLLER", "INFO", "successfully generated new configuration")
log("controller", "INFO", "successful reload") if self.reload() :
else : log("controller", "INFO", "successful reload")
log("controller", "ERROR", "failed reload") else :
self.__internal_lock.release() log("controller", "ERROR", "failed reload")
except :
log("controller", "ERROR", "exception while receiving event")
self.lock.release()
def __watch_ingress(self) : def __watch_ingress(self) :
w = watch.Watch() w = watch.Watch()
for event in w.stream(self.__extensions_api.list_ingress_for_all_namespaces, label_selector="bunkerized-nginx") : for event in w.stream(self.__extensions_api.list_ingress_for_all_namespaces, label_selector="bunkerized-nginx") :
self.__internal_lock.acquire() self.lock.acquire()
new_env = self.get_env() new_env = self.get_env()
if new_env != self.__old_env : if new_env != self.__old_env :
if self.gen_conf(new_env) : try :
self.__old_env = new_env.copy() if self.gen_conf(new_env) :
log("CONTROLLER", "INFO", "successfully generated new configuration") self.__old_env = new_env.copy()
if self.reload() : log("CONTROLLER", "INFO", "successfully generated new configuration")
log("controller", "INFO", "successful reload") if self.reload() :
else : log("controller", "INFO", "successful reload")
log("controller", "ERROR", "failed reload") else :
self.__internal_lock.release() log("controller", "ERROR", "failed reload")
except :
log("controller", "ERROR", "exception while receiving event")
self.lock.release()
def __watch_service(self) : def __watch_service(self) :
w = watch.Watch() w = watch.Watch()
for event in w.stream(self.__api.list_service_for_all_namespaces, label_selector="bunkerized-nginx") : for event in w.stream(self.__api.list_service_for_all_namespaces, label_selector="bunkerized-nginx") :
self.__internal_lock.acquire() self.lock.acquire()
new_env = self.get_env() new_env = self.get_env()
if new_env != self.__old_env : if new_env != self.__old_env :
if self.gen_conf(new_env) : try :
self.__old_env = new_env.copy() if self.gen_conf(new_env) :
log("CONTROLLER", "INFO", "successfully generated new configuration") self.__old_env = new_env.copy()
if self.reload() : log("CONTROLLER", "INFO", "successfully generated new configuration")
log("controller", "INFO", "successful reload") if self.reload() :
else : log("controller", "INFO", "successful reload")
log("controller", "ERROR", "failed reload") else :
self.__internal_lock.release() log("controller", "ERROR", "failed reload")
except :
log("controller", "ERROR", "exception while receiving event")
self.lock.release()
def reload(self) : def reload(self) :
return self._reload(self.__get_services(autoconf=True)) return self._reload(self.__get_services(autoconf=True))
def wait(self) : def wait(self) :
# Wait for at least one bunkerized-nginx pod self.lock.acquire()
pods = self.__get_pods() try :
while len(pods) == 0 : # Wait for at least one bunkerized-nginx pod
time.sleep(1)
pods = self.__get_pods() pods = self.__get_pods()
while len(pods) == 0 :
time.sleep(1)
pods = self.__get_pods()
# Wait for at least one bunkerized-nginx service # Wait for at least one bunkerized-nginx service
services = self.__get_services(autoconf=True)
while len(services) == 0 :
time.sleep(1)
services = self.__get_services(autoconf=True) services = self.__get_services(autoconf=True)
while len(services) == 0 :
time.sleep(1)
services = self.__get_services(autoconf=True)
# Generate first config # Generate first config
env = self.get_env() env = self.get_env()
if not self.gen_conf(env) : if not self.gen_conf(env) :
return False, env return False, env
# Wait for bunkerized-nginx # Wait for bunkerized-nginx
return self._config.wait(services), env return self._config.wait(services), env
except :
pass
self.lock.release()
return False, {}

View File

@ -13,12 +13,10 @@ class ReloadServerHandler(socketserver.StreamRequestHandler):
if not data or not data in [b"lock", b"reload", b"unlock"] : if not data or not data in [b"lock", b"reload", b"unlock"] :
break break
if data == b"lock" : if data == b"lock" :
log("RELOADSERVER", "ERROR", "lock")
self.server.controller.lock.acquire() self.server.controller.lock.acquire()
locked = True locked = True
self.request.sendall(b"ok") self.request.sendall(b"ok")
elif data == b"unlock" : elif data == b"unlock" :
log("RELOADSERVER", "ERROR", "unlock")
self.server.controller.lock.release() self.server.controller.lock.release()
locked = False locked = False
self.request.sendall(b"ok") self.request.sendall(b"ok")

View File

@ -44,29 +44,40 @@ class SwarmController(Controller.Controller) :
for event in self.__client.events(decode=True, filters={"type": "service"}) : for event in self.__client.events(decode=True, filters={"type": "service"}) :
new_env = self.get_env() new_env = self.get_env()
if new_env != old_env : if new_env != old_env :
log("controller", "INFO", "generating new configuration") self.lock.acquire()
if self.gen_conf(new_env) : try :
old_env = new_env.copy() log("controller", "INFO", "generating new configuration")
log("controller", "INFO", "successfully generated new configuration") if self.gen_conf(new_env) :
if self.reload() : old_env = new_env.copy()
log("controller", "INFO", "successful reload") log("controller", "INFO", "successfully generated new configuration")
if self.reload() :
log("controller", "INFO", "successful reload")
else :
log("controller", "ERROR", "failed reload")
else : else :
log("controller", "ERROR", "failed reload") log("controller", "ERROR", "can't generate new configuration")
else : except :
log("controller", "ERROR", "can't generate new configuration") log("controller", "ERROR", "exception while receiving event")
self.lock.release()
def reload(self) : def reload(self) :
return self._reload(self.__get_instances()) return self._reload(self.__get_instances())
def wait(self) : def wait(self) :
# Wait for a service self.lock.acquire()
instances = self.__get_instances() try :
while len(instances) == 0 : # Wait for a service
time.sleep(1)
instances = self.__get_instances() instances = self.__get_instances()
# Generate first config while len(instances) == 0 :
env = self.get_env() time.sleep(1)
if not self.gen_conf(env) : instances = self.__get_instances()
return False, env # Generate first config
# Wait for nginx env = self.get_env()
return self._config.wait(instances), env if not self.gen_conf(env) :
return False, env
# Wait for nginx
return self._config.wait(instances), env
except :
pass
self.lock.release()
return False, {}

View File

@ -319,7 +319,7 @@ $ docker service create \
Or do the same with docker-compose if you wish : Or do the same with docker-compose if you wish :
```yaml ```yaml
version: '3' version: '3.8'
services: services: