Last active
June 10, 2018 13:43
-
-
Save miguelgrinberg/d9f1fcafc012f3fb3f03 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# note: this script is invoked automatically by rackspace-flasky.py | |
# save command line arguments | |
GMAIL_USERNAME=$1 | |
GMAIL_PASSWORD=$2 | |
# install web server dependencies | |
apt-get update | |
apt-get -y install python python-virtualenv nginx supervisor git | |
# create a flasky user | |
adduser --disabled-password --gecos "" flasky | |
# clone application from github | |
cd /home/flasky | |
git clone https://github.com/miguelgrinberg/flasky.git | |
cd flasky | |
# Flasky configuration | |
cat <<EOF >.env | |
FLASK_CONFIG=heroku | |
SECRET_KEY=3947865439567642 | |
MAIL_USERNAME=$GMAIL_USERNAME | |
MAIL_PASSWORD=$GMAIL_PASSWORD | |
FLASKY_ADMIN=${GMAIL_USERNAME}@gmail.com | |
SSL_DISABLE=1 | |
EOF | |
# create a virtualenv and install dependencies | |
virtualenv venv | |
venv/bin/pip install -r requirements/prod.txt | |
venv/bin/pip install gunicorn | |
# create database | |
venv/bin/python manage.py deploy | |
# make the flasky user the owner of everything | |
chown -R flasky:flasky ./ | |
# configure supervisor | |
mkdir /var/log/flasky | |
cat <<EOF >/etc/supervisor/conf.d/flasky.conf | |
[program:flasky] | |
command=/home/flasky/flasky/venv/bin/gunicorn -b 127.0.0.1:8000 -w 4 --chdir /home/flasky/flasky --log-file - manage:app | |
user=flasky | |
autostart=true | |
autorestart=true | |
stderr_logfile=/var/log/flasky/stderr.log | |
stdout_logfile=/var/log/flasky/stdout.log | |
EOF | |
supervisorctl reread | |
supervisorctl update | |
# configure nginx | |
cat <<EOF >/etc/nginx/sites-available/flasky | |
server { | |
listen 80; | |
server_name _; | |
access_log /var/log/nginx/flasky.access.log; | |
error_log /var/log/nginx/flasky.error.log; | |
location / { | |
proxy_pass http://127.0.0.1:8000; | |
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; | |
} | |
location /static { | |
alias /home/flasky/flasky/static; | |
} | |
location /favicon.ico { | |
alias /home/flasky/flasky/favicon.ico; | |
} | |
} | |
EOF | |
rm -f /etc/nginx/sites-enabled/default | |
ln -s /etc/nginx/sites-available/flasky /etc/nginx/sites-enabled/ | |
service nginx restart |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import os | |
import time | |
import re | |
import argparse | |
import errno | |
from socket import error as socket_error | |
from getpass import getpass | |
import paramiko | |
import pyrax | |
nova = None | |
def get_flavor(name): | |
return nova.flavors.find(name=name) | |
def get_image(name): | |
return nova.images.find(name=name) | |
def create_web_server(flavor=None, image=None, key_name=None): | |
server = nova.servers.create(name='flasky', flavor=flavor.id, | |
image=image.id, key_name=key_name) | |
print 'Building, please wait...' | |
# wait for server create to be complete | |
while server.status == 'BUILD': | |
time.sleep(5) | |
server = nova.servers.get(server.id) # refresh server | |
# check for errors | |
if server.status != 'ACTIVE': | |
raise RuntimeError('Server did not boot, status=' + server.status) | |
# the server was assigned IPv4 and IPv6 addresses, locate the IPv4 address | |
ip_address = None | |
for network in server.networks['public']: | |
if re.match('\d+\.\d+\.\d+\.\d+', network): | |
ip_address = network | |
break | |
if ip_address is None: | |
raise RuntimeError('No IP address assigned!') | |
print 'Server is running at IP address ' + ip_address | |
return ip_address | |
def install_flasky(ip_address, gmail_username, gmail_password): | |
print 'Installing Flasky...' | |
# establish a SSH connection | |
ssh = paramiko.SSHClient() | |
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) | |
ssh.load_system_host_keys() | |
retries_left = 3 | |
while True: | |
try: | |
ssh.connect(ip_address, username='root') | |
break | |
except socket_error as e: | |
if e.errno != errno.ECONNREFUSED or retries_left <= 1: | |
raise e | |
time.sleep(10) # wait 10 seconds and retry | |
retries_left -= 1 | |
# upload deployment script | |
ftp = ssh.open_sftp() | |
ftp.put('install-flasky.sh', 'install-flasky.sh') | |
ftp.chmod('install-flasky.sh', 0544) | |
# deploy | |
stdin, stdout, stderr = ssh.exec_command( | |
'./install-flasky.sh "{0}" "{1}"'.format( | |
gmail_username, gmail_password)) | |
status = stdout.channel.recv_exit_status() | |
open('stdout.log', 'wt').write(stdout.read()) | |
open('stderr.log', 'wt').write(stderr.read()) | |
if status != 0: | |
raise RuntimeError( | |
'Deployment script returned status {0}.'.format(status)) | |
def process_args(): | |
parser = argparse.ArgumentParser( | |
description='Deploy Flasky to a Rackspace cloud server.') | |
parser.add_argument( | |
'--flavor', '-f', metavar='FLAVOR', default='512MB Standard Instance', | |
help='Flavor to use for the instance.' | |
'Default is 512MB Standard Instance.') | |
parser.add_argument( | |
'--image', '-i', metavar='IMAGE', | |
default='Ubuntu 14.04 LTS (Trusty Tahr)', | |
help='Image to use for the server. Default is Ubuntu 14.04.') | |
parser.add_argument( | |
'key_name', metavar='KEY_NAME', | |
help='Keypair name to install on the server. ' | |
'Must be uploaded to your cloud account in advance.') | |
parser.add_argument( | |
'gmail_username', metavar='GMAIL_USERNAME', | |
help='The username of the gmail account that the web app ' | |
'will use to send emails.') | |
return parser.parse_args() | |
def main(): | |
args = process_args() | |
gmail_password = getpass('gmail password: ') | |
# instantiate the nova client | |
global nova | |
pyrax.set_setting('identity_type', os.environ['OS_AUTH_SYSTEM']) | |
pyrax.set_default_region(os.environ['OS_REGION_NAME']) | |
pyrax.set_credentials(os.environ['OS_USERNAME'], os.environ['OS_PASSWORD']) | |
nova = pyrax.cloudservers | |
flavor = get_flavor(args.flavor) | |
image = get_image(args.image) | |
ip_address = create_web_server(flavor, image, args.key_name) | |
install_flasky(ip_address, args.gmail_username, gmail_password) | |
print 'Flasky is now running at http://{0}.'.format(ip_address) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment