From ed14900915eaac424881c282c95fb0c38e90773a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 25 May 2020 08:33:36 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=8A=20Add=20consistent=20errors=20for?= =?UTF-8?q?=20env=20vars=20not=20set=20(#200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {{cookiecutter.project_slug}}/README.md | 12 +- .../docker-compose.override.yml | 22 ++-- .../docker-compose.yml | 110 +++++++++--------- .../scripts/build-push.sh | 2 +- .../scripts/build.sh | 2 +- .../scripts/deploy.sh | 10 +- 6 files changed, 80 insertions(+), 78 deletions(-) diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md index beff9d1dac1..5594dd546db 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -489,10 +489,10 @@ services: deploy: placement: constraints: - - node.labels.${STACK_NAME}.app-db-data == true + - node.labels.${STACK_NAME?Variable not set}.app-db-data == true ``` -note the `${STACK_NAME}`. In the script `./scripts/deploy.sh`, the `docker-compose.yml` would be converted, and saved to a file `docker-stack.yml` containing: +note the `${STACK_NAME?Variable not set}`. In the script `./scripts/deploy.sh`, the `docker-compose.yml` would be converted, and saved to a file `docker-stack.yml` containing: ```yaml version: '3' @@ -506,6 +506,8 @@ services: - node.labels.{{cookiecutter.docker_swarm_stack_name_main}}.app-db-data == true ``` +**Note**: The `${STACK_NAME?Variable not set}` means "use the environment variable `STACK_NAME`, but if it is not set, show an error `Variable not set`". + If you add more volumes to your stack, you need to make sure you add the corresponding constraints to the services that use that named volume. Then you have to create those labels in some nodes in your Docker Swarm mode cluster. You can use `docker-auto-labels` to do it automatically. @@ -632,10 +634,10 @@ You can do the process by hand based on those same scripts if you wanted. The ge ```bash # Use the environment variables passed to this script, as TAG and FRONTEND_ENV # And re-create those variables as environment variables for the next command -TAG=${TAG} \ +TAG=${TAG?Variable not set} \ # Set the environment variable FRONTEND_ENV to the same value passed to this script with # a default value of "production" if nothing else was passed -FRONTEND_ENV=${FRONTEND_ENV-production} \ +FRONTEND_ENV=${FRONTEND_ENV-production?Variable not set} \ # The actual comand that does the work: docker-compose docker-compose \ # Pass the file that should be used, setting explicitly docker-compose.yml avoids the @@ -653,7 +655,7 @@ config > docker-stack.yml docker-auto-labels docker-stack.yml # Now this command uses that same file to deploy it -docker stack deploy -c docker-stack.yml --with-registry-auth "${STACK_NAME}" +docker stack deploy -c docker-stack.yml --with-registry-auth "${STACK_NAME?Variable not set}" ``` ### Continuous Integration / Continuous Delivery diff --git a/{{cookiecutter.project_slug}}/docker-compose.override.yml b/{{cookiecutter.project_slug}}/docker-compose.override.yml index 795caa2894c..cb9b60950f1 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.override.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.override.yml @@ -10,7 +10,7 @@ services: - --providers.docker # Add a constraint to only use services with the label for this stack # from the env var TRAEFIK_TAG - - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `${TRAEFIK_TAG}`) + - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `${TRAEFIK_TAG?Variable not set}`) # Do not expose all Docker services, only the ones explicitly exposed - --providers.docker.exposedbydefault=false # Disable Docker Swarm mode for local development @@ -25,8 +25,8 @@ services: - --api.insecure=true labels: - traefik.enable=true - - traefik.http.routers.${STACK_NAME}-traefik-public-http.rule=Host(`${DOMAIN}`) - - traefik.http.services.${STACK_NAME}-traefik-public.loadbalancer.server.port=80 + - traefik.http.routers.${STACK_NAME?Variable not set}-traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`) + - traefik.http.services.${STACK_NAME?Variable not set}-traefik-public.loadbalancer.server.port=80 pgadmin: ports: @@ -43,7 +43,7 @@ services: - ./backend/app:/app environment: - JUPYTER=jupyter lab --ip=0.0.0.0 --allow-root --NotebookApp.custom_display_url=http://127.0.0.1:8888 - - SERVER_HOST=http://${DOMAIN} + - SERVER_HOST=http://${DOMAIN?Variable not set} build: context: ./backend dockerfile: backend.dockerfile @@ -54,9 +54,9 @@ services: command: /start-reload.sh labels: - traefik.enable=true - - traefik.constraint-label-stack=${TRAEFIK_TAG} - - traefik.http.routers.${STACK_NAME}-backend-http.rule=PathPrefix(`/api`) || PathPrefix(`/docs`) || PathPrefix(`/redoc`) - - traefik.http.services.${STACK_NAME}-backend.loadbalancer.server.port=80 + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.rule=PathPrefix(`/api`) || PathPrefix(`/docs`) || PathPrefix(`/redoc`) + - traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=80 celeryworker: volumes: @@ -64,7 +64,7 @@ services: environment: - RUN=celery worker -A app.worker -l info -Q main-queue -c 1 - JUPYTER=jupyter lab --ip=0.0.0.0 --allow-root --NotebookApp.custom_display_url=http://127.0.0.1:8888 - - SERVER_HOST=http://${DOMAIN} + - SERVER_HOST=http://${DOMAIN?Variable not set} build: context: ./backend dockerfile: celeryworker.dockerfile @@ -79,9 +79,9 @@ services: FRONTEND_ENV: dev labels: - traefik.enable=true - - traefik.constraint-label-stack=${TRAEFIK_TAG} - - traefik.http.routers.${STACK_NAME}-frontend-http.rule=PathPrefix(`/`) - - traefik.http.services.${STACK_NAME}-frontend.loadbalancer.server.port=80 + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=PathPrefix(`/`) + - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 networks: traefik-public: diff --git a/{{cookiecutter.project_slug}}/docker-compose.yml b/{{cookiecutter.project_slug}}/docker-compose.yml index f2983889995..fcc48f529d0 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.yml @@ -4,7 +4,7 @@ services: proxy: image: traefik:v2.2 networks: - - ${TRAEFIK_PUBLIC_NETWORK} + - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} - default volumes: - /var/run/docker.sock:/var/run/docker.sock @@ -13,7 +13,7 @@ services: - --providers.docker # Add a constraint to only use services with the label for this stack # from the env var TRAEFIK_TAG - - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `${TRAEFIK_TAG}`) + - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `${TRAEFIK_TAG?Variable not set}`) # Do not expose all Docker services, only the ones explicitly exposed - --providers.docker.exposedbydefault=false # Enable Docker Swarm mode @@ -32,41 +32,41 @@ services: # Enable Traefik for this service, to make it available in the public network - traefik.enable=true # Use the traefik-public network (declared below) - - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK} + - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK?Variable not set} # Use the custom label "traefik.constraint-label=traefik-public" # This public Traefik will only use services with this label - - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG} + - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG?Variable not set} # traefik-http set up only to use the middleware to redirect to https - - traefik.http.middlewares.${STACK_NAME}-https-redirect.redirectscheme.scheme=https - - traefik.http.middlewares.${STACK_NAME}-https-redirect.redirectscheme.permanent=true + - traefik.http.middlewares.${STACK_NAME?Variable not set}-https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.${STACK_NAME?Variable not set}-https-redirect.redirectscheme.permanent=true # Handle host with and without "www" to redirect to only one of them # Uses environment variable DOMAIN # To disable www redirection remove the Host() you want to discard, here and # below for HTTPS - - traefik.http.routers.${STACK_NAME}-proxy-http.rule=Host(`${DOMAIN}`) || Host(`www.${DOMAIN}`) - - traefik.http.routers.${STACK_NAME}-proxy-http.entrypoints=http + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.entrypoints=http # traefik-https the actual router using HTTPS - - traefik.http.routers.${STACK_NAME}-proxy-https.rule=Host(`${DOMAIN}`) || Host(`www.${DOMAIN}`) - - traefik.http.routers.${STACK_NAME}-proxy-https.entrypoints=https - - traefik.http.routers.${STACK_NAME}-proxy-https.tls=true + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.entrypoints=https + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.tls=true # Use the "le" (Let's Encrypt) resolver created below - - traefik.http.routers.${STACK_NAME}-proxy-https.tls.certresolver=le + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.tls.certresolver=le # Define the port inside of the Docker service to use - - traefik.http.services.${STACK_NAME}-proxy.loadbalancer.server.port=80 + - traefik.http.services.${STACK_NAME?Variable not set}-proxy.loadbalancer.server.port=80 # Handle domain with and without "www" to redirect to only one # To disable www redirection remove the next line - - traefik.http.middlewares.${STACK_NAME}-www-redirect.redirectregex.regex=^https?://(www.)?(${DOMAIN})/(.*) + - traefik.http.middlewares.${STACK_NAME?Variable not set}-www-redirect.redirectregex.regex=^https?://(www.)?(${DOMAIN?Variable not set})/(.*) # Redirect a domain with www to non-www # To disable it remove the next line - - traefik.http.middlewares.${STACK_NAME}-www-redirect.redirectregex.replacement=https://${DOMAIN}/$${3} + - traefik.http.middlewares.${STACK_NAME?Variable not set}-www-redirect.redirectregex.replacement=https://${DOMAIN?Variable not set}/$${3} # Redirect a domain without www to www # To enable it remove the previous line and uncomment the next # - traefik.http.middlewares.${STACK_NAME}-www-redirect.redirectregex.replacement=https://www.${DOMAIN}/$${3} # Middleware to redirect www, to disable it remove the next line - - traefik.http.routers.${STACK_NAME}-proxy-https.middlewares=${STACK_NAME}-www-redirect + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.middlewares=${STACK_NAME?Variable not set}-www-redirect # Middleware to redirect www, and redirect HTTP to HTTPS - # to disable www redirection remove the section: ${STACK_NAME}-www-redirect, - - traefik.http.routers.${STACK_NAME}-proxy-http.middlewares=${STACK_NAME}-www-redirect,${STACK_NAME}-https-redirect + # to disable www redirection remove the section: ${STACK_NAME?Variable not set}-www-redirect, + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.middlewares=${STACK_NAME?Variable not set}-www-redirect,${STACK_NAME?Variable not set}-https-redirect db: image: postgres:12 @@ -79,12 +79,12 @@ services: deploy: placement: constraints: - - node.labels.${STACK_NAME}.app-db-data == true + - node.labels.${STACK_NAME?Variable not set}.app-db-data == true pgadmin: image: dpage/pgadmin4 networks: - - ${TRAEFIK_PUBLIC_NETWORK} + - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} - default depends_on: - db @@ -93,16 +93,16 @@ services: deploy: labels: - traefik.enable=true - - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK} - - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG} - - traefik.http.routers.${STACK_NAME}-pgadmin-http.rule=Host(`pgadmin.${DOMAIN}`) - - traefik.http.routers.${STACK_NAME}-pgadmin-http.entrypoints=http - - traefik.http.routers.${STACK_NAME}-pgadmin-http.middlewares=${STACK_NAME}-https-redirect - - traefik.http.routers.${STACK_NAME}-pgadmin-https.rule=Host(`pgadmin.${DOMAIN}`) - - traefik.http.routers.${STACK_NAME}-pgadmin-https.entrypoints=https - - traefik.http.routers.${STACK_NAME}-pgadmin-https.tls=true - - traefik.http.routers.${STACK_NAME}-pgadmin-https.tls.certresolver=le - - traefik.http.services.${STACK_NAME}-pgadmin.loadbalancer.server.port=5050 + - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK?Variable not set} + - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-http.rule=Host(`pgadmin.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-http.entrypoints=http + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-http.middlewares=${STACK_NAME?Variable not set}-https-redirect + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.rule=Host(`pgadmin.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.entrypoints=https + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.tls=true + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.tls.certresolver=le + - traefik.http.services.${STACK_NAME?Variable not set}-pgadmin.loadbalancer.server.port=5050 queue: image: rabbitmq:3 @@ -114,7 +114,7 @@ services: flower: image: mher/flower networks: - - ${TRAEFIK_PUBLIC_NETWORK} + - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} - default env_file: - .env @@ -126,26 +126,26 @@ services: deploy: labels: - traefik.enable=true - - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK} - - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG} - - traefik.http.routers.${STACK_NAME}-flower-http.rule=Host(`flower.${DOMAIN}`) - - traefik.http.routers.${STACK_NAME}-flower-http.entrypoints=http - - traefik.http.routers.${STACK_NAME}-flower-http.middlewares=${STACK_NAME}-https-redirect - - traefik.http.routers.${STACK_NAME}-flower-https.rule=Host(`flower.${DOMAIN}`) - - traefik.http.routers.${STACK_NAME}-flower-https.entrypoints=https - - traefik.http.routers.${STACK_NAME}-flower-https.tls=true - - traefik.http.routers.${STACK_NAME}-flower-https.tls.certresolver=le - - traefik.http.services.${STACK_NAME}-flower.loadbalancer.server.port=5555 + - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK?Variable not set} + - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-http.rule=Host(`flower.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-http.entrypoints=http + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-http.middlewares=${STACK_NAME?Variable not set}-https-redirect + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.rule=Host(`flower.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.entrypoints=https + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.tls=true + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.tls.certresolver=le + - traefik.http.services.${STACK_NAME?Variable not set}-flower.loadbalancer.server.port=5555 backend: - image: '${DOCKER_IMAGE_BACKEND}:${TAG-latest}' + image: '${DOCKER_IMAGE_BACKEND?Variable not set}:${TAG-latest}' depends_on: - db env_file: - .env environment: - - SERVER_NAME=${DOMAIN} - - SERVER_HOST=https://${DOMAIN} + - SERVER_NAME=${DOMAIN?Variable not set} + - SERVER_HOST=https://${DOMAIN?Variable not set} # Allow explicit env var override for tests - SMTP_HOST=${SMTP_HOST} build: @@ -156,22 +156,22 @@ services: deploy: labels: - traefik.enable=true - - traefik.constraint-label-stack=${TRAEFIK_TAG} - - traefik.http.routers.${STACK_NAME}-backend-http.rule=PathPrefix(`/api`) || PathPrefix(`/docs`) || PathPrefix(`/redoc`) - - traefik.http.services.${STACK_NAME}-backend.loadbalancer.server.port=80 + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.rule=PathPrefix(`/api`) || PathPrefix(`/docs`) || PathPrefix(`/redoc`) + - traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=80 celeryworker: - image: '${DOCKER_IMAGE_CELERYWORKER}:${TAG-latest}' + image: '${DOCKER_IMAGE_CELERYWORKER?Variable not set}:${TAG-latest}' depends_on: - db - queue env_file: - .env environment: - - SERVER_NAME=${DOMAIN} - - SERVER_HOST=https://${DOMAIN} + - SERVER_NAME=${DOMAIN?Variable not set} + - SERVER_HOST=https://${DOMAIN?Variable not set} # Allow explicit env var override for tests - - SMTP_HOST=${SMTP_HOST} + - SMTP_HOST=${SMTP_HOST?Variable not set} build: context: ./backend dockerfile: celeryworker.dockerfile @@ -179,7 +179,7 @@ services: INSTALL_DEV: ${INSTALL_DEV-false} frontend: - image: '${DOCKER_IMAGE_FRONTEND}:${TAG-latest}' + image: '${DOCKER_IMAGE_FRONTEND?Variable not set}:${TAG-latest}' build: context: ./frontend args: @@ -187,9 +187,9 @@ services: deploy: labels: - traefik.enable=true - - traefik.constraint-label-stack=${TRAEFIK_TAG} - - traefik.http.routers.${STACK_NAME}-frontend-http.rule=PathPrefix(`/`) - - traefik.http.services.${STACK_NAME}-frontend.loadbalancer.server.port=80 + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=PathPrefix(`/`) + - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 volumes: app-db-data: diff --git a/{{cookiecutter.project_slug}}/scripts/build-push.sh b/{{cookiecutter.project_slug}}/scripts/build-push.sh index fce8e34f28f..3fa3aa7e6ba 100644 --- a/{{cookiecutter.project_slug}}/scripts/build-push.sh +++ b/{{cookiecutter.project_slug}}/scripts/build-push.sh @@ -3,7 +3,7 @@ # Exit in case of error set -e -TAG=${TAG} \ +TAG=${TAG?Variable not set} \ FRONTEND_ENV=${FRONTEND_ENV-production} \ sh ./scripts/build.sh diff --git a/{{cookiecutter.project_slug}}/scripts/build.sh b/{{cookiecutter.project_slug}}/scripts/build.sh index 8ac32fb3f66..21528c538eb 100644 --- a/{{cookiecutter.project_slug}}/scripts/build.sh +++ b/{{cookiecutter.project_slug}}/scripts/build.sh @@ -3,7 +3,7 @@ # Exit in case of error set -e -TAG=${TAG} \ +TAG=${TAG?Variable not set} \ FRONTEND_ENV=${FRONTEND_ENV-production} \ docker-compose \ -f docker-compose.yml \ diff --git a/{{cookiecutter.project_slug}}/scripts/deploy.sh b/{{cookiecutter.project_slug}}/scripts/deploy.sh index 95d043e80fc..55a86ee94ce 100644 --- a/{{cookiecutter.project_slug}}/scripts/deploy.sh +++ b/{{cookiecutter.project_slug}}/scripts/deploy.sh @@ -3,14 +3,14 @@ # Exit in case of error set -e -DOMAIN=${DOMAIN} \ -TRAEFIK_TAG=${TRAEFIK_TAG} \ -STACK_NAME=${STACK_NAME} \ -TAG=${TAG} \ +DOMAIN=${DOMAIN?Variable not set} \ +TRAEFIK_TAG=${TRAEFIK_TAG?Variable not set} \ +STACK_NAME=${STACK_NAME?Variable not set} \ +TAG=${TAG?Variable not set} \ docker-compose \ -f docker-compose.yml \ config > docker-stack.yml docker-auto-labels docker-stack.yml -docker stack deploy -c docker-stack.yml --with-registry-auth "${STACK_NAME}" +docker stack deploy -c docker-stack.yml --with-registry-auth "${STACK_NAME?Variable not set}"