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. | | `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. | | `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. | | `Database` | Name of your MS SQL database. |
| `Authentication` | Authentication mode. Either using SQL Server Authentication or Windows Authentication (single sign on for Windows users). | | `Authentication` | Authentication mode. Either using SQL Server Authentication or Windows Authentication (single sign on for Windows users). |
| `User` | Database user's login/username | | `User` | Database user's login/username |

View File

@ -66,33 +66,44 @@ func ParseURL(u string) (*url.URL, error) {
}, nil }, nil
} }
func generateConnectionString(datasource *models.DataSource) (string, error) { func generateConnectionString(dataSource *models.DataSource) (string, error) {
const dfltPort = "0"
var addr util.NetworkAddress var addr util.NetworkAddress
if datasource.Url != "" { if dataSource.Url != "" {
u, err := ParseURL(datasource.Url) u, err := ParseURL(dataSource.Url)
if err != nil { if err != nil {
return "", err return "", err
} }
addr, err = util.SplitHostPortDefault(u.Host, "localhost", "1433") addr, err = util.SplitHostPortDefault(u.Host, "localhost", dfltPort)
if err != nil { if err != nil {
return "", err return "", err
} }
} else { } else {
addr = util.NetworkAddress{ addr = util.NetworkAddress{
Host: "localhost", Host: "localhost",
Port: "1433", Port: dfltPort,
} }
} }
logger.Debug("Generating connection string", "url", datasource.Url, "host", addr.Host, "port", addr.Port) args := []interface{}{
encrypt := datasource.JsonData.Get("encrypt").MustString("false") "url", dataSource.Url, "host", addr.Host,
connStr := fmt.Sprintf("server=%s;port=%s;database=%s;user id=%s;password=%s;", }
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.Host,
addr.Port, dataSource.Database,
datasource.Database, dataSource.User,
datasource.User, dataSource.DecryptedPassword(),
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" { if encrypt != "false" {
connStr += fmt.Sprintf("encrypt=%s;", encrypt) connStr += fmt.Sprintf("encrypt=%s;", encrypt)
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb/sqleng" "github.com/grafana/grafana/pkg/tsdb/sqleng"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/xorm" "xorm.io/xorm"
) )
@ -32,7 +33,7 @@ var serverIP = "localhost"
func TestMSSQL(t *testing.T) { func TestMSSQL(t *testing.T) {
SkipConvey("MSSQL", t, func() { SkipConvey("MSSQL", t, func() {
x := InitMSSQLTestDB(t) x := initMSSQLTestDB(t)
origXormEngine := sqleng.NewXormEngine origXormEngine := sqleng.NewXormEngine
sqleng.NewXormEngine = func(d, c string) (*xorm.Engine, error) { 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() testDB := sqlutil.MSSQLTestDB()
x, err := xorm.NewEngine(testDB.DriverName, strings.Replace(testDB.ConnStr, "localhost", x, err := xorm.NewEngine(testDB.DriverName, strings.Replace(testDB.ConnStr, "localhost",
serverIP, 1)) serverIP, 1))
if err != nil { require.NoError(t, err)
t.Fatalf("Failed to init mssql db %v", err)
}
x.DatabaseTZ = time.UTC x.DatabaseTZ = time.UTC
x.TZLocation = time.UTC x.TZLocation = time.UTC

View File

@ -4,7 +4,7 @@
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form max-width-30"> <div class="gf-form max-width-30">
<span class="gf-form-label width-7">Host</span> <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>
<div class="gf-form max-width-30"> <div class="gf-form max-width-30">