2
from shlex import split
3
from subprocess import check_call
4
from subprocess import check_output
6
from charmhelpers.core.hookenv import status_set
7
from charmhelpers.core.hookenv import config
8
from charmhelpers.core.host import lsb_release
9
from charmhelpers.core.host import service_restart
10
from charmhelpers.core.templating import render
11
from charmhelpers.fetch import apt_install
12
from charmhelpers.fetch import apt_update
14
from charms.reactive import remove_state
15
from charms.reactive import set_state
16
from charms.reactive import when
17
from charms.reactive import when_any
18
from charms.reactive import when_not
20
from charms.docker import DockerOpts
22
from charms import layer
24
# 2 Major events are emitted from this layer.
26
# `docker.ready` is an event intended to signal other layers that need to
27
# plug into the plumbing to extend the docker daemon. Such as fire up a
28
# bootstrap docker daemon, or predependency fetch + dockeropt rendering
30
# `docker.available` means the docker daemon setup has settled and is prepared
31
# to run workloads. This is a broad state that has large implications should
32
# you decide to remove it. Production workloads can be lost if no restart flag
35
# Be sure you bind to it appropriately in your workload layer and
36
# react to the proper event.
39
@when_not('docker.ready')
41
''' Install the docker daemon, and supporting tooling '''
42
# Often when building layer-docker based subordinates, you dont need to
43
# incur the overhead of installing docker. This tuneable layer option
44
# allows you to disable the exec of that install routine, and instead short
45
# circuit immediately to docker.available, so you can charm away!
46
layer_opts = layer.options('docker')
47
if layer_opts['skip-install']:
48
set_state('docker.available')
49
set_state('docker.ready')
52
status_set('maintenance', 'Installing AUFS and other tools')
53
kernel_release = check_output(['uname', '-r']).rstrip()
57
'linux-image-extra-{0}'.format(kernel_release),
61
# Install docker-engine from apt.
65
render('docker.defaults', '/etc/default/docker', {'opts': opts.to_s()})
66
render('docker.systemd', '/lib/systemd/system/docker.service', config())
67
reload_system_daemons()
69
status_set('active', 'Docker installed, cycling for extensions')
70
set_state('docker.ready')
72
# Make with the adding of the users to the groups
73
check_call(['usermod', '-aG', 'docker', 'ubuntu'])
76
@when_any('config.http_proxy.changed', 'config.https_proxy.changed')
78
set_state('docker.restart')
81
def install_from_apt():
82
''' Install docker from the apt repository. This is a pyton adaptation of
83
the shell script found at https://get.docker.com/ '''
84
status_set('maintenance', 'Installing docker-engine from apt')
85
keyserver = 'hkp://p80.pool.sks-keyservers.net:80'
86
key = '58118E89F3A912897C070ADBF76221572C52609D'
87
# Enter the server and key in the apt-key management tool.
88
cmd = 'apt-key adv --keyserver {0} --recv-keys {1}'.format(keyserver, key)
89
# "apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80
90
# --recv-keys 58118E89F3A912897C070ADBF76221572C52609D"
91
check_call(split(cmd))
92
# The url to the server that contains the docker apt packages.
93
apt_url = 'https://apt.dockerproject.org'
94
# Get the package architecture (amd64), not the machine hardware (x86_64)
95
arch = check_output(split('dpkg --print-architecture'))
96
arch = arch.decode('utf-8').rstrip()
97
# Get the lsb information as a dictionary.
99
# Ubuntu must be lowercased.
100
dist = lsb['DISTRIB_ID'].lower()
101
# The codename for the release.
102
code = lsb['DISTRIB_CODENAME']
103
# repo can be: main, testing or experimental
105
# deb [arch=amd64] https://apt.dockerproject.org/repo ubuntu-xenial main
106
deb = 'deb [arch={0}] {1}/repo {2}-{3} {4}'.format(
107
arch, apt_url, dist, code, repo)
108
# mkdir -p /etc/apt/sources.list.d
109
if not os.path.isdir('/etc/apt/sources.list.d'):
110
os.makedirs('/etc/apt/sources.list.d')
111
# Write the docker source file to the apt sources.list.d directory.
112
with(open('/etc/apt/sources.list.d/docker.list', 'w+')) as stream:
114
apt_update(fatal=True)
115
# apt-get install -y -q docker-engine
116
apt_install(['docker-engine'], fatal=True)
119
@when('docker.ready')
120
@when_not('cgroups.modified')
121
def enable_grub_cgroups():
123
if cfg.get('enable-cgroups'):
124
check_call(['scripts/enable_grub_cgroups.sh'])
125
set_state('cgroups.modified')
128
@when('docker.ready')
129
@when_not('docker.available')
130
def signal_workloads_start():
131
''' We can assume the pre-workload bits have completed now that docker.ready
132
has been reacted to. Lets remove the predep work and continue on to being
134
status_set('active', 'Docker installed')
135
set_state('docker.available')
138
@when('docker.restart')
139
def recycle_daemon():
140
''' Other layers should be able to trigger a daemon restart '''
141
status_set('maintenance', 'Restarting docker daemon')
143
# Re-render our docker daemon template at this time... because we're
144
# restarting. And its nice to play nice with others. Isn't that nice?
146
render('docker.defaults', '/etc/default/docker', {'opts': opts.to_s()})
147
render('docker.systemd', '/lib/systemd/system/docker.service', config())
148
reload_system_daemons()
149
service_restart('docker')
150
remove_state('docker.restart')
153
def reload_system_daemons():
154
''' Reload the system daemons from on-disk configuration changes '''
155
command = ['systemctl', 'daemon-reload']