Skip to content

Commit 4457dce

Browse files
author
Thomas Recloux
committed
Introduce support for ubuntu 15.04 and higher
Starting with version 15.04, ubuntu is based on systemd. The existing ubuntu provider did not support systemd. This commit introduce a new provider dedicated to systemd based versions. The previous provider is renamed to ubuntu_upstart. Provider detection uses the /etc/os=release VERSION_ID. Version ID is converted to a float value and then used to choose between providers. Unit tests are validating the provisioner compatibility. Fixes docker-archive-public#1891 Signed-off-by: Thomas Recloux <thomas.recloux@gmail.com>
1 parent 7e8e38e commit 4457dce

File tree

4 files changed

+310
-1
lines changed

4 files changed

+310
-1
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package provision
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strconv"
7+
"text/template"
8+
9+
"github.com/docker/machine/libmachine/auth"
10+
"github.com/docker/machine/libmachine/drivers"
11+
"github.com/docker/machine/libmachine/engine"
12+
"github.com/docker/machine/libmachine/log"
13+
"github.com/docker/machine/libmachine/mcnutils"
14+
"github.com/docker/machine/libmachine/provision/pkgaction"
15+
"github.com/docker/machine/libmachine/provision/serviceaction"
16+
"github.com/docker/machine/libmachine/swarm"
17+
)
18+
19+
func init() {
20+
Register("Ubuntu-SystemD", &RegisteredProvisioner{
21+
New: NewUbuntuSystemdProvisioner,
22+
})
23+
}
24+
25+
func NewUbuntuSystemdProvisioner(d drivers.Driver) Provisioner {
26+
return &UbuntuSystemdProvisioner{
27+
GenericProvisioner{
28+
DockerOptionsDir: "/etc/docker",
29+
DaemonOptionsFile: "/etc/systemd/system/docker.service",
30+
OsReleaseID: "ubuntu",
31+
Packages: []string{
32+
"curl",
33+
},
34+
Driver: d,
35+
},
36+
}
37+
}
38+
39+
type UbuntuSystemdProvisioner struct {
40+
GenericProvisioner
41+
}
42+
43+
func (provisioner *UbuntuSystemdProvisioner) CompatibleWithHost() bool {
44+
const FirstUbuntuSystemdVersion = 15.04
45+
46+
isUbuntu := provisioner.OsReleaseInfo.ID == provisioner.OsReleaseID
47+
if !isUbuntu {
48+
return false
49+
}
50+
versionNumber, err := strconv.ParseFloat(provisioner.OsReleaseInfo.VersionID, 64)
51+
if err != nil {
52+
return false
53+
}
54+
return versionNumber >= FirstUbuntuSystemdVersion
55+
56+
}
57+
58+
func (provisioner *UbuntuSystemdProvisioner) Service(name string, action serviceaction.ServiceAction) error {
59+
// daemon-reload to catch config updates; systemd -- ugh
60+
if _, err := provisioner.SSHCommand("sudo systemctl daemon-reload"); err != nil {
61+
return err
62+
}
63+
64+
command := fmt.Sprintf("sudo systemctl -f %s %s", action.String(), name)
65+
66+
if _, err := provisioner.SSHCommand(command); err != nil {
67+
return err
68+
}
69+
70+
return nil
71+
}
72+
73+
func (provisioner *UbuntuSystemdProvisioner) Package(name string, action pkgaction.PackageAction) error {
74+
var packageAction string
75+
76+
updateMetadata := true
77+
78+
switch action {
79+
case pkgaction.Install, pkgaction.Upgrade:
80+
packageAction = "install"
81+
case pkgaction.Remove:
82+
packageAction = "remove"
83+
updateMetadata = false
84+
}
85+
86+
switch name {
87+
case "docker":
88+
name = "docker-engine"
89+
}
90+
91+
if updateMetadata {
92+
if _, err := provisioner.SSHCommand("sudo apt-get update"); err != nil {
93+
return err
94+
}
95+
}
96+
97+
// handle the new docker-engine package; we can probably remove this
98+
// after we have a few versions
99+
if action == pkgaction.Upgrade && name == "docker-engine" {
100+
// run the force remove on the existing lxc-docker package
101+
// and remove the existing apt source list
102+
// also re-run the get.docker.com script to properly setup
103+
// the system again
104+
105+
commands := []string{
106+
"rm /etc/apt/sources.list.d/docker.list || true",
107+
"apt-get remove -y lxc-docker || true",
108+
"curl -sSL https://get.docker.com | sh",
109+
}
110+
111+
for _, cmd := range commands {
112+
command := fmt.Sprintf("sudo DEBIAN_FRONTEND=noninteractive %s", cmd)
113+
if _, err := provisioner.SSHCommand(command); err != nil {
114+
return err
115+
}
116+
}
117+
}
118+
119+
command := fmt.Sprintf("DEBIAN_FRONTEND=noninteractive sudo -E apt-get %s -y %s", packageAction, name)
120+
121+
log.Debugf("package: action=%s name=%s", action.String(), name)
122+
123+
if _, err := provisioner.SSHCommand(command); err != nil {
124+
return err
125+
}
126+
127+
return nil
128+
}
129+
130+
func (provisioner *UbuntuSystemdProvisioner) dockerDaemonResponding() bool {
131+
if _, err := provisioner.SSHCommand("sudo docker version"); err != nil {
132+
log.Warnf("Error getting SSH command to check if the daemon is up: %s", err)
133+
return false
134+
}
135+
136+
// The daemon is up if the command worked. Carry on.
137+
return true
138+
}
139+
140+
func (provisioner *UbuntuSystemdProvisioner) Provision(swarmOptions swarm.Options, authOptions auth.Options, engineOptions engine.Options) error {
141+
provisioner.SwarmOptions = swarmOptions
142+
provisioner.AuthOptions = authOptions
143+
provisioner.EngineOptions = engineOptions
144+
swarmOptions.Env = engineOptions.Env
145+
146+
if provisioner.EngineOptions.StorageDriver == "" {
147+
provisioner.EngineOptions.StorageDriver = "aufs"
148+
}
149+
150+
// HACK: since debian does not come with sudo by default we install
151+
log.Debug("installing sudo")
152+
if _, err := provisioner.SSHCommand("if ! type sudo; then apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y sudo; fi"); err != nil {
153+
return err
154+
}
155+
156+
log.Debug("setting hostname")
157+
if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil {
158+
return err
159+
}
160+
161+
log.Debug("installing base packages")
162+
for _, pkg := range provisioner.Packages {
163+
if err := provisioner.Package(pkg, pkgaction.Install); err != nil {
164+
return err
165+
}
166+
}
167+
168+
log.Debug("installing docker")
169+
if err := installDockerGeneric(provisioner, engineOptions.InstallURL); err != nil {
170+
return err
171+
}
172+
173+
log.Debug("waiting for docker daemon")
174+
if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil {
175+
return err
176+
}
177+
178+
provisioner.AuthOptions = setRemoteAuthOptions(provisioner)
179+
180+
log.Debug("configuring auth")
181+
if err := ConfigureAuth(provisioner); err != nil {
182+
return err
183+
}
184+
185+
log.Debug("configuring swarm")
186+
if err := configureSwarm(provisioner, swarmOptions, provisioner.AuthOptions); err != nil {
187+
return err
188+
}
189+
190+
// enable in systemd
191+
log.Debug("enabling docker in systemd")
192+
if err := provisioner.Service("docker", serviceaction.Enable); err != nil {
193+
return err
194+
}
195+
196+
return nil
197+
}
198+
199+
func (provisioner *UbuntuSystemdProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) {
200+
var (
201+
engineCfg bytes.Buffer
202+
)
203+
204+
driverNameLabel := fmt.Sprintf("provider=%s", provisioner.Driver.DriverName())
205+
provisioner.EngineOptions.Labels = append(provisioner.EngineOptions.Labels, driverNameLabel)
206+
207+
engineConfigTmpl := `[Service]
208+
ExecStart=/usr/bin/docker -d -H tcp://0.0.0.0:{{.DockerPort}} -H unix:///var/run/docker.sock --storage-driver {{.EngineOptions.StorageDriver}} --tlsverify --tlscacert {{.AuthOptions.CaCertRemotePath}} --tlscert {{.AuthOptions.ServerCertRemotePath}} --tlskey {{.AuthOptions.ServerKeyRemotePath}} {{ range .EngineOptions.Labels }}--label {{.}} {{ end }}{{ range .EngineOptions.InsecureRegistry }}--insecure-registry {{.}} {{ end }}{{ range .EngineOptions.RegistryMirror }}--registry-mirror {{.}} {{ end }}{{ range .EngineOptions.ArbitraryFlags }}--{{.}} {{ end }}
209+
MountFlags=slave
210+
LimitNOFILE=1048576
211+
LimitNPROC=1048576
212+
LimitCORE=infinity
213+
Environment={{range .EngineOptions.Env}}{{ printf "%q" . }} {{end}}
214+
215+
[Install]
216+
WantedBy=multi-user.target
217+
`
218+
t, err := template.New("engineConfig").Parse(engineConfigTmpl)
219+
if err != nil {
220+
return nil, err
221+
}
222+
223+
engineConfigContext := EngineConfigContext{
224+
DockerPort: dockerPort,
225+
AuthOptions: provisioner.AuthOptions,
226+
EngineOptions: provisioner.EngineOptions,
227+
}
228+
229+
t.Execute(&engineCfg, engineConfigContext)
230+
231+
return &DockerOptions{
232+
EngineOptions: engineCfg.String(),
233+
EngineOptionsPath: provisioner.DaemonOptionsFile,
234+
}, nil
235+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package provision
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestUbuntuSystemdCompatibleWithHost(t *testing.T) {
8+
info := &OsRelease{
9+
ID: "ubuntu",
10+
VersionID: "15.04",
11+
}
12+
p := NewUbuntuSystemdProvisioner(nil)
13+
p.SetOsReleaseInfo(info)
14+
15+
compatible := p.CompatibleWithHost()
16+
17+
if !compatible {
18+
t.Fatalf("expected to be compatible with ubuntu 15.04")
19+
}
20+
21+
info.VersionID = "14.04"
22+
23+
compatible = p.CompatibleWithHost()
24+
25+
if compatible {
26+
t.Fatalf("expected to NOT be compatible with ubuntu 14.04")
27+
}
28+
29+
}
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package provision
22

33
import (
44
"fmt"
5+
"strconv"
56

67
"github.com/docker/machine/libmachine/auth"
78
"github.com/docker/machine/libmachine/drivers"
@@ -14,7 +15,7 @@ import (
1415
)
1516

1617
func init() {
17-
Register("Ubuntu", &RegisteredProvisioner{
18+
Register("Ubuntu-UpStart", &RegisteredProvisioner{
1819
New: NewUbuntuProvisioner,
1920
})
2021
}
@@ -37,6 +38,21 @@ type UbuntuProvisioner struct {
3738
GenericProvisioner
3839
}
3940

41+
func (provisioner *UbuntuProvisioner) CompatibleWithHost() bool {
42+
const FirstUbuntuSystemdVersion = 15.04
43+
isUbuntu := provisioner.OsReleaseInfo.ID == provisioner.OsReleaseID
44+
if !isUbuntu {
45+
return false
46+
}
47+
versionNumber, err := strconv.ParseFloat(provisioner.OsReleaseInfo.VersionID, 64)
48+
if err != nil {
49+
return false
50+
}
51+
52+
return versionNumber < FirstUbuntuSystemdVersion
53+
54+
}
55+
4056
func (provisioner *UbuntuProvisioner) Service(name string, action serviceaction.ServiceAction) error {
4157
command := fmt.Sprintf("sudo service %s %s", name, action.String())
4258

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package provision
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestUbuntuCompatibleWithHost(t *testing.T) {
8+
info := &OsRelease{
9+
ID: "ubuntu",
10+
VersionID: "14.04",
11+
}
12+
p := NewUbuntuProvisioner(nil)
13+
p.SetOsReleaseInfo(info)
14+
15+
compatible := p.CompatibleWithHost()
16+
17+
if !compatible {
18+
t.Fatalf("expected to be compatible with ubuntu 14.04")
19+
}
20+
21+
info.VersionID = "15.04"
22+
23+
compatible = p.CompatibleWithHost()
24+
25+
if compatible {
26+
t.Fatalf("expected to NOT be compatible with ubuntu 15.04")
27+
}
28+
29+
}

0 commit comments

Comments
 (0)