mirror of https://github.com/docker/docs.git
Merge pull request #2838 from mssola/export-session
libmachine/ssh: export the NativeClient.Session function
This commit is contained in:
commit
15b5a1d10d
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue