a664ed40baf685e5579a252276c07408fe3a8842
[oweals/karmaworld.git] / fabfile.py
1
2 """ Karmaworld Fabric management script
3     Finals Club (c) 2013"""
4
5 import os
6 import ConfigParser
7
8 from fabric.api import cd, env, lcd, prefix, run, sudo, task, local, settings
9 from fabric.contrib import files
10
11 ######### GLOBAL
12 env.group = 'www-data'
13 env.proj_repo = 'git@github.com:FinalsClub/karmaworld.git'
14 env.repo_root = '~/karmaworld' # transient setting for VMs only
15 env.proj_root = '/var/www/karmaworld'
16 env.branch = 'prod' # only used for supervisor conf two lines below. cleanup?
17 env.code_root = env.proj_root
18 env.supervisor_conf = '{0}/confs/{1}/supervisord.conf'.format(env.code_root, env.branch)
19 env.usde_csv = '{0}/confs/acceditation.csv'.format(env.code_root)
20
21 env.use_ssh_config = True
22
23 ######## Run Commands in Virtual Environment
24 def virtenv_path():
25     """
26     Find and memoize the virtualenv for use internally.
27     """
28     default_venv = env.proj_root + '/venv/bin/activate'
29
30     # Return environment root if its been memoized
31     if 'env_root' in env and env['env_root']:
32         return env['env_root']
33
34     # Not memoized. Try to find a single unique virtual environment.
35     outp = run("find -L {0} -path '*/bin/activate' | grep -v '/local/'".format(env.proj_root))
36     if not len(outp) or len(outp.splitlines()) != 1:
37         # Cannot find any virtualenv or found multiple virtualenvs. 
38         if len(outp) and default_venv not in outp:
39             # Multiple venvs and the default is not present.
40             raise Exception('Cannot determine the appropriate virtualenv.')
41         # If there are no virtualenvs, then use the default (this will create
42         # one if being called by make_virtualenv, otherwise it will cause an
43         # error).
44         # If there are multiple virtualenvs and the default is in their midst,
45         # use the default.
46         outp = default_venv
47     # Pop off the /bin/activate from /venv/bin/activate
48     outp = os.path.sep.join(outp.split(os.path.sep)[:-2])
49     env['env_root'] = outp
50     return outp
51
52 def virtenv_exec(command):
53     """
54     Execute command in Virtualenv
55     """
56     with prefix('source {0}/bin/activate'.format(virtenv_path())):
57         run(command)
58
59 ######## Sync database
60 @task
61 def syncdb():
62     """
63     Sync Database
64     """
65     virtenv_exec('{0}/manage.py syncdb --migrate'.format(env.code_root))
66
67
68 ####### Collect Static Files
69 @task
70 def collect_static():
71         """
72         Collect static files (if AWS config. present, push to S3)
73         """
74
75         virtenv_exec('%s/manage.py collectstatic --noinput' % env.code_root )   
76
77 ####### Run Dev Server
78 @task
79 def dev_server():
80         """
81         Runs the built-in django webserver
82         """
83
84         virtenv_exec('%s/manage.py runserver' % env.code_root)  
85
86 ####### Create Virtual Environment
87
88 @task
89 def link_code():
90     """
91     Link the karmaworld repo into the appropriate production location
92     """
93     if not files.exists(env.code_root):
94         run('ln -s {0} {1}'.format(env.repo_root, env.code_root))
95
96 @task
97 def make_virtualenv():
98     """
99     Create our Virtualenv
100     """
101     run('virtualenv {0}'.format(virtenv_path()))
102
103 @task
104 def start_supervisord():
105     """
106     Starts supervisord
107     """
108     virtenv_exec('supervisord -c {0}'.format(env.supervisor_conf))
109
110
111 @task
112 def stop_supervisord():
113     """
114     Restarts supervisord
115     """
116     virtenv_exec('supervisorctl -c {0} shutdown'.format(env.supervisor_conf))
117
118
119 @task
120 def restart_supervisord():
121     """
122     Restarts supervisord, also making sure to load in new config data.
123     """
124     virtenv_exec('supervisorctl -c {0} update; supervisorctl -c {0} restart all
125 '.format(env.supervisor_conf))
126
127
128 def supervisorctl(action, process):
129     """
130     Takes as arguments the name of the process as is
131     defined in supervisord.conf and the action that should
132     be performed on it: start|stop|restart.
133     """
134     virtenv_exec('supervisorctl -c {0} {1} {2}'.format(env.supervisor_conf, action, process))
135
136
137 @task
138 def start_celeryd():
139     """
140     Starts the celeryd process
141     """
142     supervisorctl('start', 'celeryd')
143
144
145 @task
146 def stop_celeryd():
147     """
148     Stops the celeryd process
149     """
150     supervisorctl('stop', 'celeryd')
151
152
153 @task
154 def restart_celery():
155     """
156     Restarts the celeryd process
157     """
158     supervisorctl('restart', 'celeryd')
159
160
161 @task
162 def start_gunicorn():
163     """
164     Starts the gunicorn process
165     """
166     supervisorctl('start', 'gunicorn')
167
168
169 @task
170 def stop_gunicorn():
171     """
172     Stops the gunicorn process
173     """
174     supervisorctl('stop', 'gunicorn')
175
176
177 @task
178 def restart_gunicorn():
179     """
180     Restarts the gunicorn process
181     """
182     supervisorctl('restart', 'gunicorn')
183
184
185 ####### Update Requirements
186 @task
187 def update_reqs():
188     virtenv_exec('pip install -r {0}/reqs/prod.txt'.format(env.code_root))
189
190 ####### Pull new code
191 @task
192 def update_code():
193     virtenv_exec('cd %s; git pull' % env.proj_root )
194
195 def backup():
196     """
197     Create backup using bup
198     """
199     pass
200
201 @task
202 def file_setup():
203     """
204     Deploy expected files and directories from non-apt system services.
205     """
206     ini_parser = ConfigParser.SafeConfigParser()
207     if not ini_parser.read(env.supervisor_conf):
208       raise Exception("Could not parse INI file {0}".format(env.supervisor_conf))
209     for section, option in (('supervisord','logfile'),
210                             ('supervisord','pidfile'),
211                             ('unix_http_server','file'),
212                             ('program:celeryd','stdout_logfile')):
213       filepath = ini_parser.get(section, option)
214       # generate file's directory structure if needed
215       run('mkdir -p {0}'.format(os.path.split(filepath)[0]))
216       # touch a file and change ownership if needed
217       if 'log' in option and not files.exists(filepath):
218           sudo('touch {0}'.format(filepath))
219           sudo('chown {0}:{1} {2}'.format(env.local_user, env.group, filepath))
220
221 @task
222 def check_secrets():
223     """
224     Ensure secret files exist for syncdb to run.
225     """
226
227     secrets_path = env.code_root + '/karmaworld/secret'
228     secrets_files = ('filepicker.py', 'static_s3.py', 'db_settings.py', 'drive.py', 'client_secrets.json', 'drive.p12')
229
230     errors = []
231     for sfile in secrets_files:
232         ffile = os.path.sep.join((secrets_path,sfile))
233         if not files.exists(ffile):
234             errors.append('{0} missing. Please add and try again.'.format(ffile))
235     if errors:
236         raise Exception('\n'.join(errors))
237
238 @task
239 def fetch_usde():
240     """
241     Download USDE accreditation school CSV.
242     """
243     virtenv_exec('{0}/manage.py fetch_usde_csv {1}'.format(env.code_root, env.usde_csv))
244
245 @task
246 def import_usde():
247     """
248     Import accreditation school CSV into the database and scrub it.
249     """
250     virtenv_exec('{0}/manage.py import_usde_csv {1}'.format(env.code_root, env.usde_csv))
251     virtenv_exec('{0}/manage.py sanitize_usde_schools'.format(env.code_root))
252
253 @task
254 def install_pdf2htmlEX():
255     """
256     # Some things we need:
257     sudo apt-get install cmake libpng-dev libjpeg-dev libgtk2.0-dev pkg-config libfontconfig1-dev autoconf libtool
258
259     # Ubuntu 12.04 comes with a version of poppler that is too
260     # old, so compile our own
261     wget http://poppler.freedesktop.org/poppler-0.24.4.tar.xz
262     tar xf poppler-0.24.4.tar.gz
263     ./configure --prefix=/usr --enable-xpdf-headers
264     make
265     sudo make install
266
267     # Ubuntu 12.04 comes with a version of fontforge that is too
268     # old, so compile our own
269     git clone https://github.com/fontforge/fontforge.git
270     ./autogen.sh
271     ./configure --prefix=/usr
272     make
273     sudo make install
274
275     # Compile pdf2htmlEX
276     wget https://github.com/coolwanglu/pdf2htmlEX/archive/v0.10.tar.gz
277     tar xf x0.10.tar.gz
278     cd pdf2htmlEX
279     cmake .
280     make
281     sudo make install
282     """
283     print "not implemented yet!"
284
285 @task
286 def first_deploy():
287     """
288     Sets up and deploys the project for the first time.
289     """
290     link_code()
291     make_virtualenv()
292     file_setup()
293     check_secrets()
294     update_reqs()
295     syncdb()
296     collect_static()
297     fetch_usde()
298     import_usde()
299     start_supervisord()
300
301
302 @task
303 def deploy():
304     """
305     Deploys the latest changes
306     """
307     update_code()
308     update_reqs()
309     syncdb()
310     collect_static()
311     restart_supervisord()
312 ########## END COMMANDS