MSSQL: By default let driver choose port (#32417)

* MSSQL: By default let driver choose port

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Arve Knudsen 2021-03-30 10:54:57 +02:00 committed by GitHub
parent 0e47a476fb
commit 2c72f8babc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 19 deletions

View File

@ -16,7 +16,7 @@ Grafana ships with a built-in Microsoft SQL Server (MS SQL) data source plugin t
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `Name` | The data source name. This is how you refer to the data source in panels and queries. |
| `Default` | Default data source means that it will be pre-selected for new panels. |
| `Host` | The IP address/hostname and optional port of your MS SQL instance. If port is omitted, default 1433 will be used. |
| `Host` | The IP address/hostname and optional port of your MS SQL instance. If the port is omitted, the driver default will be used (0). |
| `Database` | Name of your MS SQL database. |
| `Authentication` | Authentication mode. Either using SQL Server Authentication or Windows Authentication (single sign on for Windows users). |
| `User` | Database user's login/username |

View File

@ -66,33 +66,44 @@ func ParseURL(u string) (*url.URL, error) {
}, nil
}
func generateConnectionString(datasource *models.DataSource) (string, error) {
func generateConnectionString(dataSource *models.DataSource) (string, error) {
const dfltPort = "0"
var addr util.NetworkAddress
if datasource.Url != "" {
u, err := ParseURL(datasource.Url)
if dataSource.Url != "" {
u, err := ParseURL(dataSource.Url)
if err != nil {
return "", err
}
addr, err = util.SplitHostPortDefault(u.Host, "localhost", "1433")
addr, err = util.SplitHostPortDefault(u.Host, "localhost", dfltPort)
if err != nil {
return "", err
}
} else {
addr = util.NetworkAddress{
Host: "localhost",
Port: "1433",
Port: dfltPort,
}
}
logger.Debug("Generating connection string", "url", datasource.Url, "host", addr.Host, "port", addr.Port)
encrypt := datasource.JsonData.Get("encrypt").MustString("false")
connStr := fmt.Sprintf("server=%s;port=%s;database=%s;user id=%s;password=%s;",
args := []interface{}{
"url", dataSource.Url, "host", addr.Host,
}
if addr.Port != "0" {
args = append(args, "port", addr.Port)
}
logger.Debug("Generating connection string", args...)
encrypt := dataSource.JsonData.Get("encrypt").MustString("false")
connStr := fmt.Sprintf("server=%s;database=%s;user id=%s;password=%s;",
addr.Host,
addr.Port,
datasource.Database,
datasource.User,
datasource.DecryptedPassword(),
dataSource.Database,
dataSource.User,
dataSource.DecryptedPassword(),
)
// Port number 0 means to determine the port automatically, so we can let the driver choose
if addr.Port != "0" {
connStr += fmt.Sprintf("port=%s;", addr.Port)
}
if encrypt != "false" {
connStr += fmt.Sprintf("encrypt=%s;", encrypt)
}

View File

@ -17,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb/sqleng"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/xorm"
)
@ -32,7 +33,7 @@ var serverIP = "localhost"
func TestMSSQL(t *testing.T) {
SkipConvey("MSSQL", t, func() {
x := InitMSSQLTestDB(t)
x := initMSSQLTestDB(t)
origXormEngine := sqleng.NewXormEngine
sqleng.NewXormEngine = func(d, c string) (*xorm.Engine, error) {
@ -1161,13 +1162,70 @@ func TestTransformQueryError(t *testing.T) {
}
}
func InitMSSQLTestDB(t *testing.T) *xorm.Engine {
func TestGenerateConnectionString(t *testing.T) {
testCases := []struct {
desc string
dataSource *models.DataSource
expConnStr string
}{
{
desc: "From URL w/ port",
dataSource: &models.DataSource{
Url: "localhost:1001",
Database: "database",
User: "user",
JsonData: simplejson.NewFromAny(map[string]interface{}{}),
},
expConnStr: "server=localhost;database=database;user id=user;password=;port=1001;",
},
// When no port is specified, the driver should be allowed to choose
{
desc: "From URL w/o port",
dataSource: &models.DataSource{
Url: "localhost",
Database: "database",
User: "user",
JsonData: simplejson.NewFromAny(map[string]interface{}{}),
},
expConnStr: "server=localhost;database=database;user id=user;password=;",
},
// Port 0 should be equivalent to not specifying a port, i.e. let the driver choose
{
desc: "From URL w port 0",
dataSource: &models.DataSource{
Url: "localhost:0",
Database: "database",
User: "user",
JsonData: simplejson.NewFromAny(map[string]interface{}{}),
},
expConnStr: "server=localhost;database=database;user id=user;password=;",
},
{
desc: "Defaults",
dataSource: &models.DataSource{
Database: "database",
User: "user",
JsonData: simplejson.NewFromAny(map[string]interface{}{}),
},
expConnStr: "server=localhost;database=database;user id=user;password=;",
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
connStr, err := generateConnectionString(tc.dataSource)
require.NoError(t, err)
assert.Equal(t, tc.expConnStr, connStr)
})
}
}
func initMSSQLTestDB(t *testing.T) *xorm.Engine {
t.Helper()
testDB := sqlutil.MSSQLTestDB()
x, err := xorm.NewEngine(testDB.DriverName, strings.Replace(testDB.ConnStr, "localhost",
serverIP, 1))
if err != nil {
t.Fatalf("Failed to init mssql db %v", err)
}
require.NoError(t, err)
x.DatabaseTZ = time.UTC
x.TZLocation = time.UTC

View File

@ -4,7 +4,7 @@
<div class="gf-form-group">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">Host</span>
<input type="text" class="gf-form-input" style="width: 352px" ng-model='ctrl.current.url' placeholder="localhost:1433" bs-typeahead="{{['localhost', 'localhost:1433']}}" required></input>
<input type="text" class="gf-form-input" style="width: 352px" ng-model='ctrl.current.url' placeholder="localhost" bs-typeahead="{{['localhost', 'localhost" required></input>
</div>
<div class="gf-form max-width-30">