mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Only set the terminal in raw mode for commands which need it
The raw mode is actually only needed when you attach to a container. Having it enabled all the time can be a pain, e.g: if docker crashes your terminal will end up in a broken state. Since we are currently missing a real API for the docker daemon to negotiate this kind of options, this changeset actually enable the raw mode on the login (because it outputs a password), run and attach commands. This "optional raw mode" is implemented by passing a more complicated interface than io.Writer as the stdout argument of each command. This interface (DockerConn) exposes a method which allows the command to set the terminal in raw mode or not. Finally, the code added by this changeset will be deprecated by a real API for the docker daemon.
This commit is contained in:
parent
4e5001b46a
commit
7d0ab3858e
4 changed files with 238 additions and 56 deletions
60
commands.go
60
commands.go
|
@ -62,7 +62,7 @@ func (srv *Server) Help() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'docker login': login / register a user to registry service.
|
// 'docker login': login / register a user to registry service.
|
||||||
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
// Read a line on raw terminal with support for simple backspace
|
// Read a line on raw terminal with support for simple backspace
|
||||||
// sequences and echo.
|
// sequences and echo.
|
||||||
//
|
//
|
||||||
|
@ -71,7 +71,7 @@ func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout io.Writer, args ...strin
|
||||||
// - we have to read a password (without echoing it);
|
// - we have to read a password (without echoing it);
|
||||||
// - the rcli "protocol" only supports cannonical and raw modes and you
|
// - the rcli "protocol" only supports cannonical and raw modes and you
|
||||||
// can't tune it once the command as been started.
|
// can't tune it once the command as been started.
|
||||||
var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string {
|
var readStringOnRawTerminal = func(stdin io.Reader, stdout rcli.DockerConn, echo bool) string {
|
||||||
char := make([]byte, 1)
|
char := make([]byte, 1)
|
||||||
buffer := make([]byte, 64)
|
buffer := make([]byte, 64)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
@ -106,13 +106,15 @@ func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout io.Writer, args ...strin
|
||||||
}
|
}
|
||||||
return string(buffer[:i])
|
return string(buffer[:i])
|
||||||
}
|
}
|
||||||
var readAndEchoString = func(stdin io.Reader, stdout io.Writer) string {
|
var readAndEchoString = func(stdin io.Reader, stdout rcli.DockerConn) string {
|
||||||
return readStringOnRawTerminal(stdin, stdout, true)
|
return readStringOnRawTerminal(stdin, stdout, true)
|
||||||
}
|
}
|
||||||
var readString = func(stdin io.Reader, stdout io.Writer) string {
|
var readString = func(stdin io.Reader, stdout rcli.DockerConn) string {
|
||||||
return readStringOnRawTerminal(stdin, stdout, false)
|
return readStringOnRawTerminal(stdin, stdout, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stdout.SetOptionRawTerminal()
|
||||||
|
|
||||||
cmd := rcli.Subcmd(stdout, "login", "", "Register or Login to the docker registry server")
|
cmd := rcli.Subcmd(stdout, "login", "", "Register or Login to the docker registry server")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -158,7 +160,7 @@ func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout io.Writer, args ...strin
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'docker wait': block until a container stops
|
// 'docker wait': block until a container stops
|
||||||
func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdWait(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
|
cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -178,14 +180,14 @@ func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'docker version': show version information
|
// 'docker version': show version information
|
||||||
func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
fmt.Fprintf(stdout, "Version:%s\n", VERSION)
|
fmt.Fprintf(stdout, "Version:%s\n", VERSION)
|
||||||
fmt.Fprintf(stdout, "Git Commit:%s\n", GIT_COMMIT)
|
fmt.Fprintf(stdout, "Git Commit:%s\n", GIT_COMMIT)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'docker info': display system-wide information.
|
// 'docker info': display system-wide information.
|
||||||
func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
images, _ := srv.runtime.graph.All()
|
images, _ := srv.runtime.graph.All()
|
||||||
var imgcount int
|
var imgcount int
|
||||||
if images == nil {
|
if images == nil {
|
||||||
|
@ -214,7 +216,7 @@ func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdStop(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
|
cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -236,7 +238,7 @@ func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
|
cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -258,7 +260,7 @@ func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...str
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdStart(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
|
cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -280,7 +282,7 @@ func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...strin
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
|
cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -315,7 +317,7 @@ func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...str
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdPort(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
|
cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -339,7 +341,7 @@ func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'docker rmi NAME' removes all images with the name NAME
|
// 'docker rmi NAME' removes all images with the name NAME
|
||||||
func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) {
|
func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) (err error) {
|
||||||
cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
|
cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -356,7 +358,7 @@ func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdHistory(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdHistory(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "history", "[OPTIONS] IMAGE", "Show the history of an image")
|
cmd := rcli.Subcmd(stdout, "history", "[OPTIONS] IMAGE", "Show the history of an image")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -382,7 +384,7 @@ func (srv *Server) CmdHistory(stdin io.ReadCloser, stdout io.Writer, args ...str
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdRm(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
|
cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -400,7 +402,7 @@ func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'docker kill NAME' kills a running container
|
// 'docker kill NAME' kills a running container
|
||||||
func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdKill(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container")
|
cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -417,7 +419,7 @@ func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
|
cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
|
||||||
var archive io.Reader
|
var archive io.Reader
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
@ -464,7 +466,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdPush(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "push", "NAME", "Push an image or a repository to the registry")
|
cmd := rcli.Subcmd(stdout, "push", "NAME", "Push an image or a repository to the registry")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -523,7 +525,7 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdPull(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "pull", "NAME", "Pull an image or a repository from the registry")
|
cmd := rcli.Subcmd(stdout, "pull", "NAME", "Pull an image or a repository from the registry")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -548,7 +550,7 @@ func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdImages(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
|
cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
|
||||||
//limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
|
//limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
|
||||||
quiet := cmd.Bool("q", false, "only show numeric IDs")
|
quiet := cmd.Bool("q", false, "only show numeric IDs")
|
||||||
|
@ -638,7 +640,7 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdPs(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout,
|
cmd := rcli.Subcmd(stdout,
|
||||||
"ps", "[OPTIONS]", "List containers")
|
"ps", "[OPTIONS]", "List containers")
|
||||||
quiet := cmd.Bool("q", false, "Only display numeric IDs")
|
quiet := cmd.Bool("q", false, "Only display numeric IDs")
|
||||||
|
@ -685,7 +687,7 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout,
|
cmd := rcli.Subcmd(stdout,
|
||||||
"commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]",
|
"commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]",
|
||||||
"Create a new image from a container's changes")
|
"Create a new image from a container's changes")
|
||||||
|
@ -706,7 +708,7 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdExport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdExport(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout,
|
cmd := rcli.Subcmd(stdout,
|
||||||
"export", "CONTAINER",
|
"export", "CONTAINER",
|
||||||
"Export the contents of a filesystem as a tar archive")
|
"Export the contents of a filesystem as a tar archive")
|
||||||
|
@ -728,7 +730,7 @@ func (srv *Server) CmdExport(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
return fmt.Errorf("No such container: %s", name)
|
return fmt.Errorf("No such container: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout,
|
cmd := rcli.Subcmd(stdout,
|
||||||
"diff", "CONTAINER [OPTIONS]",
|
"diff", "CONTAINER [OPTIONS]",
|
||||||
"Inspect changes on a container's filesystem")
|
"Inspect changes on a container's filesystem")
|
||||||
|
@ -752,7 +754,7 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
|
cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -784,7 +786,8 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return fmt.Errorf("No such container: %s", cmd.Arg(0))
|
return fmt.Errorf("No such container: %s", cmd.Arg(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
|
stdout.SetOptionRawTerminal()
|
||||||
cmd := rcli.Subcmd(stdout, "attach", "CONTAINER", "Attach to a running container")
|
cmd := rcli.Subcmd(stdout, "attach", "CONTAINER", "Attach to a running container")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -857,7 +860,7 @@ func (opts AttachOpts) Get(val string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdTag(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
|
cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
|
||||||
force := cmd.Bool("f", false, "Force")
|
force := cmd.Bool("f", false, "Force")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
@ -870,7 +873,8 @@ func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
return srv.runtime.repositories.Set(cmd.Arg(1), cmd.Arg(2), cmd.Arg(0), *force)
|
return srv.runtime.repositories.Set(cmd.Arg(1), cmd.Arg(2), cmd.Arg(0), *force)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
|
stdout.SetOptionRawTerminal()
|
||||||
config, err := ParseRun(args, stdout)
|
config, err := ParseRun(args, stdout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"github.com/dotcloud/docker"
|
"github.com/dotcloud/docker"
|
||||||
"github.com/dotcloud/docker/rcli"
|
"github.com/dotcloud/docker/rcli"
|
||||||
"github.com/dotcloud/docker/term"
|
"github.com/dotcloud/docker/term"
|
||||||
|
@ -56,15 +57,11 @@ func daemon() error {
|
||||||
return rcli.ListenAndServe("tcp", "127.0.0.1:4242", service)
|
return rcli.ListenAndServe("tcp", "127.0.0.1:4242", service)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCommand(args []string) error {
|
func setRawTerminal() (*term.State, error) {
|
||||||
var oldState *term.State
|
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||||
var err error
|
|
||||||
if term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" {
|
|
||||||
oldState, err = term.MakeRaw(int(os.Stdin.Fd()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer term.Restore(int(os.Stdin.Fd()), oldState)
|
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, os.Interrupt)
|
signal.Notify(c, os.Interrupt)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -74,12 +71,68 @@ func runCommand(args []string) error {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
return oldState, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restoreTerminal(state *term.State) {
|
||||||
|
term.Restore(int(os.Stdin.Fd()), state)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DockerLocalConn struct {
|
||||||
|
file *os.File
|
||||||
|
savedState *term.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDockerLocalConn(output *os.File) *DockerLocalConn {
|
||||||
|
return &DockerLocalConn{file: output}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerLocalConn) Read(b []byte) (int, error) { return c.file.Read(b) }
|
||||||
|
|
||||||
|
func (c *DockerLocalConn) Write(b []byte) (int, error) { return c.file.Write(b) }
|
||||||
|
|
||||||
|
func (c *DockerLocalConn) Close() error {
|
||||||
|
if c.savedState != nil {
|
||||||
|
restoreTerminal(c.savedState)
|
||||||
|
c.savedState = nil
|
||||||
|
}
|
||||||
|
return c.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerLocalConn) CloseWrite() error { return nil }
|
||||||
|
|
||||||
|
func (c *DockerLocalConn) CloseRead() error { return nil }
|
||||||
|
|
||||||
|
func (c *DockerLocalConn) GetOptions() *rcli.DockerConnOptions { return nil }
|
||||||
|
|
||||||
|
func (c *DockerLocalConn) SetOptionRawTerminal() {
|
||||||
|
if state, err := setRawTerminal(); err != nil {
|
||||||
|
fmt.Fprintf(
|
||||||
|
os.Stderr,
|
||||||
|
"Can't set the terminal in raw mode: %v",
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
c.savedState = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCommand(args []string) error {
|
||||||
// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
|
// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
|
||||||
// CloseWrite(), which we need to cleanly signal that stdin is closed without
|
// CloseWrite(), which we need to cleanly signal that stdin is closed without
|
||||||
// closing the connection.
|
// closing the connection.
|
||||||
// See http://code.google.com/p/go/issues/detail?id=3345
|
// See http://code.google.com/p/go/issues/detail?id=3345
|
||||||
if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil {
|
if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil {
|
||||||
|
options := conn.GetOptions()
|
||||||
|
if options.RawTerminal &&
|
||||||
|
term.IsTerminal(int(os.Stdin.Fd())) &&
|
||||||
|
os.Getenv("NORAW") == "" {
|
||||||
|
if oldState, err := setRawTerminal(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
defer restoreTerminal(oldState)
|
||||||
|
}
|
||||||
|
}
|
||||||
receiveStdout := docker.Go(func() error {
|
receiveStdout := docker.Go(func() error {
|
||||||
_, err := io.Copy(os.Stdout, conn)
|
_, err := io.Copy(os.Stdout, conn)
|
||||||
return err
|
return err
|
||||||
|
@ -104,12 +157,11 @@ func runCommand(args []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := rcli.LocalCall(service, os.Stdin, os.Stdout, args...); err != nil {
|
dockerConn := newDockerLocalConn(os.Stdout)
|
||||||
|
defer dockerConn.Close()
|
||||||
|
if err := rcli.LocalCall(service, os.Stdin, dockerConn, args...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if oldState != nil {
|
|
||||||
term.Restore(int(os.Stdin.Fd()), oldState)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
95
rcli/tcp.go
95
rcli/tcp.go
|
@ -2,6 +2,7 @@ package rcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -15,22 +16,104 @@ import (
|
||||||
var DEBUG_FLAG bool = false
|
var DEBUG_FLAG bool = false
|
||||||
var CLIENT_SOCKET io.Writer = nil
|
var CLIENT_SOCKET io.Writer = nil
|
||||||
|
|
||||||
|
type DockerTCPConn struct {
|
||||||
|
conn *net.TCPConn
|
||||||
|
options *DockerConnOptions
|
||||||
|
optionsBuf *[]byte
|
||||||
|
handshaked bool
|
||||||
|
client bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDockerTCPConn(conn *net.TCPConn, client bool) *DockerTCPConn {
|
||||||
|
return &DockerTCPConn{
|
||||||
|
conn: conn,
|
||||||
|
options: &DockerConnOptions{},
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerTCPConn) SetOptionRawTerminal() {
|
||||||
|
c.options.RawTerminal = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerTCPConn) GetOptions() *DockerConnOptions {
|
||||||
|
if c.client && !c.handshaked {
|
||||||
|
// Attempt to parse options encoded as a JSON dict and store
|
||||||
|
// the reminder of what we read from the socket in a buffer.
|
||||||
|
//
|
||||||
|
// bufio (and its ReadBytes method) would have been nice here,
|
||||||
|
// but if json.Unmarshal() fails (which will happen if we speak
|
||||||
|
// to a version of docker that doesn't send any option), then
|
||||||
|
// we can't put the data back in it for the next Read().
|
||||||
|
c.handshaked = true
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
if n, _ := c.conn.Read(buf); n > 0 {
|
||||||
|
buf = buf[:n]
|
||||||
|
if nl := bytes.IndexByte(buf, '\n'); nl != -1 {
|
||||||
|
if err := json.Unmarshal(buf[:nl], c.options); err == nil {
|
||||||
|
buf = buf[nl+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.optionsBuf = &buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerTCPConn) Read(b []byte) (int, error) {
|
||||||
|
if c.optionsBuf != nil {
|
||||||
|
// Consume what we buffered in GetOptions() first:
|
||||||
|
optionsBuf := *c.optionsBuf
|
||||||
|
optionsBuflen := len(optionsBuf)
|
||||||
|
copied := copy(b, optionsBuf)
|
||||||
|
if copied < optionsBuflen {
|
||||||
|
optionsBuf = optionsBuf[copied:]
|
||||||
|
c.optionsBuf = &optionsBuf
|
||||||
|
return copied, nil
|
||||||
|
}
|
||||||
|
c.optionsBuf = nil
|
||||||
|
return copied, nil
|
||||||
|
}
|
||||||
|
return c.conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerTCPConn) Write(b []byte) (int, error) {
|
||||||
|
optionsLen := 0
|
||||||
|
if !c.client && !c.handshaked {
|
||||||
|
c.handshaked = true
|
||||||
|
options, _ := json.Marshal(c.options)
|
||||||
|
options = append(options, '\n')
|
||||||
|
if optionsLen, err := c.conn.Write(options); err != nil {
|
||||||
|
return optionsLen, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err := c.conn.Write(b)
|
||||||
|
return n + optionsLen, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerTCPConn) Close() error { return c.conn.Close() }
|
||||||
|
|
||||||
|
func (c *DockerTCPConn) CloseWrite() error { return c.conn.CloseWrite() }
|
||||||
|
|
||||||
|
func (c *DockerTCPConn) CloseRead() error { return c.conn.CloseRead() }
|
||||||
|
|
||||||
// Connect to a remote endpoint using protocol `proto` and address `addr`,
|
// Connect to a remote endpoint using protocol `proto` and address `addr`,
|
||||||
// issue a single call, and return the result.
|
// issue a single call, and return the result.
|
||||||
// `proto` may be "tcp", "unix", etc. See the `net` package for available protocols.
|
// `proto` may be "tcp", "unix", etc. See the `net` package for available protocols.
|
||||||
func Call(proto, addr string, args ...string) (*net.TCPConn, error) {
|
func Call(proto, addr string, args ...string) (DockerConn, error) {
|
||||||
cmd, err := json.Marshal(args)
|
cmd, err := json.Marshal(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn, err := net.Dial(proto, addr)
|
conn, err := dialDocker(proto, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := fmt.Fprintln(conn, string(cmd)); err != nil {
|
if _, err := fmt.Fprintln(conn, string(cmd)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return conn.(*net.TCPConn), nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen on `addr`, using protocol `proto`, for incoming rcli calls,
|
// Listen on `addr`, using protocol `proto`, for incoming rcli calls,
|
||||||
|
@ -46,6 +129,10 @@ func ListenAndServe(proto, addr string, service Service) error {
|
||||||
if conn, err := listener.Accept(); err != nil {
|
if conn, err := listener.Accept(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
conn, err := newDockerServerConn(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
if DEBUG_FLAG {
|
if DEBUG_FLAG {
|
||||||
CLIENT_SOCKET = conn
|
CLIENT_SOCKET = conn
|
||||||
|
@ -63,7 +150,7 @@ func ListenAndServe(proto, addr string, service Service) error {
|
||||||
|
|
||||||
// Parse an rcli call on a new connection, and pass it to `service` if it
|
// Parse an rcli call on a new connection, and pass it to `service` if it
|
||||||
// is valid.
|
// is valid.
|
||||||
func Serve(conn io.ReadWriter, service Service) error {
|
func Serve(conn DockerConn, service Service) error {
|
||||||
r := bufio.NewReader(conn)
|
r := bufio.NewReader(conn)
|
||||||
var args []string
|
var args []string
|
||||||
if line, err := r.ReadString('\n'); err != nil {
|
if line, err := r.ReadString('\n'); err != nil {
|
||||||
|
|
|
@ -13,10 +13,49 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DockerConnOptions struct {
|
||||||
|
RawTerminal bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type DockerConn interface {
|
||||||
|
io.ReadWriteCloser
|
||||||
|
CloseWrite() error
|
||||||
|
CloseRead() error
|
||||||
|
GetOptions() *DockerConnOptions
|
||||||
|
SetOptionRawTerminal()
|
||||||
|
}
|
||||||
|
|
||||||
|
var UnknownDockerProto = errors.New("Only TCP is actually supported by Docker at the moment")
|
||||||
|
|
||||||
|
func dialDocker(proto string, addr string) (DockerConn, error) {
|
||||||
|
conn, err := net.Dial(proto, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch i := conn.(type) {
|
||||||
|
case *net.TCPConn:
|
||||||
|
return NewDockerTCPConn(i, true), nil
|
||||||
|
}
|
||||||
|
return nil, UnknownDockerProto
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDockerFromConn(conn net.Conn, client bool) (DockerConn, error) {
|
||||||
|
switch i := conn.(type) {
|
||||||
|
case *net.TCPConn:
|
||||||
|
return NewDockerTCPConn(i, client), nil
|
||||||
|
}
|
||||||
|
return nil, UnknownDockerProto
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDockerServerConn(conn net.Conn) (DockerConn, error) {
|
||||||
|
return newDockerFromConn(conn, false)
|
||||||
|
}
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
Name() string
|
Name() string
|
||||||
Help() string
|
Help() string
|
||||||
|
@ -26,11 +65,11 @@ type Cmd func(io.ReadCloser, io.Writer, ...string) error
|
||||||
type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error
|
type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error
|
||||||
|
|
||||||
// FIXME: For reverse compatibility
|
// FIXME: For reverse compatibility
|
||||||
func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func call(service Service, stdin io.ReadCloser, stdout DockerConn, args ...string) error {
|
||||||
return LocalCall(service, stdin, stdout, args...)
|
return LocalCall(service, stdin, stdout, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LocalCall(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func LocalCall(service Service, stdin io.ReadCloser, stdout DockerConn, args ...string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
args = []string{"help"}
|
args = []string{"help"}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue