InfluxDB: Upgrade InfluxDB in devenv (#26983)

* InfluxDB: Upgrade InfluxDB in devenv
* InfluxDB: De-export symbols
* InfluxDB: Remove unused code
* devenv: Make InfluxDB version configurable

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Arve Knudsen 2020-08-19 09:23:11 +02:00 committed by GitHub
parent 689ca1be87
commit 0e1e85656b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 62 additions and 150 deletions

View File

@ -0,0 +1 @@
influxdb_version=1.8.1-alpine

View File

@ -1,5 +1,5 @@
influxdb: influxdb:
image: influxdb:1.7.6 image: influxdb:${influxdb_version}
container_name: influxdb container_name: influxdb
ports: ports:
- '2004:2004' - '2004:2004'

View File

@ -27,8 +27,8 @@ type columnInfo struct {
converter *data.FieldConverter converter *data.FieldConverter
} }
// FrameBuilder This is an interface to help testing // frameBuilder is an interface to help testing.
type FrameBuilder struct { type frameBuilder struct {
tableID int64 tableID int64
active *data.Frame active *data.Frame
frames []*data.Frame frames []*data.Frame
@ -50,23 +50,23 @@ func isTag(schk string) bool {
func getConverter(t string) (*data.FieldConverter, error) { func getConverter(t string) (*data.FieldConverter, error) {
switch t { switch t {
case stringDatatype: case stringDatatype:
return &AnyToOptionalString, nil return &anyToOptionalString, nil
case timeDatatypeRFC: case timeDatatypeRFC:
return &TimeToOptionalTime, nil return &timeToOptionalTime, nil
case timeDatatypeRFCNano: case timeDatatypeRFCNano:
return &TimeToOptionalTime, nil return &timeToOptionalTime, nil
case durationDatatype: case durationDatatype:
return &Int64ToOptionalInt64, nil return &int64ToOptionalInt64, nil
case doubleDatatype: case doubleDatatype:
return &Float64ToOptionalFloat64, nil return &float64ToOptionalFloat64, nil
case boolDatatype: case boolDatatype:
return &BoolToOptionalBool, nil return &boolToOptionalBool, nil
case longDatatype: case longDatatype:
return &Int64ToOptionalInt64, nil return &int64ToOptionalInt64, nil
case uLongDatatype: case uLongDatatype:
return &UInt64ToOptionalUInt64, nil return &uint64ToOptionalUInt64, nil
case base64BinaryDataType: case base64BinaryDataType:
return &AnyToOptionalString, nil return &anyToOptionalString, nil
} }
return nil, fmt.Errorf("no matching converter found for [%v]", t) return nil, fmt.Errorf("no matching converter found for [%v]", t)
@ -75,7 +75,7 @@ func getConverter(t string) (*data.FieldConverter, error) {
// Init initializes the frame to be returned // Init initializes the frame to be returned
// fields points at entries in the frame, and provides easier access // fields points at entries in the frame, and provides easier access
// names indexes the columns encountered // names indexes the columns encountered
func (fb *FrameBuilder) Init(metadata *query.FluxTableMetadata) error { func (fb *frameBuilder) Init(metadata *query.FluxTableMetadata) error {
columns := metadata.Columns() columns := metadata.Columns()
fb.frames = make([]*data.Frame, 0) fb.frames = make([]*data.Frame, 0)
fb.tableID = -1 fb.tableID = -1
@ -153,7 +153,7 @@ func getTimeSeriesTimeColumn(columns []*query.FluxColumn) *query.FluxColumn {
// Tags are appended as labels // Tags are appended as labels
// _measurement holds the dataframe name // _measurement holds the dataframe name
// _field holds the field name. // _field holds the field name.
func (fb *FrameBuilder) Append(record *query.FluxRecord) error { func (fb *frameBuilder) Append(record *query.FluxRecord) error {
table, ok := record.ValueByKey("table").(int64) table, ok := record.ValueByKey("table").(int64)
if ok && table != fb.tableID { if ok && table != fb.tableID {
fb.totalSeries++ fb.totalSeries++

View File

@ -2,34 +2,13 @@ package flux
import ( import (
"fmt" "fmt"
"strconv"
"time" "time"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
) )
// Int64NOOP ..... // anyToOptionalString any value as a string.
var Int64NOOP = data.FieldConverter{ var anyToOptionalString = data.FieldConverter{
OutputFieldType: data.FieldTypeInt64,
}
// BoolNOOP .....
var BoolNOOP = data.FieldConverter{
OutputFieldType: data.FieldTypeBool,
}
// Float64NOOP .....
var Float64NOOP = data.FieldConverter{
OutputFieldType: data.FieldTypeFloat64,
}
// StringNOOP value is already in the proper format
var StringNOOP = data.FieldConverter{
OutputFieldType: data.FieldTypeString,
}
// AnyToOptionalString any value as a string
var AnyToOptionalString = data.FieldConverter{
OutputFieldType: data.FieldTypeNullableString, OutputFieldType: data.FieldTypeNullableString,
Converter: func(v interface{}) (interface{}, error) { Converter: func(v interface{}) (interface{}, error) {
if v == nil { if v == nil {
@ -40,8 +19,8 @@ var AnyToOptionalString = data.FieldConverter{
}, },
} }
// Float64ToOptionalFloat64 optional float value // float64ToOptionalFloat64 optional float value
var Float64ToOptionalFloat64 = data.FieldConverter{ var float64ToOptionalFloat64 = data.FieldConverter{
OutputFieldType: data.FieldTypeNullableFloat64, OutputFieldType: data.FieldTypeNullableFloat64,
Converter: func(v interface{}) (interface{}, error) { Converter: func(v interface{}) (interface{}, error) {
if v == nil { if v == nil {
@ -55,8 +34,8 @@ var Float64ToOptionalFloat64 = data.FieldConverter{
}, },
} }
// Int64ToOptionalInt64 optional int value // int64ToOptionalInt64 optional int value
var Int64ToOptionalInt64 = data.FieldConverter{ var int64ToOptionalInt64 = data.FieldConverter{
OutputFieldType: data.FieldTypeNullableInt64, OutputFieldType: data.FieldTypeNullableInt64,
Converter: func(v interface{}) (interface{}, error) { Converter: func(v interface{}) (interface{}, error) {
if v == nil { if v == nil {
@ -70,8 +49,8 @@ var Int64ToOptionalInt64 = data.FieldConverter{
}, },
} }
// UInt64ToOptionalUInt64 optional int value // uint64ToOptionalUInt64 optional int value
var UInt64ToOptionalUInt64 = data.FieldConverter{ var uint64ToOptionalUInt64 = data.FieldConverter{
OutputFieldType: data.FieldTypeNullableUint64, OutputFieldType: data.FieldTypeNullableUint64,
Converter: func(v interface{}) (interface{}, error) { Converter: func(v interface{}) (interface{}, error) {
if v == nil { if v == nil {
@ -85,8 +64,8 @@ var UInt64ToOptionalUInt64 = data.FieldConverter{
}, },
} }
// BoolToOptionalBool optional int value // boolToOptionalBool optional int value
var BoolToOptionalBool = data.FieldConverter{ var boolToOptionalBool = data.FieldConverter{
OutputFieldType: data.FieldTypeNullableBool, OutputFieldType: data.FieldTypeNullableBool,
Converter: func(v interface{}) (interface{}, error) { Converter: func(v interface{}) (interface{}, error) {
if v == nil { if v == nil {
@ -100,8 +79,8 @@ var BoolToOptionalBool = data.FieldConverter{
}, },
} }
// TimeToOptionalTime optional int value // timeToOptionalTime optional int value
var TimeToOptionalTime = data.FieldConverter{ var timeToOptionalTime = data.FieldConverter{
OutputFieldType: data.FieldTypeNullableTime, OutputFieldType: data.FieldTypeNullableTime,
Converter: func(v interface{}) (interface{}, error) { Converter: func(v interface{}) (interface{}, error) {
if v == nil { if v == nil {
@ -114,70 +93,3 @@ var TimeToOptionalTime = data.FieldConverter{
return &val, nil return &val, nil
}, },
} }
// RFC3339StringToNullableTime .....
func RFC3339StringToNullableTime(s string) (*time.Time, error) {
if s == "" {
return nil, nil
}
rv, err := time.Parse(time.RFC3339, s)
if err != nil {
return nil, err
}
u := rv.UTC()
return &u, nil
}
// StringToOptionalFloat64 string to float
var StringToOptionalFloat64 = data.FieldConverter{
OutputFieldType: data.FieldTypeNullableFloat64,
Converter: func(v interface{}) (interface{}, error) {
if v == nil {
return nil, nil
}
val, ok := v.(string)
if !ok { // or return some default value instead of erroring
return nil, fmt.Errorf("[floatz] expected string input but got type %T", v)
}
fV, err := strconv.ParseFloat(val, 64)
return &fV, err
},
}
// Float64EpochSecondsToTime numeric seconds to time
var Float64EpochSecondsToTime = data.FieldConverter{
OutputFieldType: data.FieldTypeTime,
Converter: func(v interface{}) (interface{}, error) {
fV, ok := v.(float64)
if !ok { // or return some default value instead of erroring
return nil, fmt.Errorf("[seconds] expected float64 input but got type %T", v)
}
return time.Unix(int64(fV), 0).UTC(), nil
},
}
// Float64EpochMillisToTime convert to time
var Float64EpochMillisToTime = data.FieldConverter{
OutputFieldType: data.FieldTypeTime,
Converter: func(v interface{}) (interface{}, error) {
fV, ok := v.(float64)
if !ok { // or return some default value instead of erroring
return nil, fmt.Errorf("[ms] expected float64 input but got type %T", v)
}
return time.Unix(0, int64(fV)*int64(time.Millisecond)).UTC(), nil
},
}
// Boolean ...
var Boolean = data.FieldConverter{
OutputFieldType: data.FieldTypeBool,
Converter: func(v interface{}) (interface{}, error) {
fV, ok := v.(bool)
if !ok { // or return some default value instead of erroring
return nil, fmt.Errorf("[ms] expected bool input but got type %T", v)
}
return fV, nil
},
}

View File

@ -9,12 +9,12 @@ import (
"github.com/influxdata/influxdb-client-go/v2/api" "github.com/influxdata/influxdb-client-go/v2/api"
) )
// executeQuery runs a flux query using the QueryModel to interpolate the query and the runner to execute it. // executeQuery runs a flux query using the queryModel to interpolate the query and the runner to execute it.
// maxSeries somehow limits the response. // maxSeries somehow limits the response.
func executeQuery(ctx context.Context, query QueryModel, runner queryRunner, maxSeries int) (dr backend.DataResponse) { func executeQuery(ctx context.Context, query queryModel, runner queryRunner, maxSeries int) (dr backend.DataResponse) {
dr = backend.DataResponse{} dr = backend.DataResponse{}
flux, err := Interpolate(query) flux, err := interpolate(query)
if err != nil { if err != nil {
dr.Error = err dr.Error = err
return return
@ -50,7 +50,7 @@ func readDataFrames(result *api.QueryTableResult, maxPoints int, maxSeries int)
glog.Debug("Reading data frames from query result", "maxPoints", maxPoints, "maxSeries", maxSeries) glog.Debug("Reading data frames from query result", "maxPoints", maxPoints, "maxSeries", maxSeries)
dr = backend.DataResponse{} dr = backend.DataResponse{}
builder := &FrameBuilder{ builder := &frameBuilder{
maxPoints: maxPoints, maxPoints: maxPoints,
maxSeries: maxSeries, maxSeries: maxSeries,
} }

View File

@ -55,7 +55,7 @@ func verifyGoldenResponse(name string) (*backend.DataResponse, error) {
testDataPath: name + ".csv", testDataPath: name + ".csv",
} }
dr := executeQuery(context.Background(), QueryModel{MaxDataPoints: 100}, runner, 50) dr := executeQuery(context.Background(), queryModel{MaxDataPoints: 100}, runner, 50)
err := experimental.CheckGoldenDataResponse("./testdata/"+name+".golden.txt", &dr, true) err := experimental.CheckGoldenDataResponse("./testdata/"+name+".golden.txt", &dr, true)
return &dr, err return &dr, err
} }

View File

@ -26,29 +26,29 @@ func Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQ
tRes := &tsdb.Response{ tRes := &tsdb.Response{
Results: make(map[string]*tsdb.QueryResult), Results: make(map[string]*tsdb.QueryResult),
} }
runner, err := RunnerFromDataSource(dsInfo) r, err := runnerFromDataSource(dsInfo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer runner.client.Close() defer r.client.Close()
for _, query := range tsdbQuery.Queries { for _, query := range tsdbQuery.Queries {
qm, err := GetQueryModelTSDB(query, tsdbQuery.TimeRange, dsInfo) qm, err := getQueryModelTSDB(query, tsdbQuery.TimeRange, dsInfo)
if err != nil { if err != nil {
tRes.Results[query.RefId] = &tsdb.QueryResult{Error: err} tRes.Results[query.RefId] = &tsdb.QueryResult{Error: err}
continue continue
} }
res := executeQuery(context.Background(), *qm, runner, 50) res := executeQuery(context.Background(), *qm, r, 50)
tRes.Results[query.RefId] = backendDataResponseToTSDBResponse(&res, query.RefId) tRes.Results[query.RefId] = backendDataResponseToTSDBResponse(&res, query.RefId)
} }
return tRes, nil return tRes, nil
} }
// Runner is an influxdb2 Client with an attached org property and is used // runner is an influxdb2 Client with an attached org property and is used
// for running flux queries. // for running flux queries.
type Runner struct { type runner struct {
client influxdb2.Client client influxdb2.Client
org string org string
} }
@ -59,13 +59,13 @@ type queryRunner interface {
} }
// runQuery executes fluxQuery against the Runner's organization and returns a Flux typed result. // runQuery executes fluxQuery against the Runner's organization and returns a Flux typed result.
func (r *Runner) runQuery(ctx context.Context, fluxQuery string) (*api.QueryTableResult, error) { func (r *runner) runQuery(ctx context.Context, fluxQuery string) (*api.QueryTableResult, error) {
qa := r.client.QueryAPI(r.org) qa := r.client.QueryAPI(r.org)
return qa.Query(ctx, fluxQuery) return qa.Query(ctx, fluxQuery)
} }
// RunnerFromDataSource creates a runner from the datasource model (the datasource instance's configuration). // runnerFromDataSource creates a runner from the datasource model (the datasource instance's configuration).
func RunnerFromDataSource(dsInfo *models.DataSource) (*Runner, error) { func runnerFromDataSource(dsInfo *models.DataSource) (*runner, error) {
org := dsInfo.JsonData.Get("organization").MustString("") org := dsInfo.JsonData.Get("organization").MustString("")
if org == "" { if org == "" {
return nil, fmt.Errorf("missing organization in datasource configuration") return nil, fmt.Errorf("missing organization in datasource configuration")
@ -86,7 +86,7 @@ func RunnerFromDataSource(dsInfo *models.DataSource) (*Runner, error) {
return nil, err return nil, err
} }
opts.HTTPOptions().SetHTTPClient(hc) opts.HTTPOptions().SetHTTPClient(hc)
return &Runner{ return &runner{
client: influxdb2.NewClientWithOptions(url, token, opts), client: influxdb2.NewClientWithOptions(url, token, opts),
org: org, org: org,
}, nil }, nil

View File

@ -11,8 +11,8 @@ import (
const variableFilter = `(?m)([a-zA-Z]+)\.([a-zA-Z]+)` const variableFilter = `(?m)([a-zA-Z]+)\.([a-zA-Z]+)`
// Interpolate processes macros // interpolate processes macros
func Interpolate(query QueryModel) (string, error) { func interpolate(query queryModel) (string, error) {
flux := query.RawQuery flux := query.RawQuery
variableFilterExp, err := regexp.Compile(variableFilter) variableFilterExp, err := regexp.Compile(variableFilter)

View File

@ -6,6 +6,8 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestInterpolate(t *testing.T) { func TestInterpolate(t *testing.T) {
@ -17,7 +19,7 @@ func TestInterpolate(t *testing.T) {
To: time.Unix(0, 0), To: time.Unix(0, 0),
} }
options := QueryOptions{ options := queryOptions{
Organization: "grafana1", Organization: "grafana1",
Bucket: "grafana2", Bucket: "grafana2",
DefaultBucket: "grafana3", DefaultBucket: "grafana3",
@ -36,20 +38,17 @@ func TestInterpolate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
query := QueryModel{ query := queryModel{
RawQuery: tt.before, RawQuery: tt.before,
Options: options, Options: options,
TimeRange: timeRange, TimeRange: timeRange,
MaxDataPoints: 1, MaxDataPoints: 1,
Interval: 1000 * 1000 * 1000, Interval: 1000 * 1000 * 1000,
} }
interpolatedQuery, err := Interpolate(query) interpolatedQuery, err := interpolate(query)
if err != nil { require.NoError(t, err)
t.Fatal(err) diff := cmp.Diff(tt.after, interpolatedQuery)
} assert.Equal(t, "", diff)
if diff := cmp.Diff(tt.after, interpolatedQuery); diff != "" {
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
}
}) })
} }
} }

View File

@ -10,17 +10,17 @@ import (
"github.com/grafana/grafana/pkg/tsdb" "github.com/grafana/grafana/pkg/tsdb"
) )
// QueryOptions represents datasource configuration options // queryOptions represents datasource configuration options
type QueryOptions struct { type queryOptions struct {
Bucket string `json:"bucket"` Bucket string `json:"bucket"`
DefaultBucket string `json:"defaultBucket"` DefaultBucket string `json:"defaultBucket"`
Organization string `json:"organization"` Organization string `json:"organization"`
} }
// QueryModel represents a spreadsheet query. // queryModel represents a query.
type QueryModel struct { type queryModel struct {
RawQuery string `json:"query"` RawQuery string `json:"query"`
Options QueryOptions `json:"options"` Options queryOptions `json:"options"`
// Not from JSON // Not from JSON
TimeRange backend.TimeRange `json:"-"` TimeRange backend.TimeRange `json:"-"`
@ -31,8 +31,8 @@ type QueryModel struct {
// The following is commented out but kept as it should be useful when // The following is commented out but kept as it should be useful when
// restoring this code to be closer to the SDK's models. // restoring this code to be closer to the SDK's models.
// func GetQueryModel(query backend.DataQuery) (*QueryModel, error) { // func GetQueryModel(query backend.DataQuery) (*queryModel, error) {
// model := &QueryModel{} // model := &queryModel{}
// err := json.Unmarshal(query.JSON, &model) // err := json.Unmarshal(query.JSON, &model)
// if err != nil { // if err != nil {
@ -46,9 +46,9 @@ type QueryModel struct {
// return model, nil // return model, nil
// } // }
// GetQueryModelTSDB builds a QueryModel from tsdb.Query information and datasource configuration (dsInfo). // getQueryModelTSDB builds a queryModel from tsdb.Query information and datasource configuration (dsInfo).
func GetQueryModelTSDB(query *tsdb.Query, timeRange *tsdb.TimeRange, dsInfo *models.DataSource) (*QueryModel, error) { func getQueryModelTSDB(query *tsdb.Query, timeRange *tsdb.TimeRange, dsInfo *models.DataSource) (*queryModel, error) {
model := &QueryModel{} model := &queryModel{}
queryBytes, err := query.Model.Encode() queryBytes, err := query.Model.Encode()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to re-encode the flux query into JSON: %w", err) return nil, fmt.Errorf("failed to re-encode the flux query into JSON: %w", err)