CI: add tests for metrics configuration (#2251)

This commit is contained in:
mmetc 2023-06-05 23:17:30 +02:00 committed by GitHub
parent 9ccdddaab1
commit 3cc6b2c0d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 116 additions and 57 deletions

View file

@ -5,7 +5,6 @@ import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
@ -23,6 +22,7 @@ import (
// FormatPrometheusMetrics is a complete rip from prom2json
func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error {
mfChan := make(chan *dto.MetricFamily, 1024)
errChan := make(chan error, 1)
// Start with the DefaultTransport for sane defaults.
transport := http.DefaultTransport.(*http.Transport).Clone()
@ -35,14 +35,21 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error
defer trace.CatchPanic("crowdsec/ShowPrometheus")
err := prom2json.FetchMetricFamilies(url, mfChan, transport)
if err != nil {
log.Fatalf("failed to fetch prometheus metrics : %v", err)
errChan <- fmt.Errorf("failed to fetch prometheus metrics: %w", err)
return
}
errChan <- nil
}()
result := []*prom2json.Family{}
for mf := range mfChan {
result = append(result, prom2json.NewFamily(mf))
}
if err := <-errChan; err != nil {
return err
}
log.Debugf("Finished reading prometheus output, %d entries", len(result))
/*walk*/
lapi_decisions_stats := map[string]struct {
@ -262,36 +269,44 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error
var noUnit bool
func runMetrics(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadPrometheus(); err != nil {
return fmt.Errorf("failed to load prometheus config: %w", err)
}
if csConfig.Prometheus == nil {
return fmt.Errorf("prometheus section missing, can't show metrics")
}
if !csConfig.Prometheus.Enabled {
return fmt.Errorf("prometheus is not enabled, can't show metrics")
}
if prometheusURL == "" {
prometheusURL = csConfig.Cscli.PrometheusUrl
}
if prometheusURL == "" {
return fmt.Errorf("no prometheus url, please specify in %s or via -u", *csConfig.FilePath)
}
err := FormatPrometheusMetrics(color.Output, prometheusURL+"/metrics", csConfig.Cscli.Output)
if err != nil {
return fmt.Errorf("could not fetch prometheus metrics: %w", err)
}
return nil
}
func NewMetricsCmd() *cobra.Command {
var cmdMetrics = &cobra.Command{
cmdMetrics := &cobra.Command{
Use: "metrics",
Short: "Display crowdsec prometheus metrics.",
Long: `Fetch metrics from the prometheus server and display them in a human-friendly way`,
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
if err := csConfig.LoadPrometheus(); err != nil {
log.Fatal(err)
}
if !csConfig.Prometheus.Enabled {
log.Warning("Prometheus is not enabled, can't show metrics")
os.Exit(1)
}
if prometheusURL == "" {
prometheusURL = csConfig.Cscli.PrometheusUrl
}
if prometheusURL == "" {
log.Errorf("No prometheus url, please specify in %s or via -u", *csConfig.FilePath)
os.Exit(1)
}
err := FormatPrometheusMetrics(color.Output, prometheusURL+"/metrics", csConfig.Cscli.Output)
if err != nil {
log.Fatalf("could not fetch prometheus metrics: %s", err)
}
},
RunE: runMetrics,
}
cmdMetrics.PersistentFlags().StringVarP(&prometheusURL, "url", "u", "", "Prometheus url (http://<ip>:<port>/metrics)")
cmdMetrics.PersistentFlags().BoolVar(&noUnit, "no-unit", false, "Show the real number instead of formatted with units")

View file

@ -2,7 +2,6 @@ package csconfig
import "fmt"
/**/
type PrometheusCfg struct {
Enabled bool `yaml:"enabled"`
Level string `yaml:"level"` //aggregated|full
@ -16,6 +15,5 @@ func (c *Config) LoadPrometheus() error {
c.Cscli.PrometheusUrl = fmt.Sprintf("http://%s:%d", c.Prometheus.ListenAddr, c.Prometheus.ListenPort)
}
}
return nil
}

View file

@ -1,20 +1,19 @@
package csconfig
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
"github.com/stretchr/testify/require"
)
func TestLoadPrometheus(t *testing.T) {
tests := []struct {
name string
Input *Config
expectedResult string
err string
name string
Input *Config
expectedURL string
expectedErr string
}{
{
name: "basic valid configuration",
@ -27,29 +26,17 @@ func TestLoadPrometheus(t *testing.T) {
},
Cscli: &CscliCfg{},
},
expectedResult: "http://127.0.0.1:6060",
expectedURL: "http://127.0.0.1:6060",
},
}
for idx, test := range tests {
err := test.Input.LoadPrometheus()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := tc.Input.LoadPrometheus()
cstest.RequireErrorContains(t, err, tc.expectedErr)
isOk := assert.Equal(t, test.expectedResult, test.Input.Cscli.PrometheusUrl)
if !isOk {
t.Fatalf("test '%s' failed\n", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
require.Equal(t, tc.expectedURL, tc.Input.Cscli.PrometheusUrl)
})
}
}

View file

@ -228,7 +228,6 @@ teardown() {
assert_output --partial "Route"
assert_output --partial '/v1/watchers/login'
assert_output --partial "Local Api Metrics:"
}
@test "'cscli completion' with or without configuration file" {

60
test/bats/08_metrics.bats Normal file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env bats
# vim: ft=bats:list:ts=8:sts=4:sw=4:et:ai:si:
set -u
setup_file() {
load "../lib/setup_file.sh"
}
teardown_file() {
load "../lib/teardown_file.sh"
}
setup() {
load "../lib/setup.sh"
./instance-data load
}
teardown() {
./instance-crowdsec stop
}
#----------
@test "cscli metrics (crowdsec not running)" {
rune -1 cscli metrics
# crowdsec is down
assert_stderr --partial "failed to fetch prometheus metrics"
assert_stderr --partial "connect: connection refused"
}
@test "cscli metrics (bad configuration)" {
config_set '.prometheus.foo="bar"'
rune -1 cscli metrics
assert_stderr --partial "field foo not found in type csconfig.PrometheusCfg"
}
@test "cscli metrics (.prometheus.enabled=false)" {
config_set '.prometheus.enabled=false'
rune -1 cscli metrics
assert_stderr --partial "prometheus is not enabled, can't show metrics"
}
@test "cscli metrics (missing listen_addr)" {
config_set 'del(.prometheus.listen_addr)'
rune -1 cscli metrics
assert_stderr --partial "no prometheus url, please specify"
}
@test "cscli metrics (missing listen_port)" {
config_set 'del(.prometheus.listen_addr)'
rune -1 cscli metrics
assert_stderr --partial "no prometheus url, please specify"
}
@test "cscli metrics (missing prometheus section)" {
config_set 'del(.prometheus)'
rune -1 cscli metrics
assert_stderr --partial "prometheus section missing, can't show metrics"
}