commit 397df33ef280b37c1680b5098c6355cc337bad37 Author: Filippo Giunchedi Date: Fri Aug 27 14:49:55 2021 +0200 Add mmkubernetes support Change-Id: I8e8d4d49968dc0d2b637a2b9a4479b44580ad64e diff --git a/exporter.go b/exporter.go index c2a0630..eb4faad 100644 --- a/exporter.go +++ b/exporter.go @@ -23,6 +23,7 @@ const ( rsyslogDynafileCache rsyslogInputIMDUP rsyslogForward + rsyslogKubernetes ) type rsyslogExporter struct { @@ -121,6 +122,14 @@ func (re *rsyslogExporter) handleStatLine(rawbuf []byte) error { for _, p := range f.toPoints() { re.set(p) } + case rsyslogKubernetes: + k, err := newKubernetesFromJSON(buf) + if err != nil { + return err + } + for _, p := range k.toPoints() { + re.set(p) + } default: return fmt.Errorf("unknown pstat type: %v", pstatType) diff --git a/kubernetes.go b/kubernetes.go new file mode 100644 index 0000000..8ce43ca --- /dev/null +++ b/kubernetes.go @@ -0,0 +1,125 @@ +package main + +import ( + "encoding/json" + "fmt" + "regexp" +) + +var ( + apiNameRegexp = regexp.MustCompile(`mmkubernetes\((\S+)\)`) +) + +type kubernetes struct { + Name string `json:"name"` + Url string + RecordSeen int64 `json:"recordseen"` + NamespaceMetaSuccess int64 `json:"namespacemetadatasuccess"` + NamespaceMetaNotFound int64 `json:"namespacemetadatanotfound"` + NamespaceMetaBusy int64 `json:"namespacemetadatabusy"` + NamespaceMetaError int64 `json:"namespacemetadataerror"` + PodMetaSuccess int64 `json:"podmetadatasuccess"` + PodMetaNotFound int64 `json:"podmetadatanotfound"` + PodMetaBusy int64 `json:"podmetadatabusy"` + PodMetaError int64 `json:"podmetadataerror"` +} + +func newKubernetesFromJSON(b []byte) (*kubernetes, error) { + var pstat kubernetes + err := json.Unmarshal(b, &pstat) + if err != nil { + return nil, fmt.Errorf("failed to decode kubernetes stat `%v`: %v", string(b), err) + } + matches := apiNameRegexp.FindSubmatch([]byte(pstat.Name)) + if matches != nil { + pstat.Url = string(matches[1]) + } + return &pstat, nil +} + +func (k *kubernetes) toPoints() []*point { + points := make([]*point, 9) + + points[0] = &point{ + Name: "kubernetes_namespace_metadata_success_total", + Type: counter, + Value: k.NamespaceMetaSuccess, + Description: "successful fetches of namespace metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[1] = &point{ + Name: "kubernetes_namespace_metadata_notfound_total", + Type: counter, + Value: k.NamespaceMetaNotFound, + Description: "notfound fetches of namespace metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[2] = &point{ + Name: "kubernetes_namespace_metadata_busy_total", + Type: counter, + Value: k.NamespaceMetaBusy, + Description: "busy fetches of namespace metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[3] = &point{ + Name: "kubernetes_namespace_metadata_error_total", + Type: counter, + Value: k.NamespaceMetaError, + Description: "error fetches of namespace metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[4] = &point{ + Name: "kubernetes_pod_metadata_success_total", + Type: counter, + Value: k.PodMetaSuccess, + Description: "successful fetches of pod metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[5] = &point{ + Name: "kubernetes_pod_metadata_notfound_total", + Type: counter, + Value: k.PodMetaNotFound, + Description: "notfound fetches of pod metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[6] = &point{ + Name: "kubernetes_pod_metadata_busy_total", + Type: counter, + Value: k.PodMetaBusy, + Description: "busy fetches of pod metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[7] = &point{ + Name: "kubernetes_pod_metadata_error_total", + Type: counter, + Value: k.PodMetaError, + Description: "error fetches of pod metadata", + LabelName: "url", + LabelValue: k.Url, + } + + points[8] = &point{ + Name: "kubernetes_record_seen_total", + Type: counter, + Value: k.RecordSeen, + Description: "records fetched from the api", + LabelName: "url", + LabelValue: k.Url, + } + + return points +} diff --git a/kubernetes_test.go b/kubernetes_test.go new file mode 100644 index 0000000..46d68fa --- /dev/null +++ b/kubernetes_test.go @@ -0,0 +1,203 @@ +package main + +import "testing" + +var ( + kubernetesLog = []byte(`{ "name": "mmkubernetes(https://host.domain.tld:6443)", "origin": "mmkubernetes", "recordseen": 477943, "namespacemetadatasuccess": 7, "namespacemetadatanotfound": 0, "namespacemetadatabusy": 0, "namespacemetadataerror": 0, "podmetadatasuccess": 26, "podmetadatanotfound": 0, "podmetadatabusy": 0, "podmetadataerror": 0 }`) +) + +func TestNewKubernetesFromJSON(t *testing.T) { + logType := getStatType(kubernetesLog) + if logType != rsyslogKubernetes { + t.Errorf("detected pstat type should be %d but is %d", rsyslogKubernetes, logType) + } + + pstat, err := newKubernetesFromJSON([]byte(kubernetesLog)) + if err != nil { + t.Fatalf("expected parsing action not to fail, got: %v", err) + } + + if want, got := "mmkubernetes(https://host.domain.tld:6443)", pstat.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + if want, got := "https://host.domain.tld:6443", pstat.Url; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + if want, got := int64(477943), pstat.RecordSeen; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(7), pstat.NamespaceMetaSuccess; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(0), pstat.NamespaceMetaNotFound; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(0), pstat.NamespaceMetaBusy; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(0), pstat.NamespaceMetaError; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(26), pstat.PodMetaSuccess; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(0), pstat.PodMetaNotFound; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(0), pstat.PodMetaBusy; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := int64(0), pstat.PodMetaError; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + +} + +func TestKubernetesToPoints(t *testing.T) { + pstat, err := newKubernetesFromJSON([]byte(kubernetesLog)) + if err != nil { + t.Fatalf("expected parsing action not to fail, got: %v", err) + } + points := pstat.toPoints() + + point := points[0] + if want, got := "kubernetes_namespace_metadata_success_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + if want, got := "https://host.domain.tld:6443", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[1] + if want, got := "kubernetes_namespace_metadata_notfound_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[2] + if want, got := "kubernetes_namespace_metadata_busy_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[3] + if want, got := "kubernetes_namespace_metadata_error_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[4] + if want, got := "kubernetes_pod_metadata_success_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[5] + if want, got := "kubernetes_pod_metadata_notfound_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[6] + if want, got := "kubernetes_pod_metadata_busy_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[7] + if want, got := "kubernetes_pod_metadata_error_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[8] + if want, got := "kubernetes_record_seen_total", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + /* + if want, got := int64(100000), point.Value; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := counter, point.Type; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := "test_action", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[1] + if want, got := "action_failed", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + if want, got := int64(2), point.Value; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := counter, point.Type; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := "test_action", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[2] + if want, got := "action_suspended", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + if want, got := int64(1), point.Value; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := counter, point.Type; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := "test_action", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[3] + if want, got := "action_suspended_duration", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + if want, got := int64(1000), point.Value; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := counter, point.Type; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := "test_action", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[4] + if want, got := "action_resumed", point.Name; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + if want, got := int64(1), point.Value; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := counter, point.Type; want != got { + t.Errorf("wanted '%d', got '%d'", want, got) + } + + if want, got := "test_action", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + */ +} diff --git a/utils.go b/utils.go index 624fd28..3bbaa4a 100644 --- a/utils.go +++ b/utils.go @@ -20,6 +20,8 @@ func getStatType(buf []byte) rsyslogType { return rsyslogDynafileCache } else if strings.Contains(line, "omfwd") { return rsyslogForward + } else if strings.Contains(line, "mmkubernetes") { + return rsyslogKubernetes } return rsyslogUnknown }