diff --git a/remote/remote_test.go b/remote/remote_test.go new file mode 100644 index 000000000..9c8b91738 --- /dev/null +++ b/remote/remote_test.go @@ -0,0 +1,30 @@ +package remote + +import ( + "bytes" + "fmt" + "os" + "testing" + . "github.com/smartystreets/goconvey/convey" +) + +func TestRemote(t *testing.T) { + + Convey("Login", t, func() { + pem_file := "id_dsa" + if _, err := os.Stat(pem_file); err == nil { + ssh, err := New("50.16.204.123:22", "todd", "id_dsa") + So(err, ShouldEqual, nil) + err = ssh.Launch("sleep 10 && date", "background") + So(err, ShouldEqual, nil) + content, _ := ssh.Run("date", false) + ssh.CopyTo([]byte(content), "uploadfile") + results, _ := ssh.CopyFrom("uploadfile") + ok := bytes.Equal([]byte(content), results) + So(ok, ShouldEqual, true) + } else { + fmt.Println("No credentials so SSH Test ignored") + So(true, ShouldEqual, true) + } + }) +} diff --git a/remote/ssh.go b/remote/ssh.go new file mode 100644 index 000000000..450fc34b6 --- /dev/null +++ b/remote/ssh.go @@ -0,0 +1,135 @@ +package remote + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "github.com/wsxiaoys/terminal/color" + + "code.google.com/p/go.crypto/ssh" +) + +type keychain struct { + keys []ssh.Signer +} + +func (k *keychain) Key(i int) (ssh.PublicKey, error) { + if i < 0 || i >= len(k.keys) { + return nil, nil + } + return k.keys[i].PublicKey(), nil +} + +func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) { + return k.keys[i].Sign(rand, data) +} + +func (k *keychain) add(key ssh.Signer) { + k.keys = append(k.keys, key) +} + +func (k *keychain) loadPEM(file string) error { + buf, err := ioutil.ReadFile(file) + if err != nil { + return err + } + key, err := ssh.ParsePrivateKey(buf) + if err != nil { + return err + } + k.add(key) + return nil +} + +func (k *keychain) loadPEMString(buf string) error { + key, err := ssh.ParsePrivateKey([]byte(buf)) + if err != nil { + return err + } + k.add(key) + return nil +} + +type SSH struct { + client *ssh.ClientConn +} + +func (self *SSH) Launch(command string, server_log_path string) error { + command = fmt.Sprintf("/usr/bin/nohup bash -c \\\n\"%s\" `%s 2>&1 &", command, server_log_path) + var b bytes.Buffer + var e bytes.Buffer + session, _ := self.client.NewSession() + defer session.Close() + session.Stdout = &b + session.Stderr = &e + return session.Run(command) +} + +func (self *SSH) Run(command string, sudo bool) (string, error) { + var b bytes.Buffer + var e bytes.Buffer + session, _ := self.client.NewSession() + defer session.Close() + session.Stdout = &b + session.Stderr = &e + if sudo { + command = fmt.Sprintf("/usr/bin/sudo bash <%s", dest_name)) + return err +} + +func (self *SSH) CopyFrom(dest_name string) ([]byte, error) { + + session, _ := self.client.NewSession() + defer session.Close() + var b bytes.Buffer + session.Stdout = &b + session.Run(fmt.Sprintf("/bin/cat %s", dest_name)) + + return b.Bytes(), nil + +} + +func New(host, user, pem_path string) (*SSH, error) { + ret := new(SSH) + k := new(keychain) + k.loadPEM(pem_path) + config := &ssh.ClientConfig{ + User: user, + Auth: []ssh.ClientAuth{ + ssh.ClientAuthKeyring(k), + }, + } + client, err := ssh.Dial("tcp", host, config) + if err != nil { + + color.Printf("@{!r}%s: Failed to connect: %s\n", host, err.Error()) + return nil, err + } + ret.client = client + + return ret, err + +}