45a4057f60
Using the newly provided state store methods, we periodically emit usage metrics from the servers. We decided to emit these metrics from all servers, not just the leader, because that means we do not have to care about leader election flapping causing metrics turbulence, and it seems reasonable for each server to emit its own view of the state, even if they should always converge rapidly.
129 lines
4.1 KiB
Go
129 lines
4.1 KiB
Go
package usagemetrics
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/armon/go-metrics"
|
|
"github.com/hashicorp/consul/agent/consul/state"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type mockStateProvider struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockStateProvider) State() *state.Store {
|
|
retValues := m.Called()
|
|
return retValues.Get(0).(*state.Store)
|
|
}
|
|
|
|
func TestUsageReporter_Run(t *testing.T) {
|
|
type testCase struct {
|
|
modfiyStateStore func(t *testing.T, s *state.Store)
|
|
expectedGauges map[string]metrics.GaugeValue
|
|
}
|
|
cases := map[string]testCase{
|
|
"empty-state": {
|
|
expectedGauges: map[string]metrics.GaugeValue{
|
|
"consul.usage.test.consul.state.nodes;datacenter=dc1": {
|
|
Name: "consul.usage.test.consul.state.nodes",
|
|
Value: 0,
|
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
},
|
|
"consul.usage.test.consul.state.services;datacenter=dc1": {
|
|
Name: "consul.usage.test.consul.state.services",
|
|
Value: 0,
|
|
Labels: []metrics.Label{
|
|
{Name: "datacenter", Value: "dc1"},
|
|
},
|
|
},
|
|
"consul.usage.test.consul.state.service_instances;datacenter=dc1": {
|
|
Name: "consul.usage.test.consul.state.service_instances",
|
|
Value: 0,
|
|
Labels: []metrics.Label{
|
|
{Name: "datacenter", Value: "dc1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"nodes-and-services": {
|
|
modfiyStateStore: func(t *testing.T, s *state.Store) {
|
|
require.Nil(t, s.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}))
|
|
require.Nil(t, s.EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.2"}))
|
|
require.Nil(t, s.EnsureNode(3, &structs.Node{Node: "baz", Address: "127.0.0.2"}))
|
|
|
|
// Typical services and some consul services spread across two nodes
|
|
require.Nil(t, s.EnsureService(4, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: nil, Address: "", Port: 5000}))
|
|
require.Nil(t, s.EnsureService(5, "bar", &structs.NodeService{ID: "api", Service: "api", Tags: nil, Address: "", Port: 5000}))
|
|
require.Nil(t, s.EnsureService(6, "foo", &structs.NodeService{ID: "consul", Service: "consul", Tags: nil}))
|
|
require.Nil(t, s.EnsureService(7, "bar", &structs.NodeService{ID: "consul", Service: "consul", Tags: nil}))
|
|
},
|
|
expectedGauges: map[string]metrics.GaugeValue{
|
|
"consul.usage.test.consul.state.nodes;datacenter=dc1": {
|
|
Name: "consul.usage.test.consul.state.nodes",
|
|
Value: 3,
|
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
},
|
|
"consul.usage.test.consul.state.services;datacenter=dc1": {
|
|
Name: "consul.usage.test.consul.state.services",
|
|
Value: 3,
|
|
Labels: []metrics.Label{
|
|
{Name: "datacenter", Value: "dc1"},
|
|
},
|
|
},
|
|
"consul.usage.test.consul.state.service_instances;datacenter=dc1": {
|
|
Name: "consul.usage.test.consul.state.service_instances",
|
|
Value: 4,
|
|
Labels: []metrics.Label{
|
|
{Name: "datacenter", Value: "dc1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, tcase := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Only have a single interval for the test
|
|
sink := metrics.NewInmemSink(1*time.Minute, 1*time.Minute)
|
|
cfg := metrics.DefaultConfig("consul.usage.test")
|
|
cfg.EnableHostname = false
|
|
metrics.NewGlobal(cfg, sink)
|
|
|
|
mockStateProvider := &mockStateProvider{}
|
|
s, err := newStateStore()
|
|
require.NoError(t, err)
|
|
if tcase.modfiyStateStore != nil {
|
|
tcase.modfiyStateStore(t, s)
|
|
}
|
|
mockStateProvider.On("State").Return(s)
|
|
|
|
reporter, err := NewUsageMetricsReporter(
|
|
new(Config).
|
|
WithStateProvider(mockStateProvider).
|
|
WithLogger(testutil.Logger(t)).
|
|
WithDatacenter("dc1"),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
reporter.runOnce()
|
|
|
|
intervals := sink.Data()
|
|
require.Len(t, intervals, 1)
|
|
intv := intervals[0]
|
|
|
|
// Range over the expected values instead of just doing an Equal
|
|
// comparison on the maps because of different metrics emitted between
|
|
// OSS and Ent. The enterprise tests have a full equality comparison on
|
|
// the maps.
|
|
for key, expected := range tcase.expectedGauges {
|
|
require.Equal(t, expected, intv.Gauges[key])
|
|
}
|
|
})
|
|
}
|
|
}
|