Fix MySQL DSN -> tags parsing

Closes #297
This commit is contained in:
Cameron Sparr 2015-10-22 17:11:51 -06:00
parent ae7ad2230f
commit 86d20496ea
3 changed files with 154 additions and 10 deletions

View File

@ -16,12 +16,13 @@ type Mysql struct {
var sampleConfig = ` var sampleConfig = `
# specify servers via a url matching: # specify servers via a url matching:
# [username[:password]@][protocol[(address)]]/[?tls=[true|false|skip-verify]] # [username[:password]@][protocol[(address)]]/[?tls=[true|false|skip-verify]]
# see https://github.com/go-sql-driver/mysql#dsn-data-source-name
# e.g. # e.g.
# root:root@http://10.0.0.18/?tls=false # root:passwd@tcp(127.0.0.1:3306)/?tls=false
# root:passwd@tcp(127.0.0.1:3036)/ # root@tcp(127.0.0.1:3306)/?tls=false
# #
# If no servers are specified, then localhost is used as the host. # 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 { func (m *Mysql) SampleConfig() string {
@ -113,7 +114,10 @@ var mappings = []*mapping{
} }
func (m *Mysql) gatherServer(serv string, acc plugins.Accumulator) error { 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 = "" serv = ""
} }
@ -129,14 +133,10 @@ func (m *Mysql) gatherServer(serv string, acc plugins.Accumulator) error {
return err return err
} }
// Parse out user/password from server address tag if given
var servtag string var servtag string
if strings.Contains(serv, "@") { servtag, err = parseDSN(serv)
servtag = strings.Split(serv, "@")[1] if err != nil {
} else if serv == "" {
servtag = "localhost" servtag = "localhost"
} else {
servtag = serv
} }
for rows.Next() { for rows.Next() {
var name string var name string

View File

@ -81,3 +81,62 @@ func TestMysqlDefaultsToLocal(t *testing.T) {
assert.True(t, len(acc.Points) > 0) 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)
}
}
}

View File

@ -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&paramN=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
}