From b1b34aeccd2247c426a781337a7575e56b492ba4 Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Jan 27 2021 11:33:41 +0000
Subject: [PATCH 1/2] OpenShift


---

diff --git a/.s2i/environment b/.s2i/environment
new file mode 100644
index 0000000..4ef93c8
--- /dev/null
+++ b/.s2i/environment
@@ -0,0 +1,2 @@
+UPGRADE_PIP_TO_LATEST=1
+APP_CONFIG=/opt/app-root/src/gunicorn.cfg
diff --git a/Dockerfile b/Dockerfile
index ce8825b..d905190 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,57 @@
-FROM fedoraqa/flask-base:24
+# This will produce an image to be used in Openshift
+# Build should be triggered from repo root like:
+# docker build -f openshift/Dockerfile \
+#              --tag <IMAGE_TAG>
 
-COPY . /usr/src/resultsdb
-WORKDIR /usr/src/resultsdb
-EXPOSE 5001
-ENV DEV true
-RUN pip install -r requirements.txt &&\
-    python run_cli.py init_db
+FROM fedora:32
+LABEL \
+    name="ResultsDB application" \
+    vendor="ResultsDB developers" \
+    license="GPLv2+" \
+    description="ResultsDB is a results store engine for, but not limited to, Fedora QA tools." \
+    usage="https://pagure.io/taskotron/resultsdb/blob/develop/f/openshift/README.md" \
+    build-date=""
+
+USER root
+COPY ./resultsdb.spec /opt/app-root/src/resultsdb/resultsdb.spec
+
+# install dependencies defined in RPM spec file
+RUN dnf -y install findutils rpm-build python3-pip python3-mod_wsgi httpd python3-psycopg2 python3-stomppy \
+    && rpm --query --requires --specfile /opt/app-root/src/resultsdb/resultsdb.spec | xargs -d '\n' dnf -y install
+
+COPY . /opt/app-root/src/resultsdb/
+# install using --no-deps option to ensure nothing comes from PyPi
+RUN pip3 install --no-deps /opt/app-root/src/resultsdb
+
+# fix apache config for container use
+RUN sed -i 's#^WSGISocketPrefix .*#WSGISocketPrefix /tmp/wsgi#' /opt/app-root/src/resultsdb/conf/resultsdb.conf
 
-CMD ["python", "runapp.py"]
+# config files
+RUN install -d /usr/share/resultsdb/conf \
+    && install -p -m 0644 /opt/app-root/src/resultsdb/conf/resultsdb.conf /usr/share/resultsdb/conf/ \
+    && install -p -m 0644 /opt/app-root/src/resultsdb/conf/resultsdb.wsgi /usr/share/resultsdb/ \
+    && install -d /etc/resultsdb \
+    && install -p -m 0644 /opt/app-root/src/resultsdb/conf/resultsdb.conf /etc/httpd/conf.d/
+
+# alembic
+RUN install -p -m 0644 /opt/app-root/src/resultsdb/alembic.ini /usr/share/resultsdb/alembic.ini
+RUN cp -a /opt/app-root/src/resultsdb/resultsdb/alembic /usr/share/resultsdb/alembic
+RUN chmod -R 0755 /usr/share/resultsdb/alembic
+
+# clean up
+RUN rm -rf /opt/app-root/src/resultsdb \
+    && dnf -y autoremove findutils rpm-build \
+    && dnf clean all
+
+# EXPOSE 5001/tcp
+EXPOSE 5001
+CMD ["mod_wsgi-express-3", "start-server", "/usr/share/resultsdb/resultsdb.wsgi", \
+    "--user", "apache", "--group", "apache", \
+    "--port", "5001", "--threads", "5", \
+    "--include-file", "/etc/httpd/conf.d/resultsdb.conf", \
+    "--log-level", "info", \
+    "--log-to-terminal", \
+    "--access-log", \
+    "--startup-log" \
+]
+USER 1001:0
diff --git a/gunicorn.cfg b/gunicorn.cfg
new file mode 100644
index 0000000..099d7e2
--- /dev/null
+++ b/gunicorn.cfg
@@ -0,0 +1,5 @@
+import logging
+import sys
+
+gunicorn_logger = logging.getLogger('gunicorn.error')
+gunicorn_logger.addHandler(logging.StreamHandler(sys.stdout))
diff --git a/requirements.txt b/requirements.txt
index 3e80d51..64a0848 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -21,4 +21,7 @@ SQLAlchemy >= 0.9.8
 # Test suite requirements
 pytest >= 2.4.2
 pytest-cov >= 1.6
