1package provision
2
3import (
4	"fmt"
5
6	"github.com/docker/machine/libmachine/auth"
7	"github.com/docker/machine/libmachine/drivers"
8	"github.com/docker/machine/libmachine/engine"
9	"github.com/docker/machine/libmachine/log"
10	"github.com/docker/machine/libmachine/provision/pkgaction"
11	"github.com/docker/machine/libmachine/provision/serviceaction"
12	"github.com/docker/machine/libmachine/swarm"
13)
14
15var (
16	provisioners          = make(map[string]*RegisteredProvisioner)
17	detector     Detector = &StandardDetector{}
18)
19
20type SSHCommander interface {
21	// Short-hand for accessing an SSH command from the driver.
22	SSHCommand(args string) (string, error)
23}
24
25type Detector interface {
26	DetectProvisioner(d drivers.Driver) (Provisioner, error)
27}
28
29type StandardDetector struct{}
30
31func SetDetector(newDetector Detector) {
32	detector = newDetector
33}
34
35// Provisioner defines distribution specific actions
36type Provisioner interface {
37	fmt.Stringer
38	SSHCommander
39
40	// Create the files for the daemon to consume configuration settings (return struct of content and path)
41	GenerateDockerOptions(dockerPort int) (*DockerOptions, error)
42
43	// Get the directory where the settings files for docker are to be found
44	GetDockerOptionsDir() string
45
46	// Return the auth options used to configure remote connection for the daemon.
47	GetAuthOptions() auth.Options
48
49	// Get the swarm options associated with this host.
50	GetSwarmOptions() swarm.Options
51
52	// Run a package action e.g. install
53	Package(name string, action pkgaction.PackageAction) error
54
55	// Get Hostname
56	Hostname() (string, error)
57
58	// Set hostname
59	SetHostname(hostname string) error
60
61	// Figure out if this is the right provisioner to use based on /etc/os-release info
62	CompatibleWithHost() bool
63
64	// Do the actual provisioning piece:
65	//     1. Set the hostname on the instance.
66	//     2. Install Docker if it is not present.
67	//     3. Configure the daemon to accept connections over TLS.
68	//     4. Copy the needed certificates to the server and local config dir.
69	//     5. Configure / activate swarm if applicable.
70	Provision(swarmOptions swarm.Options, authOptions auth.Options, engineOptions engine.Options) error
71
72	// Perform action on a named service e.g. stop
73	Service(name string, action serviceaction.ServiceAction) error
74
75	// Get the driver which is contained in the provisioner.
76	GetDriver() drivers.Driver
77
78	// Set the OS Release info depending on how it's represented
79	// internally
80	SetOsReleaseInfo(info *OsRelease)
81
82	// Get the OS Release info for the current provisioner
83	GetOsReleaseInfo() (*OsRelease, error)
84}
85
86// RegisteredProvisioner creates a new provisioner
87type RegisteredProvisioner struct {
88	New func(d drivers.Driver) Provisioner
89}
90
91func Register(name string, p *RegisteredProvisioner) {
92	provisioners[name] = p
93}
94
95func DetectProvisioner(d drivers.Driver) (Provisioner, error) {
96	return detector.DetectProvisioner(d)
97}
98
99func (detector StandardDetector) DetectProvisioner(d drivers.Driver) (Provisioner, error) {
100	log.Info("Waiting for SSH to be available...")
101	if err := drivers.WaitForSSH(d); err != nil {
102		return nil, err
103	}
104
105	log.Info("Detecting the provisioner...")
106
107	osReleaseOut, err := drivers.RunSSHCommandFromDriver(d, "cat /etc/os-release")
108	if err != nil {
109		return nil, fmt.Errorf("Error getting SSH command: %s", err)
110	}
111
112	osReleaseInfo, err := NewOsRelease([]byte(osReleaseOut))
113	if err != nil {
114		return nil, fmt.Errorf("Error parsing /etc/os-release file: %s", err)
115	}
116
117	for _, p := range provisioners {
118		provisioner := p.New(d)
119		provisioner.SetOsReleaseInfo(osReleaseInfo)
120
121		if provisioner.CompatibleWithHost() {
122			log.Debugf("found compatible host: %s", osReleaseInfo.ID)
123			return provisioner, nil
124		}
125	}
126
127	return nil, ErrDetectionFailed
128}
129