From d804edb801bfab59a025c34663dae162886ac20c Mon Sep 17 00:00:00 2001 From: Joel Takvorian Date: Tue, 16 Apr 2024 15:31:10 +0200 Subject: [PATCH] Prior config refactoring before adding prometheus config (#511) - Merged the different Config structs (from handler.Config, server.Config and loki.Config) in a dedicated config package - Add names to table-based tests - A few linter fixes (e.g. space after comments) --- cmd/plugin-backend.go | 105 +++------------------------ pkg/{handler => config}/config.go | 114 +++++++++++++++++++----------- pkg/config/duration.go | 56 +++++++++++++++ pkg/config/loki.go | 37 ++++++++++ pkg/handler/csv/loki_csv.go | 24 +++---- pkg/handler/export.go | 6 +- pkg/handler/flows.go | 13 ++-- pkg/handler/frontend-config.go | 26 +++++++ pkg/handler/loki.go | 26 +++---- pkg/handler/resources.go | 15 ++-- pkg/handler/resources_test.go | 15 ++-- pkg/handler/response.go | 4 +- pkg/handler/topology.go | 13 ++-- pkg/loki/config.go | 60 ---------------- pkg/loki/flow_query.go | 9 +-- pkg/loki/query_test.go | 43 ++++------- pkg/loki/topology_query.go | 7 +- pkg/server/metrics_server.go | 5 +- pkg/server/routes.go | 11 +-- pkg/server/server.go | 51 +++++-------- pkg/server/server_flows_test.go | 64 ++++++++++------- pkg/server/server_test.go | 101 +++++++++++--------------- 22 files changed, 384 insertions(+), 421 deletions(-) rename pkg/{handler => config}/config.go (71%) create mode 100644 pkg/config/duration.go create mode 100644 pkg/config/loki.go create mode 100644 pkg/handler/frontend-config.go delete mode 100644 pkg/loki/config.go diff --git a/cmd/plugin-backend.go b/cmd/plugin-backend.go index e935203d0..f154e17b3 100644 --- a/cmd/plugin-backend.go +++ b/cmd/plugin-backend.go @@ -3,17 +3,11 @@ package main import ( "flag" "fmt" - "net/url" "os" - "strings" - "time" "github.com/sirupsen/logrus" - "github.com/netobserv/network-observability-console-plugin/pkg/handler" - "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" - "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/client" - "github.com/netobserv/network-observability-console-plugin/pkg/loki" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/server" ) @@ -45,104 +39,21 @@ func main() { logrus.SetLevel(lvl) log.Infof("Starting %s at log level %s", appVersion, *logLevel) - config, err := handler.ReadConfigFile(buildVersion, buildDate, *configPath) + cfg, err := config.ReadFile(buildVersion, buildDate, *configPath) if err != nil { log.WithError(err).Fatal("error reading config file") } - // check config required fields - var configErrors []string - if len(config.Loki.Labels) == 0 { - configErrors = append(configErrors, "labels cannot be empty") - } - - // parse config urls - var lURL, lStatusURL *url.URL - if len(config.Loki.URL) == 0 { - configErrors = append(configErrors, "url cannot be empty") - } else { - lURL, err = url.Parse(config.Loki.URL) - if err != nil { - configErrors = append(configErrors, "wrong Loki URL") - } - } - if len(config.Loki.StatusURL) > 0 { - lStatusURL, err = url.Parse(config.Loki.StatusURL) - if err != nil { - configErrors = append(configErrors, "wrong Loki status URL") - } - } else { - lStatusURL = lURL - } - - // parse config timeout - ltimeout, err := time.ParseDuration(config.Loki.Timeout) - if err != nil { - configErrors = append(configErrors, "wrong Loki timeout") - } - - // parse config auth - var checkType auth.CheckType - if config.Loki.AuthCheck == "auto" { - if config.Loki.ForwardUserToken { - // FORWARD lokiAuth mode - checkType = auth.CheckAuthenticated - } else { - // HOST or DISABLED lokiAuth mode - checkType = auth.CheckAdmin - } - log.Info(fmt.Sprintf("auth-check 'auto' resolved to '%s'", checkType)) - } else { - checkType = auth.CheckType(config.Loki.AuthCheck) - } - if checkType == auth.CheckNone { - log.Warn("INSECURE: auth checker is disabled") - } - checker, err := auth.NewChecker(checkType, client.NewInCluster) + checker, err := cfg.GetAuthChecker() if err != nil { - configErrors = append(configErrors, "auth checker error") - } - - // crash on config errors - if len(configErrors) > 0 { - configErrors = append([]string{fmt.Sprintf("Config file has %d errors:\n", len(configErrors))}, configErrors...) - log.Fatal(strings.Join(configErrors, "\n - ")) + log.WithError(err).Fatal("auth checker error") } go server.StartMetrics(&server.MetricsConfig{ - Port: config.Server.MetricsPort, - CertPath: config.Server.CertPath, - KeyPath: config.Server.KeyPath, + Port: cfg.Server.MetricsPort, + CertPath: cfg.Server.CertPath, + KeyPath: cfg.Server.KeyPath, }) - server.Start(&server.Config{ - BuildVersion: buildVersion, - BuildDate: buildDate, - Port: config.Server.Port, - CertPath: config.Server.CertPath, - KeyPath: config.Server.KeyPath, - CORSAllowOrigin: config.Server.CORSOrigin, - CORSAllowMethods: config.Server.CORSMethods, - CORSAllowHeaders: config.Server.CORSHeaders, - CORSMaxAge: config.Server.CORSMaxAge, - ConfigPath: *configPath, - Loki: loki.NewConfig( - lURL, - lStatusURL, - ltimeout, - config.Loki.TenantID, - config.Loki.TokenPath, - config.Loki.ForwardUserToken, - config.Loki.SkipTLS, - config.Loki.CAPath, - config.Loki.StatusSkipTLS, - config.Loki.CAPath, - config.Loki.StatusUserCertPath, - config.Loki.StatusUserKeyPath, - config.Loki.UseMocks, - config.Loki.Labels, - config.Frontend.Deduper.Mark, - config.Frontend.Deduper.Merge, - ), - }, checker) + server.Start(cfg, checker) } diff --git a/pkg/handler/config.go b/pkg/config/config.go similarity index 71% rename from pkg/handler/config.go rename to pkg/config/config.go index 7506973b4..937089125 100644 --- a/pkg/handler/config.go +++ b/pkg/config/config.go @@ -1,12 +1,22 @@ -package handler +package config import ( - "net/http" + "fmt" + "net/url" "os" + "strings" + "time" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" + "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/client" + "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" ) +var ( + log = logrus.WithField("module", "config") +) + type Server struct { Port int `yaml:"port,omitempty" json:"port,omitempty"` MetricsPort int `yaml:"metricsPort,omitempty" json:"metricsPort,omitempty"` @@ -18,25 +28,6 @@ type Server struct { CORSMaxAge string `yaml:"corsMaxAge,omitempty" json:"corsMaxAge,omitempty"` } -type Loki struct { - URL string `yaml:"url" json:"url"` - Labels []string `yaml:"labels" json:"labels"` - - StatusURL string `yaml:"statusUrl,omitempty" json:"statusUrl,omitempty"` - Timeout string `yaml:"timeout,omitempty" json:"timeout,omitempty"` - TenantID string `yaml:"tenantID,omitempty" json:"tenantID,omitempty"` - TokenPath string `yaml:"tokenPath,omitempty" json:"tokenPath,omitempty"` - SkipTLS bool `yaml:"skipTls,omitempty" json:"skipTls,omitempty"` - CAPath string `yaml:"caPath,omitempty" json:"caPath,omitempty"` - StatusSkipTLS bool `yaml:"statusSkipTls,omitempty" json:"statusSkipTls,omitempty"` - StatusCAPath string `yaml:"statusCaPath,omitempty" json:"statusCaPath,omitempty"` - StatusUserCertPath string `yaml:"statusUserCertPath,omitempty" json:"statusUserCertPath,omitempty"` - StatusUserKeyPath string `yaml:"statusUserKeyPath,omitempty" json:"statusUserKeyPath,omitempty"` - UseMocks bool `yaml:"useMocks,omitempty" json:"useMocks,omitempty"` - ForwardUserToken bool `yaml:"forwardUserToken,omitempty" json:"forwardUserToken,omitempty"` - AuthCheck string `yaml:"authCheck,omitempty" json:"authCheck,omitempty"` -} - type PortNaming struct { Enable bool `yaml:"enable" json:"enable"` PortNames map[string]string `yaml:"portNames" json:"portNames"` @@ -110,13 +101,14 @@ type Frontend struct { type Config struct { Loki Loki `yaml:"loki" json:"loki"` Frontend Frontend `yaml:"frontend" json:"frontend"` - - Server Server `yaml:"server,omitempty" json:"server,omitempty"` + Server Server `yaml:"server,omitempty" json:"server,omitempty"` + Path string `yaml:"-" json:"-"` } -func ReadConfigFile(version, date, filename string) (*Config, error) { - //set default vales +func ReadFile(version, date, filename string) (*Config, error) { + // set default values cfg := Config{ + Path: filename, Server: Server{ Port: 9001, MetricsPort: 9002, @@ -124,7 +116,7 @@ func ReadConfigFile(version, date, filename string) (*Config, error) { CORSHeaders: "Origin, X-Requested-With, Content-Type, Accept", }, Loki: Loki{ - Timeout: "30s", + Timeout: Duration{Duration: 30 * time.Second}, AuthCheck: "auto", }, Frontend: Frontend{ @@ -140,10 +132,9 @@ func ReadConfigFile(version, date, filename string) (*Config, error) { Filters: []Filter{}, QuickFilters: []QuickFilter{}, Features: []string{}, - // TODO: update these defaults when operator will move to merge mode Deduper: Deduper{ - Mark: true, - Merge: false, + Mark: false, + Merge: true, }, Fields: []FieldConfig{ {Name: "TimeFlowEndMs", Type: "number"}, @@ -160,24 +151,63 @@ func ReadConfigFile(version, date, filename string) (*Config, error) { return nil, err } err = yaml.Unmarshal(yamlFile, &cfg) - return &cfg, err + if err != nil { + return nil, err + } + + cfg.Validate() + + return &cfg, nil } -func GetFrontendConfig(version, date, filename string) func(w http.ResponseWriter, r *http.Request) { - config, err := ReadConfigFile(version, date, filename) - if err != nil { - hlog.Errorf("Could not read config file: %v", err) +func (c *Config) Validate() { + var configErrors []string + + // check config required fields + if len(c.Loki.Labels) == 0 { + configErrors = append(configErrors, "labels cannot be empty") } - return func(w http.ResponseWriter, r *http.Request) { + + // parse config urls + if len(c.Loki.URL) == 0 { + configErrors = append(configErrors, "url cannot be empty") + } else { + _, err := url.Parse(c.Loki.URL) + if err != nil { + configErrors = append(configErrors, "wrong Loki URL") + } + } + if len(c.Loki.StatusURL) > 0 { + _, err := url.Parse(c.Loki.StatusURL) if err != nil { - config, err = ReadConfigFile(version, date, filename) - if err != nil { - writeError(w, http.StatusInternalServerError, err.Error()) - } else { - writeJSON(w, http.StatusOK, config.Frontend) - } + configErrors = append(configErrors, "wrong Loki status URL") + } + } + + // crash on config errors + if len(configErrors) > 0 { + configErrors = append([]string{fmt.Sprintf("Config file has %d errors:\n", len(configErrors))}, configErrors...) + log.Fatal(strings.Join(configErrors, "\n - ")) + } +} + +func (c *Config) GetAuthChecker() (auth.Checker, error) { + // parse config auth + var checkType auth.CheckType + if c.Loki.AuthCheck == "auto" { + if c.Loki.ForwardUserToken { + // FORWARD lokiAuth mode + checkType = auth.CheckAuthenticated } else { - writeJSON(w, http.StatusOK, config.Frontend) + // HOST or DISABLED lokiAuth mode + checkType = auth.CheckAdmin } + log.Info(fmt.Sprintf("auth-check 'auto' resolved to '%s'", checkType)) + } else { + checkType = auth.CheckType(c.Loki.AuthCheck) + } + if checkType == auth.CheckNone { + log.Warn("INSECURE: auth checker is disabled") } + return auth.NewChecker(checkType, client.NewInCluster) } diff --git a/pkg/config/duration.go b/pkg/config/duration.go new file mode 100644 index 000000000..9535bf375 --- /dev/null +++ b/pkg/config/duration.go @@ -0,0 +1,56 @@ +package config + +// TODO: move this file in a new netobserv-libs repo ? (Same in FLP) + +import ( + "encoding/json" + "fmt" + "time" +) + +// Duration is a wrapper of time.Duration that allows json marshaling. +type Duration struct { + time.Duration +} + +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +func (d *Duration) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return err + } + switch value := v.(type) { + case float64: + d.Duration = time.Duration(value) + return nil + case string: + var err error + d.Duration, err = time.ParseDuration(value) + if err != nil { + return err + } + return nil + default: + return fmt.Errorf("invalid duration %v", value) + } +} + +func (d Duration) MarshalYAML() (interface{}, error) { + return d.String(), nil +} + +func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { + var durationStr string + err := unmarshal(&durationStr) + if err != nil { + return err + } + d.Duration, err = time.ParseDuration(durationStr) + if err != nil { + return err + } + return nil +} diff --git a/pkg/config/loki.go b/pkg/config/loki.go new file mode 100644 index 000000000..24edcc2fb --- /dev/null +++ b/pkg/config/loki.go @@ -0,0 +1,37 @@ +package config + +import "github.com/netobserv/network-observability-console-plugin/pkg/utils" + +type Loki struct { + URL string `yaml:"url" json:"url"` + Labels []string `yaml:"labels" json:"labels"` + StatusURL string `yaml:"statusUrl,omitempty" json:"statusUrl,omitempty"` + Timeout Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"` + TenantID string `yaml:"tenantID,omitempty" json:"tenantID,omitempty"` + TokenPath string `yaml:"tokenPath,omitempty" json:"tokenPath,omitempty"` + SkipTLS bool `yaml:"skipTls,omitempty" json:"skipTls,omitempty"` + CAPath string `yaml:"caPath,omitempty" json:"caPath,omitempty"` + StatusSkipTLS bool `yaml:"statusSkipTls,omitempty" json:"statusSkipTls,omitempty"` + StatusCAPath string `yaml:"statusCaPath,omitempty" json:"statusCaPath,omitempty"` + StatusUserCertPath string `yaml:"statusUserCertPath,omitempty" json:"statusUserCertPath,omitempty"` + StatusUserKeyPath string `yaml:"statusUserKeyPath,omitempty" json:"statusUserKeyPath,omitempty"` + UseMocks bool `yaml:"useMocks,omitempty" json:"useMocks,omitempty"` + ForwardUserToken bool `yaml:"forwardUserToken,omitempty" json:"forwardUserToken,omitempty"` + AuthCheck string `yaml:"authCheck,omitempty" json:"authCheck,omitempty"` + labelsMap map[string]struct{} +} + +func (l *Loki) GetStatusURL() string { + if l.StatusURL != "" { + return l.StatusURL + } + return l.URL +} + +func (l *Loki) IsLabel(key string) bool { + if l.labelsMap == nil { + l.labelsMap = utils.GetMapInterface(l.Labels) + } + _, isLabel := l.labelsMap[key] + return isLabel +} diff --git a/pkg/handler/csv/loki_csv.go b/pkg/handler/csv/loki_csv.go index c6dcc94da..195966a50 100644 --- a/pkg/handler/csv/loki_csv.go +++ b/pkg/handler/csv/loki_csv.go @@ -17,20 +17,20 @@ const ( ) func GetCSVData(qr *model.AggregatedQueryResponse, columns []string) ([][]string, error) { - if streams, ok := qr.Result.(model.Streams); ok { //make csv datas containing header as first line + rows + if streams, ok := qr.Result.(model.Streams); ok { // make csv datas containing header as first line + rows data := make([][]string, 1) - //set time columns first data + // set time columns first data data[0] = append(data[0], startTimeCol, endTimeCol, receivedTimeCol) - //prepare columns for faster lookup + // prepare columns for faster lookup columnsMap := utils.GetMapInterface(columns) - //keep ordered labels / field names between each lines - //filtered by columns parameter if specified + // keep ordered labels / field names between each lines + // filtered by columns parameter if specified var labels []string var fields []string for _, stream := range streams { - //get labels from first stream + // get labels from first stream if labels == nil { labels = make([]string, 0, len(stream.Labels)) for name := range stream.Labels { @@ -41,16 +41,16 @@ func GetCSVData(qr *model.AggregatedQueryResponse, columns []string) ([][]string data[0] = append(data[0], labels...) } - //apply timestamp & labels for each entries and add json line fields + // apply timestamp & labels for each entries and add json line fields for _, entry := range stream.Entries { - //get json line + // get json line var line map[string]interface{} err := json.Unmarshal([]byte(entry.Line), &line) if err != nil { return nil, fmt.Errorf("cannot unmarshal line %s", entry.Line) } - //get fields from first line + // get fields from first line if fields == nil { fields = make([]string, 0, len(line)) for name := range line { @@ -75,17 +75,17 @@ func getRowDatas(stream model.Stream, labels, fields []string, line map[string]interface{}, size int) []string { rowDatas := make([]string, 0, size) - //set time columns + // set time columns rowDatas = append(rowDatas, fmt.Sprint(line[startTimeCol])) rowDatas = append(rowDatas, fmt.Sprint(line[endTimeCol])) rowDatas = append(rowDatas, fmt.Sprint(line[receivedTimeCol])) - //set labels values + // set labels values for _, label := range labels { rowDatas = append(rowDatas, stream.Labels[label]) } - //set field values + // set field values for _, field := range fields { rowDatas = append(rowDatas, fmt.Sprint(line[field])) } diff --git a/pkg/handler/export.go b/pkg/handler/export.go index fa4e1986d..3752b35f2 100644 --- a/pkg/handler/export.go +++ b/pkg/handler/export.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/netobserv/network-observability-console-plugin/pkg/loki" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/metrics" ) @@ -16,9 +16,9 @@ const ( exportcolumnsKey = "columns" ) -func ExportFlows(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func ExportFlows(cfg *config.Config) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - lokiClient := newLokiClient(cfg, r.Header, false) + lokiClient := newLokiClient(&cfg.Loki, r.Header, false) var code int startTime := time.Now() defer func() { diff --git a/pkg/handler/flows.go b/pkg/handler/flows.go index c487978a3..daa6c0ffa 100644 --- a/pkg/handler/flows.go +++ b/pkg/handler/flows.go @@ -6,6 +6,7 @@ import ( "net/url" "time" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/httpclient" "github.com/netobserv/network-observability-console-plugin/pkg/loki" "github.com/netobserv/network-observability-console-plugin/pkg/metrics" @@ -26,9 +27,9 @@ const ( packetLossKey = "packetLoss" ) -func GetFlows(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func GetFlows(cfg *config.Config) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - lokiClient := newLokiClient(cfg, r.Header, false) + lokiClient := newLokiClient(&cfg.Loki, r.Header, false) var code int startTime := time.Now() defer func() { @@ -49,7 +50,7 @@ func GetFlows(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { } } -func getFlows(cfg *loki.Config, client httpclient.Caller, params url.Values) (*model.AggregatedQueryResponse, int, error) { +func getFlows(cfg *config.Config, client httpclient.Caller, params url.Values) (*model.AggregatedQueryResponse, int, error) { start, err := getStartTime(params) if err != nil { return nil, http.StatusBadRequest, err @@ -67,7 +68,7 @@ func getFlows(cfg *loki.Config, client httpclient.Caller, params url.Values) (*m return nil, http.StatusBadRequest, err } dedup := params.Get(dedupKey) == "true" - if !cfg.Deduper.Mark || utils.Contains(constants.AnyConnectionType, string(recordType)) { + if !cfg.Frontend.Deduper.Mark || utils.Contains(constants.AnyConnectionType, string(recordType)) { dedup = false } packetLoss, err := getPacketLoss(params) @@ -85,7 +86,7 @@ func getFlows(cfg *loki.Config, client httpclient.Caller, params url.Values) (*m // match any, and multiple filters => run in parallel then aggregate var queries []string for _, group := range filterGroups { - qb := loki.NewFlowQueryBuilder(cfg, start, end, limit, dedup, recordType, packetLoss) + qb := loki.NewFlowQueryBuilder(&cfg.Loki, start, end, limit, dedup, recordType, packetLoss) err := qb.Filters(group) if err != nil { return nil, http.StatusBadRequest, errors.New("Can't build query: " + err.Error()) @@ -98,7 +99,7 @@ func getFlows(cfg *loki.Config, client httpclient.Caller, params url.Values) (*m } } else { // else, run all at once - qb := loki.NewFlowQueryBuilder(cfg, start, end, limit, dedup, recordType, packetLoss) + qb := loki.NewFlowQueryBuilder(&cfg.Loki, start, end, limit, dedup, recordType, packetLoss) if len(filterGroups) > 0 { err := qb.Filters(filterGroups[0]) if err != nil { diff --git a/pkg/handler/frontend-config.go b/pkg/handler/frontend-config.go new file mode 100644 index 000000000..c5ab4127c --- /dev/null +++ b/pkg/handler/frontend-config.go @@ -0,0 +1,26 @@ +package handler + +import ( + "net/http" + + "github.com/netobserv/network-observability-console-plugin/pkg/config" +) + +func GetFrontendConfig(version, date, filename string) func(w http.ResponseWriter, r *http.Request) { + cfg, err := config.ReadFile(version, date, filename) + if err != nil { + hlog.Errorf("Could not read config file: %v", err) + } + return func(w http.ResponseWriter, r *http.Request) { + if err != nil { + cfg, err = config.ReadFile(version, date, filename) + if err != nil { + writeError(w, http.StatusInternalServerError, err.Error()) + } else { + writeJSON(w, http.StatusOK, cfg.Frontend) + } + } else { + writeJSON(w, http.StatusOK, cfg.Frontend) + } + } +} diff --git a/pkg/handler/loki.go b/pkg/handler/loki.go index 652877336..cff955394 100644 --- a/pkg/handler/loki.go +++ b/pkg/handler/loki.go @@ -12,6 +12,7 @@ import ( "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/handler/lokiclientmock" "github.com/netobserv/network-observability-console-plugin/pkg/httpclient" "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" @@ -36,7 +37,7 @@ const ( lokiOrgIDHeader = "X-Scope-OrgID" ) -func newLokiClient(cfg *loki.Config, requestHeader http.Header, useStatusConfig bool) httpclient.Caller { +func newLokiClient(cfg *config.Loki, requestHeader http.Header, useStatusConfig bool) httpclient.Caller { headers := map[string][]string{} if cfg.TenantID != "" { headers[lokiOrgIDHeader] = []string{cfg.TenantID} @@ -73,8 +74,7 @@ func newLokiClient(cfg *loki.Config, requestHeader http.Header, useStatusConfig userKeyPath = cfg.StatusUserKeyPath } - // TODO: loki with auth - return httpclient.NewHTTPClient(cfg.Timeout, headers, skipTLS, caPath, userCertPath, userKeyPath) + return httpclient.NewHTTPClient(cfg.Timeout.Duration, headers, skipTLS, caPath, userCertPath, userKeyPath) } /* loki query will fail if spaces or quotes are not encoded @@ -199,10 +199,10 @@ func fetchParallel(lokiClient httpclient.Caller, queries []string, merger loki.M return codeOut, nil } -func LokiReady(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func LokiReady(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, true) - baseURL := strings.TrimRight(cfg.StatusURL.String(), "/") + baseURL := strings.TrimRight(cfg.GetStatusURL(), "/") resp, code, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "ready"), lokiClient) if err != nil { @@ -221,10 +221,10 @@ func LokiReady(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { } } -func LokiMetrics(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func LokiMetrics(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, true) - baseURL := strings.TrimRight(cfg.StatusURL.String(), "/") + baseURL := strings.TrimRight(cfg.GetStatusURL(), "/") resp, code, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "metrics"), lokiClient) if err != nil { @@ -236,10 +236,10 @@ func LokiMetrics(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) } } -func LokiBuildInfos(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func LokiBuildInfos(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, true) - baseURL := strings.TrimRight(cfg.StatusURL.String(), "/") + baseURL := strings.TrimRight(cfg.GetStatusURL(), "/") resp, code, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "loki/api/v1/status/buildinfo"), lokiClient) if err != nil { @@ -251,10 +251,10 @@ func LokiBuildInfos(cfg *loki.Config) func(w http.ResponseWriter, r *http.Reques } } -func LokiConfig(cfg *loki.Config, param string) func(w http.ResponseWriter, r *http.Request) { +func LokiConfig(cfg *config.Loki, param string) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, true) - baseURL := strings.TrimRight(cfg.StatusURL.String(), "/") + baseURL := strings.TrimRight(cfg.GetStatusURL(), "/") resp, code, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "config"), lokiClient) if err != nil { @@ -273,10 +273,10 @@ func LokiConfig(cfg *loki.Config, param string) func(w http.ResponseWriter, r *h } } -func IngesterMaxChunkAge(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func IngesterMaxChunkAge(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, true) - baseURL := strings.TrimRight(cfg.StatusURL.String(), "/") + baseURL := strings.TrimRight(cfg.GetStatusURL(), "/") resp, code, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "config"), lokiClient) if err != nil { diff --git a/pkg/handler/resources.go b/pkg/handler/resources.go index caa453c99..8dc344d35 100644 --- a/pkg/handler/resources.go +++ b/pkg/handler/resources.go @@ -10,6 +10,7 @@ import ( "github.com/gorilla/mux" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/httpclient" "github.com/netobserv/network-observability-console-plugin/pkg/loki" "github.com/netobserv/network-observability-console-plugin/pkg/metrics" @@ -19,7 +20,7 @@ import ( "github.com/netobserv/network-observability-console-plugin/pkg/utils" ) -func GetClusters(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func GetClusters(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, false) var code int @@ -40,7 +41,7 @@ func GetClusters(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) } } -func GetZones(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func GetZones(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, false) var code int @@ -72,7 +73,7 @@ func GetZones(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { } } -func GetNamespaces(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func GetNamespaces(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, false) var code int @@ -104,8 +105,8 @@ func GetNamespaces(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request } } -func getLabelValues(cfg *loki.Config, lokiClient httpclient.Caller, label string) ([]string, int, error) { - baseURL := strings.TrimRight(cfg.URL.String(), "/") +func getLabelValues(cfg *config.Loki, lokiClient httpclient.Caller, label string) ([]string, int, error) { + baseURL := strings.TrimRight(cfg.URL, "/") url := fmt.Sprintf("%s/loki/api/v1/label/%s/values", baseURL, label) hlog.Debugf("getLabelValues URL: %s", url) @@ -126,7 +127,7 @@ func getLabelValues(cfg *loki.Config, lokiClient httpclient.Caller, label string return lvr.Data, http.StatusOK, nil } -func GetNames(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func GetNames(cfg *config.Loki) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lokiClient := newLokiClient(cfg, r.Header, false) var code int @@ -161,7 +162,7 @@ func GetNames(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { } } -func getNamesForPrefix(cfg *loki.Config, lokiClient httpclient.Caller, prefix, kind, namespace string) ([]string, int, error) { +func getNamesForPrefix(cfg *config.Loki, lokiClient httpclient.Caller, prefix, kind, namespace string) ([]string, int, error) { lokiParams := filters.SingleQuery{} if namespace != "" { lokiParams = append(lokiParams, filters.NewMatch(prefix+fields.Namespace, exact(namespace))) diff --git a/pkg/handler/resources_test.go b/pkg/handler/resources_test.go index 8d225c9b3..8b01f4331 100644 --- a/pkg/handler/resources_test.go +++ b/pkg/handler/resources_test.go @@ -1,24 +1,17 @@ package handler import ( - "net/url" "testing" "github.com/stretchr/testify/assert" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/httpclient/httpclienttest" - "github.com/netobserv/network-observability-console-plugin/pkg/loki" ) -var testLokiConfig = loki.Config{ - URL: &url.URL{Scheme: "http", Host: "loki"}, - Labels: map[string]struct{}{ - "_RecordType": {}, - "SrcK8S_Namespace": {}, - "DstK8S_Namespace": {}, - "SrcK8S_OwnerName": {}, - "DstK8S_OwnerName": {}, - }, +var testLokiConfig = config.Loki{ + URL: "http://loki", + Labels: []string{"_RecordType", "SrcK8S_Namespace", "DstK8S_Namespace", "SrcK8S_OwnerName", "DstK8S_OwnerName"}, } const testLokiBaseURL = "http://loki/loki/api/v1/" diff --git a/pkg/handler/response.go b/pkg/handler/response.go index 0e3b3abb7..2e9f30e82 100644 --- a/pkg/handler/response.go +++ b/pkg/handler/response.go @@ -45,14 +45,14 @@ func writeCSV(w http.ResponseWriter, code int, qr *model.AggregatedQueryResponse hlog.Tracef("CSV data: %v", data) t := time.Now() - //output file would be 'export-stdLongYear-stdZeroMonth-stdZeroDay-stdHour-stdZeroMinute.csv' + // output file would be 'export-stdLongYear-stdZeroMonth-stdZeroDay-stdHour-stdZeroMinute.csv' w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=export-%s.csv", t.Format("2006-01-02-15-04"))) w.Header().Set("Content-Type", "text/csv") w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(code) writer := csv.NewWriter(w) for _, row := range data { - //write csv row + // write csv row err := writer.Write(row) if err != nil { writeError(w, http.StatusInternalServerError, fmt.Sprintf("Cannot write row %s", row)) diff --git a/pkg/handler/topology.go b/pkg/handler/topology.go index df2574998..fbb167ca4 100644 --- a/pkg/handler/topology.go +++ b/pkg/handler/topology.go @@ -6,6 +6,7 @@ import ( "net/url" "time" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/httpclient" "github.com/netobserv/network-observability-console-plugin/pkg/loki" "github.com/netobserv/network-observability-console-plugin/pkg/metrics" @@ -27,9 +28,9 @@ const ( defaultStep = "30s" ) -func GetTopology(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) { +func GetTopology(cfg *config.Config) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - lokiClient := newLokiClient(cfg, r.Header, false) + lokiClient := newLokiClient(&cfg.Loki, r.Header, false) var code int startTime := time.Now() defer func() { @@ -47,7 +48,7 @@ func GetTopology(cfg *loki.Config) func(w http.ResponseWriter, r *http.Request) } } -func getTopologyFlows(cfg *loki.Config, client httpclient.Caller, params url.Values) (*model.AggregatedQueryResponse, int, error) { +func getTopologyFlows(cfg *config.Config, client httpclient.Caller, params url.Values) (*model.AggregatedQueryResponse, int, error) { hlog.Debugf("GetTopology query params: %s", params) start, err := getStartTime(params) @@ -129,7 +130,7 @@ func getTopologyFlows(cfg *loki.Config, client httpclient.Caller, params url.Val } qr := merger.Get() - qr.IsMock = cfg.UseMocks + qr.IsMock = cfg.Loki.UseMocks qr.UnixTimestamp = time.Now().Unix() hlog.Tracef("GetTopology response: %v", qr) return qr, http.StatusOK, nil @@ -153,8 +154,8 @@ func expandReportersMergeQueries(queries filters.MultiQueries) filters.MultiQuer return out } -func buildTopologyQuery(cfg *loki.Config, queryFilters filters.SingleQuery, start, end, limit, rateInterval, step string, metricType string, metricFunction constants.MetricFunction, recordType constants.RecordType, packetLoss constants.PacketLoss, aggregate, groups string) (string, int, error) { - qb, err := loki.NewTopologyQuery(cfg, start, end, limit, rateInterval, step, metricType, metricFunction, recordType, packetLoss, aggregate, groups) +func buildTopologyQuery(cfg *config.Config, queryFilters filters.SingleQuery, start, end, limit, rateInterval, step string, metricType string, metricFunction constants.MetricFunction, recordType constants.RecordType, packetLoss constants.PacketLoss, aggregate, groups string) (string, int, error) { + qb, err := loki.NewTopologyQuery(&cfg.Loki, start, end, limit, rateInterval, step, metricType, metricFunction, recordType, packetLoss, aggregate, groups, cfg.Frontend.Deduper.Mark) if err != nil { return "", http.StatusBadRequest, err } diff --git a/pkg/loki/config.go b/pkg/loki/config.go deleted file mode 100644 index 67e14a427..000000000 --- a/pkg/loki/config.go +++ /dev/null @@ -1,60 +0,0 @@ -package loki - -import ( - "net/url" - "time" - - "github.com/netobserv/network-observability-console-plugin/pkg/utils" -) - -type Deduper struct { - Mark bool - Merge bool -} - -type Config struct { - URL *url.URL - StatusURL *url.URL - Timeout time.Duration - TenantID string - TokenPath string - SkipTLS bool - CAPath string - StatusSkipTLS bool - StatusCAPath string - StatusUserCertPath string - StatusUserKeyPath string - - UseMocks bool - ForwardUserToken bool - Labels map[string]struct{} - Deduper Deduper -} - -func NewConfig(url *url.URL, statusURL *url.URL, timeout time.Duration, tenantID string, tokenPath string, forwardUserToken bool, skipTLS bool, capath string, statusSkipTLS bool, statusCapath string, statusUserCertPath string, statusUserKeyPath string, useMocks bool, labels []string, deduperMark bool, deduperMerge bool) Config { - return Config{ - URL: url, - StatusURL: statusURL, - Timeout: timeout, - TenantID: tenantID, - TokenPath: tokenPath, - SkipTLS: skipTLS, - CAPath: capath, - StatusSkipTLS: statusSkipTLS, - StatusCAPath: statusCapath, - StatusUserCertPath: statusUserCertPath, - StatusUserKeyPath: statusUserKeyPath, - UseMocks: useMocks, - ForwardUserToken: forwardUserToken, - Labels: utils.GetMapInterface(labels), - Deduper: Deduper{ - Mark: deduperMark, - Merge: deduperMerge, - }, - } -} - -func (c *Config) IsLabel(key string) bool { - _, isLabel := c.Labels[key] - return isLabel -} diff --git a/pkg/loki/flow_query.go b/pkg/loki/flow_query.go index 7d0d1b05c..28ef49da9 100644 --- a/pkg/loki/flow_query.go +++ b/pkg/loki/flow_query.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/model/fields" "github.com/netobserv/network-observability-console-plugin/pkg/model/filters" "github.com/netobserv/network-observability-console-plugin/pkg/utils/constants" @@ -26,7 +27,7 @@ var filterRegexpValidation = regexp.MustCompile(`^[\w-_.,\"*:/]*$`) // FlowQueryBuilder stores a state to build a LogQL query type FlowQueryBuilder struct { - config *Config + config *config.Loki startTime string endTime string limit string @@ -35,7 +36,7 @@ type FlowQueryBuilder struct { jsonFilters [][]labelFilter } -func NewFlowQueryBuilder(cfg *Config, start, end, limit string, dedup bool, +func NewFlowQueryBuilder(cfg *config.Loki, start, end, limit string, dedup bool, recordType constants.RecordType, packetLoss constants.PacketLoss) *FlowQueryBuilder { // Always use following stream selectors labelFilters := []labelFilter{ @@ -101,7 +102,7 @@ func NewFlowQueryBuilder(cfg *Config, start, end, limit string, dedup bool, } } -func NewFlowQueryBuilderWithDefaults(cfg *Config) *FlowQueryBuilder { +func NewFlowQueryBuilderWithDefaults(cfg *config.Loki) *FlowQueryBuilder { return NewFlowQueryBuilder(cfg, "", "", "", false, constants.RecordTypeLog, constants.PacketLossAll) } @@ -232,7 +233,7 @@ func (q *FlowQueryBuilder) addIPFilters(key string, values []string) { func (q *FlowQueryBuilder) createStringBuilderURL() *strings.Builder { sb := strings.Builder{} - sb.WriteString(strings.TrimRight(q.config.URL.String(), "/")) + sb.WriteString(strings.TrimRight(q.config.URL, "/")) sb.WriteString(queryRangePath) return &sb } diff --git a/pkg/loki/query_test.go b/pkg/loki/query_test.go index a4cd6cf43..b7e66605e 100644 --- a/pkg/loki/query_test.go +++ b/pkg/loki/query_test.go @@ -1,21 +1,18 @@ package loki import ( - "net/url" "testing" - "time" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/model/filters" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestFlowQuery_AddLabelFilters(t *testing.T) { - lokiURL, err := url.Parse("/") - require.NoError(t, err) - cfg := NewConfig(lokiURL, lokiURL, time.Second, "", "", false, false, "", false, "", "", "", false, []string{"foo", "flis"}, true, false) + cfg := config.Loki{URL: "/", Labels: []string{"foo", "flis"}} query := NewFlowQueryBuilderWithDefaults(&cfg) - err = query.addFilter(filters.NewMatch("foo", `"bar"`)) + err := query.addFilter(filters.NewMatch("foo", `"bar"`)) require.NoError(t, err) err = query.addFilter(filters.NewMatch("flis", `"flas"`)) require.NoError(t, err) @@ -24,19 +21,15 @@ func TestFlowQuery_AddLabelFilters(t *testing.T) { } func TestQuery_BackQuote_Error(t *testing.T) { - lokiURL, err := url.Parse("/") - require.NoError(t, err) - cfg := NewConfig(lokiURL, lokiURL, time.Second, "", "", false, false, "", false, "", "", "", false, []string{"lab1", "lab2"}, true, false) + cfg := config.Loki{URL: "/", Labels: []string{"lab1", "lab2"}} query := NewFlowQueryBuilderWithDefaults(&cfg) assert.Error(t, query.addFilter(filters.NewMatch("key", "backquoted`val"))) } func TestFlowQuery_AddNotLabelFilters(t *testing.T) { - lokiURL, err := url.Parse("/") - require.NoError(t, err) - cfg := NewConfig(lokiURL, lokiURL, time.Second, "", "", false, false, "", false, "", "", "", false, []string{"foo", "flis"}, true, false) + cfg := config.Loki{URL: "/", Labels: []string{"foo", "flis"}} query := NewFlowQueryBuilderWithDefaults(&cfg) - err = query.addFilter(filters.NewMatch("foo", `"bar"`)) + err := query.addFilter(filters.NewMatch("foo", `"bar"`)) require.NoError(t, err) err = query.addFilter(filters.NewNotMatch("flis", `"flas"`)) require.NoError(t, err) @@ -49,22 +42,18 @@ func backtick(str string) string { } func TestFlowQuery_AddLineFilterMultipleValues(t *testing.T) { - lokiURL, err := url.Parse("/") - require.NoError(t, err) - cfg := NewConfig(lokiURL, lokiURL, time.Second, "", "", false, false, "", false, "", "", "", false, []string{}, true, false) + cfg := config.Loki{URL: "/"} query := NewFlowQueryBuilderWithDefaults(&cfg) - err = query.addFilter(filters.NewMatch("foo", `bar,baz`)) + err := query.addFilter(filters.NewMatch("foo", `bar,baz`)) require.NoError(t, err) urlQuery := query.Build() assert.Equal(t, `/loki/api/v1/query_range?query={app="netobserv-flowcollector"}|~`+backtick(`foo":"(?i)[^"]*bar.*"|foo":"(?i)[^"]*baz.*"`), urlQuery) } func TestFlowQuery_AddNotLineFilters(t *testing.T) { - lokiURL, err := url.Parse("/") - require.NoError(t, err) - cfg := NewConfig(lokiURL, lokiURL, time.Second, "", "", false, false, "", false, "", "", "", false, []string{}, true, false) + cfg := config.Loki{URL: "/"} query := NewFlowQueryBuilderWithDefaults(&cfg) - err = query.addFilter(filters.NewMatch("foo", `"bar"`)) + err := query.addFilter(filters.NewMatch("foo", `"bar"`)) require.NoError(t, err) err = query.addFilter(filters.NewNotMatch("flis", `"flas"`)) require.NoError(t, err) @@ -73,11 +62,9 @@ func TestFlowQuery_AddNotLineFilters(t *testing.T) { } func TestFlowQuery_AddLineFiltersWithEmpty(t *testing.T) { - lokiURL, err := url.Parse("/") - require.NoError(t, err) - cfg := NewConfig(lokiURL, lokiURL, time.Second, "", "", false, false, "", false, "", "", "", false, []string{}, true, false) + cfg := config.Loki{URL: "/"} query := NewFlowQueryBuilderWithDefaults(&cfg) - err = query.addFilter(filters.NewMatch("foo", `"bar"`)) + err := query.addFilter(filters.NewMatch("foo", `"bar"`)) require.NoError(t, err) err = query.addFilter(filters.NewMatch("flis", `""`)) require.NoError(t, err) @@ -86,11 +73,9 @@ func TestFlowQuery_AddLineFiltersWithEmpty(t *testing.T) { } func TestFlowQuery_AddRecordTypeLabelFilter(t *testing.T) { - lokiURL, err := url.Parse("/") - require.NoError(t, err) - cfg := NewConfig(lokiURL, lokiURL, time.Second, "", "", false, false, "", false, "", "", "", false, []string{"foo", "flis", "_RecordType"}, true, false) + cfg := config.Loki{URL: "/", Labels: []string{"foo", "flis", "_RecordType"}} query := NewFlowQueryBuilderWithDefaults(&cfg) - err = query.addFilter(filters.NewMatch("foo", `"bar"`)) + err := query.addFilter(filters.NewMatch("foo", `"bar"`)) require.NoError(t, err) err = query.addFilter(filters.NewMatch("flis", `"flas"`)) require.NoError(t, err) diff --git a/pkg/loki/topology_query.go b/pkg/loki/topology_query.go index 7fad972b1..3bea7fc80 100644 --- a/pkg/loki/topology_query.go +++ b/pkg/loki/topology_query.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/utils" "github.com/netobserv/network-observability-console-plugin/pkg/utils/constants" ) @@ -32,9 +33,9 @@ type TopologyQueryBuilder struct { topology *Topology } -func NewTopologyQuery(cfg *Config, start, end, limit, rateInterval, step string, metricType string, +func NewTopologyQuery(cfg *config.Loki, start, end, limit, rateInterval, step, metricType string, metricFunction constants.MetricFunction, recordType constants.RecordType, packetLoss constants.PacketLoss, - aggregate, groups string) (*TopologyQueryBuilder, error) { + aggregate, groups string, dedupMark bool) (*TopologyQueryBuilder, error) { l := limit if len(l) == 0 { l = topologyDefaultLimit @@ -54,7 +55,7 @@ func NewTopologyQuery(cfg *Config, start, end, limit, rateInterval, step string, dedup = false rt = "endConnection" } else { - dedup = cfg.Deduper.Mark + dedup = dedupMark rt = "flowLog" } diff --git a/pkg/server/metrics_server.go b/pkg/server/metrics_server.go index 8c9eb6ca3..b53d11195 100644 --- a/pkg/server/metrics_server.go +++ b/pkg/server/metrics_server.go @@ -28,8 +28,7 @@ func StartMetrics(cfg *MetricsConfig) { if cfg.CertPath != "" && cfg.KeyPath != "" { mlog.Infof("listening on https://:%d", cfg.Port) panic(promServer.ListenAndServeTLS(cfg.CertPath, cfg.KeyPath)) - } else { - mlog.Infof("listening on http://:%d", cfg.Port) - panic(promServer.ListenAndServe()) } + mlog.Infof("listening on http://:%d", cfg.Port) + panic(promServer.ListenAndServe()) } diff --git a/pkg/server/routes.go b/pkg/server/routes.go index 022c17cde..ae5deaacb 100644 --- a/pkg/server/routes.go +++ b/pkg/server/routes.go @@ -7,11 +7,12 @@ import ( "github.com/gorilla/mux" "github.com/sirupsen/logrus" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/handler" "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" ) -func setupRoutes(cfg *Config, authChecker auth.Checker) *mux.Router { +func setupRoutes(cfg *config.Config, authChecker auth.Checker) *mux.Router { r := mux.NewRouter() api := r.PathPrefix("/api").Subrouter() @@ -34,15 +35,15 @@ func setupRoutes(cfg *Config, authChecker auth.Checker) *mux.Router { api.HandleFunc("/loki/buildinfo", handler.LokiBuildInfos(&cfg.Loki)) api.HandleFunc("/loki/config/limits", handler.LokiConfig(&cfg.Loki, "limits_config")) api.HandleFunc("/loki/config/ingester/max_chunk_age", handler.IngesterMaxChunkAge(&cfg.Loki)) - api.HandleFunc("/loki/flow/records", handler.GetFlows(&cfg.Loki)) - api.HandleFunc("/loki/flow/metrics", handler.GetTopology(&cfg.Loki)) - api.HandleFunc("/loki/export", handler.ExportFlows(&cfg.Loki)) + api.HandleFunc("/loki/flow/records", handler.GetFlows(cfg)) + api.HandleFunc("/loki/flow/metrics", handler.GetTopology(cfg)) + api.HandleFunc("/loki/export", handler.ExportFlows(cfg)) api.HandleFunc("/resources/clusters", handler.GetClusters(&cfg.Loki)) api.HandleFunc("/resources/zones", handler.GetZones(&cfg.Loki)) api.HandleFunc("/resources/namespaces", handler.GetNamespaces(&cfg.Loki)) api.HandleFunc("/resources/namespace/{namespace}/kind/{kind}/names", handler.GetNames(&cfg.Loki)) api.HandleFunc("/resources/kind/{kind}/names", handler.GetNames(&cfg.Loki)) - api.HandleFunc("/frontend-config", handler.GetFrontendConfig(cfg.BuildVersion, cfg.BuildDate, cfg.ConfigPath)) + api.HandleFunc("/frontend-config", handler.GetFrontendConfig(cfg.Frontend.BuildVersion, cfg.Frontend.BuildDate, cfg.Path)) r.PathPrefix("/").Handler(http.FileServer(http.Dir("./web/dist/"))) return r diff --git a/pkg/server/server.go b/pkg/server/server.go index c962df881..73d2a3697 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -7,65 +7,50 @@ import ( "github.com/sirupsen/logrus" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" - "github.com/netobserv/network-observability-console-plugin/pkg/loki" ) var slog = logrus.WithField("module", "server") -type Config struct { - BuildVersion string - BuildDate string - Port int - CertPath string - KeyPath string - CORSAllowOrigin string - CORSAllowMethods string - CORSAllowHeaders string - CORSMaxAge string - Loki loki.Config - ConfigPath string -} - -func Start(cfg *Config, authChecker auth.Checker) { +func Start(cfg *config.Config, authChecker auth.Checker) { router := setupRoutes(cfg, authChecker) router.Use(corsHeader(cfg)) writeTimeout := 30 * time.Second if cfg.Loki.Timeout.Seconds() > writeTimeout.Seconds() { - writeTimeout = cfg.Loki.Timeout + writeTimeout = cfg.Loki.Timeout.Duration } httpServer := defaultServer(&http.Server{ Handler: router, - Addr: fmt.Sprintf(":%d", cfg.Port), + Addr: fmt.Sprintf(":%d", cfg.Server.Port), WriteTimeout: writeTimeout, }) - if cfg.CertPath != "" && cfg.KeyPath != "" { - slog.Infof("listening on https://:%d", cfg.Port) - panic(httpServer.ListenAndServeTLS(cfg.CertPath, cfg.KeyPath)) - } else { - slog.Infof("listening on http://:%d", cfg.Port) - panic(httpServer.ListenAndServe()) + if cfg.Server.CertPath != "" && cfg.Server.KeyPath != "" { + slog.Infof("listening on https://:%d", cfg.Server.Port) + panic(httpServer.ListenAndServeTLS(cfg.Server.CertPath, cfg.Server.KeyPath)) } + slog.Infof("listening on http://:%d", cfg.Server.Port) + panic(httpServer.ListenAndServe()) } -func corsHeader(cfg *Config) func(next http.Handler) http.Handler { +func corsHeader(cfg *config.Config) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { headers := w.Header() - if cfg.CORSAllowOrigin != "" { - headers.Set("Access-Control-Allow-Origin", cfg.CORSAllowOrigin) + if cfg.Server.CORSOrigin != "" { + headers.Set("Access-Control-Allow-Origin", cfg.Server.CORSOrigin) } - if cfg.CORSAllowHeaders != "" { - headers.Set("Access-Control-Allow-Header", cfg.CORSAllowHeaders) + if cfg.Server.CORSHeaders != "" { + headers.Set("Access-Control-Allow-Header", cfg.Server.CORSHeaders) } - if cfg.CORSAllowMethods != "" { - headers.Set("Access-Control-Allow-Methods", cfg.CORSAllowMethods) + if cfg.Server.CORSMethods != "" { + headers.Set("Access-Control-Allow-Methods", cfg.Server.CORSMethods) } - if cfg.CORSMaxAge != "" { - headers.Set("Access-Control-Max-Age", cfg.CORSMaxAge) + if cfg.Server.CORSMaxAge != "" { + headers.Set("Access-Control-Max-Age", cfg.Server.CORSMaxAge) } next.ServeHTTP(w, r) }) diff --git a/pkg/server/server_flows_test.go b/pkg/server/server_flows_test.go index e5c58d78d..5f8667326 100644 --- a/pkg/server/server_flows_test.go +++ b/pkg/server/server_flows_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/netobserv/network-observability-console-plugin/pkg/loki" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/model" ) @@ -23,6 +23,7 @@ var timeNowArg = regexp.MustCompile(`\${timeNow-(\d+)}`) func TestLokiFiltering(t *testing.T) { testCases := []struct { + name string inputPath string // Either outputQueries or outputQueryParts should be defined // Use outputQueries when multiple queries are expected (parallel queries for match any) @@ -30,11 +31,13 @@ func TestLokiFiltering(t *testing.T) { outputQueries []string outputQueryParts []string }{{ + name: "Simple line filter", inputPath: "?filters=SrcK8S_Name=test-pod", outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`SrcK8S_Name\":\"(?i)[^\"]*test-pod.*\"`", }, }, { + name: "AND line filter", inputPath: "?filters=" + url.QueryEscape("Proto=6&SrcK8S_Name=test"), outputQueryParts: []string{ "?query={app=\"netobserv-flowcollector\"}", @@ -42,32 +45,38 @@ func TestLokiFiltering(t *testing.T) { "|~`SrcK8S_Name\":\"(?i)[^\"]*test.*\"`", }, }, { + name: "OR line filter", inputPath: "?filters=" + url.QueryEscape("Proto=6|SrcK8S_Name=test"), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`Proto\":6[,}]`", "?query={app=\"netobserv-flowcollector\"}|~`SrcK8S_Name\":\"(?i)[^\"]*test.*\"`", }, }, { + name: "Simple label filter", inputPath: "?filters=" + url.QueryEscape("SrcK8S_Namespace=test-namespace"), outputQueries: []string{ `?query={app="netobserv-flowcollector",SrcK8S_Namespace=~"(?i).*test-namespace.*"}`, }, }, { + name: "OR line filter same key", inputPath: "?filters=" + url.QueryEscape("SrcK8S_Name=name1,name2"), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`SrcK8S_Name\":\"(?i)[^\"]*name1.*\"|SrcK8S_Name\":\"(?i)[^\"]*name2.*\"`", }, }, { + name: "OR label filter same key", inputPath: "?filters=" + url.QueryEscape("SrcK8S_Namespace=ns1,ns2"), outputQueries: []string{ `?query={app="netobserv-flowcollector",SrcK8S_Namespace=~"(?i).*ns1.*|(?i).*ns2.*"}`, }, }, { + name: "Several filters with dedup", inputPath: "?filters=" + url.QueryEscape("SrcPort=8080&SrcAddr=10.128.0.1&SrcK8S_Namespace=default") + "&dedup=true", outputQueries: []string{ "?query={app=\"netobserv-flowcollector\",SrcK8S_Namespace=~\"(?i).*default.*\"}!~`Duplicate\":true`|~`SrcPort\":8080[,}]`|json|SrcAddr=ip(\"10.128.0.1\")", }, }, { + name: "AND IP filters with dedup", inputPath: "?filters=" + url.QueryEscape("SrcAddr=10.128.0.1&DstAddr=10.128.0.2") + "&dedup=true", outputQueryParts: []string{ "?query={app=\"netobserv-flowcollector\"}!~`Duplicate\":true`|json", @@ -75,11 +84,13 @@ func TestLokiFiltering(t *testing.T) { "|DstAddr=ip(\"10.128.0.2\")", }, }, { + name: "OR IP filters", inputPath: "?filters=" + url.QueryEscape("SrcAddr=10.128.0.1,10.128.0.2"), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|json|SrcAddr=ip(\"10.128.0.1\")+or+SrcAddr=ip(\"10.128.0.2\")", }, }, { + name: "Several OR filters", inputPath: "?filters=" + url.QueryEscape("SrcPort=8080|SrcAddr=10.128.0.1|SrcK8S_Namespace=default"), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\",SrcK8S_Namespace=~\"(?i).*default.*\"}", @@ -87,28 +98,35 @@ func TestLokiFiltering(t *testing.T) { "?query={app=\"netobserv-flowcollector\"}|~`SrcPort\":8080[,}]`", }, }, { + name: "Start time", inputPath: "?startTime=1640991600", outputQueries: []string{`?query={app="netobserv-flowcollector"}&start=1640991600`}, }, { + name: "End time", inputPath: "?endTime=1641160800", outputQueries: []string{`?query={app="netobserv-flowcollector"}&end=1641160801`}, }, { + name: "Start and end time", inputPath: "?startTime=1640991600&endTime=1641160800", outputQueries: []string{`?query={app="netobserv-flowcollector"}&start=1640991600&end=1641160801`}, }, { + name: "Time range", inputPath: "?timeRange=300000", outputQueries: []string{`?query={app="netobserv-flowcollector"}&start=${timeNow-300000}`}, }, { + name: "Strict label match", inputPath: "?filters=" + url.QueryEscape("SrcK8S_Namespace=\"exact-namespace\""), outputQueries: []string{ `?query={app="netobserv-flowcollector",SrcK8S_Namespace="exact-namespace"}`, }, }, { + name: "Strict line match", inputPath: "?filters=" + url.QueryEscape("SrcK8S_Name=\"exact-pod\""), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`SrcK8S_Name\":\"exact-pod\"`", }, }, { + name: "Common src+dst name with AND", inputPath: "?filters=" + url.QueryEscape("Port=8080&K8S_Name=test"), outputQueryParts: []string{ "?query={app=\"netobserv-flowcollector\"}", @@ -116,18 +134,21 @@ func TestLokiFiltering(t *testing.T) { "|~`K8S_Name\":\"(?i)[^\"]*test.*\"`", }, }, { + name: "Common src+dst name with OR", inputPath: "?filters=" + url.QueryEscape("Port=8080|K8S_Name=test"), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`K8S_Name\":\"(?i)[^\"]*test.*\"`", "?query={app=\"netobserv-flowcollector\"}|~`Port\":8080[,}]`", }, }, { + name: "Common src+dst port with AND and OR", inputPath: "?filters=" + url.QueryEscape("Port=8080&SrcK8S_Namespace=test|Port=8080&DstK8S_Namespace=test"), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\",SrcK8S_Namespace=~\"(?i).*test.*\"}|~`Port\":8080[,}]`", "?query={app=\"netobserv-flowcollector\",DstK8S_Namespace=~\"(?i).*test.*\"}|~`Port\":8080[,}]`", }, }, { + name: "Common src+dst port with multiple OR", inputPath: "?filters=" + url.QueryEscape("Port=8080|SrcK8S_Namespace=test|DstK8S_Namespace=test"), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\",SrcK8S_Namespace=~\"(?i).*test.*\"}", @@ -135,50 +156,59 @@ func TestLokiFiltering(t *testing.T) { "?query={app=\"netobserv-flowcollector\"}|~`Port\":8080[,}]`", }, }, { + name: "Empty label", inputPath: "?filters=" + url.QueryEscape(`SrcK8S_Namespace=""&DstPort=70`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\",SrcK8S_Namespace=\"\"}|~`DstPort\":70[,}]`", }, }, { + name: "Empty line filter", inputPath: "?filters=" + url.QueryEscape(`SrcK8S_Name=""&DstPort=70`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`DstPort\":70[,}]`|json|SrcK8S_Name=\"\"", }, }, { + name: "Empty line filter OR same key", inputPath: "?filters=" + url.QueryEscape(`SrcK8S_Name="",foo&DstK8S_Name="hello"`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`DstK8S_Name\":\"hello\"`|json|SrcK8S_Name=\"\"+or+SrcK8S_Name=~`(?i).*foo.*`", }, }, { + name: "Empty label ORed", inputPath: "?filters=" + url.QueryEscape(`SrcK8S_Namespace=""|DstPort=70`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\",SrcK8S_Namespace=\"\"}", "?query={app=\"netobserv-flowcollector\"}|~`DstPort\":70[,}]`", }, }, { + name: "Empty line filter ORed", inputPath: "?filters=" + url.QueryEscape(`SrcK8S_Name=""|DstPort=70`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`DstPort\":70[,}]`", "?query={app=\"netobserv-flowcollector\"}|json|SrcK8S_Name=\"\"", }, }, { + name: "Empty line filter ORed (bis)", inputPath: "?filters=" + url.QueryEscape(`SrcK8S_Name="",foo|DstK8S_Name="hello"`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|~`DstK8S_Name\":\"hello\"`", "?query={app=\"netobserv-flowcollector\"}|json|SrcK8S_Name=\"\"+or+SrcK8S_Name=~`(?i).*foo.*`", }, }, { + name: "Empty line filter ORed (ter)", inputPath: "?filters=" + url.QueryEscape(`SrcK8S_Type="","Pod"`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|json|SrcK8S_Type=\"\"+or+SrcK8S_Type=\"Pod\"", }, }, { + name: "Double empty line filters", inputPath: "?filters=" + url.QueryEscape(`SrcAddr=""|DstAddr=""`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|json|DstAddr=\"\"", "?query={app=\"netobserv-flowcollector\"}|json|SrcAddr=\"\"", }, }, { + name: "Empty line port filter", inputPath: "?filters=" + url.QueryEscape(`SrcPort=""`), outputQueries: []string{ "?query={app=\"netobserv-flowcollector\"}|json|SrcPort=\"\"", @@ -210,29 +240,14 @@ func TestLokiFiltering(t *testing.T) { }).Times(numberQueriesExpected) lokiSvc := httptest.NewServer(&lokiMock) defer lokiSvc.Close() - lokiURL, err := url.Parse(lokiSvc.URL) - require.NoError(t, err) // THAT is accessed behind the NOO console plugin backend - backendRoutes := setupRoutes(&Config{ - Loki: loki.NewConfig( - lokiURL, - lokiURL, - time.Second, - "", - "", - false, - false, - "", - false, - "", - "", - "", - false, - []string{"SrcK8S_Namespace", "SrcK8S_OwnerName", "DstK8S_Namespace", "DstK8S_OwnerName", "FlowDirection"}, - true, - false, - ), + backendRoutes := setupRoutes(&config.Config{ + Loki: config.Loki{ + URL: lokiSvc.URL, + Labels: []string{"SrcK8S_Namespace", "SrcK8S_OwnerName", "DstK8S_Namespace", "DstK8S_OwnerName", "FlowDirection"}, + }, + Frontend: config.Frontend{Deduper: config.Deduper{Mark: true}}, }, &authM) backendSvc := httptest.NewServer(backendRoutes) defer backendSvc.Close() @@ -240,15 +255,14 @@ func TestLokiFiltering(t *testing.T) { nCall := 0 for _, tc := range testCases { - t.Run(tc.inputPath, func(t *testing.T) { + t.Run(tc.name, func(t *testing.T) { // WHEN the Loki flows endpoint is queried in the backend now := time.Now().Unix() res, err := backendSvc.Client().Get(backendSvc.URL + "/api/loki/flow/records" + tc.inputPath) require.NoError(t, err) body, err := io.ReadAll(res.Body) require.NoError(t, err) - require.Equalf(t, http.StatusOK, res.StatusCode, - "unexpected return %s: %s", res.Status, string(body)) + require.Equalf(t, http.StatusOK, res.StatusCode, "unexpected return %s: %s", res.Status, string(body)) // THEN each filter argument has been properly forwarded to Loki var expectedURLs []string diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index cd4d7886c..c548414d6 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -15,7 +15,6 @@ import ( "net" "net/http" "net/http/httptest" - "net/url" "os" "path/filepath" "sort" @@ -27,11 +26,10 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/netobserv/network-observability-console-plugin/pkg/config" "github.com/netobserv/network-observability-console-plugin/pkg/kubernetes/auth" - "github.com/netobserv/network-observability-console-plugin/pkg/loki" "github.com/netobserv/network-observability-console-plugin/pkg/model" "github.com/netobserv/network-observability-console-plugin/pkg/model/fields" - "github.com/netobserv/network-observability-console-plugin/pkg/utils" ) const ( @@ -57,11 +55,9 @@ func TestServerRunning(t *testing.T) { authM.MockGranted() go func() { - Start(&Config{ - Loki: loki.Config{ - URL: &url.URL{Scheme: "http", Host: "localhost:3100"}, - }, - Port: testPort, + Start(&config.Config{ + Loki: config.Loki{URL: "http://localhost:3100"}, + Server: config.Server{Port: testPort}, }, &authM) }() @@ -106,11 +102,9 @@ func TestServerUnauthorized(t *testing.T) { defer os.RemoveAll(tmpDir) go func() { - Start(&Config{ - Loki: loki.Config{ - URL: &url.URL{Scheme: "http", Host: "localhost:3100"}, - }, - Port: testPort, + Start(&config.Config{ + Loki: config.Loki{URL: "http://localhost:3100"}, + Server: config.Server{Port: testPort}, }, &auth.BearerTokenChecker{}) }() @@ -177,12 +171,12 @@ func TestSecureComm(t *testing.T) { defer os.Remove(testClientCertFile) defer os.Remove(testClientKeyFile) - conf := &Config{ - CertPath: testServerCertFile, - KeyPath: testServerKeyFile, - Port: testPort, - Loki: loki.Config{ - URL: &url.URL{Scheme: "http", Host: "localhost:3100"}, + conf := &config.Config{ + Loki: config.Loki{URL: "http://localhost:3100"}, + Server: config.Server{ + Port: testPort, + CertPath: testServerCertFile, + KeyPath: testServerKeyFile, }, } @@ -261,11 +255,9 @@ func TestServerHeaderLimits(t *testing.T) { authM.MockGranted() go func() { - Start(&Config{ - Loki: loki.Config{ - URL: &url.URL{Scheme: "http", Host: "localhost:3100"}, - }, - Port: testPort, + Start(&config.Config{ + Loki: config.Loki{URL: "http://localhost:3100"}, + Server: config.Server{Port: testPort}, }, &authM) }() @@ -304,15 +296,10 @@ func TestLokiConfiguration(t *testing.T) { defer lokiSvc.Close() authM := &authMock{} authM.MockGranted() - lokiURL, err := url.Parse(lokiSvc.URL) - require.NoError(t, err) // THAT is accessed behind the NOO console plugin backend - backendRoutes := setupRoutes(&Config{ - Loki: loki.Config{ - URL: lokiURL, - Timeout: time.Second, - }, + backendRoutes := setupRoutes(&config.Config{ + Loki: config.Loki{URL: lokiSvc.URL, Timeout: config.Duration{Duration: time.Second}}, }, authM) backendSvc := httptest.NewServer(backendRoutes) defer backendSvc.Close() @@ -346,20 +333,18 @@ func TestLokiConfigurationForTopology(t *testing.T) { defer lokiSvc.Close() authM := &authMock{} authM.MockGranted() - lokiURL, err := url.Parse(lokiSvc.URL) - require.NoError(t, err) // THAT is accessed behind the NOO console plugin backend - backendRoutes := setupRoutes(&Config{ - Loki: loki.Config{ - URL: lokiURL, - Timeout: time.Second, - Labels: utils.GetMapInterface([]string{fields.SrcNamespace, fields.DstNamespace, fields.SrcOwnerName, fields.DstOwnerName, fields.FlowDirection}), - Deduper: loki.Deduper{ - Mark: true, - Merge: false, - }, + backendRoutes := setupRoutes(&config.Config{ + Loki: config.Loki{ + URL: lokiSvc.URL, + Timeout: config.Duration{Duration: time.Second}, + Labels: []string{fields.SrcNamespace, fields.DstNamespace, fields.SrcOwnerName, fields.DstOwnerName, fields.FlowDirection}, }, + Frontend: config.Frontend{Deduper: config.Deduper{ + Mark: true, + Merge: false, + }}, }, authM) backendSvc := httptest.NewServer(backendRoutes) defer backendSvc.Close() @@ -406,20 +391,18 @@ func TestLokiConfigurationForTableHistogram(t *testing.T) { defer lokiSvc.Close() authM := &authMock{} authM.MockGranted() - lokiURL, err := url.Parse(lokiSvc.URL) - require.NoError(t, err) // THAT is accessed behind the NOO console plugin backend - backendRoutes := setupRoutes(&Config{ - Loki: loki.Config{ - URL: lokiURL, - Timeout: time.Second, - Labels: utils.GetMapInterface([]string{fields.SrcNamespace, fields.DstNamespace, fields.SrcOwnerName, fields.DstOwnerName, fields.FlowDirection}), - Deduper: loki.Deduper{ - Mark: true, - Merge: false, - }, + backendRoutes := setupRoutes(&config.Config{ + Loki: config.Loki{ + URL: lokiSvc.URL, + Timeout: config.Duration{Duration: time.Second}, + Labels: []string{fields.SrcNamespace, fields.DstNamespace, fields.SrcOwnerName, fields.DstOwnerName, fields.FlowDirection}, }, + Frontend: config.Frontend{Deduper: config.Deduper{ + Mark: true, + Merge: false, + }}, }, authM) backendSvc := httptest.NewServer(backendRoutes) defer backendSvc.Close() @@ -477,14 +460,12 @@ func TestLokiConfiguration_MultiTenant(t *testing.T) { authM.MockGranted() lokiSvc := httptest.NewServer(&lokiMock) defer lokiSvc.Close() - lokiURL, err := url.Parse(lokiSvc.URL) - require.NoError(t, err) // GIVEN a NOO console plugin backend configured for HOST Multi tenant mode - backendRoutes := setupRoutes(&Config{ - Loki: loki.Config{ - URL: lokiURL, - Timeout: time.Second, + backendRoutes := setupRoutes(&config.Config{ + Loki: config.Loki{ + URL: lokiSvc.URL, + Timeout: config.Duration{Duration: time.Second}, TenantID: "my-organisation", TokenPath: tmpDir + "/var/run/secrets/tokens/netobserv-plugin", }, @@ -493,7 +474,7 @@ func TestLokiConfiguration_MultiTenant(t *testing.T) { defer backendSvc.Close() // WHEN the Loki flows endpoint is queried in the backend - _, err = backendSvc.Client().Get(backendSvc.URL + "/api/loki/flow/records") + _, err := backendSvc.Client().Get(backendSvc.URL + "/api/loki/flow/records") require.NoError(t, err) // THEN the query has been properly forwarded to Loki with the tenant ID header