mirror of https://github.com/docker/docs.git
Add SSH client
Signed-off-by: Simon Thulbourn <simon+github@thulbourn.com>
This commit is contained in:
parent
191564564e
commit
709b0a84e3
|
@ -1,19 +1,20 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/ssh"
|
||||
)
|
||||
|
||||
func cmdSsh(c *cli.Context) {
|
||||
var (
|
||||
err error
|
||||
sshCmd *exec.Cmd
|
||||
err error
|
||||
)
|
||||
name := c.Args().First()
|
||||
|
||||
|
@ -59,19 +60,18 @@ func cmdSsh(c *cli.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
var output ssh.Output
|
||||
|
||||
if len(c.Args()) <= 1 {
|
||||
sshCmd, err = host.GetSSHCommand()
|
||||
err = host.CreateSSHShell()
|
||||
} else {
|
||||
sshCmd, err = host.GetSSHCommand(c.Args()[1:]...)
|
||||
output, err = host.RunSSHCommand(strings.Join(c.Args()[1:], " "))
|
||||
|
||||
io.Copy(os.Stderr, output.Stderr)
|
||||
io.Copy(os.Stdout, output.Stdout)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sshCmd.Stdin = os.Stdin
|
||||
sshCmd.Stdout = os.Stdout
|
||||
sshCmd.Stderr = os.Stderr
|
||||
if err := sshCmd.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package drivers
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"sort"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
@ -174,21 +173,32 @@ type DriverOptions interface {
|
|||
Bool(key string) bool
|
||||
}
|
||||
|
||||
func GetSSHCommandFromDriver(d Driver, args ...string) (*exec.Cmd, error) {
|
||||
func RunSSHCommandFromDriver(d Driver, args string) (ssh.Output, error) {
|
||||
var output ssh.Output
|
||||
|
||||
host, err := d.GetSSHHostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return output, err
|
||||
}
|
||||
|
||||
port, err := d.GetSSHPort()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return output, err
|
||||
}
|
||||
|
||||
user := d.GetSSHUsername()
|
||||
keyPath := d.GetSSHKeyPath()
|
||||
|
||||
return ssh.GetSSHCommand(host, port, user, keyPath, args...), nil
|
||||
auth := &ssh.Auth{
|
||||
Keys: []string{keyPath},
|
||||
}
|
||||
|
||||
client, err := ssh.NewClient(user, host, port, auth)
|
||||
if err != nil {
|
||||
return output, err
|
||||
}
|
||||
|
||||
return client.Run(args)
|
||||
}
|
||||
|
||||
func MachineInState(d Driver, desiredState state.State) func() bool {
|
||||
|
|
|
@ -234,9 +234,17 @@ func (c *ComputeUtil) deleteInstance() error {
|
|||
|
||||
func (c *ComputeUtil) executeCommands(commands []string, ip, sshKeyPath string) error {
|
||||
for _, command := range commands {
|
||||
cmd := ssh.GetSSHCommand(ip, 22, c.userName, sshKeyPath, command)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("error executing command: %v %v", command, err)
|
||||
auth := &ssh.Auth{
|
||||
Keys: []string{sshKeyPath},
|
||||
}
|
||||
|
||||
client, err := ssh.NewClient(c.userName, ip, 22, auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := client.Run(command); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -452,19 +452,17 @@ func (d *Driver) GetIP() (string, error) {
|
|||
if s != state.Running {
|
||||
return "", drivers.ErrHostIsNotRunning
|
||||
}
|
||||
cmd, err := drivers.GetSSHCommandFromDriver(d, "ip addr show dev eth1")
|
||||
output, err := drivers.RunSSHCommandFromDriver(d, "ip addr show dev eth1")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// reset to nil as if using from Host Stdout is already set when using DEBUG
|
||||
cmd.Stdout = nil
|
||||
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
var buf bytes.Buffer
|
||||
if _, err := buf.ReadFrom(output.Stdout); err != nil {
|
||||
return "", err
|
||||
}
|
||||
out := string(b)
|
||||
|
||||
out := buf.String()
|
||||
log.Debugf("SSH returned: %s\nEND SSH\n", out)
|
||||
// parse to find: inet 192.168.59.103/24 brd 192.168.59.255 scope global eth1
|
||||
lines := strings.Split(out, "\n")
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
|
@ -127,23 +126,49 @@ func (h *Host) Create(name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Host) GetSSHCommand(args ...string) (*exec.Cmd, error) {
|
||||
func (h *Host) RunSSHCommand(command string) (ssh.Output, error) {
|
||||
var output ssh.Output
|
||||
|
||||
addr, err := h.Driver.GetSSHHostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return output, err
|
||||
}
|
||||
|
||||
user := h.Driver.GetSSHUsername()
|
||||
|
||||
port, err := h.Driver.GetSSHPort()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return output, err
|
||||
}
|
||||
|
||||
keyPath := h.Driver.GetSSHKeyPath()
|
||||
auth := &ssh.Auth{
|
||||
Keys: []string{h.Driver.GetSSHKeyPath()},
|
||||
}
|
||||
|
||||
cmd := ssh.GetSSHCommand(addr, port, user, keyPath, args...)
|
||||
return cmd, nil
|
||||
client, err := ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth)
|
||||
|
||||
return client.Run(command)
|
||||
}
|
||||
|
||||
func (h *Host) CreateSSHShell() error {
|
||||
addr, err := h.Driver.GetSSHHostname()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := h.Driver.GetSSHPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auth := &ssh.Auth{
|
||||
Keys: []string{h.Driver.GetSSHKeyPath()},
|
||||
}
|
||||
|
||||
client, err := ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.Shell()
|
||||
}
|
||||
|
||||
func (h *Host) Start() error {
|
||||
|
@ -326,15 +351,11 @@ func sshAvailableFunc(h *Host) func() bool {
|
|||
log.Debugf("Error waiting for TCP waiting for SSH: %s", err)
|
||||
return false
|
||||
}
|
||||
cmd, err := h.GetSSHCommand("exit 0")
|
||||
if err != nil {
|
||||
|
||||
if _, err := h.RunSSHCommand("exit 0"); err != nil {
|
||||
log.Debugf("Error getting ssh command 'exit 0' : %s", err)
|
||||
return false
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Debugf("Error running ssh command 'exit 0' : %s", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
@ -12,6 +11,7 @@ import (
|
|||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/provision/pkgaction"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/docker/machine/ssh"
|
||||
"github.com/docker/machine/state"
|
||||
"github.com/docker/machine/utils"
|
||||
)
|
||||
|
@ -36,16 +36,13 @@ type Boot2DockerProvisioner struct {
|
|||
|
||||
func (provisioner *Boot2DockerProvisioner) Service(name string, action pkgaction.ServiceAction) error {
|
||||
var (
|
||||
cmd *exec.Cmd
|
||||
err error
|
||||
)
|
||||
cmd, err = provisioner.SSHCommand(fmt.Sprintf("sudo /etc/init.d/%s %s", name, action.String()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
|
||||
if _, err = provisioner.SSHCommand(fmt.Sprintf("sudo /etc/init.d/%s %s", name, action.String())); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -101,15 +98,13 @@ func (provisioner *Boot2DockerProvisioner) Package(name string, action pkgaction
|
|||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) Hostname() (string, error) {
|
||||
cmd, err := provisioner.SSHCommand(fmt.Sprintf("hostname"))
|
||||
output, err := provisioner.SSHCommand(fmt.Sprintf("hostname"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var so bytes.Buffer
|
||||
cmd.Stdout = &so
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err := so.ReadFrom(output.Stdout); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
@ -117,16 +112,15 @@ func (provisioner *Boot2DockerProvisioner) Hostname() (string, error) {
|
|||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) SetHostname(hostname string) error {
|
||||
cmd, err := provisioner.SSHCommand(fmt.Sprintf(
|
||||
if _, err := provisioner.SSHCommand(fmt.Sprintf(
|
||||
"sudo hostname %s && echo %q | sudo tee /var/lib/boot2docker/etc/hostname",
|
||||
hostname,
|
||||
hostname,
|
||||
))
|
||||
if err != nil {
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cmd.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) GetDockerOptionsDir() string {
|
||||
|
@ -188,8 +182,8 @@ func (provisioner *Boot2DockerProvisioner) Provision(swarmOptions swarm.SwarmOpt
|
|||
return nil
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) SSHCommand(args ...string) (*exec.Cmd, error) {
|
||||
return drivers.GetSSHCommandFromDriver(provisioner.Driver, args...)
|
||||
func (provisioner *Boot2DockerProvisioner) SSHCommand(args string) (ssh.Output, error) {
|
||||
return drivers.RunSSHCommandFromDriver(provisioner.Driver, args)
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) GetDriver() drivers.Driver {
|
||||
|
|
|
@ -3,12 +3,12 @@ package provision
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/provision/pkgaction"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/docker/machine/ssh"
|
||||
)
|
||||
|
||||
var provisioners = make(map[string]*RegisteredProvisioner)
|
||||
|
@ -48,7 +48,7 @@ type Provisioner interface {
|
|||
GetDriver() drivers.Driver
|
||||
|
||||
// Short-hand for accessing an SSH command from the driver.
|
||||
SSHCommand(args ...string) (*exec.Cmd, error)
|
||||
SSHCommand(args string) (ssh.Output, error)
|
||||
|
||||
// Set the OS Release info depending on how it's represented
|
||||
// internally
|
||||
|
@ -68,18 +68,13 @@ func DetectProvisioner(d drivers.Driver) (Provisioner, error) {
|
|||
var (
|
||||
osReleaseOut bytes.Buffer
|
||||
)
|
||||
catOsReleaseCmd, err := drivers.GetSSHCommandFromDriver(d, "cat /etc/os-release")
|
||||
catOsReleaseOutput, err := drivers.RunSSHCommandFromDriver(d, "cat /etc/os-release")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting SSH command: %s", err)
|
||||
}
|
||||
|
||||
// Normally I would just use Output() for this, but d.GetSSHCommand
|
||||
// defaults to sending the output of the command to stdout in debug
|
||||
// mode, so that will be broken if we don't set it ourselves.
|
||||
catOsReleaseCmd.Stdout = &osReleaseOut
|
||||
|
||||
if err := catOsReleaseCmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("Error running SSH command to get /etc/os-release: %s", err)
|
||||
if _, err := osReleaseOut.ReadFrom(catOsReleaseOutput.Stdout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
osReleaseInfo, err := NewOsRelease(osReleaseOut.Bytes())
|
||||
|
|
|
@ -3,13 +3,13 @@ package provision
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/provision/pkgaction"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/docker/machine/ssh"
|
||||
"github.com/docker/machine/utils"
|
||||
)
|
||||
|
||||
|
@ -38,12 +38,7 @@ type UbuntuProvisioner struct {
|
|||
func (provisioner *UbuntuProvisioner) Service(name string, action pkgaction.ServiceAction) error {
|
||||
command := fmt.Sprintf("sudo service %s %s", name, action.String())
|
||||
|
||||
cmd, err := provisioner.SSHCommand(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err := provisioner.SSHCommand(command); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -70,12 +65,7 @@ func (provisioner *UbuntuProvisioner) Package(name string, action pkgaction.Pack
|
|||
|
||||
command := fmt.Sprintf("DEBIAN_FRONTEND=noninteractive sudo -E apt-get %s -y %s", packageAction, name)
|
||||
|
||||
cmd, err := provisioner.SSHCommand(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err := provisioner.SSHCommand(command); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -83,15 +73,10 @@ func (provisioner *UbuntuProvisioner) Package(name string, action pkgaction.Pack
|
|||
}
|
||||
|
||||
func (provisioner *UbuntuProvisioner) dockerDaemonResponding() bool {
|
||||
cmd, err := provisioner.SSHCommand("sudo docker version")
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Debug("Error checking for daemon up: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// The daemon is up if the command worked. Carry on.
|
||||
return true
|
||||
|
@ -128,15 +113,13 @@ func (provisioner *UbuntuProvisioner) Provision(swarmOptions swarm.SwarmOptions,
|
|||
}
|
||||
|
||||
func (provisioner *UbuntuProvisioner) Hostname() (string, error) {
|
||||
cmd, err := provisioner.SSHCommand("hostname")
|
||||
output, err := provisioner.SSHCommand("hostname")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var so bytes.Buffer
|
||||
cmd.Stdout = &so
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err := so.ReadFrom(output.Stdout); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
@ -144,26 +127,24 @@ func (provisioner *UbuntuProvisioner) Hostname() (string, error) {
|
|||
}
|
||||
|
||||
func (provisioner *UbuntuProvisioner) SetHostname(hostname string) error {
|
||||
cmd, err := provisioner.SSHCommand(fmt.Sprintf(
|
||||
if _, err := provisioner.SSHCommand(fmt.Sprintf(
|
||||
"sudo hostname %s && echo %q | sudo tee /etc/hostname && echo \"127.0.0.1 %s\" | sudo tee -a /etc/hosts",
|
||||
hostname,
|
||||
hostname,
|
||||
hostname,
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cmd.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provisioner *UbuntuProvisioner) GetDockerOptionsDir() string {
|
||||
return "/etc/docker"
|
||||
}
|
||||
|
||||
func (provisioner *UbuntuProvisioner) SSHCommand(args ...string) (*exec.Cmd, error) {
|
||||
return drivers.GetSSHCommandFromDriver(provisioner.Driver, args...)
|
||||
func (provisioner *UbuntuProvisioner) SSHCommand(args string) (ssh.Output, error) {
|
||||
return drivers.RunSSHCommandFromDriver(provisioner.Driver, args)
|
||||
}
|
||||
|
||||
func (provisioner *UbuntuProvisioner) CompatibleWithHost() bool {
|
||||
|
|
|
@ -25,18 +25,13 @@ type DockerOptions struct {
|
|||
func installDockerGeneric(p Provisioner) error {
|
||||
// install docker - until cloudinit we use ubuntu everywhere so we
|
||||
// just install it using the docker repos
|
||||
cmd, err := p.SSHCommand("if ! type docker; then curl -sSL https://get.docker.com | sh -; fi")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if output, err := p.SSHCommand("if ! type docker; then curl -sSL https://get.docker.com | sh -; fi"); err != nil {
|
||||
var buf bytes.Buffer
|
||||
if _, err := buf.ReadFrom(output.Stderr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// HACK: the script above will output debug to stderr; we save it and
|
||||
// then check if the command returned an error; if so, we show the debug
|
||||
var buf bytes.Buffer
|
||||
cmd.Stderr = &buf
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("error installing docker: %s\n%s\n", err, string(buf.Bytes()))
|
||||
return fmt.Errorf("error installing docker: %s\n", buf.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -99,11 +94,7 @@ func ConfigureAuth(p Provisioner, authOptions auth.AuthOptions) error {
|
|||
|
||||
dockerDir := p.GetDockerOptionsDir()
|
||||
|
||||
cmd, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p %s", dockerDir))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p %s", dockerDir)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -132,27 +123,15 @@ func ConfigureAuth(p Provisioner, authOptions auth.AuthOptions) error {
|
|||
machineServerKeyPath := path.Join(dockerDir, "server-key.pem")
|
||||
authOptions.ServerKeyRemotePath = machineServerKeyPath
|
||||
|
||||
cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(caCert), machineCaCertPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(caCert), machineCaCertPath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverKey), machineServerKeyPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverKey), machineServerKeyPath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverCert), machineServerCertPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverCert), machineServerCertPath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -179,11 +158,7 @@ func ConfigureAuth(p Provisioner, authOptions auth.AuthOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a %s", dkrcfg.EngineOptions, dkrcfg.EngineOptionsPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a %s", dkrcfg.EngineOptions, dkrcfg.EngineOptionsPath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -234,11 +209,7 @@ func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
cmd, err := p.SSHCommand(fmt.Sprintf("sudo docker pull %s", swarm.DockerImage))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err := p.SSHCommand(fmt.Sprintf("sudo docker pull %s", swarm.DockerImage)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -248,12 +219,8 @@ func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions) error {
|
|||
if swarmOptions.Master {
|
||||
log.Debug("launching swarm master")
|
||||
log.Debugf("master args: %s", masterArgs)
|
||||
cmd, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d -p %s:%s --restart=always --name swarm-agent-master -v %s:%s %s manage %s",
|
||||
port, port, dockerDir, dockerDir, swarm.DockerImage, masterArgs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d -p %s:%s --restart=always --name swarm-agent-master -v %s:%s %s manage %s",
|
||||
port, port, dockerDir, dockerDir, swarm.DockerImage, masterArgs)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -261,12 +228,8 @@ func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions) error {
|
|||
// start node agent
|
||||
log.Debug("launching swarm node")
|
||||
log.Debugf("node args: %s", nodeArgs)
|
||||
cmd, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d --restart=always --name swarm-agent -v %s:%s %s join %s",
|
||||
dockerDir, dockerDir, swarm.DockerImage, nodeArgs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if _, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d --restart=always --name swarm-agent -v %s:%s %s join %s",
|
||||
dockerDir, dockerDir, swarm.DockerImage, nodeArgs)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Config *ssh.ClientConfig
|
||||
Hostname string
|
||||
Port int
|
||||
}
|
||||
|
||||
func NewClient(user string, host string, port int, auth *Auth) (*Client, error) {
|
||||
config, err := NewConfig(user, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{
|
||||
Config: config,
|
||||
Hostname: host,
|
||||
Port: port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewConfig(user string, auth *Auth) (*ssh.ClientConfig, error) {
|
||||
var authMethods []ssh.AuthMethod
|
||||
|
||||
for _, k := range auth.Keys {
|
||||
key, err := ioutil.ReadFile(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKey, err := ssh.ParsePrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authMethods = append(authMethods, ssh.PublicKeys(privateKey))
|
||||
}
|
||||
|
||||
for _, p := range auth.Passwords {
|
||||
authMethods = append(authMethods, ssh.Password(p))
|
||||
}
|
||||
|
||||
return &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: authMethods,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client *Client) Run(command string) (Output, error) {
|
||||
var output Output
|
||||
|
||||
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Hostname, client.Port), client.Config)
|
||||
if err != nil {
|
||||
return output, err
|
||||
}
|
||||
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
return output, err
|
||||
}
|
||||
|
||||
defer session.Close()
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
session.Stdout = &stdout
|
||||
session.Stderr = &stderr
|
||||
|
||||
output = Output{
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
}
|
||||
|
||||
return output, session.Run(command)
|
||||
}
|
||||
|
||||
func (client *Client) Shell() error {
|
||||
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Hostname, client.Port), client.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer session.Close()
|
||||
|
||||
session.Stdout = os.Stdout
|
||||
session.Stderr = os.Stderr
|
||||
session.Stdin = os.Stdin
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 1,
|
||||
}
|
||||
|
||||
var termWidth, termHeight int
|
||||
|
||||
fd := os.Stdin.Fd()
|
||||
|
||||
if term.IsTerminal(fd) {
|
||||
var oldState *term.State
|
||||
|
||||
oldState, err = term.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer term.RestoreTerminal(fd, oldState)
|
||||
|
||||
winsize, err := term.GetWinsize(fd)
|
||||
if err != nil {
|
||||
termWidth = 80
|
||||
termHeight = 24
|
||||
} else {
|
||||
termWidth = int(winsize.Width)
|
||||
termHeight = int(winsize.Height)
|
||||
}
|
||||
}
|
||||
|
||||
if err := session.RequestPty("xterm", termHeight, termWidth, modes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := session.Shell(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
Passwords []string
|
||||
Keys []string
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
Stdout io.Reader
|
||||
Stderr io.Reader
|
||||
}
|
32
ssh/ssh.go
32
ssh/ssh.go
|
@ -1,41 +1,9 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func GetSSHCommand(host string, port int, user string, sshKey string, args ...string) *exec.Cmd {
|
||||
defaultSSHArgs := []string{
|
||||
"-o", "IdentitiesOnly=yes",
|
||||
"-o", "StrictHostKeyChecking=no", // don't bother checking in ~/.ssh/known_hosts
|
||||
"-o", "UserKnownHostsFile=/dev/null", // don't write anything to ~/.ssh/known_hosts
|
||||
"-o", "ConnectionAttempts=3", // retry 3 times if SSH connection fails
|
||||
"-o", "ConnectTimeout=10", // timeout after 10 seconds
|
||||
"-o", "LogLevel=quiet", // suppress "Warning: Permanently added '[localhost]:2022' (ECDSA) to the list of known hosts."
|
||||
"-p", fmt.Sprintf("%d", port),
|
||||
"-i", sshKey,
|
||||
fmt.Sprintf("%s@%s", user, host),
|
||||
}
|
||||
|
||||
sshArgs := append(defaultSSHArgs, args...)
|
||||
cmd := exec.Command("ssh", sshArgs...)
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if os.Getenv("DEBUG") != "" {
|
||||
cmd.Stdout = os.Stdout
|
||||
}
|
||||
|
||||
log.Debugf("executing: %v", strings.Join(cmd.Args, " "))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func WaitForTCP(addr string) error {
|
||||
for {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
|
|
Loading…
Reference in New Issue