diff --git a/go.mod b/go.mod index 6a272c66f..0796e20b6 100644 --- a/go.mod +++ b/go.mod @@ -63,6 +63,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/philippseith/signalr v0.5.3-0.20211205201131-d57b5a34379a github.com/prometheus/client_golang v1.12.2 + github.com/prometheus/common v0.34.0 github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f github.com/samber/lo v1.21.0 github.com/shurcooL/graphql v0.0.0-20220520033453-bdb1221e171e @@ -118,6 +119,7 @@ require ( github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect github.com/itchyny/timefmt-go v0.1.3 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.5 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/magiconair/properties v1.8.6 // indirect @@ -129,6 +131,8 @@ require ( github.com/miekg/dns v1.1.49 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.19.0 // indirect github.com/pascaldekloe/name v1.0.1 // indirect @@ -137,7 +141,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.34.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect diff --git a/go.sum b/go.sum index 690e67b82..88944cba1 100644 --- a/go.sum +++ b/go.sum @@ -532,6 +532,7 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpfielding/go-http-digest v0.0.0-20211006141426-fbc93758452e h1:iLmt7czLE9ophoU4xZcckVNCXp+auTvVSQ9MGTWbgBY= github.com/jpfielding/go-http-digest v0.0.0-20211006141426-fbc93758452e/go.mod h1:oLt6zF2euTyCg2Cxz01B3VC5SJcPpKpYnOAqvO+480s= github.com/jpfielding/gowirelog v0.0.0-20200123170752-df8f8dccb721/go.mod h1:R8b6Hefiy6MSxbJ8dbF9Gkaa/LwFh9meFV0i9hkR5i8= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -679,6 +680,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a h1:fnzS9RRQW8B5AgNCxkN0vJ/AoX+Xfqk3sAYon3iVrzA= github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxschmitt/golang-combinations v1.1.0 h1:WlIZCnDm+Xlb2pRPf+R/qPKlGOU1w8lpN69/uy5z+Zg= github.com/mxschmitt/golang-combinations v1.1.0/go.mod h1:RbMhWvfCelHR6WROvT2bVfxJvZHoEvBj71SKe+H0MYU= diff --git a/provider/prometheus.go b/provider/prometheus.go new file mode 100644 index 000000000..b524aa18a --- /dev/null +++ b/provider/prometheus.go @@ -0,0 +1,105 @@ +package provider + +import ( + "context" + "fmt" + "github.com/evcc-io/evcc/util/request" + "math" + "time" + + v1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/model" + + "github.com/evcc-io/evcc/util" + "github.com/prometheus/client_golang/api" +) + +// Prometheus provider +type Prometheus struct { + log *util.Logger + clientV1Api v1.API + query string + timeout time.Duration +} + +func init() { + registry.Add("prometheus", NewPrometheusFromConfig) +} + +func NewPrometheusFromConfig(other map[string]interface{}) (IntProvider, error) { + cc := struct { + api.Config `mapstructure:",squash"` + Query string + Timeout time.Duration + }{ + Timeout: request.Timeout, + } + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + log := util.NewLogger("prometheus") + + client, err := api.NewClient(cc.Config) + if err != nil { + return nil, err + } + + p := NewPrometheus(log, client, cc.Query, cc.Timeout) + + return p, err +} + +func NewPrometheus(log *util.Logger, client api.Client, query string, timeout time.Duration) *Prometheus { + p := &Prometheus{ + log: log, + clientV1Api: v1.NewAPI(client), + query: query, + timeout: timeout, + } + + return p +} + +func (p *Prometheus) Query() (model.Value, error) { + ctx, cancel := context.WithTimeout(context.Background(), p.timeout) + defer cancel() + res, warn, err := p.clientV1Api.Query(ctx, p.query, time.Now()) + if err != nil { + return nil, err + } + p.log.TRACE.Printf("query %q: %+v", p.query, res) + if len(warn) > 0 { + p.log.WARN.Printf("query %q returned warnings: %v", p.query, warn) + } + return res, nil +} + +var _ FloatProvider = (*Prometheus)(nil) + +// FloatGetter expects scalar value from query response as float +func (p *Prometheus) FloatGetter() func() (float64, error) { + return func() (float64, error) { + res, err := p.Query() + if err != nil { + return 0, err + } + if res.Type() != model.ValScalar { + return 0, fmt.Errorf("query returned value of type %q, expected %q, consider wrapping query in scalar()", res.Type().String(), model.ValScalar.String()) + } + scalarVal := res.(*model.Scalar) + return float64(scalarVal.Value), nil + } +} + +var _ IntProvider = (*Prometheus)(nil) + +// IntGetter expects scalar value from query response as int +func (p *Prometheus) IntGetter() func() (int64, error) { + floatGetter := p.FloatGetter() + return func() (int64, error) { + float, err := floatGetter() + return int64(math.Round(float)), err + } +}