Fix parsing timeout (use number instead of string), fixes #1254

This commit is contained in:
Alexander Zobnin
2021-08-11 13:32:37 +03:00
parent 95afc7460d
commit 5ed80a60e7
10 changed files with 143 additions and 176 deletions

View File

@@ -2,16 +2,11 @@ package datasource
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"strconv"
"time"
"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
"github.com/alexanderzobnin/grafana-zabbix/pkg/httpclient" "github.com/alexanderzobnin/grafana-zabbix/pkg/httpclient"
"github.com/alexanderzobnin/grafana-zabbix/pkg/settings"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"
"github.com/grafana/grafana-plugin-sdk-go/backend" "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/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
@@ -33,7 +28,7 @@ type ZabbixDatasource struct {
type ZabbixDatasourceInstance struct { type ZabbixDatasourceInstance struct {
zabbix *zabbix.Zabbix zabbix *zabbix.Zabbix
dsInfo *backend.DataSourceInstanceSettings dsInfo *backend.DataSourceInstanceSettings
Settings *ZabbixDatasourceSettings Settings *settings.ZabbixDatasourceSettings
logger log.Logger logger log.Logger
} }
@@ -46,36 +41,36 @@ func NewZabbixDatasource() *ZabbixDatasource {
} }
// newZabbixDatasourceInstance returns an initialized zabbix datasource instance // newZabbixDatasourceInstance returns an initialized zabbix datasource instance
func newZabbixDatasourceInstance(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { func newZabbixDatasourceInstance(dsSettings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
logger := log.New() logger := log.New()
logger.Debug("Initializing new data source instance") logger.Debug("Initializing new data source instance")
zabbixSettings, err := readZabbixSettings(&settings) zabbixSettings, err := settings.ReadZabbixSettings(&dsSettings)
if err != nil { if err != nil {
logger.Error("Error parsing Zabbix settings", "error", err) logger.Error("Error parsing Zabbix settings", "error", err)
return nil, err return nil, err
} }
client, err := httpclient.New(&settings, zabbixSettings.Timeout) client, err := httpclient.New(&dsSettings, zabbixSettings.Timeout)
if err != nil { if err != nil {
logger.Error("Error initializing HTTP client", "error", err) logger.Error("Error initializing HTTP client", "error", err)
return nil, err return nil, err
} }
zabbixAPI, err := zabbixapi.New(settings.URL, client) zabbixAPI, err := zabbixapi.New(dsSettings.URL, client)
if err != nil { if err != nil {
logger.Error("Error initializing Zabbix API", "error", err) logger.Error("Error initializing Zabbix API", "error", err)
return nil, err return nil, err
} }
zabbixClient, err := zabbix.New(&settings, zabbixAPI) zabbixClient, err := zabbix.New(&dsSettings, zabbixSettings, zabbixAPI)
if err != nil { if err != nil {
logger.Error("Error initializing Zabbix client", "error", err) logger.Error("Error initializing Zabbix client", "error", err)
return nil, err return nil, err
} }
return &ZabbixDatasourceInstance{ return &ZabbixDatasourceInstance{
dsInfo: &settings, dsInfo: &dsSettings,
zabbix: zabbixClient, zabbix: zabbixClient,
Settings: zabbixSettings, Settings: zabbixSettings,
logger: logger, logger: logger,
@@ -153,58 +148,3 @@ func (ds *ZabbixDatasource) getDSInstance(pluginContext backend.PluginContext) (
} }
return instance.(*ZabbixDatasourceInstance), nil return instance.(*ZabbixDatasourceInstance), nil
} }
func readZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings) (*ZabbixDatasourceSettings, error) {
zabbixSettingsDTO := &ZabbixDatasourceSettingsDTO{}
err := json.Unmarshal(dsInstanceSettings.JSONData, &zabbixSettingsDTO)
if err != nil {
return nil, err
}
if zabbixSettingsDTO.TrendsFrom == "" {
zabbixSettingsDTO.TrendsFrom = "7d"
}
if zabbixSettingsDTO.TrendsRange == "" {
zabbixSettingsDTO.TrendsRange = "4d"
}
if zabbixSettingsDTO.CacheTTL == "" {
zabbixSettingsDTO.CacheTTL = "1h"
}
if zabbixSettingsDTO.Timeout == "" {
zabbixSettingsDTO.Timeout = "30"
}
trendsFrom, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsFrom)
if err != nil {
return nil, err
}
trendsRange, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsRange)
if err != nil {
return nil, err
}
cacheTTL, err := gtime.ParseInterval(zabbixSettingsDTO.CacheTTL)
if err != nil {
return nil, err
}
timeout, err := strconv.Atoi(zabbixSettingsDTO.Timeout)
if err != nil {
return nil, errors.New("failed to parse timeout: " + err.Error())
}
zabbixSettings := &ZabbixDatasourceSettings{
Trends: zabbixSettingsDTO.Trends,
TrendsFrom: trendsFrom,
TrendsRange: trendsRange,
CacheTTL: cacheTTL,
Timeout: time.Duration(timeout) * time.Second,
DisableDataAlignment: zabbixSettingsDTO.DisableDataAlignment,
DisableReadOnlyUsersAck: zabbixSettingsDTO.DisableReadOnlyUsersAck,
}
return zabbixSettings, nil
}

View File

@@ -21,30 +21,6 @@ const (
MODE_PROBLEMS = "5" MODE_PROBLEMS = "5"
) )
// ZabbixDatasourceSettingsDTO model
type ZabbixDatasourceSettingsDTO struct {
Trends bool `json:"trends"`
TrendsFrom string `json:"trendsFrom"`
TrendsRange string `json:"trendsRange"`
CacheTTL string `json:"cacheTTL"`
Timeout string `json:"timeout"`
DisableDataAlignment bool `json:"disableDataAlignment"`
DisableReadOnlyUsersAck bool `json:"disableReadOnlyUsersAck"`
}
// ZabbixDatasourceSettings model
type ZabbixDatasourceSettings struct {
Trends bool
TrendsFrom time.Duration
TrendsRange time.Duration
CacheTTL time.Duration
Timeout time.Duration
DisableDataAlignment bool `json:"disableDataAlignment"`
DisableReadOnlyUsersAck bool `json:"disableReadOnlyUsersAck"`
}
type DBConnectionPostProcessingRequest struct { type DBConnectionPostProcessingRequest struct {
Query QueryModel `json:"query"` Query QueryModel `json:"query"`
TimeRange TimeRangePostProcessingRequest `json:"timeRange"` TimeRange TimeRangePostProcessingRequest `json:"timeRange"`

View File

@@ -1,6 +1,7 @@
package datasource package datasource
import ( import (
"github.com/alexanderzobnin/grafana-zabbix/pkg/settings"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/log"
@@ -23,7 +24,7 @@ func mockZabbixQuery(method string, params zabbix.ZabbixAPIParams) *zabbix.Zabbi
} }
func MockZabbixDataSource(body string, statusCode int) *ZabbixDatasourceInstance { func MockZabbixDataSource(body string, statusCode int) *ZabbixDatasourceInstance {
zabbixSettings, _ := readZabbixSettings(basicDatasourceInfo) zabbixSettings, _ := settings.ReadZabbixSettings(basicDatasourceInfo)
zabbixClient, _ := zabbix.MockZabbixClient(basicDatasourceInfo, body, statusCode) zabbixClient, _ := zabbix.MockZabbixClient(basicDatasourceInfo, body, statusCode)
return &ZabbixDatasourceInstance{ return &ZabbixDatasourceInstance{

27
pkg/settings/models.go Normal file
View File

@@ -0,0 +1,27 @@
package settings
import "time"
// ZabbixDatasourceSettingsDTO model
type ZabbixDatasourceSettingsDTO struct {
Trends bool `json:"trends"`
TrendsFrom string `json:"trendsFrom"`
TrendsRange string `json:"trendsRange"`
CacheTTL string `json:"cacheTTL"`
Timeout interface{} `json:"timeout"`
DisableDataAlignment bool `json:"disableDataAlignment"`
DisableReadOnlyUsersAck bool `json:"disableReadOnlyUsersAck"`
}
// ZabbixDatasourceSettings model
type ZabbixDatasourceSettings struct {
Trends bool
TrendsFrom time.Duration
TrendsRange time.Duration
CacheTTL time.Duration
Timeout time.Duration
DisableDataAlignment bool `json:"disableDataAlignment"`
DisableReadOnlyUsersAck bool `json:"disableReadOnlyUsersAck"`
}

View File

@@ -1,16 +1,15 @@
package zabbix package settings
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"strconv"
"time"
"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime" "github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"strconv"
"time"
) )
func readZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings) (*ZabbixDatasourceSettings, error) { func ReadZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings) (*ZabbixDatasourceSettings, error) {
zabbixSettingsDTO := &ZabbixDatasourceSettingsDTO{} zabbixSettingsDTO := &ZabbixDatasourceSettingsDTO{}
err := json.Unmarshal(dsInstanceSettings.JSONData, &zabbixSettingsDTO) err := json.Unmarshal(dsInstanceSettings.JSONData, &zabbixSettingsDTO)
@@ -28,9 +27,9 @@ func readZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings)
zabbixSettingsDTO.CacheTTL = "1h" zabbixSettingsDTO.CacheTTL = "1h"
} }
if zabbixSettingsDTO.Timeout == "" { //if zabbixSettingsDTO.Timeout == 0 {
zabbixSettingsDTO.Timeout = "30" // zabbixSettingsDTO.Timeout = 30
} //}
trendsFrom, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsFrom) trendsFrom, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsFrom)
if err != nil { if err != nil {
@@ -47,10 +46,23 @@ func readZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings)
return nil, err return nil, err
} }
timeout, err := strconv.Atoi(zabbixSettingsDTO.Timeout) var timeout int64
switch t := zabbixSettingsDTO.Timeout.(type) {
case string:
if t == "" {
timeout = 30
break
}
timeoutInt, err := strconv.Atoi(t)
if err != nil { if err != nil {
return nil, errors.New("failed to parse timeout: " + err.Error()) return nil, errors.New("failed to parse timeout: " + err.Error())
} }
timeout = int64(timeoutInt)
case float64:
timeout = int64(t)
default:
timeout = 30
}
zabbixSettings := &ZabbixDatasourceSettings{ zabbixSettings := &ZabbixDatasourceSettings{
Trends: zabbixSettingsDTO.Trends, Trends: zabbixSettingsDTO.Trends,
@@ -58,6 +70,8 @@ func readZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings)
TrendsRange: trendsRange, TrendsRange: trendsRange,
CacheTTL: cacheTTL, CacheTTL: cacheTTL,
Timeout: time.Duration(timeout) * time.Second, Timeout: time.Duration(timeout) * time.Second,
DisableDataAlignment: zabbixSettingsDTO.DisableDataAlignment,
DisableReadOnlyUsersAck: zabbixSettingsDTO.DisableReadOnlyUsersAck,
} }
return zabbixSettings, nil return zabbixSettings, nil

View File

@@ -1,8 +1,10 @@
package zabbix package zabbix
import ( import (
"github.com/alexanderzobnin/grafana-zabbix/pkg/settings"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"time"
) )
func MockZabbixClient(dsInfo *backend.DataSourceInstanceSettings, body string, statusCode int) (*Zabbix, error) { func MockZabbixClient(dsInfo *backend.DataSourceInstanceSettings, body string, statusCode int) (*Zabbix, error) {
@@ -10,8 +12,11 @@ func MockZabbixClient(dsInfo *backend.DataSourceInstanceSettings, body string, s
if err != nil { if err != nil {
return nil, err return nil, err
} }
zabbixSettings := &settings.ZabbixDatasourceSettings{
Timeout: 10 * time.Second,
}
client, err := New(dsInfo, zabbixAPI) client, err := New(dsInfo, zabbixSettings, zabbixAPI)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -5,6 +5,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/alexanderzobnin/grafana-zabbix/pkg/settings"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"
"github.com/bitly/go-simplejson" "github.com/bitly/go-simplejson"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
@@ -22,15 +23,8 @@ type Zabbix struct {
} }
// New returns new instance of Zabbix client. // New returns new instance of Zabbix client.
func New(dsInfo *backend.DataSourceInstanceSettings, zabbixAPI *zabbixapi.ZabbixAPI) (*Zabbix, error) { func New(dsInfo *backend.DataSourceInstanceSettings, zabbixSettings *settings.ZabbixDatasourceSettings, zabbixAPI *zabbixapi.ZabbixAPI) (*Zabbix, error) {
logger := log.New() logger := log.New()
zabbixSettings, err := readZabbixSettings(dsInfo)
if err != nil {
logger.Error("Error parsing Zabbix settings", "error", err)
return nil, err
}
zabbixCache := NewZabbixCache(zabbixSettings.CacheTTL, 10*time.Minute) zabbixCache := NewZabbixCache(zabbixSettings.CacheTTL, 10*time.Minute)
return &Zabbix{ return &Zabbix{

View File

@@ -1,10 +1,11 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { getDataSourceSrv } from '@grafana/runtime'; import { getDataSourceSrv } from '@grafana/runtime';
import { DataSourcePluginOptionsEditorProps, DataSourceSettings, SelectableValue } from '@grafana/data'; import { DataSourcePluginOptionsEditorProps, DataSourceSettings, SelectableValue } from '@grafana/data';
import { DataSourceHttpSettings, LegacyForms, Field, Input, Button, InlineFormLabel, Select } from '@grafana/ui'; import { Button, DataSourceHttpSettings, InlineFormLabel, LegacyForms, Select } from '@grafana/ui';
const { FormField, Switch } = LegacyForms;
import { ZabbixDSOptions, ZabbixSecureJSONData } from '../types'; import { ZabbixDSOptions, ZabbixSecureJSONData } from '../types';
const { FormField, Switch } = LegacyForms;
const SUPPORTED_SQL_DS = ['mysql', 'postgres', 'influxdb']; const SUPPORTED_SQL_DS = ['mysql', 'postgres', 'influxdb'];
export type Props = DataSourcePluginOptionsEditorProps<ZabbixDSOptions, ZabbixSecureJSONData>; export type Props = DataSourcePluginOptionsEditorProps<ZabbixDSOptions, ZabbixSecureJSONData>;
@@ -34,7 +35,7 @@ export const ConfigEditor = (props: Props) => {
trendsFrom: '', trendsFrom: '',
trendsRange: '', trendsRange: '',
cacheTTL: '', cacheTTL: '',
timeout: '', timeout: undefined,
disableDataAlignment: false, disableDataAlignment: false,
...restJsonData, ...restJsonData,
}, },
@@ -99,7 +100,7 @@ export const ConfigEditor = (props: Props) => {
placeholder="Configured" placeholder="Configured"
/> />
<Button onClick={resetSecureJsonField('password', options, onOptionsChange)}>Reset</Button> <Button onClick={resetSecureJsonField('password', options, onOptionsChange)}>Reset</Button>
</>: </> :
<FormField <FormField
labelWidth={7} labelWidth={7}
inputWidth={15} inputWidth={15}
@@ -160,10 +161,15 @@ export const ConfigEditor = (props: Props) => {
<FormField <FormField
labelWidth={7} labelWidth={7}
inputWidth={4} inputWidth={4}
type="number"
label="Timeout" label="Timeout"
value={options.jsonData.timeout || ''} value={options.jsonData.timeout}
placeholder="30" onChange={(event) => {
onChange={jsonDataChangeHandler('timeout', options, onOptionsChange)} onOptionsChange({
...options,
jsonData: { ...options.jsonData, timeout: parseInt(event.currentTarget.value, 10) },
});
}}
tooltip="Zabbix API connection timeout in seconds. Default is 30." tooltip="Zabbix API connection timeout in seconds. Default is 30."
/> />
</div> </div>

View File

@@ -106,7 +106,7 @@ function convertToRegex(str) {
} }
} }
export const DS_CONFIG_SCHEMA = 2; export const DS_CONFIG_SCHEMA = 3;
export function migrateDSConfig(jsonData) { export function migrateDSConfig(jsonData) {
if (!jsonData) { if (!jsonData) {
@@ -127,6 +127,10 @@ export function migrateDSConfig(jsonData) {
delete jsonData.dbConnection; delete jsonData.dbConnection;
} }
if (oldVersion < 3) {
jsonData.timeout = (jsonData.timeout as string) === "" ? null : Number(jsonData.timeout as string);
}
return jsonData; return jsonData;
} }
@@ -134,7 +138,7 @@ function shouldMigrateDSConfig(jsonData): boolean {
if (jsonData.dbConnection && !_.isEmpty(jsonData.dbConnection)) { if (jsonData.dbConnection && !_.isEmpty(jsonData.dbConnection)) {
return true; return true;
} }
if (jsonData.schema && jsonData.schema !== DS_CONFIG_SCHEMA) { if (jsonData.schema && jsonData.schema < DS_CONFIG_SCHEMA) {
return true; return true;
} }
return false; return false;

View File

@@ -7,7 +7,7 @@ export interface ZabbixDSOptions extends DataSourceJsonData {
trendsFrom: string; trendsFrom: string;
trendsRange: string; trendsRange: string;
cacheTTL: string; cacheTTL: string;
timeout?: string; timeout?: number;
dbConnectionEnable: boolean; dbConnectionEnable: boolean;
dbConnectionDatasourceId?: number; dbConnectionDatasourceId?: number;
dbConnectionDatasourceName?: string; dbConnectionDatasourceName?: string;