├── project ├── __init__.py ├── urls.py ├── wsgi.py └── settings.py ├── src ├── sass │ ├── _z-index.scss │ ├── style.scss │ ├── _vars.scss │ ├── _base.scss │ ├── _mixins.scss │ ├── _reset.scss │ └── _index.scss ├── js │ └── main.js └── html │ ├── 40x.html │ ├── 50x.html │ └── index.html ├── test ├── inventory └── test.yml ├── roles ├── gulp-static │ ├── tests │ │ ├── inventory │ │ └── test.yml │ ├── vars │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── README.md │ ├── defaults │ │ └── main.yml │ ├── templates │ │ └── entrypoint.sh.j2 │ ├── .travis.yml │ ├── tasks │ │ └── main.yml │ └── meta │ │ └── main.yml └── django-gunicorn │ ├── tests │ ├── inventory │ └── test.yml │ ├── vars │ └── main.yml │ ├── handlers │ └── main.yml │ ├── README.md │ ├── files │ ├── manage_django.sh │ ├── entrypoint.sh │ └── wait_on_postgres.py │ ├── defaults │ └── main.yml │ ├── .travis.yml │ ├── tasks │ └── main.yml │ └── meta │ └── main.yml ├── requirements.yml ├── .bowerrc ├── scripts ├── gulp_build.sh └── clean.sh ├── .dockerignore ├── AUTHORS ├── .mailmap ├── .gitignore ├── manage.py ├── requirements.txt ├── bower.json ├── package.json ├── meta.yml ├── update-authors.py ├── Makefile ├── .travis.yml ├── container.yml ├── gulpfile.js └── README.md /project/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/sass/_z-index.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /roles/gulp-static/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost -------------------------------------------------------------------------------- /roles/django-gunicorn/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | - src: ansible.nginx-container 2 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "dist/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /roles/gulp-static/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for gulp-static 3 | -------------------------------------------------------------------------------- /scripts/gulp_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set +x 3 | cd /node 4 | gulp build 5 | -------------------------------------------------------------------------------- /roles/django-gunicorn/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for django-gunicorn 3 | -------------------------------------------------------------------------------- /roles/gulp-static/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for gulp-static 3 | -------------------------------------------------------------------------------- /roles/django-gunicorn/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for django-gunicorn 3 | -------------------------------------------------------------------------------- /roles/gulp-static/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - gulp-static -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | const main = function () { 2 | 3 | } 4 | 5 | document.addEventListener('DOMContentLoaded', main) 6 | -------------------------------------------------------------------------------- /roles/gulp-static/README.md: -------------------------------------------------------------------------------- 1 | gulp-static 2 | =========== 3 | 4 | Gulp static serve and build for Ansible Container. 5 | -------------------------------------------------------------------------------- /roles/django-gunicorn/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | Ansible Container role for Django running under gunicorn 5 | -------------------------------------------------------------------------------- /roles/django-gunicorn/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - django-gunicorn -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .config 2 | .local 3 | .cache 4 | .git 5 | .mailmap 6 | dist 7 | temp-space 8 | test 9 | node_modules 10 | ansible-deployment 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /roles/django-gunicorn/files/manage_django.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run django management command 4 | 5 | cd ${DJANGO_ROOT} 6 | ${DJANGO_VENV}/bin/python ./manage.py "$@" 7 | -------------------------------------------------------------------------------- /roles/gulp-static/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | NODE_USER: node 3 | NODE_HOME: /node 4 | NODE_ROOT: "" 5 | NODE_RPM_URL: https://rpm.nodesource.com/pub_5.x/el/7/x86_64/nodejs-5.9.1-1nodesource.el7.centos.x86_64.rpm 6 | -------------------------------------------------------------------------------- /src/sass/style.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | @import "reset"; 3 | 4 | // PARTIALS 5 | 6 | @import "vars"; 7 | @import "base"; 8 | @import "z-index"; 9 | 10 | // COMPONENTS 11 | 12 | // SECTIONS 13 | 14 | @import "index"; 15 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This project has been contribued to by the following authors: 2 | This list is automatically generated - please file an issue for corrections) 3 | 4 | Chris Houseknecht 5 | Joshua "jag" Ginsberg 6 | -------------------------------------------------------------------------------- /src/sass/_vars.scss: -------------------------------------------------------------------------------- 1 | // SITE 2 | 3 | $site-width: 960px; 4 | $site-gutter: 7.5%; 5 | 6 | // BREAKPOINTS 7 | 8 | $tablet: 768px; 9 | $desktop: 1024px; 10 | 11 | // FONTS 12 | 13 | // $sans: ; 14 | // $serif: ; 15 | 16 | // COLORS 17 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Chris Houseknecht 2 | Chris Houseknecht chouseknecht 3 | Chris Houseknecht chouseknecht 4 | Joshua "jag" Ginsberg 5 | -------------------------------------------------------------------------------- /roles/django-gunicorn/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | DJANGO_RPM_DEPS: 3 | - postgresql-devel 4 | - python-devel 5 | - gcc 6 | - python-virtualenv 7 | - nc 8 | - rsync 9 | DJANGO_USER: django 10 | DJANGO_ROOT: /django 11 | DJANGO_VENV: /venv 12 | DJANGO_STATIC_ROOT: /django/dist 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .npm/ 3 | .ansible 4 | node_modules/ 5 | *.retry 6 | __pycache__/ 7 | *.py[cod] 8 | *.log 9 | .v8flags* 10 | .bash_history 11 | .cache 12 | .config 13 | .local 14 | .pki 15 | ansible-deployment/* 16 | dist/* 17 | !dist/README.md 18 | package-lock.json 19 | temp-space/ 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools>=38 2 | Django 3 | psycopg2==2.6.1 4 | gunicorn 5 | dj-database-url 6 | 7 | # Demo requirements 8 | djangorestframework 9 | markdown 10 | django-filter 11 | dj-static 12 | django-appconf 13 | django-compressor 14 | drf-nested-routers 15 | six 16 | static3 17 | # wsgiref 18 | -------------------------------------------------------------------------------- /project/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls import include, url 3 | from django.conf.urls.static import static 4 | 5 | from django.contrib import admin 6 | 7 | urlpatterns = [ 8 | url(r'^admin/', admin.site.urls), 9 | ] 10 | 11 | if settings.DEBUG: 12 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 13 | 14 | -------------------------------------------------------------------------------- /src/sass/_base.scss: -------------------------------------------------------------------------------- 1 | // FONT 2 | 3 | html { 4 | // 1rem = 10px 5 | font-size: 10px; 6 | } 7 | 8 | body { 9 | // font-family: ; 10 | // font-weight: ; 11 | // font-size: ; 12 | line-height: 1.3; 13 | } 14 | 15 | // LAYOUT 16 | 17 | .container { 18 | width: 100% - 2 * $site-gutter; 19 | max-width: $site-width; 20 | margin-right: auto; 21 | margin-left: auto; 22 | } 23 | -------------------------------------------------------------------------------- /roles/django-gunicorn/files/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | if [[ $@ == *"gunicorn"* || $@ == *"runserver"* ]]; then 6 | if [ -f ${DJANGO_ROOT}/manage.py ]; then 7 | /usr/bin/wait_on_postgres.py 8 | if [ "$?" == "0" ]; then 9 | ${DJANGO_VENV}/bin/python manage.py migrate --fake-initial --noinput 10 | fi 11 | fi 12 | fi 13 | 14 | exec $@ 15 | -------------------------------------------------------------------------------- /roles/gulp-static/templates/entrypoint.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | if [ -d "/tmp/django" ]; then 5 | # Copy django static assets 6 | mkdir -p {{ NODE_HOME }}/dist 7 | cp -R /tmp/django/* {{ NODE_HOME }}/dist/ 8 | fi 9 | 10 | cd {{ NODE_HOME}} 11 | 12 | # Install node deps 13 | npm install 14 | 15 | # Build static assets 16 | gulp build 17 | 18 | # Install bower packages 19 | su - {{ NODE_USER }} -c "bower install" 20 | 21 | $@ 22 | -------------------------------------------------------------------------------- /src/html/40x.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 Error 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Error: the server responded with a 404 error. Check the log for more details.

15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/html/50x.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 Error 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Error: the server responded with a 500 error. Check the log for more details.

