From fca80b234edef061701015586c3ba442462b83d3 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 21:32:32 -0400 Subject: [PATCH] fix container issues --- .sugar.yaml | 15 +++ containers/app/Dockerfile | 95 +++++++++++++++++++ containers/app/Dockerfile.bkp | 82 ++++++++++++++++ containers/app/Dockerfile.local.bkp | 62 ++++++++++++ containers/app/scripts/entrypoint.sh | 30 ++++++ containers/app/scripts/install-deps.sh | 12 +++ containers/app/scripts/start.sh | 29 ++++++ containers/compose.base.yaml | 39 ++++++++ .../migrations/0001_initial.py | 53 +++++++++++ src/growth_plan_linker/migrations/__init__.py | 0 10 files changed, 417 insertions(+) create mode 100644 .sugar.yaml create mode 100644 containers/app/Dockerfile create mode 100644 containers/app/Dockerfile.bkp create mode 100644 containers/app/Dockerfile.local.bkp create mode 100755 containers/app/scripts/entrypoint.sh create mode 100755 containers/app/scripts/install-deps.sh create mode 100644 containers/app/scripts/start.sh create mode 100644 containers/compose.base.yaml create mode 100644 src/growth_plan_linker/migrations/0001_initial.py create mode 100644 src/growth_plan_linker/migrations/__init__.py diff --git a/.sugar.yaml b/.sugar.yaml new file mode 100644 index 0000000..34284c9 --- /dev/null +++ b/.sugar.yaml @@ -0,0 +1,15 @@ +version: 1.0 +compose-app: docker compose +defaults: + group: dev + project-name: growthplan +groups: + dev: + compose-path: + - containers/compose.base.yaml + # env-file: .env + services: + default: app,postgres + available: + - name: app + - name: postgres diff --git a/containers/app/Dockerfile b/containers/app/Dockerfile new file mode 100644 index 0000000..763f730 --- /dev/null +++ b/containers/app/Dockerfile @@ -0,0 +1,95 @@ +# ref: https://github.com/mamba-org/micromamba-docker/blob/main/Dockerfile + +FROM condaforge/mambaforge:latest + +LABEL maintainer="Ivan Ogasawara " +LABEL org.opencontainers.image.title="LiteRev" +LABEL org.opencontainers.image.authors="LiteRev Team" +LABEL org.opencontainers.image.source="https://github.com/thegraphnetwork-app/LiteRev" +LABEL org.opencontainers.image.version="latest" +LABEL org.opencontainers.image.description="LiteRev" +LABEL org.thegraphnetwork.config.version="latest" + +# it is the default, but using it here to have it explicitly +USER root + +SHELL ["/bin/bash", "-c"] +# Use bash in Dockerfile RUN commands and make sure bashrc is sourced when +# executing commands with /bin/bash -c +# Needed to have the micromamba activate command configured etc. + +ENV ENV_NAME=growth-plan-linker +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Etc/UTC +ARG UID=1000 +ARG GID=1000 + +RUN apt-get update -y \ + && apt-get install -y \ + apt-utils \ + build-essential \ + curl \ + tini \ + nginx \ + snapd \ + sudo \ + tzdata \ + gcc-multilib \ + g++-multilib \ + openssl \ + && rm -rf /var/lib/apt/lists/* \ + /var/cache/apt/archives \ + /tmp/* + +RUN addgroup --gid ${GID} app \ + && useradd --uid ${UID} --gid ${GID} -ms /bin/bash app \ + && mkdir -p /app \ + && mkdir -p /opt/appfiles \ + && chmod -R a+rwx /opt/conda /app /opt/appfiles \ + && export ENV_NAME="$ENV_NAME" \ + && chown app:app /app /opt/appfiles \ + && echo "app ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/app \ + && chmod 0440 /etc/sudoers.d/app + +USER app + +WORKDIR /app + +COPY --chown=app:app ./conda/ /tmp/conda +# COPY --chown=app:app ./.github/workflows/.condarc /home/app/.condarc + +ARG HTTP_PROXY +ARG HTTPS_PROXY + +RUN mamba env create -n $ENV_NAME --file /tmp/conda/dev.yaml \ + && conda clean --all \ + && find /opt/conda/ -type f,l -name '*.pyc' -delete \ + && find /opt/conda/ -type f,l -name '*.js.map' -delete \ + && rm -rf /opt/conda/pkgs /tmp/* + +ENV CONDA_PREFIX /opt/conda/envs/$ENV_NAME +ENV PATH ${CONDA_PREFIX}/bin:$PATH + +# install dependencies +COPY --chown=app:app pyproject.toml poetry.lock /opt/appfiles +COPY --chown=app:app containers/app/scripts/install-deps.sh /opt/appfiles/install-deps.sh + +ARG ENV=prod + +RUN /opt/appfiles/install-deps.sh + +COPY --chown=app:app containers/app/scripts/entrypoint.sh /opt/appfiles/entrypoint.sh +COPY --chown=app:app src/ /app + +# copy start script +COPY --chown=app:app containers/app/scripts/start.sh /opt/appfiles/start.sh + +RUN chmod +x /opt/appfiles/start.sh \ + && chmod +x /opt/appfiles/entrypoint.sh \ + && echo "source /opt/appfiles/entrypoint.sh" > ~/.bashrc + + +ENV PYTHONPATH='/app' + +ENTRYPOINT ["tini", "--", "/opt/appfiles/entrypoint.sh"] +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/containers/app/Dockerfile.bkp b/containers/app/Dockerfile.bkp new file mode 100644 index 0000000..206aab1 --- /dev/null +++ b/containers/app/Dockerfile.bkp @@ -0,0 +1,82 @@ + +# define an alias for the specific python version used in this file. +FROM docker.io/python:3.11.8-slim-bookworm as python + +# Python build stage +FROM docker.io/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 pip wheel --wheel-dir /usr/src/app/wheels \ + -r ${BUILD_ENVIRONMENT}.txt + + +# Python 'run' stage +FROM docker.io/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 \ + # 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 pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ + && rm -rf /wheels/ + + +COPY --chown=django:django ./compose/production/django/entrypoint /entrypoint +RUN sed -i 's/\r$//g' /entrypoint +RUN chmod +x /entrypoint + + +COPY --chown=django:django ./compose/production/django/start /start +RUN sed -i 's/\r$//g' /start +RUN chmod +x /start + + +# 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} + +USER django + +RUN DATABASE_URL="" \ + DJANGO_SETTINGS_MODULE="config.settings.test" \ + python manage.py compilemessages + +ENTRYPOINT ["/entrypoint"] diff --git a/containers/app/Dockerfile.local.bkp b/containers/app/Dockerfile.local.bkp new file mode 100644 index 0000000..87a1b24 --- /dev/null +++ b/containers/app/Dockerfile.local.bkp @@ -0,0 +1,62 @@ +# define an alias for the specific python version used in this file. +FROM docker.io/python:3.11.8-slim-bookworm as python + + +# Python build stage +FROM docker.io/python as python-build-stage + +ENV PYTHONDONTWRITEBYTECODE 1 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + # dependencies for building Python packages + build-essential \ + # psycopg2 dependencies + libpq-dev \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements /requirements + +# create python dependency wheels +RUN pip wheel --no-cache-dir --wheel-dir /usr/src/app/wheels \ + -r /requirements/local.txt -r /requirements/production.txt \ + && rm -rf /requirements + + +# Python 'run' stage +FROM docker.io/python as python-run-stage + +ARG BUILD_ENVIRONMENT +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + # To run the Makefile + make \ + # psycopg2 dependencies + libpq-dev \ + # Translations dependencies + gettext \ + # Uncomment below lines to enable Sphinx output to latex and pdf + # texlive-latex-recommended \ + # texlive-fonts-recommended \ + # texlive-latex-extra \ + # latexmk \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# 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 pip install --no-cache /wheels/* \ + && rm -rf /wheels + +COPY ./compose/local/docs/start /start-docs +RUN sed -i 's/\r$//g' /start-docs +RUN chmod +x /start-docs + +WORKDIR /docs diff --git a/containers/app/scripts/entrypoint.sh b/containers/app/scripts/entrypoint.sh new file mode 100755 index 0000000..21cc518 --- /dev/null +++ b/containers/app/scripts/entrypoint.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -ex + +# prepare the conda environment +is_conda_in_path=$(echo $PATH|grep -m 1 --count /opt/conda/) + +if [ $is_conda_in_path == 0 ]; then + export PATH="/opt/conda/condabin:/opt/conda/bin:$PATH" + echo "[II] included conda to the PATH" +fi + +echo "[II] activate growth-plan-linker" +source activate growth-plan-linker + +pushd /opt/appfiles +if [[ "${ENV:-dev}" != "dev" ]]; then + poetry install --no-root --only main +else + poetry install --no-root +fi +popd + +set +ex + +if [ $# -ne 0 ] + then + echo "Running: ${@}" + ${@} +fi diff --git a/containers/app/scripts/install-deps.sh b/containers/app/scripts/install-deps.sh new file mode 100755 index 0000000..640e466 --- /dev/null +++ b/containers/app/scripts/install-deps.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -ex +poetry config virtualenvs.create false + +pushd /opt/appfiles +if [[ "${ENV:-dev}" != "dev" ]]; then + poetry install --no-root --only main +else + poetry install --no-root +fi +popd +set +ex diff --git a/containers/app/scripts/start.sh b/containers/app/scripts/start.sh new file mode 100644 index 0000000..22eb7e5 --- /dev/null +++ b/containers/app/scripts/start.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + + +python /app/manage.py collectstatic --noinput + +compress_enabled() { +python << END +import sys + +from environ import Env + +env = Env(COMPRESS_ENABLED=(bool, True)) +if env('COMPRESS_ENABLED'): + sys.exit(0) +else: + sys.exit(1) + +END +} + +if compress_enabled; then + # NOTE this command will fail if django-compressor is disabled + python /app/manage.py compress +fi +exec /usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn.workers.UvicornWorker diff --git a/containers/compose.base.yaml b/containers/compose.base.yaml new file mode 100644 index 0000000..8850de3 --- /dev/null +++ b/containers/compose.base.yaml @@ -0,0 +1,39 @@ +version: "3.9" + +services: + app: + hostname: growthplan-app + build: + context: .. + dockerfile: containers/app/Dockerfile + args: + ENV: ${ENV:-dev} + ports: + - "8000:8000" + volumes: + - ../src:/app + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres-admin} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-1kdis94id} + POSTGRES_HOST: ${POSTGRES_HOST:-growthplan-postgres} + POSTGRES_PORT: ${POSTGRES_HOST:-5432} + POSTGRES_DBNAME: ${POSTGRES_DBNAME:-postgres} + ENV: ${ENV:-dev} + depends_on: + - postgres + + postgres: + hostname: growthplan-postgres + build: + context: .. + dockerfile: containers/postgres/Dockerfile + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres-admin} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-1kdis94id} + POSTGRES_HOST: ${POSTGRES_HOST:-growthplan-postgres} + POSTGRES_PORT: ${POSTGRES_HOST:-5432} + volumes: + - growthplandb:/var/lib/postgresql/data + +volumes: + growthplandb: diff --git a/src/growth_plan_linker/migrations/0001_initial.py b/src/growth_plan_linker/migrations/0001_initial.py new file mode 100644 index 0000000..31523d2 --- /dev/null +++ b/src/growth_plan_linker/migrations/0001_initial.py @@ -0,0 +1,53 @@ +# Generated by Django 5.0.2 on 2024-02-20 01:13 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Link', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('periodicity', models.CharField(max_length=50)), + ('times', models.IntegerField()), + ('person_one', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='person_one_links', to=settings.AUTH_USER_MODEL)), + ('person_two', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='person_two_links', to=settings.AUTH_USER_MODEL)), + ('supervisor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supervised_links', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Feedback', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField()), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('link', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedback', to='growth_plan_linker.link')), + ], + ), + migrations.CreateModel( + name='Project', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('supervisors', models.ManyToManyField(related_name='supervised_projects', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Profile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('projects', models.ManyToManyField(related_name='participants', to='growth_plan_linker.project')), + ], + ), + ] diff --git a/src/growth_plan_linker/migrations/__init__.py b/src/growth_plan_linker/migrations/__init__.py new file mode 100644 index 0000000..e69de29