package provision import ( "bytes" "fmt" "text/template" "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/engine" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/log" "github.com/docker/machine/utils" ) const ( // TODO: eventually the RPM install process will be integrated // into the get.docker.com install script; for now // we install via vendored RPMs dockerRPMPath = "https://docker-mcn.s3.amazonaws.com/public/redhat/1.6.0/docker-engine-1.6.1-0.0.20150511.171646.git1b47f9f.el7.centos.x86_64.rpm" ) func init() { Register("RedHat", &RegisteredProvisioner{ New: NewRedHatProvisioner, }) } func NewRedHatProvisioner(d drivers.Driver) Provisioner { return &RedHatProvisioner{ GenericProvisioner{ DockerOptionsDir: "/etc/docker", DaemonOptionsFile: "/lib/systemd/system/docker.service", OsReleaseId: "rhel", Packages: []string{ "curl", }, Driver: d, }, } } type RedHatProvisioner struct { GenericProvisioner } func (provisioner *RedHatProvisioner) Service(name string, action pkgaction.ServiceAction) error { reloadDaemon := false switch action { case pkgaction.Start, pkgaction.Restart: reloadDaemon = true } // systemd needs reloaded when config changes on disk; we cannot // be sure exactly when it changes from the provisioner so // we call a reload on every restart to be safe if reloadDaemon { if _, err := provisioner.SSHCommand("sudo systemctl daemon-reload"); err != nil { return err } } command := fmt.Sprintf("sudo systemctl %s %s", action.String(), name) if _, err := provisioner.SSHCommand(command); err != nil { return err } return nil } func (provisioner *RedHatProvisioner) Package(name string, action pkgaction.PackageAction) error { var packageAction string switch action { case pkgaction.Install: packageAction = "install" case pkgaction.Remove: packageAction = "remove" case pkgaction.Upgrade: packageAction = "upgrade" } command := fmt.Sprintf("sudo -E yum %s -y %s", packageAction, name) if _, err := provisioner.SSHCommand(command); err != nil { return err } return nil } func (provisioner *RedHatProvisioner) isAWS() bool { if _, err := provisioner.SSHCommand("curl -s http://169.254.169.254/latest/meta-data/ami-id"); err != nil { return false } return true } func (provisioner *RedHatProvisioner) configureRepos() error { log.Debug("configuring extra repo") repoCmd := "subscription-manager repos --enable=rhel-7-server-extras-rpms" if provisioner.isAWS() { repoCmd = "yum-config-manager --enable rhui-REGION-rhel-server-extras" } if _, err := provisioner.SSHCommand(fmt.Sprintf("sudo %s", repoCmd)); err != nil { return err } return nil } func installDocker(provisioner *RedHatProvisioner) error { if err := provisioner.installOfficialDocker(); err != nil { return err } if err := provisioner.Service("docker", pkgaction.Restart); err != nil { return err } if err := provisioner.Service("docker", pkgaction.Enable); err != nil { return err } return nil } func (provisioner *RedHatProvisioner) installOfficialDocker() error { log.Debug("installing docker") if _, err := provisioner.SSHCommand(fmt.Sprintf("sudo yum install -y --nogpgcheck %s", dockerRPMPath)); err != nil { return err } return nil } func (provisioner *RedHatProvisioner) dockerDaemonResponding() bool { if _, err := provisioner.SSHCommand("sudo docker version"); err != nil { log.Warn("Error getting SSH command to check if the daemon is up: %s", err) return false } // The daemon is up if the command worked. Carry on. return true } func (provisioner *RedHatProvisioner) Provision(swarmOptions swarm.SwarmOptions, authOptions auth.AuthOptions, engineOptions engine.EngineOptions) error { provisioner.SwarmOptions = swarmOptions provisioner.AuthOptions = authOptions provisioner.EngineOptions = engineOptions // set default storage driver for redhat if provisioner.EngineOptions.StorageDriver == "" { provisioner.EngineOptions.StorageDriver = "devicemapper" } if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil { return err } // setup extras repo if err := provisioner.configureRepos(); err != nil { return err } for _, pkg := range provisioner.Packages { if err := provisioner.Package(pkg, pkgaction.Install); err != nil { return err } } // install docker if err := installDocker(provisioner); err != nil { return err } if err := utils.WaitFor(provisioner.dockerDaemonResponding); err != nil { return err } if err := makeDockerOptionsDir(provisioner); err != nil { return err } provisioner.AuthOptions = setRemoteAuthOptions(provisioner) if err := ConfigureAuth(provisioner); err != nil { return err } if err := configureSwarm(provisioner, swarmOptions); err != nil { return err } return nil } func (provisioner *RedHatProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) { var ( engineCfg bytes.Buffer configPath = provisioner.DaemonOptionsFile ) // remove existing if _, err := provisioner.SSHCommand(fmt.Sprintf("sudo rm %s", configPath)); err != nil { return nil, err } driverNameLabel := fmt.Sprintf("provider=%s", provisioner.Driver.DriverName()) provisioner.EngineOptions.Labels = append(provisioner.EngineOptions.Labels, driverNameLabel) // systemd / redhat will not load options if they are on newlines // instead, it just continues with a different set of options; yeah... engineConfigTmpl := `[Unit] Description=Docker Application Container Engine Documentation=http://docs.docker.com After=network.target docker.socket Required=docker.socket [Service] 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 }} MountFlags=slave LimitNOFILE=1048576 LimitNPROC=1048576 LimitCORE=infinity [Install] WantedBy=multi-user.target ` t, err := template.New("engineConfig").Parse(engineConfigTmpl) if err != nil { return nil, err } engineConfigContext := EngineConfigContext{ DockerPort: dockerPort, AuthOptions: provisioner.AuthOptions, EngineOptions: provisioner.EngineOptions, DockerOptionsDir: provisioner.DockerOptionsDir, } t.Execute(&engineCfg, engineConfigContext) daemonOptsDir := configPath return &DockerOptions{ EngineOptions: engineCfg.String(), EngineOptionsPath: daemonOptsDir, }, nil }