Merge lp:~ricardokirkner/locolander/requirements into lp:locolander

Proposed by Ricardo Kirkner
Status: Merged
Approved by: Ricardo Kirkner
Approved revision: 15
Merged at revision: 10
Proposed branch: lp:~ricardokirkner/locolander/requirements
Merge into: lp:locolander
Diff against target: 507 lines (+210/-80)
6 files modified
.locolander.yml (+26/-0)
fabfile.py (+26/-0)
ns2df.py (+69/-31)
requirements.txt (+16/-2)
run_tests.sh (+1/-1)
tests/test_ns2df.py (+72/-46)
To merge this branch: bzr merge lp:~ricardokirkner/locolander/requirements
Reviewer Review Type Date Requested Status
Facundo Batista Approve
Review via email: mp+172214@code.launchpad.net

Commit message

pre-requisites to make locolander able to land itself

- updated requirements
- updated locolander config
- updated ns2df.py script to create Dockerfile from locolander config

To post a comment you must log in.
12. By Ricardo Kirkner

fixed test script path

13. By Ricardo Kirkner

improvements to ns2df.py

- allow using a requirements file
- use locolander base images
- install dependencies using a single command for improved caching
- allow ns2df.py to be called from the commandline to create a Dockerfile
- build Dockerfile so that it includes the command to run tests
- build Dockerfile so that it times out the test run

14. By Ricardo Kirkner

include ns2df tests in test script

15. By Ricardo Kirkner

added fabfile for bootstrapping and running tests

Revision history for this message
Facundo Batista (facundo) wrote :

I see you're changing back to put all apt-get installations in the same line... you told me that we should put them all in different lines because of docker image caching... did that change?

The rest looks ok!

review: Needs Information
Revision history for this message
Ricardo Kirkner (ricardokirkner-f) wrote :

I did tell you that, but I have changed my mind. The reason being that
while each new command gives us a different cached state (which was the
original reason for having it one per line), I now think that the overload
of having to invoke pip for each dependency is just too high. Also having a
single line for all dependencies should make rebuilding the image from
cache much faster than having to iterate over each intermediate state (plus
avoiding unnecessary wasted disk space by not-really reusable containers).

Hope this makes sense.

On Wed, Jul 10, 2013 at 8:15 PM, Facundo Batista <email address hidden>wrote:

> Review: Needs Information
>
> I see you're changing back to put all apt-get installations in the same
> line... you told me that we should put them all in different lines because
> of docker image caching... did that change?
>
> The rest looks ok!
> --
>
> https://code.launchpad.net/~ricardokirkner/locolander/requirements/+merge/172214
> Your team LocoLanderos is subscribed to branch lp:locolander.
>

Revision history for this message
Facundo Batista (facundo) wrote :

Good enough!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.locolander.yml'
2--- .locolander.yml 1970-01-01 00:00:00 +0000
3+++ .locolander.yml 2013-07-09 18:45:42 +0000
4@@ -0,0 +1,26 @@
5+precise:
6+ pip:
7+ distribute: 0.6.34
8+ Django: 1.5.1
9+ github3.py: 0.7.0
10+ httplib2: 0.8.0
11+ keyring: 1.5.0
12+ launchpadlib: 1.10.2
13+ lazr.authentication: 0.1.2
14+ lazr.restfulclient: 0.13.3
15+ lazr.uri: 1.0.3
16+ mock: 1.0.1
17+ oauth: 1.0.1
18+ pep8: 1.4.5
19+ pyflakes: 0.7.2
20+ PyYAML: 3.10.0
21+ requests: 1.2.3
22+ simplejson: 3.3.0
23+ South: 0.8.1
24+ testresources: 0.2.7
25+ wadllib: 1.3.2
26+ wsgi-intercept: 0.5.1
27+ zope.interface: 4.0.5
28+
29+metadata:
30+ test_script: ./run_tests.sh
31
32=== added file 'fabfile.py'
33--- fabfile.py 1970-01-01 00:00:00 +0000
34+++ fabfile.py 2013-07-09 18:45:42 +0000
35@@ -0,0 +1,26 @@
36+from fabric.api import local
37+from fabric.context_managers import prefix
38+
39+
40+def virtualenv(func):
41+ def wrapped(*args):
42+ with prefix('. .env/bin/activate'):
43+ return func(*args)
44+ return wrapped
45+
46+
47+def bootstrap():
48+ local('virtualenv -p /usr/bin/python2 .env')
49+ local('.env/bin/pip install -r requirements.txt')
50+
51+
52+@virtualenv
53+def manage(*args):
54+ cmd = ['python locolander/manage.py']
55+ cmd.extend(args)
56+ local(' '.join(cmd))
57+
58+
59+@virtualenv
60+def test():
61+ local('./run_tests.sh')
62
63=== modified file 'ns2df.py'
64--- ns2df.py 2013-06-22 23:29:05 +0000
65+++ ns2df.py 2013-07-09 18:45:42 +0000
66@@ -14,7 +14,7 @@
67
68 will be converted to
69
70- from ubuntu:precise
71+ from locolander:precise
72 run apt-get -q -y install apache2=3.3-4
73 run apt-get -q -y install bzr
74 run pip install --download=/tmp/pipcache --no-install foobar
75@@ -24,14 +24,30 @@
76 and also the system will use "./test" as script
77 """
78
79+import os.path
80+
81 import yaml
82
83 # the conversion between our nice base names and those that are
84 # needed by docker
85 BASE_TRANSLATIONS = {
86- 'precise': 'ubuntu:precise',
87+ 'precise': 'locolander:precise',
88 }
89
90+
91+def _get_pip_packages(items):
92+ if isinstance(items, basestring):
93+ return items
94+ packages = []
95+ for name, ver in sorted(items.items()):
96+ if ver is None:
97+ p = name
98+ else:
99+ p = "%s==%s" % (name, ver)
100+ packages.append(p)
101+ return ' '.join(packages)
102+
103+
104 def _get_base(config, **params):
105 """Process the system base stuff."""
106 # support only one base for now
107@@ -41,6 +57,13 @@
108 return ["from " + BASE_TRANSLATIONS[base]]
109
110
111+def _get_code(config, **params):
112+ return ['run mkdir -p {target_path}'.format(**params),
113+ 'add . {target_path}'.format(**params),
114+ 'run chown -R locolander.locolander {target_path}'.format(
115+ **params)]
116+
117+
118 def _get_depends_prev(config, **params):
119 """All the dependencies before securing machine."""
120 # support only one base for now
121@@ -50,26 +73,26 @@
122 # apt
123 items = config.get('apt', [])
124 if items:
125+ packages = []
126 for name, ver in sorted(items.items()):
127 if ver is None:
128 p = name
129 else:
130 p = "%s=%s" % (name, ver)
131- dep = "run apt-get -q -y install " + p
132- dependencies.append(dep)
133+ packages.append(p)
134+ params['packages'] = ' '.join(packages)
135+ dependencies.append('run apt-get -q -y install {packages}'.format(**params))
136
137 # pip
138 items = config.get('pip', [])
139- pip_cache_dir = params["pip_cache_dir"]
140 if items:
141- for name, ver in sorted(items.items()):
142- if ver is None:
143- p = name
144- else:
145- p = "%s==%s" % (name, ver)
146- dep = "run pip install --download=%s --no-install %s" % (
147- pip_cache_dir, p)
148- dependencies.append(dep)
149+ packages = _get_pip_packages(items)
150+ pip_cache_dir = params['pip_cache_dir']
151+ dependencies.append(
152+ 'run cd {target_path} && pip install --download={pip_cache_dir} '
153+ '--no-install {packages}'.format(
154+ pip_cache_dir=pip_cache_dir, packages=packages,
155+ target_path=params['target_path']))
156
157 return dependencies
158
159@@ -85,33 +108,39 @@
160
161 # pip
162 items = config.get('pip', [])
163- pip_cache_dir = params["pip_cache_dir"]
164 if items:
165- for name, ver in sorted(items.items()):
166- if ver is None:
167- p = name
168- else:
169- p = "%s==%s" % (name, ver)
170- dep = "run pip install --find-links=file://%s --no-index %s" % (
171- pip_cache_dir, p)
172- dependencies.append(dep)
173+ packages = _get_pip_packages(items)
174+ pip_cache_dir = params['pip_cache_dir']
175+ dependencies.append(
176+ 'run cd {target_path} && pip install '
177+ '--find-links=file://{pip_cache_dir} --no-index '
178+ '{packages}'.format(pip_cache_dir=pip_cache_dir,
179+ packages=packages,
180+ target_path=params['target_path']))
181
182 return dependencies
183
184
185 def _get_rest(config, **params):
186 """The final stuff."""
187- return []
188-
189-
190-def parse(config_text, pip_cache_dir):
191+ return ['cmd cd {target_path} && timeout 300 {test_cmd}'.format(**params)]
192+
193+
194+def parse(config_text, pip_cache_dir, project):
195 """Convert Nessita Syntax and return the text for Docker."""
196 config = yaml.load(config_text)
197 metadata = config.pop("metadata")
198
199+ params = {
200+ 'test_cmd': metadata['test_script'],
201+ 'target_path': os.path.join('/srv/locolander', project),
202+ 'pip_cache_dir': pip_cache_dir,
203+ }
204+
205 # the order for calling these functions is VERY important
206 functions = [
207 _get_base,
208+ _get_code,
209 _get_depends_prev,
210 _get_secure,
211 _get_depends_post,
212@@ -119,11 +148,20 @@
213 ]
214 data = []
215 for func in functions:
216- items = func(config, pip_cache_dir=pip_cache_dir)
217+ items = func(config, **params)
218 data.extend(items)
219 docker = "\n".join(data)
220
221- # get stuff from the metadata
222- script = metadata["test_script"]
223-
224- return script, docker
225+ return docker
226+
227+
228+if __name__ == '__main__':
229+ import sys
230+
231+ project = sys.argv[1]
232+ infile, outfile = sys.argv[2:]
233+
234+ config = open(infile, 'r').read()
235+ data = parse(config, '/var/cache/locolander', project)
236+ with open(outfile, 'w') as dockerfile:
237+ dockerfile.write(data + '\n')
238
239=== modified file 'requirements.txt'
240--- requirements.txt 2013-06-22 19:53:22 +0000
241+++ requirements.txt 2013-07-09 18:45:42 +0000
242@@ -1,7 +1,21 @@
243+distribute==0.6.34
244 Django==1.5.1
245+github3.py==0.7.0
246+httplib2==0.8
247+keyring==1.5
248+launchpadlib==1.10.2
249+lazr.authentication==0.1.2
250+lazr.restfulclient==0.13.3
251+lazr.uri==1.0.3
252 mock==1.0.1
253+oauth==1.0.1
254 pep8==1.4.5
255 pyflakes==0.7.2
256+PyYAML==3.10
257+requests==1.2.3
258+simplejson==3.3.0
259 South==0.8.1
260-github3.py==0.7.0
261-launchpadlib==1.10.2
262+testresources==0.2.7
263+wadllib==1.3.2
264+wsgi-intercept==0.5.1
265+zope.interface==4.0.5
266
267=== modified file 'run_tests.sh'
268--- run_tests.sh 2013-06-22 20:51:04 +0000
269+++ run_tests.sh 2013-07-09 18:45:42 +0000
270@@ -1,7 +1,7 @@
271 #! /bin/bash
272
273 set -e
274-set -x
275
276 python locolander/manage.py test locolanderweb
277+python -m unittest discover
278 PYTHONPATH=locolander python -m unittest discover -s locolander/repos/
279
280=== modified file 'tests/test_ns2df.py'
281--- tests/test_ns2df.py 2013-06-22 23:29:05 +0000
282+++ tests/test_ns2df.py 2013-07-09 18:45:42 +0000
283@@ -7,96 +7,120 @@
284 class DependenciesTestCase(unittest.TestCase):
285 """Tests for the dependencies transformer."""
286
287+ def setUp(self):
288+ super(DependenciesTestCase, self).setUp()
289+ self.params = {
290+ 'pip_cache_dir': '/tmp/pipcache',
291+ 'target_path': '/srv/locolander/project',
292+ }
293+
294 def test_no_dependencies(self):
295 config = dict(somebase={})
296- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
297+ res = ns2df._get_depends_prev(config, **self.params)
298 self.assertEqual(res, [])
299- res = ns2df._get_depends_post(config, pip_cache_dir="/tmp/pipcache")
300+ res = ns2df._get_depends_post(config, **self.params)
301 self.assertEqual(res, [])
302
303 def test_apt_one(self):
304 config = dict(somebase={
305 'apt': {'bzr': None}
306 })
307- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
308+ res = ns2df._get_depends_prev(config, **self.params)
309 self.assertEqual(res, ["run apt-get -q -y install bzr"])
310
311 def test_apt_several(self):
312 config = dict(somebase={
313 'apt': {'bzr': None, 'apache2': None}
314 })
315- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
316+ res = ns2df._get_depends_prev(config, **self.params)
317 self.assertEqual(res, [
318- "run apt-get -q -y install apache2",
319- "run apt-get -q -y install bzr",
320+ "run apt-get -q -y install apache2 bzr",
321 ])
322
323 def test_apt_versions(self):
324 config = dict(somebase={
325 'apt': {'bzr': None, 'apache2': '3.3-4'}
326 })
327- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
328+ res = ns2df._get_depends_prev(config, **self.params)
329 self.assertEqual(res, [
330- "run apt-get -q -y install apache2=3.3-4",
331- "run apt-get -q -y install bzr",
332+ "run apt-get -q -y install apache2=3.3-4 bzr",
333 ])
334
335 def test_pip_prev_one(self):
336 config = dict(somebase={
337 'pip': {'foo': None}
338 })
339- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
340+ res = ns2df._get_depends_prev(config, **self.params)
341 self.assertEqual(res, [
342- "run pip install --download=/tmp/pipcache --no-install foo",
343+ "run cd /srv/locolander/project && pip install --download=/tmp/pipcache --no-install foo",
344 ])
345
346 def test_pip_prev_several(self):
347 config = dict(somebase={
348 'pip': {'foo': None, 'bar': None}
349 })
350- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
351+ res = ns2df._get_depends_prev(config, **self.params)
352 self.assertEqual(res, [
353- "run pip install --download=/tmp/pipcache --no-install bar",
354- "run pip install --download=/tmp/pipcache --no-install foo",
355+ "run cd /srv/locolander/project && pip install --download=/tmp/pipcache --no-install bar foo",
356 ])
357
358 def test_pip_prev_versions(self):
359 config = dict(somebase={
360 'pip': {'foo': None, 'bar': '2.1'}
361 })
362- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
363- self.assertEqual(res, [
364- "run pip install --download=/tmp/pipcache --no-install bar==2.1",
365- "run pip install --download=/tmp/pipcache --no-install foo",
366+ res = ns2df._get_depends_prev(config, **self.params)
367+ self.assertEqual(res, [
368+ ("run cd /srv/locolander/project && pip install --download=/tmp/pipcache --no-install "
369+ "bar==2.1 foo"),
370+ ])
371+
372+ def test_pip_prev_requirements(self):
373+ config = dict(somebase={
374+ 'pip': '-r requirements.txt'
375+ })
376+ res = ns2df._get_depends_prev(config, **self.params)
377+ self.assertEqual(res, [
378+ ("run cd /srv/locolander/project && pip install --download=/tmp/pipcache --no-install "
379+ "-r requirements.txt"),
380 ])
381
382 def test_pip_post_one(self):
383 config = dict(somebase={
384 'pip': {'foo': None}
385 })
386- res = ns2df._get_depends_post(config, pip_cache_dir="/tmp/pipcache")
387+ res = ns2df._get_depends_post(config, **self.params)
388 self.assertEqual(res, [
389- "run pip install --find-links=file:///tmp/pipcache --no-index foo"
390+ "run cd /srv/locolander/project && pip install --find-links=file:///tmp/pipcache --no-index foo"
391 ])
392
393 def test_pip_post_several(self):
394 config = dict(somebase={
395 'pip': {'foo': None, 'bar': None}
396 })
397- res = ns2df._get_depends_post(config, pip_cache_dir="/tmp/pipcache")
398+ res = ns2df._get_depends_post(config, **self.params)
399 self.assertEqual(res, [
400- "run pip install --find-links=file:///tmp/pipcache --no-index bar",
401- "run pip install --find-links=file:///tmp/pipcache --no-index foo",
402+ ("run cd /srv/locolander/project && pip install --find-links=file:///tmp/pipcache --no-index "
403+ "bar foo"),
404 ])
405
406 def test_pip_post_versions(self):
407 config = dict(somebase={
408 'pip': {'foo': None, 'bar': '2.1'}
409 })
410- res = ns2df._get_depends_post(config, pip_cache_dir="/tmcache")
411- self.assertEqual(res, [
412- "run pip install --find-links=file:///tmcache --no-index bar==2.1",
413- "run pip install --find-links=file:///tmcache --no-index foo",
414+ res = ns2df._get_depends_post(config, **self.params)
415+ self.assertEqual(res, [
416+ ("run cd /srv/locolander/project && pip install --find-links=file:///tmp/pipcache --no-index "
417+ "bar==2.1 foo"),
418+ ])
419+
420+ def test_pip_post_requirements(self):
421+ config = dict(somebase={
422+ 'pip': '-r requirements.txt'
423+ })
424+ res = ns2df._get_depends_post(config, **self.params)
425+ self.assertEqual(res, [
426+ ("run cd /srv/locolander/project && pip install --find-links=file:///tmp/pipcache --no-index "
427+ "-r requirements.txt"),
428 ])
429
430 def test_mixed_prev(self):
431@@ -104,12 +128,10 @@
432 'apt': {'bzr': None, 'apache2': '3.3-4'},
433 'pip': {'foo': None, 'bar': None},
434 })
435- res = ns2df._get_depends_prev(config, pip_cache_dir="/tmp/pipcache")
436+ res = ns2df._get_depends_prev(config, **self.params)
437 self.assertEqual(res, [
438- "run apt-get -q -y install apache2=3.3-4",
439- "run apt-get -q -y install bzr",
440- "run pip install --download=/tmp/pipcache --no-install bar",
441- "run pip install --download=/tmp/pipcache --no-install foo",
442+ "run apt-get -q -y install apache2=3.3-4 bzr",
443+ "run cd /srv/locolander/project && pip install --download=/tmp/pipcache --no-install bar foo",
444 ])
445
446 def test_mixed_post(self):
447@@ -117,10 +139,10 @@
448 'apt': {'bzr': None, 'apache2': '3.3-4'},
449 'pip': {'foo': None, 'bar': None},
450 })
451- res = ns2df._get_depends_post(config, pip_cache_dir="/tmp/pipcache")
452+ res = ns2df._get_depends_post(config, **self.params)
453 self.assertEqual(res, [
454- "run pip install --find-links=file:///tmp/pipcache --no-index bar",
455- "run pip install --find-links=file:///tmp/pipcache --no-index foo",
456+ ("run cd /srv/locolander/project && pip install --find-links=file:///tmp/pipcache --no-index "
457+ "bar foo"),
458 ])
459
460
461@@ -130,7 +152,7 @@
462 def test_one_base(self):
463 config = dict(precise={})
464 res = ns2df._get_base(config)
465- self.assertEqual(res, ["from ubuntu:precise"])
466+ self.assertEqual(res, ["from locolander:precise"])
467
468 def test_several_bases(self):
469 config = dict(base1={}, base2={})
470@@ -141,8 +163,10 @@
471 self.assertEqual(res, ["run ip link set dev eth0 down"])
472
473 def test_rest(self):
474- res = ns2df._get_rest({})
475- self.assertEqual(res, [])
476+ res = ns2df._get_rest({},
477+ target_path='/srv/locolander',
478+ test_cmd='./run_tests.sh')
479+ self.assertEqual(res, ["cmd cd /srv/locolander && timeout 300 ./run_tests.sh"])
480
481 def test_all_mixed(self):
482 config_text = """
483@@ -155,14 +179,16 @@
484 metadata:
485 test_script: foo
486 """
487- script, docker = ns2df.parse(config_text, "/tmp/pipcache")
488- self.assertEqual(script, "foo")
489+ docker = ns2df.parse(config_text, "/tmp/pipcache", 'project')
490 self.assertEqual(docker,
491- "from ubuntu:precise\n"
492- "run apt-get -q -y install apache2=3.3-4\n"
493- "run apt-get -q -y install bzr\n"
494- "run pip install --download=/tmp/pipcache --no-install foobar\n"
495+ "from locolander:precise\n"
496+ "run mkdir -p /srv/locolander/project\n"
497+ "add . /srv/locolander/project\n"
498+ "run chown -R locolander.locolander /srv/locolander/project\n"
499+ "run apt-get -q -y install apache2=3.3-4 bzr\n"
500+ "run cd /srv/locolander/project && pip install --download=/tmp/pipcache --no-install foobar\n"
501 "run ip link set dev eth0 down\n"
502- "run pip install --find-links=file:///tmp/pipcache "
503- "--no-index foobar"
504+ "run cd /srv/locolander/project && pip install --find-links=file:///tmp/pipcache "
505+ "--no-index foobar\n"
506+ "cmd cd /srv/locolander/project && timeout 300 foo"
507 )

Subscribers

People subscribed via source and target branches

to all changes: