pkg/collectors: Reintroduce help text

This commit is contained in:
Max Leonard Inden 2018-11-05 17:13:27 +00:00
parent d44a8bc991
commit 581c49d593
No known key found for this signature in database
GPG Key ID: 5403C5464810BC26
8 changed files with 79 additions and 43 deletions

View File

@ -253,7 +253,7 @@ func (m *metricHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, c := range m.c {
for _, m := range c.Collect() {
_, err := fmt.Fprint(writer, *m)
_, err := fmt.Fprint(writer, m)
if err != nil {
// TODO: Handle panic
panic(err)

View File

@ -69,6 +69,11 @@ func BenchmarkKubeStateMetrics(t *testing.B) {
for i := 0; i < requestCount; i++ {
w = httptest.NewRecorder()
handler.ServeHTTP(w, req)
resp := w.Result()
if resp.StatusCode != 200 {
t.Fatalf("expected 200 status code but got %v", resp.StatusCode)
}
}
// resp := w.Result()

View File

@ -199,7 +199,10 @@ var availableCollectors = map[string]func(f *Builder) *Collector{
func (b *Builder) buildServiceCollector() *Collector {
filteredMetricFamilies := filterMetricFamilies(b.metricWhitelist, b.metricBlacklist, serviceMetricFamilies)
helpTexts := extractHelpText(filteredMetricFamilies)
store := metricsstore.NewMetricsStore(
helpTexts,
composeMetricGenFuncs(filteredMetricFamilies),
)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Service{}, store, b.namespaces, createServiceListWatch)
@ -207,28 +210,29 @@ func (b *Builder) buildServiceCollector() *Collector {
return NewCollector(store)
}
// func (b *Builder) buildNodeCollector() *Collector {
// genFunc := func(obj interface{}) []*metrics.Metric {
// return generateNodeMetrics(b.opts.DisableNodeNonGenericResourceMetrics, obj)
// }
//
// return newCollector(store)
// }
func extractHelpText(families []metrics.MetricFamily) []string {
help := make([]string, len(families))
for i, f := range families {
help[i] = f.Help
}
return help
}
// composeMetricGenFuncs takes a slice of metric families and returns a function
// that composes their metric generation functions into a single one.
func composeMetricGenFuncs(families []metrics.MetricFamily) func(obj interface{}) []*metrics.Metric {
func composeMetricGenFuncs(families []metrics.MetricFamily) func(obj interface{}) [][]*metrics.Metric {
funcs := []func(obj interface{}) []*metrics.Metric{}
for _, f := range families {
funcs = append(funcs, f.GenerateFunc)
}
return func(obj interface{}) []*metrics.Metric {
metrics := []*metrics.Metric{}
return func(obj interface{}) [][]*metrics.Metric {
metrics := make([][]*metrics.Metric, len(funcs))
for _, f := range funcs {
metrics = append(metrics, f(obj)...)
for i, f := range funcs {
metrics[i] = f(obj)
}
return metrics

View File

@ -16,12 +16,10 @@ limitations under the License.
package collectors
import (
"k8s.io/kube-state-metrics/pkg/metrics"
)
import ()
type Store interface {
GetAll() []*metrics.Metric
GetAll() []string
}
// Collector represents a kube-state-metrics metric collector. It is stripped
@ -35,6 +33,6 @@ func NewCollector(s Store) *Collector {
}
// Collect returns all metrics of the underlying store of the collector.
func (c *Collector) Collect() []*metrics.Metric {
func (c *Collector) Collect() []string {
return c.Store.GetAll()
}

View File

@ -31,11 +31,16 @@ type generateMetricsTestCase struct {
Obj interface{}
MetricNames []string
Want string
Func func(interface{}) []*metrics.Metric
Func func(interface{}) [][]*metrics.Metric
}
func (testCase *generateMetricsTestCase) run() error {
metrics := testCase.Func(testCase.Obj)
metricsByFamily := testCase.Func(testCase.Obj)
metrics := []*metrics.Metric{}
for _, m := range metricsByFamily {
metrics = append(metrics, m...)
}
metrics = filterMetrics(metrics, testCase.MetricNames)

View File

@ -9,21 +9,34 @@ import (
"k8s.io/apimachinery/pkg/types"
)
var (
helpPrefix = "# HELP "
)
// MetricsStore implements the k8s.io/kubernetes/client-go/tools/cache.Store
// interface. Instead of storing entire Kubernetes objects, it stores metrics
// generated based on them.
type MetricsStore struct {
mutex sync.RWMutex
metrics map[types.UID][]*metrics.Metric
// Protects metrics
mutex sync.RWMutex
// metrics is a map indexed by Kubernetes object id, containing a slice of
// metric families, containing a slice of metrics. We need to keep metrics
// grouped by metric families in order to zip families with their help text in
// MetricsStore.WriteAll().
metrics map[types.UID][][]*metrics.Metric
// helpTexts is later on zipped with with their corresponding metric
// families in MetricStore.WriteAll().
helpTexts []string
generateMetricsFunc func(interface{}) []*metrics.Metric
generateMetricsFunc func(interface{}) [][]*metrics.Metric
}
// NewMetricsStore returns a new MetricsStore
func NewMetricsStore(generateFunc func(interface{}) []*metrics.Metric) *MetricsStore {
func NewMetricsStore(helpTexts []string, generateFunc func(interface{}) [][]*metrics.Metric) *MetricsStore {
return &MetricsStore{
generateMetricsFunc: generateFunc,
metrics: map[types.UID][]*metrics.Metric{},
helpTexts: helpTexts,
metrics: map[types.UID][][]*metrics.Metric{},
}
}
@ -87,7 +100,7 @@ func (s *MetricsStore) GetByKey(key string) (item interface{}, exists bool, err
// TODO: What is 'name' for?
func (s *MetricsStore) Replace(list []interface{}, name string) error {
s.mutex.Lock()
s.metrics = map[types.UID][]*metrics.Metric{}
s.metrics = map[types.UID][][]*metrics.Metric{}
s.mutex.Unlock()
for _, o := range list {
@ -104,15 +117,22 @@ func (s *MetricsStore) Resync() error {
return nil
}
func (s *MetricsStore) GetAll() []*metrics.Metric {
// GetAll returns all metrics of the store, zipped with the help text of each
// metric family.
func (s *MetricsStore) GetAll() []string {
metrics := []string{}
s.mutex.RLock()
defer s.mutex.RUnlock()
m := make([]*metrics.Metric, 0, len(s.metrics))
for _, metrics := range s.metrics {
m = append(m, metrics...)
for i, help := range s.helpTexts {
metrics = append(metrics, helpPrefix+help+"\n")
for _, metricsPerObject := range s.metrics {
for _, metric := range metricsPerObject[i] {
metrics = append(metrics, string(*metric))
}
}
}
return m
return metrics
}

View File

@ -15,18 +15,19 @@ import (
func TestObjectsSameNameDifferentNamespaces(t *testing.T) {
serviceIDS := []string{"a", "b"}
genFunc := func(obj interface{}) []*metrics.Metric {
genFunc := func(obj interface{}) [][]*metrics.Metric {
o, err := meta.Accessor(obj)
if err != nil {
t.Fatal(err)
}
metric := metrics.Metric(fmt.Sprintf("kube_service_info{uid=\"%v\"} 1\n", o.GetUID()))
metricFamily := []*metrics.Metric{&metric}
return []*metrics.Metric{&metric}
return [][]*metrics.Metric{metricFamily}
}
ms := NewMetricsStore(genFunc)
ms := NewMetricsStore([]string{"Information about service."}, genFunc)
for _, id := range serviceIDS {
s := v1.Service{
@ -45,14 +46,15 @@ func TestObjectsSameNameDifferentNamespaces(t *testing.T) {
metrics := ms.GetAll()
if len(metrics) != 2 {
// Expect 3 lines, HELP + 2 metrics
if len(metrics) != 3 {
t.Fatalf("expected 2 metrics but got %v", len(metrics))
}
for _, id := range serviceIDS {
found := false
for _, m := range metrics {
if strings.Contains(string(*m), fmt.Sprintf("uid=\"%v\"", id)) {
if strings.Contains(m, fmt.Sprintf("uid=\"%v\"", id)) {
found = true
}
}

View File

@ -40,17 +40,17 @@ func TestAsLibrary(t *testing.T) {
m := c.Collect()
if len(m) != 1 {
t.Fatalf("expected one metric to be returned but got %v", len(m))
if len(m) != 2 {
t.Fatalf("expected HELP line and one metric to be returned but got %v", len(m))
}
if !strings.Contains(string(*m[0]), service.ObjectMeta.Name) {
if !strings.Contains(string(m[1]), service.ObjectMeta.Name) {
t.Fatal("expected string to contain service name")
}
}
func serviceCollector(kubeClient clientset.Interface) *collectors.Collector {
store := metricsstore.NewMetricsStore(generateServiceMetrics)
store := metricsstore.NewMetricsStore([]string{"test_metric describes a test metric"}, generateServiceMetrics)
lw := cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
@ -68,7 +68,7 @@ func serviceCollector(kubeClient clientset.Interface) *collectors.Collector {
return collectors.NewCollector(store)
}
func generateServiceMetrics(obj interface{}) []*metrics.Metric {
func generateServiceMetrics(obj interface{}) [][]*metrics.Metric {
sPointer := obj.(*v1.Service)
s := *sPointer
@ -77,5 +77,7 @@ func generateServiceMetrics(obj interface{}) []*metrics.Metric {
panic(err)
}
return []*metrics.Metric{m}
ms := []*metrics.Metric{m}
return [][]*metrics.Metric{ms}
}