From 862d1262f5878ef8fe34be14fd598e1326aa6fe5 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Mon, 6 Jul 2026 16:29:26 +0545 Subject: [PATCH 1/5] feat: integrate banjo-utils (health probes, worker heartbeat, wait_for_resources) Add banjo-utils (branch pin feature/health-endpoints) and wire its health tooling, replacing the homegrown wait_for_resources command: - INSTALLED_APPS += banjo_utils; HealthProbeMiddleware first (serves pod-local /healthz/live/ and /healthz/ready/, bypassing ALLOWED_HOSTS). - BANJO_HEALTH_PROBE_* settings; setup_worker_heartbeat(app) in celery for the banjo-celery-probe worker/beat liveness. - Sentry traces_sampler ignores health-probe paths so probes don't burn quota. - LOGGING CallbackFilter drops /healthz/* request-line noise from django.server (dev) and gunicorn.access (prod). - Delete common wait_for_resources (and its RetryHelper) in favour of banjo's; CI docker__wait_cmd --redis -> --cache to match banjo's renamed flag. - Keep django-health-check (/health-check/) as the external UptimeRobot monitor. --- .github/workflows/ci.yml | 2 +- .github/workflows/helm-publish.yml | 2 +- .../management/commands/wait_for_resources.py | 137 ------------------ pyproject.toml | 4 + risk_module/celery.py | 4 + risk_module/settings.py | 49 ++++++- uv.lock | 72 +++++++++ 7 files changed, 130 insertions(+), 140 deletions(-) delete mode 100644 common/management/commands/wait_for_resources.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d11f16f..b816e21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: # Job: Pre-commit pre_commit__apt_packages: "gdal-bin libgdal-dev" # Job: Docker - docker__wait_cmd: "./manage.py wait_for_resources --db --redis" + docker__wait_cmd: "./manage.py wait_for_resources --db --cache" docker__migration_dummy_test: "risk_module.tests.FakeTest" docker__run_graphql_check: false docker__run_openapi_check: true diff --git a/.github/workflows/helm-publish.yml b/.github/workflows/helm-publish.yml index 8962859..0164bab 100644 --- a/.github/workflows/helm-publish.yml +++ b/.github/workflows/helm-publish.yml @@ -15,7 +15,7 @@ jobs: # Job: Pre-commit pre_commit__apt_packages: "gdal-bin libgdal-dev" # Job: Docker - docker__wait_cmd: "./manage.py wait_for_resources --db --redis" + docker__wait_cmd: "./manage.py wait_for_resources --db --cache" docker__migration_dummy_test: "risk_module.tests.FakeTest" docker__run_graphql_check: false docker__run_openapi_check: true diff --git a/common/management/commands/wait_for_resources.py b/common/management/commands/wait_for_resources.py deleted file mode 100644 index e8eb270..0000000 --- a/common/management/commands/wait_for_resources.py +++ /dev/null @@ -1,137 +0,0 @@ -import signal -import time -from urllib.parse import urljoin - -import requests -from django.conf import settings -from django.core.cache import cache -from django.core.management.base import BaseCommand -from django.db import connections -from django.db.utils import OperationalError -from redis.exceptions import ConnectionError as RedisConnectionError - - -class TimeoutException(Exception): ... - - -class RetryHelper: - def __init__(self, base_wait_seconds: int = 2, wait_max_seconds: int = 60): - self.base_wait_seconds = base_wait_seconds - self.wait_max_seconds = wait_max_seconds - self.attempt = 1 - self.next_wait = base_wait_seconds - self.start_time = time.time() - - def next_wait_seconds(self) -> int: - return self.next_wait - - def wait(self) -> None: - time.sleep(self.next_wait) - self.attempt += 1 - if self.next_wait < self.wait_max_seconds: - self.next_wait = self.base_wait_seconds**self.attempt - else: - self.next_wait = self.wait_max_seconds - - def total_time(self) -> float: - return time.time() - self.start_time - - def try_again_message(self, prefix: str) -> str: - return f"{prefix}, Attempt: {self.attempt}, try again after {self.next_wait_seconds()} seconds..." - - -def timeout_handler(*_): - raise Exception("The command timed out.") - - -class Command(BaseCommand): - help = "Wait for resources our application depends on" - - def wait_for_db(self): - self.stdout.write("Waiting for DB...") - db_conn = None - retry_helper = RetryHelper() - while True: - try: - db_conn = connections["default"] - db_conn.ensure_connection() - break - except OperationalError: - ... - # Try again - self.stdout.write(self.style.WARNING(retry_helper.try_again_message("DB not available"))) - retry_helper.wait() - - self.stdout.write(self.style.SUCCESS(f"DB is available after {retry_helper.total_time()} seconds")) - - def wait_for_redis(self): - self.stdout.write("Waiting for Redis...") - redis_conn = None - retry_helper = RetryHelper() - while True: - try: - cache.set("wait-for-it-ping", "pong", timeout=1) # Set a key to check Redis availability - redis_conn = cache.get("wait-for-it-ping") # Try to get the value back from Redis - if redis_conn != "pong": - raise TypeError - break - except (RedisConnectionError, TypeError): - ... - # Try again - self.stdout.write(self.style.WARNING(retry_helper.try_again_message("Redis not available"))) - retry_helper.wait() - - self.stdout.write(self.style.SUCCESS(f"Redis is available after {retry_helper.total_time()} seconds")) - - def wait_for_minio(self): - self.stdout.write("Waiting for Minio...") - endpoint_url = getattr(settings, "AWS_S3_PROXIES", {}).get("http") or getattr(settings, "AWS_S3_ENDPOINT_URL", None) - if endpoint_url is None: - self.stdout.write(self.style.WARNING("No endpoint_url is provided. Skipping wait")) - return - - retry_helper = RetryHelper() - while True: - try: - response = requests.get(urljoin(endpoint_url, "/minio/health/live"), timeout=5) - if response.status_code == 200: - break - except requests.exceptions.RequestException: - ... - # Try again - self.stdout.write(self.style.WARNING(retry_helper.try_again_message("Minio not available"))) - retry_helper.wait() - - self.stdout.write(self.style.SUCCESS(f"Minio is available after {retry_helper.total_time()} seconds")) - - def add_arguments(self, parser): - parser.add_argument( - "--timeout", - type=int, - default=600, - help="The maximum time (in seconds) the command is allowed to run before timing out. Default is 10 min.", - ) - parser.add_argument("--db", action="store_true", help="Wait for DB to be available") - parser.add_argument("--redis", action="store_true", help="Wait for Redis to be available") - parser.add_argument("--minio", action="store_true", help="Wait for MinIO (S3) storage to be available") - parser.add_argument("--all", action="store_true", help="Wait for all to be available") - - def handle(self, **kwargs): - timeout = kwargs["timeout"] - _all = kwargs["all"] - - signal.signal(signal.SIGALRM, timeout_handler) - signal.alarm(timeout) - - try: - if _all or kwargs["db"]: - self.wait_for_db() - if _all or kwargs["minio"]: - self.wait_for_minio() - if _all or kwargs["redis"]: - self.wait_for_redis() - except TimeoutException: - ... - finally: - # Disable the alarm (cleanup) - signal.alarm(0) diff --git a/pyproject.toml b/pyproject.toml index 8bca60b..5edf351 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,8 +40,12 @@ dependencies = [ "django-health-check", "psutil", "azure-identity", + "banjo-utils", ] +[tool.uv.sources] +banjo-utils = { git = "https://github.com/toggle-corp/banjo-utils.git", branch = "feature/health-endpoints" } + [dependency-groups] dev = [ "pytest-django", diff --git a/risk_module/celery.py b/risk_module/celery.py index c1cdacf..639427c 100644 --- a/risk_module/celery.py +++ b/risk_module/celery.py @@ -30,6 +30,10 @@ def config_loggers(*args, **kwags): app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) +from banjo_utils.celery_health.worker import setup_worker_heartbeat # noqa: E402 + +setup_worker_heartbeat(app) + @app.task(bind=True) def debug_task(self): diff --git a/risk_module/settings.py b/risk_module/settings.py index 13107b8..d778b06 100644 --- a/risk_module/settings.py +++ b/risk_module/settings.py @@ -14,6 +14,10 @@ import environ from azure.identity import DefaultAzureCredential +from banjo_utils.health import ( + is_health_probe_path, + make_sentry_traces_sampler_with_health_probe_ignore, +) from celery.schedules import crontab from django.utils.log import DEFAULT_LOGGING @@ -102,6 +106,7 @@ "corsheaders", "storages", "drf_spectacular", + "banjo_utils", # Health-check "health_check", # required "health_check.db", # stock Django health checkers @@ -120,6 +125,7 @@ ] MIDDLEWARE = [ + "banjo_utils.health.HealthProbeMiddleware", "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -204,6 +210,26 @@ def log_render_extra_context(record): return True +def skip_health_probe_logs(record): + """Drop request-line log records for k8s health-probe paths (/healthz/*). + + The kubelet hits liveness/readiness/startup every few seconds; without this + the request-line logger is swamped by probe traffic. Reads the path from + ``gunicorn.access`` (dict args, key ``U``) or ``django.server`` (the + ``"GET /path HTTP/1.1"`` request line) records, honouring the + BANJO_HEALTH_PROBE_* overrides via ``is_health_probe_path``. + """ + args = record.args + path = "" + if isinstance(args, dict): # gunicorn.access + path = args.get("U", "") + elif isinstance(args, (tuple, list)) and args: # django.server request line + request_line = str(args[0]).strip('"').split(" ") + if len(request_line) >= 2: + path = request_line[1] + return not is_health_probe_path(path) + + LOGGING = { **DEFAULT_LOGGING, "formatters": { @@ -219,6 +245,10 @@ def log_render_extra_context(record): "()": "django.utils.log.CallbackFilter", "callback": log_render_extra_context, }, + "skip_health_probes": { + "()": "django.utils.log.CallbackFilter", + "callback": skip_health_probe_logs, + }, }, "handlers": { **DEFAULT_LOGGING["handlers"], @@ -229,6 +259,11 @@ def log_render_extra_context(record): "formatter": "simple", "filters": ["render_extra_context"], }, + # runserver request-line logger: drop /healthz/* probe spam + "django.server": { + **DEFAULT_LOGGING["handlers"]["django.server"], + "filters": ["skip_health_probes"], + }, }, "loggers": { **DEFAULT_LOGGING["loggers"], @@ -244,6 +279,13 @@ def log_render_extra_context(record): "level": env("APPS_LOGGING_LEVEL"), "propagate": False, }, + # gunicorn access log (prod): drop /healthz/* probe spam + "gunicorn.access": { + "handlers": ["console"], + "level": "INFO", + "propagate": False, + "filters": ["skip_health_probes"], + }, # Silence Azure "azure.core.pipeline.policies.http_logging_policy": { "level": "WARNING", @@ -413,7 +455,8 @@ def log_render_extra_context(record): SENTRY_CONFIG = { "dsn": SENTRY_DSN, "send_default_pii": True, - "traces_sample_rate": SENTRY_TRACE_SAMPLE_RATE, + # Drop health-probe transactions from tracing (banjo-utils health endpoints). + "traces_sampler": make_sentry_traces_sampler_with_health_probe_ignore(SENTRY_TRACE_SAMPLE_RATE), "profiles_sample_rate": SENTRY_PROFILE_SAMPLE_RATE, "release": RISK_RELEASE, "environment": RISK_ENVIRONMENT, @@ -444,6 +487,10 @@ def log_render_extra_context(record): ], } +# banjo-utils HealthProbeMiddleware endpoints +BANJO_HEALTH_PROBE_LIVE_URL = "/healthz/live/" +BANJO_HEALTH_PROBE_READY_URL = "/healthz/ready/" + # Health-check config REDIS_URL = env("CACHE_REDIS_URL") HEALTHCHECK_CACHE_KEY = "go_risk_healthcheck_key" diff --git a/uv.lock b/uv.lock index 67d9b21..112ccf6 100644 --- a/uv.lock +++ b/uv.lock @@ -19,6 +19,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/99/fc813cd978842c26c82534010ea849eee9ab3a13ea2b74e95cb9c99e747b/amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2", size = 50944, upload-time = "2024-11-12T19:55:41.782Z" }, ] +[[package]] +name = "anyio" +version = "4.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/f9/9a7ce600ebe7804daf90d4d48b1c0510a4561ddce43a596be46676f82343/anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b", size = 171293, upload-time = "2024-10-13T22:18:03.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/b4/f7e396030e3b11394436358ca258a81d6010106582422f23443c16ca1873/anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f", size = 89766, upload-time = "2024-10-13T22:18:01.524Z" }, +] + [[package]] name = "appnope" version = "0.1.4" @@ -139,6 +154,15 @@ tzdata = [ { name = "tzdata" }, ] +[[package]] +name = "banjo-utils" +version = "0.1.0" +source = { git = "https://github.com/toggle-corp/banjo-utils.git?branch=feature%2Fhealth-endpoints#d414fa04754a631f3956c5edcb36570552b25eb6" } +dependencies = [ + { name = "django" }, + { name = "httpx" }, +] + [[package]] name = "beautifulsoup4" version = "4.11.1" @@ -684,6 +708,43 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/69/ca/926f7cd3a2014b16870086b2d0fdc84a9e49473c68a8dff8b57f7c156f43/gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c", size = 77859, upload-time = "2019-11-26T22:52:57.223Z" }, ] +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + [[package]] name = "idna" version = "3.10" @@ -1320,6 +1381,7 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "azure-identity" }, + { name = "banjo-utils" }, { name = "beautifulsoup4" }, { name = "boto3" }, { name = "celery", extra = ["redis"] }, @@ -1365,6 +1427,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "azure-identity" }, + { name = "banjo-utils", git = "https://github.com/toggle-corp/banjo-utils.git?branch=feature%2Fhealth-endpoints" }, { name = "beautifulsoup4", specifier = "==4.11.1" }, { name = "boto3", specifier = "==1.20.33" }, { name = "celery", extras = ["redis"], specifier = "==5.1.1" }, @@ -1485,6 +1548,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + [[package]] name = "soupsieve" version = "2.7" From 5a14912f562d5cd1efb20ce5566e3492f2e4abd2 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Mon, 6 Jul 2026 16:29:36 +0545 Subject: [PATCH 2/5] feat(helm): migrate chart from toggle-django-helm to banjo-helm 0.4.0-dev3 Values-schema migration (dependency alias kept as `app`): - Drop bundled subcharts (postgresql/minio/redis/rabbitmq); infra is supplied externally (alpha DB/S3/cache secrets come from user-provided secrets). - ingress -> api.ingress; argoHook.hooks -> hooks.jobs (hook: Sync + sync-waves 10/20/25, wait-for-resources gates db-migrate/collect-static); worker.beat -> worker.addons.beat (Recreate); queueCommandPrefix / queueDefaultResources -> queueDefaults.*; operators extraEnvVars list -> env map (DragonflyDB kept via extraManifests). - db-migrate/collect-static inherit chart defaults; only wait-for-resources overrides the command (--db --cache [--minio]). - alpha create-users uses the hooks `files` mechanism (dev3): script mounted via a per-job ConfigMap at /etc/hook-files and piped into `manage.py shell`. - Enable probes in base values: api /healthz/*, worker/beat banjo-celery-probe heartbeat (RAM emptyDir); omit replicaCount (no KEDA). - Regenerate alpha-1 + staging snapshots; helm template renders both. --- helm/Chart.lock | 8 +- helm/Chart.yaml | 4 +- helm/snapshots/alpha-1.yaml | 1260 ++++++----------------------------- helm/snapshots/staging.yaml | 234 +++++-- helm/tests/alpha-1.yaml | 40 +- helm/tests/staging.yaml | 9 +- helm/values.yaml | 167 +++-- helm/values/alpha.yaml | 158 +---- helm/values/go-deploy.yaml | 9 +- helm/values/operators.yaml | 8 +- 10 files changed, 549 insertions(+), 1348 deletions(-) diff --git a/helm/Chart.lock b/helm/Chart.lock index 5b4e308..6834038 100644 --- a/helm/Chart.lock +++ b/helm/Chart.lock @@ -1,6 +1,6 @@ dependencies: -- name: toggle-django-helm +- name: banjo-helm repository: oci://ghcr.io/toggle-corp - version: 0.2.5 -digest: sha256:94f65db722244c4ba17db79c144bc75bd43f4e21c4e221a3697a78c523fc4298 -generated: "2026-03-21T22:29:52.126868712+05:45" + version: 0.4.0-dev3 +digest: sha256:ab020e258cfbfd35ba62388bee506d1d26a49ce09f5e94daeae2e24242cc8e63 +generated: "2026-07-06T20:21:03.987889187+05:45" diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 0682c3b..a7ceaee 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -7,7 +7,7 @@ sources: - https://github.com/IFRCGo/go-risk-module-api dependencies: - - name: toggle-django-helm # TODO: Migrate to banjo-helm + - name: banjo-helm alias: app - version: 0.2.5 + version: 0.4.0-dev3 repository: oci://ghcr.io/toggle-corp diff --git a/helm/snapshots/alpha-1.yaml b/helm/snapshots/alpha-1.yaml index 7aa9351..a0b386a 100644 --- a/helm/snapshots/alpha-1.yaml +++ b/helm/snapshots/alpha-1.yaml @@ -1,196 +1,3 @@ ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/networkpolicy.yaml -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: fircgo-risk-module-minio - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -spec: - podSelector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: minio - policyTypes: - - Ingress - - Egress - egress: - - {} - ingress: - # Allow inbound connections - - ports: - - port: 9001 - - port: 9000 - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/provisioning-networkpolicy.yaml -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: fircgo-risk-module-minio-provisioning - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -spec: - podSelector: - matchLabels: - app.kubernetes.io/component: minio-provisioning - policyTypes: - - Ingress - - Egress - egress: - - {} - ingress: - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/primary/networkpolicy.yaml -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: ifrcgo-risk-module-pg - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 - app.kubernetes.io/component: primary -spec: - podSelector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: postgresql - app.kubernetes.io/component: primary - policyTypes: - - Ingress - - Egress - egress: - - {} - ingress: - - ports: - - port: 5432 - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/pdb.yaml -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: fircgo-risk-module-minio - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -spec: - maxUnavailable: 1 - selector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: minio - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/primary/pdb.yaml -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: ifrcgo-risk-module-pg - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 - app.kubernetes.io/component: primary -spec: - maxUnavailable: 1 - selector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: postgresql - app.kubernetes.io/component: primary - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: fircgo-risk-module-minio - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -automountServiceAccountToken: false -secrets: - - name: fircgo-risk-module-minio - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: ifrcgo-risk-module-pg - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 -automountServiceAccountToken: false - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/secrets.yaml -apiVersion: v1 -kind: Secret -metadata: - name: fircgo-risk-module-minio - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -type: Opaque -data: - root-user: "cmlzay1tb2R1bGU=" - root-password: "WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFg=" - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/secrets.yaml -apiVersion: v1 -kind: Secret -metadata: - name: ifrcgo-risk-module-pg - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 -type: Opaque -data: - postgres-password: "WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFg=" - password: "WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFg=" - # We don't auto-generate LDAP password when it's not provided as we do for other passwords - --- # Source: ifrcgo-risk-module-helm/charts/app/templates/config/secret.yaml kind: Secret @@ -204,16 +11,6 @@ metadata: type: Opaque stringData: # secrets - AWS_S3_ACCESS_KEY_ID: "risk-module" - AWS_S3_BUCKET_NAME: "risk-module-storage" - AWS_S3_ENDPOINT_URL: "https://risk-minio-1.ifrcgo.local.example.com/" - AWS_S3_REGION: "us-east-1" - AWS_S3_SECRET_ACCESS_KEY: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - DATABASE_HOST: "ifrcgo-risk-module-pg" - DATABASE_NAME: "risk-module" - DATABASE_PASSWORD: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - DATABASE_PORT: "5432" - DATABASE_USER: "postgres" DJANGO_SECRET_KEY: "xyz" METEOSWISS_S3_ACCESS_KEY: "dummy-access-key" METEOSWISS_S3_BUCKET: "dummy-bucket" @@ -223,45 +20,6 @@ stringData: PDC_PASSWORD: "dummy-password" PDC_USERNAME: "dummy-username" SENTRY_DSN: "https://xyz@sentry.local.example.com/14" - USE_S3_BUCKET: "true" - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/provisioning-configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: fircgo-risk-module-minio-provisioning - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 - app.kubernetes.io/component: minio-provisioning -data: - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/primary/initialization-configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: ifrcgo-risk-module-pg-init-scripts - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 -data: - my_init_script.sh: | - #!/bin/sh -e - DATABASE_DUMP_URL="$DUMP_REGISTRY_URL$DUMP_FILENAME" - # Don't have curl here - /usr/lib/apt/apt-helper download-file -o "Acquire::https::Verify-Peer=false" "$DATABASE_DUMP_URL" /tmp/db.dump - du -sh /tmp/db.dump - PGPASSWORD="${POSTGRES_PASSWORD}" PGUSER=$POSTGRES_USER pg_restore -v --no-owner --no-privileges -d "$POSTGRES_DATABASE" < /tmp/db.dump --- # Source: ifrcgo-risk-module-helm/charts/app/templates/config/configmap.yaml @@ -276,6 +34,8 @@ metadata: data: # Configs APPS_LOGGING_LEVEL: "WARNING" + CACHE_REDIS_URL: "redis://risk-module-dragonfly:6379/0" + CELERY_REDIS_URL: "redis://risk-module-dragonfly:6379/1" DJANGO_ALLOWED_HOSTS: "*" DJANGO_DEBUG: "false" DJANGO_TIME_ZONE: "UTC" @@ -287,112 +47,25 @@ data: TIME_ZONE: "UTC" --- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/pvc.yaml -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: fircgo-risk-module-minio - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -spec: - accessModes: - - "ReadWriteOnce" - resources: - requests: - storage: "0.5Gi" - storageClassName: local-path - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: fircgo-risk-module-minio - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -spec: - type: ClusterIP - ports: - - name: minio-api - port: 9000 - targetPort: minio-api - nodePort: null - - name: minio-console - port: 9001 - targetPort: minio-console - nodePort: null - selector: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: minio - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/primary/svc-headless.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml +# Hook files ConfigMap for: create-users apiVersion: v1 -kind: Service +kind: ConfigMap metadata: - name: ifrcgo-risk-module-pg-hl - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 - app.kubernetes.io/component: primary + name: risk-1-create-users-files annotations: -spec: - type: ClusterIP - clusterIP: None - # We want all pods in the StatefulSet to have their addresses published for - # the sake of the other Postgresql pods even before they're ready, since they - # have to be able to talk to each other in order to become ready. - publishNotReadyAddresses: true - ports: - - name: tcp-postgresql - port: 5432 - targetPort: tcp-postgresql - selector: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: postgresql - app.kubernetes.io/component: primary - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/primary/svc.yaml -apiVersion: v1 -kind: Service -metadata: - name: ifrcgo-risk-module-pg - namespace: "default" + argocd.argoproj.io/hook: "Sync" + argocd.argoproj.io/sync-wave: "25" + # Refresh content on every sync and clean up before the next run. + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 - app.kubernetes.io/component: primary -spec: - type: ClusterIP - sessionAffinity: None - ports: - - name: tcp-postgresql - port: 5432 - targetPort: tcp-postgresql - nodePort: null - selector: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: postgresql - app.kubernetes.io/component: primary - + app: risk-1 + component: hook + hookName: create-users + environment: ALPHA-1 + release: release-name +data: + create_users.py: "import os\nfrom django.contrib.auth import get_user_model\nfrom django.db import transaction\n\nUser = get_user_model()\n\nuser_credentials = [\n user_credential.split(\":\")\n for user_credential in os.environ[\"ADMIN_USER_EMAIL_PASSWORD_SET\"].split(\",\")\n]\n\nprint(\"Parsed provided credentials:\", user_credentials)\n\n@transaction.atomic\ndef create_initial_users(user_credentials):\n for first_name, last_name, username, email, password_hash in user_credentials:\n user, created = User.objects.update_or_create(\n email=email,\n username=username,\n defaults={\n \"first_name\": first_name,\n \"last_name\": last_name,\n \"password\": password_hash,\n \"is_superuser\": True,\n \"is_staff\": True,\n },\n )\n action = \"created\" if created else \"updated\"\n print(f\"Successfully {action} user({email=})\")\n\ncreate_initial_users(user_credentials)\n" --- # Source: ifrcgo-risk-module-helm/charts/app/templates/api/service.yaml apiVersion: v1 @@ -414,159 +87,6 @@ spec: port: 80 targetPort: 80 ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/standalone/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: fircgo-risk-module-minio - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 -spec: - selector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: minio - strategy: - type: Recreate - template: - metadata: - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 - annotations: - checksum/credentials-secret: d53dea05c271cd688f9b9ef0a04ebb5f9b883ab2ae43b121274a9c9b4d3f9934 - spec: - - serviceAccountName: fircgo-risk-module-minio - affinity: - podAffinity: - - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: minio - topologyKey: kubernetes.io/hostname - weight: 1 - nodeAffinity: - - nodeSelector: - kubernetes.io/hostname: desktop-01 - automountServiceAccountToken: false - securityContext: - fsGroup: 1001 - fsGroupChangePolicy: OnRootMismatch - supplementalGroups: [] - sysctls: [] - containers: - - name: minio - image: docker.io/bitnamilegacy/minio:2024.12.18-debian-12-r1 - imagePullPolicy: "IfNotPresent" - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1001 - runAsNonRoot: true - runAsUser: 1001 - seLinuxOptions: {} - seccompProfile: - type: RuntimeDefault - env: - - name: BITNAMI_DEBUG - value: "false" - - name: MINIO_SCHEME - value: "http" - - name: MINIO_FORCE_NEW_KEYS - value: "yes" - - name: MINIO_API_PORT_NUMBER - value: "9000" - - name: MINIO_ROOT_USER - valueFrom: - secretKeyRef: - name: fircgo-risk-module-minio - key: root-user - - name: MINIO_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: fircgo-risk-module-minio - key: root-password - - name: MINIO_DEFAULT_BUCKETS - value: risk-module-storage - - name: MINIO_BROWSER - value: "off" - - name: MINIO_PROMETHEUS_AUTH_TYPE - value: "public" - - name: MINIO_CONSOLE_PORT_NUMBER - value: "9001" - - name: MINIO_DATA_DIR - value: "/bitnami/minio/data" - envFrom: - ports: - - name: minio-api - containerPort: 9000 - protocol: TCP - - name: minio-console - containerPort: 9001 - protocol: TCP - livenessProbe: - httpGet: - path: /minio/health/live - port: minio-api - scheme: "HTTP" - initialDelaySeconds: 5 - periodSeconds: 5 - timeoutSeconds: 5 - successThreshold: 1 - failureThreshold: 5 - readinessProbe: - tcpSocket: - port: minio-api - initialDelaySeconds: 5 - periodSeconds: 5 - timeoutSeconds: 1 - successThreshold: 1 - failureThreshold: 5 - resources: - limits: - cpu: "4" - memory: 256Mi - requests: - cpu: "0.1" - memory: 128Mi - volumeMounts: - - name: empty-dir - mountPath: /tmp - subPath: tmp-dir - - name: empty-dir - mountPath: /opt/bitnami/minio/tmp - subPath: app-tmp-dir - - name: empty-dir - mountPath: /.mc - subPath: app-mc-dir - - name: data - mountPath: /bitnami/minio/data - volumes: - - name: empty-dir - emptyDir: {} - - name: data - persistentVolumeClaim: - claimName: fircgo-risk-module-minio - --- # Source: ifrcgo-risk-module-helm/charts/app/templates/api/deployment.yaml apiVersion: apps/v1 @@ -574,6 +94,7 @@ kind: Deployment metadata: name: risk-1-api annotations: + argocd.argoproj.io/sync-wave: "30" reloader.stakater.com/auto: "true" labels: app: risk-1 @@ -581,7 +102,7 @@ metadata: environment: ALPHA-1 release: release-name spec: - replicas: 1 + revisionHistoryLimit: 1 selector: matchLabels: app: risk-1 @@ -589,12 +110,13 @@ spec: template: metadata: annotations: - checksum/secret: a5d73225a01c3e24b1530c338a0a44cf07ab77dc8646dc0562caada4bbc1b61f - checksum/configmap: f00be2c6cf51cc19c80358f167f85af68caa5a8c2405a310fc0fab05dfb0a0c8 + checksum/secret: 79d2dd5323b3376b79ce3f74dfc7c574a19e883e445eda033103627ff729c9a5 + checksum/configmap: 6882edae1c39e4b22afd8da880c9b404913ad4b049d51eaa3c03a464e0d89f93 labels: app: risk-1 component: api spec: + terminationGracePeriodSeconds: 38 containers: - name: api image: "local.git.com/ifrcgo/risk:develop.xxxxxxxx" @@ -608,14 +130,33 @@ spec: - name: http containerPort: 80 protocol: TCP - # TODO: livenessProbe + livenessProbe: + failureThreshold: 6 + httpGet: + path: /healthz/live/ + port: http + periodSeconds: 20 + timeoutSeconds: 2 + readinessProbe: + failureThreshold: 6 + httpGet: + path: /healthz/ready/ + port: http + periodSeconds: 10 + timeoutSeconds: 2 + startupProbe: + failureThreshold: 30 + httpGet: + path: /healthz/ready/ + port: http + periodSeconds: 5 resources: limits: cpu: "4" memory: 2Gi requests: cpu: "0.1" - memory: 2Gi + memory: 1Gi envFrom: - secretRef: name: risk-1-secret @@ -624,41 +165,54 @@ spec: env: - name: DJANGO_APP_TYPE value: "web" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + + lifecycle: + preStop: + sleep: + seconds: 8 --- -# Source: ifrcgo-risk-module-helm/charts/app/templates/worker-beat/deployment.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/worker-addons/deployment.yaml +# Addon: beat apiVersion: apps/v1 kind: Deployment metadata: name: risk-1-worker-beat annotations: + argocd.argoproj.io/sync-wave: "30" reloader.stakater.com/auto: "true" labels: app: risk-1 - component: worker-beat + component: worker-addon + addon: beat environment: ALPHA-1 release: release-name spec: - replicas: 1 # This should only 1 + strategy: + type: Recreate + revisionHistoryLimit: 1 selector: matchLabels: app: risk-1 - component: worker-beat + component: worker-addon + addon: beat template: metadata: annotations: - checksum/secret: a5d73225a01c3e24b1530c338a0a44cf07ab77dc8646dc0562caada4bbc1b61f - checksum/configmap: f00be2c6cf51cc19c80358f167f85af68caa5a8c2405a310fc0fab05dfb0a0c8 + checksum/secret: 79d2dd5323b3376b79ce3f74dfc7c574a19e883e445eda033103627ff729c9a5 + checksum/configmap: 6882edae1c39e4b22afd8da880c9b404913ad4b049d51eaa3c03a464e0d89f93 labels: app: risk-1 - component: worker-beat + component: worker-addon + addon: beat spec: + volumes: + - emptyDir: + medium: Memory + sizeLimit: 1Mi + name: celery-heartbeat containers: - - name: worker-beat + - name: beat image: "local.git.com/ifrcgo/risk:develop.xxxxxxxx" imagePullPolicy: IfNotPresent command: @@ -668,10 +222,18 @@ spec: - --max-interval=3600 - --loglevel=info - --scheduler=django_celery_beat.schedulers:DatabaseScheduler - # TODO: livenessProbe + livenessProbe: + exec: + command: + - banjo-celery-probe + - --max-age + - "360" + failureThreshold: 3 + periodSeconds: 60 + timeoutSeconds: 5 resources: limits: - cpu: "4" + cpu: "1" memory: 0.5Gi requests: cpu: "0.1" @@ -684,10 +246,12 @@ spec: env: - name: DJANGO_APP_TYPE value: "worker" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + + - name: BANJO_CELERY_HEARTBEAT_FILE + value: "/var/run/celery/beat_heartbeat" + volumeMounts: + - mountPath: /var/run/celery + name: celery-heartbeat --- # Source: ifrcgo-risk-module-helm/charts/app/templates/worker/deployment.yaml @@ -697,6 +261,7 @@ kind: Deployment metadata: name: risk-1-worker-default annotations: + argocd.argoproj.io/sync-wave: "30" reloader.stakater.com/auto: "true" labels: app: risk-1 @@ -705,7 +270,7 @@ metadata: environment: ALPHA-1 release: release-name spec: - replicas: 1 + revisionHistoryLimit: 1 selector: matchLabels: app: risk-1 @@ -714,13 +279,18 @@ spec: template: metadata: annotations: - checksum/secret: a5d73225a01c3e24b1530c338a0a44cf07ab77dc8646dc0562caada4bbc1b61f - checksum/configmap: f00be2c6cf51cc19c80358f167f85af68caa5a8c2405a310fc0fab05dfb0a0c8 + checksum/secret: 79d2dd5323b3376b79ce3f74dfc7c574a19e883e445eda033103627ff729c9a5 + checksum/configmap: 6882edae1c39e4b22afd8da880c9b404913ad4b049d51eaa3c03a464e0d89f93 labels: app: risk-1 component: worker queue: default spec: + volumes: + - emptyDir: + medium: Memory + sizeLimit: 1Mi + name: celery-heartbeat containers: - name: worker command: @@ -732,7 +302,19 @@ spec: - --max-tasks-per-child=20 image: "local.git.com/ifrcgo/risk:develop.xxxxxxxx" imagePullPolicy: IfNotPresent - # TODO: livenessProbe + livenessProbe: + exec: + command: + - banjo-celery-probe + failureThreshold: 6 + periodSeconds: 20 + timeoutSeconds: 5 + startupProbe: + exec: + command: + - banjo-celery-probe + failureThreshold: 30 + periodSeconds: 5 resources: limits: cpu: "4" @@ -748,228 +330,40 @@ spec: env: - name: DJANGO_APP_TYPE value: "worker" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 - ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/postgresql/templates/primary/statefulset.yaml -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: ifrcgo-risk-module-pg - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 - app.kubernetes.io/component: primary -spec: - replicas: 1 - serviceName: ifrcgo-risk-module-pg-hl - updateStrategy: - rollingUpdate: {} - type: RollingUpdate - selector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: postgresql - app.kubernetes.io/component: primary - template: - metadata: - name: ifrcgo-risk-module-pg - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: postgresql - app.kubernetes.io/version: 17.2.0 - helm.sh/chart: postgresql-16.4.5 - app.kubernetes.io/component: primary - spec: - serviceAccountName: ifrcgo-risk-module-pg - - automountServiceAccountToken: false - affinity: - podAffinity: - - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchLabels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/name: postgresql - app.kubernetes.io/component: primary - topologyKey: kubernetes.io/hostname - weight: 1 - nodeAffinity: - - nodeSelector: - kubernetes.io/hostname: desktop-01 - securityContext: - fsGroup: 1001 - fsGroupChangePolicy: Always - supplementalGroups: [] - sysctls: [] - hostNetwork: false - hostIPC: false - containers: - - name: postgresql - image: docker.io/bitnamilegacy/postgresql:17.2.0-debian-12-r8 - imagePullPolicy: "IfNotPresent" - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1001 - runAsNonRoot: true - runAsUser: 1001 - seLinuxOptions: {} - seccompProfile: - type: RuntimeDefault - env: - - name: BITNAMI_DEBUG - value: "false" - - name: POSTGRESQL_PORT_NUMBER - value: "5432" - - name: POSTGRESQL_VOLUME_DIR - value: "/bitnami/postgresql" - - name: PGDATA - value: "/bitnami/postgresql/data" - # Authentication - - name: POSTGRES_USER - value: "risk-module" - - name: POSTGRES_PASSWORD - valueFrom: - secretKeyRef: - name: ifrcgo-risk-module-pg - key: password - - name: POSTGRES_POSTGRES_PASSWORD - valueFrom: - secretKeyRef: - name: ifrcgo-risk-module-pg - key: postgres-password - - name: POSTGRES_DATABASE - value: "risk-module" - # LDAP - - name: POSTGRESQL_ENABLE_LDAP - value: "no" - # TLS - - name: POSTGRESQL_ENABLE_TLS - value: "no" - # Audit - - name: POSTGRESQL_LOG_HOSTNAME - value: "false" - - name: POSTGRESQL_LOG_CONNECTIONS - value: "false" - - name: POSTGRESQL_LOG_DISCONNECTIONS - value: "false" - - name: POSTGRESQL_PGAUDIT_LOG_CATALOG - value: "off" - # Others - - name: POSTGRESQL_CLIENT_MIN_MESSAGES - value: "error" - - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES - value: "pgaudit" - - name: DUMP_REGISTRY_URL - value: https://user:password@secure-dump.com - - name: DUMP_FILENAME - value: /risk/k8s-alpha-init-db.dump - ports: - - name: tcp-postgresql - containerPort: 5432 - livenessProbe: - failureThreshold: 6 - initialDelaySeconds: 30 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 5 - exec: - command: - - /bin/sh - - -c - - exec pg_isready -U "risk-module" -d "dbname=risk-module" -h 127.0.0.1 -p 5432 - readinessProbe: - failureThreshold: 6 - initialDelaySeconds: 5 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 5 - exec: - command: - - /bin/sh - - -c - - -e - - | - exec pg_isready -U "risk-module" -d "dbname=risk-module" -h 127.0.0.1 -p 5432 - resources: - limits: - cpu: "4" - requests: - cpu: "0.2" - memory: 1Gi + + - name: BANJO_CELERY_HEARTBEAT_FILE + value: "/var/run/celery/worker_heartbeat" volumeMounts: - - name: empty-dir - mountPath: /tmp - subPath: tmp-dir - - name: empty-dir - mountPath: /opt/bitnami/postgresql/conf - subPath: app-conf-dir - - name: empty-dir - mountPath: /opt/bitnami/postgresql/tmp - subPath: app-tmp-dir - - name: custom-init-scripts - mountPath: /docker-entrypoint-initdb.d/ - - name: dshm - mountPath: /dev/shm - - name: data - mountPath: /bitnami/postgresql - volumes: - - name: empty-dir - emptyDir: {} - - name: custom-init-scripts - configMap: - name: ifrcgo-risk-module-pg-init-scripts - - name: dshm - emptyDir: - medium: Memory - volumeClaimTemplates: - - apiVersion: v1 - kind: PersistentVolumeClaim - metadata: - name: data - spec: - accessModes: - - "ReadWriteOnce" - resources: - requests: - storage: "2Gi" - storageClassName: local-path + - mountPath: /var/run/celery + name: celery-heartbeat --- -# Source: ifrcgo-risk-module-helm/charts/app/templates/argo-hooks/hook-job.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml # Hook: collect-static apiVersion: batch/v1 kind: Job metadata: name: risk-1-collect-static annotations: - argocd.argoproj.io/hook: PostSync + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation + argocd.argoproj.io/sync-wave: "20" + labels: + app: risk-1 + component: hook + hookName: collect-static + environment: ALPHA-1 + release: release-name spec: template: metadata: annotations: - checksum/secret: a5d73225a01c3e24b1530c338a0a44cf07ab77dc8646dc0562caada4bbc1b61f - checksum/configmap: f00be2c6cf51cc19c80358f167f85af68caa5a8c2405a310fc0fab05dfb0a0c8 + checksum/secret: 79d2dd5323b3376b79ce3f74dfc7c574a19e883e445eda033103627ff729c9a5 + checksum/configmap: 6882edae1c39e4b22afd8da880c9b404913ad4b049d51eaa3c03a464e0d89f93 labels: app: risk-1 - component: argo-hooks + component: hook + hookName: collect-static spec: restartPolicy: "Never" containers: @@ -977,10 +371,9 @@ spec: image: "local.git.com/ifrcgo/risk:develop.xxxxxxxx" imagePullPolicy: IfNotPresent command: - - /bin/bash - - -c - args: - - ./manage.py wait_for_resources --minio && ./manage.py collectstatic --noinput + - ./manage.py + - collectstatic + - --noinput resources: limits: cpu: "4" @@ -996,71 +389,46 @@ spec: env: - name: DJANGO_APP_TYPE value: "hook" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 --- -# Source: ifrcgo-risk-module-helm/charts/app/templates/argo-hooks/hook-job.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml # Hook: create-users apiVersion: batch/v1 kind: Job metadata: name: risk-1-create-users annotations: - argocd.argoproj.io/hook: PostSync + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/sync-wave: "25" + labels: + app: risk-1 + component: hook + hookName: create-users + environment: ALPHA-1 + release: release-name spec: template: metadata: annotations: - checksum/secret: a5d73225a01c3e24b1530c338a0a44cf07ab77dc8646dc0562caada4bbc1b61f - checksum/configmap: f00be2c6cf51cc19c80358f167f85af68caa5a8c2405a310fc0fab05dfb0a0c8 + checksum/secret: 79d2dd5323b3376b79ce3f74dfc7c574a19e883e445eda033103627ff729c9a5 + checksum/configmap: 6882edae1c39e4b22afd8da880c9b404913ad4b049d51eaa3c03a464e0d89f93 labels: app: risk-1 - component: argo-hooks + component: hook + hookName: create-users spec: restartPolicy: "Never" + volumes: + - configMap: + name: risk-1-create-users-files + name: hook-files containers: - name: create-users image: "local.git.com/ifrcgo/risk:develop.xxxxxxxx" imagePullPolicy: IfNotPresent command: - - ./manage.py - - shell + - sh - -c - - | - import os; - from django.contrib.auth import get_user_model; - from django.db import transaction; - - User = get_user_model() - - user_credentials = [ - user_credential.split(":") - for user_credential in os.environ["ADMIN_USER_EMAIL_PASSWORD_SET"].split(",") - ]; - - print("Parsed provided credentials:", user_credentials); - - @transaction.atomic - def create_initial_users(user_credentials): - for first_name, last_name, username, email, password_hash in user_credentials: - user, created = User.objects.update_or_create( - email=email, - username=username, - defaults={ - "first_name": first_name, - "last_name": last_name, - "password": password_hash, - "is_superuser": True, - "is_staff": True, - }, - ) - action = "updated" - if created: - action = "created" - print(f"Successfully {action} user({email=})"); - create_initial_users(user_credentials); + - ./manage.py shell < /etc/hook-files/create_users.py resources: limits: cpu: "4" @@ -1076,30 +444,41 @@ spec: env: - name: DJANGO_APP_TYPE value: "hook" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + - name: ADMIN_USER_EMAIL_PASSWORD_SET value: "IFRC:Admin:admin@ifrc.org:password_hash" + volumeMounts: + - mountPath: /etc/hook-files + name: hook-files + readOnly: true --- -# Source: ifrcgo-risk-module-helm/charts/app/templates/argo-hooks/hook-job.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml # Hook: db-migrate apiVersion: batch/v1 kind: Job metadata: generateName: risk-1-db-migrate- annotations: - argocd.argoproj.io/hook: PostSync + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/sync-wave: "20" + labels: + app: risk-1 + component: hook + hookName: db-migrate + environment: ALPHA-1 + release: release-name spec: + activeDeadlineSeconds: 3600 + ttlSecondsAfterFinished: 604800 template: metadata: annotations: - checksum/secret: a5d73225a01c3e24b1530c338a0a44cf07ab77dc8646dc0562caada4bbc1b61f - checksum/configmap: f00be2c6cf51cc19c80358f167f85af68caa5a8c2405a310fc0fab05dfb0a0c8 + checksum/secret: 79d2dd5323b3376b79ce3f74dfc7c574a19e883e445eda033103627ff729c9a5 + checksum/configmap: 6882edae1c39e4b22afd8da880c9b404913ad4b049d51eaa3c03a464e0d89f93 labels: app: risk-1 - component: argo-hooks + component: hook + hookName: db-migrate spec: restartPolicy: "Never" containers: @@ -1107,10 +486,8 @@ spec: image: "local.git.com/ifrcgo/risk:develop.xxxxxxxx" imagePullPolicy: IfNotPresent command: - - /bin/bash - - -c - args: - - ./manage.py wait_for_resources --db && ./manage.py migrate + - ./manage.py + - migrate resources: limits: cpu: "4" @@ -1126,39 +503,61 @@ spec: env: - name: DJANGO_APP_TYPE value: "hook" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 - --- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/api-ingress.yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml +# Hook: wait-for-resources +apiVersion: batch/v1 +kind: Job metadata: - name: fircgo-risk-module-minio-api - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 + name: risk-1-wait-for-resources annotations: - nginx.ingress.kubernetes.io/proxy-body-size: 500m + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded + argocd.argoproj.io/sync-wave: "10" + labels: + app: risk-1 + component: hook + hookName: wait-for-resources + environment: ALPHA-1 + release: release-name spec: - ingressClassName: "nginx" - rules: - - host: risk-minio-1.ifrcgo.local.example.com - http: - paths: - - path: / - pathType: ImplementationSpecific - backend: - service: - name: fircgo-risk-module-minio - port: - name: minio-api + template: + metadata: + annotations: + checksum/secret: 79d2dd5323b3376b79ce3f74dfc7c574a19e883e445eda033103627ff729c9a5 + checksum/configmap: 6882edae1c39e4b22afd8da880c9b404913ad4b049d51eaa3c03a464e0d89f93 + labels: + app: risk-1 + component: hook + hookName: wait-for-resources + spec: + restartPolicy: "Never" + containers: + - name: wait-for-resources + image: "local.git.com/ifrcgo/risk:develop.xxxxxxxx" + imagePullPolicy: IfNotPresent + command: + - ./manage.py + - wait_for_resources + - --db + - --cache + - --minio + resources: + limits: + cpu: "4" + memory: 1Gi + requests: + cpu: "0.1" + memory: 1Gi + envFrom: + - secretRef: + name: risk-1-secret + - configMapRef: + name: risk-1-env-name + env: + - name: DJANGO_APP_TYPE + value: "hook" + --- # Source: ifrcgo-risk-module-helm/charts/app/templates/api/ingress.yaml @@ -1171,8 +570,9 @@ metadata: component: api environment: ALPHA-1 release: release-name + annotations: + argocd.argoproj.io/sync-wave: "30" spec: - ingressClassName: "nginx" rules: - host: "risk-1.ifrcgo.local.example.com" http: @@ -1214,219 +614,3 @@ spec: requests: storage: 1Gi storageClassName: local-path ---- -# Source: ifrcgo-risk-module-helm/charts/app/charts/minio/templates/provisioning-job.yaml -apiVersion: batch/v1 -kind: Job -metadata: - name: fircgo-risk-module-minio-provisioning - namespace: "default" - labels: - app.kubernetes.io/instance: release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: minio - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 - app.kubernetes.io/component: minio-provisioning - annotations: - helm.sh/hook: post-install,post-upgrade - helm.sh/hook-delete-policy: before-hook-creation -spec: - ttlSecondsAfterFinished: 600 - parallelism: 1 - template: - metadata: - labels: - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: 2024.12.18 - helm.sh/chart: minio-14.10.5 - app.kubernetes.io/component: minio-provisioning - spec: - - restartPolicy: OnFailure - terminationGracePeriodSeconds: 0 - securityContext: - fsGroup: 1001 - fsGroupChangePolicy: Always - supplementalGroups: [] - sysctls: [] - serviceAccountName: fircgo-risk-module-minio - initContainers: - - name: wait-for-available-minio - image: docker.io/bitnami/os-shell:12-debian-12-r35 - imagePullPolicy: "IfNotPresent" - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1001 - runAsNonRoot: true - runAsUser: 1001 - seLinuxOptions: {} - seccompProfile: - type: RuntimeDefault - command: - - /bin/bash - - -c - - |- - set -e; - echo "Waiting for Minio"; - wait-for-port \ - --host=fircgo-risk-module-minio \ - --state=inuse \ - --timeout=120 \ - 9000; - echo "Minio is available"; - resources: - limits: - cpu: 150m - ephemeral-storage: 2Gi - memory: 192Mi - requests: - cpu: 100m - ephemeral-storage: 50Mi - memory: 128Mi - containers: - - name: minio - image: docker.io/bitnamilegacy/minio:2024.12.18-debian-12-r1 - imagePullPolicy: "IfNotPresent" - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1001 - runAsNonRoot: true - runAsUser: 1001 - seLinuxOptions: {} - seccompProfile: - type: RuntimeDefault - command: - - /bin/bash - - -c - - |- - set -e; - echo "Start Minio provisioning"; - - retry_while() { - local -r cmd="${1:?cmd is missing}" - local -r retries="${2:-12}" - local -r sleep_time="${3:-5}" - local return_value=1 - - read -r -a command <<< "$cmd" - for ((i = 1 ; i <= retries ; i+=1 )); do - "${command[@]}" && return_value=0 && break - sleep "$sleep_time" - done - return $return_value - } - - function attachPolicy() { - local tmp=$(mc admin $1 info provisioning $2 | sed -n -e 's/^Policy.*: \(.*\)$/\1/p'); - IFS=',' read -r -a CURRENT_POLICIES <<< "$tmp"; - if [[ ! "${CURRENT_POLICIES[*]}" =~ "$3" ]]; then - mc admin policy attach provisioning $3 --$1=$2; - fi; - }; - - function detachDanglingPolicies() { - local tmp=$(mc admin $1 info provisioning $2 | sed -n -e 's/^Policy.*: \(.*\)$/\1/p'); - IFS=',' read -r -a CURRENT_POLICIES <<< "$tmp"; - IFS=',' read -r -a DESIRED_POLICIES <<< "$3"; - for current in "${CURRENT_POLICIES[@]}"; do - if [[ ! "${DESIRED_POLICIES[*]}" =~ "${current}" ]]; then - mc admin policy detach provisioning $current --$1=$2; - fi; - done; - } - - function addUsersFromFile() { - local username=$(grep -oP '^username=\K.+' $1); - local password=$(grep -oP '^password=\K.+' $1); - local disabled=$(grep -oP '^disabled=\K.+' $1); - local policies_list=$(grep -oP '^policies=\K.+' $1); - local set_policies=$(grep -oP '^setPolicies=\K.+' $1); - - mc admin user add provisioning "${username}" "${password}"; - - IFS=',' read -r -a POLICIES <<< "${policies_list}"; - for policy in "${POLICIES[@]}"; do - attachPolicy user "${username}" "${policy}"; - done; - if [ "${set_policies}" == "true" ]; then - detachDanglingPolicies user "${username}" "${policies_list}"; - fi; - - local user_status="enable"; - if [[ "${disabled}" != "" && "${disabled,,}" == "true" ]]; then - user_status="disable"; - fi; - - mc admin user "${user_status}" provisioning "${username}"; - }; - mc alias set provisioning $MINIO_SCHEME://fircgo-risk-module-minio:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD; - - mc admin service restart provisioning --wait --json; - - # Adding a sleep to ensure that the check below does not cause - # a race condition. We check for the MinIO port because the - # "mc admin service restart --wait" command is not working as expected - sleep 5; - echo "Waiting for Minio to be available after restart"; - if ! retry_while "mc admin info provisioning"; then - echo "Error connecting to Minio" - exit 1 - fi - echo "Minio is available. Executing provisioning commands"; - - mc anonymous set download provisioning/risk-module-storage; - - echo "End Minio provisioning"; - env: - - name: MINIO_SCHEME - value: "http" - - name: MINIO_ROOT_USER - valueFrom: - secretKeyRef: - name: fircgo-risk-module-minio - key: root-user - - name: MINIO_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: fircgo-risk-module-minio - key: root-password - envFrom: - resources: - limits: - cpu: 150m - ephemeral-storage: 2Gi - memory: 192Mi - requests: - cpu: 100m - ephemeral-storage: 50Mi - memory: 128Mi - volumeMounts: - - name: empty-dir - mountPath: /.mc - subPath: app-mc-dir - - name: empty-dir - mountPath: /opt/bitnami/minio/tmp - subPath: app-tmp-dir - - name: empty-dir - mountPath: /tmp - subPath: tmp-dir - - name: minio-provisioning - mountPath: /etc/ilm - volumes: - - name: empty-dir - emptyDir: {} - - name: minio-provisioning - configMap: - name: fircgo-risk-module-minio-provisioning - diff --git a/helm/snapshots/staging.yaml b/helm/snapshots/staging.yaml index a894a6c..8a87ac1 100644 --- a/helm/snapshots/staging.yaml +++ b/helm/snapshots/staging.yaml @@ -30,6 +30,8 @@ data: AZURE_STORAGE_CONTAINER: "AZURE_STORAGE_CONTAINER-XYZ" AZURE_STORAGE_MANAGED_IDENTITY: "true" AZURE_TENANT_ID: "AZURE_TENANT_ID-XYZ" + CACHE_REDIS_URL: "redis://risk-module-dragonfly:6379/0" + CELERY_REDIS_URL: "redis://risk-module-dragonfly:6379/1" DJANGO_ALLOWED_HOSTS: "go-risk-api-stage.ifrc.org" DJANGO_DEBUG: "false" DJANGO_TIME_ZONE: "UTC" @@ -69,6 +71,7 @@ kind: Deployment metadata: name: risk-module-api annotations: + argocd.argoproj.io/sync-wave: "30" reloader.stakater.com/auto: "true" labels: app: risk-module @@ -76,7 +79,7 @@ metadata: environment: STAGING release: release-name spec: - replicas: 1 + revisionHistoryLimit: 1 selector: matchLabels: app: risk-module @@ -85,7 +88,7 @@ spec: metadata: annotations: checksum/secret: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b - checksum/configmap: 67eb0f9044e57138910d5ed8f6553af55e8963c555414715c09f4cdb7427f0da + checksum/configmap: 6bb12019ab73830b633f2ae29f18793c8c037930dd68e415694e62dfb9453d54 labels: app: risk-module component: api @@ -99,6 +102,7 @@ spec: volumeAttributes: secretProviderClass: risk-module-secret-provider serviceAccountName: service-token-reader + terminationGracePeriodSeconds: 38 containers: - name: api image: "SET-BY-CICD-IMAGE:SET-BY-CICD-TAG" @@ -112,7 +116,26 @@ spec: - name: http containerPort: 80 protocol: TCP - # TODO: livenessProbe + livenessProbe: + failureThreshold: 6 + httpGet: + path: /healthz/live/ + port: http + periodSeconds: 20 + timeoutSeconds: 2 + readinessProbe: + failureThreshold: 6 + httpGet: + path: /healthz/ready/ + port: http + periodSeconds: 10 + timeoutSeconds: 2 + startupProbe: + failureThreshold: 30 + httpGet: + path: /healthz/ready/ + port: http + periodSeconds: 5 resources: limits: cpu: "4" @@ -128,42 +151,50 @@ spec: env: - name: DJANGO_APP_TYPE value: "web" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + + lifecycle: + preStop: + sleep: + seconds: 8 volumeMounts: - name: risk-module-secret mountPath: /mnt/secrets-store readOnly: true --- -# Source: ifrcgo-risk-module-helm/charts/app/templates/worker-beat/deployment.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/worker-addons/deployment.yaml +# Addon: beat apiVersion: apps/v1 kind: Deployment metadata: name: risk-module-worker-beat annotations: + argocd.argoproj.io/sync-wave: "30" reloader.stakater.com/auto: "true" labels: app: risk-module - component: worker-beat + component: worker-addon + addon: beat environment: STAGING release: release-name spec: - replicas: 1 # This should only 1 + strategy: + type: Recreate + revisionHistoryLimit: 1 selector: matchLabels: app: risk-module - component: worker-beat + component: worker-addon + addon: beat template: metadata: annotations: checksum/secret: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b - checksum/configmap: 67eb0f9044e57138910d5ed8f6553af55e8963c555414715c09f4cdb7427f0da + checksum/configmap: 6bb12019ab73830b633f2ae29f18793c8c037930dd68e415694e62dfb9453d54 labels: app: risk-module - component: worker-beat + component: worker-addon + addon: beat azure.workload.identity/use: "true" spec: volumes: @@ -173,9 +204,13 @@ spec: readOnly: true volumeAttributes: secretProviderClass: risk-module-secret-provider + - emptyDir: + medium: Memory + sizeLimit: 1Mi + name: celery-heartbeat serviceAccountName: service-token-reader containers: - - name: worker-beat + - name: beat image: "SET-BY-CICD-IMAGE:SET-BY-CICD-TAG" imagePullPolicy: IfNotPresent command: @@ -185,7 +220,15 @@ spec: - --max-interval=3600 - --loglevel=info - --scheduler=django_celery_beat.schedulers:DatabaseScheduler - # TODO: livenessProbe + livenessProbe: + exec: + command: + - banjo-celery-probe + - --max-age + - "360" + failureThreshold: 3 + periodSeconds: 60 + timeoutSeconds: 5 resources: limits: cpu: "1" @@ -201,14 +244,15 @@ spec: env: - name: DJANGO_APP_TYPE value: "worker" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + + - name: BANJO_CELERY_HEARTBEAT_FILE + value: "/var/run/celery/beat_heartbeat" volumeMounts: - name: risk-module-secret mountPath: /mnt/secrets-store readOnly: true + - mountPath: /var/run/celery + name: celery-heartbeat --- # Source: ifrcgo-risk-module-helm/charts/app/templates/worker/deployment.yaml @@ -218,6 +262,7 @@ kind: Deployment metadata: name: risk-module-worker-default annotations: + argocd.argoproj.io/sync-wave: "30" reloader.stakater.com/auto: "true" labels: app: risk-module @@ -226,7 +271,7 @@ metadata: environment: STAGING release: release-name spec: - replicas: 1 + revisionHistoryLimit: 1 selector: matchLabels: app: risk-module @@ -236,7 +281,7 @@ spec: metadata: annotations: checksum/secret: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b - checksum/configmap: 67eb0f9044e57138910d5ed8f6553af55e8963c555414715c09f4cdb7427f0da + checksum/configmap: 6bb12019ab73830b633f2ae29f18793c8c037930dd68e415694e62dfb9453d54 labels: app: risk-module component: worker @@ -250,6 +295,10 @@ spec: readOnly: true volumeAttributes: secretProviderClass: risk-module-secret-provider + - emptyDir: + medium: Memory + sizeLimit: 1Mi + name: celery-heartbeat serviceAccountName: service-token-reader containers: - name: worker @@ -262,7 +311,19 @@ spec: - --max-tasks-per-child=20 image: "SET-BY-CICD-IMAGE:SET-BY-CICD-TAG" imagePullPolicy: IfNotPresent - # TODO: livenessProbe + livenessProbe: + exec: + command: + - banjo-celery-probe + failureThreshold: 6 + periodSeconds: 20 + timeoutSeconds: 5 + startupProbe: + exec: + command: + - banjo-celery-probe + failureThreshold: 30 + periodSeconds: 5 resources: limits: cpu: "4" @@ -278,33 +339,43 @@ spec: env: - name: DJANGO_APP_TYPE value: "worker" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + + - name: BANJO_CELERY_HEARTBEAT_FILE + value: "/var/run/celery/worker_heartbeat" volumeMounts: - name: risk-module-secret mountPath: /mnt/secrets-store readOnly: true + - mountPath: /var/run/celery + name: celery-heartbeat --- -# Source: ifrcgo-risk-module-helm/charts/app/templates/argo-hooks/hook-job.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml # Hook: collect-static apiVersion: batch/v1 kind: Job metadata: name: risk-module-collect-static annotations: - argocd.argoproj.io/hook: PostSync + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation + argocd.argoproj.io/sync-wave: "20" + labels: + app: risk-module + component: hook + hookName: collect-static + environment: STAGING + release: release-name spec: template: metadata: annotations: checksum/secret: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b - checksum/configmap: 67eb0f9044e57138910d5ed8f6553af55e8963c555414715c09f4cdb7427f0da + checksum/configmap: 6bb12019ab73830b633f2ae29f18793c8c037930dd68e415694e62dfb9453d54 labels: app: risk-module - component: argo-hooks + component: hook + hookName: collect-static azure.workload.identity/use: "true" spec: restartPolicy: "Never" @@ -339,32 +410,39 @@ spec: env: - name: DJANGO_APP_TYPE value: "hook" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + volumeMounts: - name: risk-module-secret mountPath: /mnt/secrets-store readOnly: true --- -# Source: ifrcgo-risk-module-helm/charts/app/templates/argo-hooks/hook-job.yaml +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml # Hook: db-migrate apiVersion: batch/v1 kind: Job metadata: generateName: risk-module-db-migrate- annotations: - argocd.argoproj.io/hook: PostSync + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/sync-wave: "20" + labels: + app: risk-module + component: hook + hookName: db-migrate + environment: STAGING + release: release-name spec: + activeDeadlineSeconds: 3600 + ttlSecondsAfterFinished: 604800 template: metadata: annotations: checksum/secret: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b - checksum/configmap: 67eb0f9044e57138910d5ed8f6553af55e8963c555414715c09f4cdb7427f0da + checksum/configmap: 6bb12019ab73830b633f2ae29f18793c8c037930dd68e415694e62dfb9453d54 labels: app: risk-module - component: argo-hooks + component: hook + hookName: db-migrate azure.workload.identity/use: "true" spec: restartPolicy: "Never" @@ -381,10 +459,8 @@ spec: image: "SET-BY-CICD-IMAGE:SET-BY-CICD-TAG" imagePullPolicy: IfNotPresent command: - - /bin/bash - - -c - args: - - ./manage.py wait_for_resources --db && ./manage.py migrate + - ./manage.py + - migrate resources: limits: cpu: "4" @@ -400,10 +476,74 @@ spec: env: - name: DJANGO_APP_TYPE value: "hook" - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + + volumeMounts: + - name: risk-module-secret + mountPath: /mnt/secrets-store + readOnly: true +--- +# Source: ifrcgo-risk-module-helm/charts/app/templates/hooks/job.yaml +# Hook: wait-for-resources +apiVersion: batch/v1 +kind: Job +metadata: + name: risk-module-wait-for-resources + annotations: + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded + argocd.argoproj.io/sync-wave: "10" + labels: + app: risk-module + component: hook + hookName: wait-for-resources + environment: STAGING + release: release-name +spec: + template: + metadata: + annotations: + checksum/secret: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b + checksum/configmap: 6bb12019ab73830b633f2ae29f18793c8c037930dd68e415694e62dfb9453d54 + labels: + app: risk-module + component: hook + hookName: wait-for-resources + azure.workload.identity/use: "true" + spec: + restartPolicy: "Never" + volumes: + - name: risk-module-secret + csi: + driver: "secrets-store.csi.k8s.io" + readOnly: true + volumeAttributes: + secretProviderClass: risk-module-secret-provider + serviceAccountName: service-token-reader + containers: + - name: wait-for-resources + image: "SET-BY-CICD-IMAGE:SET-BY-CICD-TAG" + imagePullPolicy: IfNotPresent + command: + - ./manage.py + - wait_for_resources + - --db + - --cache + resources: + limits: + cpu: "4" + memory: 1Gi + requests: + cpu: "0.1" + memory: 1Gi + envFrom: + - secretRef: + name: risk-module-secret + - configMapRef: + name: risk-module-env-name + env: + - name: DJANGO_APP_TYPE + value: "hook" + volumeMounts: - name: risk-module-secret mountPath: /mnt/secrets-store @@ -420,6 +560,8 @@ metadata: component: api environment: STAGING release: release-name + annotations: + argocd.argoproj.io/sync-wave: "30" spec: ingressClassName: "nginx" rules: diff --git a/helm/tests/alpha-1.yaml b/helm/tests/alpha-1.yaml index 295b74c..725ccc6 100644 --- a/helm/tests/alpha-1.yaml +++ b/helm/tests/alpha-1.yaml @@ -6,36 +6,9 @@ app: name: local.git.com/ifrcgo/risk tag: develop.xxxxxxxx - ingress: - host: risk-1.ifrcgo.local.example.com - - postgresql: - auth: - postgresPassword: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - password: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - primary: - nodeSelector: - kubernetes.io/hostname: "desktop-01" - persistence: - storageClass: "local-path" - size: 2Gi - extraEnvVars: - - name: DUMP_REGISTRY_URL - value: https://user:password@secure-dump.com - - name: DUMP_FILENAME - value: /risk/k8s-alpha-init-db.dump - - minio: - nodeSelector: - kubernetes.io/hostname: "desktop-01" - global: - defaultStorageClass: "local-path" - apiIngress: - hostname: risk-minio-1.ifrcgo.local.example.com - auth: - rootPassword: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - persistence: - size: 0.5Gi + api: + ingress: + host: risk-1.ifrcgo.local.example.com env: # App Domain @@ -54,14 +27,13 @@ app: METEOSWISS_S3_ACCESS_KEY: "dummy-access-key" METEOSWISS_S3_SECRET_KEY: "dummy-secret-key" - argoHook: + hooks: enabled: true - hooks: + jobs: # NOTE: Make sure keys are lowercase create-users: enabled: true - hook: PostSync - env: + extraEnv: ADMIN_USER_EMAIL_PASSWORD_SET: "IFRC:Admin:admin@ifrc.org:password_hash" extraManifests: diff --git a/helm/tests/staging.yaml b/helm/tests/staging.yaml index fbfafbc..b317ce4 100644 --- a/helm/tests/staging.yaml +++ b/helm/tests/staging.yaml @@ -1,8 +1,9 @@ app: - ingress: - host: "go-risk-api-stage.ifrc.org" - tls: - secretName: "risk-helm-secret-cert" + api: + ingress: + host: "go-risk-api-stage.ifrc.org" + tls: + secretName: "risk-helm-secret-cert" env: RISK_API_FQDN: "https://go-risk-api-stage.ifrc.org" DJANGO_ALLOWED_HOSTS: "go-risk-api-stage.ifrc.org" diff --git a/helm/values.yaml b/helm/values.yaml index d9fd6e3..e75fe3e 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -10,50 +10,18 @@ app: tag: SET-BY-CICD-TAG imagePullPolicy: IfNotPresent - ingress: - enabled: true - className: - host: - - rabbitmq: - enabled: false - - # XXX: This is for local/playground usages only. - postgresql: - enabled: false - fullnameOverride: ifrcgo-risk-module-pg - architecture: standalone - auth: - postgresPassword: "" - username: "" - password: "" - database: "" - primary: - persistence: - enabled: true - storageClass: "" - size: 8Gi - - redis: - enabled: false - architecture: standalone - fullnameOverride: ifrcgo-risk-module-redis - auth: - enabled: false - master: - persistence: - enabled: true - storageClass: "" - size: .5Gi - - minio: - enabled: false - api: enabled: true - replicaCount: 1 containerPort: 80 command: ["gunicorn", "risk_module.wsgi:application", "--bind", "0.0.0.0:80"] + ingress: + enabled: true + className: + host: + probes: + # banjo-utils HealthProbeMiddleware serves /healthz/live/ and /healthz/ready/. + # Chart defaults (liveness /healthz/live/, readiness+startup /healthz/ready/) apply. + enabled: true resources: requests: cpu: "0.1" @@ -64,69 +32,98 @@ app: worker: enabled: true - flower: - enabled: false - beat: - command: + queueDefaults: + commandPrefix: - celery - --app=risk_module - - beat - - --max-interval=3600 - - --loglevel=info - - --scheduler=django_celery_beat.schedulers:DatabaseScheduler + - worker + - --loglevel=INFO + # banjo-celery-probe heartbeat wiring (writer via setup_worker_heartbeat, + # reader via banjo-celery-probe) on a RAM emptyDir. + extraEnv: + BANJO_CELERY_HEARTBEAT_FILE: /var/run/celery/worker_heartbeat + volumes: + celery-heartbeat: {emptyDir: {medium: Memory, sizeLimit: 1Mi}} + volumeMounts: + celery-heartbeat: {mountPath: /var/run/celery} + probes: + enabled: true + liveness: + exec: {command: [banjo-celery-probe]} + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 6 + startup: + exec: {command: [banjo-celery-probe]} + periodSeconds: 5 + failureThreshold: 30 resources: requests: cpu: "0.1" - memory: 0.2Gi + memory: 1Gi limits: - cpu: "1" - memory: 0.5Gi - queueDefaultResources: - requests: - cpu: "0.1" - memory: 1Gi - limits: - cpu: "4" - memory: 2Gi - queueCommandPrefix: - - celery - - --app=risk_module - - worker - - --loglevel=INFO + cpu: "4" + memory: 2Gi queues: # NOTE: Make sure keys are lowercase default: enabled: true - replicaCount: 1 celeryArgs: ["--queues=celery", "--max-tasks-per-child=20"] resources: requests: memory: 1Gi limits: memory: 2Gi + addons: + beat: + enabled: true + command: + - celery + - --app=risk_module + - beat + - --max-interval=3600 + - --loglevel=info + - --scheduler=django_celery_beat.schedulers:DatabaseScheduler + strategy: {type: Recreate} + extraEnv: + BANJO_CELERY_HEARTBEAT_FILE: /var/run/celery/beat_heartbeat + volumes: + celery-heartbeat: {emptyDir: {medium: Memory, sizeLimit: 1Mi}} + volumeMounts: + celery-heartbeat: {mountPath: /var/run/celery} + probes: + enabled: true + liveness: + # --max-age must exceed beat's persistent-scheduler tick (300s) + exec: {command: [banjo-celery-probe, --max-age, "360"]} + periodSeconds: 60 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + requests: + cpu: "0.1" + memory: 0.2Gi + limits: + cpu: "1" + memory: 0.5Gi - argoHook: + hooks: enabled: true - resources: - requests: - cpu: "0.1" - memory: 1Gi - limits: - cpu: "4" - memory: 1Gi - hooks: - # NOTE: Make sure keys are lowercase - db-migrate: - enabled: true - hook: PostSync - preserveHistory: true - command: ["/bin/bash", "-c"] - args: - - "./manage.py wait_for_resources --db && ./manage.py migrate" - collect-static: - enabled: true - hook: PostSync - command: ["./manage.py", "collectstatic", "--noinput"] + defaults: + resources: + requests: + cpu: "0.1" + memory: 1Gi + limits: + cpu: "4" + memory: 1Gi + jobs: + # NOTE: Make sure keys are lowercase. + # db-migrate and collect-static inherit the chart defaults verbatim + # (Sync hook @ wave 20, `manage.py migrate` / `collectstatic --noinput`). + # Only wait-for-resources overrides the default (bare) command. + wait-for-resources: + command: ["./manage.py", "wait_for_resources", "--db", "--cache"] env: # Application info diff --git a/helm/values/alpha.yaml b/helm/values/alpha.yaml index 951bafb..da9dac1 100644 --- a/helm/values/alpha.yaml +++ b/helm/values/alpha.yaml @@ -1,137 +1,44 @@ app: - ingress: - enabled: true - className: nginx - - postgresql: - enabled: true - auth: - username: "risk-module" - database: "risk-module" - primary: - resources: - requests: - cpu: "0.2" - memory: 1Gi - limits: - cpu: "4" - initdb: - scripts: - my_init_script.sh: | - #!/bin/sh -e - DATABASE_DUMP_URL="$DUMP_REGISTRY_URL$DUMP_FILENAME" - # Don't have curl here - /usr/lib/apt/apt-helper download-file -o "Acquire::https::Verify-Peer=false" "$DATABASE_DUMP_URL" /tmp/db.dump - du -sh /tmp/db.dump - PGPASSWORD="${POSTGRES_PASSWORD}" PGUSER=$POSTGRES_USER pg_restore -v --no-owner --no-privileges -d "$POSTGRES_DATABASE" < /tmp/db.dump - - minio: - enabled: true - disableWebUI: true - mode: standalone - fullnameOverride: fircgo-risk-module-minio - apiIngress: - enabled: true - ingressClassName: nginx - annotations: - nginx.ingress.kubernetes.io/proxy-body-size: "500m" - auth: - forceNewKeys: True - rootUser: risk-module - rootPassword: - resources: - requests: - cpu: "0.1" - memory: 128Mi - limits: - cpu: "4" - memory: 256Mi - persistence: - enabled: true - size: 500Mi - defaultBuckets: risk-module-storage - provisioning: - enabled: true - resourcesPreset: "nano" - cleanupAfterFinished: - enabled: true - extraCommands: - - "mc anonymous set download provisioning/risk-module-storage" - - api: - resources: - requests: - cpu: "0.1" - memory: "2Gi" - limits: - cpu: "4" - memory: "2Gi" - - worker: - enabled: true - beat: - resources: - limits: - cpu: "4" - queueDefaultResources: - requests: - cpu: "0.1" - memory: 1Gi - limits: - cpu: "4" - memory: 2Gi + ingress: + enabled: true env: # Sentry SENTRY_TRACE_SAMPLE_RATE: "0.8" SENTRY_PROFILE_SAMPLE_RATE: "0.8" - secrets: - # Database - DATABASE_NAME: "{{ $.Values.postgresql.auth.database }}" - DATABASE_USER: "postgres" - DATABASE_PASSWORD: "{{ $.Values.postgresql.auth.postgresPassword }}" - DATABASE_HOST: "{{ include \"postgresql.v1.primary.fullname\" $.Subcharts.postgresql }}" - DATABASE_PORT: "{{ include \"postgresql.v1.service.port\" $.Subcharts.postgresql }}" - # Minio - USE_S3_BUCKET: "true" - AWS_S3_ENDPOINT_URL: "https://{{ .Values.minio.apiIngress.hostname }}/" - AWS_S3_ACCESS_KEY_ID: "{{ required \".Values.minio.auth.rootUser\" .Values.minio.auth.rootUser }}" - AWS_S3_SECRET_ACCESS_KEY: "{{ required \".Values.minio.auth.rootPassword\" .Values.minio.auth.rootPassword }}" - AWS_S3_BUCKET_NAME: "risk-module-storage" - AWS_S3_REGION: "us-east-1" + # NOTE: DB/S3/cache connection secrets are supplied by the user + # (e.g. secretFromEnv / extraSecretsName) alongside the external deps. - argoHook: - enabled: true - hooks: + hooks: + jobs: # NOTE: Make sure keys are lowercase - collect-static: - command: ["/bin/bash", "-c"] - args: - - "./manage.py wait_for_resources --minio && ./manage.py collectstatic --noinput" + wait-for-resources: + command: ["./manage.py", "wait_for_resources", "--db", "--cache", "--minio"] create-users: enabled: true - # env: + annotations: + argocd.argoproj.io/hook: "Sync" + argocd.argoproj.io/sync-wave: "25" + # extraEnv: # ADMIN_USER_EMAIL_PASSWORD_SET: "IFRC:Admin:ifrc-admin:admin@ifrc.org:password_hash" - hook: PostSync - command: - - ./manage.py - - shell - - -c - - | - import os; - from django.contrib.auth import get_user_model; - from django.db import transaction; + # Script mounted as a file (per-job ConfigMap) and piped into the shell. + command: ["sh", "-c", "./manage.py shell < /etc/hook-files/create_users.py"] + files: + create_users.py: | + import os + from django.contrib.auth import get_user_model + from django.db import transaction User = get_user_model() user_credentials = [ - user_credential.split(":") - for user_credential in os.environ["ADMIN_USER_EMAIL_PASSWORD_SET"].split(",") - ]; + user_credential.split(":") + for user_credential in os.environ["ADMIN_USER_EMAIL_PASSWORD_SET"].split(",") + ] - print("Parsed provided credentials:", user_credentials); + print("Parsed provided credentials:", user_credentials) @transaction.atomic def create_initial_users(user_credentials): @@ -140,15 +47,14 @@ app: email=email, username=username, defaults={ - "first_name": first_name, - "last_name": last_name, - "password": password_hash, - "is_superuser": True, - "is_staff": True, + "first_name": first_name, + "last_name": last_name, + "password": password_hash, + "is_superuser": True, + "is_staff": True, }, ) - action = "updated" - if created: - action = "created" - print(f"Successfully {action} user({email=})"); - create_initial_users(user_credentials); + action = "created" if created else "updated" + print(f"Successfully {action} user({email=})") + + create_initial_users(user_credentials) diff --git a/helm/values/go-deploy.yaml b/helm/values/go-deploy.yaml index 53be93b..4e992a0 100644 --- a/helm/values/go-deploy.yaml +++ b/helm/values/go-deploy.yaml @@ -4,11 +4,12 @@ app: podLabels: azure.workload.identity/use: 'true' - ingress: - enabled: true - className: nginx - tls: + api: + ingress: enabled: true + className: nginx + tls: + enabled: true env: DJANGO_DEBUG: "false" diff --git a/helm/values/operators.yaml b/helm/values/operators.yaml index 814782c..70391f4 100644 --- a/helm/values/operators.yaml +++ b/helm/values/operators.yaml @@ -1,10 +1,8 @@ app: - extraEnvVars: + env: # Checkout the resources defined by extraManifests - - name: CACHE_REDIS_URL - value: redis://risk-module-dragonfly:6379/0 - - name: CELERY_REDIS_URL - value: redis://risk-module-dragonfly:6379/1 + CACHE_REDIS_URL: redis://risk-module-dragonfly:6379/0 + CELERY_REDIS_URL: redis://risk-module-dragonfly:6379/1 extraManifests: dragonflydb-cluster: From cd98f1a3fd6efb77d4aaac74e9317a82e8e9197e Mon Sep 17 00:00:00 2001 From: thenav56 Date: Tue, 7 Jul 2026 11:50:14 +0545 Subject: [PATCH 3/5] feat(helm): remove required checks from secrets --- helm/values.yaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/helm/values.yaml b/helm/values.yaml index e75fe3e..c771724 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -141,16 +141,16 @@ app: # Logging APPS_LOGGING_LEVEL: "WARNING" - secrets: - DJANGO_SECRET_KEY: "{{ required \".app.secrets.DJANGO_SECRET_KEY\" nil }}" - # Sentry - SENTRY_DSN: "{{ required \".app.secrets.SENTRY_DSN\" nil }}" - # PDC - PDC_USERNAME: "{{ required \".app.secrets.PDC_USERNAME\" nil }}" - PDC_PASSWORD: "{{ required \".app.secrets.PDC_PASSWORD\" nil }}" - PDC_ACCESS_TOKEN: "{{ required \".app.secrets.PDC_ACCESS_TOKEN\" nil }}" - # Meteoswiss - METEOSWISS_S3_ENDPOINT_URL: "{{ required \".app.secrets.METEOSWISS_S3_ENDPOINT_URL\" nil }}" - METEOSWISS_S3_BUCKET: "{{ required \".app.secrets.METEOSWISS_S3_BUCKET\" nil }}" - METEOSWISS_S3_ACCESS_KEY: "{{ required \".app.secrets.METEOSWISS_S3_ACCESS_KEY\" nil }}" - METEOSWISS_S3_SECRET_KEY: "{{ required \".app.secrets.METEOSWISS_S3_SECRET_KEY\" nil }}" + secrets: {} + # DJANGO_SECRET_KEY: + # # Sentry + # SENTRY_DSN: + # # PDC + # PDC_USERNAME: + # PDC_PASSWORD: + # PDC_ACCESS_TOKEN: + # # Meteoswiss + # METEOSWISS_S3_ENDPOINT_URL: + # METEOSWISS_S3_BUCKET: + # METEOSWISS_S3_ACCESS_KEY: + # METEOSWISS_S3_SECRET_KEY: From 35a50f25818e65f94c77e2245290e1c8a37088e4 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Tue, 7 Jul 2026 13:16:35 +0545 Subject: [PATCH 4/5] fix(helm): wire beat heartbeat via HeartbeatDatabaseScheduler Stock django_celery_beat DatabaseScheduler never touches the beat heartbeat file, so BANJO_CELERY_HEARTBEAT_FILE had no writer and the banjo-celery-probe liveness check always saw a missing/stale file. setup_worker_heartbeat only wires the worker bootstep, not beat. Switch beat's --scheduler to the heartbeat-aware subclass and regenerate snapshots. --- helm/snapshots/alpha-1.yaml | 2 +- helm/snapshots/staging.yaml | 2 +- helm/values.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/helm/snapshots/alpha-1.yaml b/helm/snapshots/alpha-1.yaml index a0b386a..e006a3c 100644 --- a/helm/snapshots/alpha-1.yaml +++ b/helm/snapshots/alpha-1.yaml @@ -221,7 +221,7 @@ spec: - beat - --max-interval=3600 - --loglevel=info - - --scheduler=django_celery_beat.schedulers:DatabaseScheduler + - --scheduler=banjo_utils.celery_health.database.HeartbeatDatabaseScheduler livenessProbe: exec: command: diff --git a/helm/snapshots/staging.yaml b/helm/snapshots/staging.yaml index 8a87ac1..00452bc 100644 --- a/helm/snapshots/staging.yaml +++ b/helm/snapshots/staging.yaml @@ -219,7 +219,7 @@ spec: - beat - --max-interval=3600 - --loglevel=info - - --scheduler=django_celery_beat.schedulers:DatabaseScheduler + - --scheduler=banjo_utils.celery_health.database.HeartbeatDatabaseScheduler livenessProbe: exec: command: diff --git a/helm/values.yaml b/helm/values.yaml index c771724..7f452a9 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -83,7 +83,7 @@ app: - beat - --max-interval=3600 - --loglevel=info - - --scheduler=django_celery_beat.schedulers:DatabaseScheduler + - --scheduler=banjo_utils.celery_health.database.HeartbeatDatabaseScheduler strategy: {type: Recreate} extraEnv: BANJO_CELERY_HEARTBEAT_FILE: /var/run/celery/beat_heartbeat From e5fc5d782cf9b471f3bbd08f274bd7c1d8e766b5 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Tue, 7 Jul 2026 13:41:46 +0545 Subject: [PATCH 5/5] fix(helm): include hook-delete-policy for custom alpha hook --- helm/snapshots/alpha-1.yaml | 1 + helm/values/alpha.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/helm/snapshots/alpha-1.yaml b/helm/snapshots/alpha-1.yaml index e006a3c..72cb78f 100644 --- a/helm/snapshots/alpha-1.yaml +++ b/helm/snapshots/alpha-1.yaml @@ -398,6 +398,7 @@ metadata: name: risk-1-create-users annotations: argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation argocd.argoproj.io/sync-wave: "25" labels: app: risk-1 diff --git a/helm/values/alpha.yaml b/helm/values/alpha.yaml index da9dac1..50d5682 100644 --- a/helm/values/alpha.yaml +++ b/helm/values/alpha.yaml @@ -21,6 +21,7 @@ app: annotations: argocd.argoproj.io/hook: "Sync" argocd.argoproj.io/sync-wave: "25" + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation # extraEnv: # ADMIN_USER_EMAIL_PASSWORD_SET: "IFRC:Admin:ifrc-admin:admin@ifrc.org:password_hash" # Script mounted as a file (per-job ConfigMap) and piped into the shell.