func (client *NativeClient) Start(command string) (io.ReadCloser, io.ReadCloser, error) {
	session, err := client.session(command)
	if err != nil {
		return nil, nil, err
	}
	stdout, err := session.StdoutPipe()
	if err != nil {
		return nil, nil, err
	}
	stderr, err := session.StderrPipe()
	if err != nil {
		return nil, nil, err
	}
	if err := session.Start(command); err != nil {
		return nil, nil, err
	}
	client.openSession = session
	return ioutil.NopCloser(stdout), ioutil.NopCloser(stderr), nil
}
/*
func (client *NativeClient) OutputWithPty(command string) (string, error) {
session, err := client.session(command)
if err != nil {
return "", nil
}

fd := int(os.Stdin.Fd())

termWidth, termHeight, err := terminal.GetSize(fd)
if err != nil {
return "", err
}

modes := ssh.TerminalModes{
ssh.ECHO:          0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}

request tty -- fixes error with hosts that use
"Defaults requiretty" in /etc/sudoers - I'm looking at you RedHat
if err := session.RequestPty("xterm", termHeight, termWidth, modes); err != nil {
return "", err
}

output, err := session.CombinedOutput(command)
defer session.Close()

return string(output), err
}
package ssh
import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"time"
	"golang.org/x/crypto/ssh"
	log "github.com/Sirupsen/logrus"
)
type Client interface {
	Output(command string) (string, error)
	Start(command string) (io.ReadCloser, io.ReadCloser, error)
	Wait() error
}
type ExternalClient struct {
	BaseArgs   []string
	BinaryPath string
	cmd        *exec.Cmd
}
type NativeClient struct {
	Config      ssh.ClientConfig
	Hostname    string
	Port        int
	openSession *ssh.Session
}
type Auth struct {
	Passwords []string
	Keys      []string
}
type ClientType string
const (
	maxDialAttempts = 10
)
const (
	External ClientType = "external"
	Native   ClientType = "native"
)
var (
	baseSSHArgs = []string{
		"-o", "BatchMode=yes",
		"-o", "PasswordAuthentication=no",
		"-o", "StrictHostKeyChecking=no",
		"-o", "UserKnownHostsFile=/dev/null",
		"-o", "LogLevel=quiet",  
		"-o", "ConnectionAttempts=3",  
		"-o", "ConnectTimeout=10",  
		"-o", "ControlMaster=no",  
		"-o", "ControlPath=none",
	}
	defaultClientType = External
)
func SetDefaultClient(clientType ClientType) {
	switch clientType {
	case External:
		defaultClientType = External
	case Native:
		defaultClientType = Native
	}
}
func NewClient(user string, host string, port int, auth *Auth) (Client, error) {
	sshBinaryPath, err := exec.LookPath("ssh")
	if err != nil {
		log.Debug("SSH binary not found, using native Go implementation")
		client, err := NewNativeClient(user, host, port, auth)
		log.Debug(client)
		return client, err
	}
	if defaultClientType == Native {
		log.Debug("Using SSH client type: native")
		client, err := NewNativeClient(user, host, port, auth)
		log.Debug(client)
		return client, err
	}
	log.Debug("Using SSH client type: external")
	client, err := NewExternalClient(sshBinaryPath, user, host, port, auth)
	log.Debug(client)
	return client, err
}
func NewNativeClient(user, host string, port int, auth *Auth) (Client, error) {
	config, err := NewNativeConfig(user, auth)
	if err != nil {
		return nil, fmt.Errorf("Error getting config for native Go SSH: %s", err)
	}
	return &NativeClient{
		Config:   config,
		Hostname: host,
		Port:     port,
	}, nil
}
func NewNativeConfig(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 ssh.ClientConfig{}, err
		}
		privateKey, err := ssh.ParsePrivateKey(key)
		if err != nil {
			return ssh.ClientConfig{}, 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 *NativeClient) dialSuccess() bool {
	if _, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Hostname, client.Port), &client.Config); err != nil {
		log.Debugf("Error dialing TCP: %s", err)
		return false
	}
	return true
}
func WaitForSpecificOrError(f func() (bool, error), maxAttempts int, waitInterval time.Duration) error {
	for i := 0; i < maxAttempts; i++ {
		stop, err := f()
		if err != nil {
			return err
		}
		if stop {
			return nil
		}
		time.Sleep(waitInterval)
	}
	return fmt.Errorf("Maximum number of retries (%d) exceeded", maxAttempts)
}
func WaitForSpecific(f func() bool, maxAttempts int, waitInterval time.Duration) error {
	return WaitForSpecificOrError(func() (bool, error) {
		return f(), nil
	}, maxAttempts, waitInterval)
}
func WaitFor(f func() bool) error {
	return WaitForSpecific(f, 60, 3*time.Second)
}
func (client *NativeClient) session(command string) (*ssh.Session, error) {
	if err := WaitFor(client.dialSuccess); err != nil {
		return nil, fmt.Errorf("Error attempting SSH client dial: %s", err)
	}
	conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Hostname, client.Port), &client.Config)
	if err != nil {
		return nil, fmt.Errorf("Mysterious error dialing TCP for SSH (we already succeeded at least once) : %s", err)
	}
	return conn.NewSession()
}
func (client *NativeClient) Output(command string) (string, error) {
	session, err := client.session(command)
	if err != nil {
		return "", nil
	}
	output, err := session.CombinedOutput(command)
	defer session.Close()
	return string(output), err
}