From d03588e3ffdde00774b1b2b7fdff97a5c71f6edd Mon Sep 17 00:00:00 2001 From: Mattia Giupponi Date: Mon, 18 May 2026 19:20:30 +0200 Subject: [PATCH 1/6] Improvments on docker-compose yaml file --- docker-compose.yml | 25 ++++- src/tasks.py | 270 ++++++--------------------------------------- 2 files changed, 56 insertions(+), 239 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6a0775e4..7e80516c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ x-common-django: env_file: - .env volumes: - # - './src:/usr/src/project' # Uncomment for local development + - './src:/usr/src/project' # Uncomment for local development - statics:/mnt/volumes/statics - geoserver-data-dir:/geoserver_data/data - backup-restore:/backup_restore @@ -15,6 +15,8 @@ x-common-django: depends_on: db: condition: service_healthy + nginx: + condition: service_healthy services: @@ -47,9 +49,15 @@ services: - IS_CELERY=True entrypoint: ["/usr/src/project/entrypoint.sh"] command: "celery-cmd" + healthcheck: + test: "celery -b ${BROKER_URL} inspect ping" + start_period: 30s + interval: 30s + timeout: 10s + retries: 2 # Nginx is serving django static and media files and proxies to django and geonode - geonode: + nginx: image: ${COMPOSE_PROJECT_NAME}/nginx:${NGINX_BASE_IMAGE_VERSION} build: context: ./docker/nginx @@ -69,7 +77,12 @@ services: - nginx-certificates:/geonode-certificates - statics:/mnt/volumes/statics restart: unless-stopped - + healthcheck: + test: "if ps -o pid | grep -w 1 > /dev/null; then echo 'nginx running'; else exit 1;fi" + interval: 10s + timeout: 2s + retries: 5 + start_period: 10s # memcached service memcached: image: memcached:alpine @@ -174,6 +187,12 @@ services: volumes: - redisdata:/data restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "redis-cli ping | grep -q PONG || exit 1"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 5s volumes: statics: diff --git a/src/tasks.py b/src/tasks.py index 371302ec..043e89a2 100644 --- a/src/tasks.py +++ b/src/tasks.py @@ -24,7 +24,6 @@ import time import docker import socket -import ipaddress import logging import datetime from pathlib import Path @@ -37,17 +36,10 @@ logger = logging.getLogger(__name__) -@task -def waitfordbs(ctx): - print("**************************databases*******************************") - db_host = os.getenv("DATABASE_HOST", "db") - ctx.run(f"/usr/bin/wait-for-databases {db_host}", pty=True) - - @task def update(ctx): print("***************************setting env*********************************") - ctx.run("env", pty=True) + ctx.run("echo 'getting hostname'", pty=True) pub_host = _geonode_public_host() print(f"Public Hostname or IP is {pub_host}") pub_port = _geonode_public_port() @@ -60,8 +52,9 @@ def update(ctx): geonode_docker_host = None for _cnt in range(1, 29): try: - geonode_docker_host = str(socket.gethostbyname("geonode")) + geonode_docker_host = str(socket.gethostbyname("nginx")) except Exception: + ctx.run(f"echo '...waiting for NGINX to pop-up...{_cnt}'", pty=True) print(f"...waiting for NGINX to pop-up...{_cnt}") time.sleep(1) @@ -88,6 +81,7 @@ def update(ctx): "geodburl": os.environ.get("GEODATABASE_URL", geodb_url), "static_root": os.environ.get("STATIC_ROOT", "/mnt/volumes/statics/static/"), "media_root": os.environ.get("MEDIA_ROOT", "/mnt/volumes/statics/uploaded/"), + "asset_root": os.environ.get("ASSETS_ROOT", "/mnt/volumes/statics/assets/"), "geoip_path": os.environ.get("GEOIP_PATH", "/mnt/volumes/statics/geoip.db"), "geonode_geodb_passwd": os.environ.get( "GEONODE_GEODATABASE_PASSWORD", "geonode_data" @@ -122,172 +116,36 @@ def update(ctx): except ValueError: current_allowed = [] current_allowed.extend([str(pub_host), f"{pub_host}:{pub_port}"]) - allowed_hosts = [str(c) for c in current_allowed] + ['"geonode"', '"django"'] - - ctx.run( - "echo export DJANGO_SETTINGS_MODULE=\ -{local_settings} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEONODE_GEODATABASE_PASSWORD=\ -{geonode_geodb_passwd} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export DEFAULT_BACKEND_DATASTORE=\ -{default_backend_datastore} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEONODE_DATABASE_PASSWORD=\ -{geonode_db_passwd} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEONODE_GEODATABASE=\ -{geonode_geodb} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export DATABASE_URL=\ -{db_url} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEODATABASE_URL=\ -{geodb_url} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEONODE_DATABASE=\ -{geonode_db} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEOSERVER_LOCATION=\ -{gs_loc} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEOSERVER_WEB_UI_LOCATION=\ -{gs_web_ui_loc} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEOSERVER_PUBLIC_LOCATION=\ -{gs_pub_loc} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEOSERVER_ADMIN_PASSWORD=\ -{gs_admin_pwd} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export SITEURL=\ -{siteurl} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - 'echo export ALLOWED_HOSTS=\ -"\\"{}\\"" >> {override_fn}'.format( - allowed_hosts, **envs - ), - pty=True, - ) - ctx.run( - "echo export DATABASE_URL=\ -{dburl} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEODATABASE_URL=\ -{geodburl} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export STATIC_ROOT=\ -{static_root} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export MEDIA_ROOT=\ -{media_root} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export GEOIP_PATH=\ -{geoip_path} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export LOGIN_URL=\ -{siteurl}account/login/ >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export LOGOUT_URL=\ -{siteurl}account/logout/ >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export LOGIN_REDIRECT_URL=\ -{siteurl} >> {override_fn}".format( - **envs - ), - pty=True, - ) - ctx.run( - "echo export LOGOUT_REDIRECT_URL=\ -{siteurl} >> {override_fn}".format( - **envs - ), - pty=True, - ) + allowed_hosts = [str(c) for c in current_allowed] + ['"nginx"', '"django"'] + # 1. We construct the text block using an f-string, cleanly handling the nested quotes + # Note: I kept both database keys in the exact chronological order of your original script. + content = f"""export DJANGO_SETTINGS_MODULE={envs['local_settings']} + export GEONODE_GEODATABASE_PASSWORD={envs['geonode_geodb_passwd']} + export DEFAULT_BACKEND_DATASTORE={envs['default_backend_datastore']} + export GEONODE_DATABASE_PASSWORD={envs['geonode_db_passwd']} + export GEONODE_GEODATABASE={envs['geonode_geodb']} + export DATABASE_URL={envs['db_url']} + export GEODATABASE_URL={envs['geodb_url']} + export GEONODE_DATABASE={envs['geonode_db']} + export GEOSERVER_LOCATION={envs['gs_loc']} + export GEOSERVER_WEB_UI_LOCATION={envs['gs_web_ui_loc']} + export GEOSERVER_PUBLIC_LOCATION={envs['gs_pub_loc']} + export GEOSERVER_ADMIN_PASSWORD={envs['gs_admin_pwd']} + export SITEURL={envs['siteurl']} + export ALLOWED_HOSTS="\\"{allowed_hosts}\\"" + export DATABASE_URL={envs['dburl']} + export GEODATABASE_URL={envs['geodburl']} + export STATIC_ROOT={envs['static_root']} + export MEDIA_ROOT={envs['media_root']} + export GEOIP_PATH={envs['geoip_path']} + export LOGIN_URL={envs['siteurl']}account/login/ + export LOGOUT_URL={envs['siteurl']}account/logout/ + export LOGIN_REDIRECT_URL={envs['siteurl']} + export LOGOUT_REDIRECT_URL={envs['siteurl']}""" + + # 2. Append the block to the file using a single echo command, then source it + ctx.run(f'echo "{content}" >> {envs["override_fn"]}', pty=True) ctx.run(f"source {override_env}", pty=True) - print("****************************finalize env**********************************") - ctx.run("env", pty=True) @task @@ -297,17 +155,9 @@ def migrations(ctx): f"python manage.py migrate --noinput --settings={_localsettings()}", pty=True ) ctx.run( - f"python manage.py migrate --noinput --settings={_localsettings()} --database=datastore", + f"python manage.py migrate dynamic_models --noinput --settings={_localsettings()} --database=datastore", pty=True, ) - try: - ctx.run( - f"python manage.py rebuild_index --noinput --settings={_localsettings()}", - pty=True, - ) - except Exception: - pass - @task def statics(ctx): @@ -378,16 +228,6 @@ def fixtures(ctx): print(f"Warning: Failed to load fixture {fixture}: {e}") -@task -def collectstatic(ctx): - print("************************static artifacts******************************") - ctx.run( - f"django-admin collectstatic --noinput \ ---settings={_localsettings()}", - pty=True, - ) - - @task def updateadmin(ctx): print("***********************update admin details**************************") @@ -403,16 +243,6 @@ def updateadmin(ctx): ) -@task -def collectmetrics(ctx): - print("************************collect metrics******************************") - ctx.run( - f"python -W ignore manage.py collect_metrics \ ---settings={_localsettings()} -n -t xml", - pty=True, - ) - - @task def initialized(ctx): print("**************************init file********************************") @@ -450,14 +280,6 @@ def _docker_host_ip(): return ip_list[0] -def _is_valid_ip(ip): - try: - ipaddress.IPv4Address(ip) - return True - except Exception: - return False - - def _container_exposed_port(component, instname): port = "80" try: @@ -512,23 +334,6 @@ def _localsettings(): return settings -def _gs_service_availability(url): - import requests - - try: - r = requests.request("get", url, verify=False) - r.raise_for_status() # Raises a HTTPError if the status is 4xx, 5xxx - except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e: - logger.error(f"GeoServer connection error is {e}") - return False - except requests.exceptions.HTTPError as er: - logger.error(f"GeoServer HTTP error is {er}") - return False - else: - logger.info("GeoServer API are available!") - return True - - def _geonode_public_host(): gn_pub_hostip = os.getenv("GEONODE_LB_HOST_IP", None) if not gn_pub_hostip: @@ -536,13 +341,6 @@ def _geonode_public_host(): return gn_pub_hostip -def _geonode_public_host_ip(): - gn_pub_hostip = os.getenv("GEONODE_LB_HOST_IP", None) - if not gn_pub_hostip or not _is_valid_ip(gn_pub_hostip): - gn_pub_hostip = _docker_host_ip() - return gn_pub_hostip - - def _geonode_public_port(): gn_pub_port = os.getenv("GEONODE_LB_PORT", "") if not gn_pub_port: From 14a581b7836db71bdb7d4549c5a325d4e4b69d94 Mon Sep 17 00:00:00 2001 From: Mattia Giupponi Date: Tue, 19 May 2026 10:11:43 +0200 Subject: [PATCH 2/6] Improvments on docker-compose yaml file --- .env.sample | 7 +++--- docker-compose.yml | 54 ++++++++++++++++++++++++++-------------------- src/entrypoint.sh | 11 ---------- src/tasks.py | 4 +--- 4 files changed, 35 insertions(+), 41 deletions(-) diff --git a/.env.sample b/.env.sample index d3fbf582..85eadd70 100644 --- a/.env.sample +++ b/.env.sample @@ -136,8 +136,7 @@ LOCKDOWN_GEONODE=False X_FRAME_OPTIONS="SAMEORIGIN" SESSION_ENGINE=django.contrib.sessions.backends.cached_db SESSION_EXPIRED_CONTROL_ENABLED=True -DEFAULT_ANONYMOUS_VIEW_PERMISSION=True -DEFAULT_ANONYMOUS_DOWNLOAD_PERMISSION=True +DEFAULT_ANONYMOUS_PERMISSIONS=download CORS_ALLOW_ALL_ORIGINS=True GEOSERVER_CORS_ENABLED=True @@ -147,11 +146,11 @@ GEOSERVER_CORS_ALLOWED_HEADERS=* # Users Registration ACCOUNT_OPEN_SIGNUP=True -ACCOUNT_EMAIL_REQUIRED=True +ACCOUNT_SIGNUP_FIELDS="['email*', 'username*', 'password1*', 'password2*']" ACCOUNT_APPROVAL_REQUIRED=False ACCOUNT_CONFIRM_EMAIL_ON_GET=False ACCOUNT_EMAIL_VERIFICATION=none -ACCOUNT_AUTHENTICATION_METHOD=username_email +ACCOUNT_LOGIN_METHODS="{'email', 'username'}" AUTO_ASSIGN_REGISTERED_MEMBERS_TO_REGISTERED_MEMBERS_GROUP_NAME=True # OAuth2 diff --git a/docker-compose.yml b/docker-compose.yml index 7e80516c..8c54e8a8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -# Common Django template for GeoNode and Celery services below x-common-django: &default-common-django image: ${COMPOSE_PROJECT_NAME}/geonode:${GEONODE_BASE_IMAGE_VERSION} @@ -15,7 +14,9 @@ x-common-django: depends_on: db: condition: service_healthy - nginx: + redis: + condition: service_healthy + memcached: condition: service_healthy services: @@ -28,7 +29,7 @@ services: dockerfile: Dockerfile container_name: django4${COMPOSE_PROJECT_NAME} healthcheck: - test: "curl -m 10 --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null http://django:8000/" + test: "curl -m 10 --fail --silent --output /dev/null http://django:8000/" start_period: 60s interval: 60s timeout: 10s @@ -42,9 +43,6 @@ services: celery: << : *default-common-django container_name: celery4${COMPOSE_PROJECT_NAME} - depends_on: - django: - condition: service_healthy environment: - IS_CELERY=True entrypoint: ["/usr/src/project/entrypoint.sh"] @@ -56,7 +54,7 @@ services: timeout: 10s retries: 2 - # Nginx is serving django static and media files and proxies to django and geonode + # Nginx serving django static/media and proxies to django/geonode nginx: image: ${COMPOSE_PROJECT_NAME}/nginx:${NGINX_BASE_IMAGE_VERSION} build: @@ -78,11 +76,12 @@ services: - statics:/mnt/volumes/statics restart: unless-stopped healthcheck: - test: "if ps -o pid | grep -w 1 > /dev/null; then echo 'nginx running'; else exit 1;fi" - interval: 10s - timeout: 2s - retries: 5 - start_period: 10s + test: "if ps -o pid | grep -w 1 > /dev/null; then echo 'nginx running'; else exit 1;fi" + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + # memcached service memcached: image: memcached:alpine @@ -90,11 +89,11 @@ services: command: memcached ${MEMCACHED_OPTIONS} restart: unless-stopped healthcheck: - test: nc -z 127.0.0.1 11211 + test: ["CMD", "nc", "-z", "127.0.0.1", "11211"] interval: 30s - timeout: 30s - retries: 5 - start_period: 30s + timeout: 5s + retries: 3 + start_period: 10s # Gets and installs letsencrypt certificates letsencrypt: @@ -110,6 +109,9 @@ services: volumes: - nginx-certificates:/geonode-certificates restart: unless-stopped + depends_on: + nginx: + condition: service_healthy # Geoserver backend geoserver: @@ -156,7 +158,10 @@ services: - geoserver-data-dir:/geoserver_data/data restart: unless-stopped healthcheck: - test: "ls -A '/geoserver_data/data' | wc -l" + test: ["CMD-SHELL", "[ $$(ls -A /geoserver_data/data | wc -l) -gt 0 ] || exit 1"] + interval: 10s + timeout: 5s + retries: 3 # PostGIS database. db: @@ -175,10 +180,13 @@ services: - dbbackups:/pg_backups restart: unless-stopped healthcheck: - test: "pg_isready -d postgres -U postgres" - # uncomment to enable remote connections to postgres - #ports: - # - "5432:5432" + test: ["CMD-SHELL", "pg_isready -d postgres -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + # uncomment to enable remote connections to postgres + #ports: + # - "5432:5432" # Vanilla Redis service. This is needed by celery redis: @@ -188,8 +196,8 @@ services: - redisdata:/data restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "redis-cli ping | grep -q PONG || exit 1"] - interval: 30s + test: ["CMD", "redis-cli", "ping"] + interval: 20s timeout: 3s retries: 3 start_period: 5s diff --git a/src/entrypoint.sh b/src/entrypoint.sh index c60e9d33..21c5c8e0 100755 --- a/src/entrypoint.sh +++ b/src/entrypoint.sh @@ -27,17 +27,6 @@ invoke update source $HOME/.bashrc source $HOME/.override_env -echo DOCKER_API_VERSION=$DOCKER_API_VERSION -echo POSTGRES_USER=$POSTGRES_USER -echo POSTGRES_PASSWORD=$POSTGRES_PASSWORD -echo DATABASE_URL=$DATABASE_URL -echo GEODATABASE_URL=$GEODATABASE_URL -echo SITEURL=$SITEURL -echo ALLOWED_HOSTS=$ALLOWED_HOSTS -echo GEOSERVER_PUBLIC_LOCATION=$GEOSERVER_PUBLIC_LOCATION - -# invoke waitfordbs - cmd="$@" if [ ${IS_CELERY} = "true" ] || [ ${IS_CELERY} = "True" ] diff --git a/src/tasks.py b/src/tasks.py index 043e89a2..36f10130 100644 --- a/src/tasks.py +++ b/src/tasks.py @@ -117,8 +117,7 @@ def update(ctx): current_allowed = [] current_allowed.extend([str(pub_host), f"{pub_host}:{pub_port}"]) allowed_hosts = [str(c) for c in current_allowed] + ['"nginx"', '"django"'] - # 1. We construct the text block using an f-string, cleanly handling the nested quotes - # Note: I kept both database keys in the exact chronological order of your original script. + content = f"""export DJANGO_SETTINGS_MODULE={envs['local_settings']} export GEONODE_GEODATABASE_PASSWORD={envs['geonode_geodb_passwd']} export DEFAULT_BACKEND_DATASTORE={envs['default_backend_datastore']} @@ -143,7 +142,6 @@ def update(ctx): export LOGIN_REDIRECT_URL={envs['siteurl']} export LOGOUT_REDIRECT_URL={envs['siteurl']}""" - # 2. Append the block to the file using a single echo command, then source it ctx.run(f'echo "{content}" >> {envs["override_fn"]}', pty=True) ctx.run(f"source {override_env}", pty=True) From 88e0a4454ac22f00f79a30fa7f3fe25ff4692132 Mon Sep 17 00:00:00 2001 From: Mattia Giupponi Date: Tue, 19 May 2026 11:01:09 +0200 Subject: [PATCH 3/6] Improvments on docker-compose yaml file --- src/tasks.py | 224 ++++++++++++++++++++------------------------------- 1 file changed, 87 insertions(+), 137 deletions(-) diff --git a/src/tasks.py b/src/tasks.py index 36f10130..4cf64614 100644 --- a/src/tasks.py +++ b/src/tasks.py @@ -1,22 +1,4 @@ # -*- coding: utf-8 -*- -######################################################################### -# -# Copyright (C) 2016 OSGeo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -######################################################################### import os import re import ast @@ -31,38 +13,43 @@ from urllib.parse import urlparse, urlunparse from invoke import task -BOOTSTRAP_IMAGE_CHEIP = "codenvy/che-ip:nightly" - logger = logging.getLogger(__name__) @task def update(ctx): print("***************************setting env*********************************") - ctx.run("echo 'getting hostname'", pty=True) pub_host = _geonode_public_host() print(f"Public Hostname or IP is {pub_host}") pub_port = _geonode_public_port() print(f"Public PORT is {pub_port}") + pub_protocol = "https" if pub_port == "443" else "http" if pub_protocol == "https" or pub_port == "80": pub_port = None + db_url = _update_db_connstring() geodb_url = _update_geodb_connstring() + geonode_docker_host = None - for _cnt in range(1, 29): + for _cnt in range(1, 60): try: geonode_docker_host = str(socket.gethostbyname("nginx")) + break except Exception: - ctx.run(f"echo '...waiting for NGINX to pop-up...{_cnt}'", pty=True) - print(f"...waiting for NGINX to pop-up...{_cnt}") - time.sleep(1) - - override_env = "$HOME/.override_env" + if _cnt % 4 == 0: + print(f"...waiting for NGINX to pop-up...{_cnt // 4}") + time.sleep(0.25) + + if not geonode_docker_host: + geonode_docker_host = "127.0.0.1" + + override_env = os.path.expandvars("$HOME/.override_env") if os.path.exists(override_env): - os.remove(override_env) - else: - print(f"Can not delete the {override_env} file as it doesn't exists") + try: + os.remove(override_env) + except OSError: + pass if pub_port: siteurl = f"{pub_protocol}://{pub_host}:{pub_port}/" @@ -70,6 +57,7 @@ def update(ctx): else: siteurl = f"{pub_protocol}://{pub_host}/" gs_pub_loc = f"http://{pub_host}/geoserver/" + envs = { "local_settings": str(_localsettings()), "siteurl": os.environ.get("SITEURL", siteurl), @@ -83,41 +71,30 @@ def update(ctx): "media_root": os.environ.get("MEDIA_ROOT", "/mnt/volumes/statics/uploaded/"), "asset_root": os.environ.get("ASSETS_ROOT", "/mnt/volumes/statics/assets/"), "geoip_path": os.environ.get("GEOIP_PATH", "/mnt/volumes/statics/geoip.db"), - "geonode_geodb_passwd": os.environ.get( - "GEONODE_GEODATABASE_PASSWORD", "geonode_data" - ), - "default_backend_datastore": os.environ.get( - "DEFAULT_BACKEND_DATASTORE", "datastore" - ), + "geonode_geodb_passwd": os.environ.get("GEONODE_GEODATABASE_PASSWORD", "geonode_data"), + "default_backend_datastore": os.environ.get("DEFAULT_BACKEND_DATASTORE", "datastore"), "geonode_db_passwd": os.environ.get("GEONODE_DATABASE_PASSWORD", "geonode"), "geonode_geodb": os.environ.get("GEONODE_GEODATABASE", "geonode_data"), - "db_url": os.environ.get( - "DATABASE_URL", "postgis://geonode:geonode@db:5432/geonode" - ), - "geodb_url": os.environ.get( - "GEODATABASE_URL", "postgis://geonode:geonode@db:5432/geonode_data" - ), + "db_url": os.environ.get("DATABASE_URL", "postgis://geonode:geonode@db:5432/geonode"), + "geodb_url": os.environ.get("GEODATABASE_URL", "postgis://geonode:geonode@db:5432/geonode_data"), "geonode_db": os.environ.get("GEONODE_DATABASE", "geonode"), - "gs_loc": os.environ.get( - "GEOSERVER_LOCATION", "http://geoserver:8080/geoserver/" - ), + "gs_loc": os.environ.get("GEOSERVER_LOCATION", "http://geoserver:8080/geoserver/"), "gs_web_ui_loc": os.environ.get("GEOSERVER_WEB_UI_LOCATION", gs_pub_loc), "gs_pub_loc": os.environ.get("GEOSERVER_PUBLIC_LOCATION", gs_pub_loc), "gs_admin_pwd": os.environ.get("GEOSERVER_ADMIN_PASSWORD", "geoserver"), "override_fn": override_env, } + try: current_allowed = ast.literal_eval( - os.getenv("ALLOWED_HOSTS") - or "['{public_fqdn}', '{public_host}', 'localhost', 'django', 'geonode',]".format( - **envs - ) + os.getenv("ALLOWED_HOSTS") or "['{public_fqdn}', '{public_host}', 'localhost', 'django', 'geonode',]".format(**envs) ) except ValueError: current_allowed = [] current_allowed.extend([str(pub_host), f"{pub_host}:{pub_port}"]) allowed_hosts = [str(c) for c in current_allowed] + ['"nginx"', '"django"'] + # Preserved 100% original string format to avoid parsing errors content = f"""export DJANGO_SETTINGS_MODULE={envs['local_settings']} export GEONODE_GEODATABASE_PASSWORD={envs['geonode_geodb_passwd']} export DEFAULT_BACKEND_DATASTORE={envs['default_backend_datastore']} @@ -149,14 +126,15 @@ def update(ctx): @task def migrations(ctx): print("**************************migrations*******************************") + settings = _localsettings() ctx.run( - f"python manage.py migrate --noinput --settings={_localsettings()}", pty=True - ) - ctx.run( - f"python manage.py migrate dynamic_models --noinput --settings={_localsettings()} --database=datastore", + f"source $HOME/.override_env && " + f"python manage.py migrate --noinput --settings={settings} && " + f"python manage.py migrate dynamic_models --noinput --settings={settings} --database=datastore", pty=True, ) + @task def statics(ctx): print("**************************statics*******************************") @@ -165,78 +143,74 @@ def statics(ctx): media_root = os.environ.get("MEDIA_ROOT", "/mnt/volumes/statics/uploaded/") assets_root = os.environ.get("ASSETS_ROOT", "/mnt/volumes/statics/assets/") - ctx.run(f"mkdir -pv {static_root} {media_root} {assets_root}") ctx.run( + f"source $HOME/.override_env && " + f"mkdir -pv {static_root} {media_root} {assets_root} && " f"python manage.py collectstatic --noinput --settings={_localsettings()}", pty=True, ) except Exception: import traceback - traceback.print_exc() @task def prepare(ctx): print("**********************prepare fixture***************************") - ctx.run("rm -rf /tmp/default_oauth_apps_docker.json", pty=True) + for path in ["/tmp/default_oauth_apps_docker.json", "/tmp/default_site.json"]: + try: + os.remove(path) + except OSError: + pass + _prepare_oauth_fixture() - ctx.run("rm -rf /tmp/default_site.json", pty=True) _prepare_site_fixture() @task def fixtures(ctx): print("**************************fixtures********************************") - ctx.run( - f"python manage.py loaddata sample_admin \ ---settings={_localsettings()}", - pty=True, - ) - ctx.run( - f"python manage.py loaddata /tmp/default_oauth_apps_docker.json \ ---settings={_localsettings()}", - pty=True, - ) - ctx.run( - f"python manage.py loaddata /tmp/default_site.json \ ---settings={_localsettings()}", - pty=True, - ) - ctx.run( - f"python manage.py loaddata initial_data.json \ ---settings={_localsettings()}", - pty=True, + settings = _localsettings() + + base_cmd = ( + f"source $HOME/.override_env && " + f"python manage.py loaddata sample_admin --settings={settings} && " + f"python manage.py loaddata /tmp/default_oauth_apps_docker.json --settings={settings} && " + f"python manage.py loaddata /tmp/default_site.json --settings={settings} && " + f"python manage.py loaddata initial_data.json --settings={settings}" ) + ctx.run(base_cmd, pty=True) - # Load Project related fixtures - from django.conf import settings - - project_fixtures = getattr(settings, "PROJECT_FIXTURES", []) + from django.conf import settings as django_settings + project_fixtures = getattr(django_settings, "PROJECT_FIXTURES", []) - for fixture in project_fixtures: - if fixture: - print(f"Loading project fixture: {fixture}") + if project_fixtures: + fixture_cmds = [ + f"python manage.py loaddata {fix} --settings={settings}" + for fix in project_fixtures if fix + ] + if fixture_cmds: try: - ctx.run( - f"python manage.py loaddata {fixture} --settings={_localsettings()}", - pty=True, - ) + ctx.run("source $HOME/.override_env && " + " && ".join(fixture_cmds), pty=True) except Exception as e: - print(f"Warning: Failed to load fixture {fixture}: {e}") + print(f"Warning: Failed to load project fixtures: {e}") @task def updateadmin(ctx): print("***********************update admin details**************************") - ctx.run("rm -rf /tmp/django_admin_docker.json", pty=True) + try: + os.remove("/tmp/django_admin_docker.json") + except OSError: + pass + _prepare_admin_fixture( os.environ.get("ADMIN_PASSWORD", "admin"), os.environ.get("ADMIN_EMAIL", "admin@example.org"), ) ctx.run( - f"django-admin loaddata /tmp/django_admin_docker.json \ ---settings={_localsettings()}", + f"source $HOME/.override_env && " + f"django-admin loaddata /tmp/django_admin_docker.json --settings={_localsettings()}", pty=True, ) @@ -245,60 +219,39 @@ def updateadmin(ctx): def initialized(ctx): print("**************************init file********************************") static_root = os.environ.get("STATIC_ROOT", "/mnt/volumes/statics/static/") - lockfile_dir = Path( - static_root - ).parent # quite ugly, we're assuming such dir exists and is writable - ctx.run(f"date > {lockfile_dir}/geonode_init.lock") + lockfile = Path(static_root).parent / "geonode_init.lock" + + try: + lockfile.write_text(datetime.datetime.now().ctime() + "\n") + except Exception: + ctx.run(f"date > {lockfile}") def _docker_host_ip(): try: - client = docker.from_env(version="1.24") - ip_list = client.containers.run( - BOOTSTRAP_IMAGE_CHEIP, network_mode="host" - ).split("\n") + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + local_ip = s.getsockname()[0] + s.close() + return local_ip except Exception: - import traceback - - traceback.print_exc() - ip_list = [ - "127.0.0.1", - ] - if len(ip_list) > 1: - print( - f"Docker daemon is running on more than one \ -address {ip_list}" - ) - print(f"Only the first address:{ip_list[0]} will be returned!") - else: - print( - f"Docker daemon is running at the following \ -address {ip_list[0]}" - ) - return ip_list[0] + try: + return socket.gethostbyname(socket.gethostname()) + except Exception: + return "127.0.0.1" def _container_exposed_port(component, instname): port = "80" try: client = docker.from_env(version="1.24") - ports_dict = json.dumps( - [ - c.attrs["Config"]["ExposedPorts"] - for c in client.containers.list( - filters={ - "label": f"org.geonode.component={component}", - "status": "running", - } - ) - if str(instname) in c.name - ][0] - ) - for key in json.loads(ports_dict): - port = re.split("/tcp", key)[0] + for c in client.containers.list(filters={"label": f"org.geonode.component={component}", "status": "running"}): + if str(instname) in c.name: + exposed_ports = c.attrs["Config"].get("ExposedPorts", {}) + for key in exposed_ports: + return re.split("/tcp", key)[0] except Exception: import traceback - traceback.print_exc() return port @@ -328,8 +281,7 @@ def _update_geodb_connstring(): def _localsettings(): - settings = os.getenv("DJANGO_SETTINGS_MODULE", "geonode_project.settings") - return settings + return os.getenv("DJANGO_SETTINGS_MODULE", "geonode_project.settings") def _geonode_public_host(): @@ -342,9 +294,7 @@ def _geonode_public_host(): def _geonode_public_port(): gn_pub_port = os.getenv("GEONODE_LB_PORT", "") if not gn_pub_port: - gn_pub_port = _container_exposed_port( - "nginx", os.getenv("GEONODE_INSTANCE_NAME", "geonode") - ) + gn_pub_port = _container_exposed_port("nginx", os.getenv("GEONODE_INSTANCE_NAME", "geonode")) elif gn_pub_port in ("80", "443"): gn_pub_port = None return gn_pub_port From 3f3cae3846e3f932d11681ac004755277cc3ef0c Mon Sep 17 00:00:00 2001 From: Mattia Giupponi Date: Tue, 19 May 2026 11:01:58 +0200 Subject: [PATCH 4/6] Improvments on docker-compose yaml file --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8c54e8a8..57499765 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ x-common-django: env_file: - .env volumes: - - './src:/usr/src/project' # Uncomment for local development + # - './src:/usr/src/project' # Uncomment for local development - statics:/mnt/volumes/statics - geoserver-data-dir:/geoserver_data/data - backup-restore:/backup_restore From 9e0f07c6a719663886923638324df51ca906c98e Mon Sep 17 00:00:00 2001 From: Mattia Giupponi Date: Tue, 19 May 2026 11:30:12 +0200 Subject: [PATCH 5/6] Improvments on Dockerfile and requirements --- Dockerfile | 47 +++++++++++++++++++------------------------- src/pyproject.toml | 6 +++--- src/requirements.txt | 4 ---- 3 files changed, 23 insertions(+), 34 deletions(-) delete mode 100644 src/requirements.txt diff --git a/Dockerfile b/Dockerfile index e1612013..7cbfc4d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,39 +1,32 @@ FROM geonode/geonode-base:latest-ubuntu-24.04 -RUN mkdir -p /usr/src/project - -RUN apt-get update -y && apt-get install curl wget unzip gnupg2 locales netcat-openbsd -y - -RUN sed -i -e 's/# C.UTF-8 UTF-8/C.UTF-8 UTF-8/' /etc/locale.gen && \ - locale-gen -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 WORKDIR /usr/src/project -COPY src/tasks.py \ - src/entrypoint.sh \ - src/requirements.txt \ - /usr/src/project/ +RUN apt-get update -y && apt-get install -y --no-install-recommends \ + curl \ + wget \ + unzip \ + gnupg2 \ + locales \ + netcat-openbsd \ + && sed -i -e 's/# C.UTF-8 UTF-8/C.UTF-8 UTF-8/' /etc/locale.gen \ + && locale-gen \ + && apt-get autoremove --purge -y \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN python -m pip install --no-cache-dir -U pip setuptools wheel COPY src/wait-for-databases.sh /usr/bin/wait-for-databases -RUN chmod +x /usr/bin/wait-for-databases -RUN chmod +x /usr/src/project/tasks.py \ - && chmod +x /usr/src/project/entrypoint.sh - COPY src/celery.sh /usr/bin/celery-commands -RUN chmod +x /usr/bin/celery-commands - COPY src/celery-cmd /usr/bin/celery-cmd -RUN chmod +x /usr/bin/celery-cmd - -RUN python -m pip install -U pip setuptools wheel -RUN yes w | pip install --src /usr/src -r requirements.txt - -RUN apt-get autoremove --purge &&\ - apt-get clean &&\ - rm -rf /var/lib/apt/lists/* +RUN chmod +x /usr/bin/wait-for-databases /usr/bin/celery-commands /usr/bin/celery-cmd COPY src/ /usr/src/project/ -RUN yes w | pip install -e . +RUN chmod +x /usr/src/project/tasks.py /usr/src/project/entrypoint.sh + +RUN pip install --no-cache-dir -e . EXPOSE 8000 diff --git a/src/pyproject.toml b/src/pyproject.toml index a8987c4c..43f3a847 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -11,9 +11,8 @@ requires-python = ">=3.10" license = { text = "GPL-3.0-or-later" } keywords = ["project", "geonode", "django"] -# Equivalent modern replacement for dependency_links: dependencies = [ - "GeoNode @ git+https://github.com/GeoNode/geonode.git" + "GeoNode @ git+https://github.com/GeoNode/geonode.git@master" ] [project.urls] @@ -22,6 +21,7 @@ Homepage = "https://github.com/GeoNode/geonode-project" [tool.setuptools] include-package-data = true +# Correct package discovery layout [tool.setuptools.packages.find] -# Equivalent to find_packages() where = ["."] +exclude = ["tests*"] diff --git a/src/requirements.txt b/src/requirements.txt deleted file mode 100644 index ed116f3e..00000000 --- a/src/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -# For an editable installation of GeoNode, you can perform the following inside the python venv: -# pip install -e git+https://github.com/GeoNode/geonode.git@master#egg=geonode -# pip install -e git+https://github.com/GeoNode/geonode-mapstore-client.git@master#egg=django_geonode_mapstore_client -git+https://github.com/GeoNode/geonode.git@master#egg=GeoNode \ No newline at end of file From ee53fc79fc6fe08f03c96d419dae1d6c9ecb3f0d Mon Sep 17 00:00:00 2001 From: Mattia Giupponi Date: Tue, 19 May 2026 16:58:25 +0200 Subject: [PATCH 6/6] change geoserver healthcheck --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 57499765..e4949887 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -123,11 +123,11 @@ services: - BASE_IMAGE_VERSION=${GEOSERVER_BASE_IMAGE_VERSION} container_name: geoserver4${COMPOSE_PROJECT_NAME} healthcheck: - test: "curl -m 10 --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null http://geoserver:8080/geoserver/ows" - start_period: 60s - interval: 60s + test: ["CMD", "curl", "-f", "http://geoserver:8080/geoserver/web/wicket/resource/org.geoserver.web.GeoServerBasePage/img/logo.png"] + start_period: 30s + interval: 30s timeout: 10s - retries: 2 + retries: 3 env_file: - .env ports: