Skip to content

Commit 12df29c

Browse files
authored
Create create_py3_django_project_run_env.sh
1 parent bb9c972 commit 12df29c

File tree

1 file changed

+316
-0
lines changed

1 file changed

+316
-0
lines changed

create_py3_django_project_run_env.sh

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
#!/bin/bash
2+
#
3+
# Usage:
4+
# $ create_django_project_run_env <appname>
5+
6+
# error exit function
7+
function error_exit
8+
{
9+
echo "$1" 1>&2
10+
exit 1
11+
}
12+
13+
# check if we're being run as root
14+
if [ "$EUID" -ne 0 ]; then
15+
echo "Please run as root"
16+
exit
17+
fi
18+
19+
# conventional values that we'll use throughout the script
20+
APPNAME=$1
21+
DOMAINNAME=$2
22+
GROUPNAME=webapps
23+
# app folder name under /webapps/<appname>_project
24+
APPFOLDER=$1_project
25+
APPFOLDERPATH=/$GROUPNAME/$APPFOLDER
26+
# prerequisite standard packages. If any of these are missing,
27+
# script will attempt to install it. If installation fails, it will abort.
28+
LINUX_PREREQ=('git' 'build-essential' 'python-dev' 'nginx' 'postgresql' 'libpq-dev' 'python-pip')
29+
PYTHON_PREREQ=('virtualenv' 'supervisor')
30+
31+
# check appname was supplied as argument
32+
if [ "$APPNAME" == "" ] || [ "$DOMAINNAME" == "" ]; then
33+
echo "Usage:"
34+
echo " $ create_django_project_run_env <project> <domain>"
35+
echo
36+
exit 1
37+
fi
38+
39+
# test prerequisites
40+
echo "Checking if required packages are installed..."
41+
declare -a MISSING
42+
for pkg in "${LINUX_PREREQ[@]}"
43+
do
44+
echo "Installing '$pkg'..."
45+
apt-get -y install $pkg
46+
if [ $? -ne 0 ]; then
47+
echo "Error installing system package '$pkg'"
48+
exit 1
49+
fi
50+
done
51+
52+
for ppkg in "${PYTHON_PREREQ[@]}"
53+
do
54+
echo "Installing Python package '$ppkg'..."
55+
pip install $ppkg
56+
if [ $? -ne 0 ]; then
57+
echo "Error installing python package '$ppkg'"
58+
exit 1
59+
fi
60+
done
61+
62+
if [ ${#MISSING[@]} -ne 0 ]; then
63+
echo "Following required packages are missing, please install them first."
64+
echo ${MISSING[*]}
65+
exit 1
66+
fi
67+
68+
echo "All required packages are installed!"
69+
70+
# create the app folder
71+
echo "Creating app folder '$APPFOLDERPATH'..."
72+
mkdir -p /$GROUPNAME/$APPFOLDER || error_exit "Could not create app folder"
73+
74+
# test the group 'webapps' exists, and if it doesn't create it
75+
getent group $GROUPNAME
76+
if [ $? -ne 0 ]; then
77+
echo "Creating group '$GROUPNAME' for automation accounts..."
78+
groupadd --system $GROUPNAME || error_exit "Could not create group 'webapps'"
79+
fi
80+
81+
# create the app user account, same name as the appname
82+
grep "$APPNAME:" /etc/passwd
83+
if [ $? -ne 0 ]; then
84+
echo "Creating automation user account '$APPNAME'..."
85+
useradd --system --gid $GROUPNAME --shell /bin/bash --home $APPFOLDERPATH $APPNAME || error_exit "Could not create automation user account '$APPNAME'"
86+
fi
87+
88+
# change ownership of the app folder to the newly created user account
89+
echo "Setting ownership of $APPFOLDERPATH and its descendents to $APPNAME:$GROUPNAME..."
90+
chown -R $APPNAME:$GROUPNAME $APPFOLDERPATH || error_exit "Error setting ownership"
91+
# give group execution rights in the folder;
92+
# TODO: is this necessary? why?
93+
chmod g+x $APPFOLDERPATH || error_exit "Error setting group execute flag"
94+
95+
# install python virtualenv in the APPFOLDER
96+
echo "Creating environment setup for django app..."
97+
su -l $APPNAME << 'EOF'
98+
pwd
99+
echo "Setting up python virtualenv..."
100+
virtualenv -p python3 . || error_exit "Error installing virtual environment to app folder"
101+
source ./bin/activate
102+
# upgrade pip
103+
pip install --upgrade pip || error_exist "Error upgrading pip to the latest version"
104+
# install prerequisite python packages for a django app using pip
105+
echo "Installing base python packages for the app..."
106+
# Standard django packages which will be installed. If any of these fail, script will abort
107+
DJANGO_PKGS=('django' 'psycopg2' 'gunicorn' 'setproctitle')
108+
for dpkg in "${DJANGO_PKGS[@]}"
109+
do
110+
echo "Installing $dpkg..."
111+
pip install $dpkg || error_exit "Error installing $dpkg"
112+
done
113+
# create the default folders where we store django app's resources
114+
echo "Creating static file folders..."
115+
mkdir logs run ssl static media || error_exit "Error creating static folders"
116+
117+
EOF
118+
119+
# generate secret key
120+
echo "Generating Django secret key..."
121+
DJANGO_SECRET_KEY=`openssl rand -base64 48`
122+
if [ $? -ne 0 ]; then
123+
error_exit "Error creating secret key."
124+
fi
125+
echo $DJANGO_SECRET_KEY > $APPFOLDERPATH/.django_secret_key
126+
chown $APPNAME:$GROUPNAME $APPFOLDERPATH/.django_secret_key
127+
128+
echo "Creating gunicorn startup script..."
129+
cat > /tmp/prepare_env.sh << EOF
130+
DJANGODIR=$APPFOLDERPATH/$APPNAME # Django project directory
131+
DJANGO_SETTINGS_MODULE=$APPNAME.settings # settings file for the app
132+
133+
export DJANGO_SETTINGS_MODULE=\$DJANGO_SETTINGS_MODULE
134+
export PYTHONPATH=\$DJANGODIR:\$PYTHONPATH
135+
export SECRET_KEY=`cat $APPFOLDERPATH/.django_secret_key`
136+
export DB_PASSWORD=`cat $APPFOLDERPATH/.django_db_password`
137+
138+
cd $APPFOLDERPATH
139+
source ./bin/activate
140+
EOF
141+
mv /tmp/prepare_env.sh $APPFOLDERPATH
142+
chown $APPNAME:$GROUPNAME $APPFOLDERPATH/prepare_env.sh
143+
144+
cat > /tmp/gunicorn_start.sh << EOF
145+
#!/bin/bash
146+
# Makes the following assumptions:
147+
#
148+
# 1. All applications are located in a subfolder within /webapps
149+
# 2. Each app gets a dedicated subfolder <appname> under /webapps. This will
150+
# be referred to as the app folder.
151+
# 3. The group account 'webapps' exists and each app is to be executed
152+
# under the user account <appname>.
153+
# 4. The app folder and all its recursive contents are owned by
154+
# <appname>:webapps.
155+
# 5. The django app is stored under /webapps/<appname>/<appname> folder.
156+
#
157+
158+
cd $APPFOLDERPATH
159+
source ./prepare_env.sh
160+
161+
SOCKFILE=$APPFOLDERPATH/run/gunicorn.sock # we will communicte using this unix socket
162+
USER=$APPNAME # the user to run as
163+
GROUP=$GROUPNAME # the group to run as
164+
NUM_WORKERS=3 # how many worker processes should Gunicorn spawn
165+
DJANGO_WSGI_MODULE=$APPNAME.wsgi # WSGI module name
166+
167+
echo "Starting $APPNAME as \`whoami\`"
168+
169+
# Create the run directory if it doesn't exist
170+
RUNDIR=\$(dirname \$SOCKFILE)
171+
test -d \$RUNDIR || mkdir -p \$RUNDIR
172+
173+
# Start your Django Unicorn
174+
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
175+
exec ./bin/gunicorn \${DJANGO_WSGI_MODULE}:application \
176+
--name $APPNAME \
177+
--workers \$NUM_WORKERS \
178+
--user=\$USER --group=\$GROUP \
179+
--bind=unix:\$SOCKFILE \
180+
--log-level=debug \
181+
--log-file=-
182+
EOF
183+
184+
# move the script to app folder
185+
mv /tmp/gunicorn_start.sh $APPFOLDERPATH
186+
chown $APPNAME:$GROUPNAME $APPFOLDERPATH/gunicorn_start.sh
187+
chmod u+x $APPFOLDERPATH/gunicorn_start.sh
188+
189+
# create the PostgreSQL database and associated role for the app
190+
# Database and role name would be the same as the <appname> argument
191+
echo "Creating secure password for database role..."
192+
DBPASSWORD=`openssl rand -base64 32`
193+
if [ $? -ne 0 ]; then
194+
error_exit "Error creating secure password for database role."
195+
fi
196+
echo $DBPASSWORD > $APPFOLDERPATH/.django_db_password
197+
chown $APPNAME:$GROUPNAME $APPFOLDERPATH/.django_db_password
198+
echo "Creating PostgreSQL role '$APPNAME'..."
199+
su postgres -c "createuser -S -D -R -w $APPNAME"
200+
echo "Changing password of database role..."
201+
su postgres -c "psql -c \"ALTER USER $APPNAME WITH PASSWORD '$DBPASSWORD';\""
202+
echo "Creating PostgreSQL database '$APPNAME'..."
203+
su postgres -c "createdb --owner $APPNAME $APPNAME"
204+
205+
# create nginx template in /etc/nginx/sites-available
206+
mkdir -p /etc/nginx/sites-available
207+
APPSERVERNAME=$APPNAME
208+
APPSERVERNAME+=_gunicorn
209+
cat > /etc/nginx/sites-available/$APPNAME.conf << EOF
210+
upstream $APPSERVERNAME {
211+
server unix:$APPFOLDERPATH/run/gunicorn.sock fail_timeout=0;
212+
}
213+
server {
214+
listen 80;
215+
server_name $DOMAINNAME;
216+
217+
client_max_body_size 5M;
218+
keepalive_timeout 5;
219+
underscores_in_headers on;
220+
221+
access_log $APPFOLDERPATH/logs/nginx-access.log;
222+
error_log $APPFOLDERPATH/logs/nginx-error.log;
223+
224+
location /media {
225+
alias $APPFOLDERPATH/media;
226+
}
227+
location /static {
228+
alias $APPFOLDERPATH/static;
229+
}
230+
location /static/admin {
231+
alias $APPFOLDERPATH/lib/python3.5/site-packages/django/contrib/admin/static/admin/;
232+
}
233+
# This would redirect http site access to HTTPS. Uncomment to enable
234+
#location / {
235+
# rewrite ^ https://\$http_host\$request_uri? permanent;
236+
#}
237+
# To make the site pure HTTPS, comment the following section while
238+
# uncommenting the above section. Also uncoment the HTTPS section
239+
location / {
240+
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
241+
proxy_set_header Host \$http_host;
242+
proxy_redirect off;
243+
proxy_pass http://$APPSERVERNAME;
244+
}
245+
}
246+
247+
# Uncomment this if you want to enable HTTPS access. Also, remember to install
248+
# the site certificate, either purcahased or generated.
249+
#server {
250+
# listen 443 default ssl;
251+
# server_name $DOMAINNAME;
252+
#
253+
# client_max_body_size 5M;
254+
# keepalive_timeout 5;
255+
#
256+
# ssl_certificate /etc/nginx/ssl/cert_chain.crt;
257+
# ssl_certificate_key $APPFOLDERPATH/ssl/$DOMAINNAME.key;
258+
#
259+
# access_log $APPFOLDERPATH/logs/nginx-access.log;
260+
# error_log $APPFOLDERPATH/logs/nginx-error.log;
261+
#
262+
# location /media {
263+
# alias $APPFOLDERPATH/media;
264+
# }
265+
# location /static {
266+
# alias $APPFOLDERPATH/static;
267+
# }
268+
# location /static/admin {
269+
# alias $APPFOLDERPATH/lib/python3.5/site-packages/django/contrib/admin/static/admin/;
270+
# }
271+
# location / {
272+
# proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
273+
# proxy_set_header Host \$http_host;
274+
# proxy_set_header X-Forwarded-Proto \$scheme;
275+
# proxy_redirect off;
276+
# proxy_pass http://$APPSERVERNAME;
277+
# }
278+
#}
279+
EOF
280+
# make a symbolic link to the nginx conf file in sites-enabled
281+
ln -s /etc/nginx/sites-available/$APPNAME.conf /etc/nginx/sites-enabled/$APPNAME
282+
283+
# copy supervisord.conf
284+
cp ./supervisord.conf /etc || error_exit "Error copying supervisord.conf"
285+
286+
# create the supervisor application conf file
287+
mkdir -p /etc/supervisor
288+
cat > /etc/supervisor/$APPNAME.conf << EOF
289+
[program:$APPNAME]
290+
command = $APPFOLDERPATH/gunicorn_start.sh
291+
user = $APPNAME
292+
stdout_logfile = $APPFOLDERPATH/logs/gunicorn_supervisor.log
293+
redirect_stderr = true
294+
EOF
295+
296+
# create supervisord init.d script that can be controlled with service
297+
echo "Setting up supervisor to autostart during bootup..."
298+
cp ./supervisord /etc/init.d || error_exit "Error copying /etc/init.d/supervisord"
299+
# enable execute flag on the script
300+
chmod +x /etc/init.d/supervisord || error_exit "Error setting execute flag on supervisord"
301+
# create the entries in runlevel folders to autostart supervisord
302+
update-rc.d supervisord defaults || error_exit "Error configuring supervisord to autostart"
303+
304+
# now create a quasi django project that can be run using a GUnicorn script
305+
echo "Installing quasi django project..."
306+
su -l $APPNAME << EOF
307+
source ./bin/activate
308+
django-admin.py startproject $APPNAME
309+
EOF
310+
311+
# now start the supervisord daemon
312+
service supervisord start || error_exit "Error starting supervisord"
313+
# reload nginx so that requests to domain are redirected to the gunicorn process
314+
nginx -s reload || error_exit "Error reloading nginx. Check configuration files"
315+
316+
echo "Done!"

0 commit comments

Comments
 (0)