workaround for #270
[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     with settings(warn_only=True):
36         outp = run("find -L {0} -path '*/bin/activate' | grep -v '/local/'".format(env.proj_root))
37     if not len(outp) or len(outp.splitlines()) != 1:
38         # Cannot find any virtualenv or found multiple virtualenvs. 
39         if len(outp) and default_venv not in outp:
40             # Multiple venvs and the default is not present.
41             raise Exception('Cannot determine the appropriate virtualenv.')
42         # If there are no virtualenvs, then use the default (this will create
43         # one if being called by make_virtualenv, otherwise it will cause an
44         # error).
45         # If there are multiple virtualenvs and the default is in their midst,
46         # use the default.
47         outp = default_venv
48     # Pop off the /bin/activate from /venv/bin/activate
49     outp = os.path.sep.join(outp.split(os.path.sep)[:-2])
50     env['env_root'] = outp
51     return outp
52
53 def virtenv_exec(command):
54     """
55     Execute command in Virtualenv
56     """
57     with prefix('source {0}/bin/activate'.format(virtenv_path())):
58         run(command)
59
60 ######## Sync database
61 @task
62 def syncdb():
63     """
64     Sync Database
65     """
66     virtenv_exec('{0}/manage.py syncdb --migrate --noinput'.format(env.code_root))
67
68
69 ####### Collect Static Files
70 @task
71 def collect_static():
72         """
73         Collect static files (if AWS config. present, push to S3)
74         """
75
76         virtenv_exec('{0}/manage.py collectstatic --noinput'.format(env.code_root))
77
78 ####### Run Dev Server
79 @task
80 def dev_server():
81         """
82         Runs the built-in django webserver
83         """
84
85         virtenv_exec('{0}/manage.py runserver'.format(env.code_root))
86
87 ####### Create Virtual Environment
88
89 @task
90 def link_code():
91     """
92     Link the karmaworld repo into the appropriate production location
93     """
94     if not files.exists(env.code_root):
95         run('ln -s {0} {1}'.format(env.repo_root, env.code_root))
96
97 @task
98 def make_virtualenv():
99     """
100     Create our Virtualenv
101     """
102     run('virtualenv {0}'.format(virtenv_path()))
103
104 @task
105 def start_supervisord():
106     """
107     Starts supervisord
108     """
109     virtenv_exec('supervisord -c {0}'.format(env.supervisor_conf))
110
111
112 @task
113 def stop_supervisord():
114     """
115     Restarts supervisord
116     """
117     virtenv_exec('supervisorctl -c {0} shutdown'.format(env.supervisor_conf))
118
119
120 @task
121 def restart_supervisord():
122     """
123     Restarts supervisord, also making sure to load in new config data.
124     """
125     virtenv_exec('supervisorctl -c {0} update; supervisorctl -c {0} restart all'.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_celery():
139     """
140     Starts the celeryd process
141     """
142     supervisorctl('start', 'celeryd')
143
144
145 @task
146 def stop_celery():
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 {0}; git pull'.format(env.code_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 first_deploy():
255     """
256     Sets up and deploys the project for the first time.
257     """
258     link_code()
259     make_virtualenv()
260     file_setup()
261     check_secrets()
262     update_reqs()
263     syncdb()
264     collect_static()
265     fetch_usde()
266     import_usde()
267     start_supervisord()
268     print "You should run `manage.py createsuperuser` in the virtual environment"
269
270
271 @task
272 def deploy():
273     """
274     Deploys the latest changes
275     """
276     update_code()
277     update_reqs()
278     syncdb()
279     collect_static()
280     restart_supervisord()
281 ########## END COMMANDS