2021-09-21 13:50:37 -05:00
|
|
|
package service
|
2018-05-25 06:32:55 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-01-06 09:57:31 -06:00
|
|
|
"context"
|
2022-04-08 06:41:26 -05:00
|
|
|
"encoding/json"
|
2020-12-16 05:44:33 -06:00
|
|
|
"errors"
|
2022-08-10 08:37:51 -05:00
|
|
|
"io"
|
2021-05-27 03:53:10 -05:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2018-05-25 06:32:55 -05:00
|
|
|
"runtime"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2022-01-14 03:16:07 -06:00
|
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
2022-10-19 08:02:15 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
2021-09-15 10:47:44 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
2022-10-19 08:02:15 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
2021-09-21 13:50:37 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
2023-05-23 09:29:20 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
2023-01-18 09:07:36 -06:00
|
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
2023-02-06 10:50:03 -06:00
|
|
|
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
2018-05-25 06:32:55 -05:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
|
|
)
|
|
|
|
|
2020-12-16 09:12:02 -06:00
|
|
|
// This is to ensure that the interface contract is held by the implementation
|
|
|
|
func Test_InterfaceContractValidity(t *testing.T) {
|
2021-09-21 13:50:37 -05:00
|
|
|
newUsageStats := func() usagestats.Service {
|
|
|
|
return &UsageStats{}
|
2020-12-16 09:12:02 -06:00
|
|
|
}
|
2021-09-21 13:50:37 -05:00
|
|
|
v, ok := newUsageStats().(*UsageStats)
|
2020-12-16 09:12:02 -06:00
|
|
|
|
|
|
|
assert.NotNil(t, v)
|
|
|
|
assert.True(t, ok)
|
|
|
|
}
|
|
|
|
|
2018-05-25 06:32:55 -05:00
|
|
|
func TestMetrics(t *testing.T) {
|
2022-04-08 06:41:26 -05:00
|
|
|
const metricName = "stats.test_metric.count"
|
2018-05-25 06:32:55 -05:00
|
|
|
|
2022-10-19 08:02:15 -05:00
|
|
|
sqlStore := dbtest.NewFakeDB()
|
2023-05-23 09:29:20 -05:00
|
|
|
uss := createService(t, sqlStore, false)
|
2021-03-17 04:14:53 -05:00
|
|
|
|
2023-08-30 10:46:47 -05:00
|
|
|
uss.RegisterMetricsFunc(func(context.Context) (map[string]any, error) {
|
|
|
|
return map[string]any{metricName: 1}, nil
|
2022-04-08 06:41:26 -05:00
|
|
|
})
|
2018-09-06 14:03:09 -05:00
|
|
|
|
2022-09-26 08:10:54 -05:00
|
|
|
_, err := uss.sendUsageStats(context.Background())
|
2022-04-08 06:41:26 -05:00
|
|
|
require.NoError(t, err)
|
2020-04-02 01:45:04 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
t.Run("Given reporting not enabled and sending usage stats", func(t *testing.T) {
|
|
|
|
origSendUsageStats := sendUsageStats
|
|
|
|
t.Cleanup(func() {
|
|
|
|
sendUsageStats = origSendUsageStats
|
|
|
|
})
|
|
|
|
statsSent := false
|
2022-09-26 08:10:54 -05:00
|
|
|
sendUsageStats = func(uss *UsageStats, ctx context.Context, b *bytes.Buffer) error {
|
2022-04-08 06:41:26 -05:00
|
|
|
statsSent = true
|
2022-09-26 08:10:54 -05:00
|
|
|
return nil
|
2018-09-12 12:27:21 -05:00
|
|
|
}
|
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
uss.Cfg.ReportingEnabled = false
|
2022-09-26 08:10:54 -05:00
|
|
|
_, err := uss.sendUsageStats(context.Background())
|
2020-12-15 02:32:06 -06:00
|
|
|
require.NoError(t, err)
|
2018-05-25 06:32:55 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
require.False(t, statsSent)
|
|
|
|
})
|
2018-05-25 06:32:55 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
t.Run("Given reporting enabled, stats should be gathered and sent to HTTP endpoint", func(t *testing.T) {
|
|
|
|
origCfg := uss.Cfg
|
|
|
|
t.Cleanup(func() {
|
|
|
|
uss.Cfg = origCfg
|
2018-05-25 06:32:55 -05:00
|
|
|
})
|
2022-04-08 06:41:26 -05:00
|
|
|
uss.Cfg = &setting.Cfg{
|
|
|
|
ReportingEnabled: true,
|
|
|
|
BuildVersion: "5.0.0",
|
|
|
|
AnonymousEnabled: true,
|
|
|
|
BasicAuthEnabled: true,
|
2023-03-22 12:41:59 -05:00
|
|
|
LDAPAuthEnabled: true,
|
2022-04-08 06:41:26 -05:00
|
|
|
AuthProxyEnabled: true,
|
|
|
|
Packaging: "deb",
|
|
|
|
ReportingDistributor: "hosted-grafana",
|
|
|
|
}
|
2018-05-25 06:32:55 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
ch := make(chan httpResp)
|
|
|
|
ticker := time.NewTicker(2 * time.Second)
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
2022-08-10 08:37:51 -05:00
|
|
|
buf, err := io.ReadAll(r.Body)
|
2022-04-08 06:41:26 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Logf("Fake HTTP handler received an error: %s", err.Error())
|
2021-03-10 03:14:00 -06:00
|
|
|
ch <- httpResp{
|
2022-04-08 06:41:26 -05:00
|
|
|
err: err,
|
2021-03-10 03:14:00 -06:00
|
|
|
}
|
2022-04-08 06:41:26 -05:00
|
|
|
return
|
2021-03-10 03:14:00 -06:00
|
|
|
}
|
2022-04-08 06:41:26 -05:00
|
|
|
require.NoError(t, err, "Failed to read response body, err=%v", err)
|
|
|
|
t.Logf("Fake HTTP handler received a response")
|
|
|
|
ch <- httpResp{
|
|
|
|
responseBuffer: bytes.NewBuffer(buf),
|
|
|
|
req: r,
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
t.Cleanup(ts.Close)
|
|
|
|
t.Cleanup(func() {
|
|
|
|
close(ch)
|
2018-05-25 06:32:55 -05:00
|
|
|
})
|
2022-04-08 06:41:26 -05:00
|
|
|
usageStatsURL = ts.URL
|
2022-02-11 07:04:15 -06:00
|
|
|
|
2022-09-26 08:10:54 -05:00
|
|
|
go func() {
|
|
|
|
_, err := uss.sendUsageStats(context.Background())
|
|
|
|
require.NoError(t, err)
|
|
|
|
}()
|
2019-09-17 02:32:24 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
// Wait for fake HTTP server to receive a request
|
|
|
|
var resp httpResp
|
|
|
|
select {
|
|
|
|
case resp = <-ch:
|
|
|
|
require.NoError(t, resp.err, "Fake server experienced an error")
|
|
|
|
case <-ticker.C:
|
|
|
|
t.Fatalf("Timed out waiting for HTTP request")
|
|
|
|
}
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
t.Logf("Received response from fake HTTP server: %+v\n", resp)
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
assert.NotNil(t, resp.req)
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
assert.Equal(t, http.MethodPost, resp.req.Method)
|
|
|
|
assert.Equal(t, "application/json", resp.req.Header.Get("Content-Type"))
|
2019-09-17 02:32:24 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
require.NotNil(t, resp.responseBuffer)
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2023-08-30 10:46:47 -05:00
|
|
|
j := make(map[string]any)
|
2022-04-08 06:41:26 -05:00
|
|
|
err = json.Unmarshal(resp.responseBuffer.Bytes(), &j)
|
|
|
|
require.NoError(t, err)
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
assert.Equal(t, "5_0_0", j["version"])
|
|
|
|
assert.Equal(t, runtime.GOOS, j["os"])
|
|
|
|
assert.Equal(t, runtime.GOARCH, j["arch"])
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
usageId := uss.GetUsageStatsId(context.Background())
|
|
|
|
assert.NotEmpty(t, usageId)
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2023-08-30 10:46:47 -05:00
|
|
|
metrics, ok := j["metrics"].(map[string]any)
|
2022-04-08 06:41:26 -05:00
|
|
|
require.True(t, ok)
|
|
|
|
assert.EqualValues(t, 1, metrics[metricName])
|
2020-12-16 05:44:33 -06:00
|
|
|
})
|
2022-04-08 06:41:26 -05:00
|
|
|
}
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
func TestGetUsageReport_IncludesMetrics(t *testing.T) {
|
2022-10-19 08:02:15 -05:00
|
|
|
sqlStore := dbtest.NewFakeDB()
|
2023-05-23 09:29:20 -05:00
|
|
|
uss := createService(t, sqlStore, true)
|
2022-04-08 06:41:26 -05:00
|
|
|
metricName := "stats.test_metric.count"
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2023-08-30 10:46:47 -05:00
|
|
|
uss.RegisterMetricsFunc(func(context.Context) (map[string]any, error) {
|
|
|
|
return map[string]any{metricName: 1}, nil
|
2022-04-08 06:41:26 -05:00
|
|
|
})
|
2021-01-06 09:57:31 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
report, err := uss.GetUsageReport(context.Background())
|
|
|
|
require.NoError(t, err, "Expected no error")
|
2021-01-06 09:57:31 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
metric := report.Metrics[metricName]
|
|
|
|
assert.Equal(t, 1, metric)
|
|
|
|
}
|
2021-01-06 09:57:31 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
func TestRegisterMetrics(t *testing.T) {
|
|
|
|
const goodMetricName = "stats.test_external_metric.count"
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-10-19 08:02:15 -05:00
|
|
|
sqlStore := dbtest.NewFakeDB()
|
2023-05-23 09:29:20 -05:00
|
|
|
uss := createService(t, sqlStore, false)
|
2023-08-30 10:46:47 -05:00
|
|
|
metrics := map[string]any{"stats.test_metric.count": 1, "stats.test_metric_second.count": 2}
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2023-08-30 10:46:47 -05:00
|
|
|
uss.RegisterMetricsFunc(func(context.Context) (map[string]any, error) {
|
|
|
|
return map[string]any{goodMetricName: 1}, nil
|
2022-04-08 06:41:26 -05:00
|
|
|
})
|
2022-04-08 05:42:33 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
{
|
|
|
|
extMetrics, err := uss.externalMetrics[0](context.Background())
|
|
|
|
require.NoError(t, err)
|
2023-08-30 10:46:47 -05:00
|
|
|
assert.Equal(t, map[string]any{goodMetricName: 1}, extMetrics)
|
2022-04-08 06:41:26 -05:00
|
|
|
}
|
2022-04-08 05:42:33 -05:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
uss.gatherMetrics(context.Background(), metrics)
|
|
|
|
assert.Equal(t, 1, metrics[goodMetricName])
|
2022-06-01 05:27:06 -05:00
|
|
|
metricsCount := len(metrics)
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
t.Run("do not add metrics that return an error when fetched", func(t *testing.T) {
|
|
|
|
const badMetricName = "stats.test_external_metric_error.count"
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2023-08-30 10:46:47 -05:00
|
|
|
uss.RegisterMetricsFunc(func(context.Context) (map[string]any, error) {
|
|
|
|
return map[string]any{badMetricName: 1}, errors.New("some error")
|
2021-03-10 03:14:00 -06:00
|
|
|
})
|
2022-04-08 06:41:26 -05:00
|
|
|
uss.gatherMetrics(context.Background(), metrics)
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
extErrorMetric := metrics[badMetricName]
|
|
|
|
extMetric := metrics[goodMetricName]
|
2020-12-16 05:44:33 -06:00
|
|
|
|
2022-04-08 06:41:26 -05:00
|
|
|
require.Nil(t, extErrorMetric, "Invalid metric should not be added")
|
|
|
|
assert.Equal(t, 1, extMetric)
|
2022-06-01 05:27:06 -05:00
|
|
|
assert.Len(t, metrics, metricsCount, "Expected same number of metrics before and after collecting bad metric")
|
|
|
|
assert.EqualValues(t, 1, metrics["stats.usagestats.debug.collect.error.count"])
|
2019-09-17 02:32:24 -05:00
|
|
|
})
|
2018-05-25 06:32:55 -05:00
|
|
|
}
|
|
|
|
|
2021-03-10 03:14:00 -06:00
|
|
|
type httpResp struct {
|
|
|
|
req *http.Request
|
|
|
|
responseBuffer *bytes.Buffer
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2023-05-23 09:29:20 -05:00
|
|
|
func createService(t *testing.T, sqlStore db.DB, withDB bool) *UsageStats {
|
2021-03-10 03:14:00 -06:00
|
|
|
t.Helper()
|
2022-02-11 07:04:15 -06:00
|
|
|
if withDB {
|
2022-10-19 08:02:15 -05:00
|
|
|
sqlStore = db.InitTestDB(t)
|
2022-02-11 07:04:15 -06:00
|
|
|
}
|
2022-04-08 06:41:26 -05:00
|
|
|
|
2023-05-23 09:29:20 -05:00
|
|
|
cfg := setting.NewCfg()
|
2023-01-18 09:07:36 -06:00
|
|
|
service, _ := ProvideService(
|
2023-05-23 09:29:20 -05:00
|
|
|
cfg,
|
2022-04-08 06:41:26 -05:00
|
|
|
kvstore.ProvideService(sqlStore),
|
|
|
|
routing.NewRouteRegister(),
|
2022-09-26 08:10:54 -05:00
|
|
|
tracing.InitializeTracerForTest(),
|
2023-05-23 09:29:20 -05:00
|
|
|
acimpl.ProvideAccessControl(cfg),
|
2023-01-18 09:07:36 -06:00
|
|
|
actest.FakeService{},
|
2023-02-06 10:50:03 -06:00
|
|
|
supportbundlestest.NewFakeBundleService(),
|
2022-04-08 06:41:26 -05:00
|
|
|
)
|
2023-01-18 09:07:36 -06:00
|
|
|
|
|
|
|
return service
|
2021-03-10 03:14:00 -06:00
|
|
|
}
|