diff --git a/components/engine/api.go b/components/engine/api.go index 6e2b425f04..b0ef30c0da 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -60,7 +60,7 @@ func getBoolParam(value string) (bool, error) { } func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - b, err := json.Marshal(srv.registry.GetAuthConfig()) + b, err := json.Marshal(srv.registry.GetAuthConfig(false)) if err != nil { return err } @@ -73,9 +73,9 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque if err := json.NewDecoder(r.Body).Decode(config); err != nil { return err } - - if config.Username == srv.registry.GetAuthConfig().Username { - config.Password = srv.registry.GetAuthConfig().Password + authConfig := srv.registry.GetAuthConfig(true) + if config.Username == authConfig.Username { + config.Password = authConfig.Password } newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root) @@ -686,6 +686,5 @@ func ListenAndServe(addr string, srv *Server, logging bool) error { r.Path(localRoute).Methods(localMethod).HandlerFunc(f) } } - return http.ListenAndServe(addr, r) } diff --git a/components/engine/api_test.go b/components/engine/api_test.go index de4289728e..06413e1303 100644 --- a/components/engine/api_test.go +++ b/components/engine/api_test.go @@ -56,7 +56,7 @@ func TestGetAuth(t *testing.T) { t.Fatalf("%d OK or 0 expected, received %d\n", http.StatusOK, r.Code) } - newAuthConfig := srv.registry.GetAuthConfig() + newAuthConfig := srv.registry.GetAuthConfig(false) if newAuthConfig.Username != authConfig.Username || newAuthConfig.Email != authConfig.Email { t.Fatalf("The auth configuration hasn't been set correctly") diff --git a/components/engine/commands.go b/components/engine/commands.go index 099c3686cc..1b94fea45a 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -32,15 +32,19 @@ var ( GIT_COMMIT string ) -func ParseCommands(args ...string) error { - cli := NewDockerCli("0.0.0.0", 4243) +func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { + methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) + return reflect.TypeOf(cli).MethodByName(methodName) +} + +func ParseCommands(addr string, port int, args ...string) error { + cli := NewDockerCli(addr, port) if len(args) > 0 { - methodName := "Cmd" + strings.ToUpper(args[0][:1]) + strings.ToLower(args[0][1:]) - method, exists := reflect.TypeOf(cli).MethodByName(methodName) + method, exists := cli.getMethod(args[0]) if !exists { fmt.Println("Error: Command not found:", args[0]) - return cli.CmdHelp(args...) + return cli.CmdHelp(args[1:]...) } ret := method.Func.CallSlice([]reflect.Value{ reflect.ValueOf(cli), @@ -55,7 +59,19 @@ func ParseCommands(args ...string) error { } func (cli *DockerCli) CmdHelp(args ...string) error { - help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n" + if len(args) > 0 { + method, exists := cli.getMethod(args[0]) + if !exists { + fmt.Println("Error: Command not found:", args[0]) + } else { + method.Func.CallSlice([]reflect.Value{ + reflect.ValueOf(cli), + reflect.ValueOf([]string{"--help"}), + })[0].Interface() + return nil + } + } + help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=\"%s:%d\": Host:port to bind/connect to\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", cli.addr, cli.port) for cmd, description := range map[string]string{ "attach": "Attach to a running container", "build": "Build a container from Dockerfile or via stdin", @@ -609,39 +625,13 @@ func (cli *DockerCli) CmdPush(args ...string) error { return nil } - body, _, err := cli.call("GET", "/auth", nil) + username, err := cli.checkIfLogged(*registry == "", "push") if err != nil { return err } - var out auth.AuthConfig - err = json.Unmarshal(body, &out) - if err != nil { - return err - } - - // If the login failed AND we're using the index, abort - if *registry == "" && out.Username == "" { - if err := cli.CmdLogin(args...); err != nil { - return err - } - - body, _, err = cli.call("GET", "/auth", nil) - if err != nil { - return err - } - err = json.Unmarshal(body, &out) - if err != nil { - return err - } - - if out.Username == "" { - return fmt.Errorf("Please login prior to push. ('docker login')") - } - } - if len(strings.SplitN(name, "/", 2)) == 1 { - return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in / (ex: %s/%s)", out.Username, name) + return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in / (ex: %s/%s)", username, name) } v := url.Values{} @@ -672,6 +662,12 @@ func (cli *DockerCli) CmdPull(args ...string) error { remote = remoteParts[0] } + if strings.Contains(remote, "/") { + if _, err := cli.checkIfLogged(true, "pull"); err != nil { + return err + } + } + v := url.Values{} v.Set("fromImage", remote) v.Set("tag", *tag) @@ -1161,6 +1157,40 @@ func (cli *DockerCli) CmdRun(args ...string) error { return nil } +func (cli *DockerCli) checkIfLogged(condition bool, action string) (string, error) { + body, _, err := cli.call("GET", "/auth", nil) + if err != nil { + return "", err + } + + var out auth.AuthConfig + err = json.Unmarshal(body, &out) + if err != nil { + return "", err + } + + // If condition AND the login failed + if condition && out.Username == "" { + if err := cli.CmdLogin(""); err != nil { + return "", err + } + + body, _, err = cli.call("GET", "/auth", nil) + if err != nil { + return "", err + } + err = json.Unmarshal(body, &out) + if err != nil { + return "", err + } + + if out.Username == "" { + return "", fmt.Errorf("Please login prior to %s. ('docker login')", action) + } + } + return out.Username, nil +} + func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) { var params io.Reader if data != nil { @@ -1171,7 +1201,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, params = bytes.NewBuffer(buf) } - req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%f", cli.host, cli.port, API_VERSION)+path, params) + req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.addr, cli.port, API_VERSION, path), params) if err != nil { return nil, -1, err } @@ -1203,7 +1233,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e if (method == "POST" || method == "PUT") && in == nil { in = bytes.NewReader([]byte{}) } - req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%f%s", cli.host, cli.port, API_VERSION, path), in) + req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.addr, cli.port, API_VERSION, path), in) if err != nil { return err } @@ -1234,12 +1264,12 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e } func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error { - req, err := http.NewRequest(method, fmt.Sprintf("/v%f%s", API_VERSION, path), nil) + req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", API_VERSION, path), nil) if err != nil { return err } req.Header.Set("Content-Type", "plain/text") - dial, err := net.Dial("tcp", fmt.Sprintf("%s:%d", cli.host, cli.port)) + dial, err := net.Dial("tcp", fmt.Sprintf("%s:%d", cli.addr, cli.port)) if err != nil { return err } @@ -1320,8 +1350,8 @@ func Subcmd(name, signature, description string) *flag.FlagSet { return flags } -func NewDockerCli(host string, port int) *DockerCli { - return &DockerCli{host, port} +func NewDockerCli(addr string, port int) *DockerCli { + return &DockerCli{addr, port} } type DockerCli struct { diff --git a/components/engine/docker/docker.go b/components/engine/docker/docker.go index c8c1a65603..7b8aa7f858 100644 --- a/components/engine/docker/docker.go +++ b/components/engine/docker/docker.go @@ -10,6 +10,7 @@ import ( "os" "os/signal" "strconv" + "strings" "syscall" ) @@ -23,18 +24,38 @@ func main() { docker.SysInit() return } + host := "127.0.0.1" + port := 4243 // FIXME: Switch d and D ? (to be more sshd like) flDaemon := flag.Bool("d", false, "Daemon mode") flDebug := flag.Bool("D", false, "Debug mode") flAutoRestart := flag.Bool("r", false, "Restart previously running containers") bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge") pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID") + flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to") flag.Parse() if *bridgeName != "" { docker.NetworkBridgeIface = *bridgeName } else { docker.NetworkBridgeIface = docker.DefaultNetworkBridge } + + if strings.Contains(*flHost, ":") { + hostParts := strings.Split(*flHost, ":") + if len(hostParts) != 2 { + log.Fatal("Invalid bind address format.") + os.Exit(-1) + } + if hostParts[0] != "" { + host = hostParts[0] + } + if p, err := strconv.Atoi(hostParts[1]); err == nil { + port = p + } + } else { + host = *flHost + } + if *flDebug { os.Setenv("DEBUG", "1") } @@ -44,12 +65,12 @@ func main() { flag.Usage() return } - if err := daemon(*pidfile, *flAutoRestart); err != nil { + if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil { log.Fatal(err) os.Exit(-1) } } else { - if err := docker.ParseCommands(flag.Args()...); err != nil { + if err := docker.ParseCommands(host, port, flag.Args()...); err != nil { log.Fatal(err) os.Exit(-1) } @@ -83,7 +104,10 @@ func removePidFile(pidfile string) { } } -func daemon(pidfile string, autoRestart bool) error { +func daemon(pidfile, addr string, port int, autoRestart bool) error { + if addr != "127.0.0.1" { + log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") + } if err := createPidFile(pidfile); err != nil { log.Fatal(err) } @@ -103,5 +127,5 @@ func daemon(pidfile string, autoRestart bool) error { return err } - return docker.ListenAndServe("0.0.0.0:4243", server, true) + return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true) } diff --git a/components/engine/docs/sources/commandline/cli.rst b/components/engine/docs/sources/commandline/cli.rst index 1a341d3e5d..02691b4f56 100644 --- a/components/engine/docs/sources/commandline/cli.rst +++ b/components/engine/docs/sources/commandline/cli.rst @@ -14,7 +14,8 @@ To list available commands, either run ``docker`` with no parameters or execute ``docker help``:: $ docker - Usage: docker COMMAND [arg...] + Usage: docker [OPTIONS] COMMAND [arg...] + -H="127.0.0.1:4243": Host:port to bind/connect to A self-sufficient runtime for linux containers. diff --git a/components/engine/docs/sources/installation/ubuntulinux.rst b/components/engine/docs/sources/installation/ubuntulinux.rst index 6d2d3e671d..0aaa76250e 100644 --- a/components/engine/docs/sources/installation/ubuntulinux.rst +++ b/components/engine/docs/sources/installation/ubuntulinux.rst @@ -38,7 +38,7 @@ Due to a bug in LXC docker works best on the 3.8 kernel. Precise comes with a 3. .. code-block:: bash # install the backported kernel - sudo apt-get update && sudo apt-get install linux-image-3.8.0-19-generic + sudo apt-get update && sudo apt-get install linux-image-generic-lts-raring # reboot sudo reboot diff --git a/components/engine/docs/sources/use/basics.rst b/components/engine/docs/sources/use/basics.rst index 6ae0711f11..378028703c 100644 --- a/components/engine/docs/sources/use/basics.rst +++ b/components/engine/docs/sources/use/basics.rst @@ -33,6 +33,19 @@ Running an interactive shell # allocate a tty, attach stdin and stdout docker run -i -t base /bin/bash +Bind Docker to another host/port +-------------------------------- + +If you want Docker to listen to another port and bind to another ip +use -host and -port on both deamon and client + +.. code-block:: bash + + # Run docker in daemon mode + sudo /docker -H 0.0.0.0:5555 & + # Download a base image + docker -H :5555 pull base + Starting a long-running worker process -------------------------------------- diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index ce9b4b4ac7..bd361b5e74 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -428,9 +428,14 @@ func (r *Registry) ResetClient(authConfig *auth.AuthConfig) { r.client.Jar = cookiejar.NewCookieJar() } -func (r *Registry) GetAuthConfig() *auth.AuthConfig { +func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig { + password := "" + if withPasswd { + password = r.authConfig.Password + } return &auth.AuthConfig{ Username: r.authConfig.Username, + Password: password, Email: r.authConfig.Email, } }