Skip to content

Instantly share code, notes, and snippets.

@sykesm
Created May 26, 2015 18:55
Show Gist options
  • Save sykesm/8e46310816b55e60dfea to your computer and use it in GitHub Desktop.
Save sykesm/8e46310816b55e60dfea to your computer and use it in GitHub Desktop.
Super-simple start of an ssh plugin for the cli
package main
import (
"fmt"
"io"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"code.google.com/p/go.crypto/ssh"
"code.google.com/p/go.crypto/ssh/terminal"
"github.com/cloudfoundry/cli/plugin"
)
type SshCmd struct{}
func (c *SshCmd) GetMetadata() plugin.PluginMetadata {
return plugin.PluginMetadata{
Name: "SSH",
Version: plugin.VersionType{
Major: 1,
Minor: 1,
Build: 0,
},
Commands: []plugin.Command{
{
Name: "ssh",
HelpText: "ssh to an application container instance",
UsageDetails: plugin.Usage{
Usage: "cf ssh APP-NAME [-i instance]",
},
},
},
}
}
func main() {
plugin.Start(new(SshCmd))
}
func (c *SshCmd) Run(cli plugin.CliConnection, args []string) {
index := 0
if len(args) > 2 && flagPresent(args, "-i") {
idxFlag := getFlagValue(args, "-i")
var err error
index, err = strconv.Atoi(idxFlag)
if err != nil {
fmt.Printf("Invalid index: %q\n", idxFlag)
return
}
}
result, err := cli.CliCommandWithoutTerminalOutput("app", args[1], "--guid")
if err != nil {
if strings.Contains(result[0], "FAILED") {
fmt.Printf("Application not found\n")
return
}
}
guid := strings.TrimSpace(result[0])
result, err = cli.CliCommandWithoutTerminalOutput("oauth-token")
if err != nil {
if strings.Contains(result[1], "FAILED") {
fmt.Printf("Failed to acquire authorization token\n")
return
}
}
token := result[3]
config := &ssh.ClientConfig{
User: fmt.Sprintf("%s/%d", guid, index),
Auth: []ssh.AuthMethod{
ssh.Password(token),
},
}
client, err := ssh.Dial("tcp", "ssh.10.244.0.34.xip.io:2222", config)
if err != nil {
fmt.Printf("SSH authentication failed\n")
return
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
fmt.Printf("Failed to allocate SSH session\n")
return
}
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
in, err := session.StdinPipe()
if err != nil {
fmt.Printf("Failed to connect stdin\n")
return
}
state, err := terminal.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
fmt.Printf("Failed to make stdin raw\n")
return
}
defer terminal.Restore(int(os.Stdin.Fd()), state)
modes := ssh.TerminalModes{
ssh.ECHO: 1,
ssh.TTY_OP_ISPEED: 115200,
ssh.TTY_OP_OSPEED: 115200,
}
width, height, err := terminal.GetSize(int(os.Stdin.Fd()))
if err != nil {
width = 80
height = 25
}
err = session.RequestPty("xterm", height, width, modes)
if err != nil {
fmt.Printf("PTY allocation failed\n")
return
}
err = session.Shell()
if err != nil {
fmt.Printf("Shell creation failed\n")
return
}
go func() {
io.Copy(in, os.Stdin)
}()
go resize(session)
session.Wait()
}
func resize(session *ssh.Session) {
resized := make(chan os.Signal, 16)
signal.Notify(resized, syscall.SIGWINCH)
type resizeMessage struct {
Width uint32
Height uint32
PixelWidth uint32
PixelHeight uint32
}
for {
<-resized
width, height, err := terminal.GetSize(int(os.Stdin.Fd()))
if err != nil {
continue
}
message := resizeMessage{
Width: uint32(width),
Height: uint32(height),
}
session.SendRequest("window-change", false, ssh.Marshal(message))
}
}
func flagPresent(args []string, flag string) bool {
for _, arg := range args {
if arg == flag {
return true
}
}
return false
}
func getFlagValue(args []string, flag string) string {
for i, arg := range args {
if arg == flag {
if len(args) >= i+1 {
return args[i+1]
}
break
}
}
return ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment