fixing one typo and making string formatting consistent.
[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('{0}/manage.py collectstatic --noinput'.format(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('{0}/manage.py runserver'.format(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'.format(env.supervisor_conf))
125
126
127 def supervisorctl(action, process):
128     """
129     Takes as arguments the name of the process as is
130     defined in supervisord.conf and the action that should
131     be performed on it: start|stop|restart.
132     """
133     virtenv_exec('supervisorctl -c {0} {1} {2}'.format(env.supervisor_conf, action, process))
134
135
136 @task
137 def start_celeryd():
138     """
139     Starts the celeryd process
140     """
141     supervisorctl('start', 'celeryd')
142
143
144 @task
145 def stop_celeryd():
146     """
147     Stops the celeryd process
148     """
149     supervisorctl('stop', 'celeryd')
150
151
152 @task
153 def restart_celery():
154     """
155     Restarts the celeryd process
156     """
157     supervisorctl('restart', 'celeryd')
158
159
160 @task
161 def start_gunicorn():
162     """
163     Starts the gunicorn process
164     """
165     supervisorctl('start', 'gunicorn')
166
167
168 @task
169 def stop_gunicorn():
170     """
171     Stops the gunicorn process
172     """
173     supervisorctl('stop', 'gunicorn')
174
175
176 @task
177 def restart_gunicorn():
178     """
179     Restarts the gunicorn process
180     """
181     supervisorctl('restart', 'gunicorn')
182
183
184 ####### Update Requirements
185 @task
186 def update_reqs():
187     virtenv_exec('pip install -r {0}/reqs/prod.txt'.format(env.code_root))
188
189 ####### Pull new code
190 @task
191 def update_code():
192     virtenv_exec('cd {0}; git pull'.format(env.code_root))
193
194 def backup():
195     """
196     Create backup using bup
197     """
198     pass
199
200 @task
201 def file_setup():
202     """
203     Deploy expected files and directories from non-apt system services.
204     """
205     ini_parser = ConfigParser.SafeConfigParser()
206     if not ini_parser.read(env.supervisor_conf):
207       raise Exception("Could not parse INI file {0}".format(env.supervisor_conf))
208     for section, option in (('supervisord','logfile'),
209                             ('supervisord','pidfile'),
210                             ('unix_http_server','file'),
211                             ('program:celeryd','stdout_logfile')):
212       filepath = ini_parser.get(section, option)
213       # generate file's directory structure if needed
214       run('mkdir -p {0}'.format(os.path.split(filepath)[0]))
215       # touch a file and change ownership if needed
216       if 'log' in option and not files.exists(filepath):
217           sudo('touch {0}'.format(filepath))
218           sudo('chown {0}:{1} {2}'.format(env.local_user, env.group, filepath))
219
220 @task
221 def check_secrets():
222     """
223     Ensure secret files exist for syncdb to run.
224     """
225
226     secrets_path = env.code_root + '/karmaworld/secret'
227     secrets_files = ('filepicker.py', 'static_s3.py', 'db_settings.py', 'drive.py', 'client_secrets.json', 'drive.p12')
228
229     errors = []
230     for sfile in secrets_files:
231         ffile = os.path.sep.join((secrets_path,sfile))
232         if not files.exists(ffile):
233             errors.append('{0} missing. Please add and try again.'.format(ffile))
234     if errors:
235         raise Exception('\n'.join(errors))
236
237 @task
238 def fetch_usde():
239     """
240     Download USDE accreditation school CSV.
241     """
242     virtenv_exec('{0}/manage.py fetch_usde_csv {1}'.format(env.code_root, env.usde_csv))
243
244 @task
245 def import_usde():
246     """
247     Import accreditation school CSV into the database and scrub it.
248     """
249     virtenv_exec('{0}/manage.py import_usde_csv {1}'.format(env.code_root, env.usde_csv))
250     virtenv_exec('{0}/manage.py sanitize_usde_schools'.format(env.code_root))
251
252 @task
253 def install_pdf2htmlEX():
254     """
255     # Some things we need:
256     sudo apt-get install cmake libpng-dev libjpeg-dev libgtk2.0-dev pkg-config libfontconfig1-dev autoconf libtool
257
258     # Ubuntu 12.04 comes with a version of poppler that is too
259     # old, so compile our own
260     wget http://poppler.freedesktop.org/poppler-0.24.4.tar.xz
261     tar xf poppler-0.24.4.tar.gz
262     ./configure --prefix=/usr --enable-xpdf-headers
263     make
264     sudo make install
265
266     # Ubuntu 12.04 comes with a version of fontforge that is too
267     # old, so compile our own
268     git clone https://github.com/fontforge/fontforge.git
269     ./autogen.sh
270     ./configure --prefix=/usr
271     make
272     sudo make install
273
274     # Compile pdf2htmlEX
275     wget https://github.com/coolwanglu/pdf2htmlEX/archive/v0.10.tar.gz
276     tar xf x0.10.tar.gz
277     cd pdf2htmlEX
278     cmake .
279     make
280     sudo make install
281     """
282     print "not implemented yet!"
283
284 @task
285 def first_deploy():
286     """
287     Sets up and deploys the project for the first time.
288     """
289     link_code()
290     make_virtualenv()
291     file_setup()
292     check_secrets()
293     update_reqs()
294     syncdb()
295     collect_static()
296     fetch_usde()
297     import_usde()
298     start_supervisord()
299
300
301 @task
302 def deploy():
303     """
304     Deploys the latest changes
305     """
306     update_code()
307     update_reqs()
308     syncdb()
309     collect_static()
310     restart_supervisord()
311 ########## END COMMANDS