diff --git a/plugins/mysql/mysql.go b/plugins/mysql/mysql.go index 89c4c9b44..5193f078f 100644 --- a/plugins/mysql/mysql.go +++ b/plugins/mysql/mysql.go @@ -16,12 +16,13 @@ type Mysql struct { var sampleConfig = ` # specify servers via a url matching: # [username[:password]@][protocol[(address)]]/[?tls=[true|false|skip-verify]] + # see https://github.com/go-sql-driver/mysql#dsn-data-source-name # e.g. - # root:root@http://10.0.0.18/?tls=false - # root:passwd@tcp(127.0.0.1:3036)/ + # root:passwd@tcp(127.0.0.1:3306)/?tls=false + # root@tcp(127.0.0.1:3306)/?tls=false # # If no servers are specified, then localhost is used as the host. - servers = ["localhost"] + servers = ["tcp(127.0.0.1:3306)/"] ` func (m *Mysql) SampleConfig() string { @@ -113,7 +114,10 @@ var mappings = []*mapping{ } func (m *Mysql) gatherServer(serv string, acc plugins.Accumulator) error { - if serv == "localhost" { + // If user forgot the '/', add it + if strings.HasSuffix(serv, ")") { + serv = serv + "/" + } else if serv == "localhost" { serv = "" } @@ -129,14 +133,10 @@ func (m *Mysql) gatherServer(serv string, acc plugins.Accumulator) error { return err } - // Parse out user/password from server address tag if given var servtag string - if strings.Contains(serv, "@") { - servtag = strings.Split(serv, "@")[1] - } else if serv == "" { + servtag, err = parseDSN(serv) + if err != nil { servtag = "localhost" - } else { - servtag = serv } for rows.Next() { var name string diff --git a/plugins/mysql/mysql_test.go b/plugins/mysql/mysql_test.go index 24e7401ce..b85448740 100644 --- a/plugins/mysql/mysql_test.go +++ b/plugins/mysql/mysql_test.go @@ -81,3 +81,62 @@ func TestMysqlDefaultsToLocal(t *testing.T) { assert.True(t, len(acc.Points) > 0) } + +func TestMysqlParseDSN(t *testing.T) { + tests := []struct { + input string + output string + }{ + { + "", + "127.0.0.1:3306", + }, + { + "localhost", + "127.0.0.1:3306", + }, + { + "127.0.0.1", + "127.0.0.1:3306", + }, + { + "tcp(192.168.1.1:3306)/", + "192.168.1.1:3306", + }, + { + "tcp(localhost)/", + "localhost", + }, + { + "root:passwd@tcp(192.168.1.1:3306)/?tls=false", + "192.168.1.1:3306", + }, + { + "root@tcp(127.0.0.1:3306)/?tls=false", + "127.0.0.1:3306", + }, + { + "root:passwd@tcp(localhost:3036)/dbname?allowOldPasswords=1", + "localhost:3036", + }, + { + "root:foo@bar@tcp(192.1.1.1:3306)/?tls=false", + "192.1.1.1:3306", + }, + { + "root:f00@b4r@tcp(192.1.1.1:3306)/?tls=false", + "192.1.1.1:3306", + }, + { + "root:fl!p11@tcp(192.1.1.1:3306)/?tls=false", + "192.1.1.1:3306", + }, + } + + for _, test := range tests { + output, _ := parseDSN(test.input) + if output != test.output { + t.Errorf("Expected %s, got %s\n", test.output, output) + } + } +} diff --git a/plugins/mysql/parse_dsn.go b/plugins/mysql/parse_dsn.go new file mode 100644 index 000000000..bbe948268 --- /dev/null +++ b/plugins/mysql/parse_dsn.go @@ -0,0 +1,85 @@ +// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +package mysql + +import ( + "errors" + "strings" +) + +// parseDSN parses the DSN string to a config +func parseDSN(dsn string) (string, error) { + //var user, passwd string + var addr, net string + + // [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN] + // Find the last '/' (since the password or the net addr might contain a '/') + for i := len(dsn) - 1; i >= 0; i-- { + if dsn[i] == '/' { + var j, k int + + // left part is empty if i <= 0 + if i > 0 { + // [username[:password]@][protocol[(address)]] + // Find the last '@' in dsn[:i] + for j = i; j >= 0; j-- { + if dsn[j] == '@' { + // username[:password] + // Find the first ':' in dsn[:j] + for k = 0; k < j; k++ { + if dsn[k] == ':' { + //passwd = dsn[k+1 : j] + break + } + } + //user = dsn[:k] + + break + } + } + + // [protocol[(address)]] + // Find the first '(' in dsn[j+1:i] + for k = j + 1; k < i; k++ { + if dsn[k] == '(' { + // dsn[i-1] must be == ')' if an address is specified + if dsn[i-1] != ')' { + if strings.ContainsRune(dsn[k+1:i], ')') { + return "", errors.New("Invalid DSN unescaped") + } + return "", errors.New("Invalid DSN Addr") + } + addr = dsn[k+1 : i-1] + break + } + } + net = dsn[j+1 : k] + } + + break + } + } + + // Set default network if empty + if net == "" { + net = "tcp" + } + + // Set default address if empty + if addr == "" { + switch net { + case "tcp": + addr = "127.0.0.1:3306" + case "unix": + addr = "/tmp/mysql.sock" + default: + return "", errors.New("Default addr for network '" + net + "' unknown") + } + } + + return addr, nil +}