daemon: logger: error out on daemon start if invalid logger address
| ... | ... |
@@ -94,7 +94,11 @@ func (s *Server) getContainersLogs(ctx context.Context, w http.ResponseWriter, r |
| 94 | 94 |
return fmt.Errorf("Missing parameter")
|
| 95 | 95 |
} |
| 96 | 96 |
|
| 97 |
- // Validate args here, because we can't return not StatusOK after job.Run() call |
|
| 97 |
+ // Args are validated before the stream starts because when it starts we're |
|
| 98 |
+ // sending HTTP 200 by writing an empty chunk of data to tell the client that |
|
| 99 |
+ // daemon is going to stream. By sending this initial HTTP 200 we can't report |
|
| 100 |
+ // any error after the stream starts (i.e. container not found, wrong parameters) |
|
| 101 |
+ // with the appropriate status code. |
|
| 98 | 102 |
stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr") |
| 99 | 103 |
if !(stdout || stderr) {
|
| 100 | 104 |
return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
| ... | ... |
@@ -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 |
} |
| ... | ... |
@@ -1615,14 +1615,6 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlway |
| 1615 | 1615 |
c.Assert(strings.TrimSpace(out), check.Equals, id[:12]) |
| 1616 | 1616 |
} |
| 1617 | 1617 |
|
| 1618 |
-func (s *DockerDaemonSuite) TestDaemonCorruptedSyslogAddress(c *check.C) {
|
|
| 1619 |
- c.Assert(s.d.Start("--log-driver=syslog", "--log-opt", "syslog-address=corrupted:1234"), check.NotNil)
|
|
| 1620 |
- runCmd := exec.Command("grep", "Failed to set log opts: syslog-address should be in form proto://address", s.d.LogfileName())
|
|
| 1621 |
- if out, _, err := runCommandWithOutput(runCmd); err != nil {
|
|
| 1622 |
- c.Fatalf("Expected 'Error starting daemon' message; but doesn't exist in log: %q, err: %v", out, err)
|
|
| 1623 |
- } |
|
| 1624 |
-} |
|
| 1625 |
- |
|
| 1626 | 1618 |
func (s *DockerDaemonSuite) TestDaemonWideLogConfig(c *check.C) {
|
| 1627 | 1619 |
if err := s.d.StartWithBusybox("--log-driver=json-file", "--log-opt=max-size=1k"); err != nil {
|
| 1628 | 1620 |
c.Fatal(err) |
| ... | ... |
@@ -1697,3 +1689,27 @@ func (s *DockerDaemonSuite) TestDaemonRestartLocalVolumes(c *check.C) {
|
| 1697 | 1697 |
_, err = s.d.Cmd("volume", "inspect", "test")
|
| 1698 | 1698 |
c.Assert(err, check.IsNil) |
| 1699 | 1699 |
} |
| 1700 |
+ |
|
| 1701 |
+func (s *DockerDaemonSuite) TestDaemonCorruptedLogDriverAddress(c *check.C) {
|
|
| 1702 |
+ for _, driver := range []string{
|
|
| 1703 |
+ "syslog", |
|
| 1704 |
+ "gelf", |
|
| 1705 |
+ } {
|
|
| 1706 |
+ args := []string{"--log-driver=" + driver, "--log-opt", driver + "-address=corrupted:42"}
|
|
| 1707 |
+ c.Assert(s.d.Start(args...), check.NotNil, check.Commentf(fmt.Sprintf("Expected daemon not to start with invalid %s-address provided", driver)))
|
|
| 1708 |
+ expected := fmt.Sprintf("Failed to set log opts: %s-address should be in form proto://address", driver)
|
|
| 1709 |
+ runCmd := exec.Command("grep", expected, s.d.LogfileName())
|
|
| 1710 |
+ if out, _, err := runCommandWithOutput(runCmd); err != nil {
|
|
| 1711 |
+ c.Fatalf("Expected %q message; but doesn't exist in log: %q, err: %v", expected, out, err)
|
|
| 1712 |
+ } |
|
| 1713 |
+ } |
|
| 1714 |
+} |
|
| 1715 |
+ |
|
| 1716 |
+func (s *DockerDaemonSuite) TestDaemonCorruptedFluentdAddress(c *check.C) {
|
|
| 1717 |
+ c.Assert(s.d.Start("--log-driver=fluentd", "--log-opt", "fluentd-address=corrupted:c"), check.NotNil)
|
|
| 1718 |
+ expected := "Failed to set log opts: invalid fluentd-address corrupted:c: " |
|
| 1719 |
+ runCmd := exec.Command("grep", expected, s.d.LogfileName())
|
|
| 1720 |
+ if out, _, err := runCommandWithOutput(runCmd); err != nil {
|
|
| 1721 |
+ c.Fatalf("Expected %q message; but doesn't exist in log: %q, err: %v", expected, out, err)
|
|
| 1722 |
+ } |
|
| 1723 |
+} |