From ef37dab5068134aa7caef213635671719ed167b7 Mon Sep 17 00:00:00 2001
From: Ivan Procaccini <ivanprocaccini905@gmail.com>
Date: Tue, 30 Aug 2022 15:24:09 +0100
Subject: [PATCH] Build: Add production compose, nginx files and pipeline
 deployment jobs

---
 .gitlab-ci.yml     | 61 +++++++++++++++++++++++++++++++++++++++++++++-
 app/config.py      |  8 +++---
 docker-compose.yml | 29 ++++++++++++++++++++++
 nginx/Dockerfile   | 12 +++++++++
 nginx/ssl.conf     | 39 +++++++++++++++++++++++++++++
 5 files changed, 143 insertions(+), 6 deletions(-)
 create mode 100644 docker-compose.yml
 create mode 100644 nginx/Dockerfile
 create mode 100644 nginx/ssl.conf

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 94d4c6a..caf5b58 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,7 @@ stages:
   - lint
   - test
   - build
+	- deploy
 
 before_script:
   - apt-get update
@@ -65,7 +66,40 @@ unit_test:
         coverage_format: cobertura
         path: coverage.xml
 
-build_image:
+build nginx image:
+  image: docker:latest
+  stage: building
+  interruptible: true
+  except:
+    changes:
+      - "**/README.md"
+  only:
+    - rewrite
+  variables:
+    DOCKER_TLS_CERTDIR: "/certs"
+    GIT_SUBMODULE_STRATEGY: recursive
+    CERTIFICATE: $CERT_CHAIN_PEM
+    PRIVATE_KEY: $CERT_PRIV_KEY
+  tags:
+    - docker
+  services:
+    - docker:dind
+  before_script: []
+  script:
+    - >
+      docker build
+      --tag $CI_REGISTRY_IMAGE-nginx:latest
+      --build-arg certificate="$CERTIFICATE"
+      --build-arg private_key="$PRIVATE_KEY"
+      --cache-from $CI_REGISTRY_IMAGE-nginx:latest
+      --file nginx/Dockerfile
+      .
+		- docker tag $CI_REGISTRY_IMAGE-nginx:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME-nginx
+    - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
+    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME-nginx
+
+
+build app image:
   stage: build
   except:
     refs:
@@ -92,3 +126,28 @@ build_image:
     - docker tag $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
     - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
     - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
+
+
+deploy prod:
+  stage: deploy
+  variables:
+    SECRET_KEY: $SECRET_KEY
+    POSTGRES_DB: $POSTGRES_DB
+    POSTGRES_USER: $POSTGRES_USER
+    POSTGRES_PASSWORD: $POSTGRES_PASSWORD
+  except:
+    changes:
+      - README.md
+  only:
+    - master
+  before_script: []
+  script:
+    - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
+    - docker stack deploy -c docker-compose.yml project-allocator --with-registry-auth
+    - docker exec $(docker ps -qf "name=web" | head -n1) flask db upgrade
+  tags:
+    - deploy-prod
+  environment:
+    name: production
+    url: https://project-allocator.doc.ic.ac.uk
+
diff --git a/app/config.py b/app/config.py
index baa96da..2cdd176 100644
--- a/app/config.py
+++ b/app/config.py
@@ -6,7 +6,6 @@ class BaseConfig:
 
     # Secret Keys ===============================================
     SECRET_KEY = os.environ.get("SECRET_KEY", "dev_secret_key")
-    WTF_CSRF_SECRET_KEY = os.environ.get("WTF_CSRF_SECRET_KEY", "dev_wtf_secret_key")
 
     # LDAP Service ==============================================
     LDAP_URL = "ldaps://ldaps-vip.cc.ic.ac.uk:636"
@@ -14,17 +13,16 @@ class BaseConfig:
 
     # Database ===================================================
     SQLALCHEMY_TRACK_MODIFICATIONS = True
+    SQLALCHEMY_DATABASE_URI = os.environ.get("DB_OVERRIDE", "sqlite:///dev.db")
 
 
 class DevConfig(BaseConfig):
-    SQLALCHEMY_DATABASE_URI = os.environ.get("DB_OVERRIDE", "sqlite:///dev.db")
     DEBUG = True
 
 
 class StagingConfig(BaseConfig):
-    SQLALCHEMY_DATABASE_URI = "sqlite:///staging.db"
+    pass
 
 
 class ProductionConfig(BaseConfig):
-    # This one should be changed to a postgres db
-    SQLALCHEMY_DATABASE_URI = "sqlite:///production.db"
+    SQLALCHEMY_DATABASE_URI = os.environ["DB_URL"]
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..37a68e4
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,29 @@
+version: "3.9"
+
+services:
+  db:
+    image: postgres
+    environment:
+      - POSTGRES_USER
+      - POSTGRES_PASSWORD
+      - POSTGRES_DB
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+  web:
+    image: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}
+    environment:
+      - ENV=production
+      - SECRET_KEY
+      - DB_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@db/$POSTGRES_DB
+    depends_on:
+      - db
+  reverse_proxy:
+    image: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}-nginx
+    ports:
+      - "80:80"
+      - "443:443"
+    depends_on:
+      - web
+
+volumes:
+  postgres_data:
diff --git a/nginx/Dockerfile b/nginx/Dockerfile
new file mode 100644
index 0000000..03a50f5
--- /dev/null
+++ b/nginx/Dockerfile
@@ -0,0 +1,12 @@
+FROM nginx
+ARG certificate
+ARG private_key
+
+# Copy configuration
+COPY ./nginx/ssl.conf /etc/nginx/conf.d/project-allocator.doc.ic.ac.uk.conf
+RUN mkdir /etc/nginx/certs
+RUN echo $certificate | sed 's/ CERTIFICATE/xCERTIFICATE/g;s/ /\n/g;s/xCERTIFICATE/ CERTIFICATE/g' > /etc/nginx/certs/fullchain.pem
+RUN echo $private_key | sed 's/ PRIVATE KEY/xPRIVATExKEY/g;s/ /\n/g;s/xPRIVATExKEY/ PRIVATE KEY/g' > /etc/nginx/certs/privkey.key
+
+
+
diff --git a/nginx/ssl.conf b/nginx/ssl.conf
new file mode 100644
index 0000000..5aebbdb
--- /dev/null
+++ b/nginx/ssl.conf
@@ -0,0 +1,39 @@
+# /etc/nginx/sites-available/project-allocator.doc.ic.ac.uk
+
+error_log stderr;
+access_log /dev/stdout;
+proxy_connect_timeout 500;
+proxy_send_timeout    500;
+proxy_read_timeout    500;
+send_timeout          500;
+
+server {
+    		listen 80;
+    		server_name project-allocator.doc.ic.ac.uk;
+    		return 301 https://$server_name$request_uri;
+}
+
+server {
+    		listen 443 ssl;
+    		server_name project-allocator.doc.ic.ac.uk;
+
+    		client_max_body_size 200M;
+
+    		ssl_certificate /etc/nginx/certs/fullchain.pem;
+    		ssl_certificate_key /etc/nginx/certs/privkey.key;
+    		ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+    		ssl_ciphers HIGH:!aNULL:!MD5;
+    		ssl_prefer_server_ciphers on;
+
+
+    location / {
+            # Pass the request to Gunicorn
+            proxy_pass http://127.0.0.1:8000;
+
+            # Set HTTP headers st our app knows where request came from
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+                  add_header X-Frame-Options "DENY";
+    }
+}
-- 
GitLab