Merge pull request #1255 from bunkerity/dev

Merge branch "dev" into branch "staging"
This commit is contained in:
Théophile Diot 2024-06-07 12:37:11 +01:00 committed by GitHub
commit 21ef299683
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 306 additions and 188 deletions

View file

@ -117,7 +117,7 @@ jobs:
# Check OS vulnerabilities
- name: Check OS vulnerabilities
if: ${{ inputs.CACHE_SUFFIX != 'arm' }}
uses: aquasecurity/trivy-action@fd25fed6972e341ff0007ddb61f77e88103953c2 # v0.21.0
uses: aquasecurity/trivy-action@595be6a0f6560a0a8fc419ddf630567fc623531d # v0.22.0
with:
vuln-type: os
skip-dirs: /root/.cargo

View file

@ -214,6 +214,16 @@ In other words, the scheduler is the brain of BunkerWeb.
# Setup
## BunkerWeb Cloud
<p align="center">
<img alt="Docker banner" src="https://github.com/bunkerity/bunkerweb/raw/v1.5.8/docs/assets/img/bunkerweb-cloud.svg" />
</p>
BunkerWeb Cloud is the easiest way to get started with BunkerWeb. It offers you a fully managed BunkerWeb service with no hassle. Think of a like a BunkerWeb-as-a-Service !
You will find more information about BunkerWeb Cloud beta [here](https://www.bunkerweb.io/cloud?utm_campaign=self&utm_source=docs) and you can apply for free [in the BunkerWeb panel](https://panel.bunkerweb.io/order/bunkerweb-cloud/14?utm_campaign=self&utm_source=docs).
## Docker
<p align="center">

View file

@ -1,5 +1,19 @@
# Integrations
## BunkerWeb Cloud
<figure markdown>
![Overview](assets/img/bunkerweb-cloud.svg){ align=center, width="600" }
<figcaption>BunkerWeb Cloud</figcaption>
</figure>
!!! example "Beta phase"
BunkerWeb Cloud offer is in beta phase. We are actively getting feedbacks from our precious beta tester to improve the offer.
BunkerWeb Cloud is the easiest way to get started with BunkerWeb. It offers you a fully managed BunkerWeb service with no hassle. Think of a like a BunkerWeb-as-a-Service !
You will find more information about BunkerWeb Cloud beta [here](https://www.bunkerweb.io/cloud?utm_campaign=self&utm_source=docs) and you can apply for free [in the BunkerWeb panel](https://panel.bunkerweb.io/order/bunkerweb-cloud/14?utm_campaign=self&utm_source=docs).
## Docker
<figure markdown>

View file

@ -1,4 +1,4 @@
mike==2.1.1
mkdocs-material[imaging]==9.5.25
mkdocs-material[imaging]==9.5.26
mkdocs-print-site-plugin==2.5.0
pytablewriter==1.2.0

View file

@ -317,9 +317,9 @@ mkdocs-get-deps==0.2.0 \
--hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \
--hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
# via mkdocs
mkdocs-material==9.5.25 \
--hash=sha256:68fdab047a0b9bfbefe79ce267e8a7daaf5128bcf7867065fcd201ee335fece1 \
--hash=sha256:d0662561efb725b712207e0ee01f035ca15633f29a64628e24f01ec99d7078f4
mkdocs-material==9.5.26 \
--hash=sha256:56aeb91d94cffa43b6296fa4fbf0eb7c840136e563eecfd12c2d9e92e50ba326 \
--hash=sha256:5d01fb0aa1c7946a1e3ae8689aa2b11a030621ecb54894e35aabb74c21016312
# via
# -r requirements.in
# mkdocs-print-site-plugin

View file

@ -67,7 +67,7 @@ class Config:
def wait_applying(self, startup: bool = False):
i = 0
while i < 10:
while i < 60:
curr_changes = self._db.check_changes()
if isinstance(curr_changes, str):
if not startup:

View file

@ -45,49 +45,57 @@ def backup_database(current_time: datetime, db: Database = None, backup_dir: Pat
database: Literal["sqlite", "mariadb", "mysql", "postgresql"] = db.database_uri.split(":")[0].split("+")[0] # type: ignore
backup_file = backup_dir.joinpath(f"backup-{database}-{current_time.strftime('%Y-%m-%d_%H-%M-%S')}.zip")
LOGGER.debug(f"Backup file path: {backup_file}")
stderr = "Table 'db.test_"
current_time = datetime.now()
if database == "sqlite":
match = DB_STRING_RX.search(db.database_uri)
if not match:
LOGGER.error(f"Invalid database string provided: {db.database_uri}, skipping backup ...")
while "Table 'db.test_" in stderr and (datetime.now() - current_time).total_seconds() < 10:
if database == "sqlite":
match = DB_STRING_RX.search(db.database_uri)
if not match:
LOGGER.error(f"Invalid database string provided: {db.database_uri}, skipping backup ...")
sys_exit(1)
db_path = Path(match.group("path"))
LOGGER.info("Creating a backup for the SQLite database ...")
proc = run(["sqlite3", db_path.as_posix(), ".dump"], stdout=PIPE, stderr=PIPE)
else:
db_host = db.database_uri.rsplit("@", 1)[1].split("/")[0].split(":")
db_port = None
if len(db_host) == 1:
db_host = db_host[0]
else:
db_host, db_port = db_host
db_user = db.database_uri.split("://")[1].split(":")[0]
db_password = db.database_uri.split("://")[1].split(":")[1].rsplit("@", 1)[0]
db_database_name = db.database_uri.split("/")[-1].split("?")[0]
if database in ("mariadb", "mysql"):
LOGGER.info("Creating a backup for the MariaDB/MySQL database ...")
cmd = ["mysqldump", "-h", db_host, "-u", db_user, db_database_name]
if db_port:
cmd.extend(["-P", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"MYSQL_PWD": db_password})
elif database == "postgresql":
LOGGER.info("Creating a backup for the PostgreSQL database ...")
cmd = ["pg_dump", "-h", db_host, "-U", db_user, db_database_name, "-w"]
if db_port:
cmd.extend(["-p", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"PGPASSWORD": db_password})
stderr = proc.stderr.decode()
if "Table 'db.test_" not in stderr and proc.returncode != 0:
LOGGER.error(f"Failed to dump the database: {stderr}")
sys_exit(1)
db_path = Path(match.group("path"))
LOGGER.info("Creating a backup for the SQLite database ...")
proc = run(["sqlite3", db_path.as_posix(), ".dump"], stdout=PIPE, stderr=PIPE)
else:
db_host = db.database_uri.rsplit("@", 1)[1].split("/")[0].split(":")
db_port = None
if len(db_host) == 1:
db_host = db_host[0]
else:
db_host, db_port = db_host
db_user = db.database_uri.split("://")[1].split(":")[0]
db_password = db.database_uri.split("://")[1].split(":")[1].rsplit("@", 1)[0]
db_database_name = db.database_uri.split("/")[-1].split("?")[0]
if database in ("mariadb", "mysql"):
LOGGER.info("Creating a backup for the MariaDB/MySQL database ...")
cmd = ["mysqldump", "-h", db_host, "-u", db_user, db_database_name]
if db_port:
cmd.extend(["-P", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"MYSQL_PWD": db_password})
elif database == "postgresql":
LOGGER.info("Creating a backup for the PostgreSQL database ...")
cmd = ["pg_dump", "-h", db_host, "-U", db_user, db_database_name, "-w"]
if db_port:
cmd.extend(["-p", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"PGPASSWORD": db_password})
if proc.returncode != 0:
LOGGER.error(f"Failed to dump the database: {proc.stderr.decode()}")
if (datetime.now() - current_time).total_seconds() >= 10:
LOGGER.error("Failed to dump the database: Timeout reached")
sys_exit(1)
with ZipFile(backup_file, "w", compression=ZIP_DEFLATED) as zipf:

View file

@ -17,12 +17,6 @@
"file": "mmdb-asn.py",
"every": "day",
"reload": true
},
{
"name": "download-plugins",
"file": "download-plugins.py",
"every": "once",
"reload": false
}
]
}

View file

@ -214,6 +214,12 @@
"file": "anonymous-report.py",
"every": "day",
"reload": false
},
{
"name": "download-plugins",
"file": "download-plugins.py",
"every": "once",
"reload": false
}
]
}

View file

@ -482,7 +482,7 @@ class Database:
return data
def check_changes(self) -> Union[Dict[str, bool], bool, str]:
def check_changes(self, with_date: bool = False) -> Union[Dict[str, Any], str]:
"""Check if either the config, the custom configs, plugins or instances have changed inside the database"""
with self.__db_session() as session:
try:
@ -490,30 +490,66 @@ class Database:
session.query(Metadata)
.with_entities(
Metadata.custom_configs_changed,
Metadata.last_custom_configs_change,
Metadata.external_plugins_changed,
Metadata.last_external_plugins_change,
Metadata.pro_plugins_changed,
Metadata.last_pro_plugins_change,
Metadata.instances_changed,
Metadata.last_instances_change,
)
.filter_by(id=1)
.first()
)
return dict(
custom_configs_changed=metadata is not None and metadata.custom_configs_changed,
external_plugins_changed=metadata is not None and metadata.external_plugins_changed,
pro_plugins_changed=metadata is not None and metadata.pro_plugins_changed,
instances_changed=metadata is not None and metadata.instances_changed,
plugins_config_changed=[plugin.id for plugin in session.query(Plugins).with_entities(Plugins.id).filter_by(config_changed=True).all()],
)
except BaseException as e:
return str(e)
base_data = {
"custom_configs_changed": False,
"external_plugins_changed": False,
"pro_plugins_changed": False,
"instances_changed": False,
}
def check_plugin_changes(self) -> Union[List[str], str]:
"""Check if the plugins have changed inside the database"""
with self.__db_session() as session:
try:
plugins = session.query(Plugins).with_entities(Plugins.id).filter_by(config_changed=True).all()
return [plugin.id for plugin in plugins]
if with_date:
data = base_data | {
"last_custom_configs_change": None,
"last_external_plugins_change": None,
"last_pro_plugins_change": None,
"last_instances_change": None,
"plugins_config_changed": {
plugin.id: plugin.last_config_change
for plugin in session.query(Plugins).with_entities(Plugins.id, Plugins.last_config_change).filter_by(config_changed=True).all()
},
}
if not metadata:
return data
return data | {
"custom_configs_changed": metadata.custom_configs_changed,
"last_custom_configs_change": metadata.last_custom_configs_change,
"external_plugins_changed": metadata.external_plugins_changed,
"last_external_plugins_change": metadata.last_external_plugins_change,
"pro_plugins_changed": metadata.pro_plugins_changed,
"last_pro_plugins_change": metadata.last_pro_plugins_change,
"instances_changed": metadata.instances_changed,
"last_instances_change": metadata.last_instances_change,
}
data = base_data | {
"plugins_config_changed": sorted(
plugin.id for plugin in session.query(Plugins).with_entities(Plugins.id).filter_by(config_changed=True).all()
),
}
if not metadata:
return data
return data | {
"custom_configs_changed": metadata.custom_configs_changed,
"external_plugins_changed": metadata.external_plugins_changed,
"pro_plugins_changed": metadata.pro_plugins_changed,
"instances_changed": metadata.instances_changed,
}
except BaseException as e:
return str(e)
@ -536,23 +572,31 @@ class Database:
if not metadata:
return "The metadata are not set yet, try again"
current_time = datetime.now()
if "config" in changes:
if not metadata.first_config_saved:
metadata.first_config_saved = True
if "custom_configs" in changes:
metadata.custom_configs_changed = value
metadata.last_custom_configs_change = current_time
if "external_plugins" in changes:
metadata.external_plugins_changed = value
metadata.last_external_plugins_change = current_time
if "pro_plugins" in changes:
metadata.pro_plugins_changed = value
metadata.last_pro_plugins_change = current_time
if "instances" in changes:
metadata.instances_changed = value
metadata.last_instances_change = current_time
if plugins_changes:
if plugins_changes == "all":
session.query(Plugins).update({Plugins.config_changed: value})
session.query(Plugins).update({Plugins.config_changed: value, Plugins.last_config_change: current_time})
else:
session.query(Plugins).filter(Plugins.id.in_(plugins_changes)).update({Plugins.config_changed: value})
session.query(Plugins).filter(Plugins.id.in_(plugins_changes)).update(
{Plugins.config_changed: value, Plugins.last_config_change: current_time}
)
session.commit()
except BaseException as e:
@ -581,7 +625,7 @@ class Database:
if db_version != bunkerweb_version:
self.logger.warning(f"Database version ({db_version}) is different from Bunkerweb version ({bunkerweb_version}), migrating ...")
curren_time = datetime.now()
current_time = datetime.now()
error = True
while error:
try:
@ -589,7 +633,7 @@ class Database:
metadata.reflect(self.sql_engine)
error = False
except BaseException as e:
if (datetime.now() - curren_time).total_seconds() > 10:
if (datetime.now() - current_time).total_seconds() > 10:
raise e
sleep(1)
@ -1262,8 +1306,7 @@ class Database:
changed_plugins.add(setting.plugin_id)
to_put.append(Services_settings(service_id=server_name, setting_id=key, value=value, suffix=suffix, method=method))
elif (
method == service_setting.method
or (service_setting.method not in ("scheduler", "autoconf") and method in ("scheduler", "autoconf"))
method == service_setting.method or (service_setting.method not in ("scheduler", "autoconf") and method == "autoconf")
) and service_setting.value != value:
changed_plugins.add(setting.plugin_id)
query = session.query(Services_settings).filter(
@ -1295,7 +1338,7 @@ class Database:
changed_plugins.add(setting.plugin_id)
to_put.append(Global_values(setting_id=key, value=value, suffix=suffix, method=method))
elif (
method == global_value.method or (global_value.method not in ("scheduler", "autoconf") and method in ("scheduler", "autoconf"))
method == global_value.method or (global_value.method not in ("scheduler", "autoconf") and method == "autoconf")
) and global_value.value != value:
changed_plugins.add(setting.plugin_id)
query = session.query(Global_values).filter(Global_values.setting_id == key, Global_values.suffix == suffix)
@ -1340,7 +1383,7 @@ class Database:
changed_plugins.add(setting.plugin_id)
to_put.append(Global_values(setting_id=key, value=value, suffix=suffix, method=method))
elif (
method == global_value.method or (global_value.method not in ("scheduler", "autoconf") and method in ("scheduler", "autoconf"))
method == global_value.method or (global_value.method not in ("scheduler", "autoconf") and method == "autoconf")
) and value != global_value.value:
changed_plugins.add(setting.plugin_id)
query = session.query(Global_values).filter(Global_values.setting_id == key, Global_values.suffix == suffix)
@ -1445,7 +1488,7 @@ class Database:
if not custom_conf:
to_put.append(Custom_configs(**custom_config))
elif custom_config["checksum"] != custom_conf.checksum and (
method == custom_conf.method or (custom_conf.method not in ("scheduler", "autoconf") and method in ("scheduler", "autoconf"))
method == custom_conf.method or (custom_conf.method not in ("scheduler", "autoconf") and method == "autoconf")
):
custom_conf.data = custom_config["data"]
custom_conf.checksum = custom_config["checksum"]
@ -1455,6 +1498,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.custom_configs_changed = True
metadata.last_custom_configs_change = datetime.now()
try:
session.add_all(to_put)
@ -2181,8 +2225,10 @@ class Database:
if metadata is not None:
if _type == "external":
metadata.external_plugins_changed = True
metadata.last_external_plugins_change = datetime.now()
elif _type == "pro":
metadata.pro_plugins_changed = True
metadata.last_pro_plugins_change = datetime.now()
try:
session.add_all(to_put)
@ -2398,6 +2444,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.instances_changed = True
metadata.last_instances_change = datetime.now()
try:
session.commit()
@ -2429,6 +2476,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.instances_changed = True
metadata.last_instances_change = datetime.now()
try:
session.add_all(to_put)

View file

@ -59,6 +59,7 @@ class Plugins(Base):
data = Column(LargeBinary(length=(2**32) - 1), nullable=True)
checksum = Column(String(128), nullable=True)
config_changed = Column(Boolean, default=False, nullable=True)
last_config_change = Column(DateTime, nullable=True)
settings = relationship("Settings", back_populates="plugin", cascade="all, delete-orphan")
jobs = relationship("Jobs", back_populates="plugin", cascade="all, delete-orphan")
@ -242,8 +243,12 @@ class Metadata(Base):
autoconf_loaded = Column(Boolean, default=False, nullable=True)
scheduler_first_start = Column(Boolean, nullable=True)
custom_configs_changed = Column(Boolean, default=False, nullable=True)
last_custom_configs_change = Column(DateTime, nullable=True)
external_plugins_changed = Column(Boolean, default=False, nullable=True)
last_external_plugins_change = Column(DateTime, nullable=True)
pro_plugins_changed = Column(Boolean, default=False, nullable=True)
last_pro_plugins_change = Column(DateTime, nullable=True)
instances_changed = Column(Boolean, default=False, nullable=True)
last_instances_change = Column(DateTime, nullable=True)
integration = Column(INTEGRATIONS_ENUM, default="Unknown", nullable=False)
version = Column(String(32), default="1.5.8", nullable=False)

View file

@ -1,5 +1,5 @@
docker==7.1.0
jinja2==3.1.4
kubernetes==29.0.0
kubernetes==30.1.0
python-dotenv==1.0.1
redis==5.0.4
redis==5.0.5

View file

@ -114,9 +114,9 @@ docker==7.1.0 \
--hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \
--hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0
# via -r requirements.in
google-auth==2.29.0 \
--hash=sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360 \
--hash=sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415
google-auth==2.30.0 \
--hash=sha256:8df7da660f62757388b8a7f249df13549b3373f24388cb5d2f1dd91cc18180b5 \
--hash=sha256:ab630a1320f6720909ad76a7dbdb6841cdf5c66b328d690027e4867bdfb16688
# via kubernetes
idna==3.7 \
--hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
@ -126,9 +126,9 @@ jinja2==3.1.4 \
--hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \
--hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d
# via -r requirements.in
kubernetes==29.0.0 \
--hash=sha256:ab8cb0e0576ccdfb71886366efb102c6a20f268d817be065ce7f9909c631e43e \
--hash=sha256:c4812e227ae74d07d53c88293e564e54b850452715a59a927e7e1bc6b9a60459
kubernetes==30.1.0 \
--hash=sha256:41e4c77af9f28e7a6c314e3bd06a8c6229ddd787cad684e0ab9f69b498e98ebc \
--hash=sha256:e212e8b7579031dd2e512168b617373bc1e03888d41ac4e04039240a292d478d
# via -r requirements.in
markupsafe==2.1.5 \
--hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \
@ -269,9 +269,9 @@ pyyaml==6.0.1 \
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
# via kubernetes
redis==5.0.4 \
--hash=sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91 \
--hash=sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61
redis==5.0.5 \
--hash=sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada \
--hash=sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae
# via -r requirements.in
requests==2.32.3 \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \

View file

@ -1,5 +1,5 @@
pip==24.0
pip-compile-multi==2.6.3
pip-compile-multi==2.6.4
pip-tools==7.4.1
pip-upgrader==1.4.15
setuptools==70.0.0

View file

@ -137,9 +137,9 @@ pip==24.0 \
# via
# build
# pip-upgrader
pip-compile-multi==2.6.3 \
--hash=sha256:50f7db9f5c57a2d11035805b6d86620c4d640c560f22e3c8b509e133456d8a53 \
--hash=sha256:f0b950a2175d0b86fd5a186d2f786bc8f7c013acd2e856a2bc948d194e0e4c92
pip-compile-multi==2.6.4 \
--hash=sha256:1cf1f1eaa68b7993303edf83e0dc40b8b9bb4dced334b0c18332f4aeae318e3b \
--hash=sha256:487741913c0eba7aeee6e0cd79fe123df3a22f3489e565c9851eb259dd3c3d52
# via -r requirements-deps.in
pip-tools==7.4.1 \
--hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \

View file

@ -1,4 +1,4 @@
FROM redhat/ubi8:8.10@sha256:2a5d23450fb9b0cb266b4d465b36f1d1bc7c9b5a9b785528215b470b44f04209 as builder
FROM redhat/ubi8:8.10@sha256:143123d85045df426c5bbafc6863659880ebe276eb02c77ee868b88d08dbd05d as builder
ENV OS=rhel
ENV NGINX_VERSION 1.26.1
@ -65,7 +65,7 @@ COPY src/scheduler scheduler
COPY src/ui ui
COPY src/VERSION VERSION
FROM redhat/ubi8:8.10@sha256:2a5d23450fb9b0cb266b4d465b36f1d1bc7c9b5a9b785528215b470b44f04209
FROM redhat/ubi8:8.10@sha256:143123d85045df426c5bbafc6863659880ebe276eb02c77ee868b88d08dbd05d
# Set default umask to prevent huge recursive chmod increasing the final image size
RUN umask 027

View file

@ -1,4 +1,4 @@
FROM redhat/ubi9:9.4@sha256:d7158916ab85c7463d33f89d45d26c70d064aaa28debe219fa088b8110194663 as builder
FROM redhat/ubi9:9.4@sha256:d31d3e5e92c0c47277c5011c0326b285ab7ae627eff036133be1dccc4208004d as builder
ENV OS=rhel
ENV NGINX_VERSION 1.26.1
@ -68,7 +68,7 @@ COPY src/scheduler scheduler
COPY src/ui ui
COPY src/VERSION VERSION
FROM redhat/ubi9:9.4@sha256:d7158916ab85c7463d33f89d45d26c70d064aaa28debe219fa088b8110194663
FROM redhat/ubi9:9.4@sha256:d31d3e5e92c0c47277c5011c0326b285ab7ae627eff036133be1dccc4208004d
# Set default umask to prevent huge recursive chmod increasing the final image size
RUN umask 027

View file

@ -360,14 +360,16 @@ class JobScheduler(ApiCaller):
return False
return ret
def try_database_readonly(self) -> bool:
def try_database_readonly(self, force: bool = False) -> bool:
if not self.db.readonly:
try:
self.db.test_write()
self.db.readonly = False
return False
except BaseException:
self.db.readonly = True
return True
elif self.db.last_connection_retry and (datetime.now() - self.db.last_connection_retry).total_seconds() > 30:
elif not force and self.db.last_connection_retry and (datetime.now() - self.db.last_connection_retry).total_seconds() > 30:
return True
if self.db.database_uri and self.db.readonly:

View file

@ -663,6 +663,7 @@ if __name__ == "__main__":
Thread(target=listen_for_instances_reload, name="listen_for_instances_reload").start()
changed_plugins = []
old_changes = {}
while True:
threads.clear()
@ -788,7 +789,7 @@ if __name__ == "__main__":
while RUN and not NEED_RELOAD:
try:
SCHEDULER.run_pending()
sleep(1)
sleep(3 if SCHEDULER.db.readonly else 1)
current_time = datetime.now()
while DB_LOCK_FILE.is_file() and DB_LOCK_FILE.stat().st_ctime + 30 > current_time.timestamp():
@ -797,20 +798,33 @@ if __name__ == "__main__":
DB_LOCK_FILE.unlink(missing_ok=True)
changes = SCHEDULER.db.check_changes()
changes = SCHEDULER.db.check_changes(with_date=True)
if isinstance(changes, str):
raise Exception(f"An error occurred when checking for changes in the database : {changes}")
if SCHEDULER.db.readonly and changes == old_changes:
continue
# check if the plugins have changed since last time
if changes["pro_plugins_changed"]:
if changes["pro_plugins_changed"] and (
not SCHEDULER.db.readonly
or not changes["last_pro_plugins_change"]
or not old_changes
or old_changes["last_pro_plugins_change"] != changes["last_pro_plugins_change"]
):
logger.info("Pro plugins changed, generating ...")
PRO_PLUGINS_NEED_GENERATION = True
CONFIG_NEED_GENERATION = True
RUN_JOBS_ONCE = True
NEED_RELOAD = True
if changes["external_plugins_changed"]:
if changes["external_plugins_changed"] and (
not SCHEDULER.db.readonly
or not changes["last_external_plugins_change"]
or not old_changes
or old_changes["last_external_plugins_change"] != changes["last_external_plugins_change"]
):
logger.info("External plugins changed, generating ...")
PLUGINS_NEED_GENERATION = True
CONFIG_NEED_GENERATION = True
@ -818,27 +832,44 @@ if __name__ == "__main__":
NEED_RELOAD = True
# check if the custom configs have changed since last time
if changes["custom_configs_changed"]:
if changes["custom_configs_changed"] and (
not SCHEDULER.db.readonly
or not changes["last_custom_configs_change"]
or not old_changes
or old_changes["last_custom_configs_change"] != changes["last_custom_configs_change"]
):
logger.info("Custom configs changed, generating ...")
CONFIGS_NEED_GENERATION = True
CONFIG_NEED_GENERATION = True
NEED_RELOAD = True
# check if the config have changed since last time
if changes["plugins_config_changed"]:
if changes["plugins_config_changed"] and (
not SCHEDULER.db.readonly
or not changes["last_plugins_config_change"]
or not old_changes
or old_changes["plugins_config_changed"] != changes["plugins_config_changed"]
):
logger.info("Plugins config changed, generating ...")
CONFIG_NEED_GENERATION = True
RUN_JOBS_ONCE = True
NEED_RELOAD = True
changed_plugins = changes["plugins_config_changed"]
changed_plugins = list(changes["plugins_config_changed"])
# check if the instances have changed since last time
if changes["instances_changed"]:
if changes["instances_changed"] and (
not SCHEDULER.db.readonly
or not changes["last_instances_change"]
or not old_changes
or old_changes["last_instances_change"] != changes["last_instances_change"]
):
logger.info("Instances changed, generating ...")
INSTANCES_NEED_GENERATION = True
CONFIGS_NEED_GENERATION = True
CONFIG_NEED_GENERATION = True
NEED_RELOAD = True
old_changes = changes.copy()
except BaseException:
logger.debug(format_exc())
if errors > 5:
@ -848,6 +879,8 @@ if __name__ == "__main__":
sleep(5)
if NEED_RELOAD:
logger.debug(f"Changes: {changes}")
SCHEDULER.try_database_readonly(force=True)
CHANGES.clear()
if INSTANCES_NEED_GENERATION:

View file

@ -1,4 +1,4 @@
certbot==2.10.0
certbot==2.11.0
configobj==5.0.8
cryptography==42.0.8
maxminddb==2.6.1

View file

@ -4,13 +4,13 @@
#
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
#
acme==2.10.0 \
--hash=sha256:4a46172573608195a44a13e10f2b7c862cfd1f4046a913c765a936ba1bb7261d \
--hash=sha256:de110d6550f22094c920ad6022f4b329380a6bd8f58dd671135c6226c3a470cc
acme==2.11.0 \
--hash=sha256:23213ac3074a78862b219e0a30e141fd53238a8bdcf0668bd4dea59b28873fb8 \
--hash=sha256:f4950015cf52ff0de12f37fc28034c7710aca63f64f1696253d2f6cb9f22645e
# via certbot
certbot==2.10.0 \
--hash=sha256:3a7ccd70b941783610d73f66ae1e3daf59141e9170eb466604098a53cc3f5f5b \
--hash=sha256:892aa57d4db74af174aec5e4bb7f7537b200de2545a066c049d03a53215f0e4e
certbot==2.11.0 \
--hash=sha256:257ae1cb0a534373ca50dd807c9ae96f27660e41379c45afb9b50cab0e6a7a97 \
--hash=sha256:dc4e0a48bcb09448d60362170ca1047cc9a81966da0dd35135f2561f0ea7d5b1
# via -r requirements.in
certifi==2024.6.2 \
--hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \

View file

@ -1,4 +1,4 @@
cryptography==42.0.8
psycopg[binary,pool]==3.1.17
psycopg[binary,pool]==3.1.19
PyMySQL==1.1.1
sqlalchemy==2.0.30

View file

@ -152,76 +152,74 @@ greenlet==3.0.3 \
--hash=sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da \
--hash=sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33
# via sqlalchemy
psycopg==3.1.17 \
--hash=sha256:437e7d7925459f21de570383e2e10542aceb3b9cb972ce957fdd3826ca47edc6 \
--hash=sha256:96b7b13af6d5a514118b759a66b2799a8a4aa78675fa6bb0d3f7d52d67eff002
psycopg==3.1.19 \
--hash=sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961 \
--hash=sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731
# via -r requirements.in
psycopg-binary==3.1.17 \
--hash=sha256:00377f6963ee7e4bf71cab17c2c235ef0624df9483f3b615d86aa24cde889d42 \
--hash=sha256:0227885686c2cc0104ceb22d6eebc732766e9ad48710408cb0123237432e5435 \
--hash=sha256:02ac573f5a6e79bb6df512b3a6279f01f033bbd45c47186e8872fee45f6681d0 \
--hash=sha256:02cd2eb62ffc56f8c847d68765cbf461b3d11b438fe48951e44b6c563ec27d18 \
--hash=sha256:0340ef87a888fd940796c909e038426f4901046f61856598582a817162c64984 \
--hash=sha256:0b1ec6895cab887b92c303565617f994c9b9db53befda81fa2a31b76fe8a3ab1 \
--hash=sha256:12eab8bc91b4ba01b2ecee3b5b80501934b198f6e1f8d4b13596f3f38ba6e762 \
--hash=sha256:267a82548c21476120e43dc72b961f1af52c380c0b4c951bdb34cf14cb26bd35 \
--hash=sha256:2a05400e9314fc30bc1364865ba9f6eaa2def42b5e7e67f71f9a4430f870023e \
--hash=sha256:2b2a689eaede08cf91a36b10b0da6568dd6e4669200f201e082639816737992b \
--hash=sha256:3d0d154c780cc7b28a3a0886e8a4b18689202a1dbb522b3c771eb3a1289cf7c3 \
--hash=sha256:3e2cc2bbf37ff1cf11e8b871c294e3532636a3cf7f0c82518b7537158923d77b \
--hash=sha256:40af298b209dd77ca2f3e7eb3fbcfb87a25999fc015fcd14140bde030a164c7e \
--hash=sha256:420c1eb1626539c261cf3fbe099998da73eb990f9ce1a34da7feda414012ea5f \
--hash=sha256:4b20013051f1fd7d02b8d0766cfe8d009e8078babc00a6d39bc7e2d50a7b96af \
--hash=sha256:4fa26836ce074a1104249378727e1f239a01530f36bae16e77cf6c50968599b4 \
--hash=sha256:5ccbe8b2ec444763a51ecb1213befcbb75defc1ef36e7dd5dff501a23d7ce8cf \
--hash=sha256:5f5f5bcbb772d8c243d605fc7151beec760dd27532d42145a58fb74ef9c5fbf2 \
--hash=sha256:61104b8e7a43babf2bbaa36c08e31a12023e2f967166e99d6b052b11a4c7db06 \
--hash=sha256:67a5b93101bc85a95a189c0a23d02a29cf06c1080a695a0dedfdd50dd734662a \
--hash=sha256:6a728beefd89b430ebe2729d04ba10e05036b5e9d01648da60436000d2fcd242 \
--hash=sha256:6b2ae342d69684555bfe77aed5546d125b4a99012e0b83a8b3da68c8829f0935 \
--hash=sha256:6b40fa54a02825d3d6a8009d9a82a2b4fad80387acf2b8fd6d398fd2813cb2d9 \
--hash=sha256:6d4f2e15d33ed4f9776fdf23683512d76f4e7825c4b80677e9e3ce6c1b193ff2 \
--hash=sha256:6e3543edc18553e31a3884af3cd7eea43d6c44532d8b9b16f3e743cdf6cfe6c5 \
--hash=sha256:704f6393d758b12a4369887fe956b2a8c99e4aced839d9084de8e3f056015d40 \
--hash=sha256:73e7097b81cad9ae358334e3cec625246bb3b8013ae6bb287758dd6435e12f65 \
--hash=sha256:751b31c2faae0348f87f22b45ef58f704bdcfc2abdd680fa0c743c124071157e \
--hash=sha256:78ebb43dca7d5b41eee543cd005ee5a0256cecc74d84acf0fab4f025997b837e \
--hash=sha256:7b4e4c2b05f3b431e9026e82590b217e87696e7a7548f512ae8059d59fa8af3b \
--hash=sha256:7e28024204dc0c61094268c682041d2becfedfea2e3b46bed5f6138239304d98 \
--hash=sha256:83404a353240fdff5cfe9080665fdfdcaa2d4d0c5112e15b0a2fe2e59200ed57 \
--hash=sha256:86bb3656c8d744cc1e42003414cd6c765117d70aa23da6c0f4ff2b826e0fd0fd \
--hash=sha256:8c5c38129cc79d7e3ba553035b9962a442171e9f97bb1b8795c0885213f206f3 \
--hash=sha256:9124b6db07e8d8b11f4512b8b56cbe136bf1b7d0417d1280e62291a9dcad4408 \
--hash=sha256:914254849486e14aa931b0b3382cd16887f1507068ffba775cbdc5a55fe9ef19 \
--hash=sha256:92fad8f1aa80a5ab316c0493dc6d1b54c1dba21937e43eea7296ff4a0ccc071e \
--hash=sha256:93921178b9a40c60c26e47eb44970f88c49fe484aaa3bb7ec02bb8b514eab3d9 \
--hash=sha256:9690a535d9ccd361bbc3590bfce7fe679e847f44fa7cc97f3b885f4744ca8a2c \
--hash=sha256:a0c4ba73f9e7721dd6cc3e6953016652dbac206f654229b7a1a8ac182b16e689 \
--hash=sha256:a16abab0c1abc58feb6ab11d78d0f8178a67c3586bd70628ec7c0218ec04c4ef \
--hash=sha256:a343261701a8f63f0d8268f7fd32be40ffe28d24b65d905404ca03e7281f7bb5 \
--hash=sha256:a3f1196d76860e72d338fab0d2b6722e8d47e2285d693e366ae36011c4a5898a \
--hash=sha256:a880e4113af3ab84d6a0991e3f85a2424924c8a182733ab8d964421df8b5190a \
--hash=sha256:a89f36bf7b612ff6ed3e789bd987cbd0787cf0d66c49386fa3bad816dd7bee87 \
--hash=sha256:adb670031b27949c9dc5cf585c4a5a6b4469d3879fd2fb9d39b6d53e5f66b9bc \
--hash=sha256:b0711e46361ea3047cd049868419d030c8236a9dea7e9ed1f053cbd61a853ec9 \
--hash=sha256:b447ea765e71bc33a82cf070bba814b1efa77967442d116b95ccef8ce5da7631 \
--hash=sha256:bf424d92dd7e94705b31625b02d396297a7c8fab4b6f7de8dba6388323a7b71c \
--hash=sha256:c10b7713e3ed31df7319c2a72d5fea5a2536476d7695a3e1d18a1f289060997c \
--hash=sha256:c8a46f77ba0ca7c5a5449b777170a518fa7820e1710edb40e777c9798f00d033 \
--hash=sha256:ca1757a6e080086f7234dc45684e81a47a66a6dd492a37d6ce38c58a1a93e9ff \
--hash=sha256:d01c4faae66de60fcd3afd3720dcc8ffa03bc2087f898106da127774db12aac5 \
--hash=sha256:d1c0115bdf80cf6c8c9109cb10cf6f650fd1a8d841f884925e8cb12f34eb5371 \
--hash=sha256:d2e9ed88d9a6a475c67bf70fc8285e88ccece0391727c7701e5a512e0eafbb05 \
--hash=sha256:d54bcf2dfc0880bf13f38512d44b194c092794e4ee9e01d804bc6cd3eed9bfb7 \
--hash=sha256:d613a23f8928f30acb2b6b2398cb7775ba9852e8968e15df13807ba0d3ebd565 \
--hash=sha256:d90c0531e9d591bde8cea04e75107fcddcc56811b638a34853436b23c9a3cb7d \
--hash=sha256:dceb3930ec426623c0cacc78e447a90882981e8c49d6fea8d1e48850e24a0170 \
--hash=sha256:e1e867c2a729348df218a14ba1b862e627177fd57c7b4f3db0b4c708f6d03696 \
--hash=sha256:e6ae27b0617ad3809449964b5e901b21acff8e306abacb8ba71d5ee7c8c47eeb \
--hash=sha256:ea425a8dcd808a7232a5417d2633bfa543da583a2701b5228e9e29989a50deda \
--hash=sha256:f4028443bf25c1e04ecffdc552c0a98d826903dec76a1568dfddf5ebbbb03db7 \
--hash=sha256:f6898bf1ca5aa01115807643138e3e20ec603b17a811026bc4a49d43055720a7 \
--hash=sha256:f9ba559eabb0ba1afd4e0504fa0b10e00a212cac0c4028b8a1c3b087b5c1e5de
psycopg-binary==3.1.19 \
--hash=sha256:00879d4c6be4b3afc510073f48a5e960f797200e261ab3d9bd9b7746a08c669d \
--hash=sha256:0106e42b481677c41caa69474fe530f786dcef88b11b70000f0e45a03534bc8f \
--hash=sha256:017518bd2de4851adc826a224fb105411e148ad845e11355edd6786ba3dfedf5 \
--hash=sha256:03354a9db667c27946e70162cb0042c3929154167f3678a30d23cebfe0ad55b5 \
--hash=sha256:052f5193304066318853b4b2e248f523c8f52b371fc4e95d4ef63baee3f30955 \
--hash=sha256:0e991632777e217953ac960726158987da684086dd813ac85038c595e7382c91 \
--hash=sha256:1285aa54449e362b1d30d92b2dc042ad3ee80f479cc4e323448d0a0a8a1641fa \
--hash=sha256:1622ca27d5a7a98f7d8f35e8b146dc7efda4a4b6241d2edf7e076bd6bcecbeb4 \
--hash=sha256:1cf49e91dcf699b8a449944ed898ef1466b39b92720613838791a551bc8f587a \
--hash=sha256:1d87484dd42c8783c44a30400949efb3d81ef2487eaa7d64d1c54df90cf8b97a \
--hash=sha256:29008f3f8977f600b8a7fb07c2e041b01645b08121760609cc45e861a0364dc9 \
--hash=sha256:321814a9a3ad785855a821b842aba08ca1b7de7dfb2979a2f0492dca9ec4ae70 \
--hash=sha256:3433924e1b14074798331dc2bfae2af452ed7888067f2fc145835704d8981b15 \
--hash=sha256:34a6997c80f86d3dd80a4f078bb3b200079c47eeda4fd409d8899b883c90d2ac \
--hash=sha256:38ed45ec9673709bfa5bc17f140e71dd4cca56d4e58ef7fd50d5a5043a4f55c6 \
--hash=sha256:433f1c256108f9e26f480a8cd6ddb0fb37dbc87d7f5a97e4540a9da9b881f23f \
--hash=sha256:469424e354ebcec949aa6aa30e5a9edc352a899d9a68ad7a48f97df83cc914cf \
--hash=sha256:46e50c05952b59a214e27d3606f6d510aaa429daed898e16b8a37bfbacc81acc \
--hash=sha256:49cd7af7d49e438a39593d1dd8cab106a1912536c2b78a4d814ebdff2786094e \
--hash=sha256:4aa0ca13bb8a725bb6d12c13999217fd5bc8b86a12589f28a74b93e076fbb959 \
--hash=sha256:4ae8109ff9fdf1fa0cb87ab6645298693fdd2666a7f5f85660df88f6965e0bb7 \
--hash=sha256:5c6956808fd5cf0576de5a602243af8e04594b25b9a28675feddc71c5526410a \
--hash=sha256:621a814e60825162d38760c66351b4df679fd422c848b7c2f86ad399bff27145 \
--hash=sha256:6469ebd9e93327e9f5f36dcf8692fb1e7aeaf70087c1c15d4f2c020e0be3a891 \
--hash=sha256:6cff31af8155dc9ee364098a328bab688c887c732c66b8d027e5b03818ca0287 \
--hash=sha256:6d4e67fd86758dbeac85641419a54f84d74495a8683b58ad5dfad08b7fc37a8f \
--hash=sha256:703c2f3b79037581afec7baa2bdbcb0a1787f1758744a7662099b0eca2d721cb \
--hash=sha256:7204818f05151dd08f8f851defb01972ec9d2cc925608eb0de232563f203f354 \
--hash=sha256:738c34657305b5973af6dbb6711b07b179dfdd21196d60039ca30a74bafe9648 \
--hash=sha256:76fcd33342f38e35cd6b5408f1bc117d55ab8b16e5019d99b6d3ce0356c51717 \
--hash=sha256:7c6a9a651a08d876303ed059c9553df18b3c13c3406584a70a8f37f1a1fe2709 \
--hash=sha256:81efe09ba27533e35709905c3061db4dc9fb814f637360578d065e2061fbb116 \
--hash=sha256:85bca9765c04b6be90cb46e7566ffe0faa2d7480ff5c8d5e055ac427f039fd24 \
--hash=sha256:866db42f986298f0cf15d805225eb8df2228bf19f7997d7f1cb5f388cbfc6a0f \
--hash=sha256:8a732610a5a6b4f06dadcf9288688a8ff202fd556d971436a123b7adb85596e2 \
--hash=sha256:91a645e6468c4f064b7f4f3b81074bdd68fe5aa2b8c5107de15dcd85ba6141be \
--hash=sha256:955ca8905c0251fc4af7ce0a20999e824a25652f53a558ab548b60969f1f368e \
--hash=sha256:959feabddc7fffac89b054d6f23f3b3c62d7d3c90cd414a02e3747495597f150 \
--hash=sha256:95f16ae82bc242b76cd3c3e5156441e2bd85ff9ec3a9869d750aad443e46073c \
--hash=sha256:964c307e400c5f33fa762ba1e19853e048814fcfbd9679cc923431adb7a2ead2 \
--hash=sha256:9d39d5ffc151fb33bcd55b99b0e8957299c0b1b3e5a1a5f4399c1287ef0051a9 \
--hash=sha256:a100482950a55228f648bd382bb71bfaff520002f29845274fccbbf02e28bd52 \
--hash=sha256:a53809ee02e3952fae7977c19b30fd828bd117b8f5edf17a3a94212feb57faaf \
--hash=sha256:a836610d5c75e9cff98b9fdb3559c007c785c09eaa84a60d5d10ef6f85f671e8 \
--hash=sha256:aebd1e98e865e9a28ce0cb2c25b7dfd752f0d1f0a423165b55cd32a431dcc0f4 \
--hash=sha256:affebd61aa3b7a8880fd4ac3ee94722940125ff83ff485e1a7c76be9adaabb38 \
--hash=sha256:b04f5349313529ae1f1c42fe1aa0443faaf50fdf12d13866c2cc49683bfa53d0 \
--hash=sha256:bfd2c734da9950f7afaad5f132088e0e1478f32f042881fca6651bb0c8d14206 \
--hash=sha256:c1823221a6b96e38b15686170d4fc5b36073efcb87cce7d3da660440b50077f6 \
--hash=sha256:c35fd811f339a3cbe7f9b54b2d9a5e592e57426c6cc1051632a62c59c4810208 \
--hash=sha256:c50592bc8517092f40979e4a5d934f96a1737a77724bb1d121eb78b614b30fc8 \
--hash=sha256:cd88c5cea4efe614d5004fb5f5dcdea3d7d59422be796689e779e03363102d24 \
--hash=sha256:d1bac282f140fa092f2bbb6c36ed82270b4a21a6fc55d4b16748ed9f55e50fdb \
--hash=sha256:d1d1723d7449c12bb61aca7eb6e0c6ab2863cd8dc0019273cc4d4a1982f84bdb \
--hash=sha256:d312d6dddc18d9c164e1893706269c293cba1923118349d375962b1188dafb01 \
--hash=sha256:d9b689c4a17dd3130791dcbb8c30dbf05602f7c2d56c792e193fb49adc7bf5f8 \
--hash=sha256:e12173e34b176e93ad2da913de30f774d5119c2d4d4640c6858d2d77dfa6c9bf \
--hash=sha256:e14bc8250000921fcccd53722f86b3b3d1b57db901e206e49e2ab2afc5919c2d \
--hash=sha256:e538a8671005641fa195eab962f85cf0504defbd3b548c4c8fc27102a59f687b \
--hash=sha256:e9da624a6ca4bc5f7fa1f03f8485446b5b81d5787b6beea2b4f8d9dbef878ad7 \
--hash=sha256:ed61e43bf5dc8d0936daf03a19fef3168d64191dbe66483f7ad08c4cea0bc36b \
--hash=sha256:ef8de7a1d9fb3518cc6b58e3c80b75a824209ad52b90c542686c912db8553dad \
--hash=sha256:fb9758473200384a04374d0e0cac6f451218ff6945a024f65a1526802c34e56e
# via psycopg
psycopg-pool==3.2.2 \
--hash=sha256:273081d0fbfaced4f35e69200c89cb8fbddfe277c38cc86c235b90a2ec2c8153 \

View file

@ -1,5 +1,5 @@
fastapi==0.111.0
redis==5.0.4
redis==5.0.5
requests==2.32.3
selenium<4.17.0
uvicorn[standard]==0.30.1

View file

@ -497,9 +497,9 @@ pyyaml==6.0.1 \
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
# via uvicorn
redis==5.0.4 \
--hash=sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91 \
--hash=sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61
redis==5.0.5 \
--hash=sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada \
--hash=sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae
# via -r requirements.in
requests==2.32.3 \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \

View file

@ -1,4 +1,4 @@
FROM redhat/ubi9-init:9.4-6.1716477011@sha256:df8e043878f3f459d6fcf3e9abce3f9f6e1526a3695bf0ac487d780e031ac8ab
FROM redhat/ubi9-init:9.4-6.1717075635@sha256:c86a45477e2803d2787217d3dde408512cd11af07ce12a624b2083eb18acb3e0
ENV NGINX_VERSION 1.26.1