diff --git a/.gitignore b/.gitignore index 15c7248..167c017 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Binaries for programs and plugins test_*.yaml +charts/annotations-exporter/dashboards bin *.exe *.exe~ diff --git a/Makefile b/Makefile index f58a183..79f03d5 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 0.4.0 +VERSION ?= 0.5.0 # Image URL to use all building/pushing image targets IMG ?= ghcr.io/alex123012/annotations-exporter:v$(VERSION) diff --git a/README.md b/README.md index a972f3e..0df76cd 100644 --- a/README.md +++ b/README.md @@ -5,22 +5,33 @@ Prometheus-exporter, which converts any Kubernetes resources annotations and labels to Prometheus samples. ## Usage -``` +```text Usage of annotations-exporter: - -h, --help - help for annotations-exporter - --kube.annotations strings - Annotations names to use in prometheus metric labels - --kube.labels strings - Labels names to use in prometheus metric labels - --kube.max-revisions int - Max revisions of resource labels to store (default 3) - --kube.namespaces strings - Specifies the namespace that the exporter will monitor resources in (default 'all namespaces') - --kube.resources strings - Resources (// or /) to export labels and annotations (default [deployments/apps,ingresses/v1/networking.k8s.io,statefulsets/apps,daemonsets/apps]) - -v, --version - version for annotations-exporter + -h, --help help for annotations-exporter + + --kube.annotations strings Annotations names to use in prometheus metric labels + + --kube.config string Path to kubeconfig (optional) + + --kube.labels strings Labels names to use in prometheus metric labels + + --kube.max-revisions int Max revisions of resource labels to store (default 3) + + --kube.namespaces strings Specifies the namespace that the exporter will monitor resources in (default 'all namespaces') + + --kube.only-labels-and-annotations Export only labels and annotations defined by flags (default false) + + --kube.reference-annotations strings Annotations names to use in prometheus metric labels and for count revisions (reference names) + + --kube.reference-labels strings Labels names to use in prometheus metric labels and for count revisions (reference names) + + --kube.resources strings Resources (// or /) to export labels and annotations (default [deployments/apps,ingresses/v1/networking.k8s.io,statefulsets/apps,daemonsets/apps]) + + --server.exporter-address string Address to export prometheus metrics (default ":8000") + + --server.log-level string Log level + + -v, --version version for annotations-exporter ``` ## Install @@ -42,18 +53,18 @@ The first version of helm chart is available. ## How it works Exporter will create one metric `kube_annotations_exporter` with constant labels: -* api_version - kubernetes resource apiVersion -* kind - kubernetes resource Kind -* namespace - - kubernetes resource namespace -* name - kubernetes resource name +* annotations_exporter_api_version - kubernetes resource apiVersion +* annotations_exporter_kind - kubernetes resource Kind +* annotations_exporter_namespace - - kubernetes resource namespace +* annotations_exporter_name - kubernetes resource name Other labels would be specified resource labels and annotations. For all specifeied kubernetes annotations and labels corresponding prometheus label would be lowercased and have replaced `/`, `-`, `.` symbols for `_`. -All kubernetes labels would have prefix `kube_label_` in corresponding prometheus label and all kubernetes annotations would have prefix `kube_annotation_`. +All kubernetes labels would have prefix `annotations_exporter_label_` in corresponding prometheus label and all kubernetes annotations would have prefix `annotations_exporter_annotation_`. For example, if we run annotations exporter with flags: `--kube.annotations=ci.werf.io/commit,gitlab.ci.werf.io/pipeline-url` and `--kube.labels=app`, it will create, for example for deployment, metrics like this: ```text -kube_annotations_exporter{api_version="apps/v1",kind="Deployment",kube_annotation_ci_werf_io_commit="",kube_annotation_gitlab_ci_werf_io_pipeline_url="",kube_label_app="",name="",namespace="",revision="0"} +kube_annotations_exporter{annotations_exporter_api_version="apps/v1",annotations_exporter_kind="Deployment",annotations_exporter_annotation_ci_werf_io_commit="",annotations_exporter_annotation_gitlab_ci_werf_io_pipeline_url="",annotations_exporter_label_app="",annotations_exporter_name="",annotations_exporter_namespace="",annotations_exporter_revision="0"} ``` Also, there is additional label `revision` - this label is for storing older values of annotation values. @@ -66,25 +77,41 @@ If we update some resource and provided label or annotation changes - older metr now metric for `nginx` deployment would be: ```text -kube_annotations_exporter{api_version="apps/v1",kind="Deployment",kube_label_app="nginx",name="nginx",namespace="default",revision="0"} +kube_annotations_exporter{annotations_exporter_api_version="apps/v1",annotations_exporter_kind="Deployment",annotations_exporter_label_app="nginx",annotations_exporter_name="nginx",annotations_exporter_namespace="default",revision="0"} ``` if we update label `app` to `nginx-external` in `nginx` deployment, there would be one more metric for this deployment: ```text -kube_annotations_exporter{api_version="apps/v1",kind="Deployment",kube_label_app="nginx",name="nginx",namespace="default",revision="1"} +kube_annotations_exporter{annotations_exporter_api_version="apps/v1",annotations_exporter_kind="Deployment",annotations_exporter_label_app="nginx",annotations_exporter_name="nginx",annotations_exporter_namespace="default",revision="1"} -kube_annotations_exporter{api_version="apps/v1",kind="Deployment",kube_label_app="nginx-external",name="nginx",namespace="default",revision="0"} +kube_annotations_exporter{annotations_exporter_api_version="apps/v1",annotations_exporter_kind="Deployment",annotations_exporter_label_app="nginx-external",annotations_exporter_name="nginx",annotations_exporter_namespace="default",annotations_exporter_revision="0"} ``` Now, if we another time update label `app`, for example, to `nginx-app`, metrics would be: ```text -kube_annotations_exporter{api_version="apps/v1",kind="Deployment",kube_label_app="nginx-app",name="nginx",namespace="default",revision="0"} +kube_annotations_exporter{annotations_exporter_api_version="apps/v1",annotations_exporter_kind="Deployment",annotations_exporter_label_app="nginx-app",annotations_exporter_name="nginx",annotations_exporter_namespace="default",revision="0"} -kube_annotations_exporter{api_version="apps/v1",kind="Deployment",kube_label_app="nginx-external",name="nginx",namespace="default",revision="1"} +kube_annotations_exporter{annotations_exporter_api_version="apps/v1",annotations_exporter_kind="Deployment",annotations_exporter_label_app="nginx-external",annotations_exporter_name="nginx",namespace="default",annotations_exporter_revision="1"} ``` etc. +### Only Labels And Annotations +if `--kube.only-labels-and-annotations` flag provided - exporter won't collect resource meta for metrics (`apiVersion`, `kind`, `name`, `namespace`) and will only expose collected annotations and labels. This is useful when combined with `--kube.reference-annotations` and `--kube.reference-labels` to expose, for example, helm release name and namespace: + +```bash +./annotations-exporter --kube.annotations=gitlab.ci.werf.io/pipeline-url --kube.only-labels-and-annotations=true --kube.reference-annotations=meta.helm.sh/release-name,meta.helm.sh/release-namespace +``` +This will track revisions for `meta.helm.sh/release-name` and `meta.helm.sh/release-namespace` kubernetes resource labels. For example you deployed release from `https://gitlab.com/project/project/pipelines/1` and then from `https://gitlab.com/project/project/pipelines/2`, so the metrics would be: +```text +kube_annotations_exporter{annotations_exporter_annotation_gitlab_ci_werf_io_pipeline_url="https://gitlab.com/project/project/pipelines/1", annotations_exporter_annotation_meta_helm_sh_release_name="project-dev", annotations_exporter_annotation_meta_helm_sh_release_namespace="dev", annotations_exporter_revision="1"} + +kube_annotations_exporter{annotations_exporter_annotation_gitlab_ci_werf_io_pipeline_url="https://gitlab.com/project/project/pipelines/2", annotations_exporter_annotation_meta_helm_sh_release_name="project-dev", annotations_exporter_annotation_meta_helm_sh_release_namespace="dev", annotations_exporter_revision="0"} +``` ## Dashboards -TBA +Now there is only one [summary dashboard](charts/annotations-exporter/templates/dashboard.yaml), that will be autogenerated for helm-chart to ConfigMap. It is simple table that summarises all information about exported annotations and labels + + diff --git a/charts/annotations-exporter/Chart.yaml b/charts/annotations-exporter/Chart.yaml index 5ee7581..ae244de 100644 --- a/charts/annotations-exporter/Chart.yaml +++ b/charts/annotations-exporter/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 type: application name: annotations-exporter -version: 0.4.0 +version: 0.5.0 kubeVersion: ">=1.10.0-0" description: "Prometheus-exporter, which converts any Kubernetes resources annotations and labels to Prometheus samples." diff --git a/charts/annotations-exporter/README.md b/charts/annotations-exporter/README.md index 62203ef..9a862af 100644 --- a/charts/annotations-exporter/README.md +++ b/charts/annotations-exporter/README.md @@ -1,6 +1,6 @@ # annotations-exporter -![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Prometheus-exporter, which converts any Kubernetes resources annotations and labels to Prometheus samples. @@ -15,13 +15,16 @@ Kubernetes: `>=1.10.0-0` | replicaCount | int | `1` | Number of replicas (pods) to launch. | | image.repository | string | `"ghcr.io/alex123012/annotations-exporter"` | Name of the image repository to pull the container image from. | | image.pullPolicy | string | `"IfNotPresent"` | [Image pull policy](https://kubernetes.io/docs/concepts/containers/images/#updating-images) for updating already existing images on a node. | -| image.tag | string | `"v0.4.0"` | Image tag override for the default value (chart appVersion). | +| image.tag | string | `"v0.5.0"` | Image tag override for the default value (chart appVersion). | | cmdArgs."kube.resources" | list | `["deployments/apps","ingresses/v1/networking.k8s.io","statefulsets/apps","daemonsets/apps"]` | Kubernetes resources for annotations and labels to export. | -| cmdArgs."kube.annotations" | list | `["ci.werf.io/commit","gitlab.ci.werf.io/pipeline-url"]` | Kubernetes resources annotations to export. | -| cmdArgs."kube.labels" | list | `["app"]` | Kubernetes resources labels to export. | +| cmdArgs."kube.annotations" | list | `[]` | Kubernetes resources annotations to export. | +| cmdArgs."kube.labels" | list | `[]` | Kubernetes resources labels to export. | | cmdArgs."kube.namespaces" | list | `[""]` | Kubernetes namespaces to watch. | | cmdArgs."kube.max-revisions" | int | `3` | Max revisions of resource labels to store. | | cmdArgs."server.log-level" | string | `"debug"` | Log level. | +| cmdArgs."kube.only-labels-and-annotations" | bool | `false` | Export only labels and annotations defined by flags (usefull for exposing ) | +| cmdArgs."kube.reference-labels" | list | `[]` | Labels names to use in prometheus metric labels and for count revisions (reference name) | +| cmdArgs."kube.reference-annotations" | list | `[]` | Annotations names to use in prometheus metric labels and for count revisions (reference name) | | imagePullSecrets | list | `[]` | Reference to one or more secrets to be used when [pulling images](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-pod-that-uses-your-secret) (from private registries). | | nameOverride | string | `""` | A name in place of the chart name for `app:` labels. | | fullnameOverride | string | `""` | A name to substitute for the full names of resources. | @@ -42,6 +45,7 @@ Kubernetes: `>=1.10.0-0` | affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) configuration. See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling) for details. | | strategy | object | `{}` | Deployment [strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy) configuration. | | service.annotations | object | `{}` | Annotations to be added to the service. | +| service.labels | object | `{}` | Labels to be added to the service. | | service.type | string | `"ClusterIP"` | Kubernetes [service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types). | | service.clusterIP | string | `""` | Internal cluster service IP (when applicable) | | service.ports.port | int | `8000` | HTTP service port | diff --git a/charts/annotations-exporter/templates/_helpers.tpl b/charts/annotations-exporter/templates/_helpers.tpl index 62ef7d1..4b7aa95 100644 --- a/charts/annotations-exporter/templates/_helpers.tpl +++ b/charts/annotations-exporter/templates/_helpers.tpl @@ -56,3 +56,11 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- fail ( printf "Can't parse resource string %s" $arg ) }} {{- end }} {{- end }} + +{{- define "format.prom.label" }} + {{- $arg := . }} + {{- $result := replace "/" "_" $arg }} + {{- $result = replace "." "_" $result }} + {{- $result = replace "-" "_" $result }} + {{- lower $result }} +{{- end }} diff --git a/charts/annotations-exporter/templates/dashboard.yaml b/charts/annotations-exporter/templates/dashboard.yaml new file mode 100644 index 0000000..2299709 --- /dev/null +++ b/charts/annotations-exporter/templates/dashboard.yaml @@ -0,0 +1,335 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "exporter.fullname" . }}-dashboard + namespace: {{ include "exporter.fullname" . }} + labels: + {{- include "exporter.labels" . | nindent 4 }} +data: + summary-dashboard.json: | + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "All exported annotations and labels values for all defined resources", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 30, + "iteration": 1667303912761, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 19, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.5.13", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "kube_annotations_exporter{{- if not ( index $.Values.cmdArgs "kube.only-labels-and-annotations" ) }}{\n annotations_exporter_namespace=~\"$Namespace\",\n annotations_exporter_api_version=~\"$Api_Version\",\n annotations_exporter_kind=~\"$Kind\"\n}{{- end }}", + "format": "table", + "hide": false, + "instant": true, + "range": false, + "refId": "B" + } + ], + "title": "Summary", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [], + "pattern": "annotations_exporter_.*" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + {{- range $annotation := index $.Values.cmdArgs "kube.annotations" }} + "annotations_exporter_annotation_{{ include "format.prom.label" $annotation }}": { + "aggregations": [ + "allValues" + ], + "operation": "aggregate" + }, + {{- end }} + {{- range $label := index $.Values.cmdArgs "kube.labels" }} + "annotations_exporter_label_{{ include "format.prom.label" $label }}": { + "aggregations": [ + "allValues" + ], + "operation": "aggregate" + }, + {{- end }} + {{- range $annotation := index $.Values.cmdArgs "kube.reference-annotations" }} + "annotations_exporter_annotation_{{ include "format.prom.label" $annotation }}": { + "aggregations": [], + "operation": "groupby" + }, + {{- end }} + {{- range $label := index $.Values.cmdArgs "kube.reference-labels" }} + "annotations_exporter_label_{{ include "format.prom.label" $label }}": { + "aggregations": [], + "operation": "groupby" + }, + {{- end }} + {{- if not ( index $.Values.cmdArgs "kube.only-labels-and-annotations" ) }} + "annotations_exporter_api_version": { + "aggregations": [], + "operation": "groupby" + }, + "annotations_exporter_kind": { + "aggregations": [], + "operation": "groupby" + }, + "annotations_exporter_name": { + "aggregations": [], + "operation": "groupby" + }, + "annotations_exporter_namespace": { + "aggregations": [], + "operation": "groupby" + }, + {{- end }} + "": {} + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + {{- range $annotation := index $.Values.cmdArgs "kube.reference-annotations" }} + "annotations_exporter_annotation_{{ include "format.prom.label" $annotation }}": "{{ $annotation }}", + {{- end }} + {{- range $label := index $.Values.cmdArgs "kube.reference-labels" }} + "annotations_exporter_label_{{ include "format.prom.label" $label }}": "{{ $label }}", + {{- end }} + {{- range $annotation := index $.Values.cmdArgs "kube.annotations" }} + "annotations_exporter_annotation_{{ include "format.prom.label" $annotation }} (allValues)": "{{ $annotation }}", + {{- end }} + {{- range $label := index $.Values.cmdArgs "kube.labels" }} + "annotations_exporter_label_{{ include "format.prom.label" $label }} (allValues)": "{{ $label }}", + {{- end }} + {{- if not ( index $.Values.cmdArgs "kube.only-labels-and-annotations" ) }} + "annotations_exporter_api_version": "apiVersion", + "annotations_exporter_kind": "kind", + "annotations_exporter_name": "name", + "annotations_exporter_namespace": "namespace", + {{- end }} + "": "" + } + } + } + ], + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "longterm", + "value": "longterm" + }, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } +{{- if not ( index $.Values.cmdArgs "kube.only-labels-and-annotations" ) }}, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(annotations_exporter_namespace)", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": true, + "name": "Namespace", + "options": [], + "query": { + "query": "label_values(annotations_exporter_namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(annotations_exporter_api_version)", + "hide": 0, + "includeAll": true, + "label": "Api Version", + "multi": true, + "name": "Api_Version", + "options": [], + "query": { + "query": "label_values(annotations_exporter_api_version)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(annotations_exporter_kind)", + "hide": 0, + "includeAll": true, + "label": "Kind", + "multi": true, + "name": "Kind", + "options": [], + "query": { + "query": "label_values(annotations_exporter_kind)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } +{{- end }} + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Annotations exporter summary", + "version": 1, + "weekStart": "" + } diff --git a/charts/annotations-exporter/templates/service.yaml b/charts/annotations-exporter/templates/service.yaml index 5ef5297..43a8d95 100644 --- a/charts/annotations-exporter/templates/service.yaml +++ b/charts/annotations-exporter/templates/service.yaml @@ -5,6 +5,9 @@ metadata: namespace: {{ include "exporter.fullname" . }} labels: {{- include "exporter.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} {{- with .Values.service.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/charts/annotations-exporter/values.yaml b/charts/annotations-exporter/values.yaml index 834e92a..685d5e3 100644 --- a/charts/annotations-exporter/values.yaml +++ b/charts/annotations-exporter/values.yaml @@ -9,7 +9,7 @@ image: pullPolicy: IfNotPresent # -- Image tag override for the default value (chart appVersion). - tag: "v0.4.0" + tag: "v0.5.0" cmdArgs: # -- Kubernetes resources for annotations and labels to export. @@ -19,12 +19,9 @@ cmdArgs: - "statefulsets/apps" - "daemonsets/apps" # -- Kubernetes resources annotations to export. - "kube.annotations": - - ci.werf.io/commit - - gitlab.ci.werf.io/pipeline-url + "kube.annotations": [] # -- Kubernetes resources labels to export. - "kube.labels": - - app + "kube.labels": [] # -- Kubernetes namespaces to watch. "kube.namespaces": - "" # "" (empty string) for all namespaces @@ -33,6 +30,15 @@ cmdArgs: # -- Log level. "server.log-level": debug + # -- Export only labels and annotations defined by flags (usefull for exposing ) + "kube.only-labels-and-annotations": false + + # -- Labels names to use in prometheus metric labels and for count revisions (reference name) + "kube.reference-labels": [] + + # -- Annotations names to use in prometheus metric labels and for count revisions (reference name) + "kube.reference-annotations": [] + # -- Reference to one or more secrets to be used when [pulling images](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-pod-that-uses-your-secret) (from private registries). imagePullSecrets: [] @@ -142,6 +148,9 @@ service: # -- Annotations to be added to the service. annotations: {} + # -- Labels to be added to the service. + labels: {} + # -- Kubernetes [service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types). type: ClusterIP diff --git a/cmd/annotations-exporter/main.go b/cmd/annotations-exporter/main.go index 77306cf..fe4d67b 100644 --- a/cmd/annotations-exporter/main.go +++ b/cmd/annotations-exporter/main.go @@ -30,6 +30,10 @@ var ( maxRevisions int = 3 logLevel string kubeconfig string + + onlyLabelsAndAnnotations bool + referenceAnnotations []string + referenceLabels []string ) func main() { @@ -54,6 +58,9 @@ func main() { flags.StringSliceVar(&namespaces, "kube.namespaces", namespaces, "Specifies the namespace that the exporter will monitor resources in (default 'all namespaces')") flags.IntVar(&maxRevisions, "kube.max-revisions", maxRevisions, "Max revisions of resource labels to store") flags.StringVar(&kubeconfig, "kube.config", kubeconfig, "Path to kubeconfig (optional)") + flags.StringSliceVar(&referenceAnnotations, "kube.reference-annotations", referenceAnnotations, "Annotations names to use in prometheus metric labels and for count revisions (reference names)") + flags.StringSliceVar(&referenceLabels, "kube.reference-labels", referenceLabels, "Labels names to use in prometheus metric labels and for count revisions (reference names)") + flags.BoolVar(&onlyLabelsAndAnnotations, "kube.only-labels-and-annotations", onlyLabelsAndAnnotations, "Export only labels and annotations defined by flags (default false)") ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() @@ -81,7 +88,10 @@ func Run(ctx context.Context) error { } metricVault := collector.NewVault() - if err := metricVault.RegisterMappings([]collector.Mapping{kube.ResourceMapping(labels, annotations, 3)}); err != nil { + if err := metricVault.RegisterMappings([]collector.Mapping{ + kube.ResourceMapping(labels, annotations, maxRevisions, + onlyLabelsAndAnnotations, referenceLabels, referenceAnnotations), + }); err != nil { log.Fatal(err) } diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index f1bad2b..fe151dd 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -22,7 +22,10 @@ import ( "github.com/prometheus/client_golang/prometheus" ) -const labelsSeparator = byte(255) +const ( + labelsSeparator = byte(255) + ApplicationPrefix = "annotations_exporter_" +) type ConstMetricCollector interface { Describe(chan<- *prometheus.Desc) @@ -33,7 +36,6 @@ type ConstMetricCollector interface { type ResourceGaugeMetric struct { RevisionMetrics []RevisionGaugeMetric - ResourceLabels []string } type RevisionGaugeMetric struct { RevisionValue float64 @@ -52,10 +54,14 @@ func NewConstGaugeCollector(mapping Mapping) *GaugeCollector { resultPrometheusLabels := ConcatMultipleSlices( [][]string{ - formatPromethuesLabelSlice(mapping.KubeResourceMeta, ""), - formatPromethuesLabelSlice(mapping.KubeLabels, "kube_label_"), - formatPromethuesLabelSlice(mapping.KubeAnnotations, "kube_annotation_"), - {"revision"}, + formatPromethuesLabelSlice(mapping.KubeResourceMeta, ApplicationPrefix), + + formatPromethuesLabelSlice(mapping.ReferenceLabels, ApplicationPrefix+"label_"), + formatPromethuesLabelSlice(mapping.ReferenceAnnotations, ApplicationPrefix+"annotation_"), + + formatPromethuesLabelSlice(mapping.KubeLabels, ApplicationPrefix+"label_"), + formatPromethuesLabelSlice(mapping.KubeAnnotations, ApplicationPrefix+"annotation_"), + {ApplicationPrefix + "revision"}, }) desc := prometheus.NewDesc(mapping.Name, mapping.Help, resultPrometheusLabels, nil) @@ -86,14 +92,27 @@ func (c *GaugeCollector) Collect(ch chan<- prometheus.Metric) { } func (c *GaugeCollector) Store(sample Sample) { - labelsHash := hashLabels(sample.ResourceMeta) + kubeReferenceForHash := ConcatMultipleSlices( + [][]string{ + compareLabelsSliceWithMap(c.mapping.ReferenceLabels, sample.ResourceLabels), + compareLabelsSliceWithMap(c.mapping.ReferenceAnnotations, sample.ResourceAnnotations), + }) + + if !c.mapping.OnlyLabelsAndAnnotations { + kubeReferenceForHash = ConcatMultipleSlices([][]string{ + sample.ResourceMeta, + kubeReferenceForHash, + }) + } + + labelsHash := hashLabels(kubeReferenceForHash) lastRevision := 0 newMetric := RevisionGaugeMetric{ RevisionValue: float64(lastRevision), LabelValues: ConcatMultipleSlices( [][]string{ - sample.ResourceMeta, + kubeReferenceForHash, compareLabelsSliceWithMap(c.mapping.KubeLabels, sample.ResourceLabels), compareLabelsSliceWithMap(c.mapping.KubeAnnotations, sample.ResourceAnnotations), {fmt.Sprint(lastRevision)}, @@ -106,7 +125,6 @@ func (c *GaugeCollector) Store(sample Sample) { if !ok { storedResourceMetrics = &ResourceGaugeMetric{ RevisionMetrics: make([]RevisionGaugeMetric, c.mapping.MaxRevisions), - ResourceLabels: sample.ResourceMeta, } storedResourceMetrics.RevisionMetrics[lastRevision] = newMetric } else { diff --git a/pkg/collector/valult.go b/pkg/collector/valult.go index a6309a6..2287b8a 100644 --- a/pkg/collector/valult.go +++ b/pkg/collector/valult.go @@ -27,10 +27,16 @@ type Mapping struct { Name string `yaml:"name"` Help string `yaml:"help,omitempty"` + ReferenceAnnotations []string `yaml:"reference_annotations"` + ReferenceLabels []string `yaml:"reference_labels"` + KubeResourceMeta []string `yaml:"resource_meta"` KubeAnnotations []string `yaml:"kube_labels,omitempty"` KubeLabels []string `yaml:"kube_annotations,omitempty"` - MaxRevisions int `yaml:"max_revisions,omitempty"` + + MaxRevisions int `yaml:"max_revisions,omitempty"` + + OnlyLabelsAndAnnotations bool `yaml:"only_labels_and_annotations,omitempty"` } type Sample struct { diff --git a/pkg/kube/informer.go b/pkg/kube/informer.go index 2f58aa4..2255f79 100644 --- a/pkg/kube/informer.go +++ b/pkg/kube/informer.go @@ -30,7 +30,8 @@ type InformerController struct { } // NewResourcesInformer creates cached informer to track resources from a Kubernetes cluster. -func NewResourcesInformer(config *rest.Config, namespaces []string, resources []schema.GroupVersionResource, metricCollector *collector.MetricsVault) (*InformerController, error) { +func NewResourcesInformer(config *rest.Config, namespaces []string, resources []schema.GroupVersionResource, + metricCollector *collector.MetricsVault) (*InformerController, error) { client, err := dynamic.NewForConfig(config) if err != nil { return nil, err diff --git a/pkg/kube/resource.go b/pkg/kube/resource.go index 301720b..ce4768f 100644 --- a/pkg/kube/resource.go +++ b/pkg/kube/resource.go @@ -27,27 +27,39 @@ func ResourceToSample(resource *unstructured.Unstructured) collector.Sample { if annotations == nil { annotations = make(map[string]string) } + + resourceMeta := []string{ + resource.GetAPIVersion(), + resource.GetKind(), + resource.GetNamespace(), + resource.GetName(), + } return collector.Sample{ ResourceLabels: labels, ResourceAnnotations: annotations, - ResourceMeta: []string{ - resource.GetAPIVersion(), - resource.GetKind(), - resource.GetNamespace(), - resource.GetName(), - }, + ResourceMeta: resourceMeta, } } // ResourceMapping creates the mapping for the prometheus metrics vault. The order of the labels here should match the one // from the sample converter function. -func ResourceMapping(kubeLabelNames, kubeAnnotationsNames []string, maxRevisions int) collector.Mapping { +func ResourceMapping(kubeLabelNames, kubeAnnotationsNames []string, maxRevisions int, onlyLabelsAndAnnotations bool, referenceLabels, referenceAnnotations []string) collector.Mapping { + resourceMeta := make([]string, 0) + if !onlyLabelsAndAnnotations { + resourceMeta = []string{"api_version", "kind", "namespace", "name"} + } return collector.Mapping{ - Name: ExporterMetricName, - Help: "Expose Kubernetes annotations and lables from kubernetes objects", - KubeResourceMeta: []string{"api_version", "kind", "namespace", "name"}, + Name: ExporterMetricName, + Help: "Expose Kubernetes annotations and lables from kubernetes objects", + + ReferenceLabels: referenceLabels, + ReferenceAnnotations: referenceAnnotations, + + KubeResourceMeta: resourceMeta, KubeLabels: kubeLabelNames, KubeAnnotations: kubeAnnotationsNames, - MaxRevisions: maxRevisions, + + MaxRevisions: maxRevisions, + OnlyLabelsAndAnnotations: onlyLabelsAndAnnotations, } }