Просмотр исходного кода

add basic support for unix sockets

Victor Vieux 12 лет назад
Родитель
Сommit
3adf9ce04e
5 измененных файлов с 109 добавлено и 45 удалено
  1. 16 3
      api.go
  2. 2 2
      builder_client.go
  3. 32 15
      commands.go
  4. 57 25
      docker/docker.go
  5. 2 0
      utils/utils.go

+ 16 - 3
api.go

@@ -8,12 +8,16 @@ import (
 	"github.com/gorilla/mux"
 	"io"
 	"log"
+	"net"
 	"net/http"
+	"os"
 	"strconv"
 	"strings"
 )
 
 const APIVERSION = 1.2
+const DEFAULTHTTPHOST string = "127.0.0.1"
+const DEFAULTHTTPPORT int = 4243
 
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
 	conn, _, err := w.(http.Hijacker).Hijack()
@@ -848,12 +852,21 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
 	return r, nil
 }
 
-func ListenAndServe(addr string, srv *Server, logging bool) error {
-	log.Printf("Listening for HTTP on %s\n", addr)
+func ListenAndServe(proto, addr string, srv *Server, logging bool) error {
+	log.Printf("Listening for HTTP on %s (%s)\n", addr, proto)
 
 	r, err := createRouter(srv, logging)
 	if err != nil {
 		return err
 	}
-	return http.ListenAndServe(addr, r)
+	l, e := net.Listen(proto, addr)
+	if e != nil {
+		return e
+	}
+	//as the daemon is launched as root, change to permission of the socket to allow non-root to connect
+	if proto == "unix" {
+		os.Chmod(addr, 0777)
+	}
+	httpSrv := http.Server{Addr: addr, Handler: r}
+	return httpSrv.Serve(l)
 }

+ 2 - 2
builder_client.go

@@ -304,9 +304,9 @@ func (b *builderClient) Build(dockerfile, context io.Reader) (string, error) {
 	return "", fmt.Errorf("An error occured during the build\n")
 }
 
-func NewBuilderClient(addr string, port int) BuildFile {
+func NewBuilderClient(proto, addr string) BuildFile {
 	return &builderClient{
-		cli:           NewDockerCli(addr, port),
+		cli:           NewDockerCli(proto, addr),
 		config:        &Config{},
 		tmpContainers: make(map[string]struct{}),
 		tmpImages:     make(map[string]struct{}),

+ 32 - 15
commands.go

@@ -40,8 +40,8 @@ func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
 	return reflect.TypeOf(cli).MethodByName(methodName)
 }
 
-func ParseCommands(addr string, port int, args ...string) error {
-	cli := NewDockerCli(addr, port)
+func ParseCommands(proto, addr string, args ...string) error {
+	cli := NewDockerCli(proto, addr)
 
 	if len(args) > 0 {
 		method, exists := cli.getMethod(args[0])
@@ -74,7 +74,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 			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.host, cli.port)
+	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", DEFAULTHTTPHOST, DEFAULTHTTPPORT)
 	for _, command := range [][2]string{
 		{"attach", "Attach to a running container"},
 		{"build", "Build a container from a Dockerfile"},
@@ -197,7 +197,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	v := &url.Values{}
 	v.Set("t", *tag)
 	// Send the multipart request with correct content-type
-	req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s?%s", cli.host, cli.port, "/build", v.Encode()), multipartBody)
+	req, err := http.NewRequest("POST", fmt.Sprintf("/v%s/build?%s", APIVERSION, v.Encode()), multipartBody)
 	if err != nil {
 		return err
 	}
@@ -206,8 +206,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		req.Header.Set("X-Docker-Context-Compression", compression.Flag())
 		fmt.Println("Uploading Context...")
 	}
-
-	resp, err := http.DefaultClient.Do(req)
+	dial, err := net.Dial(cli.proto, cli.addr)
+	if err != nil {
+		return err
+	}
+	clientconn := httputil.NewClientConn(dial, nil)
+	resp, err := clientconn.Do(req)
+	defer clientconn.Close()
 	if err != nil {
 		return err
 	}
@@ -1339,7 +1344,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%g%s", cli.host, cli.port, APIVERSION, path), params)
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
 	if err != nil {
 		return nil, -1, err
 	}
@@ -1349,7 +1354,13 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
 	} else if method == "POST" {
 		req.Header.Set("Content-Type", "plain/text")
 	}
-	resp, err := http.DefaultClient.Do(req)
+	dial, err := net.Dial(cli.proto, cli.addr)
+	if err != nil {
+		return nil, -1, err
+	}
+	clientconn := httputil.NewClientConn(dial, nil)
+	resp, err := clientconn.Do(req)
+	defer clientconn.Close()
 	if err != nil {
 		if strings.Contains(err.Error(), "connection refused") {
 			return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
@@ -1374,7 +1385,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%g%s", cli.host, cli.port, APIVERSION, path), in)
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
 	if err != nil {
 		return err
 	}
@@ -1382,7 +1393,13 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
 	if method == "POST" {
 		req.Header.Set("Content-Type", "plain/text")
 	}
-	resp, err := http.DefaultClient.Do(req)
+	dial, err := net.Dial(cli.proto, cli.addr)
+	if err != nil {
+		return err
+	}
+	clientconn := httputil.NewClientConn(dial, nil)
+	resp, err := clientconn.Do(req)
+	defer clientconn.Close()
 	if err != nil {
 		if strings.Contains(err.Error(), "connection refused") {
 			return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
@@ -1432,7 +1449,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
 		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(cli.proto, cli.addr)
 	if err != nil {
 		return err
 	}
@@ -1515,13 +1532,13 @@ func Subcmd(name, signature, description string) *flag.FlagSet {
 	return flags
 }
 
-func NewDockerCli(addr string, port int) *DockerCli {
+func NewDockerCli(proto, addr string) *DockerCli {
 	authConfig, _ := auth.LoadConfig(os.Getenv("HOME"))
-	return &DockerCli{addr, port, authConfig}
+	return &DockerCli{proto, addr, authConfig}
 }
 
 type DockerCli struct {
-	host       string
-	port       int
+	proto      string
+	addr       string
 	authConfig *auth.AuthConfig
 }

+ 57 - 25
docker/docker.go

@@ -24,15 +24,13 @@ 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")
+	flHost := flag.String("H", fmt.Sprintf("%s:%d", docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT), "Host:port to bind/connect to")
 	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
 	flDns := flag.String("dns", "", "Set custom dns servers")
 	flag.Parse()
@@ -42,21 +40,8 @@ func main() {
 		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
-	}
+	protoAddr := parseHost(*flHost)
+	protoAddrs := []string{protoAddr}
 
 	if *flDebug {
 		os.Setenv("DEBUG", "1")
@@ -67,12 +52,13 @@ func main() {
 			flag.Usage()
 			return
 		}
-		if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors, *flDns); err != nil {
+		if err := daemon(*pidfile, protoAddrs, *flAutoRestart, *flEnableCors, *flDns); err != nil {
 			log.Fatal(err)
 			os.Exit(-1)
 		}
 	} else {
-		if err := docker.ParseCommands(host, port, flag.Args()...); err != nil {
+		protoAddrParts := strings.SplitN(protoAddrs[0], "://", 2)
+		if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
 			log.Fatal(err)
 			os.Exit(-1)
 		}
@@ -106,10 +92,7 @@ func removePidFile(pidfile string) {
 	}
 }
 
-func daemon(pidfile, addr string, port int, autoRestart, enableCors bool, flDns string) 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 /!\\")
-	}
+func daemon(pidfile string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error {
 	if err := createPidFile(pidfile); err != nil {
 		log.Fatal(err)
 	}
@@ -131,6 +114,55 @@ func daemon(pidfile, addr string, port int, autoRestart, enableCors bool, flDns
 	if err != nil {
 		return err
 	}
+	chErrors := make(chan error, len(protoAddrs))
+	for _, protoAddr := range protoAddrs {
+		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
+		if protoAddrParts[0] == "unix" {
+			syscall.Unlink(protoAddrParts[1]);
+		} else if protoAddrParts[0] == "tcp" {
+			if !strings.HasPrefix(protoAddrParts[1], "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 /!\\")
+			}
+		} else {
+			log.Fatal("Invalid protocol format.")
+			os.Exit(-1)
+		}
+		go func() {
+			chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
+		}()
+	}
+	for i :=0 ; i < len(protoAddrs); i+=1 {
+		err := <-chErrors
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
 
-	return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true)
+func parseHost(addr string) string {
+	if strings.HasPrefix(addr, "unix://") {
+		return addr
+	}
+	host :=	docker.DEFAULTHTTPHOST
+	port := docker.DEFAULTHTTPPORT
+	if strings.HasPrefix(addr, "tcp://") {
+		addr = strings.TrimPrefix(addr, "tcp://")
+	}
+	if strings.Contains(addr, ":") {
+		hostParts := strings.Split(addr, ":")
+		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 = addr
+	}
+	return fmt.Sprintf("tcp://%s:%d", host, port)
 }

+ 2 - 0
utils/utils.go

@@ -652,3 +652,5 @@ func CheckLocalDns() bool {
 	}
 	return false
 }
+
+