Skip to content

Commit

Permalink
Merge pull request #100 from zacharlie/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
zacharlie authored Aug 7, 2022
2 parents 75a87d6 + bca3e6f commit 15a41d8
Show file tree
Hide file tree
Showing 21 changed files with 261 additions and 199 deletions.
44 changes: 0 additions & 44 deletions .github/dependabot.yml

This file was deleted.

34 changes: 0 additions & 34 deletions .github/workflows/build-push-image.yaml

This file was deleted.

57 changes: 0 additions & 57 deletions .github/workflows/ci.yml

This file was deleted.

20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Search, select, clip, and deliver spatial data sources.

[https://data.kartoza.com](http://data.kartoza.com)

![geodatamart-preview](geodatamart.gif)

## Run

The development stack is managed with Docker Compose.
Expand Down Expand Up @@ -36,7 +40,21 @@ Using the docker extension with vscode along with the `docker.commands.attach` c

## Deploy

Deployment is expected to be completed with Kubernetes.
Deployment is done with docker compose (for now).

Some notes/ caveats on the 0.1 release deployment:

- run docker compose with root level perms: otherwise running and creating users on postgresql may fail
- lowercase for postgresql user: if you generate a random username make sure everything is cast to lowercase
- file permissions issues: celery workers need to be run as root (for now) due to the qgis config and local user config not being 100%. As a result, the geodata/ qgis directory needs to probably be owned by root and have 777 permissions, due to django etc running under the django user. This may also cause issues with data removal from the admin ui in django
- cascading reference removal: many foreign key fields relate to managed file objects, which include lifecycle hooks for keeping the filesystem and the db models in some sort of sync. There's a bug there that causes an error on cascading, so the default for most fk in architecture/ conception phase was "do nothing". This is also used for accounts etc for which the lifecycle is not yet defined. This needs review and reevaluation, as for now even removing users may result in a meaningless "500/ Oops" error, because accounts and all other related items need to be removed first. Same goes for projects etc.
- docker logging config: it's the default atm and will likely cause system bloat

## Adding projects

There's a bit of a serious bug with regard to adding new project and config items... When developing/ iterating over the schema structure and what fields or requirements needed to be put into the system, the fk field was defined on the project, and then that related to the managed file objects. When the change was implemented to use dynamic file paths based on project details, it became possible to associate a file with the project and when calling save() (as demonstrated in the seed operation) the file would be uploaded to the project path. This keeps relative paths intact and prevents collisions.

The problem is that this is not exposed through the admin ui, as when uploading a config file it is not possible to specify the reverse relation to the project in order to get the dynamic file path. I thought there may be a clever hack but I haven't been able to find anything and it seems like a schema restructure is a better option, but it's a bit late in the day for that to enter the deployment, so new project definitions will have to be managed programmatically for the time being or have auxiliary files placed in the relevant geodata root directory.

## Development

Expand Down
13 changes: 8 additions & 5 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.common.BrokenLinkEmailsMiddleware",
# "django.middleware.common.BrokenLinkEmailsMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_htmx.middleware.HtmxMiddleware",
]
Expand Down Expand Up @@ -249,7 +249,8 @@
# Django Admin URL.
ADMIN_URL = "admin/"
# https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = [("""Kartoza""", "[email protected]")]
# ADMINS = [("""Kartoza""", "[email protected]")]
ADMINS = [("""Charlie""", "[email protected]")]
# https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS

Expand Down Expand Up @@ -302,11 +303,13 @@
# ------------------------------------------------------------------------------
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
# https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_AUTHENTICATION_METHOD = "username"
# ACCOUNT_AUTHENTICATION_METHOD = "username"
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
# https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_EMAIL_REQUIRED = True
# https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
# ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_EMAIL_VERIFICATION = "none"
# https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_ADAPTER = "geodata_mart.users.adapters.AccountAdapter"
# https://django-allauth.readthedocs.io/en/latest/forms.html
Expand Down Expand Up @@ -345,7 +348,7 @@
"SERVE_PERMISSIONS": ["rest_framework.permissions.AllowAny"],
"SERVERS": [
{"url": "http://127.0.0.1:8000", "description": "Local Development server"},
{"url": "https://geodata.kartoza.com", "description": "Production server"},
{"url": "https://data.kartoza.com", "description": "Production server"},
],
}

Expand Down
24 changes: 16 additions & 8 deletions config/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
SECRET_KEY = env("DJANGO_SECRET_KEY")
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["geodata.kartoza.com"])
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["data.kartoza.com"])

# DATABASES
# ------------------------------------------------------------------------------
DATABASES["default"] = env.db("DATABASE_URL") # noqa F405
DATABASES["default"]["ENGINE"] = "django.contrib.gis.db.backends.postgis" # noqa F405
DATABASES["default"]["ATOMIC_REQUESTS"] = True # noqa F405
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa F405

Expand Down Expand Up @@ -100,7 +101,7 @@
# https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email
DEFAULT_FROM_EMAIL = env(
"DJANGO_DEFAULT_FROM_EMAIL",
default="Geodata Mart <noreply@geodata.kartoza.com>",
default="Geodata Mart <noreply@data.kartoza.com>",
)
# https://docs.djangoproject.com/en/dev/ref/settings/#server-email
SERVER_EMAIL = env("DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL)
Expand All @@ -121,12 +122,10 @@
INSTALLED_APPS += ["anymail"] # noqa F405
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference
# https://anymail.readthedocs.io/en/stable/esps/mailgun/
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
# https://anymail.dev/en/stable/esps/sendgrid/
EMAIL_BACKEND = "anymail.backends.sendgrid.EmailBackend"
ANYMAIL = {
"MAILGUN_API_KEY": env("MAILGUN_API_KEY"),
"MAILGUN_SENDER_DOMAIN": env("MAILGUN_DOMAIN"),
"MAILGUN_API_URL": env("MAILGUN_API_URL", default="https://api.mailgun.net/v3"),
"SENDGRID_API_KEY": env("SENDGRID_API_KEY"),
}

# django-compressor
Expand Down Expand Up @@ -166,10 +165,19 @@
"level": "DEBUG",
"class": "logging.StreamHandler",
"formatter": "verbose",
}
},
'mail_admins': {
'level': 'CRITICAL',
"class": "django.utils.log.AdminEmailHandler",
"formatter": "verbose",
},
},
"root": {"level": "INFO", "handlers": ["console"]},
"loggers": {
'django': {
'handlers': ['console'],
'level': 'DEBUG',
},
"django.db.backends": {
"level": "ERROR",
"handlers": ["console"],
Expand Down
102 changes: 102 additions & 0 deletions docker/production/CeleryWorker
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# ARG PYTHON_VERSION=3.9-slim-bullseye

# define an alias for the specfic python version used in this file.
# FROM python:${PYTHON_VERSION} as python

FROM qgis/qgis:final-3_22_8 as python

# Alias the python command with the interpreter used with qgis
# note RUN alias python="/usr/bin/python3" will not work between build steps
# https://github.com/docker-library/python/blob/master/Dockerfile-linux.template#L278
# https://stackoverflow.com/questions/60383262/setting-alias-in-dockerfile-not-working-command-not-found#60383287
RUN cd "$(dirname $(which python3))" \
&& ln -s python3 python \
&& ln -s python3-config python-config

# Python build stage
FROM python as python-build-stage

ARG BUILD_ENVIRONMENT=production

# Install apt packages
RUN apt-get update && apt-get install --no-install-recommends -y \
# dependencies for building Python packages
build-essential \
# psycopg2 dependencies
libpq-dev

# Requirements are installed here to ensure they will be cached.
COPY ./requirements .

# Create Python Dependency and Sub-Dependency Wheels.
RUN python -m pip wheel --wheel-dir /usr/src/app/wheels \
-r ${BUILD_ENVIRONMENT}.txt


# Python 'run' stage
FROM python as python-run-stage

ARG BUILD_ENVIRONMENT=production
ARG APP_HOME=/app

ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV BUILD_ENV ${BUILD_ENVIRONMENT}

WORKDIR ${APP_HOME}

RUN addgroup --system django \
&& adduser --system --ingroup django django


# Install required system dependencies
RUN apt-get update && apt-get install --no-install-recommends -y \
# psycopg2 dependencies
libpq-dev \
# Translations dependencies
gettext \
# install additional app dependencies
libmagic1 \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*

# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction
# copy python dependency wheels from python-build-stage
COPY --from=python-build-stage /usr/src/app/wheels /wheels/

# use wheels to install python dependencies
RUN python -m pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \
&& rm -rf /wheels/


COPY --chown=django:django ./docker/production/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
RUN chmod +x /entrypoint


COPY --chown=django:django ./docker/production/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start
COPY --chown=django:django ./docker/production/celery/worker/start /start-celeryworker
RUN sed -i 's/\r$//g' /start-celeryworker
RUN chmod +x /start-celeryworker


COPY --chown=django:django ./docker/production/celery/beat/start /start-celerybeat
RUN sed -i 's/\r$//g' /start-celerybeat
RUN chmod +x /start-celerybeat


COPY ./docker/production/celery/flower/start /start-flower
RUN sed -i 's/\r$//g' /start-flower
RUN chmod +x /start-flower


# copy application code to WORKDIR
COPY --chown=django:django . ${APP_HOME}

# make django owner of the WORKDIR directory as well.
RUN chown django:django ${APP_HOME}

ENTRYPOINT ["/entrypoint"]
2 changes: 2 additions & 0 deletions docker/production/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
libpq-dev \
# Translations dependencies
gettext \
# install additional app dependencies
libmagic1 \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
Expand Down
2 changes: 1 addition & 1 deletion docker/production/traefik/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ FROM traefik:v2.2.11
RUN mkdir -p /etc/traefik/acme \
&& touch /etc/traefik/acme/acme.json \
&& chmod 600 /etc/traefik/acme/acme.json
COPY ./compose/production/traefik/traefik.yml /etc/traefik
COPY ./docker/production/traefik/traefik.yml /etc/traefik
Loading

0 comments on commit 15a41d8

Please sign in to comment.