Forráskód Böngészése

cmd/dockerd: normalize hosts when loading config

Previously, hosts were de-duplicated and normalized when starting
the API server (in `loadListeners()`), which meant that errors could
occur in that step (but not detected when using `dockerd --validate`),
as well as the list of hosts in the config not matching what would
actually be used (i.e., if duplicates were present).

This patch extracts the de-duplicating to a separate function, and
executes it as part of loading the daemon configuration, so that we
can fail early.

Moving this code also showed that some of this validation depended
on `newAPIServerConfig()` modifying the configuration (adding an
empty host if none was set) in order to have the parsing set a
default. This code was moved elsewhere, but a TODO comment added
as this logic is somewhat sketchy.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 3 éve
szülő
commit
57c20c1b79
1 módosított fájl, 45 hozzáadás és 22 törlés
  1. 45 22
      cmd/dockerd/daemon.go

+ 45 - 22
cmd/dockerd/daemon.go

@@ -8,6 +8,7 @@ import (
 	"os"
 	"path/filepath"
 	"runtime"
+	"sort"
 	"strings"
 	"time"
 
@@ -435,6 +436,10 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
 		}
 	}
 
+	if err := normalizeHosts(conf); err != nil {
+		return nil, err
+	}
+
 	if err := config.Validate(conf); err != nil {
 		return nil, err
 	}
@@ -469,6 +474,41 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
 	return conf, nil
 }
 
+// normalizeHosts normalizes the configured config.Hosts and remove duplicates.
+// It returns an error if it fails to parse a host.
+func normalizeHosts(config *config.Config) error {
+	if len(config.Hosts) == 0 {
+		// if no hosts are configured, create a single entry slice, so that the
+		// default is used.
+		//
+		// TODO(thaJeztah) implement a cleaner way for this; this depends on a
+		//                 side-effect of how we parse empty/partial hosts.
+		config.Hosts = make([]string, 1)
+	}
+	hosts := make([]string, 0, len(config.Hosts))
+	seen := make(map[string]struct{}, len(config.Hosts))
+
+	useTLS := DefaultTLSValue
+	if config.TLS != nil {
+		useTLS = *config.TLS
+	}
+
+	for _, h := range config.Hosts {
+		host, err := dopts.ParseHost(useTLS, honorXDG, h)
+		if err != nil {
+			return err
+		}
+		if _, ok := seen[host]; ok {
+			continue
+		}
+		seen[host] = struct{}{}
+		hosts = append(hosts, host)
+	}
+	sort.Strings(hosts)
+	config.Hosts = hosts
+	return nil
+}
+
 func checkDeprecatedOptions(config *config.Config) error {
 	// Overlay networks with external k/v stores have been deprecated
 	if config.ClusterAdvertise != "" || len(config.ClusterOpts) > 0 || config.ClusterStore != "" {
@@ -591,10 +631,6 @@ func newAPIServerConfig(cli *DaemonCli) (*apiserver.Config, error) {
 		serverConfig.TLSConfig = tlsConfig
 	}
 
-	if len(cli.Config.Hosts) == 0 {
-		cli.Config.Hosts = make([]string, 1)
-	}
-
 	return serverConfig, nil
 }
 
@@ -624,32 +660,19 @@ func checkTLSAuthOK(c *config.Config) bool {
 }
 
 func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, error) {
-	var hosts []string
-	seen := make(map[string]struct{}, len(cli.Config.Hosts))
-
-	useTLS := DefaultTLSValue
-	if cli.Config.TLS != nil {
-		useTLS = *cli.Config.TLS
+	if len(cli.Config.Hosts) == 0 {
+		return nil, errors.New("no hosts configured")
 	}
+	var hosts []string
 
 	for i := 0; i < len(cli.Config.Hosts); i++ {
-		var err error
-		if cli.Config.Hosts[i], err = dopts.ParseHost(useTLS, honorXDG, cli.Config.Hosts[i]); err != nil {
-			return nil, errors.Wrapf(err, "error parsing -H %s", cli.Config.Hosts[i])
-		}
-		if _, ok := seen[cli.Config.Hosts[i]]; ok {
-			continue
-		}
-		seen[cli.Config.Hosts[i]] = struct{}{}
-
 		protoAddr := cli.Config.Hosts[i]
-		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
+		protoAddrParts := strings.SplitN(cli.Config.Hosts[i], "://", 2)
 		if len(protoAddrParts) != 2 {
 			return nil, fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
 		}
 
-		proto := protoAddrParts[0]
-		addr := protoAddrParts[1]
+		proto, addr := protoAddrParts[0], protoAddrParts[1]
 
 		// It's a bad idea to bind to TCP without tlsverify.
 		authEnabled := serverConfig.TLSConfig != nil && serverConfig.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert