[MM-18625] Fix database source parsing on go 1.12.8 (#12250)

Этот коммит содержится в:
Ben Schumacher
2019-09-20 06:22:40 +02:00
коммит произвёл GitHub
родитель 33cf37bbc0
Коммит 05fae599b8
2 изменённых файлов: 24 добавлений и 17 удалений

Просмотреть файл

@@ -8,6 +8,7 @@ import (
"database/sql" "database/sql"
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"regexp"
"strings" "strings"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@@ -22,6 +23,8 @@ import (
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
var tcpStripper = regexp.MustCompile(`@tcp\((.*)\)`)
// DatabaseStore is a config store backed by a database. // DatabaseStore is a config store backed by a database.
type DatabaseStore struct { type DatabaseStore struct {
commonStore commonStore
@@ -101,23 +104,22 @@ func initializeConfigurationsTable(db *sqlx.DB) error {
// By contrast, a Postgres DSN is returned unmodified. // By contrast, a Postgres DSN is returned unmodified.
func parseDSN(dsn string) (string, string, error) { func parseDSN(dsn string) (string, string, error) {
// Treat the DSN as the URL that it is. // Treat the DSN as the URL that it is.
u, err := url.Parse(dsn) s := strings.SplitN(dsn, "://", 2)
if err != nil { if len(s) != 2 {
return "", "", errors.Wrap(err, "failed to parse DSN as URL") errors.New("failed to parse DSN as URL")
} }
scheme := u.Scheme scheme := s[0]
switch scheme { switch scheme {
case "mysql": case "mysql":
// Strip off the mysql:// for the dsn with which to connect. // Strip off the mysql:// for the dsn with which to connect.
u.Scheme = "" dsn = s[1]
dsn = strings.TrimPrefix(u.String(), "//")
case "postgres": case "postgres":
// No changes required // No changes required
default: default:
return "", "", errors.Wrapf(err, "unsupported scheme %s", scheme) return "", "", errors.Errorf("unsupported scheme %s", scheme)
} }
return scheme, dsn, nil return scheme, dsn, nil
@@ -293,7 +295,11 @@ func (ds *DatabaseStore) RemoveFile(name string) error {
// String returns the path to the database backing the config, masking the password. // String returns the path to the database backing the config, masking the password.
func (ds *DatabaseStore) String() string { func (ds *DatabaseStore) String() string {
u, _ := url.Parse(ds.originalDsn) // Remove @tcp and the parentheses from the host and parse the rest as a URL
u, err := url.Parse(tcpStripper.ReplaceAllString(ds.originalDsn, `@$1`))
if err != nil {
return "(omitted due to error parsing the DSN)"
}
// Strip out the password to avoid leaking in logs. // Strip out the password to avoid leaking in logs.
u.User = url.User(u.User.Username()) u.User = url.User(u.User.Username())

Просмотреть файл

@@ -6,7 +6,6 @@ package config_test
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"net/url"
"os" "os"
"strings" "strings"
"testing" "testing"
@@ -137,6 +136,11 @@ func TestDatabaseStoreNew(t *testing.T) {
_, err := config.NewDatabaseStore("invalid") _, err := config.NewDatabaseStore("invalid")
require.Error(t, err) require.Error(t, err)
}) })
t.Run("unsupported scheme with valid data source", func(t *testing.T) {
_, err := config.NewDatabaseStore(fmt.Sprintf("invalid://%s", *sqlSettings.DataSource))
require.Error(t, err)
})
} }
func TestDatabaseStoreGet(t *testing.T) { func TestDatabaseStoreGet(t *testing.T) {
@@ -930,14 +934,11 @@ func TestDatabaseStoreString(t *testing.T) {
sqlSettings := mainHelper.GetSqlSettings() sqlSettings := mainHelper.GetSqlSettings()
ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource)) ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, ds)
defer ds.Close() defer ds.Close()
actualStringURL, err := url.Parse(ds.String()) maskedDSN := ds.String()
require.NoError(t, err) assert.True(t, strings.HasPrefix(maskedDSN, "mysql://"))
assert.True(t, strings.Contains(maskedDSN, "mmuser"))
assert.Equal(t, *sqlSettings.DriverName, actualStringURL.Scheme) assert.False(t, strings.Contains(maskedDSN, "mostest"))
actualUsername := actualStringURL.User.Username()
actualPassword, _ := actualStringURL.User.Password()
assert.NotEmpty(t, actualUsername)
assert.Empty(t, actualPassword, "should mask password")
} }