From f3689349a40da872eb3e7ad025fc82aa8e06527b Mon Sep 17 00:00:00 2001 From: Kunal Kushwaha Date: Fri, 27 Nov 2015 17:48:35 +0900 Subject: [PATCH] Host provisioing using Docker API Replaces the code of provisioing with Go Templates with DockerAPI Signed-off-by: Kunal Kushwaha --- libmachine/provision/configure_swarm.go | 153 +++++++++++------------- libmachine/provision/docker.go | 53 ++++++++ 2 files changed, 122 insertions(+), 84 deletions(-) create mode 100644 libmachine/provision/docker.go diff --git a/libmachine/provision/configure_swarm.go b/libmachine/provision/configure_swarm.go index 256e116c22..3576776648 100644 --- a/libmachine/provision/configure_swarm.go +++ b/libmachine/provision/configure_swarm.go @@ -1,52 +1,16 @@ package provision import ( - "bytes" "fmt" "net/url" "strings" - "text/template" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/swarm" + "github.com/samalba/dockerclient" ) -type SwarmCommandContext struct { - ContainerName string - Env []string - DockerDir string - DockerPort int - IP string - Port string - AuthOptions auth.Options - SwarmOptions swarm.Options - SwarmImage string -} - -// Wrapper function to generate a docker run swarm command (manage or join) -// from a template/context and execute it. -func runSwarmCommandFromTemplate(p Provisioner, cmdTmpl string, swarmCmdContext SwarmCommandContext) error { - var ( - executedCmdTmpl bytes.Buffer - ) - - parsedMasterCmdTemplate, err := template.New("swarmMasterCmd").Parse(cmdTmpl) - if err != nil { - return err - } - - parsedMasterCmdTemplate.Execute(&executedCmdTmpl, swarmCmdContext) - - log.Debugf("The swarm command being run is: %s", executedCmdTmpl.String()) - - if _, err := p.SSHCommand(executedCmdTmpl.String()); err != nil { - return err - } - - return nil -} - func configureSwarm(p Provisioner, swarmOptions swarm.Options, authOptions auth.Options) error { if !swarmOptions.IsSwarm { return nil @@ -67,60 +31,81 @@ func configureSwarm(p Provisioner, swarmOptions swarm.Options, authOptions auth. parts := strings.Split(u.Host, ":") port := parts[1] + dockerPort := "2376" dockerDir := p.GetDockerOptionsDir() - - swarmCmdContext := SwarmCommandContext{ - ContainerName: "", - Env: swarmOptions.Env, - DockerDir: dockerDir, - DockerPort: 2376, - IP: ip, - Port: port, - AuthOptions: authOptions, - SwarmOptions: swarmOptions, - SwarmImage: swarmOptions.Image, - } - - // First things first, get the swarm image. - if _, err := p.SSHCommand(fmt.Sprintf("sudo docker pull %s", swarmOptions.Image)); err != nil { - return err - } - - swarmMasterCmdTemplate := `sudo docker run -d \ ---restart=always \ ---net=host \ -{{range .Env}} -e {{.}}{{end}} \ ---name swarm-agent-master \ --p {{.Port}}:{{.Port}} \ --v {{.DockerDir}}:{{.DockerDir}} \ -{{.SwarmImage}} \ -manage \ ---tlsverify \ ---tlscacert={{.AuthOptions.CaCertRemotePath}} \ ---tlscert={{.AuthOptions.ServerCertRemotePath}} \ ---tlskey={{.AuthOptions.ServerKeyRemotePath}} \ --H {{.SwarmOptions.Host}} \ ---strategy {{.SwarmOptions.Strategy}} --advertise {{.IP}}:{{.DockerPort}} {{range .SwarmOptions.ArbitraryFlags}} --{{.}}{{end}} {{.SwarmOptions.Discovery}} -` - - swarmWorkerCmdTemplate := `sudo docker run -d \ ---restart=always \ ---net=host \ -{{range .Env}} -e {{.}}{{end}} \ ---name swarm-agent \ -{{.SwarmImage}} \ -join --advertise {{.IP}}:{{.DockerPort}} {{.SwarmOptions.Discovery}} -` + dockerHost := fmt.Sprintf("tcp://%s:%s", ip, dockerPort) + dockerClient := DockerClient{dockerHost, authOptions} + advertiseInfo := fmt.Sprintf("%s:%s", ip, dockerPort) if swarmOptions.Master { - log.Debug("Launching swarm master") - if err := runSwarmCommandFromTemplate(p, swarmMasterCmdTemplate, swarmCmdContext); err != nil { + + cmd := fmt.Sprintf("manage --tlsverify --tlscacert=%s --tlscert=%s --tlskey=%s -H %s --strategy %s --advertise %s", + authOptions.CaCertRemotePath, + authOptions.ServerCertRemotePath, + authOptions.ServerKeyRemotePath, + swarmOptions.Host, + swarmOptions.Strategy, + advertiseInfo, + ) + + cmdMaster := strings.Fields(cmd) + for _, option := range swarmOptions.ArbitraryFlags { + cmdMaster = append(cmdMaster, "--"+option) + } + + //Discovery must be at end of command + cmdMaster = append(cmdMaster, swarmOptions.Discovery) + + hostBind := fmt.Sprintf("%s:%s", dockerDir, dockerDir) + masterHostConfig := dockerclient.HostConfig{ + RestartPolicy: dockerclient.RestartPolicy{ + Name: "Always", + MaximumRetryCount: 0, + }, + Binds: []string{hostBind}, + PortBindings: map[string][]dockerclient.PortBinding{"3376/tcp": {{"", port}}}, + NetworkMode: "host", + } + + swarmMasterConfig := &dockerclient.ContainerConfig{ + Image: swarmOptions.Image, + Env: swarmOptions.Env, + ExposedPorts: map[string]struct{}{ + "2375/tcp": {}, + "3376/tcp": {}, + }, + Cmd: cmdMaster, + HostConfig: masterHostConfig, + } + + err = CreateContainer(dockerClient, swarmMasterConfig, "swarm-agent-master") + if err != nil { return err } + } - log.Debug("Launch swarm worker") - if err := runSwarmCommandFromTemplate(p, swarmWorkerCmdTemplate, swarmCmdContext); err != nil { + workerHostConfig := dockerclient.HostConfig{ + RestartPolicy: dockerclient.RestartPolicy{ + Name: "Always", + MaximumRetryCount: 0, + }, + NetworkMode: "host", + } + + swarmWorkerConfig := &dockerclient.ContainerConfig{ + Image: swarmOptions.Image, + Env: swarmOptions.Env, + Cmd: []string{ + "join", + "--advertise", + advertiseInfo, + swarmOptions.Discovery, + }, + HostConfig: workerHostConfig, + } + + if err = CreateContainer(dockerClient, swarmWorkerConfig, "swarm-agent"); err != nil { return err } diff --git a/libmachine/provision/docker.go b/libmachine/provision/docker.go new file mode 100644 index 0000000000..754c36ab2e --- /dev/null +++ b/libmachine/provision/docker.go @@ -0,0 +1,53 @@ +package provision + +import ( + "fmt" + + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/mcndockerclient" + "github.com/samalba/dockerclient" +) + +// DockerClient implements DockerHost(mcndockerclient) interface +type DockerClient struct { + HostURL string + AuthOption auth.Options +} + +// URL returns the Docker host URL +func (dc DockerClient) URL() (string, error) { + if dc.HostURL == "" { + return "", fmt.Errorf("Docker Host URL not set") + } + + return dc.HostURL, nil +} + +// AuthOptions returns the authOptions +func (dc DockerClient) AuthOptions() *auth.Options { + return &dc.AuthOption +} + +//CreateContainer creates a docker container. +func CreateContainer(dockerHost DockerClient, config *dockerclient.ContainerConfig, name string) error { + + docker, err := mcndockerclient.DockerClient(dockerHost) + if err != nil { + return err + } + + if err = docker.PullImage(config.Image, nil); err != nil { + return fmt.Errorf("Unable to Pull Image: %s", err) + } + + containerID, err := docker.CreateContainer(config, name) + if err != nil { + return fmt.Errorf("Error while creating container: %s", err) + } + + if err = docker.StartContainer(containerID, &config.HostConfig); err != nil { + return fmt.Errorf("Error while starting container: %s", err) + } + + return nil +}