From 8489ff4c1f43913481fb8426252f682c6b5edaff Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Fri, 26 Jun 2020 13:25:41 +0200 Subject: [PATCH 1/4] add parameterizable duration to retrieve data from prometheus Signed-off-by: Augustin Husson --- cmd/promql-langserver/promql-langserver.go | 2 +- langserver/completion.go | 4 ++-- langserver/config.go | 18 ++++++++++++++++++ langserver/config_test.go | 3 +++ langserver/server.go | 12 ++++++++++-- rest/handler.go | 18 ++++++++++++------ 6 files changed, 46 insertions(+), 11 deletions(-) diff --git a/cmd/promql-langserver/promql-langserver.go b/cmd/promql-langserver/promql-langserver.go index 11781c11..7f778f14 100644 --- a/cmd/promql-langserver/promql-langserver.go +++ b/cmd/promql-langserver/promql-langserver.go @@ -58,7 +58,7 @@ func main() { logger = kitlog.NewSyncLogger(logger) - handler, err := rest.CreateInstHandler(context.Background(), prometheusClient, logger) + handler, err := rest.CreateInstHandler(context.Background(), prometheusClient, logger, config.Interval) if err != nil { log.Fatal(err) } diff --git a/langserver/completion.go b/langserver/completion.go index f300215c..373ce65c 100644 --- a/langserver/completion.go +++ b/langserver/completion.go @@ -312,7 +312,7 @@ func (s *server) completeLabel(ctx context.Context, completions *[]protocol.Comp if vs != nil { metricName = vs.Name } - allNames, err := s.metadataService.LabelNames(ctx, metricName, time.Now().Add(-100*time.Hour), time.Now()) + allNames, err := s.metadataService.LabelNames(ctx, metricName, time.Now().Add(time.Duration(-1*s.config.Interval)), time.Now()) if err != nil { // nolint: errcheck s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{ @@ -355,7 +355,7 @@ OUTER: // nolint: funlen func (s *server) completeLabelValue(ctx context.Context, completions *[]protocol.CompletionItem, location *cache.Location, labelName string) error { - labelValues, err := s.metadataService.LabelValues(ctx, labelName, time.Now().Add(-100*time.Hour), time.Now()) + labelValues, err := s.metadataService.LabelValues(ctx, labelName, time.Now().Add(time.Duration(-1*s.config.Interval)), time.Now()) if err != nil { // nolint: errcheck s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{ diff --git a/langserver/config.go b/langserver/config.go index 55edb8f1..534b6348 100644 --- a/langserver/config.go +++ b/langserver/config.go @@ -20,18 +20,24 @@ import ( "net/url" "os" "strconv" + "time" "github.com/kelseyhightower/envconfig" "github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol" + "github.com/prometheus/common/model" "gopkg.in/yaml.v3" ) +const defaultInterval = model.Duration(12 * 3600 * time.Second) + // Config contains the configuration for a server. type Config struct { RPCTrace string `yaml:"rpc_trace"` LogFormat LogFormat `yaml:"log_format"` PrometheusURL string `yaml:"prometheus_url"` RESTAPIPort uint64 `yaml:"rest_api_port"` + // Interval is the time in second used to retrieve label and metrics from Prometheus + Interval model.Duration `yaml:"interval"` } // LogFormat is the type used for describing the format of logs. @@ -49,6 +55,7 @@ var mapLogFormat = map[LogFormat]bool{ // nolint: gochecknoglobals TextFormat: true, } +// Parameterizable // UnmarshalYAML overrides a function used internally by the yaml.v3 lib. func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { tmp := &Config{} @@ -72,6 +79,7 @@ func (c *Config) unmarshalENV() error { // the envconfig lib is not able to convert an empty string to the value 0 // so we have to convert it manually RESTAPIPort string + Interval string }{} if err := envconfig.Process(prefix, conf); err != nil { return err @@ -83,6 +91,13 @@ func (c *Config) unmarshalENV() error { return parseError } } + if len(conf.Interval) > 0 { + var parseError error + c.Interval, parseError = model.ParseDuration(conf.Interval) + if parseError != nil { + return parseError + } + } c.RPCTrace = conf.RPCTrace c.PrometheusURL = conf.PrometheusURL c.LogFormat = LogFormat(conf.LogFormat) @@ -105,6 +120,9 @@ func (c *Config) Validate() error { // default value c.LogFormat = TextFormat } + if c.Interval <= 0 { + c.Interval = defaultInterval + } return nil } diff --git a/langserver/config_test.go b/langserver/config_test.go index fefa6721..26d8fbef 100644 --- a/langserver/config_test.go +++ b/langserver/config_test.go @@ -31,6 +31,7 @@ func TestUnmarshalENV(t *testing.T) { variables: map[string]string{}, expected: &Config{ LogFormat: TextFormat, + Interval: defaultInterval, }, }, { @@ -40,12 +41,14 @@ func TestUnmarshalENV(t *testing.T) { "LANGSERVER_PROMETHEUSURL": "http://localhost:9090", "LANGSERVER_RESTAPIPORT": "8080", "LANGSERVER_LOGFORMAT": "json", + "LANGSERVER_INTERVAL": "1w", }, expected: &Config{ RPCTrace: "text", PrometheusURL: "http://localhost:9090", RESTAPIPort: 8080, LogFormat: JSONFormat, + Interval: 604800000000000, }, }, } diff --git a/langserver/server.go b/langserver/server.go index 273a1871..ceff18fc 100644 --- a/langserver/server.go +++ b/langserver/server.go @@ -29,6 +29,7 @@ import ( "sync" promClient "github.com/prometheus-community/promql-langserver/prometheus" + "github.com/prometheus/common/model" "github.com/go-kit/kit/log" "github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/jsonrpc2" @@ -83,11 +84,18 @@ func (s Server) Run() error { // CreateHeadlessServer creates a locked down server instance for the REST API. // // "locked down" in this case means, that the instance cannot send or receive any JSONRPC communication. Logging messages that the instance tries to send over JSONRPC are redirected to stderr. -func CreateHeadlessServer(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger) (HeadlessServer, error) { +func CreateHeadlessServer(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, interval model.Duration) (HeadlessServer, error) { + conf := &Config{ + PrometheusURL: metadataService.GetURL(), + Interval: interval, + } + if interval <= 0 { + conf.Interval = defaultInterval + } s := &server{ client: &headlessClient{logger: logger}, headless: true, - config: &Config{PrometheusURL: metadataService.GetURL()}, + config: conf, metadataService: metadataService, } diff --git a/rest/handler.go b/rest/handler.go index 8458fc9c..0a9325cb 100644 --- a/rest/handler.go +++ b/rest/handler.go @@ -26,6 +26,7 @@ import ( "github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol" "github.com/prometheus-community/promql-langserver/langserver" promClient "github.com/prometheus-community/promql-langserver/prometheus" + "github.com/prometheus/common/model" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -38,8 +39,10 @@ import ( // otherwise you need to provide your own implementation of the interface. // // The provided Logger should be synchronized. -func CreateHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger) (http.Handler, error) { - return createHandler(ctx, metadataService, logger, false) +// +// interval is the period of time (in second) used to retrieve data such as label and metrics from metrics. +func CreateHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, interval model.Duration) (http.Handler, error) { + return createHandler(ctx, metadataService, logger, false, interval) } // CreateInstHandler creates an instrumented http.Handler for the PromQL langserver REST API. @@ -53,11 +56,14 @@ func CreateHandler(ctx context.Context, metadataService promClient.MetadataServi // otherwise you need to provide your own implementation of the interface. // // The provided Logger should be synchronized. -func CreateInstHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger) (http.Handler, error) { - return createHandler(ctx, metadataService, logger, true) +// +// interval is the period of time (in second) used to retrieve data such as label and metrics from metrics. +func CreateInstHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, interval model.Duration) (http.Handler, error) { + return createHandler(ctx, metadataService, logger, true, interval) } -func createHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, metricsEnpoint bool) (http.Handler, error) { - lgs, err := langserver.CreateHeadlessServer(ctx, metadataService, logger) + +func createHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, metricsEnpoint bool, interval model.Duration) (http.Handler, error) { + lgs, err := langserver.CreateHeadlessServer(ctx, metadataService, logger, interval) if err != nil { return nil, err } From 4992aa78ce56ebf18cc564cac47d827d7c373ceb Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Fri, 26 Jun 2020 23:39:45 +0200 Subject: [PATCH 2/4] change dinamically the interval duration Signed-off-by: Augustin Husson --- cmd/promql-langserver/promql-langserver.go | 2 +- langserver/completion.go | 4 +- langserver/config.go | 127 ++++++++++++++------- langserver/config_test.go | 24 ++-- langserver/server.go | 18 +-- 5 files changed, 106 insertions(+), 69 deletions(-) diff --git a/cmd/promql-langserver/promql-langserver.go b/cmd/promql-langserver/promql-langserver.go index 7f778f14..9493b441 100644 --- a/cmd/promql-langserver/promql-langserver.go +++ b/cmd/promql-langserver/promql-langserver.go @@ -58,7 +58,7 @@ func main() { logger = kitlog.NewSyncLogger(logger) - handler, err := rest.CreateInstHandler(context.Background(), prometheusClient, logger, config.Interval) + handler, err := rest.CreateInstHandler(context.Background(), prometheusClient, logger, config.MetadataLookbackInterval) if err != nil { log.Fatal(err) } diff --git a/langserver/completion.go b/langserver/completion.go index 373ce65c..1c562cb3 100644 --- a/langserver/completion.go +++ b/langserver/completion.go @@ -312,7 +312,7 @@ func (s *server) completeLabel(ctx context.Context, completions *[]protocol.Comp if vs != nil { metricName = vs.Name } - allNames, err := s.metadataService.LabelNames(ctx, metricName, time.Now().Add(time.Duration(-1*s.config.Interval)), time.Now()) + allNames, err := s.metadataService.LabelNames(ctx, metricName, time.Now().Add(time.Duration(-1*s.config.MetadataLookbackInterval)), time.Now()) if err != nil { // nolint: errcheck s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{ @@ -355,7 +355,7 @@ OUTER: // nolint: funlen func (s *server) completeLabelValue(ctx context.Context, completions *[]protocol.CompletionItem, location *cache.Location, labelName string) error { - labelValues, err := s.metadataService.LabelValues(ctx, labelName, time.Now().Add(time.Duration(-1*s.config.Interval)), time.Now()) + labelValues, err := s.metadataService.LabelValues(ctx, labelName, time.Now().Add(time.Duration(-1*s.config.MetadataLookbackInterval)), time.Now()) if err != nil { // nolint: errcheck s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{ diff --git a/langserver/config.go b/langserver/config.go index 534b6348..f81ee31c 100644 --- a/langserver/config.go +++ b/langserver/config.go @@ -36,8 +36,8 @@ type Config struct { LogFormat LogFormat `yaml:"log_format"` PrometheusURL string `yaml:"prometheus_url"` RESTAPIPort uint64 `yaml:"rest_api_port"` - // Interval is the time in second used to retrieve label and metrics from Prometheus - Interval model.Duration `yaml:"interval"` + // MetadataLookbackInterval is the time in second used to retrieve label and metrics from Prometheus + MetadataLookbackInterval model.Duration `yaml:"metadataLookbackInterval"` } // LogFormat is the type used for describing the format of logs. @@ -55,7 +55,6 @@ var mapLogFormat = map[LogFormat]bool{ // nolint: gochecknoglobals TextFormat: true, } -// Parameterizable // UnmarshalYAML overrides a function used internally by the yaml.v3 lib. func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { tmp := &Config{} @@ -78,8 +77,8 @@ func (c *Config) unmarshalENV() error { PrometheusURL string // the envconfig lib is not able to convert an empty string to the value 0 // so we have to convert it manually - RESTAPIPort string - Interval string + RESTAPIPort string + MetadataLookbackInterval string }{} if err := envconfig.Process(prefix, conf); err != nil { return err @@ -91,9 +90,9 @@ func (c *Config) unmarshalENV() error { return parseError } } - if len(conf.Interval) > 0 { + if len(conf.MetadataLookbackInterval) > 0 { var parseError error - c.Interval, parseError = model.ParseDuration(conf.Interval) + c.MetadataLookbackInterval, parseError = model.ParseDuration(conf.MetadataLookbackInterval) if parseError != nil { return parseError } @@ -120,8 +119,8 @@ func (c *Config) Validate() error { // default value c.LogFormat = TextFormat } - if c.Interval <= 0 { - c.Interval = defaultInterval + if c.MetadataLookbackInterval <= 0 { + c.MetadataLookbackInterval = defaultInterval } return nil @@ -155,41 +154,91 @@ func readConfigFromENV() (*Config, error) { // DidChangeConfiguration is required by the protocol.Server interface. func (s *server) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error { - langserverAddressConfigPath := []string{"promql", "url"} + if params == nil { + return nil + } + // nolint: errcheck + s.client.LogMessage( + s.lifetime, + &protocol.LogMessageParams{ + Type: protocol.Info, + Message: fmt.Sprintf("Received notification change: %v\n", params), + }) + + setting := params.Settings + + // the struct expected is the following + // promql: + // url: http:// + // interval: 3w + m, ok := setting.(map[string]map[string]string) + if !ok { + // nolint: errcheck + s.client.LogMessage(ctx, &protocol.LogMessageParams{ + Type: protocol.Error, + Message: fmt.Sprint("unexpected format of the configuration"), + }) + return nil + } + config, ok := m["promql"] + if !ok { + // nolint: errcheck + s.client.LogMessage(ctx, &protocol.LogMessageParams{ + Type: protocol.Error, + Message: fmt.Sprint("promQL key not found"), + }) + return nil + } - if params != nil { + if err := s.setURLFromChangeConfiguration(config); err != nil { // nolint: errcheck - s.client.LogMessage( - s.lifetime, - &protocol.LogMessageParams{ - Type: protocol.Info, - Message: fmt.Sprintf("Received notification change: %v\n", params), - }) - - setting := params.Settings - - for _, e := range langserverAddressConfigPath { - m, ok := setting.(map[string]interface{}) - if !ok { - break - } - - setting, ok = m[e] - if !ok { - break - } - } + s.client.LogMessage(ctx, &protocol.LogMessageParams{ + Type: protocol.Info, + Message: err.Error(), + }) + } - if str, ok := setting.(string); ok { - if err := s.connectPrometheus(str); err != nil { - // nolint: errcheck - s.client.LogMessage(ctx, &protocol.LogMessageParams{ - Type: protocol.Info, - Message: err.Error(), - }) - } + if err := s.setMetadataLookbackIntervalFromChangeConfiguration(config); err != nil { + // nolint: errcheck + s.client.LogMessage(ctx, &protocol.LogMessageParams{ + Type: protocol.Info, + Message: err.Error(), + }) + } + return nil +} + +func (s *server) setURLFromChangeConfiguration(settings map[string]string) error { + if promURL, ok := settings["url"]; ok { + if _, err := url.Parse(promURL); err != nil { + return err + } + if err := s.connectPrometheus(promURL); err != nil { + return err } } + return nil +} +func (s *server) connectPrometheus(url string) error { + if err := s.metadataService.ChangeDataSource(url); err != nil { + // nolint: errcheck + s.client.ShowMessage(s.lifetime, &protocol.ShowMessageParams{ + Type: protocol.Error, + Message: fmt.Sprintf("Failed to connect to Prometheus at %s:\n\n%s ", url, err.Error()), + }) + return err + } + return nil +} + +func (s *server) setMetadataLookbackIntervalFromChangeConfiguration(settings map[string]string) error { + if interval, ok := settings["metadataLookbackInterval"]; ok { + duration, err := model.ParseDuration(interval) + if err != nil { + return err + } + s.config.MetadataLookbackInterval = duration + } return nil } diff --git a/langserver/config_test.go b/langserver/config_test.go index 26d8fbef..82381e28 100644 --- a/langserver/config_test.go +++ b/langserver/config_test.go @@ -30,25 +30,25 @@ func TestUnmarshalENV(t *testing.T) { title: "empty config", variables: map[string]string{}, expected: &Config{ - LogFormat: TextFormat, - Interval: defaultInterval, + LogFormat: TextFormat, + MetadataLookbackInterval: defaultInterval, }, }, { title: "full config", variables: map[string]string{ - "LANGSERVER_RPCTRACE": "text", - "LANGSERVER_PROMETHEUSURL": "http://localhost:9090", - "LANGSERVER_RESTAPIPORT": "8080", - "LANGSERVER_LOGFORMAT": "json", - "LANGSERVER_INTERVAL": "1w", + "LANGSERVER_RPCTRACE": "text", + "LANGSERVER_PROMETHEUSURL": "http://localhost:9090", + "LANGSERVER_RESTAPIPORT": "8080", + "LANGSERVER_LOGFORMAT": "json", + "LANGSERVER_METADATALOOKBACKINTERVAL": "1w", }, expected: &Config{ - RPCTrace: "text", - PrometheusURL: "http://localhost:9090", - RESTAPIPort: 8080, - LogFormat: JSONFormat, - Interval: 604800000000000, + RPCTrace: "text", + PrometheusURL: "http://localhost:9090", + RESTAPIPort: 8080, + LogFormat: JSONFormat, + MetadataLookbackInterval: 604800000000000, }, }, } diff --git a/langserver/server.go b/langserver/server.go index ceff18fc..16237f2c 100644 --- a/langserver/server.go +++ b/langserver/server.go @@ -86,11 +86,11 @@ func (s Server) Run() error { // "locked down" in this case means, that the instance cannot send or receive any JSONRPC communication. Logging messages that the instance tries to send over JSONRPC are redirected to stderr. func CreateHeadlessServer(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, interval model.Duration) (HeadlessServer, error) { conf := &Config{ - PrometheusURL: metadataService.GetURL(), - Interval: interval, + PrometheusURL: metadataService.GetURL(), + MetadataLookbackInterval: interval, } if interval <= 0 { - conf.Interval = defaultInterval + conf.MetadataLookbackInterval = defaultInterval } s := &server{ client: &headlessClient{logger: logger}, @@ -146,18 +146,6 @@ func ServerFromStream(ctx context.Context, stream jsonrpc2.Stream, config *Confi return ctx, Server{s} } -func (s *server) connectPrometheus(url string) error { - if err := s.metadataService.ChangeDataSource(url); err != nil { - // nolint: errcheck - s.client.ShowMessage(s.lifetime, &protocol.ShowMessageParams{ - Type: protocol.Error, - Message: fmt.Sprintf("Failed to connect to Prometheus at %s:\n\n%s ", url, err.Error()), - }) - return err - } - return nil -} - // RunTCPServer generates a server listening on the provided TCP Address, creating a new language Server // instance using plain HTTP for every connection. func RunTCPServer(ctx context.Context, addr string, config *Config) error { From 6518f23001ceb79bc696826e8dc7a84e5ac52d5a Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Wed, 1 Jul 2020 17:04:52 +0200 Subject: [PATCH 3/4] move the management of the lookback interval to the metadata_service Signed-off-by: Augustin Husson --- cmd/promql-langserver/promql-langserver.go | 5 +-- config/config.go | 20 ++++++++++- config/config_test.go | 22 ++++++------ langserver/completion.go | 5 ++- langserver/config.go | 12 ++++--- langserver/server.go | 3 +- prometheus/compatible.go | 17 ++++++---- prometheus/empty.go | 8 +++-- prometheus/metadata_service.go | 39 ++++++++++++++-------- prometheus/not_compatible.go | 17 ++++++---- rest/handler.go | 16 ++++----- 11 files changed, 103 insertions(+), 61 deletions(-) diff --git a/cmd/promql-langserver/promql-langserver.go b/cmd/promql-langserver/promql-langserver.go index 9a9f50bd..8c4eb155 100644 --- a/cmd/promql-langserver/promql-langserver.go +++ b/cmd/promql-langserver/promql-langserver.go @@ -20,6 +20,7 @@ import ( "log" "net/http" "os" + "time" kitlog "github.com/go-kit/kit/log" "github.com/prometheus-community/promql-langserver/config" @@ -41,7 +42,7 @@ func main() { } if conf.RESTAPIPort != 0 { fmt.Fprintln(os.Stderr, "REST API: Listening on port ", conf.RESTAPIPort) - prometheusClient, err := promClient.NewClient(conf.PrometheusURL) + prometheusClient, err := promClient.NewClient(conf.PrometheusURL, time.Duration(conf.MetadataLookbackInterval)) if err != nil { log.Fatal(err) } @@ -59,7 +60,7 @@ func main() { logger = kitlog.NewSyncLogger(logger) - handler, err := rest.CreateInstHandler(context.Background(), prometheusClient, logger, config.MetadataLookbackInterval) + handler, err := rest.CreateInstHandler(context.Background(), prometheusClient, logger) if err != nil { log.Fatal(err) } diff --git a/config/config.go b/config/config.go index 8433a6c3..89490671 100644 --- a/config/config.go +++ b/config/config.go @@ -18,11 +18,15 @@ import ( "net/url" "os" "strconv" + "time" "github.com/kelseyhightower/envconfig" + "github.com/prometheus/common/model" "gopkg.in/yaml.v3" ) +const defaultInterval = model.Duration(12 * 3600 * time.Second) + // ReadConfig gets the GlobalConfig from a configFile (that is a path to the file). func ReadConfig(configFile string) (*Config, error) { if len(configFile) == 0 { @@ -70,6 +74,8 @@ type Config struct { LogFormat LogFormat `yaml:"log_format"` PrometheusURL string `yaml:"prometheus_url"` RESTAPIPort uint64 `yaml:"rest_api_port"` + // MetadataLookbackInterval is the time in second used to retrieve label and metrics from Prometheus + MetadataLookbackInterval model.Duration `yaml:"metadata_lookback_interval"` } // UnmarshalYAML overrides a function used internally by the yaml.v3 lib. @@ -94,7 +100,8 @@ func (c *Config) unmarshalENV() error { PrometheusURL string // the envconfig lib is not able to convert an empty string to the value 0 // so we have to convert it manually - RESTAPIPort string + RESTAPIPort string + MetadataLookbackInterval string }{} if err := envconfig.Process(prefix, conf); err != nil { return err @@ -106,6 +113,13 @@ func (c *Config) unmarshalENV() error { return parseError } } + if len(conf.MetadataLookbackInterval) > 0 { + var parseError error + c.MetadataLookbackInterval, parseError = model.ParseDuration(conf.MetadataLookbackInterval) + if parseError != nil { + return parseError + } + } c.ActivateRPCLog = conf.ActivateRPCLog c.PrometheusURL = conf.PrometheusURL c.LogFormat = LogFormat(conf.LogFormat) @@ -129,5 +143,9 @@ func (c *Config) Validate() error { c.LogFormat = TextFormat } + if c.MetadataLookbackInterval <= 0 { + c.MetadataLookbackInterval = defaultInterval + } + return nil } diff --git a/config/config_test.go b/config/config_test.go index 229eb745..79d541ab 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -30,24 +30,26 @@ func TestUnmarshalENV(t *testing.T) { title: "empty config", variables: map[string]string{}, expected: &Config{ - ActivateRPCLog: false, - LogFormat: TextFormat, + ActivateRPCLog: false, + LogFormat: TextFormat, + MetadataLookbackInterval: defaultInterval, }, }, { title: "full config", variables: map[string]string{ - "LANGSERVER_ACTIVATERPCLOG": "true", - "LANGSERVER_PROMETHEUSURL": "http://localhost:9090", - "LANGSERVER_RESTAPIPORT": "8080", - "LANGSERVER_LOGFORMAT": "json", + "LANGSERVER_ACTIVATERPCLOG": "true", + "LANGSERVER_PROMETHEUSURL": "http://localhost:9090", + "LANGSERVER_RESTAPIPORT": "8080", + "LANGSERVER_LOGFORMAT": "json", "LANGSERVER_METADATALOOKBACKINTERVAL": "1w", }, expected: &Config{ - ActivateRPCLog: true, - PrometheusURL: "http://localhost:9090", - RESTAPIPort: 8080, - LogFormat: JSONFormat, + ActivateRPCLog: true, + PrometheusURL: "http://localhost:9090", + RESTAPIPort: 8080, + LogFormat: JSONFormat, + MetadataLookbackInterval: 604800000000000, }, }, } diff --git a/langserver/completion.go b/langserver/completion.go index 1c562cb3..63472818 100644 --- a/langserver/completion.go +++ b/langserver/completion.go @@ -20,7 +20,6 @@ import ( "sort" "strconv" "strings" - "time" "github.com/pkg/errors" @@ -312,7 +311,7 @@ func (s *server) completeLabel(ctx context.Context, completions *[]protocol.Comp if vs != nil { metricName = vs.Name } - allNames, err := s.metadataService.LabelNames(ctx, metricName, time.Now().Add(time.Duration(-1*s.config.MetadataLookbackInterval)), time.Now()) + allNames, err := s.metadataService.LabelNames(ctx, metricName) if err != nil { // nolint: errcheck s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{ @@ -355,7 +354,7 @@ OUTER: // nolint: funlen func (s *server) completeLabelValue(ctx context.Context, completions *[]protocol.CompletionItem, location *cache.Location, labelName string) error { - labelValues, err := s.metadataService.LabelValues(ctx, labelName, time.Now().Add(time.Duration(-1*s.config.MetadataLookbackInterval)), time.Now()) + labelValues, err := s.metadataService.LabelValues(ctx, labelName) if err != nil { // nolint: errcheck s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{ diff --git a/langserver/config.go b/langserver/config.go index 16d75956..c4d3392a 100644 --- a/langserver/config.go +++ b/langserver/config.go @@ -17,8 +17,10 @@ import ( "context" "fmt" "net/url" + "time" "github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol" + "github.com/prometheus/common/model" ) // DidChangeConfiguration is required by the protocol.Server interface. @@ -67,13 +69,13 @@ func (s *server) DidChangeConfiguration(ctx context.Context, params *protocol.Di }) } -/* if err := s.setMetadataLookbackIntervalFromChangeConfiguration(config); err != nil { + if err := s.setMetadataLookbackInterval(config); err != nil { // nolint: errcheck s.client.LogMessage(ctx, &protocol.LogMessageParams{ Type: protocol.Info, Message: err.Error(), }) - }*/ + } return nil } @@ -101,13 +103,13 @@ func (s *server) connectPrometheus(url string) error { return nil } -/*func (s *server) setMetadataLookbackIntervalFromChangeConfiguration(settings map[string]string) error { +func (s *server) setMetadataLookbackInterval(settings map[string]string) error { if interval, ok := settings["metadataLookbackInterval"]; ok { duration, err := model.ParseDuration(interval) if err != nil { return err } - s.config.MetadataLookbackInterval = duration + s.metadataService.SetLookbackInterval(time.Duration(duration)) } return nil -}*/ +} diff --git a/langserver/server.go b/langserver/server.go index 759bccb9..a030d830 100644 --- a/langserver/server.go +++ b/langserver/server.go @@ -27,6 +27,7 @@ import ( "net" "os" "sync" + "time" "github.com/prometheus-community/promql-langserver/config" promClient "github.com/prometheus-community/promql-langserver/prometheus" @@ -136,7 +137,7 @@ func ServerFromStream(ctx context.Context, stream jsonrpc2.Stream, conf *config. // In order to have an error message in the IDE/editor, we are going to set the prometheusURL in the method server#Initialized. s.prometheusURL = conf.PrometheusURL - prometheusClient, err := promClient.NewClient("") + prometheusClient, err := promClient.NewClient("", time.Duration(conf.MetadataLookbackInterval)) if err != nil { // nolint: errcheck s.client.ShowMessage(s.lifetime, &protocol.ShowMessageParams{ diff --git a/prometheus/compatible.go b/prometheus/compatible.go index 591ab76a..51732f86 100644 --- a/prometheus/compatible.go +++ b/prometheus/compatible.go @@ -25,6 +25,7 @@ import ( type compatibleHTTPClient struct { MetadataService prometheusClient v1.API + lookbackInterval time.Duration } func (c *compatibleHTTPClient) MetricMetadata(ctx context.Context, metric string) (v1.Metadata, error) { @@ -46,13 +47,12 @@ func (c *compatibleHTTPClient) AllMetricMetadata(ctx context.Context) (map[strin return c.prometheusClient.Metadata(ctx, "", "") } -func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string, - startTime time.Time, endTime time.Time) ([]string, error) { +func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string) ([]string, error) { if len(name) == 0 { - names, _, err := c.prometheusClient.LabelNames(ctx, startTime, endTime) + names, _, err := c.prometheusClient.LabelNames(ctx, time.Now().Add(-1*c.lookbackInterval), time.Now()) return names, err } - labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, startTime, endTime) + labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, time.Now().Add(-1*c.lookbackInterval), time.Now()) if err != nil { return nil, err } @@ -70,9 +70,8 @@ func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string, return result, nil } -func (c *compatibleHTTPClient) LabelValues(ctx context.Context, label string, - startTime time.Time, endTime time.Time) ([]model.LabelValue, error) { - values, _, err := c.prometheusClient.LabelValues(ctx, label, startTime, endTime) +func (c *compatibleHTTPClient) LabelValues(ctx context.Context, label string) ([]model.LabelValue, error) { + values, _, err := c.prometheusClient.LabelValues(ctx, label, time.Now().Add(-1*c.lookbackInterval), time.Now()) return values, err } @@ -80,6 +79,10 @@ func (c *compatibleHTTPClient) ChangeDataSource(_ string) error { return fmt.Errorf("method not supported") } +func (c *compatibleHTTPClient) SetLookbackInterval(interval time.Duration) { + c.lookbackInterval = interval +} + func (c *compatibleHTTPClient) GetURL() string { return "" } diff --git a/prometheus/empty.go b/prometheus/empty.go index ad27d533..2419cc37 100644 --- a/prometheus/empty.go +++ b/prometheus/empty.go @@ -34,11 +34,11 @@ func (c *emptyHTTPClient) AllMetricMetadata(_ context.Context) (map[string][]v1. return make(map[string][]v1.Metadata), nil } -func (c *emptyHTTPClient) LabelNames(_ context.Context, _ string, _ time.Time, _ time.Time) ([]string, error) { +func (c *emptyHTTPClient) LabelNames(_ context.Context, _ string) ([]string, error) { return []string{}, nil } -func (c *emptyHTTPClient) LabelValues(_ context.Context, _ string, _ time.Time, _ time.Time) ([]model.LabelValue, error) { +func (c *emptyHTTPClient) LabelValues(_ context.Context, _ string) ([]model.LabelValue, error) { return []model.LabelValue{}, nil } @@ -46,6 +46,10 @@ func (c *emptyHTTPClient) ChangeDataSource(_ string) error { return fmt.Errorf("method not supported") } +func (c *emptyHTTPClient) SetLookbackInterval(_ time.Duration) { + +} + func (c *emptyHTTPClient) GetURL() string { return "" } diff --git a/prometheus/metadata_service.go b/prometheus/metadata_service.go index 967aeb3e..d9773dca 100644 --- a/prometheus/metadata_service.go +++ b/prometheus/metadata_service.go @@ -92,12 +92,14 @@ type MetadataService interface { AllMetricMetadata(ctx context.Context) (map[string][]v1.Metadata, error) // LabelNames returns all the unique label names present in the block in sorted order. // If a metric is provided, then it will return all unique label names linked to the metric during a predefined period of time - LabelNames(ctx context.Context, metricName string, startTime time.Time, endTime time.Time) ([]string, error) + LabelNames(ctx context.Context, metricName string) ([]string, error) // LabelValues performs a query for the values of the given label. - LabelValues(ctx context.Context, label string, startTime time.Time, endTime time.Time) ([]model.LabelValue, error) + LabelValues(ctx context.Context, label string) ([]model.LabelValue, error) // ChangeDataSource is used if the prometheusURL is changing. // The client should re init its own parameter accordingly if necessary ChangeDataSource(prometheusURL string) error + // SetLookbackInterval is a method to use to change the interval that then will be used to retrieve data such as label and metrics from prometheus. + SetLookbackInterval(interval time.Duration) // GetURL is returning the url used to contact the prometheus server // In case the instance is used directly in Prometheus, it should be the externalURL GetURL() string @@ -108,15 +110,17 @@ type MetadataService interface { // because it will manage which sub instance of the Client to use (like a factory). type httpClient struct { MetadataService - requestTimeout time.Duration - mutex sync.RWMutex - subClient MetadataService - url string + requestTimeout time.Duration + mutex sync.RWMutex + subClient MetadataService + url string + lookbackInterval time.Duration } -func NewClient(prometheusURL string) (MetadataService, error) { +func NewClient(prometheusURL string, lookbackInterval time.Duration) (MetadataService, error) { c := &httpClient{ - requestTimeout: 30, + requestTimeout: 30, + lookbackInterval: lookbackInterval, } if err := c.ChangeDataSource(prometheusURL); err != nil { return nil, err @@ -136,18 +140,16 @@ func (c *httpClient) AllMetricMetadata(ctx context.Context) (map[string][]v1.Met return c.subClient.AllMetricMetadata(ctx) } -func (c *httpClient) LabelNames(ctx context.Context, name string, - startTime time.Time, endTime time.Time) ([]string, error) { +func (c *httpClient) LabelNames(ctx context.Context, name string) ([]string, error) { c.mutex.RLock() defer c.mutex.RUnlock() - return c.subClient.LabelNames(ctx, name, startTime, endTime) + return c.subClient.LabelNames(ctx, name) } -func (c *httpClient) LabelValues(ctx context.Context, label string, - startTime time.Time, endTime time.Time) ([]model.LabelValue, error) { +func (c *httpClient) LabelValues(ctx context.Context, label string) ([]model.LabelValue, error) { c.mutex.RLock() defer c.mutex.RUnlock() - return c.subClient.LabelValues(ctx, label, startTime, endTime) + return c.subClient.LabelValues(ctx, label) } func (c *httpClient) GetURL() string { @@ -156,6 +158,13 @@ func (c *httpClient) GetURL() string { return c.url } +func (c *httpClient) SetLookbackInterval(interval time.Duration) { + c.mutex.Lock() + defer c.mutex.Unlock() + c.lookbackInterval = interval + c.subClient.SetLookbackInterval(interval) +} + func (c *httpClient) ChangeDataSource(prometheusURL string) error { c.mutex.Lock() defer c.mutex.Unlock() @@ -192,10 +201,12 @@ func (c *httpClient) ChangeDataSource(prometheusURL string) error { if isCompatible { c.subClient = &compatibleHTTPClient{ prometheusClient: v1.NewAPI(prometheusHTTPClient), + lookbackInterval: c.lookbackInterval, } } else { c.subClient = ¬CompatibleHTTPClient{ prometheusClient: v1.NewAPI(prometheusHTTPClient), + lookbackInterval: c.lookbackInterval, } } diff --git a/prometheus/not_compatible.go b/prometheus/not_compatible.go index 990799fb..f1993c3a 100644 --- a/prometheus/not_compatible.go +++ b/prometheus/not_compatible.go @@ -25,6 +25,7 @@ import ( type notCompatibleHTTPClient struct { MetadataService prometheusClient v1.API + lookbackInterval time.Duration } func (c *notCompatibleHTTPClient) MetricMetadata(ctx context.Context, metric string) (v1.Metadata, error) { @@ -54,13 +55,12 @@ func (c *notCompatibleHTTPClient) AllMetricMetadata(ctx context.Context) (map[st return allMetadata, nil } -func (c *notCompatibleHTTPClient) LabelNames(ctx context.Context, name string, - startTime time.Time, endTime time.Time) ([]string, error) { +func (c *notCompatibleHTTPClient) LabelNames(ctx context.Context, name string) ([]string, error) { if len(name) == 0 { - names, _, err := c.prometheusClient.LabelNames(ctx, startTime, endTime) + names, _, err := c.prometheusClient.LabelNames(ctx, time.Now().Add(-1*c.lookbackInterval), time.Now()) return names, err } - labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, startTime, endTime) + labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, time.Now().Add(-1*c.lookbackInterval), time.Now()) if err != nil { return nil, err } @@ -78,9 +78,8 @@ func (c *notCompatibleHTTPClient) LabelNames(ctx context.Context, name string, return result, nil } -func (c *notCompatibleHTTPClient) LabelValues(ctx context.Context, label string, - startTime time.Time, endTime time.Time) ([]model.LabelValue, error) { - values, _, err := c.prometheusClient.LabelValues(ctx, label, startTime, endTime) +func (c *notCompatibleHTTPClient) LabelValues(ctx context.Context, label string) ([]model.LabelValue, error) { + values, _, err := c.prometheusClient.LabelValues(ctx, label, time.Now().Add(-1*c.lookbackInterval), time.Now()) return values, err } @@ -88,6 +87,10 @@ func (c *notCompatibleHTTPClient) ChangeDataSource(_ string) error { return fmt.Errorf("method not supported") } +func (c *notCompatibleHTTPClient) SetLookbackInterval(interval time.Duration) { + c.lookbackInterval = interval +} + func (c *notCompatibleHTTPClient) GetURL() string { return "" } diff --git a/rest/handler.go b/rest/handler.go index 0a9325cb..1c2bcae5 100644 --- a/rest/handler.go +++ b/rest/handler.go @@ -26,8 +26,6 @@ import ( "github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol" "github.com/prometheus-community/promql-langserver/langserver" promClient "github.com/prometheus-community/promql-langserver/prometheus" - "github.com/prometheus/common/model" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -41,8 +39,8 @@ import ( // The provided Logger should be synchronized. // // interval is the period of time (in second) used to retrieve data such as label and metrics from metrics. -func CreateHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, interval model.Duration) (http.Handler, error) { - return createHandler(ctx, metadataService, logger, false, interval) +func CreateHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger) (http.Handler, error) { + return createHandler(ctx, metadataService, logger, false) } // CreateInstHandler creates an instrumented http.Handler for the PromQL langserver REST API. @@ -58,19 +56,19 @@ func CreateHandler(ctx context.Context, metadataService promClient.MetadataServi // The provided Logger should be synchronized. // // interval is the period of time (in second) used to retrieve data such as label and metrics from metrics. -func CreateInstHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, interval model.Duration) (http.Handler, error) { - return createHandler(ctx, metadataService, logger, true, interval) +func CreateInstHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger) (http.Handler, error) { + return createHandler(ctx, metadataService, logger, true) } -func createHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, metricsEnpoint bool, interval model.Duration) (http.Handler, error) { - lgs, err := langserver.CreateHeadlessServer(ctx, metadataService, logger, interval) +func createHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, metricsEndpoint bool) (http.Handler, error) { + lgs, err := langserver.CreateHeadlessServer(ctx, metadataService, logger) if err != nil { return nil, err } ls := &langserverHandler{langserver: lgs} ls.m = make(map[string]http.Handler) - ls.createHandlers(metricsEnpoint) + ls.createHandlers(metricsEndpoint) return ls, nil } From 4fa36065d00a2ce5304c2ac54051108bd3ca8b19 Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Wed, 1 Jul 2020 17:12:26 +0200 Subject: [PATCH 4/4] complete documentation regarding the new config attribute Signed-off-by: Augustin Husson --- doc/developing_editor.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/developing_editor.md b/doc/developing_editor.md index 769a22e3..cec1b275 100644 --- a/doc/developing_editor.md +++ b/doc/developing_editor.md @@ -16,6 +16,7 @@ activate_rpc_log: false # It's a boolean in order to activate or deactivate the log_format: "text" # The format of the log printed. Possible value: json, text. Default value: "text" prometheus_url: "http://localhost:9090" # the HTTP URL of the prometheus server. rest_api_port: 8080 # When set, the server will be started as an HTTP server that provides a REST API instead of the language server protocol. Default value: 0 +metadata_lookback_interval: 2d # Interval used to retrieve data such as label and metrics from prometheus. Default value: 12h ``` In case the file is not provided, it will read the configuration from the environment variables with the following structure: @@ -23,8 +24,9 @@ In case the file is not provided, it will read the configuration from the enviro ```bash export LANGSERVER_ACTIVATERPCLOG="true" export LANGSERVER_PROMETHEUSURL="http://localhost:9090" -export LANGSERVER_RESTAPIPORT"="8080" -export LANGSERVER_LOGFORMAT"="json" +export LANGSERVER_RESTAPIPORT="8080" +export LANGSERVER_LOGFORMAT="json" +export LANGSERVER_METADATALOOKBACKINTERVAL="1w" ``` Note: documentation and default value are the same for both configuration (yaml and environment) @@ -37,7 +39,8 @@ It has the following structure: ```json { "promql": { - "url": "http://localhost:9090" # the HTTP URL of the prometheus server. + "url": "http://localhost:9090", # the HTTP URL of the prometheus server. + "metadataLookbackInterval": "2h" } } ```