Merge lp:~ricardokirkner/locolander/requirements into lp:locolander
- requirements
- Merge into trunk
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 |
Related bugs: |
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
Description of the change
- 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
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:/
> Your team LocoLanderos is subscribed to branch lp:locolander.
>
Preview Diff
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 | ) |
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!