Double nesting won't work
[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/accreditation.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 install_reqs():
188     # first install must be done without --upgrade for a few packages that break
189     # due to a pip problem.
190     virtenv_exec('pip install -r {0}/reqs/prod.txt'.format(env.code_root))
191
192 @task
193 def update_reqs():
194     # this should generally work to install reqs too, save for a pip problem
195     # with a few packages.
196     virtenv_exec('pip install --upgrade -r {0}/reqs/prod.txt'.format(env.code_root))
197
198 ####### Pull new code
199 @task
200 def update_code():
201     virtenv_exec('cd {0}; git pull'.format(env.code_root))
202
203 def backup():
204     """
205     Create backup using bup
206     """
207     pass
208
209 @task
210 def file_setup():
211     """
212     Deploy expected files and directories from non-apt system services.
213     """
214     ini_parser = ConfigParser.SafeConfigParser()
215     if not ini_parser.read(env.supervisor_conf):
216       raise Exception("Could not parse INI file {0}".format(env.supervisor_conf))
217     for section, option in (('supervisord','logfile'),
218                             ('supervisord','pidfile'),
219                             ('unix_http_server','file'),
220                             ('program:celeryd','stdout_logfile')):
221       filepath = ini_parser.get(section, option)
222       # generate file's directory structure if needed
223       run('mkdir -p {0}'.format(os.path.split(filepath)[0]))
224       # touch a file and change ownership if needed
225       if 'log' in option and not files.exists(filepath):
226           sudo('touch {0}'.format(filepath))
227           sudo('chown {0}:{1} {2}'.format(env.local_user, env.group, filepath))
228
229 @task
230 def check_secrets():
231     """
232     Ensure secret files exist for syncdb to run.
233     """
234
235     secrets_path = env.code_root + '/karmaworld/secret'
236     secrets_files = ('filepicker.py', 'static_s3.py', 'db_settings.py', 'drive.py', 'client_secrets.json', 'drive.p12')
237
238     errors = []
239     for sfile in secrets_files:
240         ffile = os.path.sep.join((secrets_path,sfile))
241         if not files.exists(ffile):
242             errors.append('{0} missing. Please add and try again.'.format(ffile))
243     if errors:
244         raise Exception('\n'.join(errors))
245
246 @task
247 def fetch_usde():
248     """
249     Download USDE accreditation school CSV.
250     """
251     virtenv_exec('{0}/manage.py fetch_usde_csv {1}'.format(env.code_root, env.usde_csv))
252
253 @task
254 def import_usde():
255     """
256     Import accreditation school CSV into the database and scrub it.
257     """
258     virtenv_exec('{0}/manage.py import_usde_csv {1}'.format(env.code_root, env.usde_csv))
259     virtenv_exec('{0}/manage.py sanitize_usde_schools'.format(env.code_root))
260
261 @task
262 def first_deploy():
263     """
264     Sets up and deploys the project for the first time.
265     """
266     link_code()
267     make_virtualenv()
268     file_setup()
269     check_secrets()
270     install_reqs()
271     syncdb()
272     collect_static()
273     fetch_usde()
274     import_usde()
275     start_supervisord()
276     print "You should run `manage.py createsuperuser` in the virtual environment"
277
278
279 @task
280 def deploy():
281     """
282     Deploys the latest changes
283     """
284     update_code()
285     update_reqs()
286     syncdb()
287     collect_static()
288     restart_supervisord()
289 ########## END COMMANDS