Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prior config refactoring before adding prometheus config #511

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 8 additions & 97 deletions cmd/plugin-backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
}
114 changes: 72 additions & 42 deletions pkg/handler/config.go → pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -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"`
Expand All @@ -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"`
Expand Down Expand Up @@ -110,21 +101,22 @@ 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,
CORSOrigin: "*",
CORSHeaders: "Origin, X-Requested-With, Content-Type, Accept",
},
Loki: Loki{
Timeout: "30s",
Timeout: Duration{Duration: 30 * time.Second},
AuthCheck: "auto",
},
Frontend: Frontend{
Expand All @@ -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"},
Expand All @@ -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)
}
56 changes: 56 additions & 0 deletions pkg/config/duration.go
Original file line number Diff line number Diff line change
@@ -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
}
37 changes: 37 additions & 0 deletions pkg/config/loki.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading