ssh使用golang中的交互式shell执行nsenter作为远程命令来debuggingdocker容器

我正在尝试在coreos上自动debuggingDocker容器。 我想要一个通过ssh连接到主机的脚本并执行nsenter 。 这将是非常方便的直接从我的OSX框跳转到一个容器,而不需要手动做很多事情。 我知道以这种方式进入集装箱可能会很糟糕,但是如果事情变得艰难,我想使用这样的工具。 所以这里是我在golang迄今为止。

我能够创build一个交互式shell。 在这里,我有问题,像使用ctrl+R反向searchbash历史logging会中断会话。 该代码在下面评论,因此不执行。

但是,我也能够执行一个命令,在这里nsenter ,但我收到错误stdin: is not a tty ,没有更多的事情发生。 我有兴趣知道为什么stdin在我的程序中不是一个tty ,我怎么能实现这一点。

谢谢

 package main import ( "code.google.com/p/go.crypto/ssh" "io/ioutil" "log" "os" ) func privateKey() ssh.Signer { buf, err := ioutil.ReadFile("./id_rsa") if err != nil { panic(err) } key, err := ssh.ParsePrivateKey(buf) if err != nil { panic(err) } return key } func main() { privateKey := privateKey() // Create client config config := &ssh.ClientConfig{ User: "core", Auth: []ssh.AuthMethod{ ssh.PublicKeys(privateKey), }, } // Connect to ssh server conn, err := ssh.Dial("tcp", "myhost.com:22", config) if err != nil { log.Fatalf("unable to connect: %s", err) } defer conn.Close() // Create a session session, err := conn.NewSession() if err != nil { log.Fatalf("unable to create session: %s", err) } session.Stdout = os.Stdout session.Stderr = os.Stderr session.Stdin = os.Stdin // How can session.Stdin be a tty? ////////////////////////////////////////////////////////////////////// // Stuff for interactive shell // Set up terminal modes //modes := ssh.TerminalModes{ // ssh.ECHO: 1, // enable echoing // ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud // ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud //} // Request pseudo terminal //if err := session.RequestPty("xterm-256color", 80, 40, modes); err != nil { // log.Fatalf("request for pseudo terminal failed: %s", err) //} // Start remote shell //if err := session.Shell(); err != nil { // log.Fatalf("failed to start shell: %s", err) //} ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Stuff for executing remote command // 2202 in my example is actually the pid of a running container if err := session.Run("sudo nsenter --target 2202 --mount --uts --ipc --net --pid"); err != nil { panic("Failed to run: " + err.Error()) } ////////////////////////////////////////////////////////////////////// session.Wait() } 

超级酷,我得到了它的工作,但仍然有一个我不能理解的魔法。 但是,我改变了我的代码如下。 导致正确pty行为的基本变化是使用了"code.google.com/p/go.crypto/ssh/terminal"包。 使用MakeRaw(fd)似乎导致副作用,使正确的pty行为。 还要感谢我find工作示例https://github.com/coreos/fleet/blob/master/ssh/ssh.go的车队项目。

 // The following two lines makes the terminal work properly because of // side-effects I don't understand. fd := int(os.Stdin.Fd()) oldState, err := terminal.MakeRaw(fd) if err != nil { panic(err) } session.Stdout = os.Stdout session.Stderr = os.Stderr session.Stdin = os.Stdin termWidth, termHeight, err := terminal.GetSize(fd) if err != nil { panic(err) } // Set up terminal modes modes := ssh.TerminalModes{ ssh.ECHO: 1, // enable echoing ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } // Request pseudo terminal if err := session.RequestPty("xterm-256color", termHeight, termWidth, modes); err != nil { log.Fatalf("request for pseudo terminal failed: %s", err) } if err := session.Run("sudo nsenter --target 2202 --mount --uts --ipc --net --pid"); err != nil { // if the session terminated normally, err should be ExitError; in that // case, return nil error and actual exit status of command if exitErr, ok := err.(*ssh.ExitError); ok { fmt.Printf("exit code: %#v\n", exitErr.ExitStatus()) } else { panic("Failed to run: " + err.Error()) } } session.Close() terminal.Restore(fd, oldState)