├── .gitignore ├── LICENSE ├── README.md ├── TODO.org ├── Vagrantfile ├── demo_project ├── demo_project │ ├── __init__.py │ ├── demo │ │ ├── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ └── demo │ │ │ │ └── index.html │ │ ├── tests.py │ │ └── views.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py └── requirements.txt ├── fabfile.py ├── files └── salt-shaker.png ├── requirements.txt └── salt ├── keys ├── vagrant.django-salted.org.pem └── vagrant.django-salted.org.pub ├── minion.conf └── roots ├── pillar ├── top.sls ├── vagrant-django.sls ├── vagrant-postgresql.sls ├── vagrant-uwsgi.sls └── vagrant-wheel.sls └── salt ├── example-project ├── nginx.conf ├── nginx.sls ├── postgresql.sls ├── requirements.sls ├── share.sls ├── uwsgi.ini ├── uwsgi.sls ├── venv.sls └── wheel.sls ├── nginx ├── init.sls └── nginx.conf ├── postgresql ├── init.sls └── pg_hba.conf ├── requirements └── essential.sls ├── ssh ├── init.sls └── sshd_config ├── top.sls └── uwsgi ├── init.sls └── uwsgi.conf /.gitignore: -------------------------------------------------------------------------------- 1 | /.vagrant/ 2 | /.ropeproject/ 3 | *.pyc 4 | demo_project/demo_project/static/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Petar Radosevic 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | * Neither the name of the author nor the names of other 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django Salted 2 | 3 | **One-command Django and PostgreSQL development environment on Vagrant.** 4 | 5 |  6 | 7 | This is a complete [SaltStack] configuration for a Django centric stack. It 8 | enables you setup a solid Django development environment with a single 9 | command. Some of the great things which are included: 10 | 11 | - *Local development*, but the code is run within Ubuntu with the help of a 12 | share. 13 | - Server running on *[uWSGI]* and *[Nginx]*. uWSGI *reloads* your python code 14 | automagically. 15 | - *[PostgreSQL]* is installed and setup for you. No more explaining to your 16 | designer how to get the project running. 17 | - Some helpfull *[Fabric]* functions, e.g., running `syncdb` on Vagrant. 18 | 19 | **Table of Contents** 20 | 21 | - [Django Salted](#django-salted) 22 | - [Requirements](#requirements) 23 | - [Getting started (quick)](#getting-started-quick) 24 | - [Modify for your own project](#modify-for-your-own-project) 25 | - [FAQ](#faq) 26 | - [How do I get the get the latest state/version?](#how-do-i-get-the-get-the-latest-stateversion) 27 | - [Which Operating Systems are supported?](#which-operating-systems-are-supported) 28 | - [I'm getting "502 Bad Gateway" from Nginx](#i'm-getting-502-bad-gateway-from-nginx) 29 | - [OMG, you also put the private key on Github?](#omg-you-also-put-the-private-key-on-github) 30 | - [Want to help?](#want-to-help) 31 | - [License](#license) 32 | 33 | **The repository is accomponied with a blog post we put on [Gibbon]. Go read 34 | [Salting your Django Stack] if you want to know how this came to be.** 35 | 36 | ## Requirements 37 | 38 | Django salted requires the following software to be installed on your machine: 39 | 40 | - [Virtualbox] 41 | - [Vagrant] 42 | - [Salty Vagrant] 43 | 44 | You also need [Fabric] and [Fabtools] if you want to run the Fabric 45 | commands. You can find these requirements in the [requirements.txt] file in 46 | the root directory. 47 | 48 | All of the above are open-source and free to use. 49 | 50 | ## Getting started (quick) 51 | 52 | If you want to quickly try out what this is all about. Your installation will 53 | adhere to the default configuration and use the example Django project. If you 54 | have the requirements installed, it's just a single command: 55 | 56 | vagrant up 57 | 58 | After running this command, you should see the Django example website on: 59 | [http://127.0.0.1:8080/](http://127.0.0.1:8080). However, your database tables 60 | still need to be created. There is a fabric command to help you with that: 61 | 62 | fab vagrant syncdb 63 | 64 | You will also need to collect the static files to use the demo project. This can be done with fabric also: 65 | 66 | fab vagrant collectstatic 67 | 68 | The demo_project is a very simple project with the Djagno Admin [http://127.0.0.1:8080/admin](http://127.0.0.1:8080/admin) 69 | 70 | The vagrant machine uses a virtual venvironment located at `/home/vagrant/env` 71 | 72 | After getting familiar with it, using Django Salted on your own project is 73 | simple done with changing a few settings. 74 | 75 | - You will run and develop on the Django (1.5.1) `demo_project` inside the VM. 76 | - Ubuntu 12.04 will be used as OS. 77 | - Domain is: vagrant.django-salted.org 78 | 79 | ## Modify for your own project 80 | 81 | TODO: Write how to modify this repository for your own project. 82 | 83 | ## FAQ 84 | 85 | ### How do I get the get the latest state/version? 86 | 87 | The fastest way is to run `vagrant provision`. The downfall is that you don't 88 | see what's happening in colors. Or SSH to the box (my favourite) by doing the 89 | following: 90 | 91 | vagrant ssh 92 | # On the box... 93 | sudo salt-call state.highstate 94 | 95 | ### Which Operating Systems are supported? 96 | 97 | Currently I have only tested it on Ubuntu 12.04 box. In the future there will 98 | be support for Debian and FreeBSD. 99 | 100 | ### I'm getting "502 Bad Gateway" from Nginx 101 | 102 | Ajj! This means uWSGI is down. It takes 15 seconds for uWSGI to spin up from a 103 | cold boot (because we have to wait on the Vagrant share). However, if after 15 104 | seconds it's still not working, check out what went wrong by looking at the logs: 105 | 106 | vagrant ssh 107 | sudo cat /var/log/uwsgi/example.log 108 | # or check the boss (emperor) 109 | sudo cat /var/log/uwsgi/emperor.log 110 | 111 | Let me know in an issue if this is something I need to fix. 112 | 113 | ### OMG, you also put the private key on Github? 114 | 115 | Yes, this private key is used for the Vagrant example project. It's supposed 116 | to be there so you can spin up a box with a single command. Don't put your own 117 | private keys there. 118 | 119 | ## Want to help? 120 | 121 | That would be great! Check out the [TODO.org] or the [Issues] on things to do. 122 | 123 | ## License 124 | 125 | Django-salted was created by Petar Radosevic. You can reach out to me on 126 | twitter at [@wunki]. 127 | 128 | It's BSD Licensed. Use it however you want, just don't try to sell it back to 129 | me. 130 | 131 | [SaltStack]: http://saltstack.com/community.html 132 | [Gibbon]: http://blog.gibbon.co 133 | [PostgreSQL]: http://www.postgresql.org/ 134 | [Salting your Django Stack]: http://blog.gibbon.co/posts/2013-06-12-salting-your-django-stack.html 135 | [Virtualbox]: https://www.virtualbox.org/ 136 | [Vagrant]: http://www.vagrantup.com/ 137 | [Salty Vagrant]: https://github.com/saltstack/salty-vagrant 138 | [uWSGI]: http://uwsgi-docs.readthedocs.org/ 139 | [Nginx]: http://nginx.org/ 140 | [Fabric]: http://fabfile.org/ 141 | [Fabtools]: https://github.com/ronnix/fabtools 142 | [requirements.txt]: https://github.com/wunki/django-salted/blob/master/requirements.txt 143 | [Vagrantfile]: https://github.com/wunki/django-salted/blob/master/Vagrantfile 144 | [TODO.org]: https://github.com/wunki/django-salted/blob/master/TODO.org 145 | [Issues]: https://github.com/wunki/django-salted/issues 146 | [@wunki]: https://twitter.com/wunki 147 | -------------------------------------------------------------------------------- /TODO.org: -------------------------------------------------------------------------------- 1 | * Django-salted 2 | ** TODO Admin templates are not loaded with the `app_loader` 3 | 4 | Somehow, now templates from libraries are loaded. The list of template 5 | directories of apps is completely empty. I already looked at the 6 | permissions, but they seem to be fine. 7 | 8 | ** TODO Replace uWSGI sleep with hook. 9 | 10 | Currently we have to sleep for 15 seconds before starting uWSGI so that 11 | the vagrant share is up. This is a dirty way of doing it. A pull request 12 | is already on Vagrant which supplies a hook after putting the share online. 13 | 14 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby; -*- 2 | require 'etc' 3 | 4 | Vagrant.configure("2") do |config| 5 | config.vm.define :web do |web| 6 | # Ubuntu 12.04 7 | web.vm.box = "precise64" 8 | web.vm.box_url = "http://files.vagrantup.com/precise64.box" 9 | 10 | # Network 11 | web.vm.hostname = "vagrant.django-salted.org" 12 | web.vm.network :forwarded_port, guest: 80, host: 8080, auto_correct: true 13 | 14 | # Share for masterless server 15 | web.vm.synced_folder "salt/roots/", "/srv/" 16 | 17 | web.vm.provision :salt do |salt| 18 | # Configure the minion 19 | salt.minion_config = "salt/minion.conf" 20 | 21 | # Show the output of salt 22 | salt.verbose = true 23 | 24 | # Pre-distribute these keys on our local installation 25 | salt.minion_key = "salt/keys/vagrant.django-salted.org.pem" 26 | salt.minion_pub = "salt/keys/vagrant.django-salted.org.pub" 27 | 28 | # Run the highstate on start 29 | salt.run_highstate = true 30 | 31 | # Install the latest version of SaltStack 32 | salt.install_type = "daily" 33 | end 34 | 35 | # Customize the box 36 | web.vm.provider :virtualbox do |v| 37 | v.customize ["modifyvm", :id, "--memory", 512] 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /demo_project/demo_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wunki/django-salted/d24b707ff74c8423a830c2fd9b8e3df1576455d4/demo_project/demo_project/__init__.py -------------------------------------------------------------------------------- /demo_project/demo_project/demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wunki/django-salted/d24b707ff74c8423a830c2fd9b8e3df1576455d4/demo_project/demo_project/demo/__init__.py -------------------------------------------------------------------------------- /demo_project/demo_project/demo/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /demo_project/demo_project/demo/templates/demo/index.html: -------------------------------------------------------------------------------- 1 |
This Django Project is running on a Vagrant Box provisioned by Salt Stack
4 | 7 | -------------------------------------------------------------------------------- /demo_project/demo_project/demo/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /demo_project/demo_project/demo/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response 2 | from django.template import RequestContext 3 | 4 | 5 | def index(request): 6 | return render_to_response('demo/index.html', {}, context_instance=RequestContext(request)) 7 | -------------------------------------------------------------------------------- /demo_project/demo_project/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for demo_project project. 2 | import os 3 | 4 | DEBUG = True 5 | TEMPLATE_DEBUG = DEBUG 6 | PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__)) 7 | 8 | ADMINS = ( 9 | # ('Your Name', 'your_email@example.com'), 10 | ) 11 | 12 | MANAGERS = ADMINS 13 | 14 | DATABASES = { 15 | 'default': { 16 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 17 | 'NAME': 'example_db', 18 | 'USER': 'example_user', 19 | 'PASSWORD': 'example_password' 20 | } 21 | } 22 | 23 | # Hosts/domain names that are valid for this site; required if DEBUG is False 24 | # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts 25 | ALLOWED_HOSTS = [] 26 | 27 | # Local time zone for this installation. Choices can be found here: 28 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 29 | # although not all choices may be available on all operating systems. 30 | # In a Windows environment this must be set to your system time zone. 31 | TIME_ZONE = 'America/Chicago' 32 | 33 | # Language code for this installation. All choices can be found here: 34 | # http://www.i18nguy.com/unicode/language-identifiers.html 35 | LANGUAGE_CODE = 'en-us' 36 | 37 | SITE_ID = 1 38 | 39 | # If you set this to False, Django will make some optimizations so as not 40 | # to load the internationalization machinery. 41 | USE_I18N = True 42 | 43 | # If you set this to False, Django will not format dates, numbers and 44 | # calendars according to the current locale. 45 | USE_L10N = True 46 | 47 | # If you set this to False, Django will not use timezone-aware datetimes. 48 | USE_TZ = True 49 | 50 | # Absolute filesystem path to the directory that will hold user-uploaded files. 51 | # Example: "/var/www/example.com/media/" 52 | MEDIA_ROOT = '' 53 | 54 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 55 | # trailing slash. 56 | # Examples: "http://example.com/media/", "http://media.example.com/" 57 | MEDIA_URL = '' 58 | 59 | # Absolute path to the directory static files should be collected to. 60 | # Don't put anything in this directory yourself; store your static files 61 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 62 | # Example: "/var/www/example.com/static/" 63 | STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static') 64 | 65 | # URL prefix for static files. 66 | # Example: "http://example.com/static/", "http://static.example.com/" 67 | STATIC_URL = '/static/' 68 | 69 | # Additional locations of static files 70 | STATICFILES_DIRS = ( 71 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 72 | # Always use forward slashes, even on Windows. 73 | # Don't forget to use absolute paths, not relative paths. 74 | ) 75 | 76 | # List of finder classes that know how to find static files in 77 | # various locations. 78 | STATICFILES_FINDERS = ( 79 | 'django.contrib.staticfiles.finders.FileSystemFinder', 80 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 81 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 82 | ) 83 | 84 | # Make this unique, and don't share it with anybody. 85 | SECRET_KEY = '^3^orp)o&+73#l+y^@9@gyiw^$-6_#39g#f*1-drcxvk0@lr%5' 86 | 87 | # List of callables that know how to import templates from various sources. 88 | TEMPLATE_LOADERS = ( 89 | 'django.template.loaders.filesystem.Loader', 90 | 'django.template.loaders.app_directories.Loader', 91 | # 'django.template.loaders.eggs.Loader', 92 | ) 93 | 94 | MIDDLEWARE_CLASSES = ( 95 | 'django.middleware.common.CommonMiddleware', 96 | 'django.contrib.sessions.middleware.SessionMiddleware', 97 | 'django.middleware.csrf.CsrfViewMiddleware', 98 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 99 | 'django.contrib.messages.middleware.MessageMiddleware', 100 | # Uncomment the next line for simple clickjacking protection: 101 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 102 | ) 103 | 104 | ROOT_URLCONF = 'demo_project.urls' 105 | 106 | # Python dotted path to the WSGI application used by Django's runserver. 107 | WSGI_APPLICATION = 'demo_project.wsgi.application' 108 | 109 | TEMPLATE_DIRS = ( 110 | os.path.join(PROJECT_ROOT, 'templates'), 111 | ) 112 | 113 | INSTALLED_APPS = ( 114 | 'django.contrib.auth', 115 | 'django.contrib.contenttypes', 116 | 'django.contrib.sessions', 117 | 'django.contrib.sites', 118 | 'django.contrib.messages', 119 | 'django.contrib.staticfiles', 120 | 'django.contrib.admin', 121 | 'demo_project.demo' 122 | ) 123 | 124 | # A sample logging configuration. The only tangible logging 125 | # performed by this configuration is to send an email to 126 | # the site admins on every HTTP 500 error when DEBUG=False. 127 | # See http://docs.djangoproject.com/en/dev/topics/logging for 128 | # more details on how to customize your logging configuration. 129 | 130 | LOGGING = { 131 | 'version': 1, 132 | 'disable_existing_loggers': False, 133 | 'filters': { 134 | 'require_debug_false': { 135 | '()': 'django.utils.log.RequireDebugFalse' 136 | } 137 | }, 138 | 'handlers': { 139 | 'mail_admins': { 140 | 'level': 'ERROR', 141 | 'filters': ['require_debug_false'], 142 | 'class': 'django.utils.log.AdminEmailHandler' 143 | } 144 | }, 145 | 'loggers': { 146 | 'django.request': { 147 | 'handlers': ['mail_admins'], 148 | 'level': 'ERROR', 149 | 'propagate': True, 150 | }, 151 | } 152 | } 153 | # TODO: upgrade to use this logging, it was useful for debugging 154 | # LOG_FORMAT = "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s" 155 | # LOGGING = { 156 | # 'version': 1, 157 | # 'disable_existing_loggers': True, 158 | # 'formatters': { 159 | # 'standard': { 160 | # 'format': LOG_FORMAT, 161 | # 'datefmt': "%d/%b/%Y %H:%M:%S" 162 | # }, 163 | # }, 164 | # 'handlers': { 165 | # 'null': { 166 | # 'level': 'DEBUG', 167 | # 'class': 'django.utils.log.NullHandler', 168 | # }, 169 | # 'logfile': { 170 | # 'level': 'DEBUG', 171 | # 'class': 'logging.handlers.RotatingFileHandler', 172 | # 'filename': "/var/log/demo_project/error.log", 173 | # 'maxBytes': 50000, 174 | # 'backupCount': 2, 175 | # 'formatter': 'standard', 176 | # }, 177 | # 'console': { 178 | # 'level': 'INFO', 179 | # 'class': 'logging.StreamHandler', 180 | # 'formatter': 'standard' 181 | # }, 182 | # }, 183 | # 'loggers': { 184 | # '': { 185 | # 'handlers': ['logfile'], 186 | # 'level': 'WARN', 187 | # }, 188 | # 'django': { 189 | # 'handlers': ['console'], 190 | # 'propagate': True, 191 | # 'level': 'WARN', 192 | # }, 193 | # 'django.db.backends': { 194 | # 'handlers': ['console'], 195 | # 'level': 'DEBUG', 196 | # 'propagate': False, 197 | # }, 198 | # 'frames': { 199 | # 'handlers': ['console', 'logfile'], 200 | # 'level': 'DEBUG', 201 | # }, 202 | # } 203 | # } 204 | -------------------------------------------------------------------------------- /demo_project/demo_project/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | from django.contrib import admin 4 | admin.autodiscover() 5 | 6 | urlpatterns = patterns('', 7 | url(r'^$', 'demo_project.demo.views.index'), 8 | url(r'^admin/', include(admin.site.urls)), 9 | ) 10 | -------------------------------------------------------------------------------- /demo_project/demo_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for demo_project project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks 19 | # if running multiple sites in the same mod_wsgi process. To fix this, use 20 | # mod_wsgi daemon mode with each site in its own daemon process, or use 21 | # os.environ["DJANGO_SETTINGS_MODULE"] = "demo_project.settings" 22 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo_project.settings") 23 | 24 | # This application object is used by any WSGI server configured to use this 25 | # file. This includes Django's development server, if the WSGI_APPLICATION 26 | # setting points here. 27 | from django.core.wsgi import get_wsgi_application 28 | application = get_wsgi_application() 29 | 30 | # Apply WSGI middleware here. 31 | # from helloworld.wsgi import HelloWorldApplication 32 | # application = HelloWorldApplication(application) 33 | -------------------------------------------------------------------------------- /demo_project/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", "demo_project.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /demo_project/requirements.txt: -------------------------------------------------------------------------------- 1 | #-*- mode: conf -*- 2 | Django==1.5.1 3 | psycopg2 4 | -------------------------------------------------------------------------------- /fabfile.py: -------------------------------------------------------------------------------- 1 | from fabric.api import * 2 | from fabric import colors 3 | from fabtools.vagrant import vagrant 4 | 5 | import os, hashlib 6 | 7 | # Local paths 8 | LOCAL_ROOT = os.path.dirname(os.path.realpath(__file__)) 9 | 10 | # Server paths 11 | PROJECT_NAME = "example" 12 | PROJECT_PATH = "/vagrant/demo_project" 13 | 14 | MANAGE_BIN = "/vagrant/demo_project/manage.py" 15 | VENV_PATH = "/home/vagrant/env" 16 | WHEEL_PATH = "/home/vagrant/wheel" 17 | WHEEL_NAME = "wheel-requirements.tar.gz" 18 | 19 | def _md5_for_file(filename, block_size=2**20): 20 | filename = os.path.join(LOCAL_ROOT, filename) 21 | f = open(filename) 22 | md5 = hashlib.md5() 23 | while True: 24 | data = f.read(block_size) 25 | if not data: 26 | break 27 | md5.update(data) 28 | f.close() 29 | return md5.hexdigest() 30 | 31 | @task 32 | def manage_py(command): 33 | """ Runs a manage.py command on the server """ 34 | run('{python} {manage} {command}'.format(python=VENV_PATH + "/bin/python", 35 | manage=MANAGE_BIN, 36 | command=command)) 37 | 38 | @task 39 | def syncdb(): 40 | """ Django syncdb command.""" 41 | manage_py("syncdb --noinput") 42 | 43 | @task 44 | def migrate(): 45 | """ Django South migrate command.""" 46 | manage_py("migrate") 47 | 48 | @task 49 | def collectstatic(): 50 | """ Run collectstatic command. """ 51 | manage_py("collectstatic --noinput") 52 | 53 | @task 54 | def wheel(): 55 | """ Create new wheel requirements file """ 56 | # Get all the requirements 57 | print colors.green("Downloading and compiling requirements. This could take several minutes...") 58 | sudo('{pip} wheel --wheel-dir={wheel} -r {example}/requirements.txt'.format(pip=VENV_PATH + "/bin/pip", 59 | wheel=WHEEL_PATH + '/' + PROJECT_NAME, 60 | example=PROJECT_PATH), 61 | user="www-data", 62 | quiet=False) 63 | 64 | # Zip up 65 | print colors.green("Zipping all the requirements into one file...") 66 | with cd(WHEEL_PATH): 67 | sudo('tar czf {name} {project}/'.format(name=WHEEL_NAME, 68 | project=PROJECT_NAME), 69 | user="www-data", 70 | quiet=False) 71 | sudo('mv {name} /vagrant/'.format(name=WHEEL_NAME), 72 | quiet=False) 73 | 74 | # Create a MD5 75 | md5 = _md5_for_file(WHEEL_NAME) 76 | print colors.green('Upload the requirements and set the following MD5 in your pillar configuration: {md5}'.format(md5=md5)) 77 | -------------------------------------------------------------------------------- /files/salt-shaker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wunki/django-salted/d24b707ff74c8423a830c2fd9b8e3df1576455d4/files/salt-shaker.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | #-*- mode: conf -*- 2 | fabric 3 | fabtools -------------------------------------------------------------------------------- /salt/keys/vagrant.django-salted.org.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICoAIBAAKCAQEArSWwaJZvIRXg6XTSOqwjuwPA+LgAdepwo7fX1kiXMEDX8+Av 3 | 10TH1Q4zGR6YyWrsROaaOT1YYXUek2AUnwaGkO3RRyVdBCKwrB5hwg50D8hfK/TK 4 | clkwjCPA+jM75M3ZsGXnZTXR5Rb5JPfNhNygeRRf+m1F8X7x4AiVU+H7TNI2cPxQ 5 | 1LTc/FuTSLKVbnopE5yNdGmQHVkqN+pbkkmyL+FvPRpf+0lhUk2SRiPVGNq0PDi/ 6 | VO1BgXTMekfuw7w3vFTNEHX2KT8qumyuXqoo8Zdp1ajnXk5ArB85dAXukw9tFC9o 7 | gsNEXBkL0EgD0Nh6cqZ/2BkC4mqhLA5p2/dazwIBAQIBAQKBgQDUwzt4ZKH4xAIE 8 | zfb7nSilTu8/mlUYd2FmeWIoYpxoRJqH93Z6SsxlLGWdLthkg0LO7WKVSGjvhcMH 9 | hm5O9HFbyfRG1UhVrcKlenGb6y2hk68XtWWsZfOtwoYb0LwOr2A9tBsJsHf1uypO 10 | fMfEkRjbz3N6vVZWOI+xrfPmHrs87wKBgQDQVYEQaoQ5V/5VSocd410usQ7/EERp 11 | z++LIEPkuunkvEBPoMXzuzaeAAbWBxrIeIjHYFTx75ELD4FzPxiWefMkfrRhPiXF 12 | 6lJJKXWPoo2wwcve+n+tzvwdW1iWFjzkfGaK2KHIZn+j1ONBWcGgu1QfNH+Gm245 13 | l4IHpl1omHOAIQIBAQIBAQKBgQDEUA9oKJukWui6CFIT2UeJ4AFJIxE7K4Cbd1J0 14 | WTnr7j+kp7w55ltpDD1kg23gufMSqb7Nvr+EFyBgcu2aDAx1jNJdTnocraqmNsKp 15 | jScYXfyD2P54I43PnW7Lfak/N0WFV5wlzsHmCOR9fSQbclISQi1Ipf8JsDQ8MXIv 16 | JgV90g== 17 | -----END RSA PRIVATE KEY----- 18 | -------------------------------------------------------------------------------- /salt/keys/vagrant.django-salted.org.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEArSWwaJZvIRXg6XTSOqwj 3 | uwPA+LgAdepwo7fX1kiXMEDX8+Av10TH1Q4zGR6YyWrsROaaOT1YYXUek2AUnwaG 4 | kO3RRyVdBCKwrB5hwg50D8hfK/TKclkwjCPA+jM75M3ZsGXnZTXR5Rb5JPfNhNyg 5 | eRRf+m1F8X7x4AiVU+H7TNI2cPxQ1LTc/FuTSLKVbnopE5yNdGmQHVkqN+pbkkmy 6 | L+FvPRpf+0lhUk2SRiPVGNq0PDi/VO1BgXTMekfuw7w3vFTNEHX2KT8qumyuXqoo 7 | 8Zdp1ajnXk5ArB85dAXukw9tFC9ogsNEXBkL0EgD0Nh6cqZ/2BkC4mqhLA5p2/da 8 | zwIBAQ== 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /salt/minion.conf: -------------------------------------------------------------------------------- 1 | ##### Primary configuration settings ##### 2 | ########################################## 3 | # Set the location of the salt master server, if the master server cannot be 4 | # resolved, then the minion will fail to start. 5 | # master: salt 6 | 7 | # Set the port used by the master reply and authentication server 8 | #master_port: 4506 9 | 10 | # The user to run salt 11 | #user: root 12 | 13 | # The root directory prepended to these options: pki_dir, cachedir, log_file. 14 | #root_dir: / 15 | 16 | # The directory to store the pki information in 17 | #pki_dir: /etc/salt/pki 18 | 19 | # Explicitly declare the id for this minion to use, if left commented the id 20 | # will be the hostname as returned by the python call: socket.getfqdn() 21 | # Since salt uses detached ids it is possible to run multiple minions on the 22 | # same machine but with different ids, this can be useful for salt compute 23 | # clusters. 24 | # id: 25 | 26 | # Append a domain to a hostname in the event that it does not exist. This is 27 | # usefule for systems where socket.getfqdn() does not actually result in a 28 | # FQDN (for instance, Solaris). 29 | #append_domain: 30 | 31 | # If the the connection to the server is interrupted, the minion will 32 | # attempt to reconnect. sub_timeout allows you to control the rate 33 | # of reconnection attempts (in seconds). To disable reconnects, set 34 | # this value to 0. 35 | #sub_timeout: 60 36 | 37 | # Where cache data goes 38 | #cachedir: /var/cache/salt 39 | 40 | # The minion can locally cache the return data from jobs sent to it, this 41 | # can be a good way to keep track of jobs the minion has executed 42 | # (on the minion side). By default this feature is disabled, to enable 43 | # set cache_jobs to True 44 | #cache_jobs: False 45 | 46 | # When waiting for a master to accept the minion's public key, salt will 47 | # continuously attempt to reconnect until successful. This is the time, in 48 | # seconds, between those reconnection attempts. 49 | #acceptance_wait_time = 10 50 | 51 | # When healing a dns_check is run, this is to make sure that the originally 52 | # resolved dns has not changed, if this is something that does not happen in 53 | # your environment then set this value to False. 54 | #dns_check: True 55 | 56 | 57 | ##### Minion module management ##### 58 | ########################################## 59 | # Disable specific modules. This allows the admin to limit the level of 60 | # access the master has to the minion 61 | #disable_modules: [cmd,test] 62 | #disable_returners: [] 63 | # 64 | # Modules can be loaded from arbitrary paths. This enables the easy deployment 65 | # of third party modules. Modules for returners and minions can be loaded. 66 | # Specify a list of extra directories to search for minion modules and 67 | # returners. These paths must be fully qualified! 68 | #module_dirs: [] 69 | #returner_dirs: [] 70 | #states_dirs: [] 71 | #render_dirs: [] 72 | # 73 | # A module provider can be statically overwritten or extended for the minion 74 | # via the providers option, in this case the default module will be 75 | # overwritten by the specified module. In this example the pkg module will 76 | # be provided by the yumpkg5 module instead of the system default. 77 | # 78 | # providers: 79 | # pkg: yumpkg5 80 | # 81 | # Enable Cython modules searching and loading. (Default: False) 82 | #cython_enable: False 83 | 84 | ##### State Management Settings ##### 85 | ########################################### 86 | # The state management system executes all of the state templates on the minion 87 | # to enable more granular control of system state management. The type of 88 | # template and serialization used for state management needs to be configured 89 | # on the minion, the default renderer is yaml_jinja. This is a yaml file 90 | # rendered from a jinja template, the available options are: 91 | # yaml_jinja 92 | # yaml_mako 93 | # json_jinja 94 | # json_mako 95 | # 96 | #renderer: yaml_jinja 97 | # 98 | # state_verbose allows for the data returned from the minion to be more 99 | # verbose. Normaly only states that fail or states that have changes are 100 | # returned, but setting state_verbose to True will return all states that 101 | # were checked 102 | #state_verbose: False 103 | # 104 | # autoload_dynamic_modules Turns on automatic loading of modules found in the 105 | # environments on the master. This is turned on by default, to turn of 106 | # autoloading modules when states run set this value to False 107 | #autoload_dynamic_modules: True 108 | # 109 | # clean_dynamic_modules keeps the dynamic modules on the minion in sync with 110 | # the dynamic modules on the master, this means that if a dynamic module is 111 | # not on the master it will be deleted from the minion. By default this is 112 | # enabled and can be disabled by changing this value to False 113 | #clean_dynamic_modules: True 114 | # 115 | # Normally the minion is not isolated to any single environment on the master 116 | # when running states, but the environment can be isolated on the minion side 117 | # by statically setting it. Remember that the recommended way to manage 118 | # environments is to issolate via the top file. 119 | #environment: None 120 | # 121 | # If using the local file directory, then the state top file name needs to be 122 | # defined, by default this is top.sls. 123 | #state_top: top.sls 124 | 125 | ##### File Directory Settings ##### 126 | ########################################## 127 | # The Salt Minion can redirect all file server operations to a local directory, 128 | # this allows for the same state tree that is on the master to be used if 129 | # coppied completely onto the minion. This is a literal copy of the settings on 130 | # the master but used to reference a local directory on the minion. 131 | 132 | # Set the file client, the client defaults to looking on the master server for 133 | # files, but can be directed to look at the local file directory setting 134 | # defined below by setting it to local. 135 | file_client: local 136 | 137 | # The file directory works on environments passed to the minion, each environment 138 | # can have multiple root directories, the subdirectories in the multiple file 139 | # roots cannot match, otherwise the downloaded files will not be able to be 140 | # reliably ensured. A base environment is required to house the top file. 141 | # Example: 142 | # file_roots: 143 | # base: 144 | # - /srv/salt/ 145 | # dev: 146 | # - /srv/salt/dev/services 147 | # - /srv/salt/dev/states 148 | # prod: 149 | # - /srv/salt/prod/services 150 | # - /srv/salt/prod/states 151 | # 152 | # Default: 153 | #file_roots: 154 | # base: 155 | # - /srv/salt 156 | 157 | # The hash_type is the hash to use when discovering the hash of a file in 158 | # the minion directory, the default is md5, but sha1, sha224, sha256, sha384 159 | # and sha512 are also supported. 160 | #hash_type: md5 161 | 162 | # The Salt pillar is searched for locally if file_client is set to local. If 163 | # this is the case, and pillar data is defined, then the pillar_roots need to 164 | # also be configured on the minion: 165 | #pillar_roots: 166 | # base: 167 | # - /srv/pillar 168 | 169 | ###### Security settings ##### 170 | ########################################### 171 | # Enable "open mode", this mode still maintains encryption, but turns off 172 | # authentication, this is only intended for highly secure environments or for 173 | # the situation where your keys end up in a bad state. If you run in open mode 174 | # you do so at your own risk! 175 | #open_mode: False 176 | 177 | 178 | ###### Thread settings ##### 179 | ########################################### 180 | # Disable multiprocessing support, by default when a minion receives a 181 | # publication a new process is spawned and the command is executed therein. 182 | #multiprocessing: True 183 | 184 | ###### Logging settings ##### 185 | ########################################### 186 | # The location of the minion log file 187 | #log_file: /var/log/salt/minion 188 | # 189 | # The level of messages to send to the log file. 190 | # One of 'info', 'quiet', 'critical', 'error', 'debug', 'warning'. 191 | # Default: 'warning' 192 | #log_level: warning 193 | # 194 | # Logger levels can be used to tweak specific loggers logging levels. 195 | # For example, if you want to have the salt library at the 'warning' level, 196 | # but you still wish to have 'salt.modules' at the 'debug' level: 197 | # log_granular_levels: { 198 | # 'salt': 'warning', 199 | # 'salt.modules': 'debug' 200 | # } 201 | # 202 | #log_granular_levels: {} 203 | 204 | ###### Module configuration ##### 205 | ########################################### 206 | # Salt allows for modules to be passed arbitrary configuration data, any data 207 | # passed here in valid yaml format will be passed on to the salt minion modules 208 | # for use. It is STRONGLY recommended that a naming convention be used in which 209 | # the module name is followed by a . and then the value. Also, all top level 210 | # data must be applied via the yaml dict construct, some examples: 211 | # 212 | # A simple value for the test module: 213 | #test.foo: foo 214 | # 215 | # A list for the test module: 216 | #test.bar: [baz,quo] 217 | # 218 | # A dict for the test module: 219 | #test.baz: {spam: sausage, cheese: bread} 220 | -------------------------------------------------------------------------------- /salt/roots/pillar/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | 'vagrant.django-salted.org': 3 | - vagrant-django 4 | - vagrant-wheel 5 | - vagrant-uwsgi 6 | - vagrant-postgresql 7 | -------------------------------------------------------------------------------- /salt/roots/pillar/vagrant-django.sls: -------------------------------------------------------------------------------- 1 | django: 2 | path: /vagrant/demo_project 3 | settings: demo_project.settings 4 | virtualenv: /home/vagrant/env 5 | user: vagrant 6 | group: vagrant 7 | -------------------------------------------------------------------------------- /salt/roots/pillar/vagrant-postgresql.sls: -------------------------------------------------------------------------------- 1 | postgresql: 2 | db: example_db 3 | user: example_user 4 | password: example_password 5 | createdb: True 6 | -------------------------------------------------------------------------------- /salt/roots/pillar/vagrant-uwsgi.sls: -------------------------------------------------------------------------------- 1 | uwsgi: 2 | reload: True 3 | user: vagrant 4 | group: vagrant 5 | processes: 3 -------------------------------------------------------------------------------- /salt/roots/pillar/vagrant-wheel.sls: -------------------------------------------------------------------------------- 1 | wheel: 2 | path: /home/vagrant/wheel 3 | zip: wheel-requirements.tar.gz 4 | source: https://s3.amazonaws.com/gibbon-deployment/wheel-requirements.tar.gz 5 | md5: 932669db31cc05684786e4a8bf771a3c -------------------------------------------------------------------------------- /salt/roots/salt/example-project/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | # Make site accessible from http://localhost/ 5 | server_name localhost; 6 | 7 | location /static { 8 | expires off; 9 | alias {{ pillar['django']['path'] }}/demo_project/static; 10 | } 11 | location /media { 12 | expires off; 13 | alias {{ pillar['django']['path'] }}/public/media; 14 | } 15 | 16 | location /robots.txt { 17 | rewrite ^/robots.txt {{ pillar['django']['path'] }}/static/robots.txt last; 18 | } 19 | 20 | location / { 21 | include /etc/nginx/uwsgi_params; 22 | uwsgi_pass 127.0.0.1:3000; 23 | 24 | proxy_set_header X-Forwarded-Protocol $scheme; 25 | proxy_pass_header Server; 26 | proxy_set_header Host $http_host; 27 | proxy_redirect off; 28 | proxy_set_header X-Real-IP $remote_addr; 29 | proxy_set_header X-Scheme $scheme; 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /salt/roots/salt/example-project/nginx.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - nginx 3 | 4 | example-nginx-conf: 5 | file.managed: 6 | - name: /etc/nginx/sites-available/example.conf 7 | - source: salt://example-project/nginx.conf 8 | - template: jinja 9 | - user: www-data 10 | - group: www-data 11 | - mode: 755 12 | - require: 13 | - pkg: nginx 14 | 15 | # Symlink and thus enable the virtual host 16 | example-enable-nginx: 17 | file.symlink: 18 | - name: /etc/nginx/sites-enabled/example.conf 19 | - target: /etc/nginx/sites-available/example.conf 20 | - force: false 21 | - require: 22 | - file: example-nginx-conf -------------------------------------------------------------------------------- /salt/roots/salt/example-project/postgresql.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - postgresql 3 | 4 | example-postgres-user: 5 | postgres_user.present: 6 | - name: {{ pillar['postgresql']['user'] }} 7 | - createdb: {{ pillar['postgresql']['createdb'] }} 8 | - password: {{ pillar['postgresql']['password'] }} 9 | - runas: postgres 10 | - require: 11 | - service: postgresql 12 | 13 | example-postgres-db: 14 | postgres_database.present: 15 | - name: {{ pillar['postgresql']['db'] }} 16 | - encoding: UTF8 17 | - lc_ctype: en_US.UTF8 18 | - lc_collate: en_US.UTF8 19 | - template: template0 20 | - owner: {{ pillar['postgresql']['user'] }} 21 | - runas: postgres 22 | - require: 23 | - postgres_user: example-postgres-user -------------------------------------------------------------------------------- /salt/roots/salt/example-project/requirements.sls: -------------------------------------------------------------------------------- 1 | example-packages: 2 | pkg.installed: 3 | - names: 4 | - python-pip 5 | - python-virtualenv 6 | - python-dev 7 | - postgresql-server-dev-9.1 8 | - libxml2-dev 9 | - libxslt1-dev 10 | - libjpeg62-dev -------------------------------------------------------------------------------- /salt/roots/salt/example-project/share.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - nginx 3 | 4 | /vagrant: 5 | file.directory: 6 | - user: vagrant -------------------------------------------------------------------------------- /salt/roots/salt/example-project/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | home = {{ pillar['django']['virtualenv'] }} 3 | uid = {{ pillar['uwsgi']['user'] }} 4 | gid = {{ pillar['uwsgi']['user'] }} 5 | chdir = {{ pillar['django']['path'] }} 6 | socket = 127.0.0.1:3000 7 | wsgi-file = demo_project/wsgi.py 8 | processes = {{ pillar['uwsgi']['processes'] }} 9 | threads = 2 10 | env = DJANGO_SETTINGS_MODULE={{ pillar['django']['settings'] }} 11 | logto = /var/log/uwsgi/%n.log 12 | harakiri = 20 13 | vacuum = True 14 | {% if pillar['uwsgi']['reload'] %} 15 | py-auto-reload = 3 16 | {% endif %} -------------------------------------------------------------------------------- /salt/roots/salt/example-project/uwsgi.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - uwsgi 3 | 4 | example-uwsgi: 5 | file.managed: 6 | - name: /etc/uwsgi/apps-available/example.ini 7 | - source: salt://example-project/uwsgi.ini 8 | - template: jinja 9 | - user: www-data 10 | - group: www-data 11 | - mode: 755 12 | - require: 13 | - pip: uwsgi 14 | 15 | enable-uwsgi-app: 16 | file.symlink: 17 | - name: /etc/uwsgi/apps-enabled/example.ini 18 | - target: /etc/uwsgi/apps-available/example.ini 19 | - force: false 20 | - require: 21 | - file: example-uwsgi 22 | - file: /etc/uwsgi/apps-enabled -------------------------------------------------------------------------------- /salt/roots/salt/example-project/venv.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - nginx 3 | 4 | # Create the Python Virtual environment 5 | {{ pillar['django']['virtualenv'] }}: 6 | virtualenv.managed: 7 | - system_site_packages: False 8 | - distribute: True 9 | - runas: {{ pillar['django']['user'] }} 10 | - requirements: {{ pillar['django']['path'] }}/requirements.txt 11 | - no_chown: True 12 | - require: 13 | - pkg: python-virtualenv 14 | - pkg: python-dev 15 | - pkg: postgresql-server-dev-9.1 16 | - pkg: libxml2-dev 17 | - pkg: libxslt1-dev 18 | - pkg: libjpeg62-dev 19 | -------------------------------------------------------------------------------- /salt/roots/salt/example-project/wheel.sls: -------------------------------------------------------------------------------- 1 | {{ pillar['wheel']['path'] }}: 2 | file.directory: 3 | - user: {{ pillar['django']['user'] }} 4 | 5 | # Manage the tar file containing all the requirements 6 | {{ pillar['wheel']['path'] }}/{{ pillar['wheel']['zip'] }}: 7 | file.managed: 8 | - source: {{ pillar['wheel']['source'] }} 9 | - source_hash: md5={{ pillar['wheel']['md5'] }} 10 | - user: {{ pillar['django']['user'] }} 11 | - require: 12 | - file: {{ pillar['wheel']['path'] }} 13 | 14 | # Extract the archive when it changes 15 | extract-wheel: 16 | cmd.wait: 17 | - user: {{ pillar['django']['user'] }} 18 | - cwd: {{ pillar['wheel']['path'] }} 19 | - name: tar -zxf {{ pillar['wheel']['path'] }}/{{ pillar['wheel']['zip'] }} 20 | - watch: 21 | - file: {{ pillar['wheel']['path'] }}/{{ pillar['wheel']['zip'] }} 22 | - require: 23 | - file: {{ pillar['wheel']['path'] }} 24 | 25 | # Install all the requirements 26 | example-wheel: 27 | cmd.wait: 28 | - name: {{ pillar['django']['virtualenv'] }}/bin/pip install --use-wheel --no-index --find-links={{ pillar['wheel']['path'] }}/example -r {{ pillar['django']['path'] }}/requirements.txt 29 | - require: 30 | - cmd: pip-wheel 31 | - virtualenv: {{ pillar['django']['virtualenv'] }} 32 | - watch: 33 | - file: {{ pillar['wheel']['path'] }}/{{ pillar['wheel']['zip'] }} 34 | - require: 35 | - cmd: pip-dev 36 | - cmd: pip-distribute 37 | - cmd: extract-wheel 38 | - virtualenv: {{ pillar['django']['virtualenv'] }} 39 | -------------------------------------------------------------------------------- /salt/roots/salt/nginx/init.sls: -------------------------------------------------------------------------------- 1 | nginx: 2 | pkg: 3 | - installed 4 | service: 5 | - running 6 | - enable: True 7 | - watch: 8 | - file: /etc/nginx/nginx.conf 9 | - file: /etc/nginx/sites-enabled/* 10 | - require: 11 | - pkg: nginx 12 | 13 | /etc/nginx/nginx.conf: 14 | file.managed: 15 | - source: salt://nginx/nginx.conf 16 | - user: root 17 | - mode: 400 18 | - template: jinja 19 | - require: 20 | - pkg: nginx 21 | 22 | default-nginx: 23 | file.absent: 24 | - name: /etc/nginx/sites-enabled/default 25 | - require: 26 | - pkg: nginx 27 | 28 | www-data: 29 | user.present: 30 | - home: /var/www 31 | - require: 32 | - pkg: bash 33 | - pkg: nginx 34 | 35 | /var/www: 36 | file.directory: 37 | - user: www-data 38 | - group: www-data 39 | - require: 40 | - user: www-data 41 | - pkg: nginx 42 | 43 | /etc/nginx/sites-available: 44 | file.directory: 45 | - user: root 46 | - mode: 755 47 | - require: 48 | - pkg: nginx 49 | 50 | /etc/nginx/sites-enabled: 51 | file.directory: 52 | - user: root 53 | - mode: 755 54 | - require: 55 | - pkg: nginx -------------------------------------------------------------------------------- /salt/roots/salt/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | {% if grains['virtual'] == "VirtualBox" %}user vagrant; 2 | {% else %}user www-data;{% endif %} 3 | worker_processes 4; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | include mime.types; 11 | default_type application/octet-stream; 12 | 13 | {% if grains['virtual'] == "VirtualBox" %} 14 | sendfile off; 15 | {% else %} 16 | sendfile on; 17 | {% endif %} 18 | 19 | tcp_nopush on; 20 | 21 | keepalive_timeout 65; 22 | 23 | gzip on; 24 | gzip_disable "msie6"; 25 | 26 | include /etc/nginx/sites-enabled/*; 27 | } 28 | -------------------------------------------------------------------------------- /salt/roots/salt/postgresql/init.sls: -------------------------------------------------------------------------------- 1 | postgresql: 2 | pkg: 3 | - installed 4 | service.running: 5 | - watch: 6 | - file: /etc/postgresql/9.1/main/pg_hba.conf 7 | - require: 8 | - pkg: postgresql 9 | 10 | pg_hba.conf: 11 | file.managed: 12 | - name: /etc/postgresql/9.1/main/pg_hba.conf 13 | - source: salt://postgresql/pg_hba.conf 14 | - user: postgres 15 | - group: postgres 16 | - mode: 644 17 | - require: 18 | - pkg: postgresql 19 | -------------------------------------------------------------------------------- /salt/roots/salt/postgresql/pg_hba.conf: -------------------------------------------------------------------------------- 1 | # PostgreSQL Client Authentication Configuration File 2 | # =================================================== 3 | # 4 | # Refer to the "Client Authentication" section in the PostgreSQL 5 | # documentation for a complete description of this file. A short 6 | # synopsis follows. 7 | # 8 | # This file controls: which hosts are allowed to connect, how clients 9 | # are authenticated, which PostgreSQL user names they can use, which 10 | # databases they can access. Records take one of these forms: 11 | # 12 | # local DATABASE USER METHOD [OPTIONS] 13 | local sogdb sog md5 14 | local tc2012db tc2012 md5 15 | # host DATABASE USER ADDRESS METHOD [OPTIONS] 16 | # hostssl DATABASE USER ADDRESS METHOD [OPTIONS] 17 | # hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] 18 | # 19 | # (The uppercase items must be replaced by actual values.) 20 | # 21 | # The first field is the connection type: "local" is a Unix-domain 22 | # socket, "host" is either a plain or SSL-encrypted TCP/IP socket, 23 | # "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a 24 | # plain TCP/IP socket. 25 | # 26 | # DATABASE can be "all", "sameuser", "samerole", "replication", a 27 | # database name, or a comma-separated list thereof. The "all" 28 | # keyword does not match "replication". Access to replication 29 | # must be enabled in a separate record (see example below). 30 | # 31 | # USER can be "all", a user name, a group name prefixed with "+", or a 32 | # comma-separated list thereof. In both the DATABASE and USER fields 33 | # you can also write a file name prefixed with "@" to include names 34 | # from a separate file. 35 | # 36 | # ADDRESS specifies the set of hosts the record matches. It can be a 37 | # host name, or it is made up of an IP address and a CIDR mask that is 38 | # an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that 39 | # specifies the number of significant bits in the mask. A host name 40 | # that starts with a dot (.) matches a suffix of the actual host name. 41 | # Alternatively, you can write an IP address and netmask in separate 42 | # columns to specify the set of hosts. Instead of a CIDR-address, you 43 | # can write "samehost" to match any of the server's own IP addresses, 44 | # or "samenet" to match any address in any subnet that the server is 45 | # directly connected to. 46 | # 47 | # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", 48 | # "krb5", "ident", "peer", "pam", "ldap", "radius" or "cert". Note that 49 | # "password" sends passwords in clear text; "md5" is preferred since 50 | # it sends encrypted passwords. 51 | # 52 | # OPTIONS are a set of options for the authentication in the format 53 | # NAME=VALUE. The available options depend on the different 54 | # authentication methods -- refer to the "Client Authentication" 55 | # section in the documentation for a list of which options are 56 | # available for which authentication methods. 57 | # 58 | # Database and user names containing spaces, commas, quotes and other 59 | # special characters must be quoted. Quoting one of the keywords 60 | # "all", "sameuser", "samerole" or "replication" makes the name lose 61 | # its special character, and just match a database or username with 62 | # that name. 63 | # 64 | # This file is read on server startup and when the postmaster receives 65 | # a SIGHUP signal. If you edit the file on a running system, you have 66 | # to SIGHUP the postmaster for the changes to take effect. You can 67 | # use "pg_ctl reload" to do that. 68 | 69 | # Put your actual configuration here 70 | # ---------------------------------- 71 | # 72 | # If you want to allow non-local connections, you need to add more 73 | # "host" records. In that case you will also need to make PostgreSQL 74 | # listen on a non-local interface via the listen_addresses 75 | # configuration parameter, or via the -i or -h command line switches. 76 | 77 | 78 | 79 | 80 | # DO NOT DISABLE! 81 | # If you change this first entry you will need to make sure that the 82 | # database superuser can access the database using some other method. 83 | # Noninteractive access to all databases is required during automatic 84 | # maintenance (custom daily cronjobs, replication, and similar tasks). 85 | # 86 | # Database administrative login by Unix domain socket 87 | local all postgres peer 88 | 89 | # TYPE DATABASE USER ADDRESS METHOD 90 | 91 | # "local" is for Unix domain socket connections only 92 | local all all trust 93 | # IPv4 local connections: 94 | host all all 127.0.0.1/32 md5 95 | # IPv6 local connections: 96 | host all all ::1/128 md5 97 | # Allow replication connections from localhost, by a user with the 98 | # replication privilege. 99 | #local replication postgres peer 100 | #host replication postgres 127.0.0.1/32 md5 101 | #host replication postgres ::1/128 md5 102 | -------------------------------------------------------------------------------- /salt/roots/salt/requirements/essential.sls: -------------------------------------------------------------------------------- 1 | essential-packages: 2 | pkg.installed: 3 | - names: 4 | - vim-nox 5 | - sysstat 6 | - build-essential 7 | # We need this to recognize rxvt terminal 8 | - ncurses-term 9 | - bash 10 | - git 11 | - tmux -------------------------------------------------------------------------------- /salt/roots/salt/ssh/init.sls: -------------------------------------------------------------------------------- 1 | openssh-server: 2 | pkg: 3 | - installed 4 | 5 | ssh: 6 | service: 7 | - running 8 | - watch: 9 | - file: /etc/ssh/sshd_config 10 | 11 | /etc/ssh/sshd_config: 12 | file.managed: 13 | - source: salt://ssh/sshd_config 14 | - mode: 644 15 | - user: root -------------------------------------------------------------------------------- /salt/roots/salt/ssh/sshd_config: -------------------------------------------------------------------------------- 1 | # $OpenBSD: sshd_config,v 1.82 2010/09/06 17:10:19 naddy Exp $ 2 | # $FreeBSD: releng/9.1/crypto/openssh/sshd_config 224638 2011-08-03 19:14:22Z brooks $ 3 | 4 | # This is the sshd server system-wide configuration file. See 5 | # sshd_config(5) for more information. 6 | 7 | # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin 8 | 9 | # The strategy used for options in the default sshd_config shipped with 10 | # OpenSSH is to specify options with their default value where 11 | # possible, but leave them commented. Uncommented options change a 12 | # default value. 13 | 14 | # Note that some of FreeBSD's defaults differ from OpenBSD's, and 15 | # FreeBSD has a few additional options. 16 | 17 | #VersionAddendum FreeBSD-20110503 18 | 19 | #Port 22 20 | #AddressFamily any 21 | #ListenAddress 0.0.0.0 22 | #ListenAddress :: 23 | 24 | # The default requires explicit activation of protocol 1 25 | #Protocol 2 26 | 27 | # HostKey for protocol version 1 28 | #HostKey /etc/ssh/ssh_host_key 29 | # HostKeys for protocol version 2 30 | #HostKey /etc/ssh/ssh_host_rsa_key 31 | #HostKey /etc/ssh/ssh_host_dsa_key 32 | #HostKey /etc/ssh/ssh_host_ecdsa_key 33 | 34 | # Lifetime and size of ephemeral version 1 server key 35 | #KeyRegenerationInterval 1h 36 | #ServerKeyBits 1024 37 | 38 | # Logging 39 | # obsoletes QuietMode and FascistLogging 40 | #SyslogFacility AUTH 41 | #LogLevel INFO 42 | 43 | # Authentication: 44 | 45 | #LoginGraceTime 2m 46 | PermitRootLogin no 47 | #StrictModes yes 48 | #MaxAuthTries 6 49 | #MaxSessions 10 50 | 51 | #RSAAuthentication yes 52 | #PubkeyAuthentication yes 53 | #AuthorizedKeysFile .ssh/authorized_keys 54 | 55 | # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts 56 | #RhostsRSAAuthentication no 57 | # similar for protocol version 2 58 | #HostbasedAuthentication no 59 | # Change to yes if you don't trust ~/.ssh/known_hosts for 60 | # RhostsRSAAuthentication and HostbasedAuthentication 61 | #IgnoreUserKnownHosts no 62 | # Don't read the user's ~/.rhosts and ~/.shosts files 63 | #IgnoreRhosts yes 64 | 65 | # Change to yes to enable built-in password authentication. 66 | PasswordAuthentication no 67 | #PermitEmptyPasswords no 68 | 69 | # Change to no to disable PAM authentication 70 | #ChallengeResponseAuthentication yes 71 | 72 | # Kerberos options 73 | #KerberosAuthentication no 74 | #KerberosOrLocalPasswd yes 75 | #KerberosTicketCleanup yes 76 | #KerberosGetAFSToken no 77 | 78 | # GSSAPI options 79 | #GSSAPIAuthentication no 80 | #GSSAPICleanupCredentials yes 81 | 82 | # Set this to 'no' to disable PAM authentication, account processing, 83 | # and session processing. If this is enabled, PAM authentication will 84 | # be allowed through the ChallengeResponseAuthentication and 85 | # PasswordAuthentication. Depending on your PAM configuration, 86 | # PAM authentication via ChallengeResponseAuthentication may bypass 87 | # the setting of "PermitRootLogin without-password". 88 | # If you just want the PAM account and session checks to run without 89 | # PAM authentication, then enable this but set PasswordAuthentication 90 | # and ChallengeResponseAuthentication to 'no'. 91 | #UsePAM yes 92 | 93 | #AllowAgentForwarding yes 94 | #AllowTcpForwarding yes 95 | #GatewayPorts no 96 | #X11Forwarding yes 97 | #X11DisplayOffset 10 98 | #X11UseLocalhost yes 99 | #PrintMotd yes 100 | #PrintLastLog yes 101 | #TCPKeepAlive yes 102 | #UseLogin no 103 | #UsePrivilegeSeparation yes 104 | #PermitUserEnvironment no 105 | #Compression delayed 106 | #ClientAliveInterval 0 107 | #ClientAliveCountMax 3 108 | #UseDNS yes 109 | #PidFile /var/run/sshd.pid 110 | #MaxStartups 10 111 | #PermitTunnel no 112 | #ChrootDirectory none 113 | 114 | # no default banner path 115 | #Banner none 116 | 117 | # override default of no subsystems 118 | Subsystem sftp /usr/lib/openssh/sftp-server 119 | 120 | # Disable HPN tuning improvements. 121 | #HPNDisabled no 122 | 123 | # Buffer size for HPN to non-HPN connections. 124 | #HPNBufferSize 2048 125 | 126 | # TCP receive socket buffer polling for HPN. Disable on non autotuning kernels. 127 | #TcpRcvBufPoll yes 128 | 129 | # Allow the use of the NONE cipher. 130 | #NoneEnabled no 131 | 132 | # Example of overriding settings on a per-user basis 133 | #Match User anoncvs 134 | # X11Forwarding no 135 | # AllowTcpForwarding no 136 | # ForceCommand cvs server 137 | -------------------------------------------------------------------------------- /salt/roots/salt/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': 3 | - requirements.essential 4 | - ssh 5 | 'vagrant.django-salted.org': 6 | - example-project.requirements 7 | - example-project.nginx 8 | - example-project.share 9 | - example-project.venv 10 | - example-project.uwsgi 11 | - example-project.postgresql 12 | 13 | -------------------------------------------------------------------------------- /salt/roots/salt/uwsgi/init.sls: -------------------------------------------------------------------------------- 1 | uwsgi-packages: 2 | pkg.installed: 3 | - names: 4 | - python-dev 5 | - python-pip 6 | 7 | uwsgi: 8 | pip.installed: 9 | - pkgs: 10 | uwsgi 11 | - require: 12 | - pkg: python-dev 13 | - pkg: python-pip 14 | 15 | /etc/uwsgi/apps-available: 16 | file.directory: 17 | - user: www-data 18 | - group: www-data 19 | - makedirs: true 20 | - require: 21 | - pip: uwsgi 22 | - pkg: nginx 23 | 24 | /etc/uwsgi/apps-enabled: 25 | file.directory: 26 | - user: www-data 27 | - group: www-data 28 | - makedirs: true 29 | - require: 30 | - pip: uwsgi 31 | - pkg: nginx 32 | 33 | /etc/init/uwsgi.conf: 34 | file.managed: 35 | - source: salt://uwsgi/uwsgi.conf 36 | - template: jinja 37 | - require: 38 | - pip: uwsgi 39 | 40 | uwsgi-service: 41 | service.running: 42 | - enable: True 43 | - name: uwsgi 44 | - watch: 45 | - file: /etc/uwsgi/apps-enabled/* 46 | - require: 47 | - file: /etc/init/uwsgi.conf 48 | 49 | /var/log/uwsgi: 50 | file.directory: 51 | - user: www-data 52 | - group: www-data 53 | - makedirs: true 54 | - require: 55 | - pip: uwsgi 56 | - pkg: nginx 57 | 58 | /var/log/uwsgi/emperor.log: 59 | file.managed: 60 | - user: www-data 61 | - group: www-data 62 | - require: 63 | - pip: uwsgi 64 | - pkg: nginx 65 | -------------------------------------------------------------------------------- /salt/roots/salt/uwsgi/uwsgi.conf: -------------------------------------------------------------------------------- 1 | description "uWSGI" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [06] 5 | respawn 6 | 7 | env UWSGI=/usr/local/bin/uwsgi 8 | env LOGTO=/var/log/uwsgi/emperor.log 9 | 10 | script 11 | # This is a dirty hack, we sleep for 15 seconds, because uWSGI get's booted before the vagrant 12 | # share is available. 13 | {% if grains['virtual'] == "VirtualBox" %}sleep 15{% endif %} 14 | exec $UWSGI --master --emperor /etc/uwsgi/apps-enabled --die-on-term --uid root --gid root --chmod-socket --logto $LOGTO 15 | end script --------------------------------------------------------------------------------