diff --git a/collector/indices_settings.go b/collector/indices_settings.go index 141c2e69..9bcc2d70 100644 --- a/collector/indices_settings.go +++ b/collector/indices_settings.go @@ -32,11 +32,9 @@ type IndicesSettings struct { client *http.Client url *url.URL - up prometheus.Gauge readOnlyIndices prometheus.Gauge - totalScrapes, jsonParseFailures prometheus.Counter - metrics []*indicesSettingsMetric + metrics []*indicesSettingsMetric } var ( @@ -58,22 +56,11 @@ func NewIndicesSettings(logger *slog.Logger, client *http.Client, url *url.URL) client: client, url: url, - up: prometheus.NewGauge(prometheus.GaugeOpts{ - Name: prometheus.BuildFQName(namespace, "indices_settings_stats", "up"), - Help: "Was the last scrape of the Elasticsearch Indices Settings endpoint successful.", - }), - totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{ - Name: prometheus.BuildFQName(namespace, "indices_settings_stats", "total_scrapes"), - Help: "Current total Elasticsearch Indices Settings scrapes.", - }), readOnlyIndices: prometheus.NewGauge(prometheus.GaugeOpts{ Name: prometheus.BuildFQName(namespace, "indices_settings_stats", "read_only_indices"), Help: "Current number of read only indices within cluster", }), - jsonParseFailures: prometheus.NewCounter(prometheus.CounterOpts{ - Name: prometheus.BuildFQName(namespace, "indices_settings_stats", "json_parse_failures"), - Help: "Number of errors while parsing JSON.", - }), + metrics: []*indicesSettingsMetric{ { Type: prometheus.GaugeValue, @@ -126,10 +113,11 @@ func NewIndicesSettings(logger *slog.Logger, client *http.Client, url *url.URL) // Describe add Snapshots metrics descriptions func (cs *IndicesSettings) Describe(ch chan<- *prometheus.Desc) { - ch <- cs.up.Desc() - ch <- cs.totalScrapes.Desc() ch <- cs.readOnlyIndices.Desc() - ch <- cs.jsonParseFailures.Desc() + + for _, metric := range cs.metrics { + ch <- metric.Desc + } } func (cs *IndicesSettings) getAndParseURL(u *url.URL, data interface{}) error { @@ -155,12 +143,10 @@ func (cs *IndicesSettings) getAndParseURL(u *url.URL, data interface{}) error { bts, err := io.ReadAll(res.Body) if err != nil { - cs.jsonParseFailures.Inc() return err } if err := json.Unmarshal(bts, data); err != nil { - cs.jsonParseFailures.Inc() return err } return nil @@ -181,26 +167,15 @@ func (cs *IndicesSettings) fetchAndDecodeIndicesSettings() (IndicesSettingsRespo // Collect gets all indices settings metric values func (cs *IndicesSettings) Collect(ch chan<- prometheus.Metric) { - - cs.totalScrapes.Inc() - defer func() { - ch <- cs.up - ch <- cs.totalScrapes - ch <- cs.jsonParseFailures - ch <- cs.readOnlyIndices - }() - asr, err := cs.fetchAndDecodeIndicesSettings() if err != nil { cs.readOnlyIndices.Set(0) - cs.up.Set(0) cs.logger.Warn( "failed to fetch and decode cluster settings stats", "err", err, ) return } - cs.up.Set(1) var c int for indexName, value := range asr { @@ -217,4 +192,6 @@ func (cs *IndicesSettings) Collect(ch chan<- prometheus.Metric) { } } cs.readOnlyIndices.Set(float64(c)) + + ch <- cs.readOnlyIndices } diff --git a/collector/indices_settings_test.go b/collector/indices_settings_test.go index 3b68b0c7..c5abffd4 100644 --- a/collector/indices_settings_test.go +++ b/collector/indices_settings_test.go @@ -14,12 +14,16 @@ package collector import ( - "fmt" + "io" "net/http" "net/http/httptest" "net/url" + "os" + "path" + "strings" "testing" + "github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/common/promslog" ) @@ -54,53 +58,65 @@ func TestIndicesSettings(t *testing.T) { // curl http://localhost:9200/_all/_settings - tcs := map[string]string{ - "6.5.4": `{"viber":{"settings":{"index":{"creation_date":"1618593207186","number_of_shards":"5","number_of_replicas":"1","uuid":"lWg86KTARzO3r7lELytT1Q","version":{"created":"6050499"},"provided_name":"viber"}}},"instagram":{"settings":{"index":{"mapping":{"total_fields":{"limit":"10000"}},"number_of_shards":"5","blocks":{"read_only_allow_delete":"true"},"provided_name":"instagram","creation_date":"1618593203353","number_of_replicas":"1","uuid":"msb6eG7aT8GmNe-a4oyVtQ","version":{"created":"6050499"}}}},"twitter":{"settings":{"index":{"number_of_shards":"5","blocks":{"read_only_allow_delete":"true"},"provided_name":"twitter","creation_date":"1618593193641","number_of_replicas":"1","uuid":"YRUT8t4aSkKsNmGl7K3y4Q","version":{"created":"6050499"}}}},"facebook":{"settings":{"index":{"creation_date":"1618593199101","number_of_shards":"5","number_of_replicas":"1","uuid":"trZhb_YOTV-RWKitTYw81A","version":{"created":"6050499"},"provided_name":"facebook"}}}}`, + tests := []struct { + name string + file string + want string + }{ + { + name: "6.5.4", + file: "6.5.4.json", + want: `# HELP elasticsearch_indices_settings_creation_timestamp_seconds index setting creation_date + # TYPE elasticsearch_indices_settings_creation_timestamp_seconds gauge + elasticsearch_indices_settings_creation_timestamp_seconds{index="facebook"} 1.618593199101e+09 + elasticsearch_indices_settings_creation_timestamp_seconds{index="instagram"} 1.618593203353e+09 + elasticsearch_indices_settings_creation_timestamp_seconds{index="twitter"} 1.618593193641e+09 + elasticsearch_indices_settings_creation_timestamp_seconds{index="viber"} 1.618593207186e+09 + # HELP elasticsearch_indices_settings_replicas index setting number_of_replicas + # TYPE elasticsearch_indices_settings_replicas gauge + elasticsearch_indices_settings_replicas{index="facebook"} 1 + elasticsearch_indices_settings_replicas{index="instagram"} 1 + elasticsearch_indices_settings_replicas{index="twitter"} 1 + elasticsearch_indices_settings_replicas{index="viber"} 1 + # HELP elasticsearch_indices_settings_stats_read_only_indices Current number of read only indices within cluster + # TYPE elasticsearch_indices_settings_stats_read_only_indices gauge + elasticsearch_indices_settings_stats_read_only_indices 2 + # HELP elasticsearch_indices_settings_total_fields index mapping setting for total_fields + # TYPE elasticsearch_indices_settings_total_fields gauge + elasticsearch_indices_settings_total_fields{index="facebook"} 1000 + elasticsearch_indices_settings_total_fields{index="instagram"} 10000 + elasticsearch_indices_settings_total_fields{index="twitter"} 1000 + elasticsearch_indices_settings_total_fields{index="viber"} 1000 + `, + }, } - for ver, out := range tcs { - for hn, handler := range map[string]http.Handler{ - "plain": http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, out) - }), - } { - ts := httptest.NewServer(handler) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(path.Join("../fixtures/indices_settings", tt.file)) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + io.Copy(w, f) + })) defer ts.Close() u, err := url.Parse(ts.URL) if err != nil { - t.Fatalf("Failed to parse URL: %s", err) + t.Fatal(err) } + c := NewIndicesSettings(promslog.NewNopLogger(), http.DefaultClient, u) - nsr, err := c.fetchAndDecodeIndicesSettings() if err != nil { - t.Fatalf("Failed to fetch or decode indices settings: %s", err) - } - t.Logf("[%s/%s] All Indices Settings Response: %+v", hn, ver, nsr) - // if nsr.Cluster.Routing.Allocation.Enabled != "ALL" { - // t.Errorf("Wrong setting for cluster routing allocation enabled") - // } - var counter int - var totalFields int - for key, value := range nsr { - if value.Settings.IndexInfo.Blocks.ReadOnly == "true" { - counter++ - if key != "instagram" && key != "twitter" { - t.Errorf("Wrong read_only index") - } - } - if value.Settings.IndexInfo.Mapping.TotalFields.Limit == "10000" { - totalFields++ - if key != "instagram" { - t.Errorf("Expected 10000 total_fields only for instagram") - } - } + t.Fatal(err) } - if counter != 2 { - t.Errorf("Wrong number of read_only indexes") - } - if totalFields != 1 { - t.Errorf(("Wrong number of total_fields found")) + + if err := testutil.CollectAndCompare(c, strings.NewReader(tt.want)); err != nil { + t.Fatal(err) } - } + }) } } diff --git a/fixtures/indices_settings/6.5.4.json b/fixtures/indices_settings/6.5.4.json new file mode 100644 index 00000000..b0509b7b --- /dev/null +++ b/fixtures/indices_settings/6.5.4.json @@ -0,0 +1,69 @@ +{ + "viber": { + "settings": { + "index": { + "creation_date": "1618593207186", + "number_of_shards": "5", + "number_of_replicas": "1", + "uuid": "lWg86KTARzO3r7lELytT1Q", + "version": { + "created": "6050499" + }, + "provided_name": "viber" + } + } + }, + "instagram": { + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_shards": "5", + "blocks": { + "read_only_allow_delete": "true" + }, + "provided_name": "instagram", + "creation_date": "1618593203353", + "number_of_replicas": "1", + "uuid": "msb6eG7aT8GmNe-a4oyVtQ", + "version": { + "created": "6050499" + } + } + } + }, + "twitter": { + "settings": { + "index": { + "number_of_shards": "5", + "blocks": { + "read_only_allow_delete": "true" + }, + "provided_name": "twitter", + "creation_date": "1618593193641", + "number_of_replicas": "1", + "uuid": "YRUT8t4aSkKsNmGl7K3y4Q", + "version": { + "created": "6050499" + } + } + } + }, + "facebook": { + "settings": { + "index": { + "creation_date": "1618593199101", + "number_of_shards": "5", + "number_of_replicas": "1", + "uuid": "trZhb_YOTV-RWKitTYw81A", + "version": { + "created": "6050499" + }, + "provided_name": "facebook" + } + } + } +}