Browse code

daemon: logger: error out on daemon start if invalid logger address

If an invalid logger address is provided on daemon start it will
silently fail. As syslog driver is doing, this check should be done on
daemon start and prevent it from starting even in other drivers.
This patch also adds integration tests for this behavior.

Signed-off-by: Antonio Murdaca <runcom@linux.com>

Antonio Murdaca authored on 2015/09/20 20:03:09
Showing 3 changed files
... ...
@@ -38,44 +38,20 @@ func init() {
38 38
 	}
39 39
 }
40 40
 
41
-func parseConfig(ctx logger.Context) (string, int, string, error) {
42
-	host := defaultHostName
43
-	port := defaultPort
44
-
45
-	config := ctx.Config
46
-
47
-	tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}")
48
-	if err != nil {
49
-		return "", 0, "", err
50
-	}
51
-
52
-	if address := config["fluentd-address"]; address != "" {
53
-		if h, p, err := net.SplitHostPort(address); err != nil {
54
-			if !strings.Contains(err.Error(), "missing port in address") {
55
-				return "", 0, "", err
56
-			}
57
-			host = h
58
-		} else {
59
-			portnum, err := strconv.Atoi(p)
60
-			if err != nil {
61
-				return "", 0, "", err
62
-			}
63
-			host = h
64
-			port = portnum
65
-		}
66
-	}
67
-
68
-	return host, port, tag, nil
69
-}
70
-
71 41
 // New creates a fluentd logger using the configuration passed in on
72 42
 // the context. Supported context configuration variables are
73 43
 // fluentd-address & fluentd-tag.
74 44
 func New(ctx logger.Context) (logger.Logger, error) {
75
-	host, port, tag, err := parseConfig(ctx)
45
+	host, port, err := parseAddress(ctx.Config["fluentd-address"])
46
+	if err != nil {
47
+		return nil, err
48
+	}
49
+
50
+	tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}")
76 51
 	if err != nil {
77 52
 		return nil, err
78 53
 	}
54
+
79 55
 	logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s.", ctx.ContainerID, host, port, tag)
80 56
 
81 57
 	// logger tries to recoonect 2**32 - 1 times
... ...
@@ -104,6 +80,14 @@ func (f *fluentd) Log(msg *logger.Message) error {
104 104
 	return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
105 105
 }
106 106
 
107
+func (f *fluentd) Close() error {
108
+	return f.writer.Close()
109
+}
110
+
111
+func (f *fluentd) Name() string {
112
+	return name
113
+}
114
+
107 115
 // ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag.
108 116
 func ValidateLogOpt(cfg map[string]string) error {
109 117
 	for key := range cfg {
... ...
@@ -115,13 +99,30 @@ func ValidateLogOpt(cfg map[string]string) error {
115 115
 			return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
116 116
 		}
117 117
 	}
118
+
119
+	if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil {
120
+		return err
121
+	}
122
+
118 123
 	return nil
119 124
 }
120 125
 
121
-func (f *fluentd) Close() error {
122
-	return f.writer.Close()
123
-}
126
+func parseAddress(address string) (string, int, error) {
127
+	if address == "" {
128
+		return defaultHostName, defaultPort, nil
129
+	}
124 130
 
125
-func (f *fluentd) Name() string {
126
-	return name
131
+	host, port, err := net.SplitHostPort(address)
132
+	if err != nil {
133
+		if !strings.Contains(err.Error(), "missing port in address") {
134
+			return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
135
+		}
136
+		return host, defaultPort, nil
137
+	}
138
+
139
+	portnum, err := strconv.Atoi(port)
140
+	if err != nil {
141
+		return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
142
+	}
143
+	return host, portnum, nil
127 144
 }
... ...
@@ -147,28 +147,35 @@ func ValidateLogOpt(cfg map[string]string) error {
147 147
 			return fmt.Errorf("unknown log opt '%s' for gelf log driver", key)
148 148
 		}
149 149
 	}
150
+
151
+	if _, err := parseAddress(cfg["gelf-address"]); err != nil {
152
+		return err
153
+	}
154
+
150 155
 	return nil
151 156
 }
152 157
 
