ixc / ixc-django-docker

Scripts and config files that make it easier to run Django projects consistently with and without Docker.
5 stars 1 forks source link

Dockerize legacy projects #1

Closed mrmachine closed 6 years ago

mrmachine commented 7 years ago

@jmurty @sam-mi Could you take a quick look at the changes in this branch? Notably:

The default settings modules that get combined are intended to be 1) a safe default; 2) deal with the most common requirements arising in an ephemeral containerised environment.

Additional settings modules (not used by default) are commonly used, but not strictly required to avoid an issue we might encounter in an ephemeral containerised environment. This is to avoid having to make more changes than necessary when dockerizing existing projects, while still making it easy to opt-in if we choose.

There is a django-icekit feature branch that uses ixc-django-docker instead of the scripts and project settings in ICEkit itself, but it hasn't been updated to work with this branch of ixc-django-docker.

I had some thoughts but have not yet tested how we might also make ICEkit specific settings modular and configurable. Basically, for an ICEkit project, alter DJANGO_SETTINGS_MODULE to point to an ICEkit version of ixc_django_docker/settings/__init__.py which uses what we can from ixc-django-docker's base settings, and allows the project to configure additional ICEkit settings modules.

jmurty commented 7 years ago

Hi @mrmachine I have some feedback about the split settings. I have been thinking about this in the abstract for a while, so there's a high risk of bike-shedding here, but here are my thoughts.

Here is what I envisage (possibly naively)

In settings/init.py:

# Start by loading all known environment variables in one place
# (base, then optional project/local ones).
include(
    'envvars.py',
    optional(join(PROJECT_SETTINGS_DIR, 'envvars.py')),
    # Local version would probably only ever forcibly override actual envvars
    optional(join(PROJECT_SETTINGS_DIR, 'local', 'envvars.py')),
)

# Define or calculate basic Django settings (replaces 'base.py')
include(
    'django.py',
    optional(join(PROJECT_SETTINGS_DIR, 'django.py')),
    optional(join(PROJECT_SETTINGS_DIR, 'local', 'django.py')),
)

# Load site environment settings next (base, then overrides)
# by loading anything in a directory path named to match
# BASE_SETTINGS_MODULE. All are optional to permit arbitrary
# site environment names, and we use glob filename matching
# to permit project to split settings as desired.
include(
    optional(join('_' + BASE_SETTINGS_MODULE, '*.py')),
    optional(join(PROJECT_SETTINGS_DIR, '_' + BASE_SETTINGS_MODULE, '*.py')),
    # No need for a 'local' variant, use e.g. project/settings/develop/local.py
)

# Conditionally apply settings for optional features, where
# GK_ENABLE_xyz feature flags are set via envvars and/or site
# environment settings.
# Notes:
# - feature configs expect key settings to exist already, and
#   raise useful error messages if not
# - group settings into 'features' subdir in ixc-django-docker
# - possible to completely override settings in project with a
#   clean slate by setting GK_ENABLE_xyz=False and defining
#   everything in project-specific settings file e.g:
#   project/settings/develop/compressor.py

settings.GK_ENABLE_COMPRESSOR and include(
    'features/compressor.py',
    optional(join(PROJECT_SETTINGS_DIR, 'compressor.py')),
    optional(join(PROJECT_SETTINGS_DIR, 'local', 'compressor.py')),
)

settings.GK_ENABLE_WHITENOISE and include(
    'features/whitenoise.py',
    optional(join(PROJECT_SETTINGS_DIR, 'whitenoise.py')),
    optional(join(PROJECT_SETTINGS_DIR, 'local', 'whitenoise.py')),
)

This would lead to a _ixc_djangodocker/settings/ directory hierarchy something like this:

settings/__init__.py
settings/_develop/django.py
settings/_production/storages.py
settings/_staging/email.py
settings/_test/django.py
settings/envvars.py
settings/django.py
settings/features/compressor.py
settings/features/whitenoise.py

And a project settings directory hierarchy like:

project/settings/_production/email.py
project/settings/_production/storages.py
project/settings/_staging/email.py
project/settings/_test/dummy_services.py
project/settings/django.py
project/settings/envvars.py  (extra project-specific envvars)
jmurty commented 7 years ago

@mrmachine Also, I made a management command to dump all a site's settings into a document that can be easily compared over time or between different environments, as a first sanity-retaining step toward changing ICEkit's settings. This command might make more sense in this project?

https://github.com/ic-labs/django-icekit/commit/a986477b40169cc9a2a0abcb0942c1a11aba41e0

mrmachine commented 7 years ago

@jmurty Thanks for this feedback. This looks very flexible and well structured, but I wonder if it's overkill, at least for ixc-django-docker?

The purpose of ixc-django-docker (in my mind) is to do just enough to run a Django project with or without Docker locally, or in Docker Cloud.

I want ICEkit to use it so we can improve the way we host ICEkit sites without having to upgrade the version of ICEkit they are running, but I also want it to be quite un-opinionated about what a project must do to use it, except for things that are really required when deployed to scaleable ephemeral infrastructure (e.g. compressor, whitenoise, remote storage).

Ideally, we should be able to start with safe django settings, plus required settings (compressor, whitenoise, remote storage, etc.), and then pull in whatever project settings already happen to exist, wherever they happen to exist, and just tweak those existing project settings a little to avoid conflicting with or undoing the safe default and other required base settings. It can be a significant effort to refactor existing legacy project settings (hundreds of lines) into a specific modular architecture.

I am worried that conflating a mechanism to merely load settings modules with a mechanism to enable features will more tightly couple the project settings to the architecture of and the features configured by ixc-django-docker. I think I could be persuaded that something like this should be in django-icekit, though, where we will see less variety in the projects.

Can you elaborate on the risks you see with specifying settings modules in an environment variable and loading them in that order?

What I like about this approach is that project's have absolute freedom to load whatever settings they need, from anywhere really, in whatever order they need. Legacy projects don't even need a project_settings module or a settings package in a pre-defined location. Individual projects could include a settings module that itself imposes a more rigid structure, like the one you are proposing. E.g. an ICEkit project could load join(ICEKIT_DIR, 'settings', '__init__.py') which behaves as you describe.

Can you also give an example of what you see going in envvars.py in ixc-django-docker and in projects that use it? I'm cautious about defining environment variables in a Python settings module, because I've found a few times now that actually, I need access to that environment variable in shell scripts, too. We already have .env.* files for populating the shell with environment variables?

I think that in many cases, having multiple subdirectories of settings as the prescribed architecture will be overkill and many projects will only really need one or two settings files to override. Those settings files should be able to optionally configure features based on environment variables as they do now (e.g. pull in credentials).

To answer your question about calculated.py, possibly. It currently only does one thing -- create directories that might not exist at runtime. It's a separate file that is loaded near the end because other settings modules may be loaded after the base settings which alter settings that specify paths that need to exist.

jmurty commented 7 years ago

Hi @mrmachine here's my delayed follow-up:

jmurty commented 7 years ago

You may need to help us find our way out of the bike shed here @sam-mi

jmurty commented 6 years ago

@mrmachine It would be great it we could move forward with this PR to make it easier to set up projects in general – GLAMkit or otherwise – with dockerised environments. I am still wary of the envvar-defined settings, but not enough to so further delay merging this

mrmachine commented 6 years ago

@jmurty thanks for this feedback. I've just reviewed the PR again to refresh my memory. Here are my thoughts:

Does any of this alleviate your concerns?

jmurty commented 6 years ago

@mrmachine Thanks for taking the time to weigh the pros and cons but at this stage I say just merge the PR – assuming we can do so without breaking projects that already use it like https://github.com/ixc/iwc-forum/ – and see if including optional settings modules turns out to be a problem in reality rather than just my imagination.