import datetime import io import os import subprocess import time from requests import get, post from mongo import mongo from blob import minio class Response: code: int out: str err: str def send_notification(text: str): post( "http://queues:1239/api/v1/put", headers={"queue": "botalka_mailbox"}, json={ "payload": { "project": "notifications-bot", "name": "telegram-bot", "body": { "text": text, "chat_id": 84367486, }, }, "seconds_to_execute": 1, "delay": None, }, ) def call(command: str) -> Response: p = subprocess.Popen( command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True ) resp = p.wait() response = Response() response.code = resp response.out, response.err = p.stdout.read().decode( "utf-8" ), p.stderr.read().decode("utf-8") return response def get_hosts() -> list[str]: response = get( f"http://configurator/api/v1/fetch?project=certupdater&stage={os.getenv("STAGE")}" ).json() hosts = response["configs"]["hosts"] return list(hosts) def update_host(host: str) -> str | None: if os.getenv("STAGE") == "development": container_id_run = call("echo $(docker ps -q -f name=infra-development_nginx)") else: container_id_run = call("echo $(docker ps -q -f name=infra_nginx)") if container_id_run.code != 0: return container_id_run.err container_name = container_id_run.out.strip() if not container_name: return "no nginx container" gen_command = f'docker exec {container_name} certbot --nginx --email emmtvv@gmail.com --agree-tos --non-interactive -d "{host}"' gen_cert = call(gen_command) if gen_cert.code != 0: log = call( f"docker exec {container_name} cat /var/log/letsencrypt/letsencrypt.log" ).out return f"failed generating certificate: {log}" fullchain_command = call( f"docker exec {container_name} cat /etc/letsencrypt/live/{host}/fullchain.pem" ) if fullchain_command.code != 0: return f"failed getting fullchain: {fullchain_command.err}" privkey_command = call( f"docker exec {container_name} cat /etc/letsencrypt/live/{host}/privkey.pem" ) if privkey_command.code != 0: return f"failed getting fullchain: {privkey_command.err}" fullchain = fullchain_command.out.encode("utf-8") privkey = privkey_command.out.encode("utf-8") minio.put_object( "certupdater", f"certificates/{host}/fullchain.pem", io.BytesIO(fullchain), len(fullchain), ) minio.put_object( "certupdater", f"certificates/{host}/privkey.pem", io.BytesIO(privkey), len(privkey), ) return None if __name__ == "__main__": while True: now = datetime.datetime.now() mongo_hosts = mongo.hosts hosts = get_hosts() print(f"got hosts {hosts}") updated = False for host in hosts: if ( now + datetime.timedelta(days=14) > mongo_hosts.get( host, {"expire_time": datetime.datetime.fromtimestamp(1)} )["expire_time"] ): success = update_host(host) if success: print(success) send_notification( f"host {host} was not updated with an error: {success}" ) else: mongo.update_date(host) updated = True send_notification(f"host {host} updated") else: print(f"Host {host} does not need to be updated") if updated: if os.getenv("STAGE") == "development": container_id_run = call( "echo $(docker ps -q -f name=infra-development_nginx)" ) else: container_id_run = call("echo $(docker ps -q -f name=infra_nginx)") print(container_id_run.code, container_id_run.out, container_id_run.err) command = f"docker exec {container_id_run.out.strip()} ./refre.sh" print(command) restart = call(command) print(restart.code, restart.out, restart.err) send_notification(f"Balancer for {os.getenv("STAGE")} was restarted") time.sleep(30)