Compare commits

..

23 Commits

Author SHA1 Message Date
75b1ccdeb2 Merge pull request 'fix' (#59) from master into prod
Reviewed-on: #59
2025-09-15 01:37:13 +03:00
Egor Matveev
40907f9d07 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 1m16s
Deploy Dev / Push (pull_request) Successful in 21s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
Deploy Prod / Build (pull_request) Successful in 5s
Deploy Prod / Push (pull_request) Successful in 10s
Deploy Prod / Deploy prod (pull_request) Successful in 11s
2025-09-15 01:33:01 +03:00
d2f8022910 Merge pull request 'master' (#57) from master into prod
Reviewed-on: #57
2025-06-12 13:30:56 +03:00
Egor Matveev
4c0bafa7cf fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 8s
Deploy Prod / Build (pull_request) Successful in 5s
Deploy Prod / Push (pull_request) Successful in 9s
Deploy Prod / Deploy prod (pull_request) Successful in 6s
2025-06-12 12:28:21 +03:00
Egor Matveev
4daae8d2a0 fix
All checks were successful
Deploy Dev / Deploy dev (pull_request) Successful in 9s
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 9s
2025-06-12 02:45:41 +03:00
Egor Matveev
c25796c5e0 fix
All checks were successful
Deploy Dev / Deploy dev (pull_request) Successful in 10s
Deploy Dev / Build (pull_request) Successful in 4s
Deploy Dev / Push (pull_request) Successful in 9s
2025-06-12 02:39:08 +03:00
Egor Matveev
eed31426b2 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
2025-06-12 02:35:03 +03:00
Egor Matveev
1822a24442 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 8s
2025-06-12 02:25:17 +03:00
Egor Matveev
52ec293aaf fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 4s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 7s
2025-06-12 02:19:13 +03:00
Egor Matveev
627f044b08 fix
All checks were successful
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 8s
Deploy Dev / Build (pull_request) Successful in 5s
2025-06-12 02:15:47 +03:00
Egor Matveev
bfb417f70b fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 7s
2025-06-12 02:09:47 +03:00
911c6225dc Merge pull request 'master' (#48) from master into prod
Reviewed-on: #48
2025-06-12 01:58:33 +03:00
Egor Matveev
fb7716e8fb fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 17s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 9s
Deploy Prod / Build (pull_request) Successful in 5s
Deploy Prod / Push (pull_request) Successful in 9s
Deploy Prod / Deploy prod (pull_request) Successful in 7s
2025-06-12 01:10:55 +03:00
Egor Matveev
32377d93fe fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 11s
Deploy Dev / Deploy dev (pull_request) Successful in 14s
2025-06-12 00:54:10 +03:00
Egor Matveev
cab3256c10 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 11s
2025-06-10 02:04:24 +03:00
Egor Matveev
f7b2b7b7ee fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
2025-06-10 02:02:10 +03:00
Egor Matveev
4baf1c94fa fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
2025-06-10 01:48:47 +03:00
Egor Matveev
fa0fc6f3bc fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
2025-06-08 11:49:18 +03:00
Egor Matveev
9b89423d95 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 16s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
2025-06-08 11:38:06 +03:00
Egor Matveev
66eefeb324 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 9s
2025-06-08 11:05:13 +03:00
Egor Matveev
7ef998d635 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 9s
2025-06-08 11:01:53 +03:00
Egor Matveev
00c307fd1e fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 10s
Deploy Dev / Deploy dev (pull_request) Successful in 11s
2025-06-08 10:58:58 +03:00
Egor Matveev
8853542182 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 1m14s
Deploy Dev / Push (pull_request) Successful in 21s
Deploy Dev / Deploy dev (pull_request) Successful in 14s
2025-06-07 13:23:40 +03:00
8 changed files with 130 additions and 189 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -6,15 +6,16 @@ services:
image: mathwave/sprint-repo:certupdater
command: worker
environment:
MINIO_HOST: "minio.develop.sprinthub.ru"
MINIO_SECRET_KEY: $MINIO_SECRET_KEY_DEV
MONGO_HOST: "mongo.develop.sprinthub.ru"
MONGO_PASSWORD: $MONGO_PASSWORD_DEV
STAGE: "development"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- configurator
- queues-development
- minio-development
- mongo-development
deploy:
mode: replicated
restart_policy:
@@ -28,3 +29,9 @@ services:
networks:
configurator:
external: true
queues-development:
external: true
minio-development:
external: true
mongo-development:
external: true

View File

@@ -6,15 +6,16 @@ services:
image: mathwave/sprint-repo:certupdater
command: worker
environment:
MINIO_HOST: "minio.sprinthub.ru"
MINIO_SECRET_KEY: $MINIO_SECRET_KEY_PROD
MONGO_HOST: "mongo.sprinthub.ru"
MONGO_PASSWORD: $MONGO_PASSWORD_PROD
STAGE: "production"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- configurator
- queues
- minio
- mongo
deploy:
mode: replicated
restart_policy:
@@ -28,3 +29,9 @@ services:
networks:
configurator:
external: true
queues:
external: true
minio:
external: true
mongo:
external: true

View File

@@ -1,7 +1,7 @@
import os
from minio import Minio
MINIO_HOST = os.getenv("MINIO_HOST", "localhost") + ":9000"
MINIO_HOST = "minio:9000"
MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY", "serviceminioadmin")
MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY", "minioadmin")

View File

@@ -1,88 +0,0 @@
import json
import os
import urllib.parse
from threading import Thread
from time import sleep
from requests import get
class ConfiguratorClient:
def __init__(self, app_name: str, stage: str, need_poll: bool = True):
self.app_name = app_name
self.stage = stage
self.endpoint = 'http://configurator/'
self.fetch_url = urllib.parse.urljoin(self.endpoint, '/api/v1/fetch')
self.config_storage = {}
self.experiment_storage = {}
self.staff_storage = {}
self.poll_data()
if need_poll:
self.poll_data_in_thread()
def poll_data_in_thread(self):
def inner():
while True:
sleep(30)
self.fetch()
Thread(target=inner, daemon=True).start()
def poll_data(self):
self.fetch(with_exception=True)
def request_with_retries(self, url, params, with_exception=False, retries_count=3):
exception_to_throw = None
for _ in range(retries_count):
try:
response = get(
url,
params=params
)
if response.status_code == 200:
return response.json()
print(f'Failed to request {url}, status_code={response.status_code}')
exception_to_throw = Exception('Not 200 status')
except Exception as exc:
print(exc)
exception_to_throw = exc
sleep(1)
print(f'Failed fetching with retries: {url}, {params}')
if with_exception:
raise exception_to_throw
def fetch(self, with_exception=False):
if self.stage == 'local':
local_platform = json.loads(open('local_platform.json', 'r').read())
self.config_storage = local_platform['configs']
self.experiment_storage = local_platform['experiments']
self.staff_storage = {
key: set(value)
for key, value in local_platform['platform_staff'].items()
}
return
response_data = self.request_with_retries(self.fetch_url, {
'project': self.app_name,
'stage': self.stage,
}, with_exception)
self.config_storage = response_data['configs']
self.experiment_storage = response_data['experiments']
self.staff_storage = {
key: set(value)
for key, value in response_data['platform_staff'].items()
}
def is_staff(self, **kwargs):
for key, value in kwargs.items():
if value in self.staff_storage[key]:
return True
return False
def get_config(self, name):
return self.config_storage[name]
def get_experiment(self, name):
return self.experiment_storage[name]
configurator = ConfiguratorClient("certupdater", os.getenv("STAGE"))

132
main.py
View File

@@ -3,7 +3,8 @@ import io
import os
import subprocess
import time
from configurator import configurator
from requests import get, post
from mongo import mongo
from blob import minio
@@ -14,79 +15,136 @@ class Response:
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)
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')
response.out, response.err = p.stdout.read().decode(
"utf-8"
), p.stderr.read().decode("utf-8")
return response
def get_hosts() -> list[str]:
if os.getenv("STAGE") == "development":
return list(set(list(configurator.get_config("hosts")) + ["platform.develop.sprinthub.ru"]))
else:
return list(set(list(configurator.get_config("hosts")) + ["platform.sprinthub.ru"]))
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) -> bool:
def update_host(host: str) -> str | None:
if os.getenv("STAGE") == "development":
container_id_run = call(f"echo $(docker ps -q -f name=infra-development_nginx)")
container_id_run = call("echo $(docker ps -q -f name=infra-development_nginx)")
else:
container_id_run = call(f"echo $(docker ps -q -f name=infra_nginx)")
container_id_run = call("echo $(docker ps -q -f name=infra_nginx)")
if container_id_run.code != 0:
print(f"something wrong {container_id_run.err}")
return False
return container_id_run.err
container_name = container_id_run.out.strip()
if not container_name:
print("No nginx container")
return False
return "no nginx container"
gen_command = f"docker exec {container_name} certbot --nginx --email emmtvv@gmail.com --agree-tos --non-interactive -d \"{host}\""
print(gen_command)
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:
print(f"failed generating certificate: {gen_cert.err}")
print("Here is the log")
print(call(f"docker exec {container_name} cat /var/log/letsencrypt/letsencrypt.log").out)
return False
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")
fullchain_command = call(
f"docker exec {container_name} cat /etc/letsencrypt/live/{host}/fullchain.pem"
)
if fullchain_command.code != 0:
print(f"failed getting fullchain: {fullchain_command.err}")
return True
return f"failed getting fullchain: {fullchain_command.err}"
privkey_command = call(f"docker exec {container_name} cat /etc/letsencrypt/live/{host}/privkey.pem")
privkey_command = call(
f"docker exec {container_name} cat /etc/letsencrypt/live/{host}/privkey.pem"
)
if privkey_command.code != 0:
print(f"failed getting fullchain: {privkey_command.err}")
return True
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 True
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
while True:
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 get_hosts():
if now + datetime.timedelta(days=14) > mongo_hosts.get(host, {"expire_time": datetime.datetime.fromtimestamp(1)})["expire_time"]:
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(f"Host {host} updated")
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(f"echo $(docker ps -q -f name=infra-development_nginx)")
result = call(f"docker restart {container_id_run.out}")
container_id_run = call(
"echo $(docker ps -q -f name=infra-development_nginx)"
)
else:
result = call("docker service update --force infra_nginx")
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")
print(result.err, result.out)
time.sleep(30)

View File

@@ -4,12 +4,11 @@ import os
MONGO_USER = os.getenv("MONGO_USER", "mongo")
MONGO_PASSWORD = os.getenv("MONGO_PASSWORD", "password")
MONGO_HOST = os.getenv("MONGO_HOST", "localhost")
class Mongo:
def __init__(self):
url = f"mongodb://{MONGO_USER}:{MONGO_PASSWORD}@{MONGO_HOST}:27017/"
url = f"mongodb://{MONGO_USER}:{MONGO_PASSWORD}@mongo:27017/"
self.client: pymongo.MongoClient = pymongo.MongoClient(url)
self.database = self.client.get_database("certupdater")
self.hosts_collection.create_index([

View File

@@ -1,42 +0,0 @@
from cachetools import TTLCache
import os
from utils.mongo import mongo
CACHE_SIZE = int(os.getenv("CACHE_SIZE", 1000))
CACHE_TTL = int(os.getenv("CACHE_TTL", 3600))
cache = TTLCache(CACHE_SIZE, CACHE_TTL)
def get_chat_info(chat_id: int) -> dict:
cached_info = cache.get(chat_id)
if cached_info is not None:
return cached_info
mongo_info = mongo.chats_collection.find_one({"chat_id": chat_id})
if mongo_info is not None:
cache[chat_id] = mongo_info
return mongo_info
chat_info = {"chat_id": chat_id, "state": "default", "probability": 100}
mongo.chats_collection.insert_one(chat_info)
cache[chat_id] = chat_info
return chat_info
def set_values(chat_id: int, **values):
cached_info = cache.get(chat_id)
if cached_info is None:
mongo_info = mongo.chats_collection.find_one({"chat_id": chat_id})
if mongo_info is None:
chat_info = {"chat_id": chat_id, "state": "default", "probability": 100}
chat_info.update(values)
mongo.chats_collection.insert_one(chat_info)
cache[chat_id] = chat_info
else:
mongo.chats_collection.update_one({"chat_id": chat_id}, {"$set": values})
mongo_info = dict(mongo_info)
mongo_info.update(values)
cache[chat_id] = mongo_info
else:
cached_info.update(values)
mongo.chats_collection.update_one({"chat_id": chat_id}, {"$set": values})