15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /project/wsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import socket 4 | import time 5 | 6 | postgres_is_alive = False 7 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | 9 | while not postgres_is_alive: 10 | try: 11 | s.connect(('postgresql', 5432)) 12 | except socket.error: 13 | time.sleep(1) 14 | else: 15 | postgres_is_alive = True 16 | 17 | 18 | from django.core.wsgi import get_wsgi_application 19 | 20 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 21 | 22 | application = get_wsgi_application() 23 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "django_angular_boilerplate", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/ansible/django-gulp-nginx", 5 | "license": "Apache v2", 6 | "ignore": [ 7 | "**/.*", 8 | "node_modules", 9 | "bower_components", 10 | "test", 11 | "tests" 12 | ], 13 | "dependencies": { 14 | "angular": "~1.3.1", 15 | "angular-route": "~1.3.1", 16 | "angular-cookies": "~1.3.1", 17 | "bootstrap": "~3.3.0", 18 | "bootstrap-material-design": "~0.1.5", 19 | "jquery": "~2.1.1", 20 | "ngDialog": "~0.3.3", 21 | "underscore": "~1.7.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /roles/django-gunicorn/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | 5 | # Use the new container infrastructure 6 | sudo: false 7 | 8 | # Install ansible 9 | addons: 10 | apt: 11 | packages: 12 | - python-pip 13 | 14 | install: 15 | # Install ansible 16 | - pip install ansible 17 | 18 | # Check ansible version 19 | - ansible --version 20 | 21 | # Create ansible.cfg with correct roles_path 22 | - printf '[defaults]\nroles_path=../' >ansible.cfg 23 | 24 | script: 25 | # Basic role syntax check 26 | - ansible-playbook tests/test.yml -i tests/inventory --syntax-check 27 | 28 | notifications: 29 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ -------------------------------------------------------------------------------- /roles/gulp-static/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | 5 | # Use the new container infrastructure 6 | sudo: false 7 | 8 | # Install ansible 9 | addons: 10 | apt: 11 | packages: 12 | - python-pip 13 | 14 | install: 15 | # Install ansible 16 | - pip install ansible 17 | 18 | # Check ansible version 19 | - ansible --version 20 | 21 | # Create ansible.cfg with correct roles_path 22 | - printf '[defaults]\nroles_path=../' >ansible.cfg 23 | 24 | script: 25 | # Basic role syntax check 26 | - ansible-playbook tests/test.yml -i tests/inventory --syntax-check 27 | 28 | notifications: 29 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ -------------------------------------------------------------------------------- /src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &::before, 3 | &::after { 4 | content: ""; 5 | display: table; 6 | clear: both; 7 | } 8 | } 9 | 10 | @mixin flex($grow: 0, $shrink: 1, $basis: auto) { 11 | flex-grow: $grow; 12 | flex-shrink: $shrink; 13 | flex-basis: $basis; 14 | } 15 | 16 | @mixin font-face($font-name, $file-name) { 17 | @font-face { 18 | font-family: $font-name; 19 | src: url("fonts/#{$file-name}.woff2") format("woff2"), 20 | url("fonts/#{$file-name}.woff") format("woff"); 21 | } 22 | } 23 | 24 | @mixin hide-text { 25 | overflow: hidden; 26 | text-indent: 101%; 27 | white-space: nowrap; 28 | } 29 | 30 | @mixin min($width) { 31 | @media screen and (min-width: $width) { 32 | @content; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/test.yml: -------------------------------------------------------------------------------- 1 | - name: Test that we can access the gulp web server 2 | hosts: localhost 3 | gather_facts: no 4 | connection: local 5 | vars: 6 | host: "0.0.0.0" 7 | tasks: 8 | - name: Pause 9 | pause: minutes=5 10 | 11 | - name: Access the admin page 12 | get_url: 13 | url: "http://{{ host }}:8080/admin" 14 | dest: ./home_page.html 15 | 16 | - name: Grep the output file for expected content 17 | command: grep "Django administration" ./home_page.html 18 | register: output 19 | 20 | - name: Assert expected output found 21 | assert: 22 | that: 23 | - "'Django administration' in output.stdout" 24 | 25 | - name: Remove html file 26 | file: path=./home_page.html state=absent 27 | -------------------------------------------------------------------------------- /src/sass/_reset.scss: -------------------------------------------------------------------------------- 1 | html, body, div, 2 | header, footer, main, section, nav, 3 | h1, h2, h3, h4, h5, h6, img, svg, 4 | p, a, hr, span, ol, ul, li, 5 | form, input, label { 6 | margin: 0; 7 | padding: 0; 8 | border: none; 9 | font-size: 100%; 10 | font: inherit; 11 | vertical-align: baseline; 12 | } 13 | 14 | *, 15 | *::before, 16 | *::after { 17 | box-sizing: border-box; 18 | } 19 | 20 | img { 21 | max-width: 100%; 22 | } 23 | 24 | svg:not(:root) { 25 | overflow: hidden; 26 | } 27 | 28 | input { 29 | border-radius: 0; 30 | } 31 | 32 | ::placeholder { 33 | opacity: 1; 34 | } 35 | 36 | a { 37 | background-color: transparent; 38 | 39 | text-decoration: none; 40 | color: inherit; 41 | 42 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 43 | outline: none; 44 | } 45 | -------------------------------------------------------------------------------- /roles/django-gunicorn/files/wait_on_postgres.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import socket 5 | import time 6 | 7 | if __name__ == '__main__': 8 | 9 | postgres_is_alive = False 10 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 11 | tries = 0 12 | print ("Waiting on postgresql to start...") 13 | while not postgres_is_alive and tries < 20: 14 | tries += 1 15 | try: 16 | s.connect(('postgresql', 5432)) 17 | except socket.error: 18 | time.sleep(3) 19 | else: 20 | postgres_is_alive = True 21 | 22 | if postgres_is_alive: 23 | print ("Postgresql started!") 24 | sys.exit(0) 25 | else: 26 | print ("Unable to reach postgresql on port 5432") 27 | sys.exit(1) 28 | -------------------------------------------------------------------------------- /src/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

17 | Hello 18 |

19 |
    20 |
  • world !
  • 21 |
  • users !
  • 22 |
  • you!
  • 23 |
  • everybody !
  • 24 |
25 |
26 |
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "django-gulp-nginx", 3 | "version": "1.0.0", 4 | "description": "", 5 | "author": "", 6 | "license": "", 7 | "files": [], 8 | "dependencies": { 9 | "autoprefixer": "*", 10 | "babel-preset-es2015-rollup": "*", 11 | "bower": "*", 12 | "browser-sync": "*", 13 | "connect-history-api-fallback": "^1.1.0", 14 | "cssnano": "*", 15 | "del": "*", 16 | "gulp": "^3.9.1", 17 | "gulp-changed": "*", 18 | "gulp-cli": "^1.3.0", 19 | "gulp-concat": "*", 20 | "gulp-file-include": "*", 21 | "gulp-htmlmin": "*", 22 | "gulp-imagemin": "*", 23 | "gulp-plumber": "*", 24 | "gulp-postcss": "*", 25 | "gulp-sass": "*", 26 | "gulp-sourcemaps": "*", 27 | "gulp-uglify": "^1.0.1", 28 | "gulp-util": "*", 29 | "http-proxy-middleware": "*", 30 | "node-notifier": "*" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /meta.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Ansible 3 | description: Ansible Container Django demo 4 | company: Ansible by Red Hat 5 | 6 | # If the issue tracker for your role is not on GitHub, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: Apache 18 | 19 | min_ansible_container_version: 0.9.0 20 | 21 | # Optionally specify the branch Galaxy will use when accessing the GitHub 22 | # repo for this role. During role install, if no tags are available, 23 | # Galaxy will use this branch. During import Galaxy will access files on 24 | # this branch. If travis integration is cofigured, only notification for this 25 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 26 | # (usually master) will be used. 27 | #github_branch: 28 | 29 | tags: 30 | - container 31 | - django 32 | - demo 33 | 34 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Remove containers and images associated with the app 4 | # 5 | # Pass 'all' to remove everything or 'containers' or 'images' 6 | # 7 | 8 | if [[ $1 == 'all' || $1 == 'containers' ]]; then 9 | echo "Removing all django-gulp-nginx containers..." 10 | containers=$(docker ps -a --format "{{.Names}}" | grep -e ansible_django -e ansible_gulp -e ansible_nginx -e ansible_postgres | wc -l | tr -d '[[:space:]]') 11 | if [ ${containers} -gt 0 ]; then 12 | docker rm --force $(docker ps -a --format "{{.Names}}" | grep -e ansible_django -e ansible_nginx -e ansible_postgres -e ansible_gulp) 13 | else 14 | echo "No ansible_django, ansible_postgres, ansible_gulp, ansible_nginx containers found" 15 | fi 16 | fi 17 | 18 | if [[ $1 == 'all' || $1 == 'images' ]]; then 19 | project_name=$(basename $(python -c "from os import path; print(path.abspath(path.join(path.dirname('$0'), '..')))")) 20 | echo "Removing all ${project_name} images..." 21 | images=$(docker images -a --format "{{.Repository}}:{{.Tag}}" | grep ${project_name} | wc -l | tr -d '[[:space:]]') 22 | if [ ${images} -gt 0 ]; then 23 | docker rmi --force $(docker images -a --format "{{.Repository}}:{{.Tag}}" | grep ${project_name}) 24 | else 25 | echo "No ${project_name} images found" 26 | fi 27 | fi 28 | -------------------------------------------------------------------------------- /update-authors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import absolute_import, print_function 5 | 6 | import logging 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | import subprocess 11 | from collections import defaultdict 12 | 13 | user_scores = defaultdict(int) 14 | 15 | git_log = subprocess.check_output("git log --shortstat --no-merges --pretty='%aN <%aE>'", 16 | shell=True) 17 | log_entries = git_log.strip().split('\n') 18 | while log_entries: 19 | author = log_entries.pop(0) 20 | _ = log_entries.pop(0) 21 | commit_line = log_entries.pop(0) 22 | commit_parts = [s.strip() for s in commit_line.split(', ')] 23 | commit_data = {'files': 0, 'insertions': 0, 'deletions': 0} 24 | for clause in commit_parts: 25 | count, action = clause.split(' ', 1) 26 | if action.endswith('(+)'): 27 | user_scores[author] += int(count) 28 | elif action.endswith('(-)'): 29 | user_scores[author] += int(count) 30 | else: 31 | user_scores[author] += int(count) 32 | 33 | sorted_user_scores = sorted(user_scores.items(), key=lambda tpl: tpl[1], reverse=True) 34 | 35 | print("This project has been contribued to by the following authors:\n" 36 | "This list is automatically generated - please file an issue for corrections)\n") 37 | for author, _ in sorted_user_scores: 38 | print(author) 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | project_name = $(shell basename $$PWD) 2 | 3 | .PHONY: build build_from_scratch build_debug clean clean_containers 4 | run run_prod stop django_manage django_exec gulp_build 5 | 6 | clean: 7 | @./scripts/clean.sh all 8 | -docker volume rm djangogulpnginx_postgres-data 9 | -docker volume rm djangogulpnginx_temp-space 10 | 11 | clean_containers: 12 | @./scripts/clean.sh containers 13 | -docker volume rm djangogulpnginx_postgres-data 14 | -docker volume rm djangogulpnginx_temp-space 15 | 16 | build: 17 | @./scripts/clean.sh containers 18 | -docker volume rm djangogulpnginx_postgres-data 19 | -docker volume rm djangogulpnginx_temp-space 20 | ansible-container build 21 | 22 | build_from_scratch: clean 23 | ansible-container build 24 | 25 | build_debug: 26 | @./scripts/clean.sh containers 27 | -docker volume rm djangogulpnginx_postgres-data 28 | -docker volume rm djangogulpnginx_temp-space 29 | ansible-container --debug build 30 | 31 | run: 32 | ansible-container run 33 | 34 | run_debug: 35 | ansible-container --debug run 36 | 37 | run_prod: 38 | ansible-container run --production 39 | 40 | stop: 41 | ansible-container stop 42 | 43 | django_manage: 44 | @docker exec -it djangogulpnginx_django_1 manage_django.sh $(filter-out $@,$(MAKECMDGOALS)) 45 | 46 | django_exec: 47 | @docker exec -it djangogulpnginx_django_1 /bin/bash 48 | 49 | gulp_build: 50 | @docker exec -it djangogulpnginx_gulp_1 /node/scripts/gulp_build.sh 51 | 52 | %: 53 | @: 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | dist: trusty 3 | sudo: required 4 | group: edge # https://blog.travis-ci.com/2017-06-19-trusty-updates-2017-Q2 5 | services: 6 | - docker 7 | before_install: 8 | - sudo apt-add-repository 'deb http://archive.ubuntu.com/ubuntu trusty-backports universe' 9 | - sudo apt-get update -qq 10 | install: 11 | - pip install https://github.com/ansible/ansible/archive/devel.tar.gz 12 | - pip install -e git+https://github.com/ansible/ansible-container.git@develop#egg=ansible_container[docker] 13 | script: 14 | - docker version 15 | - docker-compose version 16 | - docker info 17 | - ansible-container --debug --devel build 18 | - ansible-container --devel run 19 | - docker ps -a 20 | - docker images 21 | - cd test 22 | - ansible-playbook -i inventory test.yml 23 | - cd .. 24 | - ansible-container --devel stop -f 25 | notifications: 26 | email: false 27 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ 28 | slack: 29 | rooms: 30 | secure: BhmCskXdew4YthYIRonVBqEmvHYCHSnGuctmQ8EG2HInSxE232nQFoKh4QgfHY+VzJVZ+Id6oyea/VoqQg76tItNXvnyRqVXBrJO2Q+5kL3sO5Xxg7NpJJNolSnbsow8VVdXjgwH9qZ5kZeWhW/bvnZC5ZxiaYzakVCjS23y9YG6bX7Y7Owbkw2o33VTMg43LT4bZ87iygEQDaZBsYsi8EXSo5RW/uCYF04Y3CPVpLcR3FWwTRKoxvHCMkDsOYiLfkYVY6+B2oUS0YAzBZi4StzURxY5ekUz7QdDgU1CpJwX6qGgQWwa3VeUIIT20SgE/G8O5PAbirDllpvT0Ovtw9Lqv7ILs7AfmxzWzw/a1zk6UPLZWYUE5E1P3yhh21kgZIgbWvBObnCarMvLAQI9DMv8dYC/go+5fLr7dH2O8HBtUu0okV85vlLdD8yhz4bjh8O4L2kDV3GDQgSZNA5FJuKOS1MKsf8X9Z84u6/FjGi/pjP1lGycZQ4ShJS4swxE1N/zLw/s+2pc4tU66LthJPSk7vq6Rl9Alk8yOIKloUjA+Mat1jUPP5qm6aM5yCxFC/ln+KwvXByqBxOiuNfl8GoJzlgzJXgBnRIZ3JYuJpNDUrKinItH2JJhrPXXZOiJ/h7KRAqGb4a62fQqQYsymIMEyC5IaUjnE/yWwUE4RnY= 31 | on_success: change 32 | on_failure: always 33 | -------------------------------------------------------------------------------- /src/sass/_index.scss: -------------------------------------------------------------------------------- 1 | // Inspired by http://codepen.io/yoannhel/pen/sJpDj 2 | 3 | body { 4 | width:100%; 5 | height:100%; 6 | position:fixed; 7 | background-color:#34495e; 8 | } 9 | 10 | .content { 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | transform: translate(-50%, -50%); 15 | font-size: 35px; 16 | line-height: 40px; 17 | font-family: 'Lato', sans-serif; 18 | color: #ecf0f1; 19 | height: 160px; 20 | overflow:hidden; 21 | } 22 | 23 | .visible { 24 | font-weight:600; 25 | overflow:hidden; 26 | height:40px; 27 | padding:0 40px; 28 | 29 | &:before { 30 | content:'['; 31 | left: 0; 32 | line-height:40px; 33 | } 34 | &:after { 35 | content:']'; 36 | position:absolute; 37 | right: 0; 38 | line-height:40px; 39 | } 40 | &:after, &:before { 41 | position:absolute; 42 | top:0; 43 | color:#16a085; 44 | font-size:42px; 45 | -webkit-animation-name: opacity; 46 | -webkit-animation-duration: 2s; 47 | -webkit-animation-iteration-count: infinite; 48 | animation-name: opacity; 49 | animation-duration: 2s; 50 | animation-iteration-count: infinite; 51 | 52 | } 53 | } 54 | 55 | p { 56 | display:inline; 57 | float:left; 58 | margin:0; 59 | } 60 | 61 | ul { 62 | margin-top:0; 63 | padding-left:110px; 64 | text-align:left; 65 | list-style:none; 66 | -webkit-animation-name: change; 67 | -webkit-animation-duration: 6s; 68 | -webkit-animation-iteration-count: infinite; 69 | animation-name: change; 70 | animation-duration: 6s; 71 | animation-iteration-count: infinite; 72 | } 73 | 74 | ul li { 75 | line-height:40px; 76 | margin:0; 77 | } 78 | 79 | @-webkit-keyframes opacity { 80 | 0%, 100% {opacity:0;} 81 | 50% {opacity:1;} 82 | } 83 | 84 | @-webkit-keyframes change { 85 | 0%, 12%, 100% {transform:translateY(0);} 86 | 17%,29% {transform:translateY(-25%);} 87 | 34%,46% {transform:translateY(-50%);} 88 | 51%,63% {transform:translateY(-75%);} 89 | 68%,80% {transform:translateY(-50%);} 90 | 85%,97% {transform:translateY(-25%);} 91 | } 92 | 93 | @keyframes opacity { 94 | 0%, 100% {opacity:0;} 95 | 50% {opacity:1;} 96 | } 97 | 98 | @keyframes change { 99 | 0%, 12%, 100% {transform:translateY(0);} 100 | 17%,29% {transform:translateY(-25%);} 101 | 34%,46% {transform:translateY(-50%);} 102 | 51%,63% {transform:translateY(-75%);} 103 | 68%,80% {transform:translateY(-50%);} 104 | 85%,97% {transform:translateY(-25%);} 105 | } 106 | -------------------------------------------------------------------------------- /project/settings.py: -------------------------------------------------------------------------------- 1 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 2 | import os 3 | 4 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | 6 | 7 | # Quick-start development settings - unsuitable for production 8 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 9 | 10 | # SECURITY WARNING: keep the secret key used in production secret! 11 | SECRET_KEY = 'wj9rt420!n0(np*gi6#rpb7)y*b2qzp#@3l8p66na#q(4svs!e' 12 | 13 | # SECURITY WARNING: don't run with debug turned on in production! 14 | DEBUG = True 15 | 16 | ALLOWED_HOSTS = ['*'] 17 | 18 | 19 | # Application definition 20 | 21 | INSTALLED_APPS = ( 22 | 'django.contrib.admin', 23 | 'django.contrib.auth', 24 | 'django.contrib.contenttypes', 25 | 'django.contrib.sessions', 26 | 'django.contrib.messages', 27 | 'django.contrib.staticfiles', 28 | 'gunicorn', 29 | ) 30 | 31 | MIDDLEWARE = [ 32 | 'django.middleware.security.SecurityMiddleware', 33 | 'django.contrib.sessions.middleware.SessionMiddleware', 34 | 'django.middleware.common.CommonMiddleware', 35 | 'django.middleware.csrf.CsrfViewMiddleware', 36 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 37 | 'django.contrib.messages.middleware.MessageMiddleware', 38 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 39 | ] 40 | 41 | ROOT_URLCONF = 'project.urls' 42 | 43 | TEMPLATES = [ 44 | { 45 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 46 | 'DIRS': [], 47 | 'APP_DIRS': True, 48 | 'OPTIONS': { 49 | 'context_processors': [ 50 | 'django.template.context_processors.debug', 51 | 'django.template.context_processors.request', 52 | 'django.contrib.auth.context_processors.auth', 53 | 'django.contrib.messages.context_processors.messages', 54 | ], 55 | }, 56 | }, 57 | ] 58 | 59 | WSGI_APPLICATION = 'project.wsgi.application' 60 | 61 | 62 | # Database 63 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 64 | 65 | import dj_database_url 66 | DATABASES = { 67 | 'default': dj_database_url.config() 68 | } 69 | 70 | 71 | # Internationalization 72 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 73 | 74 | LANGUAGE_CODE = 'en-us' 75 | 76 | TIME_ZONE = 'UTC' 77 | 78 | USE_I18N = True 79 | 80 | USE_L10N = True 81 | 82 | USE_TZ = True 83 | 84 | 85 | # Static files (CSS, JavaScript, Images) 86 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 87 | 88 | STATIC_URL = '/static/' 89 | STATIC_ROOT = '/django/dist/' 90 | -------------------------------------------------------------------------------- /roles/gulp-static/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install dumb init 3 | get_url: 4 | dest: /usr/bin/dumb-init 5 | url: https://github.com/Yelp/dumb-init/releases/download/v1.0.2/dumb-init_1.0.2_amd64 6 | mode: 0775 7 | validate_certs: no 8 | 9 | - name: Make node user 10 | user: 11 | name: "{{ NODE_USER }}" 12 | state: present 13 | createhome: yes 14 | home: "{{ NODE_HOME }}/" 15 | uid: 1000 16 | group: root 17 | 18 | - name: Make node home 19 | file: 20 | path: "{{ NODE_HOME }}" 21 | state: directory 22 | owner: "{{ NODE_USER }}" 23 | group: root 24 | mode: 0777 25 | 26 | - name: Create dist directory 27 | file: 28 | path: "{{ NODE_HOME }}/dist" 29 | state: directory 30 | owner: "{{ NODE_USER }}" 31 | group: root 32 | mode: 0777 33 | 34 | - name: Install nodejs 35 | yum: 36 | name: "{{ item }}" 37 | disable_gpg_check: yes 38 | with_items: 39 | - "{{ NODE_RPM_URL }}" 40 | - git 41 | - which 42 | 43 | - name: Create /tmp/django 44 | file: 45 | path: /tmp/django 46 | state: directory 47 | owner: "{{ NODE_USER }}" 48 | group: root 49 | mode: 0777 50 | 51 | - name: Clean npm cache 52 | command: npm cache clean -f 53 | 54 | - name: Install the node helper 55 | command: npm install -g n 56 | 57 | - name: Install the latest node 58 | command: n stable 59 | 60 | - name: Copy frontend source files 61 | synchronize: 62 | src: "/src/{{ item }}" 63 | dest: "{{ NODE_HOME }}" 64 | with_items: 65 | - bower.json 66 | - package.json 67 | - gulpfile.js 68 | - .bowerrc 69 | - src 70 | 71 | - name: Install node packages 72 | command: npm install 73 | args: 74 | chdir: "{{ NODE_HOME }}" 75 | 76 | - name: Install gulp 77 | command: npm install --global gulp-cli 78 | 79 | - name: Install bower 80 | command: npm install --global bower 81 | 82 | - name: Template entrypoint script 83 | template: 84 | src: entrypoint.sh.j2 85 | dest: /entrypoint.sh 86 | mode: 0777 87 | 88 | # Create static content and copy back to conductor, 89 | # making it available for Nginx build. 90 | 91 | - name: Build static assets 92 | command: gulp build 93 | args: 94 | chdir: "{{ NODE_HOME }}" 95 | 96 | - name: Install bower packages 97 | command: bower install 98 | args: 99 | chdir: "{{ NODE_HOME }}" 100 | remote_user: "{{ NODE_USER }}" 101 | 102 | - name: Copy static assets to conductor 103 | synchronize: 104 | src: "{{ NODE_HOME }}/dist/" 105 | dest: /tmp/dist/ 106 | mode: pull 107 | -------------------------------------------------------------------------------- /container.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | settings: 4 | 5 | conductor: 6 | base: 'centos:7' 7 | volumes: 8 | - ${PWD}/temp-space:/tmp 9 | 10 | k8s_namespace: 11 | name: demo 12 | display_name: Ansible Container Demo 13 | description: Django framework demo 14 | 15 | defaults: 16 | POSTGRES_USER: django 17 | POSTGRES_PASSWORD: sesame 18 | POSTGRES_DB: django 19 | DJANGO_ROOT: /django 20 | DJANGO_USER: django 21 | DJANGO_PORT: 8080 22 | DJANGO_VENV: /venv 23 | NODE_USER: node 24 | NODE_HOME: /node 25 | NODE_ROOT: '' 26 | GULP_DEV_PORT: 8080 27 | 28 | services: 29 | django: 30 | from: 'centos:7' 31 | roles: 32 | - role: django-gunicorn 33 | environment: 34 | DATABASE_URL: 'pgsql://{{ POSTGRES_USER }}:{{ POSTGRES_PASSWORD }}@postgresql:5432/{{ POSTGRES_DB }}' 35 | DJANGO_ROOT: '{{ DJANGO_ROOT }}' 36 | DJANGO_VENV: '{{ DJANGO_VENV }}' 37 | expose: 38 | - '{{ DJANGO_PORT }}' 39 | working_dir: '{{ DJANGO_ROOT }}' 40 | links: 41 | - postgresql 42 | user: '{{ DJANGO_USER }}' 43 | command: ['/usr/bin/dumb-init', '{{ DJANGO_VENV }}/bin/gunicorn', -w, '2', -b, '0.0.0.0:{{ DJANGO_PORT }}', 'project.wsgi:application'] 44 | entrypoint: [/usr/bin/entrypoint.sh] 45 | dev_overrides: 46 | volumes: 47 | - '$PWD:{{ DJANGO_ROOT }}' 48 | command: [/usr/bin/dumb-init, '{{ DJANGO_VENV }}/bin/python', manage.py, runserver, '0.0.0.0:{{ DJANGO_PORT }}'] 49 | depends_on: 50 | - postgresql 51 | 52 | gulp: 53 | from: 'centos:7' 54 | roles: 55 | - role: gulp-static 56 | working_dir: '{{ NODE_HOME }}' 57 | command: ['/bin/false'] 58 | environment: 59 | NODE_HOME: '{{ NODE_HOME }}' 60 | dev_overrides: 61 | entrypoint: [/entrypoint.sh] 62 | command: [/usr/bin/dumb-init, /usr/local/bin/gulp] 63 | ports: 64 | - '8080:{{ GULP_DEV_PORT }}' 65 | - 3001:3001 66 | links: 67 | - django 68 | volumes: 69 | - '$PWD:{{ NODE_HOME }}' 70 | openshift: 71 | state: absent 72 | 73 | nginx: 74 | from: 'centos:7' 75 | roles: 76 | - role: ansible.nginx-container 77 | ASSET_PATHS: 78 | - /tmp/dist 79 | PROXY: yes 80 | PROXY_PASS: 'http://django:8080' 81 | PROXY_LOCATION: "~* /(admin|api)" 82 | ports: 83 | - '{{ DJANGO_PORT }}:8000' 84 | links: 85 | - django 86 | dev_overrides: 87 | ports: [] 88 | command: /bin/false 89 | 90 | postgresql: 91 | # Uses a pre-built postgresql image from Docker Hub 92 | from: 'centos/postgresql-95-centos7:latest' 93 | environment: 94 | - 'POSTGRESQL_DATABASE={{ POSTGRES_DB }}' 95 | - 'POSTGRESQL_USER={{ POSTGRES_USER }}' 96 | - 'POSTGRESQL_PASSWORD={{ POSTGRES_PASSWORD }}' 97 | expose: 98 | - 5432 99 | 100 | registries: 101 | local_openshift: 102 | url: https://local.openshift # The push-to URL, created by chouseknecht.minishift-up role 103 | namespace: demo 104 | pull_from_url: 172.30.1.1:5000 # The pull-from URL from within minishift 105 | -------------------------------------------------------------------------------- /roles/django-gunicorn/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Make Django user 3 | user: 4 | name: "{{ DJANGO_USER }}" 5 | uid: 1000 6 | group: root 7 | state: present 8 | createhome: yes 9 | home: "{{ DJANGO_ROOT }}" 10 | 11 | - name: Clean yum cache 12 | command: yum -y clean all 13 | 14 | - name: Fix repos 15 | command: yum -y --disablerepo=\* --enablerepo=base,updates update 16 | 17 | - name: Update all 18 | yum: 19 | name: '*' 20 | state: latest 21 | 22 | - name: Install development tools 23 | yum: 24 | name: "{{ item }}" 25 | state: present 26 | with_items: 27 | - yum-utils 28 | - '@Development tools' 29 | 30 | - name: Install yum repos 31 | yum: 32 | name: '{{ item }}' 33 | state: present 34 | disable_gpg_check: yes 35 | with_items: 36 | - 'https://centos7.iuscommunity.org/ius-release.rpm' 37 | - epel-release 38 | 39 | - name: Rebuild yum cache 40 | shell: yum -y clean headers && yum -y clean metadata && yum -y makecache 41 | 42 | - name: Install python36 43 | yum: 44 | name: "{{ item }}" 45 | state: present 46 | update_cache: yes 47 | with_items: 48 | - python36u 49 | - python36u-devel 50 | 51 | - name: Install python36 52 | yum: 53 | name: "{{ item }}" 54 | state: present 55 | update_cache: yes 56 | with_items: 57 | - python36u 58 | - python36u-devel 59 | 60 | - name: Create a virtualenv 61 | command: 'python3.6 -m venv {{ DJANGO_VENV }}' 62 | args: 63 | chdir: / 64 | 65 | - name: Set venv permissions 66 | file: 67 | path: '{{ DJANGO_VENV }}' 68 | state: directory 69 | owner: '{{ DJANGO_USER }}' 70 | group: root 71 | recurse: yes 72 | 73 | - name: Install dumb init 74 | get_url: 75 | dest: /usr/bin/dumb-init 76 | url: https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 77 | mode: 0775 78 | validate_certs: no 79 | 80 | - name: Install RPM deps 81 | yum: 82 | name: "{{ item }}" 83 | state: latest 84 | update_cache: yes 85 | disable_gpg_check: yes 86 | with_items: "{{ DJANGO_RPM_DEPS }}" 87 | 88 | - name: Create /django 89 | file: 90 | path: "{{ DJANGO_ROOT }}" 91 | state: directory 92 | owner: "{{ DJANGO_USER }}" 93 | group: root 94 | mode: 0775 95 | 96 | - name: Copy core source items 97 | synchronize: 98 | src: "/src/{{ item }}" 99 | dest: "{{ DJANGO_ROOT }}" 100 | remote_user: "{{ DJANGO_USER }}" 101 | with_items: 102 | - manage.py 103 | - package.json 104 | - project 105 | - requirements.txt 106 | - requirements.yml 107 | 108 | - name: Install Python requirements 109 | pip: 110 | executable: "{{ DJANGO_VENV }}/bin/pip" 111 | requirements: "{{ DJANGO_ROOT }}/requirements.txt" 112 | remote_user: "{{ DJANGO_USER }}" 113 | 114 | - name: Remove staticfiles dir 115 | file: 116 | name: "{{ DJANGO_STATIC_ROOT }}" 117 | state: absent 118 | 119 | - name: Make staticfiles dir 120 | file: 121 | name: "{{ DJANGO_STATIC_ROOT }}" 122 | state: directory 123 | owner: "{{ DJANGO_USER }}" 124 | group: root 125 | mode: 0775 126 | 127 | - name: Collect staticfiles 128 | command: "{{ DJANGO_VENV }}/bin/python manage.py collectstatic --noinput" 129 | args: 130 | chdir: "{{ DJANGO_ROOT }}" 131 | remote_user: "{{ DJANGO_USER }}" 132 | 133 | - name: Copy static assets to conductor 134 | synchronize: 135 | src: "{{ DJANGO_STATIC_ROOT}}/" 136 | dest: /tmp/dist/ 137 | mode: pull 138 | 139 | - name: Copy entrypoint 140 | copy: 141 | src: "{{ role_path }}/files/{{ item }}" 142 | dest: "/usr/bin/{{ item }}" 143 | owner: root 144 | group: root 145 | mode: 0775 146 | with_items: 147 | - entrypoint.sh 148 | - wait_on_postgres.py 149 | - manage_django.sh 150 | -------------------------------------------------------------------------------- /roles/gulp-static/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Joshua "jag" Ginsberg 3 | description: Gulp static build and serve 4 | company: Ansible 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 2.1 20 | 21 | # Optionally specify the branch Galaxy will use when accessing the GitHub 22 | # repo for this role. During role install, if no tags are available, 23 | # Galaxy will use this branch. During import Galaxy will access files on 24 | # this branch. If travis integration is cofigured, only notification for this 25 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 26 | # (usually master) will be used. 27 | #github_branch: 28 | 29 | # 30 | # Below are all platforms currently available. Just uncomment 31 | # the ones that apply to your role. If you don't see your 32 | # platform on this list, let us know and we'll get it added! 33 | # 34 | platforms: 35 | - name: EL 36 | versions: 37 | # - all 38 | # - 5 39 | # - 6 40 | - 7 41 | #- name: GenericUNIX 42 | # versions: 43 | # - all 44 | # - any 45 | #- name: OpenBSD 46 | # versions: 47 | # - all 48 | # - 5.6 49 | # - 5.7 50 | # - 5.8 51 | # - 5.9 52 | # - 6.0 53 | #- name: Fedora 54 | # versions: 55 | # - all 56 | # - 16 57 | # - 17 58 | # - 18 59 | # - 19 60 | # - 20 61 | # - 21 62 | # - 22 63 | # - 23 64 | #- name: opensuse 65 | # versions: 66 | # - all 67 | # - 12.1 68 | # - 12.2 69 | # - 12.3 70 | # - 13.1 71 | # - 13.2 72 | #- name: MacOSX 73 | # versions: 74 | # - all 75 | # - 10.10 76 | # - 10.11 77 | # - 10.12 78 | # - 10.7 79 | # - 10.8 80 | # - 10.9 81 | #- name: IOS 82 | # versions: 83 | # - all 84 | # - any 85 | #- name: Solaris 86 | # versions: 87 | # - all 88 | # - 10 89 | # - 11.0 90 | # - 11.1 91 | # - 11.2 92 | # - 11.3 93 | #- name: SmartOS 94 | # versions: 95 | # - all 96 | # - any 97 | #- name: eos 98 | # versions: 99 | # - all 100 | # - Any 101 | #- name: Windows 102 | # versions: 103 | # - all 104 | # - 2012R2 105 | #- name: Amazon 106 | # versions: 107 | # - all 108 | # - 2013.03 109 | # - 2013.09 110 | #- name: GenericBSD 111 | # versions: 112 | # - all 113 | # - any 114 | #- name: Junos 115 | # versions: 116 | # - all 117 | # - any 118 | #- name: FreeBSD 119 | # versions: 120 | # - all 121 | # - 10.0 122 | # - 10.1 123 | # - 10.2 124 | # - 10.3 125 | # - 8.0 126 | # - 8.1 127 | # - 8.2 128 | # - 8.3 129 | # - 8.4 130 | # - 9.0 131 | # - 9.1 132 | # - 9.1 133 | # - 9.2 134 | # - 9.3 135 | #- name: Ubuntu 136 | # versions: 137 | # - all 138 | # - lucid 139 | # - maverick 140 | # - natty 141 | # - oneiric 142 | # - precise 143 | # - quantal 144 | # - raring 145 | # - saucy 146 | # - trusty 147 | # - utopic 148 | # - vivid 149 | # - wily 150 | # - xenial 151 | #- name: SLES 152 | # versions: 153 | # - all 154 | # - 10SP3 155 | # - 10SP4 156 | # - 11 157 | # - 11SP1 158 | # - 11SP2 159 | # - 11SP3 160 | # - 11SP4 161 | # - 12 162 | # - 12SP1 163 | #- name: GenericLinux 164 | # versions: 165 | # - all 166 | # - any 167 | #- name: NXOS 168 | # versions: 169 | # - all 170 | # - any 171 | #- name: Debian 172 | # versions: 173 | # - all 174 | # - etch 175 | # - jessie 176 | # - lenny 177 | # - sid 178 | # - squeeze 179 | # - stretch 180 | # - wheezy 181 | 182 | galaxy_tags: [] 183 | # List tags for your role here, one per line. A tag is 184 | # a keyword that describes and categorizes the role. 185 | # Users find roles by searching for tags. Be sure to 186 | # remove the '[]' above if you add tags to this list. 187 | # 188 | # NOTE: A tag is limited to a single word comprised of 189 | # alphanumeric characters. Maximum 20 tags per role. 190 | 191 | dependencies: [] 192 | # List your role dependencies here, one per line. 193 | # Be sure to remove the '[]' above if you add dependencies 194 | # to this list. 195 | -------------------------------------------------------------------------------- /roles/django-gunicorn/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Joshua "jag" Ginsberg 3 | description: Django running on gunicorn 4 | company: Ansible 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 2.1 20 | 21 | # Optionally specify the branch Galaxy will use when accessing the GitHub 22 | # repo for this role. During role install, if no tags are available, 23 | # Galaxy will use this branch. During import Galaxy will access files on 24 | # this branch. If travis integration is cofigured, only notification for this 25 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 26 | # (usually master) will be used. 27 | #github_branch: 28 | 29 | # 30 | # Below are all platforms currently available. Just uncomment 31 | # the ones that apply to your role. If you don't see your 32 | # platform on this list, let us know and we'll get it added! 33 | # 34 | platforms: 35 | - name: EL 36 | versions: 37 | # - all 38 | # - 5 39 | # - 6 40 | - 7 41 | #- name: GenericUNIX 42 | # versions: 43 | # - all 44 | # - any 45 | #- name: OpenBSD 46 | # versions: 47 | # - all 48 | # - 5.6 49 | # - 5.7 50 | # - 5.8 51 | # - 5.9 52 | # - 6.0 53 | #- name: Fedora 54 | # versions: 55 | # - all 56 | # - 16 57 | # - 17 58 | # - 18 59 | # - 19 60 | # - 20 61 | # - 21 62 | # - 22 63 | # - 23 64 | #- name: opensuse 65 | # versions: 66 | # - all 67 | # - 12.1 68 | # - 12.2 69 | # - 12.3 70 | # - 13.1 71 | # - 13.2 72 | #- name: MacOSX 73 | # versions: 74 | # - all 75 | # - 10.10 76 | # - 10.11 77 | # - 10.12 78 | # - 10.7 79 | # - 10.8 80 | # - 10.9 81 | #- name: IOS 82 | # versions: 83 | # - all 84 | # - any 85 | #- name: Solaris 86 | # versions: 87 | # - all 88 | # - 10 89 | # - 11.0 90 | # - 11.1 91 | # - 11.2 92 | # - 11.3 93 | #- name: SmartOS 94 | # versions: 95 | # - all 96 | # - any 97 | #- name: eos 98 | # versions: 99 | # - all 100 | # - Any 101 | #- name: Windows 102 | # versions: 103 | # - all 104 | # - 2012R2 105 | #- name: Amazon 106 | # versions: 107 | # - all 108 | # - 2013.03 109 | # - 2013.09 110 | #- name: GenericBSD 111 | # versions: 112 | # - all 113 | # - any 114 | #- name: Junos 115 | # versions: 116 | # - all 117 | # - any 118 | #- name: FreeBSD 119 | # versions: 120 | # - all 121 | # - 10.0 122 | # - 10.1 123 | # - 10.2 124 | # - 10.3 125 | # - 8.0 126 | # - 8.1 127 | # - 8.2 128 | # - 8.3 129 | # - 8.4 130 | # - 9.0 131 | # - 9.1 132 | # - 9.1 133 | # - 9.2 134 | # - 9.3 135 | #- name: Ubuntu 136 | # versions: 137 | # - all 138 | # - lucid 139 | # - maverick 140 | # - natty 141 | # - oneiric 142 | # - precise 143 | # - quantal 144 | # - raring 145 | # - saucy 146 | # - trusty 147 | # - utopic 148 | # - vivid 149 | # - wily 150 | # - xenial 151 | #- name: SLES 152 | # versions: 153 | # - all 154 | # - 10SP3 155 | # - 10SP4 156 | # - 11 157 | # - 11SP1 158 | # - 11SP2 159 | # - 11SP3 160 | # - 11SP4 161 | # - 12 162 | # - 12SP1 163 | #- name: GenericLinux 164 | # versions: 165 | # - all 166 | # - any 167 | #- name: NXOS 168 | # versions: 169 | # - all 170 | # - any 171 | #- name: Debian 172 | # versions: 173 | # - all 174 | # - etch 175 | # - jessie 176 | # - lenny 177 | # - sid 178 | # - squeeze 179 | # - stretch 180 | # - wheezy 181 | 182 | galaxy_tags: [] 183 | # List tags for your role here, one per line. A tag is 184 | # a keyword that describes and categorizes the role. 185 | # Users find roles by searching for tags. Be sure to 186 | # remove the '[]' above if you add tags to this list. 187 | # 188 | # NOTE: A tag is limited to a single word comprised of 189 | # alphanumeric characters. Maximum 20 tags per role. 190 | 191 | dependencies: [] 192 | # List your role dependencies here, one per line. 193 | # Be sure to remove the '[]' above if you add dependencies 194 | # to this list. 195 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const prefixer = require('autoprefixer') 2 | const sync = require('browser-sync') 3 | const cssnano = require('cssnano') 4 | const del = require('del') 5 | const fs = require('fs') 6 | const gulp = require('gulp') 7 | const changed = require('gulp-changed') 8 | const include = require('gulp-file-include') 9 | const htmlmin = require('gulp-htmlmin') 10 | const imagemin = require('gulp-imagemin') 11 | const plumber = require('gulp-plumber') 12 | const postcss = require('gulp-postcss') 13 | const sass = require('gulp-sass') 14 | const maps = require('gulp-sourcemaps') 15 | const notifier = require('node-notifier') 16 | const uglify = require('gulp-uglify') 17 | const concat = require('gulp-concat') 18 | const proxy = require('http-proxy-middleware') 19 | const util = require('gulp-util') 20 | const history = require('connect-history-api-fallback') 21 | 22 | // error handler 23 | 24 | const onError = function(error) { 25 | notifier.notify({ 26 | 'title': 'Error', 27 | 'message': 'Compilation failure.' 28 | }) 29 | 30 | console.log(error) 31 | this.emit('end') 32 | } 33 | 34 | // clean 35 | 36 | gulp.task('clean', function() { 37 | return del(['dist/*.html', 'dist/*.css', 'dist/js/**/*', 'dist/lib/**/*', 38 | 'dist/maps/**/*', 'dist/templates/**/*', 'dist/html/**/*', 39 | 'dist/images/**/*']) 40 | }) 41 | 42 | // html 43 | 44 | gulp.task('html', ['images'], function() { 45 | return gulp.src('src/html/**/*.html') 46 | .pipe(plumber({ errorHandler: onError })) 47 | .pipe(include({ prefix: '@', basepath: 'dist/images/' })) 48 | .pipe(htmlmin({ collapseWhitespace: true, removeComments: true })) 49 | .pipe(gulp.dest('dist')) 50 | }) 51 | 52 | // templates 53 | 54 | gulp.task('templates', function() { 55 | return gulp.src('src/templates/**/*.html') 56 | .pipe(plumber({ errorHandler: onError })) 57 | .pipe(include({ prefix: '@', basepath: 'dist/images/' })) 58 | .pipe(htmlmin({ collapseWhitespace: true, removeComments: true })) 59 | .pipe(gulp.dest('dist/templates')) 60 | }) 61 | 62 | // sass 63 | 64 | const processors = [ 65 | prefixer({ browsers: 'last 2 versions' }), 66 | cssnano({ safe: true }) 67 | ] 68 | 69 | gulp.task('sass', function() { 70 | return gulp.src('src/sass/style.scss') 71 | .pipe(plumber({ errorHandler: onError })) 72 | .pipe(maps.init()) 73 | .pipe(sass()) 74 | .pipe(postcss(processors)) 75 | .pipe(maps.write('./maps', { addComment: false })) 76 | .pipe(gulp.dest('dist/')) 77 | }) 78 | 79 | // js 80 | 81 | gulp.task('clean-js', function() { 82 | return del(['dist/js/**']) 83 | }) 84 | 85 | gulp.task('js', ['clean-js'], function() { 86 | return gulp.src('src/js/**/*.js') 87 | .pipe(maps.init()) 88 | .pipe(concat('bundle.min.js')) 89 | .pipe(uglify().on('error', util.log)) 90 | .pipe(maps.write()) 91 | .pipe(gulp.dest('dist/js')); 92 | }) 93 | 94 | 95 | // images 96 | 97 | gulp.task('images', function() { 98 | return gulp.src('src/images/**/*.{gif,jpg,png,svg}') 99 | .pipe(plumber({ errorHandler: onError })) 100 | .pipe(changed('dist/images')) 101 | .pipe(imagemin({ progressive: true, interlaced: true })) 102 | .pipe(gulp.dest('dist/images')) 103 | }) 104 | 105 | // fonts, videos, favicon, lib 106 | 107 | const others = [ 108 | { 109 | name: 'fonts', 110 | src: '/fonts/**/*.{woff,woff2}', 111 | dest: '/fonts' 112 | }, { 113 | name: 'videos', 114 | src: '/videos/**/*', 115 | dest: '/videos' 116 | }, { 117 | name: 'favicon', 118 | src: '/favicon.ico', 119 | dest: '' 120 | }, { 121 | name: 'lib', 122 | src: '/lib/**/*', 123 | dest: '/lib' 124 | } 125 | ] 126 | 127 | others.forEach(function(object) { 128 | gulp.task(object.name, function() { 129 | return gulp.src('src' + object.src) 130 | .pipe(plumber({ errorHandler: onError })) 131 | .pipe(gulp.dest('dist' + object.dest)) 132 | }) 133 | }) 134 | 135 | // server 136 | 137 | const server = sync.create() 138 | const reload = sync.reload 139 | 140 | const sendMaps = function(req, res, next) { 141 | const filename = req.url.split('/').pop() 142 | const extension = filename.split('.').pop() 143 | 144 | if(extension === 'css' || extension === 'js') { 145 | res.setHeader('X-SourceMap', '/maps/' + filename + '.map') 146 | } 147 | 148 | return next() 149 | } 150 | 151 | gulp.task('server', function() { 152 | 153 | // django endpoints 154 | var django = 'http://django:8080' 155 | var proxyAdmin = proxy('/admin', {target: django, xfwd: true}) 156 | var proxyStatic= proxy('/static', {target: django, xfwd: true}) 157 | var proxyApi = proxy('/api', {target: django, xfwd: true}) 158 | 159 | sync({ 160 | notify: false, 161 | open: false, 162 | port: 8080, 163 | watchOptions: { 164 | ignored: '*.map' 165 | }, 166 | server: { 167 | baseDir: 'dist', 168 | index: 'index.html', 169 | middleware: [proxyApi, proxyAdmin, proxyStatic, history()] 170 | } 171 | }); 172 | }) 173 | 174 | // watch 175 | 176 | gulp.task('watch', function() { 177 | gulp.watch('src/html/**/*.html', ['html', reload]) 178 | gulp.watch('src/templates/**/*.html', ['templates', reload]) 179 | gulp.watch('src/sass/**/*.scss', ['sass', reload]) 180 | gulp.watch('src/js/**/*.js', ['js', reload]) 181 | gulp.watch('src/images/**/*.{gif,jpg,png,svg}', ['images', reload]) 182 | }) 183 | 184 | // build and default tasks 185 | 186 | gulp.task('build', ['clean'], function() { 187 | try { 188 | // create dist directories 189 | fs.mkdirSync('dist') 190 | } catch (err) { 191 | // whatever. 192 | } 193 | 194 | try { 195 | fs.mkdirSync('dist/maps') 196 | } catch (err) { 197 | // whatever. 198 | } 199 | 200 | 201 | // run the tasks 202 | gulp.start('html', 'templates', 'sass', 'js', 'images', 'fonts', 'videos', 'favicon', 'lib') 203 | }) 204 | 205 | gulp.task('default', ['build', 'server', 'watch']) 206 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/ansible/django-gulp-nginx.svg?branch=master)](https://travis-ci.org/ansible/django-gulp-nginx) 2 | 3 | # django-gulp-nginx 4 | 5 | A framework for building containerized [django](https://www.djangoproject.com/) applications. Utilizes [Ansible Container](https://github.com/ansible/ansible-container) to manage each phase of the application lifecycle, and enables you to begin developing immediately with containers. 6 | 7 | Includes *django*, *gulp*, *nginx*, and *postgresql* services, pre-configured to work together, and ready for development. You can easily adjust the settings of each, as well as drop in new services directly from [Ansible Galaxy](https://galaxy.ansible.com). The following topics will help you get started: 8 | 9 | - [Requirements](#requirements) 10 | - [Getting Started](#getting-started) 11 | - [Developing](#developing) 12 | - [Adding Service](#adding) 13 | - [Testing](#testing) 14 | - [Deploying](#openshift) 15 | - [Contributing](#contributing) 16 | - [Dependencies](#dependencies) 17 | - [License](#license) 18 | - [Authors](#author) 19 | 20 |

Requirements

21 | 22 | Before starting, you'll need to have the following: 23 | 24 | - Ansible Container, running from source. See the [Running from source guide](http://docs.ansible.com/ansible-container/installation.html#running-from-source), for assistance. Be sure to install *docker* engine support, and if you intend to run the deployment example, install *openshift* engine support. 25 | - [Docker Engine](https://www.docker.com/products/docker-engine) or [Docker for Mac](https://docs.docker.com/engine/installation/mac/) 26 | - make 27 | - git 28 | 29 |

Getting Started

30 | 31 | To start creating your Django application, create a new directory, and initialize it with a copy of this project: 32 | 33 | ``` 34 | # Create a new directory for your project 35 | $ mkdir demo 36 | 37 | # Set the working directory 38 | $ cd demo 39 | 40 | # Initialize the project 41 | $ ansible-container init ansible.django-gulp-nginx 42 | ``` 43 | 44 | Next, build a local copy of the project's images. From the new project directory, start the build process by running the following: 45 | 46 | ```bash 47 | # Create the container images 48 | $ ansible-container build 49 | ``` 50 | 51 | The build process will take a few minutes to complete. It will take longer the first time you run it, because it needs to pull the base image, and build the Conductor image. 52 | 53 | After the Conductor build completes, each service in the [container.yml](./container.yml) file will be built. Services are built by executing one or more Ansible roles, and as the build process progresses, task names will scroll across your session window as each role executes. 54 | 55 | Once completed, you'll have a local copy of the built images that can be used to run the application. When you're ready to start the application, run the following: 56 | 57 | ```bash 58 | # Start the containers 59 | $ ansible-container run 60 | ``` 61 | 62 | Requests are proxied through the *gulp* service. Before you can view the sample web page, the gulp web server needs to be started. This may take a couple moments, as the process first install node modules and bower components prior to starting the service. You can watch the logs by running the following: 63 | 64 | ```bash 65 | # Watch the logs for gulp service 66 | $ docker logs -f demo_gulp_1 67 | ``` 68 | 69 | The following message in the logs indicates the service is running: 70 | 71 | ``` 72 | [17:05:29] Starting 'js'... 73 | [BS] Access URLs: 74 | ----------------------------------- 75 | Local: http://localhost:8080 76 | External: http://172.21.0.4:8080 77 | ----------------------------------- 78 | UI: http://localhost:3001 79 | UI External: http://172.21.0.4:3001 80 | ----------------------------------- 81 | [BS] Serving files from: dist 82 | [17:05:30] Finished 'lib' after 601 ms 83 | [17:05:30] Finished 'html' after 574 ms 84 | [17:05:30] Finished 'sass' after 644 ms 85 | [17:05:30] Finished 'templates' after 685 ms 86 | [17:05:30] Finished 'js' after 631 ms 87 | ``` 88 | 89 | To view the sample app, open a browser and go to [http://localhost:8080](http://localhost:8080), where you'll see a simple "Hello World!" message. 90 | 91 |

Developing

92 | 93 | When you start the containers with `ansible-container run`, they start in development mode, which means that the *dev_overrides* section of each service definition in [container.yml](./container.yml) takes precedence, causing the *gulp*, *django* and *postgresql* services to start, and the *nginx* service to stop. 94 | 95 | The frontend code can be found in the [src](./src) directory, and the backend django code is found in the [project](./project) directory. You can begin making changes right away, and as you do, you'll see the results reflected in your browser almost immediately. 96 | 97 | Here's a brief overview of each of the running services: 98 | 99 | ### gulp 100 | 101 | While developing, the *gulp* service will actively watch for changes to files in the [src](./src) directory tree, where custom frontend components (i.e. html, javascript, css, etc.) live. As new files are created, or existing files modified, the *gulp* service will compile the updates, place results in the [dist](./dist) directory, and using [browsersync](https://browsersync.io/), refresh your browser. 102 | 103 | In addition to compiling the frontend components, the *gulp* service will proxy requests beginning with */static* or */admin* to the *django* service. The proxy settings are configurable in *gulpfile.js*, so as you add additional routes to the django service, you can expand the number of paths forwarded by the *gulp* service. 104 | 105 | **NOTE** 106 | > *As you add new routes to the backend, be sure to update the nginx service definition by modifying [container.yml](./container.yml), and adjusting the parameters passed to the ansible.nginx-container role. Specifically, you'll need to update the PROXY_LOCATION value.* 107 | 108 | ### django 109 | 110 | The *django* service provides the backend of the application. During development the *runserver* process executes, and accepts requests from the *gulp* service. The source code to the Django app lives in the [project](./project) directory tree. To add additional Python and Django modules, add the module names and versions to [requirements.txt](./requirements.txt), and run the `ansible-container build` command to install and incorporate them into the *django* image. 111 | 112 | When the *django* container starts, it waits for the PostgreSQL database to be ready, and then it performs migrations, all before starting the server process. Use `make django_manage makemigrations` and `make django_manage migrate` to create and run migrations during development. 113 | 114 | ### postgresql 115 | 116 | The *postgresql* service provides the *django* service with access to a database, and by default stores the database on the *postgres-data* volume. Modify [container.ym](./container.yml) to set the database name, and credentials. 117 | 118 |

Adding Services

119 | 120 | You can add preconfigured services to the application by installing *Container Enabled* roles directly from the [Galaxy web site](https://galaxy.ansible.com). Look for roles on the site by going to the [Browse Roles](https://galaxy.ansible.com/list#/roles?page=1&page_size=10&role_type=CON) page, setting the filter to *Role Type*, and choosing *Containr Enabled*. 121 | 122 | For example, if you want to install a Redis service, you can install the `j00bar.redis-container` role by running the following: 123 | 124 | ``` 125 | # Set the working directory to your project root 126 | $ cd demo 127 | 128 | # Install the role 129 | $ ansible-container install j00bar.redis-container 130 | ``` 131 | 132 | After the install completes, the new service will be included in [container.yml](./container.yml). You'll then need to run the `build` process to update the project's images: 133 | 134 | ```bash 135 | # Rebuild the project images 136 | $ ansible-container build 137 | ``` 138 | 139 | After the build process completes, restart the application by running the following: 140 | 141 | ```bash 142 | # Run the application 143 | $ ansible-container restart 144 | ``` 145 | 146 |

Testing

147 | 148 | After you've made changes to the app, and you're ready to test, you'll first run `ansible-container build` to create a new set of images containing the latest code. During the build process, the [project](./project) directory, which contains your custom Django files, will be copied into the *django* image at */django*, and your frontend assets, contained in [src](./src), will be compiled and copied to the [dist](./dist) directory, and then copied into the *nginx* image at */static*. 149 | 150 | Once the new images are built, run the following to test the images: 151 | 152 | ```bash 153 | # Restart the application in production mode for testing 154 | $ ansible-container stop 155 | $ ansible-container run --production 156 | ``` 157 | 158 | The above starts the containers in production mode, ignoring the *dev_overrides* section of each service definition in [container.yml](./container.yml)`, and executing the containers as if they were deployed to production. This time the *django*, *nginx*, and *postgresql* containers starts, and the *gulp* container stops. Just as before, access the application at [http://localhost:8080](http://localhost:8080). 159 | 160 | ### django 161 | 162 | In production this service will run the *gunicorn* process to accept requests from the *nginx* service. Just as before, when the service starts it will wait for the PostgreSQL database to become available, and then perform migrations, before starting the server process. 163 | 164 | ### nginx 165 | 166 | This service will respond to requests for frontend assets, and proxy requests to *django* service endpoints. Before running `ansible-container build`, if you added new routes to your django application, be sure to update the nginx configuration by modifying [container.yml](./container.yml), and adjusting the PROXY_LOCATION parameter passed to the *ansible.nginx-container* role. This will impact the *nginx.conf* file that gets added to the image. 167 | 168 | ### postgresql 169 | 170 | Just as before, the *postgresql* sevice provides the *django* service with access to a database, and by default stores the database on the *postgres-data* volume. 171 | 172 | NOTE 173 | > *If you start the image build process by running `make build`, the postgres-data volume will be deleted, and the application will start with an empty database.* 174 | 175 |

Deploying

176 | 177 | Ansible Container can deploy to Kubernetes and OpenShift. For the purposes of demonstrating the deployment workflow, we'll use OpenShift. If you want to carry out the actual steps, you'll need access to an OpenShift instance. The [Install and Configure Openshift](http://docs.ansible.com/ansible-container/configure_openshift.html) guide at our doc site provides a how-to that will help you create a containerized instance. 178 | 179 | Log into the cluster using your *developer* account: 180 | 181 | 182 | ```bash 183 | # Log into the local cluster 184 | $ oc login -u developer 185 | ``` 186 | 187 | Create a *demo* project: 188 | 189 | ```bash 190 | # Create a new project 191 | $ oc new-project demo 192 | ``` 193 | 194 | The project name is defined in [container.yml](./container.yml). Within the *settings* section, you will find a *k8s_namespace* section that sets the name. The project name is arbitrary. However, before running the `deploy` command, the project must already exist, and the user you're logged in as, must have access to it. 195 | 196 | Next, use the `deploy` command to push the project images to the local registry, and create the deployment playbook. For demonstration purposes, we're referencing the *local_openshift* registry defined in [container.yml](./container.yml). Depending on how you created the local OpenShift cluster, you may need to adjust the registry attributes. 197 | 198 | One of the registry attributes is *namespace*. For OpenShift and K8s, the registry *namespace* should match the *name* value set in *k8s_namespace* within the *settings* section. In the case of OpenShift, the *name* in *k8s_namespace* will be the *project* name, and for K8s, it's the *Namespace*. 199 | 200 | Once you're ready to push the images, run the following from the root of the *demo* project directory: 201 | 202 | ```bash 203 | # Push the built images and generate the deployment playbook 204 | $ ansible-container --engine openshift deploy --push-to local_openshift --username developer --password $(oc whoami -t) 205 | ``` 206 | 207 | The above will authenticate to the registry using the `developer` username, and a token generated by the `oc whoami -t` command. This presumes that your cluster has a `developer` account, and that you previously authenticated to the cluster with this account. 208 | 209 | After pushing the images, a playbook is generated and written to the `ansible-deployment` directory. The name of the playbook will match the project name, and have a `.yml` extension. In this case, the name of the playbook will be `demo.yml`. 210 | 211 | You will also find a `roles` directory containing the `ansible.kubernetes-modules` role. The deployment playbook relies on this role for access to the Ansible Kubernetes modules. 212 | 213 | To deploy the application, execute the playbook, making sure to include the appropriate tag. Possible tags include: `start`, `stop`, `restart`, and `destroy`. To start the application, run the following: 214 | 215 | ```bash 216 | # Run the deployment playbook 217 | $ ansible-playbook ./ansible-deployment/demo.yml --tags start 218 | ``` 219 | Once the playbook completes, log into the OpenShift console to check the status of the deployment. From the *Applications* menu, choose *Routes*, and find the URL that points to the *nginx* service. Using this URL, you can access the appication running on the cluster. 220 | 221 | 222 |

Contributing

223 | 224 | If you work with this project and find issues, please [submit an issue](https://github.com/ansible/django-gulp-nginx/issues). 225 | 226 | Pull requests are welcome. If you want to help add features and maintain the project, please feel free to jump in, and we'll review your request quickly, and help you get it merged. 227 | 228 |

Dependencies

229 | 230 | This project depends on the following [Galaxy](https://galaxy.ansible.com) roles: 231 | 232 | - [ansible.nginx-container](https://galaxy.ansible.com/ansible/nginx-container) 233 | 234 |

License

235 | 236 | [Apache v2](https://www.apache.org/licenses/LICENSE-2.0) 237 | 238 |

Authors

239 | 240 | View [AUTHORS](./AUTHORS) for a list contributors. Thanks everyone! 241 | 242 | 243 | 244 | --------------------------------------------------------------------------------