Merge lp:~lazypower/charms/trusty/docker/trunk into lp:charms/trusty/docker

Proposed by Charles Butler on 2015-06-02
Status: Merged
Merged at revision: 6
Proposed branch: lp:~lazypower/charms/trusty/docker/trunk
Merge into: lp:charms/trusty/docker
Diff against target: 984 lines (+623/-125)
27 files modified
.drone.yml (+9/-0)
Makefile (+25/-22)
README.md (+12/-0)
config.yaml (+8/-3)
hooks/hooks.py (+10/-24)
hooks/install (+13/-9)
hooks/python-pkgs.txt (+5/-0)
hooks/setup.py (+13/-10)
hooks/setup.sh (+9/-0)
metadata.yaml (+4/-4)
modules/docker_opts.py (+190/-0)
playbooks/config-changed.yaml (+27/-1)
playbooks/install-compose.yaml (+4/-0)
playbooks/install-or-upgrade.yaml (+1/-0)
playbooks/installation-status.yaml (+7/-3)
playbooks/latest-docker.yaml (+3/-0)
playbooks/network-relation-changed.yaml (+19/-31)
playbooks/site.yaml (+10/-2)
playbooks/templates/docker-defaults (+12/-0)
playbooks/universe-docker.yaml (+2/-0)
pytest.ini (+2/-0)
templates/docker (+0/-14)
tests/10-deploy-test (+3/-2)
tests/notes.md (+1/-0)
tests/tests.yaml (+4/-0)
tox.ini (+42/-0)
unit_tests/test_dockeropts_module.py (+188/-0)
To merge this branch: bzr merge lp:~lazypower/charms/trusty/docker/trunk
Reviewer Review Type Date Requested Status
Tim Van Steenburgh 2015-06-02 Approve on 2015-07-17
Review via email: mp+260867@code.launchpad.net

Description of the change

  Major Release update of v0.1.0

  - Support for docker tagging enabled
  - Docker Opts manager
  - New Ansible charm support (breakout from charmhelpers)
  - Updates to tests for testing version upgrade (no longer relies on metapackage)

  Big thanks to @whitmo for these contributions

To post a comment you must log in.
Charles Butler (lazypower) wrote :

Discovered breaking changes in this release, please hold review until the all clear is given.

Charles Butler (lazypower) wrote :

Corrected breaking bugs, ready for review.

Benjamin Saller (bcsaller) wrote :

Hey,

Got a chance to look this charm over, things look pretty good. Here are some general notes, monstly minor.

README: be sure to update the CS URI to point to the proper location. You reference a full manual at the github loc but don't include the link there, ah, I see the github version actually has this. Not sure why this is behind.

config.yaml: you might want to highlight the precedence between latest and version keys a little more.

unit_tests: If you don't have them I'd drop the directory. This is ok to me as you do have the integration tests. It does feel like the docker opts mgr should have unit tests to get that merged in though.

integration-tests: Do assertions around the relation live in a bundle outside this? You test basic runtime but not that the relations work.

hooks/install -> setup.py -> hooks.py -> playbook dance: Maybe more complicated than it needs to be, if you don't call into hooks after the basic apt-get work you can let config-changed handle the rest (as it deals with version/latest), might be a little cleaner, what do you think?

playbooks/*: Try as I might I don't find these any easier to read, write, modify than the other best practices we have, but I am all for trying to standardize on something. After some time with Ansible charms I would like to have a talk with the team and see how everyone feels it plays out in practice.

Cory Johns (johnsca) wrote :

I agree with Ben's assessment that DockerOpts needs tests (and documentation). There are also still some missing hooks, including a newly added relation missing a hook. I created issues on GitHub for these:

https://github.com/chuckbutler/docker-charm/issues/54
https://github.com/chuckbutler/docker-charm/issues/53

I also updated an existing issue for the missing start hook because I still think it's a significant issue:

https://github.com/chuckbutler/docker-charm/issues/21

Along with Ben's comment about the install hook, and Whit's comment in a Hangouts session we had, I think it would be worth replacing the install hook with just bash (a la setup.sh) and eschew Python there completely:

https://github.com/chuckbutler/docker-charm/issues/55

Cory Johns (johnsca) wrote :

As I noted on the GitHub issue, I completely misunderstood the point (or lack thereof) of the start hook. Ignore that part of my comment, and let the start hook pass gently into the night.

Tim Van Steenburgh (tvansteenburgh) wrote :

Previous review comments have been addressed and tests pass, LGTM.

review: Approve
6. By Tim Van Steenburgh on 2015-07-17

[lazypower] v0.1.4.1 - includes new 'compose' option

- New 'compose' option for installing docker-compose
- Default docker version bumped to 1.7.0

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.drone.yml'
2--- .drone.yml 1970-01-01 00:00:00 +0000
3+++ .drone.yml 2015-07-10 20:52:43 +0000
4@@ -0,0 +1,9 @@
5+image: lazypower/ubuntu-drone
6+git:
7+ path: github.com/chuckbutler/docker-charm
8+script:
9+ - sudo apt-get update
10+ - make tox
11+ - make lint
12+ - make unit_test
13+
14
15=== modified file 'Makefile'
16--- Makefile 2015-02-13 14:15:18 +0000
17+++ Makefile 2015-07-10 20:52:43 +0000
18@@ -1,26 +1,29 @@
19 #!/usr/bin/make
20
21-build: virtualenv lint test
22-
23-virtualenv: .venv/bin/python
24-.venv/bin/python:
25- sudo apt-get install -y python-virtualenv
26- virtualenv .venv
27- .venv/bin/pip install nose flake8 mock pyyaml charmhelpers charm-tools ecdsa ansible ansible-lint
28-
29-lint: .venv/bin/python
30- @.venv/bin/flake8 hooks unit_tests
31- @.venv/bin/ansible-lint playbooks/site.yaml
32- @.venv/bin/charm proof
33-
34-unit_test: .venv/bin/python
35- @echo Starting tests...
36- @CHARM_DIR=. PYTHONPATH=./hooks .venv/bin/nosetests -vv --nologcapture unit_tests
37-
38-func_test:
39- @echo functional tests...
40- @juju test
41+build: tox lint test
42+
43+tox:
44+/usr/bin/tox:
45+ sudo apt-get install -y python-tox python-dev python-virtualenv
46+
47+lint: /usr/bin/tox
48+ tox -e lint
49+
50+
51+unit_test: /usr/bin/tox
52+ tox
53+
54+release: check-path virtualenv
55+ @.venv/bin/pip install git-vendor
56+ @.venv/bin/git-vendor sync -d ${DOCKER_MASTER_BZR}
57+
58+check-path:
59+ifndef DOCKER_MASTER_BZR
60+ $(error DOCKER_MASTER_BZR is undefined)
61+endif
62+
63
64 clean:
65- rm -rf .venv
66- find -name *.pyc -delete
67+ @rm -rf .venv
68+ @rm -rf .tox
69+ @find -name *.pyc -delete
70
71=== modified file 'README.md'
72--- README.md 2015-03-17 16:41:51 +0000
73+++ README.md 2015-07-10 20:52:43 +0000
74@@ -1,5 +1,6 @@
75 # Docker ![](https://d3oypxn00j2a10.cloudfront.net/0.12.10/img/nav/docker-logo-loggedout.png)
76
77+[![Build Status](http://drone.dasroot.net/api/badge/github.com/chuckbutler/docker-charm/status.svg?branch=master)](http://drone.dasroot.net/github.com/chuckbutler/docker-charm)
78
79
80 This charm provides [Docker](http://docker.io). Docker is an open platform for
81@@ -35,6 +36,17 @@
82 repositories. If you wish to deploy the latest upstream docker runtime enable
83 this option.
84
85+- version : String representation of the version you wish to deploy. This helps
86+ when scaling a cluster post deployment, to ensure you dont have mismatched
87+ versions deployed due to a new release
88+
89+ juju set docker version=1.6.2
90+
91+- compose : Boolean representtion on if you wish to include `docker compose`
92+ to be installed during the installation of `docker`. This allows you to
93+ leverage a yaml file to spin up and manage multiple containers that
94+ comprise a single application stack.
95+
96 ## Known Limitations and Issues
97
98
99
100=== modified file 'config.yaml'
101--- config.yaml 2015-04-22 21:35:40 +0000
102+++ config.yaml 2015-07-10 20:52:43 +0000
103@@ -2,13 +2,18 @@
104 latest:
105 type: boolean
106 default: False
107- description: |
108- If True, deploy the latest version of docker, when False (default)
109+ description: |
110+ If True, deploy the latest version of docker, when False (default)
111 installs docker.io from the Ubuntu package archive."
112 version:
113 type: string
114- default: 1.5.0
115+ default: 1.7.0
116 description: |
117 When latest = true, specify the version to install from the PPA.
118 latest = false will always assume use the current stable in distro.
119+ compose:
120+ type: boolean
121+ default: true
122+ description: |
123+ Install docker compose, the formation launch utility
124
125
126=== modified file 'hooks/hooks.py'
127--- hooks/hooks.py 2015-01-16 22:42:54 +0000
128+++ hooks/hooks.py 2015-07-10 20:52:43 +0000
129@@ -1,28 +1,14 @@
130 #!/usr/bin/env python
131-from charmhelpers.contrib.ansible import AnsibleHooks
132-from charmhelpers.contrib.ansible import install_ansible_support
133+from ansiblecharm.runner import AnsibleHooks
134 import sys
135-
136-hook_names = [
137- 'config-changed',
138- 'install',
139- 'start',
140- 'stop',
141- 'upgrade-charm',
142- 'network-relation-changed',
143- ]
144-
145-hooks = AnsibleHooks(playbook_path='playbooks/site.yaml',
146- default_hooks=hook_names)
147-
148-
149-@hooks.hook('install', 'upgrade-charm')
150-def install():
151- """
152- Install or upgrade ansible.
153- """
154- install_ansible_support(from_ppa=True)
155-
156+import os
157+
158+
159+def main(args=sys.argv):
160+ hooks = AnsibleHooks(playbook_path='playbooks/site.yaml',
161+ hook_dir=os.path.dirname(__file__),
162+ default_hooks=['install'])
163+ hooks.execute(args)
164
165 if __name__ == "__main__":
166- hooks.execute(sys.argv)
167+ main()
168
169=== modified file 'hooks/install'
170--- hooks/install 2015-01-16 22:42:54 +0000
171+++ hooks/install 2015-07-10 20:52:43 +0000
172@@ -1,9 +1,13 @@
173-#!/usr/bin/env python
174-import setup
175-import sys
176-
177-if __name__ == "__main__":
178- setup.pre_install()
179-
180- from hooks import hooks
181- hooks.execute(sys.argv)
182+#!/bin/bash
183+set -e
184+
185+apt-get update
186+
187+apt-get install -y \
188+ python-dev \
189+ python-pip \
190+ git
191+
192+easy_install -U pip
193+
194+pip install -r hooks/python-pkgs.txt
195
196=== added file 'hooks/python-pkgs.txt'
197--- hooks/python-pkgs.txt 1970-01-01 00:00:00 +0000
198+++ hooks/python-pkgs.txt 2015-07-10 20:52:43 +0000
199@@ -0,0 +1,5 @@
200+-e git+https://github.com/whitmo/ansible-charm.git#egg=AnsibleCharm
201+charmhelpers
202+ansible
203+path.py
204+docker-py
205
206=== modified file 'hooks/setup.py'
207--- hooks/setup.py 2015-01-16 22:42:54 +0000
208+++ hooks/setup.py 2015-07-10 20:52:43 +0000
209@@ -1,19 +1,22 @@
210 import subprocess
211+import sys
212+import os
213
214
215 def pre_install():
216 """
217 Do any setup required before the install hook.
218 """
219- install_charmhelpers()
220-
221-
222-def install_charmhelpers():
223- """
224- Install the charmhelpers library, if not present.
225- """
226 try:
227- import charmhelpers # noqa
228+ import charmhelpers #noqa
229+ import ansiblecharm # noqa
230+ from path import path # noqa
231 except ImportError:
232- subprocess.check_call(['apt-get', 'install', '-y', 'python-pip'])
233- subprocess.check_call(['pip', 'install', 'charmhelpers'])
234+ subprocess.check_call(['hooks/setup.sh'])
235+ subprocess.check_call("pip install -r hooks/python-pkgs.txt",
236+ shell=True)
237+
238+ from path import path
239+
240+ pth = str(path(os.environ['CHARM_DIR']) / 'src/ansiblecharm')
241+ sys.path.append(pth)
242
243=== added file 'hooks/setup.sh'
244--- hooks/setup.sh 1970-01-01 00:00:00 +0000
245+++ hooks/setup.sh 2015-07-10 20:52:43 +0000
246@@ -0,0 +1,9 @@
247+#!/bin/bash
248+set -e
249+
250+apt-get install -y \
251+ python-dev \
252+ python-pip \
253+ git
254+
255+easy_install -U pip
256
257=== modified file 'metadata.yaml'
258--- metadata.yaml 2015-03-11 12:45:49 +0000
259+++ metadata.yaml 2015-07-10 20:52:43 +0000
260@@ -10,10 +10,10 @@
261 provides:
262 docker-containers:
263 interface: containers
264+# events:
265+# interface: docker-socket
266 requires:
267 network:
268 interface: overlay-network
269- optional: true
270- logging:
271- interface: docker-socket
272- optional: true
273\ No newline at end of file
274+# logging:
275+# interface: docker-socket
276
277=== added directory 'modules'
278=== added file 'modules/docker_opts.py'
279--- modules/docker_opts.py 1970-01-01 00:00:00 +0000
280+++ modules/docker_opts.py 2015-07-10 20:52:43 +0000
281@@ -0,0 +1,190 @@
282+#!/usr/bin/env python
283+from ansible.module_utils.basic import * # noqa
284+import yaml
285+from path import path
286+
287+
288+class param(object):
289+ def __init__(self, key, type_=None):
290+ self.key = key
291+ self.type = type_
292+
293+ def __get__(self, obj, type=None):
294+ val = obj.mod.params.get(self.key)
295+ if self.type is None:
296+ return val
297+ return self.type(val)
298+
299+
300+class DockerOptsManager(object):
301+ """
302+ Ansible module to manage a yaml file that holds key:val
303+ pairs representing docker daemon options.
304+
305+ Values may either be strings or sets.
306+
307+ When the "read" action is called, the module will compute a value to
308+ use directly with the docker command. The module injects this into
309+ the global playbook namespace as `docker_daemon_opts`. Keys render
310+ as `--$key=$val`. Set collections render as
311+ `--$key=$member0..--$key=$memberN`.
312+
313+ Examples:
314+
315+ # task setting a value
316+ - name: set subnet daemon option
317+ docker_opts: action=set
318+ key=mtu
319+ val="{{ flannel_mtu }}"
320+ yaml="/path/to/opts.yaml"
321+ when: flannel_mtu != ''
322+
323+ # render all sets and keys to a set of docker options flags
324+ # Adds rendered value to global namespace as {{ docker_daemon_opts }}
325+ - name: calculate docker opts
326+ docker_opts: "action=read yaml=/path/to/opts.yaml"
327+
328+ # Populate a flag that has multiple values
329+ - name: set service name as a label
330+ docker_opts: action=add
331+ key=label val="{{ service_label }}"
332+ yaml="/path/to/opts.yaml"
333+
334+ - name: set unit as a label
335+ docker_opts: action=add
336+ key=label val="{{ unit_label }}"
337+ yaml="/path/to/opts.yaml"
338+ """
339+ action = param('action')
340+ key = param('key')
341+ val = param('val')
342+ path = param('yaml', path)
343+
344+ def __init__(self, module):
345+ self.mod = module
346+ self.fail = module.fail_json
347+ self.exit = module.exit_json
348+
349+ @staticmethod
350+ def make_module():
351+ # apparently only the * import modules
352+ # gets replacements
353+ module = AnsibleModule(
354+ argument_spec=dict(
355+ action=dict(default='read',
356+ choices=['read',
357+ 'set',
358+ 'delete',
359+ 'add',
360+ 'remove']),
361+ yaml=dict(required=True, type='str'),
362+ key=dict(type='str'),
363+ val=dict(type='str'),
364+ )
365+ )
366+ return module
367+
368+ @property
369+ def data(self):
370+ if self.path.exists():
371+ with open(self.path) as stream:
372+ data = yaml.safe_load(stream)
373+ return data
374+ return {}
375+
376+ @staticmethod
377+ def multiops(sets):
378+ for key, coll in sets:
379+ for val in coll:
380+ yield "--{0} {1}".format(key, val)
381+
382+ def read(self, data):
383+ di = data.items()
384+ flags = ["--%s %s" % (key, val)
385+ for key, val in di
386+ if isinstance(val, basestring)]
387+
388+ sets = ((key, val) for key, val in di if isinstance(val, set))
389+ mops = list(self.multiops(sets))
390+ flags.extend(mops)
391+ docker_opts = " ".join(flags)
392+
393+ return self.exit(changed=data and True or False,
394+ msg="Data from %s" % self.path,
395+ ansible_facts=dict(docker_daemon_opts=docker_opts))
396+
397+ def write_data(self, data):
398+ new_yaml = yaml.safe_dump(data)
399+ self.path.write_text(new_yaml)
400+
401+ def delete(self, data):
402+ changed = False
403+ if self.key in data:
404+ changed = True
405+ del data[self.key]
406+ self.write_data(data)
407+ return self.exit(changed=changed)
408+
409+ def set(self, data):
410+ changed = False
411+ msg = "No change to %s" % self.path
412+ oldval = data.get(self.key, None)
413+
414+ if isinstance(oldval, set):
415+ return self.fail("%s is a set" % self.key)
416+
417+ newval = oldval != self.val
418+ if oldval is None or newval:
419+ changed = True
420+ data[self.key] = self.val
421+ action = newval and "Created" or "Updated"
422+ msg = "%s key %s=%s in %s" % (action,
423+ self.key,
424+ self.val,
425+ self.path)
426+ self.write_data(data)
427+ return self.exit(changed=changed, msg=msg)
428+
429+ def add(self, data):
430+ changed = False
431+ msg = "No change to %s" % self.path
432+ oldset = data.setdefault(self.key, set())
433+ if not isinstance(oldset, set):
434+ return self.fail(msg="%s is not a set")
435+
436+ if self.val not in oldset:
437+ changed = True
438+ msg = "Added %s to %s:%s" % (self.val, self.key, oldset)
439+ oldset.add(self.val)
440+ self.write_data(data)
441+ return self.exit(changed=changed, msg=msg)
442+
443+ def remove(self, data):
444+ changed = False
445+ msg = "No change to %s" % self.path
446+ oldset = data.get(self.key, None)
447+ if oldset is not None and not isinstance(oldset, set):
448+ return self.fail(msg="%s is not a set")
449+ if self.val in oldset:
450+ changed = True
451+ msg = "Removed %s from %s:%s" % (self.val, self.key, oldset)
452+ oldset.remove(self.val)
453+ self.write_data(data)
454+ return self.exit(changed=changed, msg=msg)
455+
456+ def dispatch(self, action=None):
457+ if action is None:
458+ action = self.action
459+
460+ action = getattr(self, action)
461+ return action(self.data)
462+
463+ @classmethod
464+ def main(cls):
465+ module = cls.make_module()
466+ om = cls(module)
467+ return om.dispatch()
468+
469+
470+if __name__ == '__main__':
471+ DockerOptsManager.main()
472
473=== modified file 'playbooks/config-changed.yaml'
474--- playbooks/config-changed.yaml 2015-01-16 22:42:54 +0000
475+++ playbooks/config-changed.yaml 2015-07-10 20:52:43 +0000
476@@ -1,8 +1,34 @@
477 ---
478+- include: installation-status.yaml
479+
480 - name: enforce latest docker
481- when: latest == true and latest_installed == 'False'
482+ when: latest == true and latest_installed == "False"
483 include: latest-docker.yaml
484
485 - name: enforce universe docker
486 when: latest == false and universe_installed == "False"
487 include: universe-docker.yaml
488+
489+- set_fact:
490+ service_label: "service={{ service_name }}"
491+ unit_label: "unit={{ local_unit }}"
492+
493+- name: set service name as a label
494+ docker_opts: action=add
495+ key=label val="{{ service_label }}"
496+ yaml="{{ opts_yaml }}"
497+ notify:
498+ - calculate docker opts
499+ - render docker defaults
500+
501+- name: set unit as a label
502+ docker_opts: action=add
503+ key=label val="{{ unit_label }}"
504+ yaml="{{ opts_yaml }}"
505+ notify:
506+ - calculate docker opts
507+ - render docker defaults
508+
509+- name: Install docker compose
510+ include: install-compose.yaml
511+ when: compose == true
512
513=== added file 'playbooks/install-compose.yaml'
514--- playbooks/install-compose.yaml 1970-01-01 00:00:00 +0000
515+++ playbooks/install-compose.yaml 2015-07-10 20:52:43 +0000
516@@ -0,0 +1,4 @@
517+---
518+
519+- name: pip install docker-compose
520+ pip: name=docker-compose version=1.2.0
521
522=== modified file 'playbooks/install-or-upgrade.yaml'
523--- playbooks/install-or-upgrade.yaml 2015-04-22 21:35:40 +0000
524+++ playbooks/install-or-upgrade.yaml 2015-07-10 20:52:43 +0000
525@@ -1,3 +1,4 @@
526+
527 ---
528 - name: Add Docker Group
529 group: name=docker state=present
530
531=== modified file 'playbooks/installation-status.yaml'
532--- playbooks/installation-status.yaml 2015-04-22 21:35:40 +0000
533+++ playbooks/installation-status.yaml 2015-07-10 20:52:43 +0000
534@@ -1,5 +1,4 @@
535 ---
536-
537 - name: check for docker.io
538 shell: "dpkg -l docker.io | grep docker.io | cut -d' ' -f1"
539 register: universe_check
540@@ -13,6 +12,11 @@
541 universe_installed: "{{universe_check.stdout == 'ii'}}"
542 latest_installed: "{{latest_check.stdout == 'ii'}}"
543
544-- debug: msg="{{latest_installed}}"
545-- debug: msg="{{universe_installed}}"
546+- set_fact:
547+ docker_version_name: docker
548+ when: latest_installed == "True"
549+
550+- set_fact:
551+ docker_version_name: docker.io
552+ when: universe_installed == "True"
553
554
555=== modified file 'playbooks/latest-docker.yaml'
556--- playbooks/latest-docker.yaml 2015-04-22 21:35:40 +0000
557+++ playbooks/latest-docker.yaml 2015-07-10 20:52:43 +0000
558@@ -13,3 +13,6 @@
559
560 - name: Install required packages.
561 apt: name=lxc-docker-{{ version }} state=present update_cache=yes
562+
563+- set_fact:
564+ docker_version_name: docker
565\ No newline at end of file
566
567=== modified file 'playbooks/network-relation-changed.yaml'
568--- playbooks/network-relation-changed.yaml 2015-04-22 21:35:40 +0000
569+++ playbooks/network-relation-changed.yaml 2015-07-10 20:52:43 +0000
570@@ -26,29 +26,22 @@
571 chdir: "{{ charm_dir }}"
572 creates: .flannel-mtu
573
574-- set_fact:
575- docker_opts: "--bip {{ flannel_subnet }}"
576- when: "{{ flannel_subnet != '' }}"
577-
578-- set_fact:
579- docker_opts: "{{ docker_opts }} --mtu {{ flannel_mtu }}"
580- when: "{{ flannel_mtu != '' }}"
581-
582-- name: Render docker.io defaults template
583- template: src=docker dest=/etc/default/docker.io
584- when: universe_installed == "True" and flannel_subnet != ''
585-
586-- name: Render lxc-docker defaults template
587- template: src=docker dest=/etc/default/docker
588- when: latest_installed == "True" and flannel_subnet != ''
589-
590-- name: Halt docker service
591- service: name=docker state=stopped
592- when: latest_installed
593-
594-- name: Halt docker service
595- service: name=docker.io state=stopped
596- when: universe_installed
597+- name: set subnet daemon option
598+ docker_opts: action=set key=bip val="{{ flannel_subnet }}" yaml="{{opts_yaml}}"
599+ when: flannel_subnet != ''
600+ notify:
601+ - calculate docker opts
602+ - render docker defaults
603+
604+- name: set subnet daemon option
605+ docker_opts: action=set key=mtu val="{{ flannel_mtu }}" yaml="{{opts_yaml}}"
606+ when: flannel_mtu != ''
607+ notify:
608+ - calculate docker opts
609+ - render docker defaults
610+
611+- name: Halt docker service
612+ service: name={{docker_version_name}} state=stopped
613
614 - name: Bring Docker Bridge offline
615 shell: ifconfig docker0 down
616@@ -57,12 +50,7 @@
617 - name: Delete Docker Bridge
618 shell: brctl delbr docker0
619 ignore_errors: yes
620-
621-- name: Restart Docker
622- service: name=docker state=restarted
623- when: latest_installed
624-
625-- name: Restart Docker
626- service: name=docker.io state=restarted
627- when: universe_installed
628+ notify:
629+ - restart docker
630+
631
632
633=== modified file 'playbooks/site.yaml'
634--- playbooks/site.yaml 2015-04-22 21:35:40 +0000
635+++ playbooks/site.yaml 2015-07-10 20:52:43 +0000
636@@ -1,10 +1,18 @@
637 - hosts: all
638+ vars:
639+ opts_yaml: /etc/ansible/docker-opts.yaml
640+ docker_version_name: false
641+ handlers:
642+ - name: restart docker
643+ service: "name={{docker_version_name}} state=restarted"
644+ - name: calculate docker opts
645+ docker_opts: "action=read yaml={{opts_yaml}}"
646+ - name: render docker defaults
647+ template: "src=templates/docker-defaults dest=/etc/default/{{docker_version_name}}"
648 tasks:
649-
650 - name: Check Installation Status
651 include: installation-status.yaml
652 tags:
653- - config-changed
654 - network-relation-changed
655 - stop
656
657
658=== added directory 'playbooks/templates'
659=== added file 'playbooks/templates/docker-defaults'
660--- playbooks/templates/docker-defaults 1970-01-01 00:00:00 +0000
661+++ playbooks/templates/docker-defaults 2015-07-10 20:52:43 +0000
662@@ -0,0 +1,12 @@
663+# !! WARNING !! THIS FILE IS MANAGED BY JUJU
664+# DO NOT EDIT BY HAND, AS YOUR CHANGES WILL NOT BE SAVED.
665+
666+# Docker Upstart and SysVinit configuration file
667+
668+# Customize location of Docker binary (especially for development testing).
669+#DOCKER="/usr/local/bin/docker"
670+
671+{% if docker_daemon_opts %}
672+ # Use DOCKER_OPTS to modify the daemon startup options.
673+ DOCKER_OPTS="{{ docker_daemon_opts }} "
674+{% endif %}
675
676=== modified file 'playbooks/universe-docker.yaml'
677--- playbooks/universe-docker.yaml 2015-01-16 22:42:54 +0000
678+++ playbooks/universe-docker.yaml 2015-07-10 20:52:43 +0000
679@@ -20,3 +20,5 @@
680 - name: Add ubuntu to Docker group
681 user: name=ubuntu state=present groups="docker"
682
683+- set_fact:
684+ docker_version_name: docker.io
685\ No newline at end of file
686
687=== added file 'pytest.ini'
688--- pytest.ini 1970-01-01 00:00:00 +0000
689+++ pytest.ini 2015-07-10 20:52:43 +0000
690@@ -0,0 +1,2 @@
691+[pytest]
692+addopts = --tb=short -s --cov=hooks --cov=modules --cov-report=term-missing
693
694=== removed directory 'templates'
695=== removed file 'templates/docker'
696--- templates/docker 2015-01-16 22:42:54 +0000
697+++ templates/docker 1970-01-01 00:00:00 +0000
698@@ -1,14 +0,0 @@
699-# !! WARNING !! THIS FILE IS MANAGED BY JUJU
700-# DO NOT EDIT BY HAND, AS YOUR CHANGES WILL NOT BE SAVED.
701-
702-# Docker Upstart and SysVinit configuration file
703-
704-# Customize location of Docker binary (especially for development testing).
705-#DOCKER="/usr/local/bin/docker"
706-
707-{% if docker_opts %}
708- # Use DOCKER_OPTS to modify the daemon startup options.
709- DOCKER_OPTS="{{ docker_opts }} "
710-{% endif %}
711-
712-
713
714=== modified file 'tests/10-deploy-test'
715--- tests/10-deploy-test 2015-01-16 22:42:54 +0000
716+++ tests/10-deploy-test 2015-07-10 20:52:43 +0000
717@@ -65,9 +65,10 @@
718
719 def test_latest_config_option(self):
720 """ Set config option to latest and verify docker is installed """
721- self.deployment.configure('docker', {'latest': True})
722+ self.deployment.configure('docker', {'latest': True,
723+ 'version': '1.5.0'})
724 self.deployment.sentry.wait()
725- command = 'dpkg -l lxc-docker'
726+ command = 'dpkg -l lxc-docker-1.5.0'
727 output, code = self.docker_unit.run(command)
728 print(output)
729 if output.find('ii') == -1:
730
731=== added file 'tests/notes.md'
732--- tests/notes.md 1970-01-01 00:00:00 +0000
733+++ tests/notes.md 2015-07-10 20:52:43 +0000
734@@ -0,0 +1,1 @@
735+- test uprade
736
737=== added file 'tests/tests.yaml'
738--- tests/tests.yaml 1970-01-01 00:00:00 +0000
739+++ tests/tests.yaml 2015-07-10 20:52:43 +0000
740@@ -0,0 +1,4 @@
741+virtualenv: false
742+makefile:
743+ - lint
744+ - unit_test
745
746=== added file 'tox.ini'
747--- tox.ini 1970-01-01 00:00:00 +0000
748+++ tox.ini 2015-07-10 20:52:43 +0000
749@@ -0,0 +1,42 @@
750+[tox]
751+envlist = py27
752+skipsdist = True
753+
754+[testenv]
755+install_command = pip install {opts} --pre --use-wheel {packages}
756+deps =
757+ pytest
758+ pytest-cov
759+ pytest-capturelog
760+ mock
761+ charmhelpers
762+ pyyaml
763+ ansible
764+ path.py
765+ funcsigs
766+
767+setenv =
768+ PYTHONPATH = {toxinidir}/hooks
769+
770+commands =
771+ py.test {posargs}
772+
773+[testenv:lint]
774+install_command = pip install {opts} --pre --use-wheel {packages}
775+deps =
776+ flake8
777+ charm-tools
778+ ansible-lint
779+
780+commands =
781+ flake8 {toxinidir}/hooks {toxinidir}/modules
782+ charm proof
783+ ansible-lint {toxinidir}/playbooks/site.yaml
784+ ansible-lint {toxinidir}/playbooks/config-changed.yaml
785+ ansible-lint {toxinidir}/playbooks/install-compose.yaml
786+ ansible-lint {toxinidir}/playbooks/install-or-upgrade.yaml
787+ ansible-lint {toxinidir}/playbooks/installation-status.yaml
788+ ansible-lint {toxinidir}/playbooks/latest-docker.yaml
789+ ansible-lint {toxinidir}/playbooks/network-relation-changed.yaml
790+ ansible-lint {toxinidir}/playbooks/uninstall-docker.yaml
791+ ansible-lint {toxinidir}/playbooks/universe-docker.yaml
792
793=== added file 'unit_tests/test_dockeropts_module.py'
794--- unit_tests/test_dockeropts_module.py 1970-01-01 00:00:00 +0000
795+++ unit_tests/test_dockeropts_module.py 2015-07-10 20:52:43 +0000
796@@ -0,0 +1,188 @@
797+import yaml
798+from mock import Mock
799+from mock import call
800+from path import path
801+from unittest import TestCase
802+import pytest
803+import sys
804+import tempfile
805+
806+# https://pytest.org/latest/fixture.html
807+
808+here = path(__file__).parent
809+
810+def read_data(manager):
811+ """
812+ Read the yaml from a file using the manager path return the data object.
813+ """
814+ assert manager.path.exists()
815+ txt = manager.path.text()
816+ data = yaml.safe_load(txt)
817+ return data
818+
819+
820+@pytest.fixture
821+def docker_opts():
822+ sys.path.insert(0, str(here.parent / "modules"))
823+ import docker_opts
824+ return docker_opts
825+
826+
827+@pytest.fixture
828+def paramed_object(docker_opts):
829+
830+ class Classy(object):
831+ wat = docker_opts.param("wat", path)
832+ ok = docker_opts.param("ok")
833+
834+ def __init__(self, **kw):
835+ self.mod = Mock(name="ansible-module")
836+ self.params = kw
837+ return Classy(wat="/etc", ok=True)
838+
839+
840+@pytest.fixture(scope="module")
841+def tempdir(request):
842+ tmpdir = path(tempfile.mkdtemp(prefix="docker_opts_test-"))
843+ request.addfinalizer(tmpdir.rmtree)
844+ return tmpdir
845+
846+
847+def_state = dict(key='wat',
848+ val='yeah',
849+ action='ok',
850+ yaml="/wat/hey.yaml")
851+
852+
853+@pytest.fixture
854+def ansible_module_obj(state=def_state):
855+ amo = Mock(name="module")
856+
857+ def getter(key):
858+ return state.get(key)
859+
860+ amo.params.get.side_effect = getter
861+ return amo
862+
863+
864+def test_param(paramed_object):
865+ assert paramed_object.ok
866+ assert isinstance(paramed_object.wat, path)
867+
868+
869+def test_init(docker_opts, ansible_module_obj):
870+
871+ dom = docker_opts.DockerOptsManager(ansible_module_obj)
872+
873+ assert dom.action == 'ok'
874+ assert dom.val == 'yeah'
875+
876+
877+def test_add_remove(docker_opts, tempdir):
878+ """
879+ Verify the collection methods: add and remove work with test data.
880+ """
881+ key = 'add-remove-key'
882+ value = 'add-remove-value'
883+ filepath = tempdir / 'add_remove.yaml'
884+ amo = ansible_module_obj(dict(key=key,
885+ val=value,
886+ yaml=filepath,
887+ action='add'))
888+ dom = docker_opts.DockerOptsManager(amo)
889+ out = dom.dispatch()
890+ data = read_data(dom)
891+
892+ # Verify the value is in the collection at data[key]
893+ assert key in data and value in data[key]
894+ # Now remove the key from the collection.
895+ amo = ansible_module_obj(dict(key=key,
896+ val=value,
897+ yaml=filepath,
898+ action='remove'))
899+ dom = docker_opts.DockerOptsManager(amo)
900+ out = dom.dispatch()
901+ data = read_data(dom)
902+ # Verify the value was removed from the collection.
903+ assert key in data and value not in data[key]
904+
905+
906+def test_set_delete(docker_opts, tempdir):
907+ """
908+ Verify the key/value methods: set and delete work with test data.
909+ """
910+ key = 'set-delete-key'
911+ value = 'set-delete-value'
912+ filepath = tempdir / 'set_delete.yaml'
913+ amo = ansible_module_obj(dict(key=key,
914+ val=value,
915+ yaml=filepath,
916+ action='set'))
917+ dom = docker_opts.DockerOptsManager(amo)
918+ out = dom.dispatch()
919+ data = read_data(dom)
920+ # Verify that the value is equal to the data[key]
921+ assert key in data and data[key] == value
922+ # Now delete the key from the data object.
923+ amo = ansible_module_obj(dict(key=key,
924+ val=value,
925+ yaml=filepath,
926+ action='delete'))
927+ dom = docker_opts.DockerOptsManager(amo)
928+ out = dom.dispatch()
929+ data = read_data(dom)
930+ # Verify the value was removed from the data object.
931+ assert key not in data
932+
933+
934+def test_read(docker_opts, tempdir):
935+ """
936+ Verify the read method works on the DockerOptsManager class.
937+ """
938+ key = 'read-key'
939+ value = 'read-value'
940+ filepath = tempdir / 'read.yaml'
941+ amo = ansible_module_obj(dict(key=key,
942+ val=value,
943+ yaml=filepath,
944+ action='read'))
945+ dom = docker_opts.DockerOptsManager(amo)
946+ # The data propery reads the file, which is non existant.
947+ dom.read(dom.data)
948+ assert dom.exit.called
949+ args = dom.exit.call_args[1]
950+ assert args
951+ # Ensure the docker_daemon_opts are empty.
952+ assert args['ansible_facts']['docker_daemon_opts'] == ''
953+ amo = ansible_module_obj(dict(key=key,
954+ val=value,
955+ yaml=filepath,
956+ action='set'))
957+ dom = docker_opts.DockerOptsManager(amo)
958+ dom.dispatch()
959+ # Now read the file, and it should have the key and value inside.
960+ dom.read(dom.data)
961+ assert dom.exit.called
962+ args = dom.exit.call_args[1]
963+ assert args
964+ options = '--{0} {1}'.format(key, value)
965+ # Ensure the docker_daemon_opts match the options string.
966+ assert args['ansible_facts']['docker_daemon_opts'] == options
967+
968+
969+def test_simple_set(docker_opts, tempdir):
970+ amo = ansible_module_obj(dict(key='wat',
971+ val='hey',
972+ yaml=tempdir / 'set.yaml',
973+ action='set'))
974+ dom = docker_opts.DockerOptsManager(amo)
975+ out = dom.dispatch()
976+ assert amo.exit_json.called
977+
978+ args = amo.exit_json.call_args[1]
979+ assert args
980+ assert args['msg'].startswith("Created key wat=hey in /tmp/docker_opts_test")
981+ assert args['changed']
982+
983+ data = read_data(dom)
984+ assert 'wat' in data and data['wat'] == 'hey'

Subscribers

People subscribed via source and target branches

to all changes: