serverinit: allow specifying port numbers in user@server:port:pass

With heuristics and tests.

Change-Id: Ifacfc725db15100c447a3251c23267a5bfd313cf
This commit is contained in:
Brad Fitzpatrick 2014-08-06 15:07:43 -07:00
parent 0757937177
commit b65868d754
3 changed files with 77 additions and 10 deletions

View File

@ -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,
},
}

View File

@ -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)
}
}
}

View File

@ -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"
)