153 158
 func parseAddress(address string) (string, error) {
154
-	if urlutil.IsTransportURL(address) {
155
-		url, err := url.Parse(address)
156
-		if err != nil {
157
-			return "", err
158
-		}
159
-
160
-		// we support only udp
161
-		if url.Scheme != "udp" {
162
-			return "", fmt.Errorf("gelf: endpoint needs to be UDP")
163
-		}
159
+	if address == "" {
160
+		return "", nil
161
+	}
162
+	if !urlutil.IsTransportURL(address) {
163
+		return "", fmt.Errorf("gelf-address should be in form proto://address, got %v", address)
164
+	}
165
+	url, err := url.Parse(address)
166
+	if err != nil {
167
+		return "", err
168
+	}
164 169
 
165
-		// get host and port
166
-		if _, _, err = net.SplitHostPort(url.Host); err != nil {
167
-			return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port")
168
-		}
170
+	// we support only udp
171
+	if url.Scheme != "udp" {
172
+		return "", fmt.Errorf("gelf: endpoint needs to be UDP")
173
+	}
169 174
 
170
-		return url.Host, nil
175
+	// get host and port
176
+	if _, _, err = net.SplitHostPort(url.Host); err != nil {
177
+		return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port")
171 178
 	}
172 179
 
173
-	return "", nil
180
+	return url.Host, nil
174 181
 }
... ...
@@ -1609,14 +1609,6 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlway
1609 1609
 	c.Assert(strings.TrimSpace(out), check.Equals, id[:12])
1610 1610
 }
1611 1611
 
1612
-func (s *DockerDaemonSuite) TestDaemonCorruptedSyslogAddress(c *check.C) {
1613
-	c.Assert(s.d.Start("--log-driver=syslog", "--log-opt", "syslog-address=corrupted:1234"), check.NotNil)
1614
-	runCmd := exec.Command("grep", "Failed to set log opts: syslog-address should be in form proto://address", s.d.LogfileName())
1615
-	if out, _, err := runCommandWithOutput(runCmd); err != nil {
1616
-		c.Fatalf("Expected 'Error starting daemon' message; but doesn't exist in log: %q, err: %v", out, err)
1617
-	}
1618
-}
1619
-
1620 1612
 func (s *DockerDaemonSuite) TestDaemonWideLogConfig(c *check.C) {
1621 1613
 	c.Assert(s.d.Start("--log-driver=json-file", "--log-opt=max-size=1k"), check.IsNil)
1622 1614
 	out, err := s.d.Cmd("run", "-d", "--name=logtest", "busybox", "top")
... ...
@@ -1689,3 +1681,27 @@ func (s *DockerDaemonSuite) TestDaemonRestartLocalVolumes(c *check.C) {
1689 1689
 	_, err = s.d.Cmd("volume", "inspect", "test")
1690 1690
 	c.Assert(err, check.IsNil)
1691 1691
 }
1692
+
1693
+func (s *DockerDaemonSuite) TestDaemonCorruptedLogDriverAddress(c *check.C) {
1694
+	for _, driver := range []string{
1695
+		"syslog",
1696
+		"gelf",
1697
+	} {
1698
+		args := []string{"--log-driver=" + driver, "--log-opt", driver + "-address=corrupted:42"}
1699
+		c.Assert(s.d.Start(args...), check.NotNil, check.Commentf(fmt.Sprintf("Expected daemon not to start with invalid %s-address provided", driver)))
1700
+		expected := fmt.Sprintf("Failed to set log opts: %s-address should be in form proto://address", driver)
1701
+		runCmd := exec.Command("grep", expected, s.d.LogfileName())
1702
+		if out, _, err := runCommandWithOutput(runCmd); err != nil {
1703
+			c.Fatalf("Expected %q message; but doesn't exist in log: %q, err: %v", expected, out, err)
1704
+		}
1705
+	}
1706
+}
1707
+
1708
+func (s *DockerDaemonSuite) TestDaemonCorruptedFluentdAddress(c *check.C) {
1709
+	c.Assert(s.d.Start("--log-driver=fluentd", "--log-opt", "fluentd-address=corrupted:c"), check.NotNil)
1710
+	expected := "Failed to set log opts: invalid fluentd-address corrupted:c: "
1711
+	runCmd := exec.Command("grep", expected, s.d.LogfileName())
1712
+	if out, _, err := runCommandWithOutput(runCmd); err != nil {
1713
+		c.Fatalf("Expected %q message; but doesn't exist in log: %q, err: %v", expected, out, err)
1714
+	}
1715
+}