mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
fix bwcli when using redis sentinels, init work on linux ha and init work on static instances
This commit is contained in:
parent
936600a0d7
commit
21be7c12ac
8 changed files with 281 additions and 119 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -2,17 +2,16 @@
|
|||
|
||||
## v1.5.7 - ????/??/??
|
||||
|
||||
- [LINUX] Fix potential issues when removing the bunkerweb package
|
||||
- [BUGFIX] Fix rare error when the cache is not properly initialized and jobs are executed
|
||||
- [FEATURE] Add an automatic renaming of old database tables when upgrading to a new version in order to avoid data loss
|
||||
- [FEATURE] Add the possibility to add custom bwcli commands in plugins
|
||||
- [BUGFIX] Fix bug when downloading new mmdb files
|
||||
- [BUGFIX] Remove potential false positives with ModSecurity on the jobs page of the web UI
|
||||
- [BUGFIX] Fix bwcli not working with Redis sentinel
|
||||
- [BUGFIX] Fix potential issues when removing the bunkerweb Linux package
|
||||
- [FEATURE] Add backup plugin to backup and restore easily the database
|
||||
- [FEATURE] Add LETS_ENCRYPT_CLEAR_OLD_CERTS setting to control if old certificates should be removed when generating Let's Encrypt certificates (default is no)
|
||||
- [FEATURE] Add DISABLE_DEFAULT_SERVER_STRICT_SNI setting to allow/block requests when SNI is unknown or unset (default is no)
|
||||
- [MISC] Remove potential false positives with ModSecurity on the jobs page of the web UI
|
||||
- [MISC] Fix rare bug when downloading new mmdb files
|
||||
- [DOCUMENTATION] Add procedure to follow when upgrading from 1.5.7+
|
||||
- [DOCUMENTATION] Add documentation about the procedure to follow when upgrading from a version prior to 1.5.0
|
||||
- [DOCUMENTATION] Add upgrade procedure for 1.5.7+
|
||||
- [MISC] Support custom bwcli commands using plugins
|
||||
- [DEPS] Updated LuaJIT version to v2.1-20240314
|
||||
|
||||
## v1.5.6 - 2024/03/25
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ class CLI(ApiCaller):
|
|||
if self.__use_redis:
|
||||
self.__logger.info("Fetching redis configuration")
|
||||
redis_host = self.__get_variable("REDIS_HOST")
|
||||
if redis_host:
|
||||
sentinel_hosts = self.__get_variable("REDIS_SENTINEL_HOSTS")
|
||||
if redis_host or sentinel_hosts:
|
||||
redis_port = self.__get_variable("REDIS_PORT", "6379")
|
||||
assert isinstance(redis_port, str), "REDIS_PORT is not a string"
|
||||
if not redis_port.isdigit():
|
||||
|
|
@ -107,8 +108,6 @@ class CLI(ApiCaller):
|
|||
redis_keepalive_pool = "10"
|
||||
redis_keepalive_pool = int(redis_keepalive_pool)
|
||||
|
||||
self.__logger.info("Redis configuration is valid")
|
||||
|
||||
redis_ssl = self.__get_variable("REDIS_SSL", "no") == "yes"
|
||||
username = self.__get_variable("REDIS_USERNAME", None) or None
|
||||
password = self.__get_variable("REDIS_PASSWORD", None) or None
|
||||
|
|
@ -177,7 +176,7 @@ class CLI(ApiCaller):
|
|||
self.__use_redis = False
|
||||
self.__logger.info("Connected to redis")
|
||||
else:
|
||||
self.__logger.error("USE_REDIS is set to yes but REDIS_HOST is not set, disabling redis")
|
||||
self.__logger.error("USE_REDIS is set to yes but REDIS_HOST or REDIS_SENTINEL_HOSTS is not set, disabling redis")
|
||||
self.__use_redis = False
|
||||
|
||||
if self.__integration == "linux":
|
||||
|
|
|
|||
|
|
@ -197,6 +197,8 @@ class Configurator:
|
|||
"NJS_VERSION",
|
||||
"PKG_RELEASE",
|
||||
"DOCKER_HOST",
|
||||
"SLAVE_MODE",
|
||||
"MASTER_MODE",
|
||||
)
|
||||
):
|
||||
self.__logger.warning(f"Ignoring variable {variable} : {err}")
|
||||
|
|
|
|||
|
|
@ -317,12 +317,12 @@
|
|||
"emerg"
|
||||
]
|
||||
},
|
||||
"STATIC_INSTANCES": {
|
||||
"OVERRIDE_INSTANCES": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "List of additional BunkerWeb instances separated with spaces (format : fqdn-or-ip:5000 fqdn-or-ip:5000)",
|
||||
"id": "static-instances",
|
||||
"label": "Static instances",
|
||||
"help": "List of BunkerWeb instances separated with spaces (format : fqdn-or-ip:5000 fqdn-or-ip:5000)",
|
||||
"id": "override-instances",
|
||||
"label": "Override instances",
|
||||
"regex": "^.*$",
|
||||
"type": "text"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,98 +100,104 @@ function start() {
|
|||
log "SYSTEMCTL" "ℹ️" "Created dummy variables.env file"
|
||||
fi
|
||||
|
||||
# Create PID folder
|
||||
if [ ! -f /var/run/bunkerweb ] ; then
|
||||
mkdir -p /var/run/bunkerweb
|
||||
chown nginx:nginx /var/run/bunkerweb
|
||||
fi
|
||||
|
||||
# Stop scheduler if it's running
|
||||
stop_scheduler
|
||||
|
||||
# Stop nginx if it's running
|
||||
stop_nginx
|
||||
|
||||
# Generate temp conf for jobs and start nginx
|
||||
DNS_RESOLVERS="$(grep "^DNS_RESOLVERS=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$DNS_RESOLVERS" = "" ] ; then
|
||||
DNS_RESOLVERS="8.8.8.8 8.8.4.4"
|
||||
fi
|
||||
API_LISTEN_IP="$(grep "^API_LISTEN_IP=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_LISTEN_IP" = "" ] ; then
|
||||
API_LISTEN_IP="127.0.0.1"
|
||||
fi
|
||||
API_HTTP_PORT="$(grep "^API_HTTP_PORT=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_HTTP_PORT" = "" ] ; then
|
||||
API_HTTP_PORT="5000"
|
||||
fi
|
||||
API_SERVER_NAME="$(grep "^API_SERVER_NAME=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_SERVER_NAME" = "" ] ; then
|
||||
API_SERVER_NAME="bwapi"
|
||||
fi
|
||||
API_WHITELIST_IP="$(grep "^API_WHITELIST_IP=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_WHITELIST_IP" = "" ] ; then
|
||||
API_WHITELIST_IP="127.0.0.0/8"
|
||||
fi
|
||||
USE_REAL_IP="$(grep "^USE_REAL_IP=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$USE_REAL_IP" = "" ] ; then
|
||||
USE_REAL_IP="no"
|
||||
fi
|
||||
USE_PROXY_PROTOCOL="$(grep "^USE_PROXY_PROTOCOL=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$USE_PROXY_PROTOCOL" = "" ] ; then
|
||||
USE_PROXY_PROTOCOL="no"
|
||||
fi
|
||||
REAL_IP_FROM="$(grep "^REAL_IP_FROM=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$REAL_IP_FROM" = "" ] ; then
|
||||
REAL_IP_FROM="192.168.0.0/16 172.16.0.0/12 10.0.0.0/8"
|
||||
fi
|
||||
REAL_IP_HEADER="$(grep "^REAL_IP_HEADER=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$REAL_IP_HEADER" = "" ] ; then
|
||||
REAL_IP_HEADER="X-Forwarded-For"
|
||||
fi
|
||||
HTTP_PORT="$(grep "^HTTP_PORT=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$HTTP_PORT" = "" ] ; then
|
||||
HTTP_PORT="80"
|
||||
fi
|
||||
HTTPS_PORT="$(grep "^HTTPS_PORT=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$HTTPS_PORT" = "" ] ; then
|
||||
HTTPS_PORT="443"
|
||||
fi
|
||||
MODSECURITY_CRS_VERSION="$(grep "^MODSECURITY_CRS_VERSION=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$MODSECURITY_CRS_VERSION" = "" ] ; then
|
||||
MODSECURITY_CRS_VERSION="4"
|
||||
fi
|
||||
sudo -E -u nginx -g nginx /bin/bash -c "echo -ne 'IS_LOADING=yes\nUSE_BUNKERNET=no\nSEND_ANONYMOUS_REPORT=no\nSERVER_NAME=\nMODSECURITY_CRS_VERSION=${MODSECURITY_CRS_VERSION}\nDNS_RESOLVERS=${DNS_RESOLVERS}\nAPI_HTTP_PORT=${API_HTTP_PORT}\nAPI_LISTEN_IP=${API_LISTEN_IP}\nAPI_SERVER_NAME=${API_SERVER_NAME}\nAPI_WHITELIST_IP=${API_WHITELIST_IP}\nUSE_REAL_IP=${USE_REAL_IP}\nUSE_PROXY_PROTOCOL=${USE_PROXY_PROTOCOL}\nREAL_IP_FROM=${REAL_IP_FROM}\nREAL_IP_HEADER=${REAL_IP_HEADER}\nHTTP_PORT=${HTTP_PORT}\nHTTPS_PORT=${HTTPS_PORT}\n' > /var/tmp/bunkerweb/tmp.env"
|
||||
sudo -E -u nginx -g nginx /bin/bash -c "PYTHONPATH=/usr/share/bunkerweb/deps/python/ /usr/share/bunkerweb/gen/main.py --variables /var/tmp/bunkerweb/tmp.env --no-linux-reload"
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
log "SYSTEMCTL" "❌" "Error while generating config from /var/tmp/bunkerweb/tmp.env"
|
||||
exit 1
|
||||
fi
|
||||
# Check if we are in slave/master mode
|
||||
export MASTER_MODE="$(grep "^MASTER_MODE=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
export SLAVE_MODE="$(grep "^SLAVE_MODE=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
|
||||
if [ ! -f /var/run/bunkerweb ] ; then
|
||||
mkdir -p /var/run/bunkerweb
|
||||
chown nginx:nginx /var/run/bunkerweb
|
||||
fi
|
||||
|
||||
# Start nginx
|
||||
log "SYSTEMCTL" "ℹ️" "Starting nginx ..."
|
||||
sudo -E -u nginx -g nginx /usr/sbin/nginx -e /var/log/bunkerweb/error.log
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
log "SYSTEMCTL" "❌" "Error while executing temp nginx"
|
||||
exit 1
|
||||
fi
|
||||
count=0
|
||||
while [ $count -lt 10 ] ; do
|
||||
check="$(curl -s -H "Host: healthcheck.bunkerweb.io" http://127.0.0.1:6000/healthz 2>&1)"
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -eq 0 ] && [ "$check" = "ok" ] ; then
|
||||
break
|
||||
if [ "$MASTER_MODE" != "yes" ] ; then
|
||||
# Generate temp conf for jobs and start nginx
|
||||
DNS_RESOLVERS="$(grep "^DNS_RESOLVERS=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$DNS_RESOLVERS" = "" ] ; then
|
||||
DNS_RESOLVERS="8.8.8.8 8.8.4.4"
|
||||
fi
|
||||
count=$((count + 1))
|
||||
sleep 1
|
||||
log "SYSTEMCTL" "ℹ️" "Waiting for nginx to start ..."
|
||||
done
|
||||
if [ $count -ge 10 ] ; then
|
||||
log "SYSTEMCTL" "❌" "nginx is not started"
|
||||
exit 1
|
||||
API_LISTEN_IP="$(grep "^API_LISTEN_IP=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_LISTEN_IP" = "" ] ; then
|
||||
API_LISTEN_IP="127.0.0.1"
|
||||
fi
|
||||
API_HTTP_PORT="$(grep "^API_HTTP_PORT=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_HTTP_PORT" = "" ] ; then
|
||||
API_HTTP_PORT="5000"
|
||||
fi
|
||||
API_SERVER_NAME="$(grep "^API_SERVER_NAME=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_SERVER_NAME" = "" ] ; then
|
||||
API_SERVER_NAME="bwapi"
|
||||
fi
|
||||
API_WHITELIST_IP="$(grep "^API_WHITELIST_IP=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$API_WHITELIST_IP" = "" ] ; then
|
||||
API_WHITELIST_IP="127.0.0.0/8"
|
||||
fi
|
||||
USE_REAL_IP="$(grep "^USE_REAL_IP=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$USE_REAL_IP" = "" ] ; then
|
||||
USE_REAL_IP="no"
|
||||
fi
|
||||
USE_PROXY_PROTOCOL="$(grep "^USE_PROXY_PROTOCOL=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$USE_PROXY_PROTOCOL" = "" ] ; then
|
||||
USE_PROXY_PROTOCOL="no"
|
||||
fi
|
||||
REAL_IP_FROM="$(grep "^REAL_IP_FROM=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$REAL_IP_FROM" = "" ] ; then
|
||||
REAL_IP_FROM="192.168.0.0/16 172.16.0.0/12 10.0.0.0/8"
|
||||
fi
|
||||
REAL_IP_HEADER="$(grep "^REAL_IP_HEADER=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$REAL_IP_HEADER" = "" ] ; then
|
||||
REAL_IP_HEADER="X-Forwarded-For"
|
||||
fi
|
||||
HTTP_PORT="$(grep "^HTTP_PORT=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$HTTP_PORT" = "" ] ; then
|
||||
HTTP_PORT="80"
|
||||
fi
|
||||
HTTPS_PORT="$(grep "^HTTPS_PORT=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$HTTPS_PORT" = "" ] ; then
|
||||
HTTPS_PORT="443"
|
||||
fi
|
||||
MODSECURITY_CRS_VERSION="$(grep "^MODSECURITY_CRS_VERSION=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
|
||||
if [ "$MODSECURITY_CRS_VERSION" = "" ] ; then
|
||||
MODSECURITY_CRS_VERSION="3"
|
||||
fi
|
||||
sudo -E -u nginx -g nginx /bin/bash -c "echo -ne 'IS_LOADING=yes\nUSE_BUNKERNET=no\nSEND_ANONYMOUS_REPORT=no\nSERVER_NAME=\nMODSECURITY_CRS_VERSION=${MODSECURITY_CRS_VERSION}\nDNS_RESOLVERS=${DNS_RESOLVERS}\nAPI_HTTP_PORT=${API_HTTP_PORT}\nAPI_LISTEN_IP=${API_LISTEN_IP}\nAPI_SERVER_NAME=${API_SERVER_NAME}\nAPI_WHITELIST_IP=${API_WHITELIST_IP}\nUSE_REAL_IP=${USE_REAL_IP}\nUSE_PROXY_PROTOCOL=${USE_PROXY_PROTOCOL}\nREAL_IP_FROM=${REAL_IP_FROM}\nREAL_IP_HEADER=${REAL_IP_HEADER}\nHTTP_PORT=${HTTP_PORT}\nHTTPS_PORT=${HTTPS_PORT}\n' > /var/tmp/bunkerweb/tmp.env"
|
||||
sudo -E -u nginx -g nginx /bin/bash -c "PYTHONPATH=/usr/share/bunkerweb/deps/python/ /usr/share/bunkerweb/gen/main.py --variables /var/tmp/bunkerweb/tmp.env --no-linux-reload"
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
log "SYSTEMCTL" "❌" "Error while generating config from /var/tmp/bunkerweb/tmp.env"
|
||||
exit 1
|
||||
fi
|
||||
# Start nginx
|
||||
log "SYSTEMCTL" "ℹ️" "Starting nginx ..."
|
||||
sudo -E -u nginx -g nginx /usr/sbin/nginx -e /var/log/bunkerweb/error.log
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
log "SYSTEMCTL" "❌" "Error while executing temp nginx"
|
||||
exit 1
|
||||
fi
|
||||
count=0
|
||||
while [ $count -lt 10 ] ; do
|
||||
check="$(curl -s -H "Host: healthcheck.bunkerweb.io" http://127.0.0.1:6000/healthz 2>&1)"
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -eq 0 ] && [ "$check" = "ok" ] ; then
|
||||
break
|
||||
fi
|
||||
count=$((count + 1))
|
||||
sleep 1
|
||||
log "SYSTEMCTL" "ℹ️" "Waiting for nginx to start ..."
|
||||
done
|
||||
if [ $count -ge 10 ] ; then
|
||||
log "SYSTEMCTL" "❌" "nginx is not started"
|
||||
exit 1
|
||||
fi
|
||||
log "SYSTEMCTL" "ℹ️" "nginx started ..."
|
||||
fi
|
||||
log "SYSTEMCTL" "ℹ️" "nginx started ..."
|
||||
|
||||
# Execute scheduler
|
||||
log "SYSTEMCTL" "ℹ️ " "Executing scheduler ..."
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ from common_utils import bytes_hash, dict_to_frozenset, get_integration # type:
|
|||
from logger import setup_logger # type: ignore
|
||||
from Database import Database # type: ignore
|
||||
from JobScheduler import JobScheduler
|
||||
from API import API
|
||||
|
||||
RUN = True
|
||||
SCHEDULER: Optional[JobScheduler] = None
|
||||
|
|
@ -70,6 +71,9 @@ SCHEDULER_TMP_ENV_PATH.touch()
|
|||
DB_LOCK_FILE = Path(sep, "var", "lib", "bunkerweb", "db.lock")
|
||||
logger = setup_logger("Scheduler", getenv("LOG_LEVEL", "INFO"))
|
||||
|
||||
SLAVE_MODE = environ.get("SLAVE_MODE", "no") == "yes"
|
||||
MASTER_MODE = environ.get("MASTER_MODE", "no") == "yes"
|
||||
|
||||
|
||||
def handle_stop(signum, frame):
|
||||
if SCHEDULER is not None:
|
||||
|
|
@ -198,6 +202,53 @@ def generate_external_plugins(plugins: List[Dict[str, Any]], *, original_path: U
|
|||
if not ret:
|
||||
logger.error(f"Sending {'pro ' if pro else ''}external plugins failed, configuration will not work as expected...")
|
||||
|
||||
def generate_caches(plugins: List[Any], db: Database):
|
||||
for plugin in plugins:
|
||||
job_cache_files = db.get_jobs_cache_files(plugin_id=plugin["id"])
|
||||
plugin_cache_files = set()
|
||||
ignored_dirs = set()
|
||||
job_path = Path(sep, "var", "cache", "bunkerweb", plugin["id"])
|
||||
for job_cache_file in job_cache_files:
|
||||
cache_path = job_path.joinpath(job_cache_file["service_id"] or "", job_cache_file["file_name"])
|
||||
plugin_cache_files.add(cache_path)
|
||||
|
||||
try:
|
||||
if job_cache_file["file_name"].endswith(".tgz"):
|
||||
extract_path = cache_path.parent
|
||||
if job_cache_file["file_name"].startswith("folder:"):
|
||||
extract_path = Path(job_cache_file["file_name"].split("folder:", 1)[1].rsplit(".tgz", 1)[0])
|
||||
ignored_dirs.add(extract_path.as_posix())
|
||||
rmtree(extract_path, ignore_errors=True)
|
||||
extract_path.mkdir(parents=True, exist_ok=True)
|
||||
with tar_open(fileobj=BytesIO(job_cache_file["data"]), mode="r:gz") as tar:
|
||||
try:
|
||||
tar.extractall(extract_path, filter="fully_trusted")
|
||||
except TypeError:
|
||||
tar.extractall(extract_path)
|
||||
else:
|
||||
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
cache_path.write_bytes(job_cache_file["data"])
|
||||
except BaseException as e:
|
||||
logger.error(f"Exception while restoring cache file {job_cache_file['file_name']} :\n{e}")
|
||||
if job_path.is_dir():
|
||||
for file in job_path.rglob("*"):
|
||||
skipped = False
|
||||
if file.as_posix().startswith(tuple(ignored_dirs)):
|
||||
skipped = True
|
||||
if skipped:
|
||||
continue
|
||||
logger.debug(f"Checking if {file} should be removed")
|
||||
if file not in plugin_cache_files and file.is_file():
|
||||
logger.debug(f"Removing non-cached file {file}")
|
||||
file.unlink(missing_ok=True)
|
||||
if file.parent.is_dir() and not list(file.parent.iterdir()):
|
||||
logger.debug(f"Removing empty directory {file.parent}")
|
||||
rmtree(file.parent, ignore_errors=True)
|
||||
if file.parent == job_path:
|
||||
break
|
||||
elif file.is_dir() and not list(file.iterdir()):
|
||||
logger.debug(f"Removing empty directory {file}")
|
||||
rmtree(file, ignore_errors=True)
|
||||
|
||||
def api_to_instance(api):
|
||||
hostname_port = api.endpoint.replace("http://", "").replace("https://", "").replace("/", "").split(":")
|
||||
|
|
@ -206,6 +257,62 @@ def api_to_instance(api):
|
|||
"env": {"API_HTTP_PORT": int(hostname_port[1]), "API_SERVER_NAME": api.host},
|
||||
}
|
||||
|
||||
def run_in_slave_mode(db: Database, dotenv_env: Dict[str, Any]):
|
||||
# Instantiate db
|
||||
db = Database(logger, sqlalchemy_string=dotenv_env.get("DATABASE_URI", getenv("DATABASE_URI", None)))
|
||||
|
||||
# Wait for init
|
||||
while not db.is_initialized():
|
||||
logger.warning("Database is not initialized, retrying in 5s ...")
|
||||
sleep(5)
|
||||
|
||||
# Wait for first config
|
||||
env = db.get_config()
|
||||
while not db.is_first_config_saved() or not env:
|
||||
logger.warning("Database doesn't have any config saved yet, retrying in 5s ...")
|
||||
sleep(5)
|
||||
env = db.get_config()
|
||||
|
||||
# Download plugins
|
||||
pro_plugins = db.get_plugins(_type="pro", with_data=True)
|
||||
generate_external_plugins(pro_plugins, original_path=PRO_PLUGINS_PATH)
|
||||
external_plugins = db.get_plugins(_type="external", with_data=True)
|
||||
generate_external_plugins(external_plugins)
|
||||
|
||||
# Download custom configs
|
||||
generate_custom_configs(db.get_custom_configs())
|
||||
|
||||
# Download caches
|
||||
generate_caches(pro_plugins + external_plugins, db)
|
||||
|
||||
# Gen config
|
||||
content = ""
|
||||
for k, v in env.items():
|
||||
content += f"{k}={v}\n"
|
||||
SCHEDULER_TMP_ENV_PATH.write_text(content)
|
||||
proc = subprocess_run(
|
||||
[
|
||||
"python3",
|
||||
join(sep, "usr", "share", "bunkerweb", "gen", "main.py"),
|
||||
"--settings",
|
||||
join(sep, "usr", "share", "bunkerweb", "settings.json"),
|
||||
"--templates",
|
||||
join(sep, "usr", "share", "bunkerweb", "confs"),
|
||||
"--output",
|
||||
join(sep, "etc", "nginx"),
|
||||
"--variables",
|
||||
str(SCHEDULER_TMP_ENV_PATH),
|
||||
],
|
||||
stdin=DEVNULL,
|
||||
stderr=STDOUT,
|
||||
check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
logger.error("Config generator failed, configuration will not work as expected...")
|
||||
|
||||
# TODO : check nginx status + check DB status
|
||||
while True:
|
||||
sleep(5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
|
|
@ -232,6 +339,10 @@ if __name__ == "__main__":
|
|||
|
||||
db = Database(logger, sqlalchemy_string=dotenv_env.get("DATABASE_URI", getenv("DATABASE_URI", None)))
|
||||
|
||||
if SLAVE_MODE:
|
||||
run_in_slave_mode(db, dotenv_env)
|
||||
stop(1)
|
||||
|
||||
if INTEGRATION in ("Swarm", "Kubernetes", "Autoconf"):
|
||||
while not db.is_initialized():
|
||||
logger.warning("Database is not initialized, retrying in 5s ...")
|
||||
|
|
@ -280,8 +391,15 @@ if __name__ == "__main__":
|
|||
|
||||
env["DATABASE_URI"] = db.database_uri
|
||||
|
||||
# Override instances if needed
|
||||
override_instances = env.get("OVERRIDE_INSTANCES", "")
|
||||
apis=[]
|
||||
if override_instances:
|
||||
for instance in override_instances.split(" "):
|
||||
apis.append(API(instance))
|
||||
|
||||
# Instantiate scheduler
|
||||
SCHEDULER = JobScheduler(env | environ, logger, INTEGRATION, db=db)
|
||||
SCHEDULER = JobScheduler(env | environ, logger, INTEGRATION, db=db, apis=apis)
|
||||
|
||||
if INTEGRATION in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
# Automatically setup the scheduler apis
|
||||
|
|
@ -462,7 +580,7 @@ if __name__ == "__main__":
|
|||
if event["Action"] == "start":
|
||||
db.checked_changes(value=True)
|
||||
|
||||
if INTEGRATION == "Docker":
|
||||
if INTEGRATION == "Docker" and not override_instances:
|
||||
Thread(target=listen_for_instances_reload, args=(db,), name="listen_for_instances_reload").start()
|
||||
|
||||
while True:
|
||||
|
|
@ -490,19 +608,22 @@ if __name__ == "__main__":
|
|||
content += f"{k}={v}\n"
|
||||
SCHEDULER_TMP_ENV_PATH.write_text(content)
|
||||
# run the generator
|
||||
args = [
|
||||
"python3",
|
||||
join(sep, "usr", "share", "bunkerweb", "gen", "main.py"),
|
||||
"--settings",
|
||||
join(sep, "usr", "share", "bunkerweb", "settings.json"),
|
||||
"--templates",
|
||||
join(sep, "usr", "share", "bunkerweb", "confs"),
|
||||
"--output",
|
||||
join(sep, "etc", "nginx"),
|
||||
"--variables",
|
||||
str(SCHEDULER_TMP_ENV_PATH),
|
||||
]
|
||||
if MASTER_MODE:
|
||||
args.append("--no-linux-reload")
|
||||
proc = subprocess_run(
|
||||
[
|
||||
"python3",
|
||||
join(sep, "usr", "share", "bunkerweb", "gen", "main.py"),
|
||||
"--settings",
|
||||
join(sep, "usr", "share", "bunkerweb", "settings.json"),
|
||||
"--templates",
|
||||
join(sep, "usr", "share", "bunkerweb", "confs"),
|
||||
"--output",
|
||||
join(sep, "etc", "nginx"),
|
||||
"--variables",
|
||||
str(SCHEDULER_TMP_ENV_PATH),
|
||||
],
|
||||
args,
|
||||
stdin=DEVNULL,
|
||||
stderr=STDOUT,
|
||||
check=False,
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ bw_version = Path(sep, "usr", "share", "bunkerweb", "VERSION").read_text(encodin
|
|||
try:
|
||||
app.config.update(
|
||||
DEBUG=True,
|
||||
INSTANCES=Instances(docker_client, kubernetes_client, INTEGRATION),
|
||||
INSTANCES=Instances(docker_client, kubernetes_client, INTEGRATION, db),
|
||||
CONFIG=Config(db),
|
||||
CONFIGFILES=ConfigFiles(),
|
||||
WTF_CSRF_SSL_STRICT=False,
|
||||
|
|
@ -634,8 +634,10 @@ def home():
|
|||
if r and r.status_code == 200:
|
||||
remote_version = basename(r.url).strip().replace("v", "")
|
||||
|
||||
instances = app.config["INSTANCES"].get_instances()
|
||||
config = app.config["CONFIG"].get_config(with_drafts=True)
|
||||
override_instances = config["OVERRIDE_INSTANCES"]["value"] != ""
|
||||
instances = app.config["INSTANCES"].get_instances(override_instances=override_instances)
|
||||
|
||||
instance_health_count = 0
|
||||
|
||||
for instance in instances:
|
||||
|
|
@ -848,7 +850,9 @@ def instances():
|
|||
)
|
||||
|
||||
# Display instances
|
||||
instances = app.config["INSTANCES"].get_instances()
|
||||
config = app.config["CONFIG"].get_config()
|
||||
override_instances = config["OVERRIDE_INSTANCES"]["value"] != ""
|
||||
instances = app.config["INSTANCES"].get_instances(override_instances=override_instances)
|
||||
return render_template("instances.html", title="Instances", instances=instances, username=current_user.get_id())
|
||||
|
||||
|
||||
|
|
@ -1664,7 +1668,10 @@ def cache():
|
|||
@app.route("/logs", methods=["GET"])
|
||||
@login_required
|
||||
def logs():
|
||||
return render_template("logs.html", instances=app.config["INSTANCES"].get_instances(), username=current_user.get_id())
|
||||
config = app.config["CONFIG"].get_config(with_drafts=True)
|
||||
override_instances = config["OVERRIDE_INSTANCES"]["value"] != ""
|
||||
instances = app.config["INSTANCES"].get_instances(override_instances=override_instances)
|
||||
return render_template("logs.html", instances=instances, username=current_user.get_id())
|
||||
|
||||
|
||||
@app.route("/logs/local", methods=["GET"])
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ from typing import Any, List, Optional, Tuple, Union
|
|||
|
||||
from API import API # type: ignore
|
||||
from ApiCaller import ApiCaller # type: ignore
|
||||
from dotenv import dotenv_values
|
||||
from dotenv import dotenv_values # type: ignore
|
||||
from Database import Database # type: ignore
|
||||
|
||||
|
||||
class Instance:
|
||||
|
|
@ -134,10 +135,11 @@ class Instance:
|
|||
|
||||
|
||||
class Instances:
|
||||
def __init__(self, docker_client, kubernetes_client, integration: str):
|
||||
def __init__(self, docker_client, kubernetes_client, integration: str, db):
|
||||
self.__docker_client = docker_client
|
||||
self.__kubernetes_client = kubernetes_client
|
||||
self.__integration = integration
|
||||
self.__db = db
|
||||
|
||||
def __instance_from_id(self, _id) -> Instance:
|
||||
instances: list[Instance] = self.get_instances()
|
||||
|
|
@ -147,8 +149,34 @@ class Instances:
|
|||
|
||||
raise ValueError(f"Can't find instance with _id {_id}")
|
||||
|
||||
def get_instances(self) -> list[Instance]:
|
||||
def get_instances(self, override_instances=None) -> list[Instance]:
|
||||
instances = []
|
||||
# Override case : only return instances from DB
|
||||
if override_instances is None:
|
||||
config = self.__db.get_config()
|
||||
override_instances = config["OVERRIDE_INSTANCES"]["value"] != ""
|
||||
if override_instances:
|
||||
for instance in self.__db.get_instances():
|
||||
instances.append(
|
||||
Instance(
|
||||
instance["hostname"],
|
||||
instance["hostname"],
|
||||
instance["hostname"],
|
||||
"override",
|
||||
"up",
|
||||
None,
|
||||
ApiCaller(
|
||||
[
|
||||
API(
|
||||
f"http://{instance["hostname"]}:{str(instance["port"])}",
|
||||
instance.server_name,
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
return instances
|
||||
# Docker instances (containers or services)
|
||||
if self.__docker_client is not None:
|
||||
for instance in self.__docker_client.containers.list(all=True, filters={"label": "bunkerweb.INSTANCE"}):
|
||||
|
|
|
|||
Loading…
Reference in a new issue