CI: add tests for metrics configuration (#2251)
This commit is contained in:
parent
9ccdddaab1
commit
3cc6b2c0d0
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
60
test/bats/08_metrics.bats
Normal 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"
|
||||
}
|
Loading…
Reference in a new issue