We can't symlink roles because they need to be mounted into a container.
+++ /dev/null
-../../../ch08/playbooks/roles/database
\ No newline at end of file
+++ /dev/null
-../../../ch08/playbooks/roles/mezzanine
\ No newline at end of file
--- /dev/null
+---
+tls_enabled: True
--- /dev/null
+---
+- name: restart supervisor
+ supervisorctl: name=gunicorn_mezzanine state=restarted
+ become: True
+- name: restart nginx
+ service: name=nginx state=restarted
+ become: True
--- /dev/null
+- name: create a logs directory
+ file: path="{{ ansible_env.HOME }}/logs" state=directory
+- name: check out the repository on the host
+ git: repo={{ mezzanine_repo_url }} dest={{ mezzanine_proj_path }} accept_hostkey=yes
+- name: install Python requirements globally via pip
+ pip: name={{ item }} state=latest
+ with_items:
+ - pip
+ - virtualenv
+ - virtualenvwrapper
+ become: True
+- name: install required python packages
+ pip: name={{ item }} virtualenv={{ mezzanine_venv_path }}
+ with_items:
+ - gunicorn
+ - setproctitle
+ - psycopg2
+ - django-compressor
+ - python-memcached
+- name: install requirements.txt
+ pip: >
+ requirements={{ mezzanine_proj_path }}/{{ mezzanine_reqs_path }}
+ virtualenv={{ mezzanine_venv_path }}
+- name: generate the settings file
+ template: src=local_settings.py.j2 dest={{ mezzanine_settings_path }}/local_settings.py
+- name: apply migrations to create the database, collect static content
+ django_manage:
+ command: "{{ item }}"
+ app_path: "{{ mezzanine_proj_path }}"
+ virtualenv: "{{ mezzanine_venv_path }}"
+ with_items:
+ - migrate
+ - collectstatic
+- name: set the site id
+ script: scripts/setsite.py
+ environment:
+ PATH: "{{ mezzanine_venv_path }}/bin"
+ PROJECT_DIR: "{{ mezzanine_proj_path }}"
+ PROJECT_APP: "{{ mezzanine_proj_app }}"
+ WEBSITE_DOMAIN: "{{ live_hostname }}"
+- name: set the admin password
+ script: scripts/setadmin.py
+ environment:
+ PATH: "{{ mezzanine_venv_path }}/bin"
+ PROJECT_DIR: "{{ mezzanine_proj_path }}"
+ PROJECT_APP: "{{ mezzanine_proj_app }}"
+ ADMIN_PASSWORD: "{{ admin_pass }}"
+- name: set the gunicorn config file
+ template: src=gunicorn.conf.py.j2 dest={{ mezzanine_proj_path }}/gunicorn.conf.py
+- name: set the supervisor config file
+ template: src=supervisor.conf.j2 dest=/etc/supervisor/conf.d/mezzanine.conf
+ become: True
+ notify: restart supervisor
+- name: ensure config path exists
+ file: path={{ mezzanine_conf_path }} state=directory
+ become: True
+ when: tls_enabled
+- name: install poll twitter cron job
+ cron: >
+ name="poll twitter" minute="*/5" user={{ mezzanine_user }}
+ job="{{ mezzanine_manage }} poll_twitter"
--- /dev/null
+---
+- name: install apt packages
+ apt: pkg={{ item }} update_cache=yes cache_valid_time=3600
+ become: True
+ with_items:
+ - git
+ - libjpeg-dev
+ - libpq-dev
+ - memcached
+ - nginx
+ - python-dev
+ - python-pip
+ - python-psycopg2
+ - python-setuptools
+ - python-virtualenv
+ - supervisor
+- include: django.yml
+- include: nginx.yml
--- /dev/null
+- name: set the nginx config file
+ template: src=nginx.conf.j2 dest=/etc/nginx/sites-available/mezzanine.conf
+ notify: restart nginx
+ become: True
+- name: enable the nginx config file
+ file:
+ src: /etc/nginx/sites-available/mezzanine.conf
+ dest: /etc/nginx/sites-enabled/mezzanine.conf
+ state: link
+ notify: restart nginx
+ become: True
+- name: remove the default nginx config file
+ file: path=/etc/nginx/sites-enabled/default state=absent
+ notify: restart nginx
+ become: True
+- name: create ssl certificates
+ command: >
+ openssl req -new -x509 -nodes -out {{ mezzanine_proj_name }}.crt
+ -keyout {{ mezzanine_proj_name }}.key -subj '/CN={{ domains[0] }}' -days 3650
+ chdir={{ mezzanine_conf_path }}
+ creates={{ mezzanine_conf_path }}/{{ mezzanine_proj_name }}.crt
+ become: True
+ when: tls_enabled
+ notify: restart nginx
--- /dev/null
+from __future__ import unicode_literals
+import multiprocessing
+
+bind = "unix:{{ mezzanine_proj_path }}/gunicorn.sock"
+workers = {{ mezzanine_num_workers }}
+errorlog = "/home/{{ mezzanine_user }}/logs/{{ mezzanine_proj_name }}_error.log"
+loglevel = "error"
+proc_name = "{{ mezzanine_proj_name }}"
--- /dev/null
+from __future__ import unicode_literals
+
+SECRET_KEY = "{{ secret_key }}"
+NEVERCACHE_KEY = "{{ nevercache_key }}"
+ALLOWED_HOSTS = [{{ domains|surround_by_quote|join(", ") }}]
+
+DATABASES = {
+ "default": {
+ # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
+ "ENGINE": "django.db.backends.postgresql_psycopg2",
+ # DB name or path to database file if using sqlite3.
+ "NAME": "{{ proj_name }}",
+ # Not used with sqlite3.
+ "USER": "{{ proj_name }}",
+ # Not used with sqlite3.
+ "PASSWORD": "{{ db_pass }}",
+ # Set to empty string for localhost. Not used with sqlite3.
+ "HOST": "127.0.0.1",
+ # Set to empty string for default. Not used with sqlite3.
+ "PORT": "",
+ }
+}
+
+SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTOCOL", "https")
+
+CACHE_MIDDLEWARE_SECONDS = 60
+
+CACHE_MIDDLEWARE_KEY_PREFIX = "{{ proj_name }}"
+
+CACHES = {
+ "default": {
+ "BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
+ "LOCATION": "127.0.0.1:11211",
+ }
+}
+
+SESSION_ENGINE = "django.contrib.sessions.backends.cache"
+
+TWITTER_ACCESS_TOKEN_KEY = "{{ twitter_access_token_key }}"
+TWITTER_ACCESS_TOKEN_SECRET = "{{ twitter_access_token_secret }}"
+TWITTER_CONSUMER_KEY = "{{ twitter_consumer_key }}"
+TWITTER_CONSUMER_SECRET = "{{ twitter_consumer_secret }}"
+TWITTER_DEFAULT_QUERY = "from:ansiblebook"
--- /dev/null
+from __future__ import unicode_literals
+
+SECRET_KEY = "{{ secret_key }}"
+NEVERCACHE_KEY = "{{ nevercache_key }}"
+ALLOWED_HOSTS = [{% for domain in domains %}"{{ domain }}",{% endfor %}]
+
+DATABASES = {
+ "default": {
+ # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
+ "ENGINE": "django.db.backends.postgresql_psycopg2",
+ # DB name or path to database file if using sqlite3.
+ "NAME": "{{ mezzanine_proj_name }}",
+ # Not used with sqlite3.
+ "USER": "{{ mezzanine_proj_name }}",
+ # Not used with sqlite3.
+ "PASSWORD": "{{ db_pass }}",
+ # Set to empty string for localhost. Not used with sqlite3.
+ "HOST": "{{ database_host | default("localhost") }}",
+ # Set to empty string for default. Not used with sqlite3.
+ "PORT": "{{ database_port }}",
+ }
+}
+
+SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTOCOL", "https")
+
+CACHE_MIDDLEWARE_SECONDS = 60
+
+CACHE_MIDDLEWARE_KEY_PREFIX = "{{ mezzanine_proj_name }}"
+
+CACHES = {
+ "default": {
+ "BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
+ "LOCATION": "127.0.0.1:11211",
+ }
+}
+
+SESSION_ENGINE = "django.contrib.sessions.backends.cache"
+
+TWITTER_ACCESS_TOKEN_KEY = "{{ twitter_access_token_key }}"
+TWITTER_ACCESS_TOKEN_SECRET = "{{ twitter_access_token_secret }}"
+TWITTER_CONSUMER_KEY = "{{ twitter_consumer_key }}"
+TWITTER_CONSUMER_SECRET = "{{ twitter_consumer_secret }}"
+TWITTER_DEFAULT_QUERY = "from:ansiblebook"
--- /dev/null
+upstream {{ mezzanine_proj_name }} {
+ server unix:{{ mezzanine_proj_path }}/gunicorn.sock fail_timeout=0;
+}
+
+server {
+
+ listen 80;
+
+ {% if tls_enabled %}
+ listen 443 ssl;
+ {% endif %}
+ server_name {{ domains|join(", ") }};
+ client_max_body_size 10M;
+ keepalive_timeout 15;
+
+ {% if tls_enabled %}
+ ssl_certificate conf/{{ mezzanine_proj_name }}.crt;
+ ssl_certificate_key conf/{{ mezzanine_proj_name }}.key;
+ ssl_session_cache shared:SSL:10m;
+ ssl_session_timeout 10m;
+ ssl_ciphers CDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
+ ssl_prefer_server_ciphers on;
+ {% endif %}
+
+ location / {
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Protocol $scheme;
+ proxy_pass http://{{ mezzanine_proj_name }};
+ }
+
+ location /static/ {
+ root {{ mezzanine_proj_path }};
+ access_log off;
+ log_not_found off;
+ }
+
+ location /robots.txt {
+ root {{ mezzanine_proj_path }}/static;
+ access_log off;
+ log_not_found off;
+ }
+
+ location /favicon.ico {
+ root {{ mezzanine_proj_path }}/static/img;
+ access_log off;
+ log_not_found off;
+ }
+}
--- /dev/null
+[program:{{ mezzanine_gunicorn_procname }}]
+command={{ mezzanine_venv_path }}/bin/gunicorn -c gunicorn.conf.py -p gunicorn.pid {{ mezzanine_proj_app }}.wsgi:application
+directory={{ mezzanine_proj_path }}
+user={{ mezzanine_user }}
+autostart=true
+stdout_logfile=/home/{{ mezzanine_user }}/logs/{{ mezzanine_proj_name }}_supervisor
+autorestart=true
+redirect_stderr=true
+environment=LANG="{{ locale }}",LC_ALL="{{ locale }}",LC_LANG="{{ locale }}"
--- /dev/null
+---
+# vars file for mezzanine
+mezzanine_user: "{{ ansible_user }}"
+mezzanine_venv_home: "{{ ansible_env.HOME }}/.virtualenvs"
+mezzanine_venv_path: "{{ mezzanine_venv_home }}/{{ mezzanine_proj_name }}"
+mezzanine_repo_url: git@github.com:ansiblebook/mezzanine_example.git
+mezzanine_proj_path: "{{ ansible_env.HOME }}/mezzanine/{{ mezzanine_proj_name }}"
+mezzanine_settings_path: "{{ mezzanine_proj_path }}/{{ mezzanine_proj_name }}"
+mezzanine_reqs_path: requirements.txt
+mezzanine_conf_path: /etc/nginx/conf
+mezzanine_python: "{{ mezzanine_venv_path }}/bin/python"
+mezzanine_manage: "{{ mezzanine_python }} {{ mezzanine_proj_path }}/manage.py"
+mezzanine_num_workers: "multiprocessing.cpu_count() * 2 + 1"
+mezzanine_gunicorn_procname: gunicorn_mezzanine