├── .gitignore ├── README.md ├── conf └── reload.py ├── manage.py ├── openshift ├── scripts │ └── run-in-container.sh └── templates │ ├── django-postgresql-persistent.json │ ├── django-postgresql.json │ └── django.json ├── project ├── __init__.py ├── database.py ├── settings.py └── urls.py ├── requirements.txt ├── welcome ├── __init__.py ├── admin.py ├── database.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── templates │ └── welcome │ │ └── index.html ├── tests.py └── views.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Django 2 | db.sqlite3 3 | staticfiles/ 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | 57 | # Sphinx documentation 58 | docs/_build/ 59 | 60 | # PyBuilder 61 | target/ 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Openshift quickstart: Django 2 | 3 | This is a [Django](http://www.djangoproject.com) project that you can use as the starting point to develop your own and deploy it on an [OpenShift](https://github.com/openshift/origin) cluster. 4 | 5 | The steps in this document assume that you have access to an OpenShift deployment that you can deploy applications on. 6 | 7 | ## What has been done for you 8 | 9 | This is a minimal Django 1.8 project. It was created with these steps: 10 | 11 | 1. Create a virtualenv 12 | 2. Manually install Django and other dependencies 13 | 3. `pip freeze > requirements.txt` 14 | 4. `django-admin startproject project .` 15 | 3. Update `project/settings.py` to configure `SECRET_KEY`, `DATABASE` and `STATIC_ROOT` entries 16 | 4. `./manage.py startapp welcome`, to create the welcome page's app 17 | 18 | From this initial state you can: 19 | * create new Django apps 20 | * remove the `welcome` app 21 | * rename the Django project 22 | * update settings to suit your needs 23 | * install more Python libraries and add them to the `requirements.txt` file 24 | 25 | 26 | ## Special files in this repository 27 | 28 | Apart from the regular files created by Django (`project/*`, `welcome/*`, `manage.py`), this repository contains: 29 | 30 | ``` 31 | openshift/ - OpenShift-specific files 32 | ├── scripts - helper scripts 33 | └── templates - application templates 34 | 35 | requirements.txt - list of dependencies 36 | ``` 37 | 38 | 39 | ## Local development 40 | 41 | To run this project in your development machine, follow these steps: 42 | 43 | 1. (optional) Create and activate a [virtualenv](https://virtualenv.pypa.io/) (you may want to use [virtualenvwrapper](http://virtualenvwrapper.readthedocs.org/)). 44 | 45 | 2. Fork this repo and clone your fork: 46 | 47 | `git clone https://github.com/openshift/django-ex.git` 48 | 49 | 3. Install dependencies: 50 | 51 | `pip install -r requirements.txt` 52 | 53 | 4. Create a development database: 54 | 55 | `./manage.py migrate` 56 | 57 | 5. If everything is alright, you should be able to start the Django development server: 58 | 59 | `./manage.py runserver` 60 | 61 | 6. Open your browser and go to http://127.0.0.1:8000, you will be greeted with a welcome page. 62 | 63 | 64 | ## Deploying to OpenShift 65 | 66 | To follow the next steps, you need to be logged in to an OpenShift cluster and have an OpenShift project where you can work on. 67 | 68 | 69 | ### Using an application template 70 | 71 | The directory `openshift/templates/` contains OpenShift application templates that you can add to your OpenShift project with: 72 | 73 | oc create -f openshift/templates/.json 74 | 75 | The template `django.json` contains just a minimal set of components to get your Django application into OpenShift. 76 | 77 | The template `django-postgresql.json` contains all of the components from `django.json`, plus a PostgreSQL database service and an Image Stream for the Python base image. For simplicity, the PostgreSQL database in this template uses ephemeral storage and, therefore, is not production ready. 78 | 79 | After adding your templates, you can go to your OpenShift web console, browse to your project and click the create button. Create a new app from one of the templates that you have just added. 80 | 81 | Adjust the parameter values to suit your configuration. Most times you can just accept the default values, however you will probably want to set the `GIT_REPOSITORY` parameter to point to your fork and the `DATABASE_*` parameters to match your database configuration. 82 | 83 | Alternatively, you can use the command line to create your new app, assuming your OpenShift deployment has the default set of ImageStreams defined. Instructions for installing the default ImageStreams are available [here](https://docs.openshift.org/latest/install_config/imagestreams_templates.html). If you are defining the set of ImageStreams now, remember to pass in the proper cluster-admin credentials and to create the ImageStreams in the 'openshift' namespace: 84 | 85 | oc new-app openshift/templates/django.json -p SOURCE_REPOSITORY_URL= 86 | 87 | Your application will be built and deployed automatically. If that doesn't happen, you can debug your build: 88 | 89 | oc get builds 90 | # take build name from the command above 91 | oc logs build/ 92 | 93 | And you can see information about your deployment too: 94 | 95 | oc describe dc/django-example 96 | 97 | In the web console, the overview tab shows you a service, by default called "django-example", that encapsulates all pods running your Django application. You can access your application by browsing to the service's IP address and port. You can determine these by running 98 | 99 | oc get svc 100 | 101 | 102 | ### Without an application template 103 | 104 | Templates give you full control of each component of your application. 105 | Sometimes your application is simple enough and you don't want to bother with templates. In that case, you can let OpenShift inspect your source code and create the required components automatically for you: 106 | 107 | ```bash 108 | $ oc new-app centos/python-35-centos7~https://github.com/openshift/django-ex 109 | imageStreams/python-35-centos7 110 | imageStreams/django-ex 111 | buildConfigs/django-ex 112 | deploymentConfigs/django-ex 113 | services/django-ex 114 | A build was created - you can run `oc start-build django-ex` to start it. 115 | Service "django-ex" created at 172.30.16.213 with port mappings 8080. 116 | ``` 117 | 118 | You can access your application by browsing to the service's IP address and port. 119 | 120 | 121 | ## Logs 122 | 123 | By default your Django application is served with gunicorn and configured to output its access log to stderr. 124 | You can look at the combined stdout and stderr of a given pod with this command: 125 | 126 | oc get pods # list all pods in your project 127 | oc logs 128 | 129 | This can be useful to observe the correct functioning of your application. 130 | 131 | 132 | ## Special environment variables 133 | 134 | ### APP_CONFIG 135 | 136 | You can fine tune the gunicorn configuration through the environment variable `APP_CONFIG` that, when set, should point to a config file as documented [here](http://docs.gunicorn.org/en/latest/settings.html). 137 | 138 | ### DJANGO_SECRET_KEY 139 | 140 | When using one of the templates provided in this repository, this environment variable has its value automatically generated. For security purposes, make sure to set this to a random string as documented [here](https://docs.djangoproject.com/en/1.8/ref/settings/#std:setting-SECRET_KEY). 141 | 142 | 143 | ## One-off command execution 144 | 145 | At times you might want to manually execute some command in the context of a running application in OpenShift. 146 | You can drop into a Python shell for debugging, create a new user for the Django Admin interface, or perform any other task. 147 | 148 | You can do all that by using regular CLI commands from OpenShift. 149 | To make it a little more convenient, you can use the script `openshift/scripts/run-in-container.sh` that wraps some calls to `oc`. 150 | In the future, the `oc` CLI tool might incorporate changes 151 | that make this script obsolete. 152 | 153 | Here is how you would run a command in a pod specified by label: 154 | 155 | 1. Inspect the output of the command below to find the name of a pod that matches a given label: 156 | 157 | oc get pods -l 158 | 159 | 2. Open a shell in the pod of your choice. Because of how the images produced 160 | with CentOS and RHEL work currently, we need to wrap commands with `bash` to 161 | enable any Software Collections that may be used (done automatically inside 162 | every bash shell). 163 | 164 | oc exec -p -it -- bash 165 | 166 | 3. Finally, execute any command that you need and exit the shell. 167 | 168 | Related GitHub issues: 169 | 1. https://github.com/GoogleCloudPlatform/kubernetes/issues/8876 170 | 2. https://github.com/openshift/origin/issues/2001 171 | 172 | 173 | The wrapper script combines the steps above into one. You can use it like this: 174 | 175 | ./run-in-container.sh ./manage.py migrate # manually migrate the database 176 | # (done for you as part of the deployment process) 177 | ./run-in-container.sh ./manage.py createsuperuser # create a user to access Django Admin 178 | ./run-in-container.sh ./manage.py shell # open a Python shell in the context of your app 179 | 180 | If your Django pods are labeled with a name other than "django", you can use: 181 | 182 | POD_NAME=name ./run-in-container.sh ./manage.py check 183 | 184 | If there is more than one replica, you can also specify a POD by index: 185 | 186 | POD_INDEX=1 ./run-in-container.sh ./manage.py shell 187 | 188 | Or both together: 189 | 190 | POD_NAME=django-example POD_INDEX=2 ./run-in-container.sh ./manage.py shell 191 | 192 | 193 | ## Data persistence 194 | 195 | You can deploy this application without a configured database in your OpenShift project, in which case Django will use a temporary SQLite database that will live inside your application's container, and persist only until you redeploy your application. 196 | 197 | After each deploy you get a fresh, empty, SQLite database. That is fine for a first contact with OpenShift and perhaps Django, but sooner or later you will want to persist your data across deployments. 198 | 199 | To do that, you should add a properly configured database server or ask your OpenShift administrator to add one for you. Then use `oc env` to update the `DATABASE_*` environment variables in your DeploymentConfig to match your database settings. 200 | 201 | Redeploy your application to have your changes applied, and open the welcome page again to make sure your application is successfully connected to the database server. 202 | 203 | 204 | ## Looking for help 205 | 206 | If you get stuck at some point, or think that this document needs further details or clarification, you can give feedback and look for help using the channels mentioned in [the OpenShift Origin repo](https://github.com/openshift/origin), or by filing an issue. 207 | 208 | 209 | ## License 210 | 211 | This code is dedicated to the public domain to the maximum extent permitted by applicable law, pursuant to [CC0](http://creativecommons.org/publicdomain/zero/1.0/). 212 | -------------------------------------------------------------------------------- /conf/reload.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can set APP_CONFIG to point to this file to enable automatic reloading of 3 | modules. 4 | """ 5 | 6 | reload = True 7 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /openshift/scripts/run-in-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Use this script to run one-off commands inside a container of a pod where your 4 | # Python application code lives in. 5 | 6 | # You can accomplish the same results by using regular commands from OpenShift. 7 | # This script is just wrapping calls to `oc` to make it a little more 8 | # convenient to use. In the future, the `oc` cli tool might incorporate changes 9 | # that make this script obsolete. 10 | 11 | # Related GitHub issues: 12 | # - https://github.com/GoogleCloudPlatform/kubernetes/issues/8876 13 | # - https://github.com/openshift/origin/issues/2001 14 | 15 | 16 | # Usage examples: 17 | # 18 | # ./run-in-container.sh ./manage.py migrate 19 | # ./run-in-container.sh ./manage.py createsuperuser 20 | # ./run-in-container.sh ./manage.py shell 21 | # 22 | # If your Python pods are labeled with a name other than "django", you can use: 23 | # 24 | # POD_NAME=name ./run-in-container.sh ./manage.py check 25 | # 26 | # If there is more than one replica, you can also specify a POD by index: 27 | # 28 | # POD_INDEX=1 ./run-in-container.sh ./manage.py shell 29 | # 30 | # Or both together: 31 | # 32 | # POD_NAME=frontend POD_INDEX=2 ./run-in-container.sh ./manage.py shell 33 | 34 | 35 | # Get name of a currently deployed pod by label and index 36 | POD_INSTANCE_NAME=`oc get pods \ 37 | -l "name=${POD_NAME:-django-frontend}" \ 38 | -t "{{ with index .items ${POD_INDEX:-0} }}{{ .metadata.name }}{{ end }}"` 39 | 40 | # Run command in a container of the specified pod: 41 | oc exec -p "$POD_INSTANCE_NAME" -it -- bash -c "${@:-echo}" 42 | -------------------------------------------------------------------------------- /openshift/templates/django-postgresql-persistent.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Template", 3 | "apiVersion": "v1", 4 | "metadata": { 5 | "name": "django-psql-persistent", 6 | "annotations": { 7 | "openshift.io/display-name": "Django + PostgreSQL (Persistent)", 8 | "description": "An example Django application with a PostgreSQL database. For more information about using this template, including OpenShift considerations, see https://github.com/openshift/django-ex/blob/master/README.md.", 9 | "tags": "quickstart,python,django", 10 | "iconClass": "icon-python", 11 | "template.openshift.io/long-description": "This template defines resources needed to develop a Django based application, including a build configuration, application deployment configuration, and database deployment configuration.", 12 | "template.openshift.io/provider-display-name": "Red Hat, Inc.", 13 | "template.openshift.io/documentation-url": "https://github.com/openshift/django-ex", 14 | "template.openshift.io/support-url": "https://access.redhat.com" 15 | } 16 | }, 17 | "message": "The following service(s) have been created in your project: ${NAME}, ${DATABASE_SERVICE_NAME}.\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/openshift/django-ex/blob/master/README.md.", 18 | "labels": { 19 | "template": "django-psql-persistent" 20 | }, 21 | "objects": [ 22 | { 23 | "kind": "Secret", 24 | "apiVersion": "v1", 25 | "metadata": { 26 | "name": "${NAME}" 27 | }, 28 | "stringData" : { 29 | "database-user" : "${DATABASE_USER}", 30 | "database-password" : "${DATABASE_PASSWORD}", 31 | "django-secret-key" : "${DJANGO_SECRET_KEY}" 32 | } 33 | }, 34 | { 35 | "kind": "Service", 36 | "apiVersion": "v1", 37 | "metadata": { 38 | "name": "${NAME}", 39 | "annotations": { 40 | "description": "Exposes and load balances the application pods", 41 | "service.alpha.openshift.io/dependencies": "[{\"name\": \"${DATABASE_SERVICE_NAME}\", \"kind\": \"Service\"}]" 42 | } 43 | }, 44 | "spec": { 45 | "ports": [ 46 | { 47 | "name": "web", 48 | "port": 8080, 49 | "targetPort": 8080 50 | } 51 | ], 52 | "selector": { 53 | "name": "${NAME}" 54 | } 55 | } 56 | }, 57 | { 58 | "kind": "Route", 59 | "apiVersion": "v1", 60 | "metadata": { 61 | "name": "${NAME}" 62 | }, 63 | "spec": { 64 | "host": "${APPLICATION_DOMAIN}", 65 | "to": { 66 | "kind": "Service", 67 | "name": "${NAME}" 68 | } 69 | } 70 | }, 71 | { 72 | "kind": "ImageStream", 73 | "apiVersion": "v1", 74 | "metadata": { 75 | "name": "${NAME}", 76 | "annotations": { 77 | "description": "Keeps track of changes in the application image" 78 | } 79 | } 80 | }, 81 | { 82 | "kind": "BuildConfig", 83 | "apiVersion": "v1", 84 | "metadata": { 85 | "name": "${NAME}", 86 | "annotations": { 87 | "description": "Defines how to build the application" 88 | } 89 | }, 90 | "spec": { 91 | "source": { 92 | "type": "Git", 93 | "git": { 94 | "uri": "${SOURCE_REPOSITORY_URL}", 95 | "ref": "${SOURCE_REPOSITORY_REF}" 96 | }, 97 | "contextDir": "${CONTEXT_DIR}" 98 | }, 99 | "strategy": { 100 | "type": "Source", 101 | "sourceStrategy": { 102 | "from": { 103 | "kind": "ImageStreamTag", 104 | "namespace": "${NAMESPACE}", 105 | "name": "python:3.5" 106 | }, 107 | "env": [ 108 | { 109 | "name": "PIP_INDEX_URL", 110 | "value": "${PIP_INDEX_URL}" 111 | } 112 | ] 113 | } 114 | }, 115 | "output": { 116 | "to": { 117 | "kind": "ImageStreamTag", 118 | "name": "${NAME}:latest" 119 | } 120 | }, 121 | "triggers": [ 122 | { 123 | "type": "ImageChange" 124 | }, 125 | { 126 | "type": "ConfigChange" 127 | }, 128 | { 129 | "type": "GitHub", 130 | "github": { 131 | "secret": "${GITHUB_WEBHOOK_SECRET}" 132 | } 133 | } 134 | ], 135 | "postCommit": { 136 | "script": "./manage.py test" 137 | } 138 | } 139 | }, 140 | { 141 | "kind": "DeploymentConfig", 142 | "apiVersion": "v1", 143 | "metadata": { 144 | "name": "${NAME}", 145 | "annotations": { 146 | "description": "Defines how to deploy the application server" 147 | } 148 | }, 149 | "spec": { 150 | "strategy": { 151 | "type": "Rolling" 152 | }, 153 | "triggers": [ 154 | { 155 | "type": "ImageChange", 156 | "imageChangeParams": { 157 | "automatic": true, 158 | "containerNames": [ 159 | "django-psql-persistent" 160 | ], 161 | "from": { 162 | "kind": "ImageStreamTag", 163 | "name": "${NAME}:latest" 164 | } 165 | } 166 | }, 167 | { 168 | "type": "ConfigChange" 169 | } 170 | ], 171 | "replicas": 1, 172 | "selector": { 173 | "name": "${NAME}" 174 | }, 175 | "template": { 176 | "metadata": { 177 | "name": "${NAME}", 178 | "labels": { 179 | "name": "${NAME}" 180 | } 181 | }, 182 | "spec": { 183 | "containers": [ 184 | { 185 | "name": "django-psql-persistent", 186 | "image": " ", 187 | "ports": [ 188 | { 189 | "containerPort": 8080 190 | } 191 | ], 192 | "readinessProbe": { 193 | "timeoutSeconds": 3, 194 | "initialDelaySeconds": 3, 195 | "httpGet": { 196 | "path": "/health", 197 | "port": 8080 198 | } 199 | }, 200 | "livenessProbe": { 201 | "timeoutSeconds": 3, 202 | "initialDelaySeconds": 30, 203 | "httpGet": { 204 | "path": "/health", 205 | "port": 8080 206 | } 207 | }, 208 | "env": [ 209 | { 210 | "name": "DATABASE_SERVICE_NAME", 211 | "value": "${DATABASE_SERVICE_NAME}" 212 | }, 213 | { 214 | "name": "DATABASE_ENGINE", 215 | "value": "${DATABASE_ENGINE}" 216 | }, 217 | { 218 | "name": "DATABASE_NAME", 219 | "value": "${DATABASE_NAME}" 220 | }, 221 | { 222 | "name": "DATABASE_USER", 223 | "valueFrom": { 224 | "secretKeyRef" : { 225 | "name" : "${NAME}", 226 | "key" : "database-user" 227 | } 228 | } 229 | }, 230 | { 231 | "name": "DATABASE_PASSWORD", 232 | "valueFrom": { 233 | "secretKeyRef" : { 234 | "name" : "${NAME}", 235 | "key" : "database-password" 236 | } 237 | } 238 | }, 239 | { 240 | "name": "APP_CONFIG", 241 | "value": "${APP_CONFIG}" 242 | }, 243 | { 244 | "name": "DJANGO_SECRET_KEY", 245 | "valueFrom": { 246 | "secretKeyRef" : { 247 | "name" : "${NAME}", 248 | "key" : "django-secret-key" 249 | } 250 | } 251 | } 252 | ], 253 | "resources": { 254 | "limits": { 255 | "memory": "${MEMORY_LIMIT}" 256 | } 257 | } 258 | } 259 | ] 260 | } 261 | } 262 | } 263 | }, 264 | { 265 | "kind": "PersistentVolumeClaim", 266 | "apiVersion": "v1", 267 | "metadata": { 268 | "name": "${DATABASE_SERVICE_NAME}" 269 | }, 270 | "spec": { 271 | "accessModes": [ 272 | "ReadWriteOnce" 273 | ], 274 | "resources": { 275 | "requests": { 276 | "storage": "${VOLUME_CAPACITY}" 277 | } 278 | } 279 | } 280 | }, 281 | { 282 | "kind": "Service", 283 | "apiVersion": "v1", 284 | "metadata": { 285 | "name": "${DATABASE_SERVICE_NAME}", 286 | "annotations": { 287 | "description": "Exposes the database server" 288 | } 289 | }, 290 | "spec": { 291 | "ports": [ 292 | { 293 | "name": "postgresql", 294 | "port": 5432, 295 | "targetPort": 5432 296 | } 297 | ], 298 | "selector": { 299 | "name": "${DATABASE_SERVICE_NAME}" 300 | } 301 | } 302 | }, 303 | { 304 | "kind": "DeploymentConfig", 305 | "apiVersion": "v1", 306 | "metadata": { 307 | "name": "${DATABASE_SERVICE_NAME}", 308 | "annotations": { 309 | "description": "Defines how to deploy the database" 310 | } 311 | }, 312 | "spec": { 313 | "strategy": { 314 | "type": "Recreate" 315 | }, 316 | "triggers": [ 317 | { 318 | "type": "ImageChange", 319 | "imageChangeParams": { 320 | "automatic": true, 321 | "containerNames": [ 322 | "postgresql" 323 | ], 324 | "from": { 325 | "kind": "ImageStreamTag", 326 | "namespace": "${NAMESPACE}", 327 | "name": "postgresql:9.5" 328 | } 329 | } 330 | }, 331 | { 332 | "type": "ConfigChange" 333 | } 334 | ], 335 | "replicas": 1, 336 | "selector": { 337 | "name": "${DATABASE_SERVICE_NAME}" 338 | }, 339 | "template": { 340 | "metadata": { 341 | "name": "${DATABASE_SERVICE_NAME}", 342 | "labels": { 343 | "name": "${DATABASE_SERVICE_NAME}" 344 | } 345 | }, 346 | "spec": { 347 | "volumes": [ 348 | { 349 | "name": "${DATABASE_SERVICE_NAME}-data", 350 | "persistentVolumeClaim": { 351 | "claimName": "${DATABASE_SERVICE_NAME}" 352 | } 353 | } 354 | ], 355 | "containers": [ 356 | { 357 | "name": "postgresql", 358 | "image": " ", 359 | "ports": [ 360 | { 361 | "containerPort": 5432 362 | } 363 | ], 364 | "env": [ 365 | { 366 | "name": "POSTGRESQL_USER", 367 | "valueFrom": { 368 | "secretKeyRef" : { 369 | "name" : "${NAME}", 370 | "key" : "database-user" 371 | } 372 | } 373 | }, 374 | { 375 | "name": "POSTGRESQL_PASSWORD", 376 | "valueFrom": { 377 | "secretKeyRef" : { 378 | "name" : "${NAME}", 379 | "key" : "database-password" 380 | } 381 | } 382 | }, 383 | { 384 | "name": "POSTGRESQL_DATABASE", 385 | "value": "${DATABASE_NAME}" 386 | } 387 | ], 388 | "volumeMounts": [ 389 | { 390 | "name": "${DATABASE_SERVICE_NAME}-data", 391 | "mountPath": "/var/lib/pgsql/data" 392 | } 393 | ], 394 | "readinessProbe": { 395 | "timeoutSeconds": 1, 396 | "initialDelaySeconds": 5, 397 | "exec": { 398 | "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U ${POSTGRESQL_USER} -q -d ${POSTGRESQL_DATABASE} -c 'SELECT 1'"] 399 | } 400 | }, 401 | "livenessProbe": { 402 | "timeoutSeconds": 1, 403 | "initialDelaySeconds": 30, 404 | "tcpSocket": { 405 | "port": 5432 406 | } 407 | }, 408 | "resources": { 409 | "limits": { 410 | "memory": "${MEMORY_POSTGRESQL_LIMIT}" 411 | } 412 | } 413 | } 414 | ] 415 | } 416 | } 417 | } 418 | } 419 | ], 420 | "parameters": [ 421 | { 422 | "name": "NAME", 423 | "displayName": "Name", 424 | "description": "The name assigned to all of the frontend objects defined in this template.", 425 | "required": true, 426 | "value": "django-psql-persistent" 427 | }, 428 | { 429 | "name": "NAMESPACE", 430 | "displayName": "Namespace", 431 | "required": true, 432 | "description": "The OpenShift Namespace where the ImageStream resides.", 433 | "value": "openshift" 434 | }, 435 | { 436 | "name": "MEMORY_LIMIT", 437 | "displayName": "Memory Limit", 438 | "required": true, 439 | "description": "Maximum amount of memory the Django container can use.", 440 | "value": "512Mi" 441 | }, 442 | { 443 | "name": "MEMORY_POSTGRESQL_LIMIT", 444 | "displayName": "Memory Limit (PostgreSQL)", 445 | "required": true, 446 | "description": "Maximum amount of memory the PostgreSQL container can use.", 447 | "value": "512Mi" 448 | }, 449 | { 450 | "name": "VOLUME_CAPACITY", 451 | "displayName": "Volume Capacity", 452 | "description": "Volume space available for data, e.g. 512Mi, 2Gi", 453 | "value": "1Gi", 454 | "required": true 455 | }, 456 | { 457 | "name": "SOURCE_REPOSITORY_URL", 458 | "displayName": "Git Repository URL", 459 | "required": true, 460 | "description": "The URL of the repository with your application source code.", 461 | "value": "https://github.com/openshift/django-ex.git" 462 | }, 463 | { 464 | "name": "SOURCE_REPOSITORY_REF", 465 | "displayName": "Git Reference", 466 | "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." 467 | }, 468 | { 469 | "name": "CONTEXT_DIR", 470 | "displayName": "Context Directory", 471 | "description": "Set this to the relative path to your project if it is not in the root of your repository." 472 | }, 473 | { 474 | "name": "APPLICATION_DOMAIN", 475 | "displayName": "Application Hostname", 476 | "description": "The exposed hostname that will route to the Django service, if left blank a value will be defaulted.", 477 | "value": "" 478 | }, 479 | { 480 | "name": "GITHUB_WEBHOOK_SECRET", 481 | "displayName": "GitHub Webhook Secret", 482 | "description": "A secret string used to configure the GitHub webhook.", 483 | "generate": "expression", 484 | "from": "[a-zA-Z0-9]{40}" 485 | }, 486 | { 487 | "name": "DATABASE_SERVICE_NAME", 488 | "displayName": "Database Service Name", 489 | "required": true, 490 | "value": "postgresql" 491 | }, 492 | { 493 | "name": "DATABASE_ENGINE", 494 | "displayName": "Database Engine", 495 | "required": true, 496 | "description": "Database engine: postgresql, mysql or sqlite (default).", 497 | "value": "postgresql" 498 | }, 499 | { 500 | "name": "DATABASE_NAME", 501 | "displayName": "Database Name", 502 | "required": true, 503 | "value": "default" 504 | }, 505 | { 506 | "name": "DATABASE_USER", 507 | "displayName": "Database Username", 508 | "required": true, 509 | "value": "django" 510 | }, 511 | { 512 | "name": "DATABASE_PASSWORD", 513 | "displayName": "Database User Password", 514 | "generate": "expression", 515 | "from": "[a-zA-Z0-9]{16}" 516 | }, 517 | { 518 | "name": "APP_CONFIG", 519 | "displayName": "Application Configuration File Path", 520 | "description": "Relative path to Gunicorn configuration file (optional)." 521 | }, 522 | { 523 | "name": "DJANGO_SECRET_KEY", 524 | "displayName": "Django Secret Key", 525 | "description": "Set this to a long random string.", 526 | "generate": "expression", 527 | "from": "[\\w]{50}" 528 | }, 529 | { 530 | "name": "PIP_INDEX_URL", 531 | "displayName": "Custom PyPi Index URL", 532 | "description": "The custom PyPi index URL", 533 | "value": "" 534 | } 535 | ] 536 | } 537 | -------------------------------------------------------------------------------- /openshift/templates/django-postgresql.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Template", 3 | "apiVersion": "v1", 4 | "metadata": { 5 | "name": "django-psql-example", 6 | "annotations": { 7 | "openshift.io/display-name": "Django + PostgreSQL (Ephemeral)", 8 | "description": "An example Django application with a PostgreSQL database. For more information about using this template, including OpenShift considerations, see https://github.com/openshift/django-ex/blob/master/README.md.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing.", 9 | "tags": "quickstart,python,django", 10 | "iconClass": "icon-python", 11 | "template.openshift.io/long-description": "This template defines resources needed to develop a Django based application, including a build configuration, application deployment configuration, and database deployment configuration. The database is stored in non-persistent storage, so this configuration should be used for experimental purposes only.", 12 | "template.openshift.io/provider-display-name": "Red Hat, Inc.", 13 | "template.openshift.io/documentation-url": "https://github.com/openshift/django-ex", 14 | "template.openshift.io/support-url": "https://access.redhat.com" 15 | } 16 | }, 17 | "message": "The following service(s) have been created in your project: ${NAME}, ${DATABASE_SERVICE_NAME}.\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/openshift/django-ex/blob/master/README.md.", 18 | "labels": { 19 | "template": "django-psql-example" 20 | }, 21 | "objects": [ 22 | { 23 | "kind": "Secret", 24 | "apiVersion": "v1", 25 | "metadata": { 26 | "name": "${NAME}" 27 | }, 28 | "stringData" : { 29 | "database-user" : "${DATABASE_USER}", 30 | "database-password" : "${DATABASE_PASSWORD}", 31 | "django-secret-key" : "${DJANGO_SECRET_KEY}" 32 | } 33 | }, 34 | { 35 | "kind": "Service", 36 | "apiVersion": "v1", 37 | "metadata": { 38 | "name": "${NAME}", 39 | "annotations": { 40 | "description": "Exposes and load balances the application pods", 41 | "service.alpha.openshift.io/dependencies": "[{\"name\": \"${DATABASE_SERVICE_NAME}\", \"kind\": \"Service\"}]" 42 | } 43 | }, 44 | "spec": { 45 | "ports": [ 46 | { 47 | "name": "web", 48 | "port": 8080, 49 | "targetPort": 8080 50 | } 51 | ], 52 | "selector": { 53 | "name": "${NAME}" 54 | } 55 | } 56 | }, 57 | { 58 | "kind": "Route", 59 | "apiVersion": "v1", 60 | "metadata": { 61 | "name": "${NAME}" 62 | }, 63 | "spec": { 64 | "host": "${APPLICATION_DOMAIN}", 65 | "to": { 66 | "kind": "Service", 67 | "name": "${NAME}" 68 | } 69 | } 70 | }, 71 | { 72 | "kind": "ImageStream", 73 | "apiVersion": "v1", 74 | "metadata": { 75 | "name": "${NAME}", 76 | "annotations": { 77 | "description": "Keeps track of changes in the application image" 78 | } 79 | } 80 | }, 81 | { 82 | "kind": "BuildConfig", 83 | "apiVersion": "v1", 84 | "metadata": { 85 | "name": "${NAME}", 86 | "annotations": { 87 | "description": "Defines how to build the application" 88 | } 89 | }, 90 | "spec": { 91 | "source": { 92 | "type": "Git", 93 | "git": { 94 | "uri": "${SOURCE_REPOSITORY_URL}", 95 | "ref": "${SOURCE_REPOSITORY_REF}" 96 | }, 97 | "contextDir": "${CONTEXT_DIR}" 98 | }, 99 | "strategy": { 100 | "type": "Source", 101 | "sourceStrategy": { 102 | "from": { 103 | "kind": "ImageStreamTag", 104 | "namespace": "${NAMESPACE}", 105 | "name": "python:3.5" 106 | }, 107 | "env": [ 108 | { 109 | "name": "PIP_INDEX_URL", 110 | "value": "${PIP_INDEX_URL}" 111 | } 112 | ] 113 | } 114 | }, 115 | "output": { 116 | "to": { 117 | "kind": "ImageStreamTag", 118 | "name": "${NAME}:latest" 119 | } 120 | }, 121 | "triggers": [ 122 | { 123 | "type": "ImageChange" 124 | }, 125 | { 126 | "type": "ConfigChange" 127 | }, 128 | { 129 | "type": "GitHub", 130 | "github": { 131 | "secret": "${GITHUB_WEBHOOK_SECRET}" 132 | } 133 | } 134 | ], 135 | "postCommit": { 136 | "script": "./manage.py test" 137 | } 138 | } 139 | }, 140 | { 141 | "kind": "DeploymentConfig", 142 | "apiVersion": "v1", 143 | "metadata": { 144 | "name": "${NAME}", 145 | "annotations": { 146 | "description": "Defines how to deploy the application server" 147 | } 148 | }, 149 | "spec": { 150 | "strategy": { 151 | "type": "Rolling" 152 | }, 153 | "triggers": [ 154 | { 155 | "type": "ImageChange", 156 | "imageChangeParams": { 157 | "automatic": true, 158 | "containerNames": [ 159 | "django-psql-example" 160 | ], 161 | "from": { 162 | "kind": "ImageStreamTag", 163 | "name": "${NAME}:latest" 164 | } 165 | } 166 | }, 167 | { 168 | "type": "ConfigChange" 169 | } 170 | ], 171 | "replicas": 1, 172 | "selector": { 173 | "name": "${NAME}" 174 | }, 175 | "template": { 176 | "metadata": { 177 | "name": "${NAME}", 178 | "labels": { 179 | "name": "${NAME}" 180 | } 181 | }, 182 | "spec": { 183 | "containers": [ 184 | { 185 | "name": "django-psql-example", 186 | "image": " ", 187 | "ports": [ 188 | { 189 | "containerPort": 8080 190 | } 191 | ], 192 | "readinessProbe": { 193 | "timeoutSeconds": 3, 194 | "initialDelaySeconds": 3, 195 | "httpGet": { 196 | "path": "/health", 197 | "port": 8080 198 | } 199 | }, 200 | "livenessProbe": { 201 | "timeoutSeconds": 3, 202 | "initialDelaySeconds": 30, 203 | "httpGet": { 204 | "path": "/health", 205 | "port": 8080 206 | } 207 | }, 208 | "env": [ 209 | { 210 | "name": "DATABASE_SERVICE_NAME", 211 | "value": "${DATABASE_SERVICE_NAME}" 212 | }, 213 | { 214 | "name": "DATABASE_ENGINE", 215 | "value": "${DATABASE_ENGINE}" 216 | }, 217 | { 218 | "name": "DATABASE_NAME", 219 | "value": "${DATABASE_NAME}" 220 | }, 221 | { 222 | "name": "DATABASE_USER", 223 | "valueFrom": { 224 | "secretKeyRef" : { 225 | "name" : "${NAME}", 226 | "key" : "database-user" 227 | } 228 | } 229 | }, 230 | { 231 | "name": "DATABASE_PASSWORD", 232 | "valueFrom": { 233 | "secretKeyRef" : { 234 | "name" : "${NAME}", 235 | "key" : "database-password" 236 | } 237 | } 238 | }, 239 | { 240 | "name": "APP_CONFIG", 241 | "value": "${APP_CONFIG}" 242 | }, 243 | { 244 | "name": "DJANGO_SECRET_KEY", 245 | "valueFrom": { 246 | "secretKeyRef" : { 247 | "name" : "${NAME}", 248 | "key" : "django-secret-key" 249 | } 250 | } 251 | } 252 | ], 253 | "resources": { 254 | "limits": { 255 | "memory": "${MEMORY_LIMIT}" 256 | } 257 | } 258 | } 259 | ] 260 | } 261 | } 262 | } 263 | }, 264 | { 265 | "kind": "Service", 266 | "apiVersion": "v1", 267 | "metadata": { 268 | "name": "${DATABASE_SERVICE_NAME}", 269 | "annotations": { 270 | "description": "Exposes the database server" 271 | } 272 | }, 273 | "spec": { 274 | "ports": [ 275 | { 276 | "name": "postgresql", 277 | "port": 5432, 278 | "targetPort": 5432 279 | } 280 | ], 281 | "selector": { 282 | "name": "${DATABASE_SERVICE_NAME}" 283 | } 284 | } 285 | }, 286 | { 287 | "kind": "DeploymentConfig", 288 | "apiVersion": "v1", 289 | "metadata": { 290 | "name": "${DATABASE_SERVICE_NAME}", 291 | "annotations": { 292 | "description": "Defines how to deploy the database" 293 | } 294 | }, 295 | "spec": { 296 | "strategy": { 297 | "type": "Recreate" 298 | }, 299 | "triggers": [ 300 | { 301 | "type": "ImageChange", 302 | "imageChangeParams": { 303 | "automatic": true, 304 | "containerNames": [ 305 | "postgresql" 306 | ], 307 | "from": { 308 | "kind": "ImageStreamTag", 309 | "namespace": "${NAMESPACE}", 310 | "name": "postgresql:9.5" 311 | } 312 | } 313 | }, 314 | { 315 | "type": "ConfigChange" 316 | } 317 | ], 318 | "replicas": 1, 319 | "selector": { 320 | "name": "${DATABASE_SERVICE_NAME}" 321 | }, 322 | "template": { 323 | "metadata": { 324 | "name": "${DATABASE_SERVICE_NAME}", 325 | "labels": { 326 | "name": "${DATABASE_SERVICE_NAME}" 327 | } 328 | }, 329 | "spec": { 330 | "volumes": [ 331 | { 332 | "name": "data", 333 | "emptyDir": {} 334 | } 335 | ], 336 | "containers": [ 337 | { 338 | "name": "postgresql", 339 | "image": " ", 340 | "ports": [ 341 | { 342 | "containerPort": 5432 343 | } 344 | ], 345 | "env": [ 346 | { 347 | "name": "POSTGRESQL_USER", 348 | "valueFrom": { 349 | "secretKeyRef" : { 350 | "name" : "${NAME}", 351 | "key" : "database-user" 352 | } 353 | } 354 | }, 355 | { 356 | "name": "POSTGRESQL_PASSWORD", 357 | "valueFrom": { 358 | "secretKeyRef" : { 359 | "name" : "${NAME}", 360 | "key" : "database-password" 361 | } 362 | } 363 | }, 364 | { 365 | "name": "POSTGRESQL_DATABASE", 366 | "value": "${DATABASE_NAME}" 367 | } 368 | ], 369 | "volumeMounts": [ 370 | { 371 | "name": "data", 372 | "mountPath": "/var/lib/pgsql/data" 373 | } 374 | ], 375 | "readinessProbe": { 376 | "timeoutSeconds": 1, 377 | "initialDelaySeconds": 5, 378 | "exec": { 379 | "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U ${POSTGRESQL_USER} -q -d ${POSTGRESQL_DATABASE} -c 'SELECT 1'"] 380 | } 381 | }, 382 | "livenessProbe": { 383 | "timeoutSeconds": 1, 384 | "initialDelaySeconds": 30, 385 | "tcpSocket": { 386 | "port": 5432 387 | } 388 | }, 389 | "resources": { 390 | "limits": { 391 | "memory": "${MEMORY_POSTGRESQL_LIMIT}" 392 | } 393 | } 394 | } 395 | ] 396 | } 397 | } 398 | } 399 | } 400 | ], 401 | "parameters": [ 402 | { 403 | "name": "NAME", 404 | "displayName": "Name", 405 | "description": "The name assigned to all of the frontend objects defined in this template.", 406 | "required": true, 407 | "value": "django-psql-example" 408 | }, 409 | { 410 | "name": "NAMESPACE", 411 | "displayName": "Namespace", 412 | "required": true, 413 | "description": "The OpenShift Namespace where the ImageStream resides.", 414 | "value": "openshift" 415 | }, 416 | { 417 | "name": "MEMORY_LIMIT", 418 | "displayName": "Memory Limit", 419 | "required": true, 420 | "description": "Maximum amount of memory the Django container can use.", 421 | "value": "512Mi" 422 | }, 423 | { 424 | "name": "MEMORY_POSTGRESQL_LIMIT", 425 | "displayName": "Memory Limit (PostgreSQL)", 426 | "required": true, 427 | "description": "Maximum amount of memory the PostgreSQL container can use.", 428 | "value": "512Mi" 429 | }, 430 | { 431 | "name": "SOURCE_REPOSITORY_URL", 432 | "displayName": "Git Repository URL", 433 | "required": true, 434 | "description": "The URL of the repository with your application source code.", 435 | "value": "https://github.com/openshift/django-ex.git" 436 | }, 437 | { 438 | "name": "SOURCE_REPOSITORY_REF", 439 | "displayName": "Git Reference", 440 | "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." 441 | }, 442 | { 443 | "name": "CONTEXT_DIR", 444 | "displayName": "Context Directory", 445 | "description": "Set this to the relative path to your project if it is not in the root of your repository." 446 | }, 447 | { 448 | "name": "APPLICATION_DOMAIN", 449 | "displayName": "Application Hostname", 450 | "description": "The exposed hostname that will route to the Django service, if left blank a value will be defaulted.", 451 | "value": "" 452 | }, 453 | { 454 | "name": "GITHUB_WEBHOOK_SECRET", 455 | "displayName": "GitHub Webhook Secret", 456 | "description": "A secret string used to configure the GitHub webhook.", 457 | "generate": "expression", 458 | "from": "[a-zA-Z0-9]{40}" 459 | }, 460 | { 461 | "name": "DATABASE_SERVICE_NAME", 462 | "displayName": "Database Service Name", 463 | "required": true, 464 | "value": "postgresql" 465 | }, 466 | { 467 | "name": "DATABASE_ENGINE", 468 | "displayName": "Database Engine", 469 | "required": true, 470 | "description": "Database engine: postgresql, mysql or sqlite (default).", 471 | "value": "postgresql" 472 | }, 473 | { 474 | "name": "DATABASE_NAME", 475 | "displayName": "Database Name", 476 | "required": true, 477 | "value": "default" 478 | }, 479 | { 480 | "name": "DATABASE_USER", 481 | "displayName": "Database Username", 482 | "required": true, 483 | "value": "django" 484 | }, 485 | { 486 | "name": "DATABASE_PASSWORD", 487 | "displayName": "Database User Password", 488 | "generate": "expression", 489 | "from": "[a-zA-Z0-9]{16}" 490 | }, 491 | { 492 | "name": "APP_CONFIG", 493 | "displayName": "Application Configuration File Path", 494 | "description": "Relative path to Gunicorn configuration file (optional)." 495 | }, 496 | { 497 | "name": "DJANGO_SECRET_KEY", 498 | "displayName": "Django Secret Key", 499 | "description": "Set this to a long random string.", 500 | "generate": "expression", 501 | "from": "[\\w]{50}" 502 | }, 503 | { 504 | "name": "PIP_INDEX_URL", 505 | "displayName": "Custom PyPi Index URL", 506 | "description": "The custom PyPi index URL", 507 | "value": "" 508 | } 509 | ] 510 | } 511 | -------------------------------------------------------------------------------- /openshift/templates/django.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Template", 3 | "apiVersion": "v1", 4 | "metadata": { 5 | "name": "django-example", 6 | "annotations": { 7 | "openshift.io/display-name": "Django", 8 | "description": "An example Django application with no database. For more information about using this template, including OpenShift considerations, see https://github.com/openshift/django-ex/blob/master/README.md.", 9 | "tags": "quickstart,python,django", 10 | "iconClass": "icon-python", 11 | "template.openshift.io/long-description": "This template defines resources needed to develop a Django based application, including a build configuration and application deployment configuration. It does not include a database.", 12 | "template.openshift.io/provider-display-name": "Red Hat, Inc.", 13 | "template.openshift.io/documentation-url": "https://github.com/openshift/django-ex", 14 | "template.openshift.io/support-url": "https://access.redhat.com" 15 | } 16 | }, 17 | "labels": { 18 | "template": "django-example" 19 | }, 20 | "message": "The following service(s) have been created in your project: ${NAME}.\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/openshift/django-ex/blob/master/README.md.", 21 | "objects": [ 22 | { 23 | "kind": "Secret", 24 | "apiVersion": "v1", 25 | "metadata": { 26 | "name": "${NAME}" 27 | }, 28 | "stringData" : { 29 | "django-secret-key" : "${DJANGO_SECRET_KEY}" 30 | } 31 | }, 32 | { 33 | "kind": "Service", 34 | "apiVersion": "v1", 35 | "metadata": { 36 | "name": "${NAME}", 37 | "annotations": { 38 | "description": "Exposes and load balances the application pods" 39 | } 40 | }, 41 | "spec": { 42 | "ports": [ 43 | { 44 | "name": "web", 45 | "port": 8080, 46 | "targetPort": 8080 47 | } 48 | ], 49 | "selector": { 50 | "name": "${NAME}" 51 | } 52 | } 53 | }, 54 | { 55 | "kind": "Route", 56 | "apiVersion": "v1", 57 | "metadata": { 58 | "name": "${NAME}" 59 | }, 60 | "spec": { 61 | "host": "${APPLICATION_DOMAIN}", 62 | "to": { 63 | "kind": "Service", 64 | "name": "${NAME}" 65 | } 66 | } 67 | }, 68 | { 69 | "kind": "ImageStream", 70 | "apiVersion": "v1", 71 | "metadata": { 72 | "name": "${NAME}", 73 | "annotations": { 74 | "description": "Keeps track of changes in the application image" 75 | } 76 | } 77 | }, 78 | { 79 | "kind": "BuildConfig", 80 | "apiVersion": "v1", 81 | "metadata": { 82 | "name": "${NAME}", 83 | "annotations": { 84 | "description": "Defines how to build the application" 85 | } 86 | }, 87 | "spec": { 88 | "source": { 89 | "type": "Git", 90 | "git": { 91 | "uri": "${SOURCE_REPOSITORY_URL}", 92 | "ref": "${SOURCE_REPOSITORY_REF}" 93 | }, 94 | "contextDir": "${CONTEXT_DIR}" 95 | }, 96 | "strategy": { 97 | "type": "Source", 98 | "sourceStrategy": { 99 | "from": { 100 | "kind": "ImageStreamTag", 101 | "namespace": "${NAMESPACE}", 102 | "name": "python:3.5" 103 | }, 104 | "env": [ 105 | { 106 | "name": "PIP_INDEX_URL", 107 | "value": "${PIP_INDEX_URL}" 108 | } 109 | ] 110 | } 111 | }, 112 | "output": { 113 | "to": { 114 | "kind": "ImageStreamTag", 115 | "name": "${NAME}:latest" 116 | } 117 | }, 118 | "triggers": [ 119 | { 120 | "type": "ImageChange" 121 | }, 122 | { 123 | "type": "ConfigChange" 124 | }, 125 | { 126 | "type": "GitHub", 127 | "github": { 128 | "secret": "${GITHUB_WEBHOOK_SECRET}" 129 | } 130 | } 131 | ], 132 | "postCommit": { 133 | "script": "./manage.py test" 134 | } 135 | } 136 | }, 137 | { 138 | "kind": "DeploymentConfig", 139 | "apiVersion": "v1", 140 | "metadata": { 141 | "name": "${NAME}", 142 | "annotations": { 143 | "description": "Defines how to deploy the application server" 144 | } 145 | }, 146 | "spec": { 147 | "strategy": { 148 | "type": "Rolling" 149 | }, 150 | "triggers": [ 151 | { 152 | "type": "ImageChange", 153 | "imageChangeParams": { 154 | "automatic": true, 155 | "containerNames": [ 156 | "django-example" 157 | ], 158 | "from": { 159 | "kind": "ImageStreamTag", 160 | "name": "${NAME}:latest" 161 | } 162 | } 163 | }, 164 | { 165 | "type": "ConfigChange" 166 | } 167 | ], 168 | "replicas": 1, 169 | "selector": { 170 | "name": "${NAME}" 171 | }, 172 | "template": { 173 | "metadata": { 174 | "name": "${NAME}", 175 | "labels": { 176 | "name": "${NAME}" 177 | } 178 | }, 179 | "spec": { 180 | "containers": [ 181 | { 182 | "name": "django-example", 183 | "image": " ", 184 | "ports": [ 185 | { 186 | "containerPort": 8080 187 | } 188 | ], 189 | "readinessProbe": { 190 | "timeoutSeconds": 3, 191 | "initialDelaySeconds": 3, 192 | "httpGet": { 193 | "path": "/", 194 | "port": 8080 195 | } 196 | }, 197 | "livenessProbe": { 198 | "timeoutSeconds": 3, 199 | "initialDelaySeconds": 30, 200 | "httpGet": { 201 | "path": "/", 202 | "port": 8080 203 | } 204 | }, 205 | "env": [ 206 | { 207 | "name": "APP_CONFIG", 208 | "value": "${APP_CONFIG}" 209 | }, 210 | { 211 | "name": "DJANGO_SECRET_KEY", 212 | "valueFrom": { 213 | "secretKeyRef" : { 214 | "name" : "${NAME}", 215 | "key" : "django-secret-key" 216 | } 217 | } 218 | } 219 | ], 220 | "resources": { 221 | "limits": { 222 | "memory": "${MEMORY_LIMIT}" 223 | } 224 | } 225 | } 226 | ] 227 | } 228 | } 229 | } 230 | } 231 | ], 232 | "parameters": [ 233 | { 234 | "name": "NAME", 235 | "displayName": "Name", 236 | "description": "The name assigned to all of the frontend objects defined in this template.", 237 | "required": true, 238 | "value": "django-example" 239 | }, 240 | { 241 | "name": "NAMESPACE", 242 | "displayName": "Namespace", 243 | "required": true, 244 | "description": "The OpenShift Namespace where the ImageStream resides.", 245 | "value": "openshift" 246 | }, 247 | { 248 | "name": "MEMORY_LIMIT", 249 | "displayName": "Memory Limit", 250 | "required": true, 251 | "description": "Maximum amount of memory the container can use.", 252 | "value": "512Mi" 253 | }, 254 | { 255 | "name": "SOURCE_REPOSITORY_URL", 256 | "displayName": "Git Repository URL", 257 | "required": true, 258 | "description": "The URL of the repository with your application source code.", 259 | "value": "https://github.com/openshift/django-ex.git" 260 | }, 261 | { 262 | "name": "SOURCE_REPOSITORY_REF", 263 | "displayName": "Git Reference", 264 | "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." 265 | }, 266 | { 267 | "name": "CONTEXT_DIR", 268 | "displayName": "Context Directory", 269 | "description": "Set this to the relative path to your project if it is not in the root of your repository." 270 | }, 271 | { 272 | "name": "APPLICATION_DOMAIN", 273 | "displayName": "Application Hostname", 274 | "description": "The exposed hostname that will route to the Django service, if left blank a value will be defaulted.", 275 | "value": "" 276 | }, 277 | { 278 | "name": "GITHUB_WEBHOOK_SECRET", 279 | "displayName": "GitHub Webhook Secret", 280 | "description": "A secret string used to configure the GitHub webhook.", 281 | "generate": "expression", 282 | "from": "[a-zA-Z0-9]{40}" 283 | }, 284 | { 285 | "name": "APP_CONFIG", 286 | "displayName": "Application Configuration File Path", 287 | "description": "Relative path to Gunicorn configuration file (optional)." 288 | }, 289 | { 290 | "name": "DJANGO_SECRET_KEY", 291 | "displayName": "Django Secret Key", 292 | "description": "Set this to a long random string.", 293 | "generate": "expression", 294 | "from": "[\\w]{50}" 295 | }, 296 | { 297 | "name": "PIP_INDEX_URL", 298 | "displayName": "Custom PyPi Index URL", 299 | "description": "The custom PyPi index URL", 300 | "value": "" 301 | } 302 | ] 303 | } 304 | -------------------------------------------------------------------------------- /project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Terry7879/django-ex/a1c42b0d61d15abbaba776c99ea98fed69d85cf4/project/__init__.py -------------------------------------------------------------------------------- /project/database.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.conf import settings 4 | 5 | 6 | engines = { 7 | 'sqlite': 'django.db.backends.sqlite3', 8 | 'postgresql': 'django.db.backends.postgresql_psycopg2', 9 | 'mysql': 'django.db.backends.mysql', 10 | } 11 | 12 | 13 | def config(): 14 | service_name = os.getenv('DATABASE_SERVICE_NAME', '').upper().replace('-', '_') 15 | if service_name: 16 | engine = engines.get(os.getenv('DATABASE_ENGINE'), engines['sqlite']) 17 | else: 18 | engine = engines['sqlite'] 19 | name = os.getenv('DATABASE_NAME') 20 | if not name and engine == engines['sqlite']: 21 | name = os.path.join(settings.BASE_DIR, 'db.sqlite3') 22 | return { 23 | 'ENGINE': engine, 24 | 'NAME': name, 25 | 'USER': os.getenv('DATABASE_USER'), 26 | 'PASSWORD': os.getenv('DATABASE_PASSWORD'), 27 | 'HOST': os.getenv('{}_SERVICE_HOST'.format(service_name)), 28 | 'PORT': os.getenv('{}_SERVICE_PORT'.format(service_name)), 29 | } 30 | -------------------------------------------------------------------------------- /project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for this project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | # The SECRET_KEY is provided via an environment variable in OpenShift 24 | SECRET_KEY = os.getenv( 25 | 'DJANGO_SECRET_KEY', 26 | # safe value used for development when DJANGO_SECRET_KEY might not be set 27 | '9e4@&tw46$l31)zrqe3wi+-slqm(ruvz&se0^%9#6(_w3ui!c0' 28 | ) 29 | 30 | # SECURITY WARNING: don't run with debug turned on in production! 31 | DEBUG = True 32 | 33 | ALLOWED_HOSTS = ['*'] 34 | 35 | 36 | # Application definition 37 | 38 | INSTALLED_APPS = ( 39 | 'django.contrib.admin', 40 | 'django.contrib.auth', 41 | 'django.contrib.contenttypes', 42 | 'django.contrib.sessions', 43 | 'django.contrib.messages', 44 | 'django.contrib.staticfiles', 45 | 'debug_toolbar', 46 | 'welcome', 47 | ) 48 | 49 | MIDDLEWARE_CLASSES = ( 50 | 'django.contrib.sessions.middleware.SessionMiddleware', 51 | 'django.middleware.common.CommonMiddleware', 52 | 'django.middleware.csrf.CsrfViewMiddleware', 53 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 54 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 55 | 'django.contrib.messages.middleware.MessageMiddleware', 56 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 57 | 'django.middleware.security.SecurityMiddleware', 58 | 'whitenoise.middleware.WhiteNoiseMiddleware', 59 | ) 60 | 61 | ROOT_URLCONF = 'project.urls' 62 | 63 | TEMPLATES = [ 64 | { 65 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 66 | 'DIRS': [], 67 | 'APP_DIRS': True, 68 | 'OPTIONS': { 69 | 'context_processors': [ 70 | 'django.template.context_processors.debug', 71 | 'django.template.context_processors.request', 72 | 'django.contrib.auth.context_processors.auth', 73 | 'django.contrib.messages.context_processors.messages', 74 | ], 75 | }, 76 | }, 77 | ] 78 | 79 | WSGI_APPLICATION = 'wsgi.application' 80 | 81 | 82 | # Database 83 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 84 | 85 | from . import database 86 | 87 | DATABASES = { 88 | 'default': database.config() 89 | } 90 | 91 | 92 | # Internationalization 93 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 94 | 95 | LANGUAGE_CODE = 'en-us' 96 | 97 | TIME_ZONE = 'UTC' 98 | 99 | USE_I18N = True 100 | 101 | USE_L10N = True 102 | 103 | USE_TZ = True 104 | 105 | 106 | # Static files (CSS, JavaScript, Images) 107 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 108 | 109 | STATIC_URL = '/static/' 110 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 111 | 112 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 113 | -------------------------------------------------------------------------------- /project/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from django.contrib import admin 3 | 4 | from welcome.views import index, health 5 | 6 | urlpatterns = [ 7 | # Examples: 8 | # url(r'^$', 'project.views.home', name='home'), 9 | # url(r'^blog/', include('blog.urls')), 10 | 11 | url(r'^$', index), 12 | url(r'^health$', health), 13 | url(r'^admin/', include(admin.site.urls)), 14 | ] 15 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django>=1.8,<1.9 2 | django-debug-toolbar==1.5 3 | gunicorn==19.4.5 4 | psycopg2==2.6.1 5 | whitenoise==3.0 6 | mysqlclient==1.3.10 7 | django-mysql==1.1.0 8 | -------------------------------------------------------------------------------- /welcome/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Terry7879/django-ex/a1c42b0d61d15abbaba776c99ea98fed69d85cf4/welcome/__init__.py -------------------------------------------------------------------------------- /welcome/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import PageView 4 | 5 | # Register your models here. 6 | 7 | 8 | class PageViewAdmin(admin.ModelAdmin): 9 | list_display = ['hostname', 'timestamp'] 10 | 11 | admin.site.register(PageView, PageViewAdmin) 12 | -------------------------------------------------------------------------------- /welcome/database.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def info(): 5 | db_settings = settings.DATABASES['default'] 6 | if 'postgres' in db_settings['ENGINE']: 7 | engine = 'PostgreSQL' 8 | url = '{HOST}:{PORT}/{NAME}'.format(**db_settings) 9 | elif 'mysql' in db_settings['ENGINE']: 10 | engine = 'MySQL' 11 | url = '{HOST}:{PORT}/{NAME}'.format(**db_settings) 12 | elif 'sqlite' in db_settings['ENGINE']: 13 | engine = 'SQLite' 14 | url = '{NAME}'.format(**db_settings) 15 | else: 16 | engine = 'unknown' 17 | url = '' 18 | return { 19 | 'engine': engine, 20 | 'url': url, 21 | 'is_sqlite': engine == 'SQLite', 22 | } 23 | -------------------------------------------------------------------------------- /welcome/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='PageView', 15 | fields=[ 16 | ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)), 17 | ('hostname', models.CharField(max_length=32)), 18 | ('timestamp', models.DateTimeField(auto_now_add=True)), 19 | ], 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /welcome/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Terry7879/django-ex/a1c42b0d61d15abbaba776c99ea98fed69d85cf4/welcome/migrations/__init__.py -------------------------------------------------------------------------------- /welcome/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | class PageView(models.Model): 6 | hostname = models.CharField(max_length=32) 7 | timestamp = models.DateTimeField(auto_now_add=True) 8 | -------------------------------------------------------------------------------- /welcome/templates/welcome/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to OpenShift 7 | 8 | 9 | 209 | 210 | 211 | 212 | 213 |
214 |
215 |

Welcome to your Django application on OpenShift

216 |
217 | 218 | 219 |
220 |
221 |
222 |

How to use this example application

223 |

For instructions on how to use this application with OpenShift, start by reading the Developer Guide.

224 | 225 |

Deploying code changes

226 |

227 | The source code for this application is available to be forked from the OpenShift GitHub repository. 228 | You can configure a webhook in your repository to make OpenShift automatically start a build whenever you push your code: 229 |

230 | 231 |
    232 |
  1. From the Web Console homepage, navigate to your project
  2. 233 |
  3. Click on Browse > Builds
  4. 234 |
  5. Click the link with your BuildConfig name
  6. 235 |
  7. Click the Configuration tab
  8. 236 |
  9. Click the "Copy to clipboard" icon to the right of the "GitHub webhook URL" field
  10. 237 |
  11. Navigate to your repository on GitHub and click on repository settings > webhooks > Add webhook
  12. 238 |
  13. Paste your webhook URL provided by OpenShift
  14. 239 |
  15. Leave the defaults for the remaining fields — that's it!
  16. 240 |
241 |

After you save your webhook, if you refresh your settings page you can see the status of the ping that Github sent to OpenShift to verify it can reach the server.

242 |

Note: adding a webhook requires your OpenShift server to be reachable from GitHub.

243 | 244 |

Working in your local Git repository

245 |

If you forked the application from the OpenShift GitHub example, you'll need to manually clone the repository to your local system. Copy the application's source code Git URL and then run:

246 | 247 |
$ git clone <git_url> <directory_to_create>
248 | 
249 | # Within your project directory
250 | # Commit your changes and push to OpenShift
251 | 
252 | $ git commit -a -m 'Some commit message'
253 | $ git push
254 | 255 |

After pushing changes, you'll need to manually trigger a build if you did not setup a webhook as described above.

256 | 257 |

Expanding on sample app

258 |

This project has just a skeleton for you to get started. It includes:

259 |
    260 |
  1. Django project created with python manage.py startproject project .
  2. 261 |
  3. Appropriate database configuration
  4. 262 |
  5. Sample Django app created with python manage.py startapp welcome
  6. 263 |
264 |

265 | Now it is time to add your own code. 266 | Follow along the Django tutorial to learn more about Django. 267 |

268 |

269 | If you are running Django's development server in your local host, you can see more information 270 | about this request using the toolbar on the right.
271 | Please note that by default your Django settings have DEBUG = True and that is not appropriate for production use, 272 | but very handy during development.
273 | Refer to the Deployment Checklist 274 | before taking this project into a production environment. 275 |

