1package provision
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/docker/machine/libmachine/auth"
8	"github.com/docker/machine/libmachine/drivers"
9	"github.com/docker/machine/libmachine/engine"
10	"github.com/docker/machine/libmachine/log"
11	"github.com/docker/machine/libmachine/mcnutils"
12	"github.com/docker/machine/libmachine/provision/pkgaction"
13	"github.com/docker/machine/libmachine/provision/serviceaction"
14	"github.com/docker/machine/libmachine/swarm"
15)
16
17func init() {
18	Register("SUSE", &RegisteredProvisioner{
19		New: NewSUSEProvisioner,
20	})
21}
22
23func NewSUSEProvisioner(d drivers.Driver) Provisioner {
24	return &SUSEProvisioner{
25		NewSystemdProvisioner("SUSE", d),
26	}
27}
28
29type SUSEProvisioner struct {
30	SystemdProvisioner
31}
32
33func (provisioner *SUSEProvisioner) CompatibleWithHost() bool {
34	ids := strings.Split(provisioner.OsReleaseInfo.IDLike, " ")
35	for _, id := range ids {
36		if id == "suse" {
37			return true
38		}
39	}
40	return false
41}
42
43func (provisioner *SUSEProvisioner) String() string {
44	return "SUSE"
45}
46
47func (provisioner *SUSEProvisioner) Package(name string, action pkgaction.PackageAction) error {
48	var packageAction string
49
50	switch action {
51	case pkgaction.Install:
52		packageAction = "in"
53		// This is an optimization that reduces the provisioning time of certain
54		// systems in a significant way.
55		// The invocation of "zypper in <pkg>" causes the download of the metadata
56		// of all the repositories that have never been refreshed or that have
57		// automatic refresh toggled and have not been refreshed recently.
58		// Refreshing the repository metadata can take quite some time and can cause
59		// longer provisioning times for machines that have been pre-optimized for
60		// docker by including all the needed packages.
61		if _, err := provisioner.SSHCommand(fmt.Sprintf("rpm -q %s", name)); err == nil {
62			log.Debugf("%s is already installed, skipping operation", name)
63			return nil
64		}
65	case pkgaction.Remove:
66		packageAction = "rm"
67	case pkgaction.Upgrade:
68		packageAction = "up"
69	}
70
71	command := fmt.Sprintf("sudo -E zypper -n %s %s", packageAction, name)
72
73	log.Debugf("zypper: action=%s name=%s", action.String(), name)
74
75	if _, err := provisioner.SSHCommand(command); err != nil {
76		return err
77	}
78
79	return nil
80}
81
82func (provisioner *SUSEProvisioner) dockerDaemonResponding() bool {
83	log.Debug("checking docker daemon")
84
85	if out, err := provisioner.SSHCommand("sudo docker version"); err != nil {
86		log.Warnf("Error getting SSH command to check if the daemon is up: %s", err)
87		log.Debugf("'sudo docker version' output:\n%s", out)
88		return false
89	}
90
91	// The daemon is up if the command worked.  Carry on.
92	return true
93}
94
95func (provisioner *SUSEProvisioner) Provision(swarmOptions swarm.Options, authOptions auth.Options, engineOptions engine.Options) error {
96	provisioner.SwarmOptions = swarmOptions
97	provisioner.AuthOptions = authOptions
98	provisioner.EngineOptions = engineOptions
99	swarmOptions.Env = engineOptions.Env
100
101	// figure out the filesystem used by /var/lib/docker
102	fs, err := provisioner.SSHCommand("stat -f -c %T /var/lib/docker")
103	if err != nil {
104		// figure out the filesystem used by /var/lib
105		fs, err = provisioner.SSHCommand("stat -f -c %T /var/lib/")
106		if err != nil {
107			return err
108		}
109	}
110	graphDriver := "overlay"
111	if strings.Contains(fs, "btrfs") {
112		graphDriver = "btrfs"
113	}
114
115	storageDriver, err := decideStorageDriver(provisioner, graphDriver, engineOptions.StorageDriver)
116	if err != nil {
117		return err
118	}
119	provisioner.EngineOptions.StorageDriver = storageDriver
120
121	log.Debug("Setting hostname")
122	if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil {
123		return err
124	}
125
126	if !strings.HasPrefix(strings.ToLower(provisioner.OsReleaseInfo.ID), "opensuse") {
127		// This is a SLE machine, enable the containers module to have access
128		// to the docker packages
129		if _, err := provisioner.SSHCommand("sudo -E SUSEConnect -p sle-module-containers/12/$(uname -m) -r ''"); err != nil {
130			return fmt.Errorf(
131				"Error while adding the 'containers' module, make sure this machine is registered either against SUSE Customer Center (SCC) or to a local Subscription Management Tool (SMT): %v",
132				err)
133		}
134	}
135
136	log.Debug("Installing base packages")
137	for _, pkg := range provisioner.Packages {
138		if err := provisioner.Package(pkg, pkgaction.Install); err != nil {
139			return err
140		}
141	}
142
143	log.Debug("Installing docker")
144	if err := provisioner.Package("docker", pkgaction.Install); err != nil {
145		return err
146	}
147
148	// create symlinks for containerd, containerd-shim and optional runc.
149	// We have to do that because machine overrides the openSUSE systemd
150	// unit of docker
151	if _, err := provisioner.SSHCommand("yes no | sudo -E ln -si /usr/sbin/runc /usr/sbin/docker-runc"); err != nil {
152		return err
153	}
154	if _, err := provisioner.SSHCommand("sudo -E ln -sf /usr/sbin/containerd /usr/sbin/docker-containerd"); err != nil {
155		return err
156	}
157	if _, err := provisioner.SSHCommand("sudo -E ln -sf /usr/sbin/containerd-shim /usr/sbin/docker-containerd-shim"); err != nil {
158		return err
159	}
160
161	// Is yast2 firewall installed?
162	if _, installed := provisioner.SSHCommand("rpm -q yast2-firewall"); installed == nil {
163		// Open the firewall port required by docker
164		if _, err := provisioner.SSHCommand("sudo -E /sbin/yast2 firewall services add ipprotocol=tcp tcpport=2376 zone=EXT"); err != nil {
165			return err
166		}
167	}
168
169	log.Debug("Starting systemd docker service")
170	if err := provisioner.Service("docker", serviceaction.Start); err != nil {
171		return err
172	}
173
174	log.Debug("Waiting for docker daemon")
175	if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil {
176		return err
177	}
178
179	provisioner.AuthOptions = setRemoteAuthOptions(provisioner)
180
181	log.Debug("Configuring auth")
182	if err := ConfigureAuth(provisioner); err != nil {
183		return err
184	}
185
186	log.Debug("Configuring swarm")
187	if err := configureSwarm(provisioner, swarmOptions, provisioner.AuthOptions); err != nil {
188		return err
189	}
190
191	// enable in systemd
192	log.Debug("Enabling docker in systemd")
193	err = provisioner.Service("docker", serviceaction.Enable)
194	return err
195}
196