package provision import ( "bytes" "fmt" "io/ioutil" "strings" "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" ) func init() { Register("Debian", &RegisteredProvisioner{ New: NewDebianProvisioner, }) } func NewDebianProvisioner(d drivers.Driver) Provisioner { return &DebianProvisioner{ GenericProvisioner{ DockerOptionsDir: "/etc/docker", DaemonOptionsFile: "/lib/systemd/system/docker.service", OsReleaseId: "debian", Packages: []string{ "curl", }, Driver: d, }, } } type DebianProvisioner struct { GenericProvisioner } func (provisioner *DebianProvisioner) Service(name string, action pkgaction.ServiceAction) error { // WAT: for daemon-reload to catch config updates; systemd -- ugh 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 *DebianProvisioner) Package(name string, action pkgaction.PackageAction) error { var packageAction string updateMetadata := true switch action { case pkgaction.Install: packageAction = "install" case pkgaction.Remove: packageAction = "remove" updateMetadata = false case pkgaction.Upgrade: packageAction = "upgrade" } switch name { case "docker": name = "lxc-docker" } if updateMetadata { if _, err := provisioner.SSHCommand("sudo apt-get update"); err != nil { return err } } command := fmt.Sprintf("DEBIAN_FRONTEND=noninteractive sudo -E apt-get %s -y %s", packageAction, name) log.Debugf("package: action=%s name=%s", action.String(), name) if _, err := provisioner.SSHCommand(command); err != nil { return err } return nil } func (provisioner *DebianProvisioner) dockerDaemonResponding() bool { if _, err := provisioner.SSHCommand("sudo docker version"); err != nil { log.Warnf("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 *DebianProvisioner) Provision(swarmOptions swarm.SwarmOptions, authOptions auth.AuthOptions, engineOptions engine.EngineOptions) error { provisioner.SwarmOptions = swarmOptions provisioner.AuthOptions = authOptions provisioner.EngineOptions = engineOptions if provisioner.EngineOptions.StorageDriver == "" { provisioner.EngineOptions.StorageDriver = "aufs" } // HACK: since debian does not come with sudo by default, // we check if present and install first out, err := provisioner.SSHCommand("which sudo") if err != nil { log.Debug("sudo not found; attempting to install") // HACK: command not found which is what we are looking for; // since we only get back the string we check it :( if strings.Index(err.Error(), "Process exited with: 1") != -1 { res, err := ioutil.ReadAll(out.Stdout) if err != nil { return err } if string(res) == "" { log.Debug("installing sudo") if _, err := provisioner.SSHCommand("apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y sudo"); err != nil { return err } } } else { return err } } log.Debug("setting hostname") if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil { return err } log.Debug("installing base packages") for _, pkg := range provisioner.Packages { if err := provisioner.Package(pkg, pkgaction.Install); err != nil { return err } } log.Debug("installing docker") if err := installDockerGeneric(provisioner); err != nil { return err } log.Debug("waiting for docker daemon") if err := utils.WaitFor(provisioner.dockerDaemonResponding); err != nil { return err } log.Debug("creating options directory") if err := makeDockerOptionsDir(provisioner); err != nil { return err } // remove existing systemd config file if _, err := provisioner.SSHCommand(fmt.Sprintf("sudo rm %s", provisioner.DaemonOptionsFile)); err != nil { return err } provisioner.AuthOptions = setRemoteAuthOptions(provisioner) log.Debug("configuring auth") if err := ConfigureAuth(provisioner); err != nil { return err } log.Debug("configuring swarm") if err := configureSwarm(provisioner, swarmOptions); err != nil { return err } // enable in systemd log.Debug("enabling docker in systemd") if err := provisioner.Service("docker", pkgaction.Enable); err != nil { return err } return nil } func (provisioner *DebianProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) { var ( engineCfg bytes.Buffer ) driverNameLabel := fmt.Sprintf("provider=%s", provisioner.Driver.DriverName()) provisioner.EngineOptions.Labels = append(provisioner.EngineOptions.Labels, driverNameLabel) engineConfigTmpl := `[Unit] Description=Docker Application Container Engine Documentation=http://docs.docker.com After=network.target docker.socket Requires=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, } t.Execute(&engineCfg, engineConfigContext) return &DockerOptions{ EngineOptions: engineCfg.String(), EngineOptionsPath: provisioner.DaemonOptionsFile, }, nil }