+
+# s2i requirements
 psycopg2
+gunicorn
diff --git a/resultsdb/__init__.py b/resultsdb/__init__.py
index 7313e1f..f42e535 100644
--- a/resultsdb/__init__.py
+++ b/resultsdb/__init__.py
@@ -19,6 +19,7 @@
 #   Ralph Bean <rbean@redhat.com>
 
 from resultsdb import proxy
+from . import config
 
 import flask
 from flask import Flask
@@ -67,11 +68,13 @@ def jsonify_with_jsonp(*args, **kwargs):
 
 flask.jsonify = jsonify_with_jsonp
 
+openshift = os.getenv('OPENSHIFT_PROD')
+
 # Load default config, then override that with a config file
 if os.getenv('DEV') == 'true':
     default_config_obj = 'resultsdb.config.DevelopmentConfig'
     default_config_file = os.getcwd() + '/conf/settings.py'
-elif os.getenv('TEST') == 'true':
+elif os.getenv('TEST') == 'true' or openshift == "0":
     default_config_obj = 'resultsdb.config.TestingConfig'
     default_config_file = os.getcwd() + '/conf/settings.py'
 else:
@@ -80,6 +83,9 @@ else:
 
 app.config.from_object(default_config_obj)
 
+if openshift:
+    config.openshift_config(app.config, openshift)
+
 config_file = os.environ.get('RESULTSDB_CONFIG', default_config_file)
 
 if os.path.exists(config_file):
diff --git a/resultsdb/config.py b/resultsdb/config.py
index 768e5f1..b436624 100644
--- a/resultsdb/config.py
+++ b/resultsdb/config.py
@@ -17,8 +17,9 @@
 # Authors:
 #   Josef Skladanka <jskladan@redhat.com>
 #   Ralph Bean <rbean@redhat.com>
-import os
 
+import os
+import sys
 
 class Config(object):
     DEBUG = True
@@ -116,3 +117,32 @@ class TestingConfig(Config):
     ADDITIONAL_RESULT_OUTCOMES = ('AMAZING',)
     MESSAGE_BUS_PLUGIN = 'dummy'
     MESSAGE_BUS_KWARGS = {}
+
+def openshift_config(config_object, openshift_production):
+    # First, get db details from env
+    try:
+        config_object["SQLALCHEMY_DATABASE_URI"] = "postgresql+psycopg2://%s:%s@%s:%s/%s" % (
+            os.environ["POSTGRESQL_USER"],
+            os.environ["POSTGRESQL_PASSWORD"],
+            os.environ["POSTGRESQL_SERVICE_HOST"],
+            os.environ["POSTGRESQL_SERVICE_PORT"],
+            os.environ["POSTGRESQL_DATABASE"]
+        )
+        config_object["SECRET_KEY"] = os.environ["SECRET_KEY"]
+    except(KeyError):
+        print("OpenShift mode enabled but required values couldn't be fetched. "
+              "Check, if you have these variables defined in you env: "
+              "(POSTGRESQL_[USER, PASSWORD, DATABASE, SERVICE_HOST, SERVICE_PORT], SECRET_KEY)", file=sys.stderr)
+        sys.exit(1)
+
+    # Nuke out messaging, we don't support this in OpenShift mode
+    # Inject settings.py and disable OpenShift mode if you need this
+    config_object["MESSAGE_BUS_PLUGIN"] = 'dummy'
+    config_object["MESSAGE_BUS_KWARGS"] = {}
+
+    if os.getenv("MESSAGE_BUS_PLUGIN") or os.getenv("MESSAGE_BUS_KWARGS"):
+        print("It appears you've tried to set up messaging in OpenShift mode.")
+        print("This is not supported, you need to inject setting.py and disable OpenShift mode if you need messaging.")
+
+    # Danger zone, keep this False out in the wild, always
+    config_object["SHOW_DB_URI"] = False
diff --git a/wsgi.py b/wsgi.py
new file mode 100644
index 0000000..bc863f7
--- /dev/null
+++ b/wsgi.py
@@ -0,0 +1 @@
+from resultsdb import app as application

From 85f3ee724177dffe2f7e8f0a970b449fea2d51cd Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Jan 27 2021 11:53:18 +0000
Subject: [PATCH 2/2] PR Review


---

diff --git a/Dockerfile b/Dockerfile
index d905190..8db44a3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,7 +3,7 @@
 # docker build -f openshift/Dockerfile \
 #              --tag <IMAGE_TAG>
 
-FROM fedora:32
+FROM registry.fedoraproject.org/fedora:33
 LABEL \
     name="ResultsDB application" \
     vendor="ResultsDB developers" \
diff --git a/resultsdb/__init__.py b/resultsdb/__init__.py
index f42e535..d3be913 100644
--- a/resultsdb/__init__.py
+++ b/resultsdb/__init__.py
@@ -68,6 +68,11 @@ def jsonify_with_jsonp(*args, **kwargs):
 
 flask.jsonify = jsonify_with_jsonp
 
+# Checks for env variable OPENSHIFT_PROD to trigger OpenShift codepath on init
+# The main difference is that settings will be queried from env (check config.openshift_config())
+# Possible values are:
+# "1" - OpenShift production deployment
+# "0" - OpenShift testing deployment
 openshift = os.getenv('OPENSHIFT_PROD')
 
 # Load default config, then override that with a config file
diff --git a/resultsdb/config.py b/resultsdb/config.py
index b436624..43d49cd 100644
--- a/resultsdb/config.py
+++ b/resultsdb/config.py
@@ -21,6 +21,7 @@
 import os
 import sys
 
+
 class Config(object):
     DEBUG = True
     PRODUCTION = False
@@ -118,6 +119,7 @@ class TestingConfig(Config):
     MESSAGE_BUS_PLUGIN = 'dummy'
     MESSAGE_BUS_KWARGS = {}
 
+
 def openshift_config(config_object, openshift_production):
     # First, get db details from env
     try:
@@ -129,10 +131,11 @@ def openshift_config(config_object, openshift_production):
             os.environ["POSTGRESQL_DATABASE"]
         )
         config_object["SECRET_KEY"] = os.environ["SECRET_KEY"]
-    except(KeyError):
+    except KeyError:
         print("OpenShift mode enabled but required values couldn't be fetched. "
               "Check, if you have these variables defined in you env: "
-              "(POSTGRESQL_[USER, PASSWORD, DATABASE, SERVICE_HOST, SERVICE_PORT], SECRET_KEY)", file=sys.stderr)
+              "(POSTGRESQL_[USER, PASSWORD, DATABASE, SERVICE_HOST, SERVICE_PORT], "
+              "SECRET_KEY)", file=sys.stderr)
         sys.exit(1)
 
     # Nuke out messaging, we don't support this in OpenShift mode
@@ -142,7 +145,8 @@ def openshift_config(config_object, openshift_production):
 
     if os.getenv("MESSAGE_BUS_PLUGIN") or os.getenv("MESSAGE_BUS_KWARGS"):
         print("It appears you've tried to set up messaging in OpenShift mode.")
-        print("This is not supported, you need to inject setting.py and disable OpenShift mode if you need messaging.")
+        print("This is not supported, you need to inject setting.py and disable "
+              "OpenShift mode if you need messaging.")
 
     # Danger zone, keep this False out in the wild, always
     config_object["SHOW_DB_URI"] = False

