mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 02:10:45 -06:00
MSSQL: decouple plugin (#89597)
* decouple from core * yarn decouple * make health check work and azure config * f * driver error not needed * merge
This commit is contained in:
parent
4755eb5176
commit
eac194815e
@ -1084,7 +1084,7 @@
|
||||
"keywords": null
|
||||
},
|
||||
"dependencies": {
|
||||
"grafanaDependency": "",
|
||||
"grafanaDependency": "\u003e=10.4.0",
|
||||
"grafanaVersion": "*",
|
||||
"plugins": []
|
||||
},
|
||||
|
@ -48,7 +48,7 @@ const (
|
||||
func ProvideService(cfg *setting.Cfg) *Service {
|
||||
logger := backend.NewLoggerWith("logger", "tsdb.mssql")
|
||||
return &Service{
|
||||
im: datasource.NewInstanceManager(newInstanceSettings(cfg, logger)),
|
||||
im: datasource.NewInstanceManager(NewInstanceSettings(cfg, logger)),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
@ -136,12 +136,17 @@ func newMSSQL(ctx context.Context, driverName string, userFacingDefaultError str
|
||||
return db, handler, nil
|
||||
}
|
||||
|
||||
func newInstanceSettings(cfg *setting.Cfg, logger log.Logger) datasource.InstanceFactoryFunc {
|
||||
func NewInstanceSettings(cfg *setting.Cfg, logger log.Logger) datasource.InstanceFactoryFunc {
|
||||
return func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
|
||||
grafCfg := backend.GrafanaConfigFromContext(ctx)
|
||||
sqlCfg, err := grafCfg.SQL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonData := sqleng.JsonData{
|
||||
MaxOpenConns: cfg.SqlDatasourceMaxOpenConnsDefault,
|
||||
MaxIdleConns: cfg.SqlDatasourceMaxIdleConnsDefault,
|
||||
ConnMaxLifetime: cfg.SqlDatasourceMaxConnLifetimeDefault,
|
||||
MaxOpenConns: sqlCfg.DefaultMaxOpenConns,
|
||||
MaxIdleConns: sqlCfg.DefaultMaxIdleConns,
|
||||
ConnMaxLifetime: sqlCfg.DefaultMaxConnLifetimeSeconds,
|
||||
Encrypt: "false",
|
||||
ConnectionTimeout: 0,
|
||||
SecureDSProxy: false,
|
||||
@ -176,20 +181,22 @@ func newInstanceSettings(cfg *setting.Cfg, logger log.Logger) datasource.Instanc
|
||||
UID: settings.UID,
|
||||
DecryptedSecureJSONData: settings.DecryptedSecureJSONData,
|
||||
}
|
||||
cnnstr, err := generateConnectionString(dsInfo, cfg, azureCredentials, kerberosAuth, logger)
|
||||
cnnstr, err := generateConnectionString(dsInfo, cfg.Azure.ManagedIdentityClientId, cfg.Azure.AzureEntraPasswordCredentialsEnabled, azureCredentials, kerberosAuth, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.Env == setting.Dev {
|
||||
logger.Debug("GetEngine", "connection", cnnstr)
|
||||
}
|
||||
driverName := "mssql"
|
||||
if jsonData.AuthenticationType == azureAuthentication {
|
||||
driverName = "azuresql"
|
||||
}
|
||||
|
||||
_, handler, err := newMSSQL(ctx, driverName, cfg.UserFacingDefaultError, cfg.DataProxyRowLimit, dsInfo, cnnstr, logger, settings)
|
||||
userFacingDefaultError, err := grafCfg.UserFacingDefaultError()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, handler, err := newMSSQL(ctx, driverName, userFacingDefaultError, sqlCfg.RowLimit, dsInfo, cnnstr, logger, settings)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("Failed connecting to MSSQL", "err", err)
|
||||
@ -230,7 +237,7 @@ func ParseURL(u string, logger DebugOnlyLogger) (*url.URL, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func generateConnectionString(dsInfo sqleng.DataSourceInfo, cfg *setting.Cfg, azureCredentials azcredentials.AzureCredentials, kerberosAuth kerberos.KerberosAuth, logger log.Logger) (string, error) {
|
||||
func generateConnectionString(dsInfo sqleng.DataSourceInfo, azureManagedIdentityClientId string, azureEntraPasswordCredentialsEnabled bool, azureCredentials azcredentials.AzureCredentials, kerberosAuth kerberos.KerberosAuth, logger log.Logger) (string, error) {
|
||||
const dfltPort = "0"
|
||||
var addr util.NetworkAddress
|
||||
if dsInfo.URL != "" {
|
||||
@ -268,7 +275,7 @@ func generateConnectionString(dsInfo sqleng.DataSourceInfo, cfg *setting.Cfg, az
|
||||
|
||||
switch dsInfo.JsonData.AuthenticationType {
|
||||
case azureAuthentication:
|
||||
azureCredentialDSNFragment, err := getAzureCredentialDSNFragment(azureCredentials, cfg)
|
||||
azureCredentialDSNFragment, err := getAzureCredentialDSNFragment(azureCredentials, azureManagedIdentityClientId, azureEntraPasswordCredentialsEnabled)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -305,12 +312,12 @@ func generateConnectionString(dsInfo sqleng.DataSourceInfo, cfg *setting.Cfg, az
|
||||
return connStr, nil
|
||||
}
|
||||
|
||||
func getAzureCredentialDSNFragment(azureCredentials azcredentials.AzureCredentials, cfg *setting.Cfg) (string, error) {
|
||||
func getAzureCredentialDSNFragment(azureCredentials azcredentials.AzureCredentials, azureManagedIdentityClientId string, azureEntraPasswordCredentialsEnabled bool) (string, error) {
|
||||
connStr := ""
|
||||
switch c := azureCredentials.(type) {
|
||||
case *azcredentials.AzureManagedIdentityCredentials:
|
||||
if cfg.Azure.ManagedIdentityClientId != "" {
|
||||
connStr += fmt.Sprintf("user id=%s;", cfg.Azure.ManagedIdentityClientId)
|
||||
if azureManagedIdentityClientId != "" {
|
||||
connStr += fmt.Sprintf("user id=%s;", azureManagedIdentityClientId)
|
||||
}
|
||||
connStr += fmt.Sprintf("fedauth=%s;",
|
||||
"ActiveDirectoryManagedIdentity")
|
||||
@ -322,7 +329,7 @@ func getAzureCredentialDSNFragment(azureCredentials azcredentials.AzureCredentia
|
||||
"ActiveDirectoryApplication",
|
||||
)
|
||||
case *azcredentials.AzureEntraPasswordCredentials:
|
||||
if cfg.Azure.AzureEntraPasswordCredentialsEnabled {
|
||||
if azureEntraPasswordCredentialsEnabled {
|
||||
connStr += fmt.Sprintf("user id=%s;password=%s;applicationclientid=%s;fedauth=%s;",
|
||||
c.UserId,
|
||||
c.Password,
|
||||
@ -360,13 +367,7 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dsHandler.Ping()
|
||||
|
||||
if err != nil {
|
||||
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: dsHandler.TransformQueryError(s.logger, err).Error()}, nil
|
||||
}
|
||||
|
||||
return &backend.CheckHealthResult{Status: backend.HealthStatusOk, Message: "Database Connection OK"}, nil
|
||||
return dsHandler.CheckHealth(ctx, req)
|
||||
}
|
||||
|
||||
func (t *mssqlQueryResultTransformer) GetConverterList() []sqlutil.StringConverter {
|
||||
|
@ -1550,7 +1550,7 @@ func TestGenerateConnectionString(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
connStr, err := generateConnectionString(tc.dataSource, nil, nil, tc.kerberosCfg, logger)
|
||||
connStr, err := generateConnectionString(tc.dataSource, "", false, nil, tc.kerberosCfg, logger)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expConnStr, connStr)
|
||||
})
|
||||
|
@ -152,8 +152,13 @@ func (e *DataSourceHandler) Dispose() {
|
||||
e.log.Debug("DB disposed")
|
||||
}
|
||||
|
||||
func (e *DataSourceHandler) Ping() error {
|
||||
return e.db.Ping()
|
||||
func (e *DataSourceHandler) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
err := e.db.Ping()
|
||||
|
||||
if err != nil {
|
||||
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: e.TransformQueryError(e.log, err).Error()}, nil
|
||||
}
|
||||
return &backend.CheckHealthResult{Status: backend.HealthStatusOk, Message: "Database Connection OK"}, nil
|
||||
}
|
||||
|
||||
func (e *DataSourceHandler) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
|
28
pkg/tsdb/mssql/standalone/main.go
Normal file
28
pkg/tsdb/mssql/standalone/main.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Start listening to requests sent from Grafana. This call is blocking so
|
||||
// it won't finish until Grafana shuts down the process or the plugin choose
|
||||
// to exit by itself using os.Exit. Manage automatically manages life cycle
|
||||
// of datasource instances. It accepts datasource instance factory as first
|
||||
// argument. This factory will be automatically called on incoming request
|
||||
// from Grafana to create different instances of SampleDatasource (per datasource
|
||||
// ID). When datasource configuration changed Dispose method will be called and
|
||||
// new datasource instance created using NewSampleDatasource factory.
|
||||
logger := backend.NewLoggerWith("logger", "tsdb.mssql")
|
||||
cfg := setting.NewCfg()
|
||||
if err := datasource.Manage("mssql", mssql.NewInstanceSettings(cfg, logger), datasource.ManageOpts{}); err != nil {
|
||||
log.DefaultLogger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
@ -17,8 +17,6 @@ const mixedPlugin = async () =>
|
||||
await import(/* webpackChunkName: "mixedPlugin" */ 'app/plugins/datasource/mixed/module');
|
||||
const prometheusPlugin = async () =>
|
||||
await import(/* webpackChunkName: "prometheusPlugin" */ 'app/plugins/datasource/prometheus/module');
|
||||
const mssqlPlugin = async () =>
|
||||
await import(/* webpackChunkName: "mssqlPlugin" */ 'app/plugins/datasource/mssql/module');
|
||||
const alertmanagerPlugin = async () =>
|
||||
await import(/* webpackChunkName: "alertmanagerPlugin" */ 'app/plugins/datasource/alertmanager/module');
|
||||
|
||||
@ -79,7 +77,6 @@ const builtInPlugins: Record<string, System.Module | (() => Promise<System.Modul
|
||||
'core:plugin/influxdb': influxdbPlugin,
|
||||
'core:plugin/loki': lokiPlugin,
|
||||
'core:plugin/mixed': mixedPlugin,
|
||||
'core:plugin/mssql': mssqlPlugin,
|
||||
'core:plugin/prometheus': prometheusPlugin,
|
||||
'core:plugin/alertmanager': alertmanagerPlugin,
|
||||
// panels
|
||||
|
1
public/app/plugins/datasource/mssql/CHANGELOG.md
Normal file
1
public/app/plugins/datasource/mssql/CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
||||
# Changelog
|
@ -11,7 +11,9 @@ import {
|
||||
updateDatasourcePluginResetOption,
|
||||
} from '@grafana/data';
|
||||
import { ConfigSection, ConfigSubSection, DataSourceDescription } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { ConnectionLimits, useMigrateDatabaseFields } from '@grafana/sql';
|
||||
import { NumberInput } from '@grafana/sql/src/components/configuration/NumberInput';
|
||||
import {
|
||||
Alert,
|
||||
FieldSet,
|
||||
@ -25,8 +27,6 @@ import {
|
||||
Field,
|
||||
Switch,
|
||||
} from '@grafana/ui';
|
||||
import { NumberInput } from 'app/core/components/OptionsUI/NumberInput';
|
||||
import { config } from 'app/core/config';
|
||||
|
||||
import { AzureAuthSettings } from '../azureauth/AzureAuthSettings';
|
||||
import {
|
||||
@ -92,7 +92,10 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
|
||||
};
|
||||
|
||||
const onConnectionTimeoutChanged = (connectionTimeout?: number) => {
|
||||
updateDatasourcePluginJsonDataOption(props, 'connectionTimeout', connectionTimeout ?? 0);
|
||||
if (connectionTimeout && connectionTimeout < 0) {
|
||||
connectionTimeout = 0;
|
||||
}
|
||||
updateDatasourcePluginJsonDataOption(props, 'connectionTimeout', connectionTimeout);
|
||||
};
|
||||
|
||||
const buildAuthenticationOptions = (): Array<SelectableValue<MSSQLAuthenticationType>> => {
|
||||
@ -366,9 +369,8 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
|
||||
>
|
||||
<NumberInput
|
||||
width={LONG_WIDTH}
|
||||
placeholder="60"
|
||||
min={0}
|
||||
value={jsonData.connectionTimeout}
|
||||
defaultValue={60}
|
||||
value={jsonData.connectionTimeout || 0}
|
||||
onChange={onConnectionTimeoutChanged}
|
||||
/>
|
||||
</Field>
|
||||
|
41
public/app/plugins/datasource/mssql/package.json
Normal file
41
public/app/plugins/datasource/mssql/package.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@grafana-plugins/mssql",
|
||||
"description": "MSSQL data source plugin",
|
||||
"private": true,
|
||||
"version": "11.3.0-pre",
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.11.2",
|
||||
"@grafana/data": "workspace:*",
|
||||
"@grafana/experimental": "1.7.12",
|
||||
"@grafana/runtime": "11.3.0-pre",
|
||||
"@grafana/sql": "11.3.0-pre",
|
||||
"@grafana/ui": "11.3.0-pre",
|
||||
"lodash": "4.17.21",
|
||||
"react": "18.2.0",
|
||||
"rxjs": "7.8.1",
|
||||
"tslib": "2.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@grafana/e2e-selectors": "workspace:*",
|
||||
"@grafana/plugin-configs": "workspace:*",
|
||||
"@testing-library/react": "15.0.2",
|
||||
"@testing-library/user-event": "14.5.2",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/lodash": "4.17.4",
|
||||
"@types/node": "20.14.2",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/testing-library__jest-dom": "5.14.9",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.4.5",
|
||||
"webpack": "5.91.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@grafana/runtime": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack -c ./webpack.config.ts --env production",
|
||||
"build:commit": "webpack -c ./webpack.config.ts --env production --env commit=$(git rev-parse --short HEAD)",
|
||||
"dev": "webpack -w -c ./webpack.config.ts --env development"
|
||||
},
|
||||
"packageManager": "yarn@4.4.0"
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
"type": "datasource",
|
||||
"name": "Microsoft SQL Server",
|
||||
"id": "mssql",
|
||||
"executable": "gpx_mssql",
|
||||
"category": "sql",
|
||||
|
||||
"info": {
|
||||
@ -13,7 +14,11 @@
|
||||
"logos": {
|
||||
"small": "img/sql_server_logo.svg",
|
||||
"large": "img/sql_server_logo.svg"
|
||||
}
|
||||
},
|
||||
"version": "%VERSION%"
|
||||
},
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=10.4.0"
|
||||
},
|
||||
|
||||
"alerting": true,
|
||||
|
4
public/app/plugins/datasource/mssql/tsconfig.json
Normal file
4
public/app/plugins/datasource/mssql/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@grafana/plugin-configs/tsconfig.json",
|
||||
"include": ["."]
|
||||
}
|
4
public/app/plugins/datasource/mssql/webpack.config.ts
Normal file
4
public/app/plugins/datasource/mssql/webpack.config.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import config from '@grafana/plugin-configs/webpack.config';
|
||||
|
||||
// eslint-disable-next-line no-barrel-files/no-barrel-files
|
||||
export default config;
|
47
yarn.lock
47
yarn.lock
@ -3102,6 +3102,37 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana-plugins/mssql@workspace:public/app/plugins/datasource/mssql":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana-plugins/mssql@workspace:public/app/plugins/datasource/mssql"
|
||||
dependencies:
|
||||
"@emotion/css": "npm:11.11.2"
|
||||
"@grafana/data": "workspace:*"
|
||||
"@grafana/e2e-selectors": "workspace:*"
|
||||
"@grafana/experimental": "npm:1.7.12"
|
||||
"@grafana/plugin-configs": "workspace:*"
|
||||
"@grafana/runtime": "npm:11.3.0-pre"
|
||||
"@grafana/sql": "npm:11.3.0-pre"
|
||||
"@grafana/ui": "npm:11.3.0-pre"
|
||||
"@testing-library/react": "npm:15.0.2"
|
||||
"@testing-library/user-event": "npm:14.5.2"
|
||||
"@types/jest": "npm:29.5.12"
|
||||
"@types/lodash": "npm:4.17.4"
|
||||
"@types/node": "npm:20.14.2"
|
||||
"@types/react": "npm:18.3.3"
|
||||
"@types/testing-library__jest-dom": "npm:5.14.9"
|
||||
lodash: "npm:4.17.21"
|
||||
react: "npm:18.2.0"
|
||||
rxjs: "npm:7.8.1"
|
||||
ts-node: "npm:10.9.2"
|
||||
tslib: "npm:2.6.3"
|
||||
typescript: "npm:5.4.5"
|
||||
webpack: "npm:5.91.0"
|
||||
peerDependencies:
|
||||
"@grafana/runtime": "*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana-plugins/mysql@workspace:public/app/plugins/datasource/mysql":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana-plugins/mysql@workspace:public/app/plugins/datasource/mysql"
|
||||
@ -9821,6 +9852,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash@npm:4.17.4":
|
||||
version: 4.17.4
|
||||
resolution: "@types/lodash@npm:4.17.4"
|
||||
checksum: 10/3ec19f9fc48200006e71733e08bcb1478b0398673657fcfb21a8643d41a80bcce09a01000077c3b23a3c6d86b9b314abe0672a8fdfc0fd66b893bd41955cfab8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/logfmt@npm:^1.2.3":
|
||||
version: 1.2.6
|
||||
resolution: "@types/logfmt@npm:1.2.6"
|
||||
@ -9932,6 +9970,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:20.14.2":
|
||||
version: 20.14.2
|
||||
resolution: "@types/node@npm:20.14.2"
|
||||
dependencies:
|
||||
undici-types: "npm:~5.26.4"
|
||||
checksum: 10/c38e47b190fa0a8bdfde24b036dddcf9401551f2fb170a90ff33625c7d6f218907e81c74e0fa6e394804a32623c24c60c50e249badc951007830f0d02c48ee0f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^18.0.0":
|
||||
version: 18.19.33
|
||||
resolution: "@types/node@npm:18.19.33"
|
||||
|
Loading…
Reference in New Issue
Block a user