From b65868d754ec7b0d07e1e92f4f91d628f32a4728 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Aug 2014 15:07:43 -0700 Subject: [PATCH] serverinit: allow specifying port numbers in user@server:port:pass With heuristics and tests. Change-Id: Ifacfc725db15100c447a3251c23267a5bfd313cf --- pkg/serverinit/genconfig.go | 40 ++++++++++++++++++++------- pkg/serverinit/genconfig_test.go | 46 ++++++++++++++++++++++++++++++++ pkg/sorted/mysql/mysqlkv.go | 1 - 3 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 pkg/serverinit/genconfig_test.go diff --git a/pkg/serverinit/genconfig.go b/pkg/serverinit/genconfig.go index 7225818e5..036fe05cf 100644 --- a/pkg/serverinit/genconfig.go +++ b/pkg/serverinit/genconfig.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "strings" "camlistore.org/pkg/blob" @@ -165,14 +166,35 @@ func addMongoConfig(prefixes jsonconfig.Obj, dbname string, dbinfo string) { prefixes["/index/"] = ob } -func addSQLConfig(rdbms string, prefixes jsonconfig.Obj, dbname string, dbinfo string) { - fields := strings.Split(dbinfo, "@") - if len(fields) != 2 { - exitFailure("Malformed " + rdbms + " config string. Want: \"user@host:password\"") +// parses "user@host:password", which you think would be easy, but we +// documented this format without thinking about port numbers, so this +// uses heuristics to guess what extra colons mean. +func parseUserHostPass(v string) (user, host, password string, ok bool) { + f := strings.SplitN(v, "@", 2) + if len(f) != 2 { + return } - user := fields[0] - fields = strings.Split(fields[1], ":") - if len(fields) != 2 { + user = f[0] + f = strings.Split(f[1], ":") + if len(f) < 2 { + return "", "", "", false + } + host = f[0] + f = f[1:] + if len(f) >= 2 { + if _, err := strconv.ParseUint(f[0], 10, 16); err == nil { + host = host + ":" + f[0] + f = f[1:] + } + } + password = strings.Join(f, ":") + ok = true + return +} + +func addSQLConfig(rdbms string, prefixes jsonconfig.Obj, dbname string, dbinfo string) { + user, host, password, ok := parseUserHostPass(dbinfo) + if !ok { exitFailure("Malformed " + rdbms + " config string. Want: \"user@host:password\"") } ob := map[string]interface{}{} @@ -182,9 +204,9 @@ func addSQLConfig(rdbms string, prefixes jsonconfig.Obj, dbname string, dbinfo s "blobSource": "/bs/", "storage": map[string]interface{}{ "type": rdbms, - "host": fields[0], + "host": host, "user": user, - "password": fields[1], + "password": password, "database": dbname, }, } diff --git a/pkg/serverinit/genconfig_test.go b/pkg/serverinit/genconfig_test.go new file mode 100644 index 000000000..095049add --- /dev/null +++ b/pkg/serverinit/genconfig_test.go @@ -0,0 +1,46 @@ +/* +Copyright 2014 The Camlistore Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package serverinit + +import "testing" + +func TestParseUserHostPass(t *testing.T) { + tests := []struct { + in string + user, host, password string + }{ + {in: "foo"}, + {in: "foo@bar"}, + {"bob@server:pass", "bob", "server", "pass"}, + {"bob@server:3307:pass", "bob", "server:3307", "pass"}, + {"bob@server:pass:word", "bob", "server", "pass:word"}, + {"bob@server:9999999:word", "bob", "server", "9999999:word"}, + {"bob@server:123:123:word", "bob", "server:123", "123:word"}, + {"bob@server:123", "bob", "server", "123"}, + {"bob@server:123:", "bob", "server:123", ""}, + } + for _, tt := range tests { + user, host, password, ok := parseUserHostPass(tt.in) + if ok != (user != "" || host != "" || password != "") { + t.Errorf("For input %q, inconsistent output %q, %q, %q, %v", tt.in, user, host, password, ok) + continue + } + if user != tt.user || host != tt.host || password != tt.password { + t.Errorf("parseUserHostPass(%q) = %q, %q, %q; want %q, %q, %q", tt.in, user, host, password, tt.user, tt.host, tt.password) + } + } +} diff --git a/pkg/sorted/mysql/mysqlkv.go b/pkg/sorted/mysql/mysqlkv.go index 406cc1380..5e0c8a91f 100644 --- a/pkg/sorted/mysql/mysqlkv.go +++ b/pkg/sorted/mysql/mysqlkv.go @@ -27,7 +27,6 @@ import ( "camlistore.org/pkg/jsonconfig" "camlistore.org/pkg/sorted" "camlistore.org/pkg/sorted/sqlkv" - _ "camlistore.org/third_party/github.com/go-sql-driver/mysql" )