276 | 277 |
278 | 279 |
280 |
281 | 282 |

Managing your application

283 | 284 |

Documentation on how to manage your application from the Web Console or Command Line is available at the Developer Guide.

285 | 286 |

Web Console

287 |

You can use the Web Console to view the state of your application components and launch new builds.

288 | 289 |

Command Line

290 |

With the OpenShift command line interface (CLI), you can create applications and manage projects from a terminal.

291 | 292 |

Development Resources

293 | 301 | 302 |

Request information

303 |
304 | Server hostname: {{ hostname }}
305 | Database server: {{ database.engine }} ({{ database.url }})
306 | {% if database.is_sqlite %}Data persistence warning: You are currently using SQLite. This is fine for development, but your data won't be persisted across application deployments.{% endif %}
307 | Page views: {{ count }}
308 | 
309 | 310 |
311 |
312 | 313 | 316 |
317 | 318 | 319 | 320 | 321 | -------------------------------------------------------------------------------- /welcome/tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .models import PageView 4 | from .database import info 5 | from django.test import TestCase 6 | 7 | # These basic tests are to be used as an example for running tests in S2I 8 | # and OpenShift when building an application image. 9 | class PageViewModelTest(TestCase): 10 | def test_viewpage_model(self): 11 | pageview = PageView.objects.create(hostname='localhost') 12 | pagetest = PageView.objects.get(hostname='localhost') 13 | self.assertEqual(pagetest.hostname, 'localhost') 14 | 15 | class PageViewTest(TestCase): 16 | def test_index(self): 17 | resp = self.client.get('/') 18 | self.assertEqual(resp.status_code, 200) 19 | 20 | class DbEngine(TestCase): 21 | def setUp(self): 22 | os.environ['ENGINE'] = 'SQLite' 23 | 24 | def test_engine_setup(self): 25 | settings = info() 26 | self.assertEqual(settings['engine'], 'SQLite') 27 | self.assertEqual(settings['is_sqlite'], True) 28 | -------------------------------------------------------------------------------- /welcome/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.shortcuts import render 3 | from django.conf import settings 4 | from django.http import HttpResponse 5 | 6 | from . import database 7 | from .models import PageView 8 | 9 | # Create your views here. 10 | 11 | def index(request): 12 | hostname = os.getenv('HOSTNAME', 'unknown') 13 | PageView.objects.create(hostname=hostname) 14 | 15 | return render(request, 'welcome/index.html', { 16 | 'hostname': hostname, 17 | 'database': database.info(), 18 | 'count': PageView.objects.count() 19 | }) 20 | 21 | def health(request): 22 | return HttpResponse(PageView.objects.count()) 23 | -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------