Merge lp:~ricardokirkner/locolander/fix-deploy into lp:locolander

Proposed by Ricardo Kirkner
Status: Superseded
Proposed branch: lp:~ricardokirkner/locolander/fix-deploy
Merge into: lp:locolander
Diff against target: 548 lines (+137/-126) (has conflicts)
12 files modified
.locolander.yml (+7/-3)
config/supervisord.conf (+0/-37)
docker/Dockerfile (+5/-4)
docker/scripts/locolander (+9/-6)
fabfile.py (+8/-22)
locolander/locolander/settings.py (+3/-2)
locolander/locolanderweb/models.py (+9/-0)
locolander/locolanderweb/tasks.py (+15/-17)
locolander/locolanderweb/tests/test_models.py (+15/-1)
locolander/locolanderweb/tests/test_tasks.py (+28/-31)
locolander/supervisord.conf (+30/-0)
requirements.txt (+8/-3)
Text conflict in locolander/locolanderweb/tasks.py
To merge this branch: bzr merge lp:~ricardokirkner/locolander/fix-deploy
Reviewer Review Type Date Requested Status
LocoLanderos Pending
Review via email: mp+180668@code.launchpad.net
To post a comment you must log in.

Unmerged revisions

37. By Ricardo Kirkner

fixes to supervisor config

36. By Ricardo Kirkner

use proper path to locolander script

35. By Ricardo Kirkner

avoid bzr diff aborting locolander script

34. By Ricardo Kirkner

better output from script

33. By Ricardo Kirkner

replace subprocess with envoy

use envoy instead of subprocess, because the latter fails to run properly
when celeryd is started under supervisor.

32. By Ricardo Kirkner

customized model repr strings

31. By Ricardo Kirkner

use absolute path for database file

30. By Ricardo Kirkner

better output handling

29. By Ricardo Kirkner

disable autoreload

28. By Ricardo Kirkner

