Merge pull request #2838 from mssola/export-session

libmachine/ssh: export the NativeClient.Session function
This commit is contained in:
David Gageot 2016-02-09 17:36:25 -08:00
commit 15b5a1d10d
6 changed files with 253 additions and 75 deletions

145
libmachine/examples/main.go Normal file
View File

@ -0,0 +1,145 @@
package main
import (
"bufio"
"encoding/json"
"fmt"
"os"
"github.com/docker/machine/drivers/virtualbox"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log"
)
func usage() {
fmt.Println("Usage: go run main.go <example>\n" +
"Available examples: create streaming.")
os.Exit(1)
}
// Sample Virtualbox create independent of Machine CLI.
func create() {
log.SetDebug(true)
client := libmachine.NewClient("/tmp/automatic", "/tmp/automatic/certs")
defer client.Close()
hostName := "myfunhost"
// Set some options on the provider...
driver := virtualbox.NewDriver(hostName, "/tmp/automatic")
driver.CPU = 2
driver.Memory = 2048
data, err := json.Marshal(driver)
if err != nil {
log.Error(err)
return
}
h, err := client.NewHost("virtualbox", data)
if err != nil {
log.Error(err)
return
}
h.HostOptions.EngineOptions.StorageDriver = "overlay"
if err := client.Create(h); err != nil {
log.Error(err)
return
}
out, err := h.RunSSHCommand("df -h")
if err != nil {
log.Error(err)
return
}
fmt.Printf("Results of your disk space query:\n%s\n", out)
fmt.Println("Powering down machine now...")
if err := h.Stop(); err != nil {
log.Error(err)
return
}
}
// Streaming the output of an SSH session in virtualbox.
func streaming() {
log.SetDebug(true)
client := libmachine.NewClient("/tmp/automatic", "/tmp/automatic/certs")
defer client.Close()
hostName := "myfunhost"
// Set some options on the provider...
driver := virtualbox.NewDriver(hostName, "/tmp/automatic")
data, err := json.Marshal(driver)
if err != nil {
log.Error(err)
return
}
h, err := client.NewHost("virtualbox", data)
if err != nil {
log.Error(err)
return
}
if err := client.Create(h); err != nil {
log.Error(err)
return
}
h.HostOptions.EngineOptions.StorageDriver = "overlay"
sshClient, err := h.CreateSSHClient()
if err != nil {
log.Error(err)
return
}
stdout, stderr, err := sshClient.Start("yes | head -n 10000")
if err != nil {
log.Error(err)
return
}
defer func() {
_ = stdout.Close()
_ = stderr.Close()
}()
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Error(err)
}
if err := sshClient.Wait(); err != nil {
log.Error(err)
}
fmt.Println("Powering down machine now...")
if err := h.Stop(); err != nil {
log.Error(err)
return
}
}
func main() {
if len(os.Args) != 2 {
usage()
}
switch os.Args[1] {
case "create":
create()
case "streaming":
streaming()
default:
usage()
}
}

View File

@ -1,58 +0,0 @@
package main
// Sample Virtualbox create independent of Machine CLI.
import (
"encoding/json"
"fmt"
"github.com/docker/machine/drivers/virtualbox"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log"
)
func main() {
log.SetDebug(true)
client := libmachine.NewClient("/tmp/automatic", "/tmp/automatic/certs")
defer client.Close()
hostName := "myfunhost"
// Set some options on the provider...
driver := virtualbox.NewDriver(hostName, "/tmp/automatic")
driver.CPU = 2
driver.Memory = 2048
data, err := json.Marshal(driver)
if err != nil {
log.Error(err)
return
}
h, err := client.NewHost("virtualbox", data)
if err != nil {
log.Error(err)
return
}
h.HostOptions.EngineOptions.StorageDriver = "overlay"
if err := client.Create(h); err != nil {
log.Error(err)
return
}
out, err := h.RunSSHCommand("df -h")
if err != nil {
log.Error(err)
return
}
fmt.Printf("Results of your disk space query:\n%s\n", out)
fmt.Println("Powering down machine now...")
if err := h.Stop(); err != nil {
log.Error(err)
return
}
}

View File

@ -76,12 +76,12 @@ func (h *Host) CreateSSHClient() (ssh.Client, error) {
func (creator *StandardSSHClientCreator) CreateSSHClient(d drivers.Driver) (ssh.Client, error) {
addr, err := d.GetSSHHostname()
if err != nil {
return ssh.ExternalClient{}, err
return &ssh.ExternalClient{}, err
}
port, err := d.GetSSHPort()
if err != nil {
return ssh.ExternalClient{}, err
return &ssh.ExternalClient{}, err
}
auth := &ssh.Auth{}

View File

@ -20,10 +20,10 @@ func (sshCmder RedHatSSHCommander) SSHCommand(args string) (string, error) {
// Note: CentOS 7.0 needs multiple "-tt" to force tty allocation when ssh has
// no local tty.
switch c := client.(type) {
case ssh.ExternalClient:
case *ssh.ExternalClient:
c.BaseArgs = append(c.BaseArgs, "-tt")
client = c
case ssh.NativeClient:
case *ssh.NativeClient:
return c.OutputWithPty(args)
}

View File

@ -2,6 +2,7 @@ package ssh
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
@ -17,17 +18,31 @@ import (
type Client interface {
Output(command string) (string, error)
Shell(args ...string) error
// Start starts the specified command without waiting for it to finish. You
// have to call the Wait function for that.
//
// The first two io.ReadCloser are the standard output and the standard
// error of the executing command respectively. The returned error follows
// the same logic as in the exec.Cmd.Start function.
Start(command string) (io.ReadCloser, io.ReadCloser, error)
// Wait waits for the command started by the Start function to exit. The
// returned error follows the same logic as in the exec.Cmd.Wait function.
Wait() error
}
type ExternalClient struct {
BaseArgs []string
BinaryPath string
cmd *exec.Cmd
}
type NativeClient struct {
Config ssh.ClientConfig
Hostname string
Port int
Config ssh.ClientConfig
Hostname string
Port int
openSession *ssh.Session
}
type Auth struct {
@ -101,7 +116,7 @@ func NewNativeClient(user, host string, port int, auth *Auth) (Client, error) {
return nil, fmt.Errorf("Error getting config for native Go SSH: %s", err)
}
return NativeClient{
return &NativeClient{
Config: config,
Hostname: host,
Port: port,
@ -137,7 +152,7 @@ func NewNativeConfig(user string, auth *Auth) (ssh.ClientConfig, error) {
}, nil
}
func (client NativeClient) dialSuccess() bool {
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
@ -145,7 +160,7 @@ func (client NativeClient) dialSuccess() bool {
return true
}
func (client NativeClient) session(command string) (*ssh.Session, error) {
func (client *NativeClient) session(command string) (*ssh.Session, error) {
if err := mcnutils.WaitFor(client.dialSuccess); err != nil {
return nil, fmt.Errorf("Error attempting SSH client dial: %s", err)
}
@ -158,7 +173,7 @@ func (client NativeClient) session(command string) (*ssh.Session, error) {
return conn.NewSession()
}
func (client NativeClient) Output(command string) (string, error) {
func (client *NativeClient) Output(command string) (string, error) {
session, err := client.session(command)
if err != nil {
return "", nil
@ -170,7 +185,7 @@ func (client NativeClient) Output(command string) (string, error) {
return string(output), err
}
func (client NativeClient) OutputWithPty(command string) (string, error) {
func (client *NativeClient) OutputWithPty(command string) (string, error) {
session, err := client.session(command)
if err != nil {
return "", nil
@ -201,7 +216,36 @@ func (client NativeClient) OutputWithPty(command string) (string, error) {
return string(output), err
}
func (client NativeClient) Shell(args ...string) error {
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) Wait() error {
err := client.openSession.Wait()
_ = client.openSession.Close()
client.openSession = nil
return err
}
func (client *NativeClient) Shell(args ...string) error {
var (
termWidth, termHeight int
)
@ -261,8 +305,8 @@ func (client NativeClient) Shell(args ...string) error {
return nil
}
func NewExternalClient(sshBinaryPath, user, host string, port int, auth *Auth) (ExternalClient, error) {
client := ExternalClient{
func NewExternalClient(sshBinaryPath, user, host string, port int, auth *Auth) (*ExternalClient, error) {
client := &ExternalClient{
BinaryPath: sshBinaryPath,
}
@ -293,14 +337,14 @@ func getSSHCmd(binaryPath string, args ...string) *exec.Cmd {
return exec.Command(binaryPath, args...)
}
func (client ExternalClient) Output(command string) (string, error) {
func (client *ExternalClient) Output(command string) (string, error) {
args := append(client.BaseArgs, command)
cmd := getSSHCmd(client.BinaryPath, args...)
output, err := cmd.CombinedOutput()
return string(output), err
}
func (client ExternalClient) Shell(args ...string) error {
func (client *ExternalClient) Shell(args ...string) error {
args = append(client.BaseArgs, args...)
cmd := getSSHCmd(client.BinaryPath, args...)
@ -312,3 +356,40 @@ func (client ExternalClient) Shell(args ...string) error {
return cmd.Run()
}
func (client *ExternalClient) Start(command string) (io.ReadCloser, io.ReadCloser, error) {
args := append(client.BaseArgs, command)
cmd := getSSHCmd(client.BinaryPath, args...)
log.Debug(cmd)
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
if closeErr := stdout.Close(); closeErr != nil {
return nil, nil, fmt.Errorf("%s, %s", err, closeErr)
}
return nil, nil, err
}
if err := cmd.Start(); err != nil {
stdOutCloseErr := stdout.Close()
stdErrCloseErr := stderr.Close()
if stdOutCloseErr != nil || stdErrCloseErr != nil {
return nil, nil, fmt.Errorf("%s, %s, %s",
err, stdOutCloseErr, stdErrCloseErr)
}
return nil, nil, err
}
client.cmd = cmd
return stdout, stderr, nil
}
func (client *ExternalClient) Wait() error {
err := client.cmd.Wait()
client.cmd = nil
return err
}

View File

@ -1,5 +1,7 @@
package sshtest
import "io"
type CmdResult struct {
Out string
Err error
@ -19,3 +21,11 @@ func (fsc *FakeClient) Shell(args ...string) error {
fsc.ActivatedShell = args
return nil
}
func (fsc *FakeClient) Start(command string) (io.ReadCloser, io.ReadCloser, error) {
return nil, nil, nil
}
func (fsc *FakeClient) Wait() error {
return nil
}