diff --git a/internal/collector/api.go b/internal/collector/api.go index ad3154d..821a091 100644 --- a/internal/collector/api.go +++ b/internal/collector/api.go @@ -48,7 +48,6 @@ func (collector *Icinga2APICollector) Collect(ch chan<- prometheus.Metric) { return } - // TODO: Use a custom unmarshal to avoid this r := result.Results[0] // There might be a better way var perfdata = make(map[string]float64, len(r.Perfdata)) diff --git a/internal/collector/application.go b/internal/collector/application.go index 5737ae2..1648a31 100644 --- a/internal/collector/application.go +++ b/internal/collector/application.go @@ -46,7 +46,6 @@ func (collector *Icinga2ApplicationCollector) Collect(ch chan<- prometheus.Metri return } - // TODO: Use a custom unmarshal to avoid this r := result.Results[0] collector.info.Reset() diff --git a/internal/collector/cib.go b/internal/collector/cib.go index d36ad99..61e9ef4 100644 --- a/internal/collector/cib.go +++ b/internal/collector/cib.go @@ -69,19 +69,39 @@ func (collector *Icinga2CIBCollector) Collect(ch chan<- prometheus.Metric) { return } - // TODO: Use a custom unmarshal to avoid this r := result.Results[0] - // TODO: We should make sure the keys exist - ch <- prometheus.MustNewConstMetric(collector.uptime, prometheus.CounterValue, r.Status["uptime"]) - ch <- prometheus.MustNewConstMetric(collector.num_hosts_up, prometheus.GaugeValue, r.Status["num_hosts_up"]) - ch <- prometheus.MustNewConstMetric(collector.num_hosts_down, prometheus.GaugeValue, r.Status["num_hosts_down"]) - ch <- prometheus.MustNewConstMetric(collector.num_services_ok, prometheus.GaugeValue, r.Status["num_services_ok"]) - ch <- prometheus.MustNewConstMetric(collector.num_services_critical, prometheus.GaugeValue, r.Status["num_services_critical"]) - ch <- prometheus.MustNewConstMetric(collector.avg_execution_time, prometheus.GaugeValue, r.Status["avg_execution_time"]) - ch <- prometheus.MustNewConstMetric(collector.avg_latency, prometheus.GaugeValue, r.Status["avg_latency"]) - ch <- prometheus.MustNewConstMetric(collector.max_execution_time, prometheus.GaugeValue, r.Status["max_execution_time"]) - ch <- prometheus.MustNewConstMetric(collector.max_latency, prometheus.GaugeValue, r.Status["max_latency"]) - ch <- prometheus.MustNewConstMetric(collector.min_execution_time, prometheus.GaugeValue, r.Status["min_execution_time"]) - ch <- prometheus.MustNewConstMetric(collector.min_latency, prometheus.GaugeValue, r.Status["min_latency"]) + if v, ok := r.Status["uptime"]; ok { + ch <- prometheus.MustNewConstMetric(collector.uptime, prometheus.CounterValue, v) + } + if v, ok := r.Status["num_hosts_up"]; ok { + ch <- prometheus.MustNewConstMetric(collector.num_hosts_up, prometheus.GaugeValue, v) + } + if v, ok := r.Status["num_hosts_down"]; ok { + ch <- prometheus.MustNewConstMetric(collector.num_hosts_down, prometheus.GaugeValue, v) + } + if v, ok := r.Status["num_services_ok"]; ok { + ch <- prometheus.MustNewConstMetric(collector.num_services_ok, prometheus.GaugeValue, v) + } + if v, ok := r.Status["num_services_critical"]; ok { + ch <- prometheus.MustNewConstMetric(collector.num_services_critical, prometheus.GaugeValue, v) + } + if v, ok := r.Status["avg_execution_time"]; ok { + ch <- prometheus.MustNewConstMetric(collector.avg_execution_time, prometheus.GaugeValue, v) + } + if v, ok := r.Status["avg_latency"]; ok { + ch <- prometheus.MustNewConstMetric(collector.avg_latency, prometheus.GaugeValue, v) + } + if v, ok := r.Status["max_execution_time"]; ok { + ch <- prometheus.MustNewConstMetric(collector.max_execution_time, prometheus.GaugeValue, v) + } + if v, ok := r.Status["max_latency"]; ok { + ch <- prometheus.MustNewConstMetric(collector.max_latency, prometheus.GaugeValue, v) + } + if v, ok := r.Status["min_execution_time"]; ok { + ch <- prometheus.MustNewConstMetric(collector.min_execution_time, prometheus.GaugeValue, v) + } + if v, ok := r.Status["min_latency"]; ok { + ch <- prometheus.MustNewConstMetric(collector.min_latency, prometheus.GaugeValue, v) + } } diff --git a/internal/icinga/client_test.go b/internal/icinga/client_test.go index 7f09ae8..604aaff 100644 --- a/internal/icinga/client_test.go +++ b/internal/icinga/client_test.go @@ -12,6 +12,8 @@ import ( const ( icingaTestDataCIB1 = "testdata/cib1.json" + icingaTestDataAPP1 = "testdata/app1.json" + icingaTestDataAPI1 = "testdata/api1.json" ) func loadTestdata(filepath string) []byte { @@ -80,3 +82,106 @@ func Test_GetCIBMetrics(t *testing.T) { }) } } + +func Test_GetApplicationMetrics(t *testing.T) { + testcases := map[string]struct { + expected ApplicationResult + server *httptest.Server + }{ + "application": { + server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write(loadTestdata(icingaTestDataAPP1)) + })), + expected: ApplicationResult{ + Results: []struct { + Name string `json:"name"` + Status struct { + IcingaApplication IcingaApplication `json:"icingaapplication"` + } `json:"status"` + }{ + { + Name: "IcingaApplication", + Status: struct { + IcingaApplication IcingaApplication `json:"icingaapplication"` + }{ + IcingaApplication: IcingaApplication{ + App: App{ + EnableEventHandlers: true, + Version: "r2.15.2-1", + }, + }, + }, + }, + }, + }, + }, + } + + for name, test := range testcases { + t.Run(name, func(t *testing.T) { + defer test.server.Close() + + cfg := testConfig(test.server) + + cli, _ := NewClient(cfg) + + actual, err := cli.GetApplicationMetrics() + + if err != nil { + t.Fatalf("did not expect error got:\n %+v", err) + } + + if !reflect.DeepEqual(test.expected, actual) { + t.Fatalf("expected:\n %+v \ngot:\n %+v", test.expected, actual) + } + }) + } +} + +func Test_GetApiListenerMetrics(t *testing.T) { + testcases := map[string]struct { + expected APIResult + server *httptest.Server + }{ + "application": { + server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write(loadTestdata(icingaTestDataAPI1)) + })), + expected: APIResult{ + Results: []struct { + Name string `json:"name"` + Perfdata []Perfdata `json:"perfdata,omitempty"` + }{ + { + Name: "ApiListener", + Perfdata: []Perfdata{ + {Label: "api_num_conn_endpoints", Value: 11}, + }, + }, + }, + }, + }, + } + + for name, test := range testcases { + t.Run(name, func(t *testing.T) { + defer test.server.Close() + + cfg := testConfig(test.server) + + cli, _ := NewClient(cfg) + + actual, err := cli.GetApiListenerMetrics() + + if err != nil { + t.Fatalf("did not expect error got:\n %+v", err) + } + + if !reflect.DeepEqual(test.expected, actual) { + t.Fatalf("expected:\n %+v \ngot:\n %+v", test.expected, actual) + } + }) + } +} diff --git a/internal/icinga/testdata/api1.json b/internal/icinga/testdata/api1.json new file mode 100644 index 0000000..be0726a --- /dev/null +++ b/internal/icinga/testdata/api1.json @@ -0,0 +1,51 @@ +{ + "results": [ + { + "name": "ApiListener", + "perfdata": [ + { + "counter": false, + "crit": null, + "label": "api_num_conn_endpoints", + "max": null, + "min": null, + "type": "PerfdataValue", + "unit": "", + "value": 11, + "warn": null + } + ], + "status": { + "api": { + "conn_endpoints": [], + "http": { + "clients": 1 + }, + "identity": "unittest", + "json_rpc": { + "anonymous_clients": 0, + "relay_queue_item_rate": 0.999, + "relay_queue_items": 0, + "sync_queue_item_rate": 0, + "sync_queue_items": 0, + "work_queue_item_rate": 0 + }, + "not_conn_endpoints": [], + "num_conn_endpoints": 0, + "num_endpoints": 0, + "num_not_conn_endpoints": 0, + "zones": { + "unittest": { + "client_log_lag": 0, + "connected": true, + "endpoints": [ + "unittest" + ], + "parent_zone": "" + } + } + } + } + } + ] +} diff --git a/internal/icinga/testdata/app1.json b/internal/icinga/testdata/app1.json new file mode 100644 index 0000000..7a038a8 --- /dev/null +++ b/internal/icinga/testdata/app1.json @@ -0,0 +1,16 @@ +{ + "results": [ + { + "name": "IcingaApplication", + "perfdata": [], + "status": { + "icingaapplication": { + "app": { + "enable_event_handlers": true, + "version": "r2.15.2-1" + } + } + } + } + ] +}