added envoy and django-supervisor as dependencies

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.locolander.yml'
2--- .locolander.yml 2013-07-23 23:55:56 +0000
3+++ .locolander.yml 2013-08-16 23:21:52 +0000
4@@ -5,9 +5,6 @@
5 libxml2-dev:
6 libxslt1-dev:
7 pip:
8- Django: 1.5.1
9- PyYAML: 3.10.0
10- South: 0.8.1
11 amqp: 1.0.12
12 anyjson: 0.3.3
13 argparse: 1.2.1
14@@ -16,8 +13,11 @@
15 celery: 3.0.21
16 cssselect: 0.8
17 distribute: 0.6.34
18+ Django: 1.5.1
19 django-allauth: 0.12.0
20 django-celery: 3.0.17
21+ django-supervisor: 0.3.1
22+ envoy: 0.0.2
23 flower: 0.5.2
24 github3.py: 0.7.0
25 gunicorn: 17.5
26@@ -33,6 +33,7 @@
27 mock: 1.0.1
28 oauth: 1.0.1
29 oauthlib: 0.5.0
30+ pathtools: 0.1.2
31 pep8: 1.4.6
32 psycopg2: 2.5.1
33 pyflakes: 0.7.3
34@@ -40,15 +41,18 @@
35 python-dateutil: 2.1
36 python-openid: 2.2.5
37 pytz: 2013b
38+ PyYAML: 3.10.0
39 redis: 2.7.6
40 requests: 1.2.3
41 requests-oauthlib: 0.3.2
42 simplejson: 3.3.0
43 six: 1.3.0
44+ South: 0.8.1
45 supervisor: 3.0b2
46 testresources: 0.2.7
47 tornado: 3.1
48 wadllib: 1.3.2
49+ watchdog: 0.6.0
50 wsgi-intercept: 0.5.1
51 wsgiref: 0.1.2
52 zope.interface: 4.0.5
53
54=== removed file 'config/supervisord.conf'
55--- config/supervisord.conf 2013-07-13 17:01:27 +0000
56+++ config/supervisord.conf 1970-01-01 00:00:00 +0000
57@@ -1,37 +0,0 @@
58-; Sample supervisor config file.
59-
60-[supervisord]
61-nodaemon = true
62-loglevel = info
63-logfile = supervisord.log
64-
65-
66-; service configuration sections
67-
68-[program:gunicorn]
69-command = gunicorn_django locolander/locolander/settings.py
70-autostart = true
71-autorestart = true
72-stdout_logfile = supervisord.log
73-redirect_stderr = true
74-
75-[program:redis]
76-command = redis-server
77-autostart = true
78-autorestart = true
79-stdout_logfile = supervisord.log
80-redirect_stderr = true
81-
82-[program:celeryd]
83-command = python locolander/manage.py celeryd
84-autostart = true
85-autorestart = true
86-stdout_logfile = supervisord.log
87-redirect_stderr = true
88-
89-[program:flower]
90-command = python locolander/manage.py celery flower
91-autostart = true
92-autorestart = true
93-stdout_logfile = supervisord.log
94-redirect_stderr = true
95
96=== modified file 'docker/Dockerfile'
97--- docker/Dockerfile 2013-06-21 21:48:36 +0000
98+++ docker/Dockerfile 2013-08-16 23:21:52 +0000
99@@ -24,10 +24,6 @@
100 # add locolander user
101 run adduser locolander
102
103-# install main locolander script
104-add ./scripts/run_tests.sh /usr/local/bin/run_tests.sh
105-run chown locolander.locolander /usr/local/bin/run_tests.sh
106-
107 # configure ssh
108 run mkdir /home/locolander/.ssh
109 add ./files/ssh/id_rsa /home/locolander/.ssh/id_rsa
110@@ -40,3 +36,8 @@
111
112 # ensure proper ownership
113 run chown -R locolander.locolander /home/locolander/
114+
115+# install main locolander script
116+add ./scripts/locolander /usr/local/bin/locolander
117+run chown locolander.locolander /usr/local/bin/locolander
118+
119
120=== modified file 'docker/scripts/locolander'
121--- docker/scripts/locolander 2013-07-27 00:04:40 +0000
122+++ docker/scripts/locolander 2013-08-16 23:21:52 +0000
123@@ -33,6 +33,9 @@
124 cd target
125 OUTPUT=$(bzr merge $SOURCE 2>&1)
126
127+# ignore any errors here as bzr diff will exit with code 1
128+# if the diff is not empty
129+set +e
130 # figure out if there are any changes
131 DIFF=$(bzr diff)
132 CONFLICTS=$(bzr conflicts)
133@@ -47,6 +50,7 @@
134 echo "SUCCESS - Nothing to do."
135 exit 0
136 fi
137+set -e
138
139 # 3. build image using config file
140 echo "Building project image..."
141@@ -56,8 +60,8 @@
142 OUTPUT=$(python ns2df.py $PROJECT .locolander.yml Dockerfile 2>&1)
143 OUTPUT=$(docker build -t $PROJECT . 2>&1)
144 if [ -z $? -o "`echo $OUTPUT | grep 'Error'`" != "" ]; then
145- echo "FAILURE - Build failed:"
146 echo $OUTPUT
147+ echo "FAILURE - Build failed"
148 exit 1
149 fi
150
151@@ -67,9 +71,8 @@
152
153 # 4. run test script using image
154 echo "Running tests ..."
155-docker run $PROJECT
156-
157-if [ -z $? -o "`echo $OUTPUT | grep 'Error'`" != "" ]; then
158+docker run $PROJECT 2>&1
159+if [ -z $? ]; then
160 echo "FAILURE - Tests failed"
161 exit 1
162 fi
163@@ -87,7 +90,7 @@
164 echo "Pushing merge ..."
165 OUTPUT=$(bzr push $TARGET 2>&1)
166
167-echo "SUCCESS"
168-
169 # cleanup
170 rm -rf $TMPFOLDER
171+
172+echo "SUCCESS"
173
174=== modified file 'fabfile.py'
175--- fabfile.py 2013-07-17 13:33:49 +0000
176+++ fabfile.py 2013-08-16 23:21:52 +0000
177@@ -23,30 +23,16 @@
178
179
180 @virtualenv
181+def run():
182+ manage('supervisor -d')
183+
184+
185+@virtualenv
186 def test():
187 local('./run_tests.sh')
188
189
190 @virtualenv
191-def start_redis():
192- local('redis-server')
193-
194-
195-@virtualenv
196-def start_gunicorn():
197- local('gunicorn_django locolander/locolander/settings.py')
198-
199-
200-@virtualenv
201-def start_celery():
202- manage('celeryd')
203-
204-
205-@virtualenv
206-def start_flower():
207- manage('celery flower')
208-
209-
210-@virtualenv
211-def start_supervisor():
212- local('supervisord -c config/supervisord.conf -n')
213+def docker_build():
214+ local('python ns2df.py locolander .locolander.yml Dockerfile')
215+ local('docker build -t locolander .')
216
217=== modified file 'locolander/locolander/settings.py'
218--- locolander/locolander/settings.py 2013-07-21 03:05:23 +0000
219+++ locolander/locolander/settings.py 2013-08-16 23:21:52 +0000
220@@ -16,7 +16,7 @@
221 DATABASES = {
222 'default': {
223 'ENGINE': 'django.db.backends.sqlite3',
224- 'NAME': 'locolander.db',
225+ 'NAME': os.path.abspath(os.path.join(os.curdir, 'locolander.db')),
226 # The following settings are not used with sqlite3:
227 'USER': '',
228 'PASSWORD': '',
229@@ -126,9 +126,10 @@
230 'django.contrib.messages',
231 'django.contrib.staticfiles',
232 'django.contrib.admin',
233- 'djcelery',
234 'locolanderweb',
235 'south',
236+ 'djcelery',
237+ 'djsupervisor',
238 'allauth',
239 'allauth.account',
240 'allauth.socialaccount',
241
242=== modified file 'locolander/locolanderweb/models.py'
243--- locolander/locolanderweb/models.py 2013-07-13 18:44:18 +0000
244+++ locolander/locolanderweb/models.py 2013-08-16 23:21:52 +0000
245@@ -33,6 +33,9 @@
246 name = models.TextField()
247 url = models.CharField(max_length=256, validators=[service_url_validator])
248
249+ def __repr__(self):
250+ return u"<Project: %s>" % self.name
251+
252 @property
253 def service(self):
254 return get_service_from_url(self.url)
255@@ -56,6 +59,9 @@
256 date_created = models.DateTimeField()
257 date_completed = models.DateTimeField(null=True, blank=True)
258
259+ def __repr__(self):
260+ return u"<MergeRequest: %s -> %s>" % (self.source, self.target)
261+
262 def complete(self, error=False):
263 self.date_completed = datetime.utcnow()
264 self.status = STATUS_MERGED if not error else STATUS_ERROR
265@@ -71,3 +77,6 @@
266
267 class Meta:
268 get_latest_by = 'timestamp'
269+
270+ def __repr__(self):
271+ return u"<RunLog: %r at %s>" % (self.merge_request, self.timestamp)
272
273=== modified file 'locolander/locolanderweb/tasks.py'
274--- locolander/locolanderweb/tasks.py 2013-08-03 17:06:41 +0000
275+++ locolander/locolanderweb/tasks.py 2013-08-16 23:21:52 +0000
276@@ -1,7 +1,11 @@
277 import os
278+<<<<<<< TREE
279 import subprocess
280 from datetime import datetime
281+=======
282+>>>>>>> MERGE-SOURCE
283
284+import envoy
285 from celery import task
286
287 from locolanderweb.models import MergeRequest, RunLog
288@@ -21,23 +25,17 @@
289 author = request.author
290 message = request.commit_message
291
292- cmd = [
293- os.path.realpath(os.path.join(
294- os.path.abspath('.'), 'docker/scripts/locolander')),
295- project.name,
296- source,
297- target,
298- message,
299- author,
300- ]
301- try:
302- output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
303- return_code = 0
304- error = False
305- except subprocess.CalledProcessError as err:
306- output = err.output
307- return_code = err.returncode
308- error = True
309+ cwd = os.path.dirname(os.path.abspath(__file__))
310+ script = os.path.abspath(os.path.join(cwd, '..', '..',
311+ 'docker', 'scripts', 'locolander'))
312+ cmd = '{script} {project} {source} {target} "{message}" "{author}"'.format(
313+ script=script, project=project.name, source=source, target=target,
314+ message=message, author=author)
315+
316+ r = envoy.run(cmd)
317+ output = r.std_out
318+ return_code = r.status_code
319+ error = return_code != 0
320
321 RunLog.objects.create(merge_request=request, raw=output,
322 return_code=return_code)
323
324=== modified file 'locolander/locolanderweb/tests/test_models.py'
325--- locolander/locolanderweb/tests/test_models.py 2013-07-20 21:10:37 +0000
326+++ locolander/locolanderweb/tests/test_models.py 2013-08-16 23:21:52 +0000
327@@ -96,6 +96,10 @@
328 result = self.obj.approved_requests()
329 self.assertEqual(len(result), 1)
330
331+ def test_repr(self):
332+ expected = u'<Project: foo>'
333+ self.assertEqual(repr(self.obj), expected)
334+
335
336 class MergeRequestTestCase(BaseTestCase):
337
338@@ -156,14 +160,20 @@
339 self.assertEqual(self.obj.status, STATUS_ERROR)
340 self.assertEqual(self.obj.date_completed, completed)
341
342+ def test_repr(self):
343+ expected = u'<MergeRequest: source -> target>'
344+ self.assertEqual(repr(self.obj), expected)
345+
346
347 class RunLogTestCase(BaseTestCase):
348
349 def setUp(self):
350 super(RunLogTestCase, self).setUp()
351 self.merge_request = self.factory.make_merge_request()
352+ self.timestamp = datetime.utcnow()
353 self.obj = RunLog.objects.create(
354- merge_request=self.merge_request, return_code=0)
355+ merge_request=self.merge_request, return_code=0,
356+ timestamp=self.timestamp)
357
358 def test_merge_request(self):
359 self.assertEqual(self.obj.merge_request, self.merge_request)
360@@ -187,3 +197,7 @@
361 merge_request=self.merge_request, return_code=0,
362 timestamp=self.obj.timestamp + timedelta(seconds=1))
363 self.assertEqual(RunLog.objects.latest(), other)
364+
365+ def test_repr(self):
366+ expected = u"<RunLog: %r at %s>" % (self.merge_request, self.timestamp)
367+ self.assertEqual(repr(self.obj), expected)
368
369=== modified file 'locolander/locolanderweb/tests/test_tasks.py'
370--- locolander/locolanderweb/tests/test_tasks.py 2013-07-20 21:07:18 +0000
371+++ locolander/locolanderweb/tests/test_tasks.py 2013-08-16 23:21:52 +0000
372@@ -1,7 +1,7 @@
373-import subprocess
374+import os
375 from datetime import datetime
376
377-from mock import ANY, patch
378+from mock import patch
379
380 from locolanderweb.models import (
381 STATUS_ERROR,
382@@ -55,29 +55,28 @@
383
384 def setUp(self):
385 super(RunRequestTestCase, self).setUp()
386- patcher = patch('locolanderweb.tasks.subprocess.check_output')
387- self.mock_check_output = patcher.start()
388+ patcher = patch('locolanderweb.tasks.envoy.run')
389+ self.mock_run = patcher.start()
390 self.addCleanup(patcher.stop)
391
392 def test_run_request(self):
393- self.mock_check_output.return_value = 'SUCCESS'
394+ self.mock_run.return_value.std_out = 'SUCCESS'
395+ self.mock_run.return_value.status_code = 0
396
397 merge_request = self.factory.make_merge_request()
398
399 run_request(merge_request)
400
401- cmd = [
402- ANY,
403- merge_request.project.name,
404- merge_request.source,
405- merge_request.target,
406- merge_request.commit_message,
407- merge_request.author,
408- ]
409- self.mock_check_output.assert_called_once_with(
410- cmd, stderr=subprocess.STDOUT)
411- real_cmd = self.mock_check_output.call_args[0][0][0]
412- self.assertTrue(real_cmd.endswith('docker/scripts/locolander'))
413+ cmd = ('{script} {project} {source} {target} '
414+ '"{message}" "{author}"').format(
415+ script=os.path.abspath('./docker/scripts/locolander'),
416+ project=merge_request.project.name,
417+ source=merge_request.source,
418+ target=merge_request.target,
419+ message=merge_request.commit_message,
420+ author=merge_request.author
421+ )
422+ self.mock_run.assert_called_once_with(cmd)
423
424 self.assertEqual(RunLog.objects.all().count(), 1)
425 run_log = RunLog.objects.get(merge_request=merge_request)
426@@ -91,23 +90,21 @@
427 def test_run_request_with_error(self):
428 merge_request = self.factory.make_merge_request()
429
430- cmd = [
431- ANY,
432- merge_request.project.name,
433- merge_request.source,
434- merge_request.target,
435- merge_request.commit_message,
436- merge_request.author,
437- ]
438- error = subprocess.CalledProcessError(1, cmd, output='FAILURE')
439- self.mock_check_output.side_effect = error
440+ cmd = ('{script} {project} {source} {target} '
441+ '"{message}" "{author}"').format(
442+ script=os.path.abspath('./docker/scripts/locolander'),
443+ project=merge_request.project.name,
444+ source=merge_request.source,
445+ target=merge_request.target,
446+ message=merge_request.commit_message,
447+ author=merge_request.author
448+ )
449+ self.mock_run.return_value.std_out = 'FAILURE'
450+ self.mock_run.return_value.status_code = 1
451
452 run_request(merge_request)
453
454- self.mock_check_output.assert_called_once_with(
455- cmd, stderr=subprocess.STDOUT)
456- real_cmd = self.mock_check_output.call_args[0][0][0]
457- self.assertTrue(real_cmd.endswith('docker/scripts/locolander'))
458+ self.mock_run.assert_called_once_with(cmd)
459
460 self.assertEqual(RunLog.objects.all().count(), 1)
461 run_log = RunLog.objects.get(merge_request=merge_request)
462
463=== added file 'locolander/supervisord.conf'
464--- locolander/supervisord.conf 1970-01-01 00:00:00 +0000
465+++ locolander/supervisord.conf 2013-08-16 23:21:52 +0000
466@@ -0,0 +1,30 @@
467+[program:gunicorn]
468+command = gunicorn locolander.wsgi:application
469+autostart = true
470+autorestart = true
471+stdout_logfile = supervisord.log
472+redirect_stderr = true
473+
474+[program:redis]
475+command = redis-server
476+autostart = false
477+autorestart = true
478+stdout_logfile = supervisord.log
479+redirect_stderr = true
480+
481+[program:celeryd]
482+command = {{ PYTHON }} {{ PROJECT_DIR }}/manage.py celeryd
483+autostart = true
484+autorestart = true
485+stdout_logfile = supervisord.log
486+redirect_stderr = true
487+
488+[program:flower]
489+command = {{ PYTHON }} {{ PROJECT_DIR }}/manage.py celery flower
490+autostart = false
491+autorestart = true
492+stdout_logfile = supervisord.log
493+redirect_stderr = true
494+
495+[program:autoreload]
496+exclude = true
497
498=== modified file 'requirements.txt'
499--- requirements.txt 2013-07-20 23:52:03 +0000
500+++ requirements.txt 2013-08-16 23:21:52 +0000
501@@ -1,16 +1,17 @@
502-Django==1.5.1
503-PyYAML==3.10
504-South==0.8.1
505 amqp==1.0.12
506 anyjson==0.3.3
507+argh==0.23.2
508 argparse==1.2.1
509 billiard==2.7.3.31
510 bzr==2.6b2
511 celery==3.0.21
512 cssselect==0.8
513 distribute==0.6.34
514+Django==1.5.1
515 django-allauth==0.12.0
516 django-celery==3.0.17
517+django-supervisor==0.3.1
518+envoy==0.0.2
519 flower==0.5.2
520 github3.py==0.7.0
521 gunicorn==17.5
522@@ -26,6 +27,7 @@
523 mock==1.0.1
524 oauth==1.0.1
525 oauthlib==0.5.0
526+pathtools==0.1.2
527 pep8==1.4.6
528 psycopg2==2.5.1
529 pyflakes==0.7.3
530@@ -33,15 +35,18 @@
531 python-dateutil==2.1
532 python-openid==2.2.5
533 pytz==2013b
534+PyYAML==3.10
535 redis==2.7.6
536 requests==1.2.3
537 requests-oauthlib==0.3.2
538 simplejson==3.3.0
539 six==1.3.0
540+South==0.8.1
541 supervisor==3.0b2
542 testresources==0.2.7
543 tornado==3.1
544 wadllib==1.3.2
545+watchdog==0.6.0
546 wsgi-intercept==0.5.1
547 wsgiref==0.1.2
548 zope.interface==4.0.5

Subscribers

People subscribed via source and target branches

to all changes: