Browse Source

Merge pull request #12495 from LK4D4/apiserver_refactoring

Refactoring of api/server for reduce engine usage
Phil Estes 10 years ago
parent
commit
f0016c5917

+ 205 - 209
api/server/server.go

@@ -40,10 +40,6 @@ import (
 	"github.com/docker/docker/utils"
 )
 
-var (
-	activationLock = make(chan struct{})
-)
-
 type ServerConfig struct {
 	Logging     bool
 	EnableCors  bool
@@ -57,6 +53,75 @@ type ServerConfig struct {
 	TlsKey      string
 }
 
+type Server struct {
+	daemon *daemon.Daemon
+	cfg    *ServerConfig
+	router *mux.Router
+	start  chan struct{}
+
+	// TODO: delete engine
+	eng *engine.Engine
+}
+
+func New(cfg *ServerConfig, eng *engine.Engine) *Server {
+	srv := &Server{
+		cfg:   cfg,
+		start: make(chan struct{}),
+		eng:   eng,
+	}
+	r := createRouter(srv, eng)
+	srv.router = r
+	return srv
+}
+
+func (s *Server) SetDaemon(d *daemon.Daemon) {
+	s.daemon = d
+}
+
+type serverCloser interface {
+	Serve() error
+	Close() error
+}
+
+// ServeApi loops through all of the protocols sent in to docker and spawns
+// off a go routine to setup a serving http.Server for each.
+func (s *Server) ServeApi(protoAddrs []string) error {
+	var chErrors = make(chan error, len(protoAddrs))
+
+	for _, protoAddr := range protoAddrs {
+		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
+		if len(protoAddrParts) != 2 {
+			return fmt.Errorf("bad format, expected PROTO://ADDR")
+		}
+		go func(proto, addr string) {
+			logrus.Infof("Listening for HTTP on %s (%s)", proto, addr)
+			srv, err := s.newServer(proto, addr)
+			if err != nil {
+				chErrors <- err
+				return
+			}
+			s.eng.OnShutdown(func() {
+				if err := srv.Close(); err != nil {
+					logrus.Error(err)
+				}
+			})
+			if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
+				err = nil
+			}
+			chErrors <- err
+		}(protoAddrParts[0], protoAddrParts[1])
+	}
+
+	for i := 0; i < len(protoAddrs); i++ {
+		err := <-chErrors
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 type HttpServer struct {
 	srv *http.Server
 	l   net.Listener
@@ -180,19 +245,14 @@ func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
 	}
 }
 
-func getDaemon(eng *engine.Engine) *daemon.Daemon {
-	return eng.HackGetGlobalVar("httpapi.daemon").(*daemon.Daemon)
-}
-
-func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	var config *registry.AuthConfig
 	err := json.NewDecoder(r.Body).Decode(&config)
 	r.Body.Close()
 	if err != nil {
 		return err
 	}
-	d := getDaemon(eng)
-	status, err := d.RegistryService.Auth(config)
+	status, err := s.daemon.RegistryService.Auth(config)
 	if err != nil {
 		return err
 	}
@@ -201,7 +261,7 @@ func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter
 	})
 }
 
-func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	w.Header().Set("Content-Type", "application/json")
 
 	v := &types.Version{
@@ -219,7 +279,7 @@ func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWrit
 	return writeJSON(w, http.StatusOK, v)
 }
 
-func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -247,7 +307,7 @@ func postContainersKill(eng *engine.Engine, version version.Version, w http.Resp
 		}
 	}
 
-	if err = getDaemon(eng).ContainerKill(name, sig); err != nil {
+	if err = s.daemon.ContainerKill(name, sig); err != nil {
 		return err
 	}
 
@@ -255,7 +315,7 @@ func postContainersKill(eng *engine.Engine, version version.Version, w http.Resp
 	return nil
 }
 
-func postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -264,8 +324,7 @@ func postContainersPause(eng *engine.Engine, version version.Version, w http.Res
 	}
 
 	name := vars["name"]
-	d := getDaemon(eng)
-	cont, err := d.Get(name)
+	cont, err := s.daemon.Get(name)
 	if err != nil {
 		return err
 	}
@@ -280,7 +339,7 @@ func postContainersPause(eng *engine.Engine, version version.Version, w http.Res
 	return nil
 }
 
-func postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -289,8 +348,7 @@ func postContainersUnpause(eng *engine.Engine, version version.Version, w http.R
 	}
 
 	name := vars["name"]
-	d := getDaemon(eng)
-	cont, err := d.Get(name)
+	cont, err := s.daemon.Get(name)
 	if err != nil {
 		return err
 	}
@@ -305,17 +363,15 @@ func postContainersUnpause(eng *engine.Engine, version version.Version, w http.R
 	return nil
 }
 
-func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 
-	d := getDaemon(eng)
-
-	return d.ContainerExport(vars["name"], w)
+	return s.daemon.ContainerExport(vars["name"], w)
 }
 
-func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -327,7 +383,7 @@ func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseW
 		All:    toBool(r.Form.Get("all")),
 	}
 
-	images, err := getDaemon(eng).Repositories().Images(&imagesConfig)
+	images, err := s.daemon.Repositories().Images(&imagesConfig)
 	if err != nil {
 		return err
 	}
@@ -356,7 +412,7 @@ func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseW
 	return writeJSON(w, http.StatusOK, legacyImages)
 }
 
-func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if version.GreaterThan("1.6") {
 		w.WriteHeader(http.StatusNotFound)
 		return fmt.Errorf("This is now implemented in the client.")
@@ -365,10 +421,10 @@ func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWr
 	return nil
 }
 
-func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	w.Header().Set("Content-Type", "application/json")
 
-	info, err := getDaemon(eng).SystemInfo()
+	info, err := s.daemon.SystemInfo()
 	if err != nil {
 		return err
 	}
@@ -376,7 +432,7 @@ func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter,
 	return writeJSON(w, http.StatusOK, info)
 }
 
-func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -427,7 +483,7 @@ func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWrite
 		return true
 	}
 
-	d := getDaemon(eng)
+	d := s.daemon
 	es := d.EventsService
 	w.Header().Set("Content-Type", "application/json")
 	enc := json.NewEncoder(utils.NewWriteFlusher(w))
@@ -480,13 +536,13 @@ func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWrite
 	}
 }
 
-func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 
 	name := vars["name"]
-	history, err := getDaemon(eng).Repositories().History(name)
+	history, err := s.daemon.Repositories().History(name)
 	if err != nil {
 		return err
 	}
@@ -494,14 +550,13 @@ func getImagesHistory(eng *engine.Engine, version version.Version, w http.Respon
 	return writeJSON(w, http.StatusOK, history)
 }
 
-func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 
 	name := vars["name"]
-	d := getDaemon(eng)
-	cont, err := d.Get(name)
+	cont, err := s.daemon.Get(name)
 	if err != nil {
 		return err
 	}
@@ -514,7 +569,7 @@ func getContainersChanges(eng *engine.Engine, version version.Version, w http.Re
 	return writeJSON(w, http.StatusOK, changes)
 }
 
-func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if version.LessThan("1.4") {
 		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
 	}
@@ -527,7 +582,7 @@ func getContainersTop(eng *engine.Engine, version version.Version, w http.Respon
 		return err
 	}
 
-	procList, err := getDaemon(eng).ContainerTop(vars["name"], r.Form.Get("ps_args"))
+	procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
 	if err != nil {
 		return err
 	}
@@ -535,7 +590,7 @@ func getContainersTop(eng *engine.Engine, version version.Version, w http.Respon
 	return writeJSON(w, http.StatusOK, procList)
 }
 
-func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -556,7 +611,7 @@ func getContainersJSON(eng *engine.Engine, version version.Version, w http.Respo
 		config.Limit = limit
 	}
 
-	containers, err := getDaemon(eng).Containers(config)
+	containers, err := s.daemon.Containers(config)
 	if err != nil {
 		return err
 	}
@@ -564,7 +619,7 @@ func getContainersJSON(eng *engine.Engine, version version.Version, w http.Respo
 	return writeJSON(w, http.StatusOK, containers)
 }
 
-func getContainersStats(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getContainersStats(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -572,12 +627,10 @@ func getContainersStats(eng *engine.Engine, version version.Version, w http.Resp
 		return fmt.Errorf("Missing parameter")
 	}
 
-	d := getDaemon(eng)
-
-	return d.ContainerStats(vars["name"], utils.NewWriteFlusher(w))
+	return s.daemon.ContainerStats(vars["name"], utils.NewWriteFlusher(w))
 }
 
-func getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -600,15 +653,14 @@ func getContainersLogs(eng *engine.Engine, version version.Version, w http.Respo
 		OutStream:  utils.NewWriteFlusher(w),
 	}
 
-	d := getDaemon(eng)
-	if err := d.ContainerLogs(vars["name"], logsConfig); err != nil {
+	if err := s.daemon.ContainerLogs(vars["name"], logsConfig); err != nil {
 		fmt.Fprintf(w, "Error running logs job: %s\n", err)
 	}
 
 	return nil
 }
 
-func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -616,18 +668,17 @@ func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseW
 		return fmt.Errorf("Missing parameter")
 	}
 
-	d := getDaemon(eng)
 	repo := r.Form.Get("repo")
 	tag := r.Form.Get("tag")
 	force := toBool(r.Form.Get("force"))
-	if err := d.Repositories().Tag(repo, tag, vars["name"], force); err != nil {
+	if err := s.daemon.Repositories().Tag(repo, tag, vars["name"], force); err != nil {
 		return err
 	}
 	w.WriteHeader(http.StatusCreated)
 	return nil
 }
 
-func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -653,9 +704,7 @@ func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWrit
 		Config:  r.Body,
 	}
 
-	d := getDaemon(eng)
-
-	imgID, err := d.ContainerCommit(cont, containerCommitConfig)
+	imgID, err := s.daemon.ContainerCommit(cont, containerCommitConfig)
 	if err != nil {
 		return err
 	}
@@ -666,7 +715,7 @@ func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWrit
 }
 
 // Creates an image from Pull or from Import
-func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -687,8 +736,6 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon
 		}
 	}
 
-	d := getDaemon(eng)
-
 	if image != "" { //pull
 		if tag == "" {
 			image, tag = parsers.ParseRepositoryTag(image)
@@ -713,7 +760,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon
 			imagePullConfig.Json = false
 		}
 
-		if err := d.Repositories().Pull(image, tag, imagePullConfig, eng); err != nil {
+		if err := s.daemon.Repositories().Pull(image, tag, imagePullConfig, eng); err != nil {
 			return err
 		}
 	} else { //import
@@ -734,7 +781,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon
 			imageImportConfig.Json = false
 		}
 
-		if err := d.Repositories().Import(src, repo, tag, imageImportConfig, eng); err != nil {
+		if err := s.daemon.Repositories().Import(src, repo, tag, imageImportConfig, eng); err != nil {
 			return err
 		}
 
@@ -743,7 +790,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon
 	return nil
 }
 
-func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -766,15 +813,14 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons
 			headers[k] = v
 		}
 	}
-	d := getDaemon(eng)
-	query, err := d.RegistryService.Search(r.Form.Get("term"), config, headers)
+	query, err := s.daemon.RegistryService.Search(r.Form.Get("term"), config, headers)
 	if err != nil {
 		return err
 	}
 	return json.NewEncoder(w).Encode(query.Results)
 }
 
-func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -826,7 +872,7 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response
 	return nil
 }
 
-func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -846,14 +892,14 @@ func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWr
 	return job.Run()
 }
 
-func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	job := eng.Job("load")
 	job.Stdin.Add(r.Body)
 	job.Stdout.Add(w)
 	return job.Run()
 }
 
-func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return nil
 	}
@@ -870,7 +916,7 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re
 		return err
 	}
 
-	containerId, warnings, err := getDaemon(eng).ContainerCreate(name, config, hostConfig)
+	containerId, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig)
 	if err != nil {
 		return err
 	}
@@ -881,7 +927,7 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re
 	})
 }
 
-func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -889,13 +935,12 @@ func postContainersRestart(eng *engine.Engine, version version.Version, w http.R
 		return fmt.Errorf("Missing parameter")
 	}
 
-	s, err := strconv.Atoi(r.Form.Get("t"))
+	timeout, err := strconv.Atoi(r.Form.Get("t"))
 	if err != nil {
 		return err
 	}
 
-	d := getDaemon(eng)
-	if err := d.ContainerRestart(vars["name"], s); err != nil {
+	if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
 		return err
 	}
 
@@ -904,7 +949,7 @@ func postContainersRestart(eng *engine.Engine, version version.Version, w http.R
 	return nil
 }
 
-func postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -912,17 +957,16 @@ func postContainerRename(eng *engine.Engine, version version.Version, w http.Res
 		return fmt.Errorf("Missing parameter")
 	}
 
-	d := getDaemon(eng)
 	name := vars["name"]
 	newName := r.Form.Get("name")
-	if err := d.ContainerRename(name, newName); err != nil {
+	if err := s.daemon.ContainerRename(name, newName); err != nil {
 		return err
 	}
 	w.WriteHeader(http.StatusNoContent)
 	return nil
 }
 
-func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -931,14 +975,13 @@ func deleteContainers(eng *engine.Engine, version version.Version, w http.Respon
 	}
 
 	name := vars["name"]
-	d := getDaemon(eng)
 	config := &daemon.ContainerRmConfig{
 		ForceRemove:  toBool(r.Form.Get("force")),
 		RemoveVolume: toBool(r.Form.Get("v")),
 		RemoveLink:   toBool(r.Form.Get("link")),
 	}
 
-	if err := d.ContainerRm(name, config); err != nil {
+	if err := s.daemon.ContainerRm(name, config); err != nil {
 		// Force a 404 for the empty string
 		if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
 			return fmt.Errorf("no such id: \"\"")
@@ -951,7 +994,7 @@ func deleteContainers(eng *engine.Engine, version version.Version, w http.Respon
 	return nil
 }
 
-func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -959,12 +1002,11 @@ func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWr
 		return fmt.Errorf("Missing parameter")
 	}
 
-	d := getDaemon(eng)
 	name := vars["name"]
 	force := toBool(r.Form.Get("force"))
 	noprune := toBool(r.Form.Get("noprune"))
 
-	list, err := d.ImageDelete(name, force, noprune)
+	list, err := s.daemon.ImageDelete(name, force, noprune)
 	if err != nil {
 		return err
 	}
@@ -972,7 +1014,7 @@ func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWr
 	return writeJSON(w, http.StatusOK, list)
 }
 
-func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -997,7 +1039,7 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res
 		hostConfig = c
 	}
 
-	if err := getDaemon(eng).ContainerStart(vars["name"], hostConfig); err != nil {
+	if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
 		if err.Error() == "Container already started" {
 			w.WriteHeader(http.StatusNotModified)
 			return nil
@@ -1008,7 +1050,7 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res
 	return nil
 }
 
-func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -1016,13 +1058,12 @@ func postContainersStop(eng *engine.Engine, version version.Version, w http.Resp
 		return fmt.Errorf("Missing parameter")
 	}
 
-	d := getDaemon(eng)
 	seconds, err := strconv.Atoi(r.Form.Get("t"))
 	if err != nil {
 		return err
 	}
 
-	if err := d.ContainerStop(vars["name"], seconds); err != nil {
+	if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
 		if err.Error() == "Container already stopped" {
 			w.WriteHeader(http.StatusNotModified)
 			return nil
@@ -1034,14 +1075,13 @@ func postContainersStop(eng *engine.Engine, version version.Version, w http.Resp
 	return nil
 }
 
-func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 
 	name := vars["name"]
-	d := getDaemon(eng)
-	cont, err := d.Get(name)
+	cont, err := s.daemon.Get(name)
 	if err != nil {
 		return err
 	}
@@ -1053,7 +1093,7 @@ func postContainersWait(eng *engine.Engine, version version.Version, w http.Resp
 	})
 }
 
-func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -1070,8 +1110,7 @@ func postContainersResize(eng *engine.Engine, version version.Version, w http.Re
 		return err
 	}
 
-	d := getDaemon(eng)
-	cont, err := d.Get(vars["name"])
+	cont, err := s.daemon.Get(vars["name"])
 	if err != nil {
 		return err
 	}
@@ -1079,7 +1118,7 @@ func postContainersResize(eng *engine.Engine, version version.Version, w http.Re
 	return cont.Resize(height, width)
 }
 
-func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -1087,9 +1126,7 @@ func postContainersAttach(eng *engine.Engine, version version.Version, w http.Re
 		return fmt.Errorf("Missing parameter")
 	}
 
-	d := getDaemon(eng)
-
-	cont, err := d.Get(vars["name"])
+	cont, err := s.daemon.Get(vars["name"])
 	if err != nil {
 		return err
 	}
@@ -1136,16 +1173,14 @@ func postContainersAttach(eng *engine.Engine, version version.Version, w http.Re
 	return nil
 }
 
-func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
-	d := getDaemon(eng)
-
-	cont, err := d.Get(vars["name"])
+	cont, err := s.daemon.Get(vars["name"])
 	if err != nil {
 		return err
 	}
@@ -1164,7 +1199,7 @@ func wsContainersAttach(eng *engine.Engine, version version.Version, w http.Resp
 	return nil
 }
 
-func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -1176,13 +1211,12 @@ func getContainersByName(eng *engine.Engine, version version.Version, w http.Res
 	return job.Run()
 }
 
-func getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter 'id'")
 	}
 
-	d := getDaemon(eng)
-	eConfig, err := d.ContainerExecInspect(vars["id"])
+	eConfig, err := s.daemon.ContainerExecInspect(vars["id"])
 	if err != nil {
 		return err
 	}
@@ -1190,7 +1224,7 @@ func getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWri
 	return writeJSON(w, http.StatusOK, eConfig)
 }
 
-func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -1202,7 +1236,7 @@ func getImagesByName(eng *engine.Engine, version version.Version, w http.Respons
 	return job.Run()
 }
 
-func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if version.LessThan("1.3") {
 		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
 	}
@@ -1292,7 +1326,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
 	return nil
 }
 
-func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
@@ -1316,7 +1350,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
 		res = res[1:]
 	}
 
-	cont, err := getDaemon(eng).Get(vars["name"])
+	cont, err := s.daemon.Get(vars["name"])
 	if err != nil {
 		logrus.Errorf("%v", err)
 		if strings.Contains(strings.ToLower(err.Error()), "no such id") {
@@ -1342,7 +1376,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
 	return nil
 }
 
-func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return nil
 	}
@@ -1379,7 +1413,7 @@ func postContainerExecCreate(eng *engine.Engine, version version.Version, w http
 }
 
 // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
-func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return nil
 	}
@@ -1430,7 +1464,7 @@ func postContainerExecStart(eng *engine.Engine, version version.Version, w http.
 	return nil
 }
 
-func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
@@ -1447,12 +1481,10 @@ func postContainerExecResize(eng *engine.Engine, version version.Version, w http
 		return err
 	}
 
-	d := getDaemon(eng)
-
-	return d.ContainerExecResize(vars["name"], height, width)
+	return s.daemon.ContainerExecResize(vars["name"], height, width)
 }
 
-func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	w.WriteHeader(http.StatusOK)
 	return nil
 }
@@ -1463,7 +1495,7 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string
 	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
 }
 
-func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *Server) ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	_, err := w.Write([]byte{'O', 'K'})
 	return err
 }
@@ -1504,71 +1536,72 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local
 }
 
 // we keep enableCors just for legacy usage, need to be removed in the future
-func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders string, dockerVersion string) *mux.Router {
+func createRouter(s *Server, eng *engine.Engine) *mux.Router {
 	r := mux.NewRouter()
 	if os.Getenv("DEBUG") != "" {
 		ProfilerSetup(r, "/debug/")
 	}
 	m := map[string]map[string]HttpApiFunc{
 		"GET": {
-			"/_ping":                          ping,
-			"/events":                         getEvents,
-			"/info":                           getInfo,
-			"/version":                        getVersion,
-			"/images/json":                    getImagesJSON,
-			"/images/viz":                     getImagesViz,
-			"/images/search":                  getImagesSearch,
-			"/images/get":                     getImagesGet,
-			"/images/{name:.*}/get":           getImagesGet,
-			"/images/{name:.*}/history":       getImagesHistory,
-			"/images/{name:.*}/json":          getImagesByName,
-			"/containers/ps":                  getContainersJSON,
-			"/containers/json":                getContainersJSON,
-			"/containers/{name:.*}/export":    getContainersExport,
-			"/containers/{name:.*}/changes":   getContainersChanges,
-			"/containers/{name:.*}/json":      getContainersByName,
-			"/containers/{name:.*}/top":       getContainersTop,
-			"/containers/{name:.*}/logs":      getContainersLogs,
-			"/containers/{name:.*}/stats":     getContainersStats,
-			"/containers/{name:.*}/attach/ws": wsContainersAttach,
-			"/exec/{id:.*}/json":              getExecByID,
+			"/_ping":                          s.ping,
+			"/events":                         s.getEvents,
+			"/info":                           s.getInfo,
+			"/version":                        s.getVersion,
+			"/images/json":                    s.getImagesJSON,
+			"/images/viz":                     s.getImagesViz,
+			"/images/search":                  s.getImagesSearch,
+			"/images/get":                     s.getImagesGet,
+			"/images/{name:.*}/get":           s.getImagesGet,
+			"/images/{name:.*}/history":       s.getImagesHistory,
+			"/images/{name:.*}/json":          s.getImagesByName,
+			"/containers/ps":                  s.getContainersJSON,
+			"/containers/json":                s.getContainersJSON,
+			"/containers/{name:.*}/export":    s.getContainersExport,
+			"/containers/{name:.*}/changes":   s.getContainersChanges,
+			"/containers/{name:.*}/json":      s.getContainersByName,
+			"/containers/{name:.*}/top":       s.getContainersTop,
+			"/containers/{name:.*}/logs":      s.getContainersLogs,
+			"/containers/{name:.*}/stats":     s.getContainersStats,
+			"/containers/{name:.*}/attach/ws": s.wsContainersAttach,
+			"/exec/{id:.*}/json":              s.getExecByID,
 		},
 		"POST": {
-			"/auth":                         postAuth,
-			"/commit":                       postCommit,
-			"/build":                        postBuild,
-			"/images/create":                postImagesCreate,
-			"/images/load":                  postImagesLoad,
-			"/images/{name:.*}/push":        postImagesPush,
-			"/images/{name:.*}/tag":         postImagesTag,
-			"/containers/create":            postContainersCreate,
-			"/containers/{name:.*}/kill":    postContainersKill,
-			"/containers/{name:.*}/pause":   postContainersPause,
-			"/containers/{name:.*}/unpause": postContainersUnpause,
-			"/containers/{name:.*}/restart": postContainersRestart,
-			"/containers/{name:.*}/start":   postContainersStart,
-			"/containers/{name:.*}/stop":    postContainersStop,
-			"/containers/{name:.*}/wait":    postContainersWait,
-			"/containers/{name:.*}/resize":  postContainersResize,
-			"/containers/{name:.*}/attach":  postContainersAttach,
-			"/containers/{name:.*}/copy":    postContainersCopy,
-			"/containers/{name:.*}/exec":    postContainerExecCreate,
-			"/exec/{name:.*}/start":         postContainerExecStart,
-			"/exec/{name:.*}/resize":        postContainerExecResize,
-			"/containers/{name:.*}/rename":  postContainerRename,
+			"/auth":                         s.postAuth,
+			"/commit":                       s.postCommit,
+			"/build":                        s.postBuild,
+			"/images/create":                s.postImagesCreate,
+			"/images/load":                  s.postImagesLoad,
+			"/images/{name:.*}/push":        s.postImagesPush,
+			"/images/{name:.*}/tag":         s.postImagesTag,
+			"/containers/create":            s.postContainersCreate,
+			"/containers/{name:.*}/kill":    s.postContainersKill,
+			"/containers/{name:.*}/pause":   s.postContainersPause,
+			"/containers/{name:.*}/unpause": s.postContainersUnpause,
+			"/containers/{name:.*}/restart": s.postContainersRestart,
+			"/containers/{name:.*}/start":   s.postContainersStart,
+			"/containers/{name:.*}/stop":    s.postContainersStop,
+			"/containers/{name:.*}/wait":    s.postContainersWait,
+			"/containers/{name:.*}/resize":  s.postContainersResize,
+			"/containers/{name:.*}/attach":  s.postContainersAttach,
+			"/containers/{name:.*}/copy":    s.postContainersCopy,
+			"/containers/{name:.*}/exec":    s.postContainerExecCreate,
+			"/exec/{name:.*}/start":         s.postContainerExecStart,
+			"/exec/{name:.*}/resize":        s.postContainerExecResize,
+			"/containers/{name:.*}/rename":  s.postContainerRename,
 		},
 		"DELETE": {
-			"/containers/{name:.*}": deleteContainers,
-			"/images/{name:.*}":     deleteImages,
+			"/containers/{name:.*}": s.deleteContainers,
+			"/images/{name:.*}":     s.deleteImages,
 		},
 		"OPTIONS": {
-			"": optionsHandler,
+			"": s.optionsHandler,
 		},
 	}
 
 	// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
 	// otherwise, all head values will be passed to HTTP handler
-	if corsHeaders == "" && enableCors {
+	corsHeaders := s.cfg.CorsHeaders
+	if corsHeaders == "" && s.cfg.EnableCors {
 		corsHeaders = "*"
 	}
 
@@ -1581,7 +1614,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders stri
 			localMethod := method
 
 			// build the handler function
-			f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, corsHeaders, version.Version(dockerVersion))
+			f := makeHttpHandler(eng, s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version))
 
 			// add the new route
 			if localRoute == "" {
@@ -1600,7 +1633,14 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders stri
 // FIXME: refactor this to be part of Server and not require re-creating a new
 // router each time. This requires first moving ListenAndServe into Server.
 func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) {
-	router := createRouter(eng, false, true, "", "")
+	cfg := &ServerConfig{
+		EnableCors: true,
+		Version:    string(apiversion),
+	}
+	api := New(cfg, eng)
+	daemon, _ := eng.HackGetGlobalVar("httpapi.daemon").(*daemon.Daemon)
+	api.AcceptConnections(daemon)
+	router := createRouter(api, eng)
 	// Insert APIVERSION into the request as a convenience
 	req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
 	router.ServeHTTP(w, req)
@@ -1632,50 +1672,6 @@ func allocateDaemonPort(addr string) error {
 	return nil
 }
 
-type Server interface {
-	Serve() error
-	Close() error
-}
-
-// ServeApi loops through all of the protocols sent in to docker and spawns
-// off a go routine to setup a serving http.Server for each.
-func ServeApi(protoAddrs []string, conf *ServerConfig, eng *engine.Engine) error {
-	var chErrors = make(chan error, len(protoAddrs))
-
-	for _, protoAddr := range protoAddrs {
-		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
-		if len(protoAddrParts) != 2 {
-			return fmt.Errorf("bad format, expected PROTO://ADDR")
-		}
-		go func() {
-			logrus.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
-			srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], conf, eng)
-			if err != nil {
-				chErrors <- err
-				return
-			}
-			eng.OnShutdown(func() {
-				if err := srv.Close(); err != nil {
-					logrus.Error(err)
-				}
-			})
-			if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
-				err = nil
-			}
-			chErrors <- err
-		}()
-	}
-
-	for i := 0; i < len(protoAddrs); i++ {
-		err := <-chErrors
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
 func toBool(s string) bool {
 	s = strings.ToLower(strings.TrimSpace(s))
 	return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")

+ 13 - 19
api/server/server_linux.go

@@ -8,22 +8,15 @@ import (
 	"net/http"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/engine"
+	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/pkg/systemd"
 )
 
-// NewServer sets up the required Server and does protocol specific checking.
-func NewServer(proto, addr string, conf *ServerConfig, eng *engine.Engine) (Server, error) {
+// newServer sets up the required serverCloser and does protocol specific checking.
+func (s *Server) newServer(proto, addr string) (serverCloser, error) {
 	var (
 		err error
 		l   net.Listener
-		r   = createRouter(
-			eng,
-			conf.Logging,
-			conf.EnableCors,
-			conf.CorsHeaders,
-			conf.Version,
-		)
 	)
 	switch proto {
 	case "fd":
@@ -35,13 +28,13 @@ func NewServer(proto, addr string, conf *ServerConfig, eng *engine.Engine) (Serv
 		// We don't want to start serving on these sockets until the
 		// daemon is initialized and installed. Otherwise required handlers
 		// won't be ready.
-		<-activationLock
+		<-s.start
 		// Since ListenFD will return one or more sockets we have
 		// to create a go func to spawn off multiple serves
 		for i := range ls {
 			listener := ls[i]
 			go func() {
-				httpSrv := http.Server{Handler: r}
+				httpSrv := http.Server{Handler: s.router}
 				chErrors <- httpSrv.Serve(listener)
 			}()
 		}
@@ -52,17 +45,17 @@ func NewServer(proto, addr string, conf *ServerConfig, eng *engine.Engine) (Serv
 		}
 		return nil, nil
 	case "tcp":
-		if !conf.TlsVerify {
+		if !s.cfg.TlsVerify {
 			logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
 		}
-		if l, err = NewTcpSocket(addr, tlsConfigFromServerConfig(conf)); err != nil {
+		if l, err = NewTcpSocket(addr, tlsConfigFromServerConfig(s.cfg), s.start); err != nil {
 			return nil, err
 		}
 		if err := allocateDaemonPort(addr); err != nil {
 			return nil, err
 		}
 	case "unix":
-		if l, err = NewUnixSocket(addr, conf.SocketGroup); err != nil {
+		if l, err = NewUnixSocket(addr, s.cfg.SocketGroup, s.start); err != nil {
 			return nil, err
 		}
 	default:
@@ -71,19 +64,20 @@ func NewServer(proto, addr string, conf *ServerConfig, eng *engine.Engine) (Serv
 	return &HttpServer{
 		&http.Server{
 			Addr:    addr,
-			Handler: r,
+			Handler: s.router,
 		},
 		l,
 	}, nil
 }
 
-func AcceptConnections() {
+func (s *Server) AcceptConnections(d *daemon.Daemon) {
 	// Tell the init daemon we are accepting requests
+	s.daemon = d
 	go systemd.SdNotify("READY=1")
 	// close the lock so the listeners start accepting connections
 	select {
-	case <-activationLock:
+	case <-s.start:
 	default:
-		close(activationLock)
+		close(s.start)
 	}
 }

+ 16 - 14
api/server/server_windows.go

@@ -5,30 +5,24 @@ package server
 import (
 	"errors"
 	"net"
+	"net/http"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/engine"
+	"github.com/docker/docker/daemon"
 )
 
 // NewServer sets up the required Server and does protocol specific checking.
-func NewServer(proto, addr string, job *engine.Job) (Server, error) {
+func (s *Server) newServer(proto, addr string) (Server, error) {
 	var (
 		err error
 		l   net.Listener
-		r   = createRouter(
-			job.Eng,
-			job.GetenvBool("Logging"),
-			job.GetenvBool("EnableCors"),
-			job.Getenv("CorsHeaders"),
-			job.Getenv("Version"),
-		)
 	)
 	switch proto {
 	case "tcp":
-		if !job.GetenvBool("TlsVerify") {
+		if !s.cfg.TlsVerify {
 			logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
 		}
-		if l, err = NewTcpSocket(addr, tlsConfigFromJob(job)); err != nil {
+		if l, err = NewTcpSocket(addr, tlsConfigFromServerConfig(s.cfg)); err != nil {
 			return nil, err
 		}
 		if err := allocateDaemonPort(addr); err != nil {
@@ -37,13 +31,21 @@ func NewServer(proto, addr string, job *engine.Job) (Server, error) {
 	default:
 		return nil, errors.New("Invalid protocol format. Windows only supports tcp.")
 	}
+	return &HttpServer{
+		&http.Server{
+			Addr:    addr,
+			Handler: s.router,
+		},
+		l,
+	}, nil
 }
 
-func AcceptConnections() {
+func (s *Server) AcceptConnections(d *daemon.Daemon) {
+	s.daemon = d
 	// close the lock so the listeners start accepting connections
 	select {
-	case <-activationLock:
+	case <-s.start:
 	default:
-		close(activationLock)
+		close(s.start)
 	}
 }

+ 2 - 2
api/server/tcp_socket.go

@@ -31,8 +31,8 @@ func tlsConfigFromServerConfig(conf *ServerConfig) *tlsConfig {
 	}
 }
 
-func NewTcpSocket(addr string, config *tlsConfig) (net.Listener, error) {
-	l, err := listenbuffer.NewListenBuffer("tcp", addr, activationLock)
+func NewTcpSocket(addr string, config *tlsConfig, activate <-chan struct{}) (net.Listener, error) {
+	l, err := listenbuffer.NewListenBuffer("tcp", addr, activate)
 	if err != nil {
 		return nil, err
 	}

+ 2 - 2
api/server/unix_socket.go

@@ -12,13 +12,13 @@ import (
 	"github.com/docker/libcontainer/user"
 )
 
-func NewUnixSocket(path, group string) (net.Listener, error) {
+func NewUnixSocket(path, group string, activate <-chan struct{}) (net.Listener, error) {
 	if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
 		return nil, err
 	}
 	mask := syscall.Umask(0777)
 	defer syscall.Umask(mask)
-	l, err := listenbuffer.NewListenBuffer("unix", path, activationLock)
+	l, err := listenbuffer.NewListenBuffer("unix", path, activate)
 	if err != nil {
 		return nil, err
 	}

+ 28 - 54
docker/daemon.go

@@ -7,7 +7,6 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"strings"
 
 	"github.com/Sirupsen/logrus"
 	apiserver "github.com/docker/docker/api/server"
@@ -93,40 +92,6 @@ func mainDaemon() {
 	}
 	daemonCfg.TrustKeyPath = *flTrustKey
 
-	registryService := registry.NewService(registryCfg)
-	// load the daemon in the background so we can immediately start
-	// the http api so that connections don't fail while the daemon
-	// is booting
-	daemonInitWait := make(chan error)
-	go func() {
-		d, err := daemon.NewDaemon(daemonCfg, eng, registryService)
-		if err != nil {
-			daemonInitWait <- err
-			return
-		}
-
-		logrus.WithFields(logrus.Fields{
-			"version":     dockerversion.VERSION,
-			"commit":      dockerversion.GITCOMMIT,
-			"execdriver":  d.ExecutionDriver().Name(),
-			"graphdriver": d.GraphDriver().String(),
-		}).Info("Docker daemon")
-
-		if err := d.Install(eng); err != nil {
-			daemonInitWait <- err
-			return
-		}
-
-		b := &builder.BuilderJob{eng, d}
-		b.Install()
-
-		// after the daemon is done setting up we can tell the api to start
-		// accepting connections
-		apiserver.AcceptConnections()
-
-		daemonInitWait <- nil
-	}()
-
 	serverConfig := &apiserver.ServerConfig{
 		Logging:     true,
 		EnableCors:  daemonCfg.EnableCors,
@@ -140,12 +105,14 @@ func mainDaemon() {
 		TlsKey:      *flKey,
 	}
 
+	api := apiserver.New(serverConfig, eng)
+
 	// The serve API routine never exits unless an error occurs
 	// We need to start it as a goroutine and wait on it so
 	// daemon doesn't exit
 	serveAPIWait := make(chan error)
 	go func() {
-		if err := apiserver.ServeApi(flHosts, serverConfig, eng); err != nil {
+		if err := api.ServeApi(flHosts); err != nil {
 			logrus.Errorf("ServeAPI error: %v", err)
 			serveAPIWait <- err
 			return
@@ -153,30 +120,37 @@ func mainDaemon() {
 		serveAPIWait <- nil
 	}()
 
-	// Wait for the daemon startup goroutine to finish
-	// This makes sure we can actually cleanly shutdown the daemon
-	logrus.Debug("waiting for daemon to initialize")
-	errDaemon := <-daemonInitWait
-	if errDaemon != nil {
+	registryService := registry.NewService(registryCfg)
+	d, err := daemon.NewDaemon(daemonCfg, eng, registryService)
+	if err != nil {
 		eng.Shutdown()
-		outStr := fmt.Sprintf("Shutting down daemon due to errors: %v", errDaemon)
-		if strings.Contains(errDaemon.Error(), "engine is shutdown") {
-			// if the error is "engine is shutdown", we've already reported (or
-			// will report below in API server errors) the error
-			outStr = "Shutting down daemon due to reported errors"
-		}
-		// we must "fatal" exit here as the API server may be happy to
-		// continue listening forever if the error had no impact to API
-		logrus.Fatal(outStr)
-	} else {
-		logrus.Info("Daemon has completed initialization")
+		logrus.Fatalf("Error starting daemon: %v", err)
+	}
+
+	if err := d.Install(eng); err != nil {
+		eng.Shutdown()
+		logrus.Fatalf("Error starting daemon: %v", err)
 	}
 
+	logrus.Info("Daemon has completed initialization")
+
+	logrus.WithFields(logrus.Fields{
+		"version":     dockerversion.VERSION,
+		"commit":      dockerversion.GITCOMMIT,
+		"execdriver":  d.ExecutionDriver().Name(),
+		"graphdriver": d.GraphDriver().String(),
+	}).Info("Docker daemon")
+
+	b := &builder.BuilderJob{eng, d}
+	b.Install()
+
+	// after the daemon is done setting up we can tell the api to start
+	// accepting connections with specified daemon
+	api.AcceptConnections(d)
+
 	// Daemon is fully initialized and handling API traffic
 	// Wait for serve API job to complete
 	errAPI := <-serveAPIWait
-	// If we have an error here it is unique to API (as daemonErr would have
-	// exited the daemon process above)
 	eng.Shutdown()
 	if errAPI != nil {
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)

+ 2 - 2
integration-cli/docker_cli_daemon_test.go

@@ -498,9 +498,9 @@ func TestDaemonExitOnFailure(t *testing.T) {
 			t.Fatalf("Expected daemon not to start, got %v", err)
 		}
 		// look in the log and make sure we got the message that daemon is shutting down
-		runCmd := exec.Command("grep", "Shutting down daemon due to", d.LogfileName())
+		runCmd := exec.Command("grep", "Error starting daemon", d.LogfileName())
 		if out, _, err := runCommandWithOutput(runCmd); err != nil {
-			t.Fatalf("Expected 'shutting down daemon due to error' message; but doesn't exist in log: %q, err: %v", out, err)
+			t.Fatalf("Expected 'Error starting daemon' message; but doesn't exist in log: %q, err: %v", out, err)
 		}
 	} else {
 		//if we didn't get an error and the daemon is running, this is a failure

+ 15 - 16
integration/runtime_test.go

@@ -48,9 +48,7 @@ const (
 )
 
 var (
-	// FIXME: globalDaemon is deprecated by globalEngine. All tests should be converted.
 	globalDaemon           *daemon.Daemon
-	globalEngine           *engine.Engine
 	globalHttpsEngine      *engine.Engine
 	globalRogueHttpsEngine *engine.Engine
 	startFds               int
@@ -153,9 +151,10 @@ func spawnGlobalDaemon() {
 	}
 	t := std_log.New(os.Stderr, "", 0)
 	eng := NewTestEngine(t)
-	globalEngine = eng
 	globalDaemon = mkDaemonFromEngine(eng, t)
 
+	serverConfig := &apiserver.ServerConfig{Logging: true}
+	api := apiserver.New(serverConfig, eng)
 	// Spawn a Daemon
 	go func() {
 		logrus.Debugf("Spawning global daemon for integration tests")
@@ -164,8 +163,7 @@ func spawnGlobalDaemon() {
 			Host:   testDaemonAddr,
 		}
 
-		serverConfig := &apiserver.ServerConfig{Logging: true}
-		if err := apiserver.ServeApi([]string{listenURL.String()}, serverConfig, eng); err != nil {
+		if err := api.ServeApi([]string{listenURL.String()}); err != nil {
 			logrus.Fatalf("Unable to spawn the test daemon: %s", err)
 		}
 	}()
@@ -174,7 +172,7 @@ func spawnGlobalDaemon() {
 	// FIXME: use inmem transports instead of tcp
 	time.Sleep(time.Second)
 
-	apiserver.AcceptConnections()
+	api.AcceptConnections(getDaemon(eng))
 }
 
 func spawnLegitHttpsDaemon() {
@@ -204,6 +202,15 @@ func spawnHttpsDaemon(addr, cacert, cert, key string) *engine.Engine {
 
 	eng := newTestEngine(t, true, root)
 
+	serverConfig := &apiserver.ServerConfig{
+		Logging:   true,
+		Tls:       true,
+		TlsVerify: true,
+		TlsCa:     cacert,
+		TlsCert:   cert,
+		TlsKey:    key,
+	}
+	api := apiserver.New(serverConfig, eng)
 	// Spawn a Daemon
 	go func() {
 		logrus.Debugf("Spawning https daemon for integration tests")
@@ -211,15 +218,7 @@ func spawnHttpsDaemon(addr, cacert, cert, key string) *engine.Engine {
 			Scheme: testDaemonHttpsProto,
 			Host:   addr,
 		}
-		serverConfig := &apiserver.ServerConfig{
-			Logging:   true,
-			Tls:       true,
-			TlsVerify: true,
-			TlsCa:     cacert,
-			TlsCert:   cert,
-			TlsKey:    key,
-		}
-		if err := apiserver.ServeApi([]string{listenURL.String()}, serverConfig, eng); err != nil {
+		if err := api.ServeApi([]string{listenURL.String()}); err != nil {
 			logrus.Fatalf("Unable to spawn the test daemon: %s", err)
 		}
 	}()
@@ -227,7 +226,7 @@ func spawnHttpsDaemon(addr, cacert, cert, key string) *engine.Engine {
 	// Give some time to ListenAndServer to actually start
 	time.Sleep(time.Second)
 
-	apiserver.AcceptConnections()
+	api.AcceptConnections(getDaemon(eng))
 
 	return eng
 }

+ 4 - 4
pkg/listenbuffer/buffer.go

@@ -32,7 +32,7 @@ import "net"
 // NewListenBuffer returns a net.Listener listening on addr with the protocol
 // passed. The channel passed is used to activate the listenbuffer when the
 // caller is ready to accept connections.
-func NewListenBuffer(proto, addr string, activate chan struct{}) (net.Listener, error) {
+func NewListenBuffer(proto, addr string, activate <-chan struct{}) (net.Listener, error) {
 	wrapped, err := net.Listen(proto, addr)
 	if err != nil {
 		return nil, err
@@ -46,9 +46,9 @@ func NewListenBuffer(proto, addr string, activate chan struct{}) (net.Listener,
 
 // defaultListener is the buffered wrapper around the net.Listener
 type defaultListener struct {
-	wrapped  net.Listener  // The net.Listener wrapped by listenbuffer
-	ready    bool          // Whether the listenbuffer has been activated
-	activate chan struct{} // Channel to control activation of the listenbuffer
+	wrapped  net.Listener    // The net.Listener wrapped by listenbuffer
+	ready    bool            // Whether the listenbuffer has been activated
+	activate <-chan struct{} // Channel to control activation of the listenbuffer
 }
 
 // Close closes the wrapped socket.