1package provision 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "regexp" 8 "text/template" 9 10 "github.com/docker/machine/libmachine/auth" 11 "github.com/docker/machine/libmachine/drivers" 12 "github.com/docker/machine/libmachine/engine" 13 "github.com/docker/machine/libmachine/log" 14 "github.com/docker/machine/libmachine/mcnutils" 15 "github.com/docker/machine/libmachine/provision/pkgaction" 16 "github.com/docker/machine/libmachine/provision/serviceaction" 17 "github.com/docker/machine/libmachine/swarm" 18) 19 20var ( 21 ErrUnknownYumOsRelease = errors.New("unknown OS for Yum repository") 22 23 packageListTemplate = `[docker] 24name=Docker Stable Repository 25baseurl=https://yum.dockerproject.org/repo/main/{{.OsRelease}}/{{.OsReleaseVersion}} 26priority=1 27enabled=1 28gpgkey=https://yum.dockerproject.org/gpg 29` 30 engineConfigTemplate = `[Unit] 31Description=Docker Application Container Engine 32After=network.target 33 34[Service] 35Type=notify 36ExecStart=/usr/bin/dockerd -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 }} 37ExecReload=/bin/kill -s HUP $MAINPID 38MountFlags=slave 39LimitNOFILE=infinity 40LimitNPROC=infinity 41LimitCORE=infinity 42TimeoutStartSec=0 43Delegate=yes 44KillMode=process 45Environment={{range .EngineOptions.Env}}{{ printf "%q" . }} {{end}} 46 47[Install] 48WantedBy=multi-user.target 49` 50 51 majorVersionRE = regexp.MustCompile(`^(\d+)(\..*)?`) 52) 53 54type PackageListInfo struct { 55 OsRelease string 56 OsReleaseVersion string 57} 58 59func init() { 60 Register("RedHat", &RegisteredProvisioner{ 61 New: func(d drivers.Driver) Provisioner { 62 return NewRedHatProvisioner("rhel", d) 63 }, 64 }) 65} 66 67func NewRedHatProvisioner(osReleaseID string, d drivers.Driver) *RedHatProvisioner { 68 systemdProvisioner := NewSystemdProvisioner(osReleaseID, d) 69 systemdProvisioner.SSHCommander = RedHatSSHCommander{Driver: d} 70 return &RedHatProvisioner{ 71 systemdProvisioner, 72 } 73} 74 75type RedHatProvisioner struct { 76 SystemdProvisioner 77} 78 79func (provisioner *RedHatProvisioner) String() string { 80 return "redhat" 81} 82 83func (provisioner *RedHatProvisioner) SetHostname(hostname string) error { 84 // we have to have SetHostname here as well to use the RedHat provisioner 85 // SSHCommand to add the tty allocation 86 if _, err := provisioner.SSHCommand(fmt.Sprintf( 87 "sudo hostname %s && echo %q | sudo tee /etc/hostname", 88 hostname, 89 hostname, 90 )); err != nil { 91 return err 92 } 93 94 if _, err := provisioner.SSHCommand(fmt.Sprintf( 95 "if grep -xq 127.0.1.1.* /etc/hosts; then sudo sed -i 's/^127.0.1.1.*/127.0.1.1 %s/g' /etc/hosts; else echo '127.0.1.1 %s' | sudo tee -a /etc/hosts; fi", 96 hostname, 97 hostname, 98 )); err != nil { 99 return err 100 } 101 102 return nil 103} 104 105func (provisioner *RedHatProvisioner) Package(name string, action pkgaction.PackageAction) error { 106 var packageAction string 107 108 switch action { 109 case pkgaction.Install: 110 packageAction = "install" 111 case pkgaction.Remove: 112 packageAction = "remove" 113 case pkgaction.Upgrade: 114 packageAction = "upgrade" 115 } 116 117 command := fmt.Sprintf("sudo -E yum %s -y %s", packageAction, name) 118 119 if _, err := provisioner.SSHCommand(command); err != nil { 120 return err 121 } 122 123 return nil 124} 125 126func installDocker(provisioner *RedHatProvisioner) error { 127 if err := installDockerGeneric(provisioner, provisioner.EngineOptions.InstallURL); err != nil { 128 return err 129 } 130 131 if err := provisioner.Service("docker", serviceaction.Restart); err != nil { 132 return err 133 } 134 135 if err := provisioner.Service("docker", serviceaction.Enable); err != nil { 136 return err 137 } 138 139 return nil 140} 141 142func (provisioner *RedHatProvisioner) dockerDaemonResponding() bool { 143 log.Debug("checking docker daemon") 144 145 if out, err := provisioner.SSHCommand("sudo docker version"); err != nil { 146 log.Warnf("Error getting SSH command to check if the daemon is up: %s", err) 147 log.Debugf("'sudo docker version' output:\n%s", out) 148 return false 149 } 150 151 // The daemon is up if the command worked. Carry on. 152 return true 153} 154 155func (provisioner *RedHatProvisioner) Provision(swarmOptions swarm.Options, authOptions auth.Options, engineOptions engine.Options) error { 156 provisioner.SwarmOptions = swarmOptions 157 provisioner.AuthOptions = authOptions 158 provisioner.EngineOptions = engineOptions 159 swarmOptions.Env = engineOptions.Env 160 161 // set default storage driver for redhat 162 storageDriver, err := decideStorageDriver(provisioner, "devicemapper", engineOptions.StorageDriver) 163 if err != nil { 164 return err 165 } 166 provisioner.EngineOptions.StorageDriver = storageDriver 167 168 if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil { 169 return err 170 } 171 172 for _, pkg := range provisioner.Packages { 173 log.Debugf("installing base package: name=%s", pkg) 174 if err := provisioner.Package(pkg, pkgaction.Install); err != nil { 175 return err 176 } 177 } 178 179 // update OS -- this is needed for libdevicemapper and the docker install 180 if _, err := provisioner.SSHCommand("sudo -E yum -y update -x docker-*"); err != nil { 181 return err 182 } 183 184 // install docker 185 if err := installDocker(provisioner); err != nil { 186 return err 187 } 188 189 if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil { 190 return err 191 } 192 193 if err := makeDockerOptionsDir(provisioner); err != nil { 194 return err 195 } 196 197 provisioner.AuthOptions = setRemoteAuthOptions(provisioner) 198 199 if err := ConfigureAuth(provisioner); err != nil { 200 return err 201 } 202 203 if err := configureSwarm(provisioner, swarmOptions, provisioner.AuthOptions); err != nil { 204 return err 205 } 206 207 return nil 208} 209 210func (provisioner *RedHatProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) { 211 var ( 212 engineCfg bytes.Buffer 213 configPath = provisioner.DaemonOptionsFile 214 ) 215 216 driverNameLabel := fmt.Sprintf("provider=%s", provisioner.Driver.DriverName()) 217 provisioner.EngineOptions.Labels = append(provisioner.EngineOptions.Labels, driverNameLabel) 218 219 // systemd / redhat will not load options if they are on newlines 220 // instead, it just continues with a different set of options; yeah... 221 t, err := template.New("engineConfig").Parse(engineConfigTemplate) 222 if err != nil { 223 return nil, err 224 } 225 226 engineConfigContext := EngineConfigContext{ 227 DockerPort: dockerPort, 228 AuthOptions: provisioner.AuthOptions, 229 EngineOptions: provisioner.EngineOptions, 230 DockerOptionsDir: provisioner.DockerOptionsDir, 231 } 232 233 t.Execute(&engineCfg, engineConfigContext) 234 235 daemonOptsDir := configPath 236 return &DockerOptions{ 237 EngineOptions: engineCfg.String(), 238 EngineOptionsPath: daemonOptsDir, 239 }, nil 240} 241