diff --git a/docs/certificatessigningrequest-metrics.md b/docs/certificatessigningrequest-metrics.md index 52548ab535..a5f195464e 100644 --- a/docs/certificatessigningrequest-metrics.md +++ b/docs/certificatessigningrequest-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_certificatesigningrequest_annotations | Gauge | `certificatesigningrequest`=<certificatesigningrequest-name>| STABLE | | kube_certificatesigningrequest_created| Gauge | `certificatesigningrequest`=<certificatesigningrequest-name>| STABLE | | kube_certificatesigningrequest_condition | Gauge | `certificatesigningrequest`=<certificatesigningrequest-name>
`condition`=<approved\|denied> | STABLE | | kube_certificatesigningrequest_labels | Gauge | `certificatesigningrequest`=<certificatesigningrequest-name>| STABLE | diff --git a/docs/cli-arguments.md b/docs/cli-arguments.md index 5ace27edd5..efcd0bd903 100644 --- a/docs/cli-arguments.md +++ b/docs/cli-arguments.md @@ -39,6 +39,7 @@ Usage of ./kube-state-metrics: --logtostderr log to standard error instead of files (default true) --metric-allowlist string Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive. --metric-denylist string Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive. + --metric-annotations-allowlist string Comma-separated list of additional Kubernetes annotations keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional annotations provide a list of resource names in their plural form and Kubernetes annotations keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any annotation, but that has severe performance implications (Example: '=pods=[*]'). --metric-labels-allowlist string Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional labels provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]'). --namespaces string Comma-separated list of namespaces to be enabled. Defaults to "" --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level) diff --git a/docs/cronjob-metrics.md b/docs/cronjob-metrics.md index b7d4d5b07e..3d5100cd3b 100644 --- a/docs/cronjob-metrics.md +++ b/docs/cronjob-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_cronjob_annotations | Gauge | `cronjob`=<cronjob-name>
`namespace`=<cronjob-namespace>
`annotation_CRONJOB_LABEL`=<CRONJOB_ANNOTATION> | STABLE | kube_cronjob_info | Gauge | `cronjob`=<cronjob-name>
`namespace`=<cronjob-namespace>
`schedule`=<schedule>
`concurrency_policy`=<concurrency-policy> | STABLE | kube_cronjob_labels | Gauge | `cronjob`=<cronjob-name>
`namespace`=<cronjob-namespace>
`label_CRONJOB_LABEL`=<CRONJOB_LABEL> | STABLE | kube_cronjob_created | Gauge | `cronjob`=<cronjob-name>
`namespace`=<cronjob-namespace> | STABLE diff --git a/docs/daemonset-metrics.md b/docs/daemonset-metrics.md index d95f63101f..bb80159650 100644 --- a/docs/daemonset-metrics.md +++ b/docs/daemonset-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_daemonset_annotations | Gauge | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace>
`annotation_DAEMONSET_ANNOTATION`=<DAEMONSET_ANNOTATION> | STABLE | | kube_daemonset_created | Gauge | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | | kube_daemonset_status_current_number_scheduled | Gauge | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | | kube_daemonset_status_desired_number_scheduled | Gauge | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | diff --git a/docs/deployment-metrics.md b/docs/deployment-metrics.md index 4c7ca99339..8b3ef5810f 100644 --- a/docs/deployment-metrics.md +++ b/docs/deployment-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_deployment_annotations | Gauge | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`annotation_DEPLOYMENT_ANNOTATION`=<DEPLOYMENT_ANNOTATION> | STABLE | | kube_deployment_status_replicas | Gauge | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_status_replicas_available | Gauge | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_status_replicas_unavailable | Gauge | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | diff --git a/docs/endpoint-metrics.md b/docs/endpoint-metrics.md index 2e0b416030..746c832681 100644 --- a/docs/endpoint-metrics.md +++ b/docs/endpoint-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_endpoint_annotations | Gauge | `endpoint`=<endpoint-name>
`namespace`=<endpoint-namespace>
`annotation_ENDPOINT_LABEL`=<ENDPOINT_ANNOTATION> | STABLE | | kube_endpoint_address_not_ready | Gauge | `endpoint`=<endpoint-name>
`namespace`=<endpoint-namespace> | STABLE | | kube_endpoint_address_available | Gauge | `endpoint`=<endpoint-name>
`namespace`=<endpoint-namespace> | STABLE | | kube_endpoint_info | Gauge | `endpoint`=<endpoint-name>
`namespace`=<endpoint-namespace> | STABLE | diff --git a/docs/horizontalpodautoscaler-metrics.md b/docs/horizontalpodautoscaler-metrics.md index 5c603598ff..d8116f8691 100644 --- a/docs/horizontalpodautoscaler-metrics.md +++ b/docs/horizontalpodautoscaler-metrics.md @@ -2,6 +2,7 @@ | Metric name | Metric type | Labels/tags | Status | | -------------------------------- | ----------- | ------------------------------------------------------------- | ------ | +| kube_horizontalpodautoscaler_annotations | Gauge | `horizontalpodautoscaler`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE | | kube_horizontalpodautoscaler_labels | Gauge | `horizontalpodautoscaler`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE | | kube_horizontalpodautoscaler_metadata_generation | Gauge | `horizontalpodautoscaler`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE | | kube_horizontalpodautoscaler_spec_max_replicas | Gauge | `horizontalpodautoscaler`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE | diff --git a/docs/ingress-metrics.md b/docs/ingress-metrics.md index cfd9643d6a..e00fc05aa5 100644 --- a/docs/ingress-metrics.md +++ b/docs/ingress-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_ingress_annotations | Gauge | `ingress`=<ingress-name>
`namespace`=<ingress-namespace>
`annotation_INGRESS_LABEL`=<ANNOTATION_LABEL> | STABLE | | kube_ingress_info | Gauge | `ingress`=<ingress-name>
`namespace`=<ingress-namespace> | STABLE | | kube_ingress_labels | Gauge | `ingress`=<ingress-name>
`namespace`=<ingress-namespace>
`label_INGRESS_LABEL`=<INGRESS_LABEL> | STABLE | | kube_ingress_created | Gauge | `ingress`=<ingress-name>
`namespace`=<ingress-namespace> | STABLE | diff --git a/docs/job-metrics.md b/docs/job-metrics.md index d0ec58ce60..9d28610881 100644 --- a/docs/job-metrics.md +++ b/docs/job-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_job_annotations | Gauge | `job_name`=<job-name>
`namespace`=<job-namespace>
`annotation_JOB_LABEL`=<JOB_ANNOTATION> | STABLE | | kube_job_info | Gauge | `job_name`=<job-name>
`namespace`=<job-namespace> | STABLE | | kube_job_labels | Gauge | `job_name`=<job-name>
`namespace`=<job-namespace>
`label_JOB_LABEL`=<JOB_LABEL> | STABLE | | kube_job_owner | Gauge | `job_name`=<job-name>
`namespace`=<job-namespace>
`owner_kind`=<owner kind>
`owner_name`=<owner name>
`owner_is_controller`=<whether owner is controller> | STABLE | diff --git a/docs/networkpolicy-metrics.md b/docs/networkpolicy-metrics.md index 2a023d46c7..c5fa1f64f0 100644 --- a/docs/networkpolicy-metrics.md +++ b/docs/networkpolicy-metrics.md @@ -3,6 +3,7 @@ | Metric name | Metric type | Labels/tags | Status | | ------------------------------------- | ----------- | ------------------------------------------------------------------------------ | ------------ | +| kube_networkpolicy_annotations | Gauge | `namespace`=<namespace name> `networkpolicy`=<networkpolicy name> | EXPERIMENTAL | | kube_networkpolicy_created | Gauge | `namespace`=<namespace name> `networkpolicy`=<networkpolicy name> | EXPERIMENTAL | | kube_networkpolicy_labels | Gauge | `namespace`=<namespace name> `networkpolicy`=<networkpolicy name> | EXPERIMENTAL | | kube_networkpolicy_spec_egress_rules | Gauge | `namespace`=<namespace name> `networkpolicy`=<networkpolicy name> | EXPERIMENTAL | diff --git a/docs/node-metrics.md b/docs/node-metrics.md index 448e836f41..a4b7c9a11d 100644 --- a/docs/node-metrics.md +++ b/docs/node-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Description | Unit (where applicable) | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------------------- | ----------- | ------ | +| kube_node_annotations | Gauge | Kubernetes annotations converted to Prometheus labels | | `node`=<node-address>
`annotation_NODE_LABEL`=<NODE_ANNOTATION> | STABLE | | kube_node_info | Gauge | Information about a cluster node| |`node`=<node-address>
`kernel_version`=<kernel-version>
`os_image`=<os-image-name>
`container_runtime_version`=<container-runtime-and-version-combination>
`kubelet_version`=<kubelet-version>
`kubeproxy_version`=<kubeproxy-version>
`pod_cidr`=<pod-cidr>
`provider_id`=<provider-id>
`internal_ip`=<internal-ip> | STABLE | | kube_node_labels | Gauge | Kubernetes labels converted to Prometheus labels | | `node`=<node-address>
`label_NODE_LABEL`=<NODE_LABEL> | STABLE | | kube_node_role | Gauge | The role of a cluster node | | `node`=<node-address>
`role`=<NODE_ROLE> | EXPERIMENTAL | diff --git a/docs/persistentvolume-metrics.md b/docs/persistentvolume-metrics.md index 18624cf76a..b16fccc483 100644 --- a/docs/persistentvolume-metrics.md +++ b/docs/persistentvolume-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_persistentvolume_annotations | Gauge | `persistentvolume`=<persistentvolume-name>
`annotation_PERSISTENTVOLUME_LABEL`=<PERSISTENTVOLUME_ANNOTATION> | STABLE | | kube_persistentvolume_capacity_bytes | Gauge | `persistentvolume`=<pv-name> | STABLE | | kube_persistentvolume_status_phase | Gauge | `persistentvolume`=<pv-name>
`phase`=<Bound\|Failed\|Pending\|Available\|Released>| STABLE | | kube_persistentvolume_claim_ref | Gauge | `persistentvolume`=<pv-name>
`claim_namespace`=<>
`name`=<> | STABLE | diff --git a/docs/persistentvolumeclaim-metrics.md b/docs/persistentvolumeclaim-metrics.md index 40025b2ca2..14a367c4fc 100644 --- a/docs/persistentvolumeclaim-metrics.md +++ b/docs/persistentvolumeclaim-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_persistentvolumeclaim_annotations | Gauge | `persistentvolumeclaim`=<persistentvolumeclaim-name>
`namespace`=<persistentvolumeclaim-namespace>
`annotation_PERSISTENTVOLUMECLAIM_LABEL`=<PERSISTENTVOLUMECLAIM_ANNOATION> | STABLE | | kube_persistentvolumeclaim_access_mode | Gauge | `access_mode`=<persistentvolumeclaim-access-mode>
`namespace`=<persistentvolumeclaim-namespace>
`persistentvolumeclaim`=<persistentvolumeclaim-name> | STABLE | | kube_persistentvolumeclaim_info | Gauge | `namespace`=<persistentvolumeclaim-namespace>
`persistentvolumeclaim`=<persistentvolumeclaim-name>
`storageclass`=<persistentvolumeclaim-storageclassname>
`volumename`=<volumename> | STABLE | | kube_persistentvolumeclaim_labels | Gauge | `persistentvolumeclaim`=<persistentvolumeclaim-name>
`namespace`=<persistentvolumeclaim-namespace>
`label_PERSISTENTVOLUMECLAIM_LABEL`=<PERSISTENTVOLUMECLAIM_LABEL> | STABLE | diff --git a/docs/pod-metrics.md b/docs/pod-metrics.md index 1d1db19d0d..e5a0ce1090 100644 --- a/docs/pod-metrics.md +++ b/docs/pod-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Description | Unit (where applicable) | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------------------- | ----------- | ------ | +| kube_pod_annotations | Gauge | Kubernetes annotations converted to Prometheus labels | | `pod`=<pod-name>
`namespace`=<pod-namespace>
`annotation_POD_LABEL`=<POD_ANNOTATION>
`uid`=<pod-uid> | STABLE | | kube_pod_info | Gauge | Information about pod | | `pod`=<pod-name>
`namespace`=<pod-namespace>
`host_ip`=<host-ip>
`pod_ip`=<pod-ip>
`node`=<node-name>
`created_by_kind`=<created_by_kind>
`created_by_name`=<created_by_name>
`uid`=<pod-uid>
`priority_class`=<priority_class>
`host_network`=<host_network>| STABLE | | kube_pod_start_time | Gauge | Start time in unix timestamp for a pod | seconds | `pod`=<pod-name>
`namespace`=<pod-namespace>
`uid`=<pod-uid> | STABLE | | kube_pod_completion_time | Gauge | Completion time in unix timestamp for a pod | seconds | `pod`=<pod-name>
`namespace`=<pod-namespace>
`uid`=<pod-uid> | STABLE | diff --git a/docs/replicaset-metrics.md b/docs/replicaset-metrics.md index ab2e3d16f0..744a7aca4e 100644 --- a/docs/replicaset-metrics.md +++ b/docs/replicaset-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_replicaset_annotations | Gauge | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace>
`annotation_REPLICASET_LABEL`=<REPLICASET_ANNOTATION> | STABLE | | kube_replicaset_status_replicas | Gauge | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | | kube_replicaset_status_fully_labeled_replicas | Gauge | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | | kube_replicaset_status_ready_replicas | Gauge | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | diff --git a/docs/secret-metrics.md b/docs/secret-metrics.md index 780df876de..16716faa04 100644 --- a/docs/secret-metrics.md +++ b/docs/secret-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_secret_annotations | Gauge | `secret`=<secret-name>
`namespace`=<secret-namespace>
`annotations_SECRET_LABEL`=<SECRET_ANNOTATION> | STABLE | | kube_secret_info | Gauge | `secret`=<secret-name>
`namespace`=<secret-namespace> | STABLE | | kube_secret_type | Gauge | `secret`=<secret-name>
`namespace`=<secret-namespace>
`type`=<secret-type> | STABLE | | kube_secret_labels | Gauge | `secret`=<secret-name>
`namespace`=<secret-namespace>
`label_SECRET_LABEL`=<SECRET_LABEL> | STABLE | diff --git a/docs/service-metrics.md b/docs/service-metrics.md index fa2044e657..1883ac55d3 100644 --- a/docs/service-metrics.md +++ b/docs/service-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Description | Unit (where applicable) | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------------------- | ----------- | ------ | +| kube_service_annoations | Gauge | Kubernetes annotations converted to Prometheus labels | |`service`=<service-name>
`namespace`=<service-namespace>
`annotation_SERVICE_LABEL`=<SERVICE_ANNOTATION> | STABLE | | kube_service_info | Gauge | Information about service | |`service`=<service-name>
`namespace`=<service-namespace>
`cluster_ip`=<service cluster ip>
`external_name`=<service external name>
`load_balancer_ip`=<service load balancer ip> | STABLE | | kube_service_labels | Gauge | Kubernetes labels converted to Prometheus labels | |`service`=<service-name>
`namespace`=<service-namespace>
`label_SERVICE_LABEL`=<SERVICE_LABEL> | STABLE | | kube_service_created | Gauge | Unix creation timestamp | seconds |`service`=<service-name>
`namespace`=<service-namespace> | STABLE | diff --git a/docs/statefulset-metrics.md b/docs/statefulset-metrics.md index a1ac97196c..0054144a56 100644 --- a/docs/statefulset-metrics.md +++ b/docs/statefulset-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_statefulset_annotations | Gauge | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`annotation_STATEFULSET_LABEL`=<STATEFULSET_ANNOTATION> | STABLE | | kube_statefulset_status_replicas | Gauge | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | | kube_statefulset_status_replicas_current | Gauge | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | | kube_statefulset_status_replicas_ready | Gauge | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | diff --git a/docs/storageclass-metrics.md b/docs/storageclass-metrics.md index 184a92fa48..843330280f 100644 --- a/docs/storageclass-metrics.md +++ b/docs/storageclass-metrics.md @@ -2,6 +2,7 @@ | Metric name| Metric type | Labels/tags | Status | | ---------- | ----------- | ----------- | ----------- | +| kube_storageclass_annotations | Gauge | `storageclass`=<storageclass-name>
`annotation_STORAGECLASS_LABEL`=<STORAGECLASS_ANNOTATION> | STABLE | | kube_storageclass_info | Gauge | `storageclass`=<storageclass-name>
`provisioner`=<storageclass-provisioner>
`reclaim_policy`=<storageclass-reclaimPolicy>
`volume_binding_mode`=<storageclass-volumeBindingMode> | STABLE | | kube_storageclass_labels | Gauge | `storageclass`=<storageclass-name>
`label_STORAGECLASS_LABEL`=<STORAGECLASS_LABEL> | STABLE | | kube_storageclass_created | Gauge | `storageclass`=<storageclass-name> | STABLE | diff --git a/docs/verticalpodautoscaler-metrics.md b/docs/verticalpodautoscaler-metrics.md index 143d671e78..3077c2395c 100644 --- a/docs/verticalpodautoscaler-metrics.md +++ b/docs/verticalpodautoscaler-metrics.md @@ -2,6 +2,7 @@ | Metric name | Metric type | Labels/tags | Status | | -------------------------------- | ----------- | ------------------------------------------------------------- | ------ | +| kube_verticalpodautoscaler_annotations | Gauge | `annotation_app`=<foo>
`namespace`=<namespace>
`target_api_version`=<api version>
`target_kind`=<target kind>
`target_name`=<target name>
`verticalpodautoscaler`=<vertical pod autoscaler name> | EXPERIMENTAL | | kube_verticalpodautoscaler_spec_resourcepolicy_container_policies_minallowed | Gauge | `container`=<container name>
`namespace`=<namespace>
`resource`=<cpu memory>
`target_api_version`=<api version>
`target_kind`=<target kind>
`target_name`=<target name>
`unit`=<core byte>
`verticalpodautoscaler`=<vertical pod autoscaler name> | EXPERIMENTAL | | kube_verticalpodautoscaler_spec_resourcepolicy_container_policies_maxallowed | Gauge | `container`=<container name>
`namespace`=<namespace>
`resource`=<cpu memory>
`target_api_version`=<api version>
`target_kind`=<target kind>
`target_name`=<target name>
`unit`=<core byte>
`verticalpodautoscaler`=<vertical pod autoscaler name> | EXPERIMENTAL | | kube_verticalpodautoscaler_status_recommendation_containerrecommendations_lowerbound | Gauge | `container`=<container name>
`namespace`=<namespace>
`resource`=<cpu memory>
`target_api_version`=<api version>
`target_kind`=<target kind>
`target_name`=<target name>
`unit`=<core byte>
`verticalpodautoscaler`=<vertical pod autoscaler name> | EXPERIMENTAL | diff --git a/internal/store/builder.go b/internal/store/builder.go index 7a5844d870..27d7d1e781 100644 --- a/internal/store/builder.go +++ b/internal/store/builder.go @@ -54,18 +54,19 @@ import ( // Builder helps to build store. It follows the builder pattern // (https://en.wikipedia.org/wiki/Builder_pattern). type Builder struct { - kubeClient clientset.Interface - vpaClient vpaclientset.Interface - namespaces options.NamespaceList - ctx context.Context - enabledResources []string - allowDenyList ksmtypes.AllowDenyLister - listWatchMetrics *watch.ListWatchMetrics - shardingMetrics *sharding.Metrics - shard int32 - totalShards int - buildStoreFunc ksmtypes.BuildStoreFunc - allowLabelsList map[string][]string + kubeClient clientset.Interface + vpaClient vpaclientset.Interface + namespaces options.NamespaceList + ctx context.Context + enabledResources []string + allowDenyList ksmtypes.AllowDenyLister + listWatchMetrics *watch.ListWatchMetrics + shardingMetrics *sharding.Metrics + shard int32 + totalShards int + buildStoreFunc ksmtypes.BuildStoreFunc + allowAnnotationsList map[string][]string + allowLabelsList map[string][]string } // NewBuilder returns a new builder. @@ -143,6 +144,13 @@ func (b *Builder) DefaultGenerateStoreFunc() ksmtypes.BuildStoreFunc { return b.buildStore } +// WithAllowAnnotations configures which annotations can be returned for metrics +func (b *Builder) WithAllowAnnotations(annotations map[string][]string) { + if len(annotations) > 0 { + b.allowAnnotationsList = annotations + } +} + // WithAllowLabels configures which labels can be returned for metrics func (b *Builder) WithAllowLabels(labels map[string][]string) { if len(labels) > 0 { @@ -223,31 +231,31 @@ func (b *Builder) buildConfigMapStore() cache.Store { } func (b *Builder) buildCronJobStore() cache.Store { - return b.buildStoreFunc(cronJobMetricFamilies(b.allowLabelsList["cronjobs"]), &batchv1beta1.CronJob{}, createCronJobListWatch) + return b.buildStoreFunc(cronJobMetricFamilies(b.allowAnnotationsList["cronjobs"], b.allowLabelsList["cronjobs"]), &batchv1beta1.CronJob{}, createCronJobListWatch) } func (b *Builder) buildDaemonSetStore() cache.Store { - return b.buildStoreFunc(daemonSetMetricFamilies(b.allowLabelsList["daemonsets"]), &appsv1.DaemonSet{}, createDaemonSetListWatch) + return b.buildStoreFunc(daemonSetMetricFamilies(b.allowAnnotationsList["daemonsets"], b.allowLabelsList["daemonsets"]), &appsv1.DaemonSet{}, createDaemonSetListWatch) } func (b *Builder) buildDeploymentStore() cache.Store { - return b.buildStoreFunc(deploymentMetricFamilies(b.allowLabelsList["deployments"]), &appsv1.Deployment{}, createDeploymentListWatch) + return b.buildStoreFunc(deploymentMetricFamilies(b.allowAnnotationsList["deployments"], b.allowLabelsList["deployments"]), &appsv1.Deployment{}, createDeploymentListWatch) } func (b *Builder) buildEndpointsStore() cache.Store { - return b.buildStoreFunc(endpointMetricFamilies(b.allowLabelsList["endpoints"]), &v1.Endpoints{}, createEndpointsListWatch) + return b.buildStoreFunc(endpointMetricFamilies(b.allowAnnotationsList["endpoints"], b.allowLabelsList["endpoints"]), &v1.Endpoints{}, createEndpointsListWatch) } func (b *Builder) buildHPAStore() cache.Store { - return b.buildStoreFunc(hpaMetricFamilies(b.allowLabelsList["horizontalpodautoscalers"]), &autoscaling.HorizontalPodAutoscaler{}, createHPAListWatch) + return b.buildStoreFunc(hpaMetricFamilies(b.allowAnnotationsList["horizontalpodautoscalers"], b.allowLabelsList["horizontalpodautoscalers"]), &autoscaling.HorizontalPodAutoscaler{}, createHPAListWatch) } func (b *Builder) buildIngressStore() cache.Store { - return b.buildStoreFunc(ingressMetricFamilies(b.allowLabelsList["ingresses"]), &networkingv1.Ingress{}, createIngressListWatch) + return b.buildStoreFunc(ingressMetricFamilies(b.allowAnnotationsList["ingresses"], b.allowLabelsList["ingresses"]), &networkingv1.Ingress{}, createIngressListWatch) } func (b *Builder) buildJobStore() cache.Store { - return b.buildStoreFunc(jobMetricFamilies(b.allowLabelsList["jobs"]), &batchv1.Job{}, createJobListWatch) + return b.buildStoreFunc(jobMetricFamilies(b.allowAnnotationsList["jobs"], b.allowLabelsList["jobs"]), &batchv1.Job{}, createJobListWatch) } func (b *Builder) buildLimitRangeStore() cache.Store { @@ -259,23 +267,23 @@ func (b *Builder) buildMutatingWebhookConfigurationStore() cache.Store { } func (b *Builder) buildNamespaceStore() cache.Store { - return b.buildStoreFunc(namespaceMetricFamilies(b.allowLabelsList["namespaces"]), &v1.Namespace{}, createNamespaceListWatch) + return b.buildStoreFunc(namespaceMetricFamilies(b.allowAnnotationsList["namespaces"], b.allowLabelsList["namespaces"]), &v1.Namespace{}, createNamespaceListWatch) } func (b *Builder) buildNetworkPolicyStore() cache.Store { - return b.buildStoreFunc(networkPolicyMetricFamilies(b.allowLabelsList["networkpolicies"]), &networkingv1.NetworkPolicy{}, createNetworkPolicyListWatch) + return b.buildStoreFunc(networkPolicyMetricFamilies(b.allowAnnotationsList["networkpolicies"], b.allowLabelsList["networkpolicies"]), &networkingv1.NetworkPolicy{}, createNetworkPolicyListWatch) } func (b *Builder) buildNodeStore() cache.Store { - return b.buildStoreFunc(nodeMetricFamilies(b.allowLabelsList["nodes"]), &v1.Node{}, createNodeListWatch) + return b.buildStoreFunc(nodeMetricFamilies(b.allowAnnotationsList["nodes"], b.allowLabelsList["nodes"]), &v1.Node{}, createNodeListWatch) } func (b *Builder) buildPersistentVolumeClaimStore() cache.Store { - return b.buildStoreFunc(persistentVolumeClaimMetricFamilies(b.allowLabelsList["persistentvolumeclaims"]), &v1.PersistentVolumeClaim{}, createPersistentVolumeClaimListWatch) + return b.buildStoreFunc(persistentVolumeClaimMetricFamilies(b.allowAnnotationsList["persistentvolumeclaims"], b.allowLabelsList["persistentvolumeclaims"]), &v1.PersistentVolumeClaim{}, createPersistentVolumeClaimListWatch) } func (b *Builder) buildPersistentVolumeStore() cache.Store { - return b.buildStoreFunc(persistentVolumeMetricFamilies(b.allowLabelsList["persistentvolumes"]), &v1.PersistentVolume{}, createPersistentVolumeListWatch) + return b.buildStoreFunc(persistentVolumeMetricFamilies(b.allowAnnotationsList["persistentvolumes"], b.allowLabelsList["persistentvolumes"]), &v1.PersistentVolume{}, createPersistentVolumeListWatch) } func (b *Builder) buildPodDisruptionBudgetStore() cache.Store { @@ -283,7 +291,7 @@ func (b *Builder) buildPodDisruptionBudgetStore() cache.Store { } func (b *Builder) buildReplicaSetStore() cache.Store { - return b.buildStoreFunc(replicaSetMetricFamilies(b.allowLabelsList["replicasets"]), &appsv1.ReplicaSet{}, createReplicaSetListWatch) + return b.buildStoreFunc(replicaSetMetricFamilies(b.allowAnnotationsList["replicasets"], b.allowLabelsList["replicasets"]), &appsv1.ReplicaSet{}, createReplicaSetListWatch) } func (b *Builder) buildReplicationControllerStore() cache.Store { @@ -295,27 +303,27 @@ func (b *Builder) buildResourceQuotaStore() cache.Store { } func (b *Builder) buildSecretStore() cache.Store { - return b.buildStoreFunc(secretMetricFamilies(b.allowLabelsList["secrets"]), &v1.Secret{}, createSecretListWatch) + return b.buildStoreFunc(secretMetricFamilies(b.allowAnnotationsList["secrets"], b.allowLabelsList["secrets"]), &v1.Secret{}, createSecretListWatch) } func (b *Builder) buildServiceStore() cache.Store { - return b.buildStoreFunc(serviceMetricFamilies(b.allowLabelsList["services"]), &v1.Service{}, createServiceListWatch) + return b.buildStoreFunc(serviceMetricFamilies(b.allowAnnotationsList["services"], b.allowLabelsList["services"]), &v1.Service{}, createServiceListWatch) } func (b *Builder) buildStatefulSetStore() cache.Store { - return b.buildStoreFunc(statefulSetMetricFamilies(b.allowLabelsList["statefulsets"]), &appsv1.StatefulSet{}, createStatefulSetListWatch) + return b.buildStoreFunc(statefulSetMetricFamilies(b.allowAnnotationsList["statefulsets"], b.allowLabelsList["statefulsets"]), &appsv1.StatefulSet{}, createStatefulSetListWatch) } func (b *Builder) buildStorageClassStore() cache.Store { - return b.buildStoreFunc(storageClassMetricFamilies(b.allowLabelsList["storageclasses"]), &storagev1.StorageClass{}, createStorageClassListWatch) + return b.buildStoreFunc(storageClassMetricFamilies(b.allowAnnotationsList["storageclasses"], b.allowLabelsList["storageclasses"]), &storagev1.StorageClass{}, createStorageClassListWatch) } func (b *Builder) buildPodStore() cache.Store { - return b.buildStoreFunc(podMetricFamilies(b.allowLabelsList["pods"]), &v1.Pod{}, createPodListWatch) + return b.buildStoreFunc(podMetricFamilies(b.allowAnnotationsList["pods"], b.allowLabelsList["pods"]), &v1.Pod{}, createPodListWatch) } func (b *Builder) buildCsrStore() cache.Store { - return b.buildStoreFunc(csrMetricFamilies(b.allowLabelsList["certificatesigningrequests"]), &certv1.CertificateSigningRequest{}, createCSRListWatch) + return b.buildStoreFunc(csrMetricFamilies(b.allowAnnotationsList["certificatesigningrequests"], b.allowLabelsList["certificatesigningrequests"]), &certv1.CertificateSigningRequest{}, createCSRListWatch) } func (b *Builder) buildValidatingWebhookConfigurationStore() cache.Store { @@ -327,7 +335,7 @@ func (b *Builder) buildVolumeAttachmentStore() cache.Store { } func (b *Builder) buildVPAStore() cache.Store { - return b.buildStoreFunc(vpaMetricFamilies(b.allowLabelsList["verticalpodautoscalers"]), &vpaautoscaling.VerticalPodAutoscaler{}, createVPAListWatchFunc(b.vpaClient)) + return b.buildStoreFunc(vpaMetricFamilies(b.allowAnnotationsList["verticalpodautoscalers"], b.allowLabelsList["verticalpodautoscalers"]), &vpaautoscaling.VerticalPodAutoscaler{}, createVPAListWatchFunc(b.vpaClient)) } func (b *Builder) buildLeases() cache.Store { diff --git a/internal/store/certificatesigningrequest.go b/internal/store/certificatesigningrequest.go index 02452b013d..0583eb6845 100644 --- a/internal/store/certificatesigningrequest.go +++ b/internal/store/certificatesigningrequest.go @@ -31,13 +31,33 @@ import ( ) var ( + descCSRAnnotationsName = "kube_certificatesigningrequest_annotations" + descCSRAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descCSRLabelsName = "kube_certificatesigningrequest_labels" descCSRLabelsHelp = "Kubernetes labels converted to Prometheus labels." descCSRLabelsDefaultLabels = []string{"certificatesigningrequest"} ) -func csrMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func csrMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ + *generator.NewFamilyGenerator( + descCSRAnnotationsName, + descCSRAnnotationsHelp, + metric.Gauge, + "", + wrapCSRFunc(func(j *certv1.CertificateSigningRequest) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(j.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descCSRLabelsName, descCSRLabelsHelp, diff --git a/internal/store/certificatesigningrequest_test.go b/internal/store/certificatesigningrequest_test.go index 5e5e7ec3c7..9618e3c3b4 100644 --- a/internal/store/certificatesigningrequest_test.go +++ b/internal/store/certificatesigningrequest_test.go @@ -212,8 +212,8 @@ func TestCsrStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(csrMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(csrMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(csrMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(csrMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected error when collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/cronjob.go b/internal/store/cronjob.go index ff8f6267cd..955903feba 100644 --- a/internal/store/cronjob.go +++ b/internal/store/cronjob.go @@ -34,13 +34,33 @@ import ( ) var ( + descCronJobAnnotationsName = "kube_cronjob_annotations" + descCronJobAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descCronJobLabelsName = "kube_cronjob_labels" descCronJobLabelsHelp = "Kubernetes labels converted to Prometheus labels." descCronJobLabelsDefaultLabels = []string{"namespace", "cronjob"} ) -func cronJobMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func cronJobMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ + *generator.NewFamilyGenerator( + descCronJobAnnotationsName, + descCronJobAnnotationsHelp, + metric.Gauge, + "", + wrapCronJobFunc(func(j *batchv1beta1.CronJob) *metric.Family { + annotationKeys, annotationValues := createLabelKeysValues(j.Annotations, allowLabelsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descCronJobLabelsName, descCronJobLabelsHelp, diff --git a/internal/store/cronjob_test.go b/internal/store/cronjob_test.go index 919c6b7c94..c767c1d370 100644 --- a/internal/store/cronjob_test.go +++ b/internal/store/cronjob_test.go @@ -255,8 +255,8 @@ func TestCronJobStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(cronJobMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(cronJobMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(cronJobMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(cronJobMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/daemonset.go b/internal/store/daemonset.go index fc5886263b..8a76d58e06 100644 --- a/internal/store/daemonset.go +++ b/internal/store/daemonset.go @@ -31,12 +31,14 @@ import ( ) var ( + descDaemonSetAnnotationsName = "kube_daemonset_annotations" + descDaemonSetAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descDaemonSetLabelsName = "kube_daemonset_labels" descDaemonSetLabelsHelp = "Kubernetes labels converted to Prometheus labels." descDaemonSetLabelsDefaultLabels = []string{"namespace", "daemonset"} ) -func daemonSetMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func daemonSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_daemonset_created", @@ -210,6 +212,24 @@ func daemonSetMetricFamilies(allowLabelsList []string) []generator.FamilyGenerat } }), ), + *generator.NewFamilyGenerator( + descDaemonSetAnnotationsName, + descDaemonSetAnnotationsHelp, + metric.Gauge, + "", + wrapDaemonSetFunc(func(d *v1.DaemonSet) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(d.Annotations, allowLabelsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descDaemonSetLabelsName, descDaemonSetLabelsHelp, diff --git a/internal/store/daemonset_test.go b/internal/store/daemonset_test.go index 20709f7ae6..5a9bb2faa0 100644 --- a/internal/store/daemonset_test.go +++ b/internal/store/daemonset_test.go @@ -222,8 +222,8 @@ func TestDaemonSetStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(daemonSetMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(daemonSetMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(daemonSetMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(daemonSetMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/deployment.go b/internal/store/deployment.go index 41d17f55c8..39477671d2 100644 --- a/internal/store/deployment.go +++ b/internal/store/deployment.go @@ -32,12 +32,14 @@ import ( ) var ( + descDeploymentAnnotationsName = "kube_deployment_annotations" + descDeploymentAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descDeploymentLabelsName = "kube_deployment_labels" descDeploymentLabelsHelp = "Kubernetes labels converted to Prometheus labels." descDeploymentLabelsDefaultLabels = []string{"namespace", "deployment"} ) -func deploymentMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_deployment_created", @@ -251,6 +253,24 @@ func deploymentMetricFamilies(allowLabelsList []string) []generator.FamilyGenera } }), ), + *generator.NewFamilyGenerator( + descDeploymentAnnotationsName, + descDeploymentAnnotationsHelp, + metric.Gauge, + "", + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(d.Annotations, allowLabelsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descDeploymentLabelsName, descDeploymentLabelsHelp, diff --git a/internal/store/deployment_test.go b/internal/store/deployment_test.go index c4b246fbd6..afe563484a 100644 --- a/internal/store/deployment_test.go +++ b/internal/store/deployment_test.go @@ -43,6 +43,8 @@ func TestDeploymentStore(t *testing.T) { // Fixed metadata on type and help text. We prepend this to every expected // output so we only have to modify a single place when doing adjustments. const metadata = ` + # HELP kube_deployment_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_deployment_annotations gauge # HELP kube_deployment_created Unix creation timestamp # TYPE kube_deployment_created gauge # HELP kube_deployment_metadata_generation Sequence number representing a specific generation of the desired state. @@ -104,6 +106,7 @@ func TestDeploymentStore(t *testing.T) { }, }, Want: metadata + ` + kube_deployment_annotations{deployment="depl1",namespace="ns1"} 1 kube_deployment_created{deployment="depl1",namespace="ns1"} 1.5e+09 kube_deployment_labels{deployment="depl1",namespace="ns1"} 1 kube_deployment_metadata_generation{deployment="depl1",namespace="ns1"} 21 @@ -158,7 +161,8 @@ func TestDeploymentStore(t *testing.T) { }, }, Want: metadata + ` - kube_deployment_labels{deployment="depl2",namespace="ns2"} 1 + kube_deployment_annotations{deployment="depl2",namespace="ns2"} 1 + kube_deployment_labels{deployment="depl2",namespace="ns2"} 1 kube_deployment_metadata_generation{deployment="depl2",namespace="ns2"} 14 kube_deployment_spec_paused{deployment="depl2",namespace="ns2"} 1 kube_deployment_spec_replicas{deployment="depl2",namespace="ns2"} 5 @@ -183,8 +187,8 @@ func TestDeploymentStore(t *testing.T) { } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(deploymentMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(deploymentMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(deploymentMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(deploymentMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/endpoint.go b/internal/store/endpoint.go index f86ee654a0..a262985a7e 100644 --- a/internal/store/endpoint.go +++ b/internal/store/endpoint.go @@ -31,12 +31,14 @@ import ( ) var ( + descEndpointAnnotationsName = "kube_endpoint_annotations" + descEndpointAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descEndpointLabelsName = "kube_endpoint_labels" descEndpointLabelsHelp = "Kubernetes labels converted to Prometheus labels." descEndpointLabelsDefaultLabels = []string{"namespace", "endpoint"} ) -func endpointMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func endpointMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_endpoint_info", @@ -73,6 +75,24 @@ func endpointMetricFamilies(allowLabelsList []string) []generator.FamilyGenerato } }), ), + *generator.NewFamilyGenerator( + descEndpointAnnotationsName, + descEndpointAnnotationsHelp, + metric.Gauge, + "", + wrapEndpointFunc(func(e *v1.Endpoints) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(e.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descEndpointLabelsName, descEndpointLabelsHelp, diff --git a/internal/store/endpoint_test.go b/internal/store/endpoint_test.go index b1033ff93f..26f3afc933 100644 --- a/internal/store/endpoint_test.go +++ b/internal/store/endpoint_test.go @@ -30,6 +30,8 @@ func TestEndpointStore(t *testing.T) { // Fixed metadata on type and help text. We prepend this to every expected // output so we only have to modify a single place when doing adjustments. const metadata = ` + # HELP kube_endpoint_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_endpoint_annotations gauge # HELP kube_endpoint_address_available Number of addresses available in endpoint. # TYPE kube_endpoint_address_available gauge # HELP kube_endpoint_address_not_ready Number of addresses not ready in endpoint @@ -84,6 +86,7 @@ func TestEndpointStore(t *testing.T) { }, }, Want: metadata + ` + kube_endpoint_annotations{endpoint="test-endpoint",namespace="default"} 1 kube_endpoint_address_available{endpoint="test-endpoint",namespace="default"} 6 kube_endpoint_address_not_ready{endpoint="test-endpoint",namespace="default"} 6 kube_endpoint_created{endpoint="test-endpoint",namespace="default"} 1.5e+09 @@ -93,8 +96,8 @@ func TestEndpointStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(endpointMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(endpointMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(endpointMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(endpointMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } @@ -109,6 +112,8 @@ func TestEndpointStoreWithLabels(t *testing.T) { # TYPE kube_endpoint_address_available gauge # HELP kube_endpoint_address_not_ready Number of addresses not ready in endpoint # TYPE kube_endpoint_address_not_ready gauge + # HELP kube_endpoint_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_endpoint_annotations gauge # HELP kube_endpoint_created Unix creation timestamp # TYPE kube_endpoint_created gauge # HELP kube_endpoint_info Information about endpoint. @@ -123,6 +128,9 @@ func TestEndpointStoreWithLabels(t *testing.T) { Name: "test-endpoint", CreationTimestamp: metav1.Time{Time: time.Unix(1500000000, 0)}, Namespace: "default", + Annotations: map[string]string{ + "app": "foobar", + }, Labels: map[string]string{ "app": "foobar", }, @@ -161,6 +169,7 @@ func TestEndpointStoreWithLabels(t *testing.T) { Want: metadata + ` kube_endpoint_address_available{endpoint="test-endpoint",namespace="default"} 6 kube_endpoint_address_not_ready{endpoint="test-endpoint",namespace="default"} 6 + kube_endpoint_annotations{endpoint="test-endpoint",annotation_app="foobar",namespace="default"} 1 kube_endpoint_created{endpoint="test-endpoint",namespace="default"} 1.5e+09 kube_endpoint_info{endpoint="test-endpoint",namespace="default"} 1 kube_endpoint_labels{endpoint="test-endpoint",label_app="foobar",namespace="default"} 1 @@ -168,11 +177,14 @@ func TestEndpointStoreWithLabels(t *testing.T) { }, } for i, c := range cases { + allowAnnotations := []string{ + "app", + } allowLabels := []string{ "app", } - c.Func = generator.ComposeMetricGenFuncs(endpointMetricFamilies(allowLabels)) - c.Headers = generator.ExtractMetricFamilyHeaders(endpointMetricFamilies(allowLabels)) + c.Func = generator.ComposeMetricGenFuncs(endpointMetricFamilies(allowAnnotations, allowLabels)) + c.Headers = generator.ExtractMetricFamilyHeaders(endpointMetricFamilies(allowAnnotations, allowLabels)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/horizontalpodautoscaler.go b/internal/store/horizontalpodautoscaler.go index 55af5bcfb0..9582628af2 100644 --- a/internal/store/horizontalpodautoscaler.go +++ b/internal/store/horizontalpodautoscaler.go @@ -45,6 +45,8 @@ func (m metricTargetType) String() string { } var ( + descHorizontalPodAutoscalerAnnotationsName = "kube_horizontalpodautoscaler_annotations" + descHorizontalPodAutoscalerAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descHorizontalPodAutoscalerLabelsName = "kube_horizontalpodautoscaler_labels" descHorizontalPodAutoscalerLabelsHelp = "Kubernetes labels converted to Prometheus labels." descHorizontalPodAutoscalerLabelsDefaultLabels = []string{"namespace", "horizontalpodautoscaler"} @@ -52,7 +54,7 @@ var ( targetMetricLabels = []string{"metric_name", "metric_target_type"} ) -func hpaMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func hpaMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_horizontalpodautoscaler_metadata_generation", @@ -192,6 +194,24 @@ func hpaMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { } }), ), + *generator.NewFamilyGenerator( + descHorizontalPodAutoscalerAnnotationsName, + descHorizontalPodAutoscalerAnnotationsHelp, + metric.Gauge, + "", + wrapHPAFunc(func(a *autoscaling.HorizontalPodAutoscaler) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(a.Annotations, allowLabelsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descHorizontalPodAutoscalerLabelsName, descHorizontalPodAutoscalerLabelsHelp, diff --git a/internal/store/horizontalpodautoscaler_test.go b/internal/store/horizontalpodautoscaler_test.go index 09e20ba468..a26e849f7a 100644 --- a/internal/store/horizontalpodautoscaler_test.go +++ b/internal/store/horizontalpodautoscaler_test.go @@ -309,8 +309,8 @@ func TestHPAStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(hpaMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(hpaMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(hpaMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(hpaMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/ingress.go b/internal/store/ingress.go index 85c0b001ee..137acf88b5 100644 --- a/internal/store/ingress.go +++ b/internal/store/ingress.go @@ -32,12 +32,14 @@ import ( ) var ( + descIngressAnnotationsName = "kube_ingress_annotations" + descIngressAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descIngressLabelsName = "kube_ingress_labels" descIngressLabelsHelp = "Kubernetes labels converted to Prometheus labels." descIngressLabelsDefaultLabels = []string{"namespace", "ingress"} ) -func ingressMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func ingressMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_ingress_info", @@ -53,6 +55,24 @@ func ingressMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator }} }), ), + *generator.NewFamilyGenerator( + descIngressAnnotationsName, + descIngressAnnotationsHelp, + metric.Gauge, + "", + wrapIngressFunc(func(i *networkingv1.Ingress) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(i.Annotations, allowLabelsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }} + + }), + ), *generator.NewFamilyGenerator( descIngressLabelsName, descIngressLabelsHelp, diff --git a/internal/store/ingress_test.go b/internal/store/ingress_test.go index 790c04807d..e4871943ab 100644 --- a/internal/store/ingress_test.go +++ b/internal/store/ingress_test.go @@ -169,8 +169,8 @@ func TestIngressStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(ingressMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(ingressMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(ingressMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(ingressMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/job.go b/internal/store/job.go index 5cf2ab74b6..b042f1ffd7 100644 --- a/internal/store/job.go +++ b/internal/store/job.go @@ -32,14 +32,34 @@ import ( ) var ( + descJobAnnotationsName = "kube_job_annotations" + descJobAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descJobLabelsName = "kube_job_labels" descJobLabelsHelp = "Kubernetes labels converted to Prometheus labels." descJobLabelsDefaultLabels = []string{"namespace", "job_name"} jobFailureReasons = []string{"BackoffLimitExceeded", "DeadLineExceeded", "Evicted"} ) -func jobMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func jobMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ + *generator.NewFamilyGenerator( + descJobAnnotationsName, + descJobAnnotationsHelp, + metric.Gauge, + "", + wrapJobFunc(func(j *v1batch.Job) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(j.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descJobLabelsName, descJobLabelsHelp, diff --git a/internal/store/job_test.go b/internal/store/job_test.go index c81de4f15a..3cfb68ef70 100644 --- a/internal/store/job_test.go +++ b/internal/store/job_test.go @@ -48,6 +48,8 @@ func TestJobStore(t *testing.T) { // Fixed metadata on type and help text. We prepend this to every expected // output so we only have to modify a single place when doing adjustments. const metadata = ` + # HELP kube_job_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_job_annotations gauge # HELP kube_job_created Unix creation timestamp # TYPE kube_job_created gauge # HELP kube_job_owner Information about the Job's owner. @@ -110,6 +112,7 @@ func TestJobStore(t *testing.T) { }, }, Want: metadata + ` + kube_job_annotations{job_name="RunningJob1",namespace="ns1"} 1 kube_job_owner{job_name="RunningJob1",namespace="ns1",owner_is_controller="true",owner_kind="CronJob",owner_name="cronjob-name"} 1 kube_job_created{job_name="RunningJob1",namespace="ns1"} 1.5e+09 kube_job_info{job_name="RunningJob1",namespace="ns1"} 1 @@ -150,6 +153,7 @@ func TestJobStore(t *testing.T) { }, }, Want: metadata + ` + kube_job_annotations{job_name="SuccessfulJob1",namespace="ns1"} 1 kube_job_owner{job_name="SuccessfulJob1",namespace="ns1",owner_is_controller="",owner_kind="",owner_name=""} 1 kube_job_complete{condition="false",job_name="SuccessfulJob1",namespace="ns1"} 0 kube_job_complete{condition="true",job_name="SuccessfulJob1",namespace="ns1"} 1 @@ -193,6 +197,7 @@ func TestJobStore(t *testing.T) { }, }, Want: metadata + ` + kube_job_annotations{job_name="FailedJob1",namespace="ns1"} 1 kube_job_owner{job_name="FailedJob1",namespace="ns1",owner_is_controller="",owner_kind="",owner_name=""} 1 kube_job_failed{condition="false",job_name="FailedJob1",namespace="ns1"} 0 kube_job_failed{condition="true",job_name="FailedJob1",namespace="ns1"} 1 @@ -242,6 +247,7 @@ func TestJobStore(t *testing.T) { kube_job_complete{condition="false",job_name="SuccessfulJob2NoActiveDeadlineSeconds",namespace="ns1"} 0 kube_job_complete{condition="true",job_name="SuccessfulJob2NoActiveDeadlineSeconds",namespace="ns1"} 1 + kube_job_annotations{job_name="SuccessfulJob2NoActiveDeadlineSeconds",namespace="ns1"} 1 kube_job_complete{condition="unknown",job_name="SuccessfulJob2NoActiveDeadlineSeconds",namespace="ns1"} 0 kube_job_info{job_name="SuccessfulJob2NoActiveDeadlineSeconds",namespace="ns1"} 1 kube_job_labels{job_name="SuccessfulJob2NoActiveDeadlineSeconds",namespace="ns1"} 1 @@ -256,8 +262,8 @@ func TestJobStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(jobMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(jobMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(jobMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(jobMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/namespace.go b/internal/store/namespace.go index c1e994f543..5d256c13d8 100644 --- a/internal/store/namespace.go +++ b/internal/store/namespace.go @@ -31,12 +31,14 @@ import ( ) var ( + descNamespaceAnnotationsName = "kube_namespace_annotations" + descNamespaceAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descNamespaceLabelsName = "kube_namespace_labels" descNamespaceLabelsHelp = "Kubernetes labels converted to Prometheus labels." descNamespaceLabelsDefaultLabels = []string{"namespace"} ) -func namespaceMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func namespaceMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_namespace_created", @@ -56,6 +58,24 @@ func namespaceMetricFamilies(allowLabelsList []string) []generator.FamilyGenerat } }), ), + *generator.NewFamilyGenerator( + descNamespaceAnnotationsName, + descNamespaceAnnotationsHelp, + metric.Gauge, + "", + wrapNamespaceFunc(func(n *v1.Namespace) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(n.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descNamespaceLabelsName, descNamespaceLabelsHelp, diff --git a/internal/store/namespace_test.go b/internal/store/namespace_test.go index 1849ff665e..f550ed0e1e 100644 --- a/internal/store/namespace_test.go +++ b/internal/store/namespace_test.go @@ -30,6 +30,8 @@ func TestNamespaceStore(t *testing.T) { // Fixed metadata on type and help text. We prepend this to every expected // output so we only have to modify a single place when doing adjustments. const metadata = ` + # HELP kube_namespace_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_namespace_annotations gauge # HELP kube_namespace_created Unix creation timestamp # TYPE kube_namespace_created gauge # HELP kube_namespace_labels Kubernetes labels converted to Prometheus labels. @@ -54,6 +56,7 @@ func TestNamespaceStore(t *testing.T) { }, }, Want: metadata + ` + kube_namespace_annotations{namespace="nsActiveTest"} 1 kube_namespace_labels{namespace="nsActiveTest"} 1 kube_namespace_status_phase{namespace="nsActiveTest",phase="Active"} 1 kube_namespace_status_phase{namespace="nsActiveTest",phase="Terminating"} 0 @@ -72,6 +75,7 @@ func TestNamespaceStore(t *testing.T) { }, }, Want: metadata + ` + kube_namespace_annotations{namespace="nsTerminateTest"} 1 kube_namespace_labels{namespace="nsTerminateTest"} 1 kube_namespace_status_phase{namespace="nsTerminateTest",phase="Active"} 0 kube_namespace_status_phase{namespace="nsTerminateTest",phase="Terminating"} 1 @@ -95,6 +99,7 @@ func TestNamespaceStore(t *testing.T) { }, }, Want: metadata + ` + kube_namespace_annotations{namespace="nsTerminateWithConditionTest"} 1 kube_namespace_labels{namespace="nsTerminateWithConditionTest"} 1 kube_namespace_status_phase{namespace="nsTerminateWithConditionTest",phase="Active"} 0 kube_namespace_status_phase{namespace="nsTerminateWithConditionTest",phase="Terminating"} 1 @@ -127,6 +132,7 @@ func TestNamespaceStore(t *testing.T) { }, }, Want: metadata + ` + kube_namespace_annotations{namespace="ns1"} 1 kube_namespace_created{namespace="ns1"} 1.5e+09 kube_namespace_labels{namespace="ns1"} 1 kube_namespace_status_phase{namespace="ns1",phase="Active"} 1 @@ -150,6 +156,7 @@ func TestNamespaceStore(t *testing.T) { }, }, Want: metadata + ` + kube_namespace_annotations{namespace="ns2"} 1 kube_namespace_labels{namespace="ns2"} 1 kube_namespace_status_phase{namespace="ns2",phase="Active"} 1 kube_namespace_status_phase{namespace="ns2",phase="Terminating"} 0 @@ -158,8 +165,8 @@ func TestNamespaceStore(t *testing.T) { } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(namespaceMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(namespaceMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(namespaceMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(namespaceMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/networkpolicy.go b/internal/store/networkpolicy.go index 96d095299b..c9ee0df8ff 100644 --- a/internal/store/networkpolicy.go +++ b/internal/store/networkpolicy.go @@ -31,10 +31,14 @@ import ( ) var ( + descNetworkPolicyAnnotationsName = "kube_networkpolicy_annotations" + descNetworkPolicyAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." + descNetworkPolicyLabelsName = "kube_networkpolicy_labels" + descNetworkPolicyLabelsHelp = "Kubernetes labels converted to Prometheus labels." descNetworkPolicyLabelsDefaultLabels = []string{"namespace", "networkpolicy"} ) -func networkPolicyMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func networkPolicyMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_networkpolicy_created", @@ -54,8 +58,26 @@ func networkPolicyMetricFamilies(allowLabelsList []string) []generator.FamilyGen }), ), *generator.NewFamilyGenerator( - "kube_networkpolicy_labels", - "Kubernetes labels converted to Prometheus labels", + descNetworkPolicyAnnotationsName, + descNetworkPolicyAnnotationsHelp, + metric.Gauge, + "", + wrapNetworkPolicyFunc(func(n *networkingv1.NetworkPolicy) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(n.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), + *generator.NewFamilyGenerator( + descNetworkPolicyLabelsName, + descNetworkPolicyLabelsHelp, metric.Gauge, "", wrapNetworkPolicyFunc(func(n *networkingv1.NetworkPolicy) *metric.Family { diff --git a/internal/store/networkpolicy_test.go b/internal/store/networkpolicy_test.go index 5a5ef2bb03..22b693c081 100644 --- a/internal/store/networkpolicy_test.go +++ b/internal/store/networkpolicy_test.go @@ -68,7 +68,7 @@ func TestNetworkPolicyStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(networkPolicyMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(networkPolicyMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %dth run:\n%s", i, err) } diff --git a/internal/store/node.go b/internal/store/node.go index efdeb9d831..3162897070 100644 --- a/internal/store/node.go +++ b/internal/store/node.go @@ -33,15 +33,18 @@ import ( ) var ( + descNodeAnnotationsName = "kube_node_annotations" + descNodeAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descNodeLabelsName = "kube_node_labels" descNodeLabelsHelp = "Kubernetes labels converted to Prometheus labels." descNodeLabelsDefaultLabels = []string{"node"} ) -func nodeMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func nodeMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ createNodeCreatedFamilyGenerator(), createNodeInfoFamilyGenerator(), + createNodeAnnotationsGenerator(allowAnnotationsList), createNodeLabelsGenerator(allowLabelsList), createNodeRoleFamilyGenerator(), createNodeSpecTaintFamilyGenerator(), @@ -123,6 +126,27 @@ func createNodeInfoFamilyGenerator() generator.FamilyGenerator { ) } +func createNodeAnnotationsGenerator(allowAnnotationsList []string) generator.FamilyGenerator { + return *generator.NewFamilyGenerator( + descNodeAnnotationsName, + descNodeAnnotationsHelp, + metric.Gauge, + "", + wrapNodeFunc(func(n *v1.Node) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(n.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ) +} + func createNodeLabelsGenerator(allowLabelsList []string) generator.FamilyGenerator { return *generator.NewFamilyGenerator( descNodeLabelsName, diff --git a/internal/store/node_test.go b/internal/store/node_test.go index ce5f00cfbd..73e05f26a6 100644 --- a/internal/store/node_test.go +++ b/internal/store/node_test.go @@ -276,8 +276,8 @@ func TestNodeStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(nodeMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(nodeMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(nodeMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(nodeMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/persistentvolume.go b/internal/store/persistentvolume.go index cfa74943de..a1348732e2 100644 --- a/internal/store/persistentvolume.go +++ b/internal/store/persistentvolume.go @@ -36,12 +36,14 @@ var ( descPersistentVolumeClaimRefHelp = "Information about the Persitant Volume Claim Reference." descPersistentVolumeClaimRefDefaultLabels = []string{"persistentvolume"} + descPersistentVolumeAnnotationsName = "kube_persistentvolume_annotations" + descPersistentVolumeAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descPersistentVolumeLabelsName = "kube_persistentvolume_labels" descPersistentVolumeLabelsHelp = "Kubernetes labels converted to Prometheus labels." descPersistentVolumeLabelsDefaultLabels = []string{"persistentvolume"} ) -func persistentVolumeMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func persistentVolumeMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( descPersistentVolumeClaimRefName, @@ -73,6 +75,24 @@ func persistentVolumeMetricFamilies(allowLabelsList []string) []generator.Family } }), ), + *generator.NewFamilyGenerator( + descPersistentVolumeAnnotationsName, + descPersistentVolumeAnnotationsHelp, + metric.Gauge, + "", + wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(p.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descPersistentVolumeLabelsName, descPersistentVolumeLabelsHelp, diff --git a/internal/store/persistentvolume_test.go b/internal/store/persistentvolume_test.go index 82b40c62c9..c9b3e61f83 100644 --- a/internal/store/persistentvolume_test.go +++ b/internal/store/persistentvolume_test.go @@ -482,8 +482,8 @@ func TestPersistentVolumeStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(persistentVolumeMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(persistentVolumeMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(persistentVolumeMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(persistentVolumeMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/persistentvolumeclaim.go b/internal/store/persistentvolumeclaim.go index e68928898c..eeebaa2bf7 100644 --- a/internal/store/persistentvolumeclaim.go +++ b/internal/store/persistentvolumeclaim.go @@ -31,12 +31,14 @@ import ( ) var ( + descPersistentVolumeClaimAnnotationsName = "kube_persistentvolumeclaim_annotations" + descPersistentVolumeClaimAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descPersistentVolumeClaimLabelsName = "kube_persistentvolumeclaim_labels" descPersistentVolumeClaimLabelsHelp = "Kubernetes labels converted to Prometheus labels." descPersistentVolumeClaimLabelsDefaultLabels = []string{"namespace", "persistentvolumeclaim"} ) -func persistentVolumeClaimMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func persistentVolumeClaimMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( descPersistentVolumeClaimLabelsName, @@ -44,12 +46,12 @@ func persistentVolumeClaimMetricFamilies(allowLabelsList []string) []generator.F metric.Gauge, "", wrapPersistentVolumeClaimFunc(func(p *v1.PersistentVolumeClaim) *metric.Family { - labelKeys, labelValues := createLabelKeysValues(p.Labels, allowLabelsList) + annotationKeys, annotationValues := createAnnotationKeysValues(p.Annotations, allowAnnotationsList) return &metric.Family{ Metrics: []*metric.Metric{ { - LabelKeys: labelKeys, - LabelValues: labelValues, + LabelKeys: annotationKeys, + LabelValues: annotationValues, Value: 1, }, }, diff --git a/internal/store/persistentvolumeclaim_test.go b/internal/store/persistentvolumeclaim_test.go index 7a87986553..f2369abb8d 100644 --- a/internal/store/persistentvolumeclaim_test.go +++ b/internal/store/persistentvolumeclaim_test.go @@ -183,8 +183,8 @@ func TestPersistentVolumeClaimStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(persistentVolumeClaimMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(persistentVolumeClaimMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(persistentVolumeClaimMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(persistentVolumeClaimMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/pod.go b/internal/store/pod.go index 1589b7ab14..9d3d46357e 100644 --- a/internal/store/pod.go +++ b/internal/store/pod.go @@ -37,7 +37,7 @@ var ( podStatusReasons = []string{"NodeLost", "Evicted", "UnexpectedAdmissionError"} ) -func podMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func podMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ createPodCompletionTimeFamilyGenerator(), createPodContainerInfoFamilyGenerator(), @@ -74,6 +74,7 @@ func podMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { createPodInitContainerStatusTerminatedReasonFamilyGenerator(), createPodInitContainerStatusWaitingFamilyGenerator(), createPodInitContainerStatusWaitingReasonFamilyGenerator(), + createPodAnnotationsGenerator(allowAnnotationsList), createPodLabelsGenerator(allowLabelsList), createPodOverheadCPUCoresFamilyGenerator(), createPodOverheadMemoryBytesFamilyGenerator(), @@ -1125,6 +1126,26 @@ func createPodInitContainerStatusWaitingReasonFamilyGenerator() generator.Family ) } +func createPodAnnotationsGenerator(allowAnnotations []string) generator.FamilyGenerator { + return *generator.NewFamilyGenerator( + "kube_pod_annotations", + "Kubernetes annotations converted to Prometheus labels.", + metric.Gauge, + "", + wrapPodFunc(func(p *v1.Pod) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(p.Annotations, allowAnnotations) + m := metric.Metric{ + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + } + return &metric.Family{ + Metrics: []*metric.Metric{&m}, + } + }), + ) +} + func createPodLabelsGenerator(allowLabelsList []string) generator.FamilyGenerator { return *generator.NewFamilyGenerator( "kube_pod_labels", diff --git a/internal/store/pod_test.go b/internal/store/pod_test.go index 949c9134c6..07c9800924 100644 --- a/internal/store/pod_test.go +++ b/internal/store/pod_test.go @@ -1537,11 +1537,33 @@ func TestPodStore(t *testing.T) { "kube_pod_labels", }, }, + { + Obj: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "ns1", + UID: "uid1", + Annotations: map[string]string{ + "app": "example", + }, + }, + Spec: v1.PodSpec{}, + }, + AllowAnnotationsList: []string{options.LabelWildcard}, + Want: ` + # HELP kube_pod_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_pod_annotations gauge + kube_pod_annotations{annotation_app="example",namespace="ns1",pod="pod1",uid="uid1"} 1 + `, + MetricNames: []string{ + "kube_pod_annotations", + }, + }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(podMetricFamilies(c.AllowLabelsList)) - c.Headers = generator.ExtractMetricFamilyHeaders(podMetricFamilies(c.AllowLabelsList)) + c.Func = generator.ComposeMetricGenFuncs(podMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) + c.Headers = generator.ExtractMetricFamilyHeaders(podMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } @@ -1551,7 +1573,7 @@ func TestPodStore(t *testing.T) { func BenchmarkPodStore(b *testing.B) { b.ReportAllocs() - f := generator.ComposeMetricGenFuncs(podMetricFamilies(nil)) + f := generator.ComposeMetricGenFuncs(podMetricFamilies(nil, nil)) pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ diff --git a/internal/store/replicaset.go b/internal/store/replicaset.go index 6c8a259365..1159a68388 100644 --- a/internal/store/replicaset.go +++ b/internal/store/replicaset.go @@ -33,11 +33,13 @@ import ( var ( descReplicaSetLabelsDefaultLabels = []string{"namespace", "replicaset"} + descReplicaSetAnnotationsName = "kube_replicaset_annotations" + descReplicaSetAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descReplicaSetLabelsName = "kube_replicaset_labels" descReplicaSetLabelsHelp = "Kubernetes labels converted to Prometheus labels." ) -func replicaSetMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func replicaSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_replicaset_created", @@ -197,6 +199,24 @@ func replicaSetMetricFamilies(allowLabelsList []string) []generator.FamilyGenera } }), ), + *generator.NewFamilyGenerator( + descReplicaSetAnnotationsName, + descReplicaSetAnnotationsHelp, + metric.Gauge, + "", + wrapReplicaSetFunc(func(r *v1.ReplicaSet) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(r.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descReplicaSetLabelsName, descReplicaSetLabelsHelp, diff --git a/internal/store/replicaset_test.go b/internal/store/replicaset_test.go index 3972fb8b25..7735c95308 100644 --- a/internal/store/replicaset_test.go +++ b/internal/store/replicaset_test.go @@ -36,9 +36,11 @@ func TestReplicaSetStore(t *testing.T) { // Fixed metadata on type and help text. We prepend this to every expected // output so we only have to modify a single place when doing adjustments. const metadata = ` + # HELP kube_replicaset_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_replicaset_annotations gauge # HELP kube_replicaset_created Unix creation timestamp # TYPE kube_replicaset_created gauge - # HELP kube_replicaset_metadata_generation Sequence number representing a specific generation of the desired state. + # HELP kube_replicaset_metadata_generation Sequence number representing a specific generation of the desired state. # TYPE kube_replicaset_metadata_generation gauge # HELP kube_replicaset_status_replicas The number of replicas per ReplicaSet. # TYPE kube_replicaset_status_replicas gauge @@ -85,6 +87,7 @@ func TestReplicaSetStore(t *testing.T) { }, }, Want: metadata + ` + kube_replicaset_annotations{replicaset="rs1",namespace="ns1"} 1 kube_replicaset_labels{replicaset="rs1",namespace="ns1"} 1 kube_replicaset_created{namespace="ns1",replicaset="rs1"} 1.5e+09 kube_replicaset_metadata_generation{namespace="ns1",replicaset="rs1"} 21 @@ -118,6 +121,7 @@ func TestReplicaSetStore(t *testing.T) { }, }, Want: metadata + ` + kube_replicaset_annotations{replicaset="rs2",namespace="ns2"} 1 kube_replicaset_labels{replicaset="rs2",namespace="ns2"} 1 kube_replicaset_metadata_generation{namespace="ns2",replicaset="rs2"} 14 kube_replicaset_status_replicas{namespace="ns2",replicaset="rs2"} 0 @@ -130,8 +134,8 @@ func TestReplicaSetStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(replicaSetMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(replicaSetMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(replicaSetMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(replicaSetMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/secret.go b/internal/store/secret.go index 9b1599798e..f443f9a76f 100644 --- a/internal/store/secret.go +++ b/internal/store/secret.go @@ -31,12 +31,14 @@ import ( ) var ( + descSecretAnnotationsName = "kube_secret_annotations" + descSecretAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descSecretLabelsName = "kube_secret_labels" descSecretLabelsHelp = "Kubernetes labels converted to Prometheus labels." descSecretLabelsDefaultLabels = []string{"namespace", "secret"} ) -func secretMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func secretMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_secret_info", @@ -70,6 +72,25 @@ func secretMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator } }), ), + *generator.NewFamilyGenerator( + descSecretAnnotationsName, + descSecretAnnotationsHelp, + metric.Gauge, + "", + wrapSecretFunc(func(s *v1.Secret) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(s.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + + }), + ), *generator.NewFamilyGenerator( descSecretLabelsName, descSecretLabelsHelp, diff --git a/internal/store/secret_test.go b/internal/store/secret_test.go index 052021ff72..96f88e1753 100644 --- a/internal/store/secret_test.go +++ b/internal/store/secret_test.go @@ -116,8 +116,8 @@ func TestSecretStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(secretMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(secretMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(secretMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(secretMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/service.go b/internal/store/service.go index 15a1393a24..91f170f31c 100644 --- a/internal/store/service.go +++ b/internal/store/service.go @@ -31,12 +31,14 @@ import ( ) var ( + descServiceAnnotationsName = "kube_service_annotations" + descServiceAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descServiceLabelsName = "kube_service_labels" descServiceLabelsHelp = "Kubernetes labels converted to Prometheus labels." descServiceLabelsDefaultLabels = []string{"namespace", "service"} ) -func serviceMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func serviceMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_service_info", @@ -84,6 +86,21 @@ func serviceMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator return &metric.Family{Metrics: []*metric.Metric{&m}} }), ), + *generator.NewFamilyGenerator( + descServiceAnnotationsName, + descServiceAnnotationsHelp, + metric.Gauge, + "", + wrapSvcFunc(func(s *v1.Service) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(s.Annotations, allowAnnotationsList) + m := metric.Metric{ + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + } + return &metric.Family{Metrics: []*metric.Metric{&m}} + }), + ), *generator.NewFamilyGenerator( descServiceLabelsName, descServiceLabelsHelp, diff --git a/internal/store/service_test.go b/internal/store/service_test.go index b9545d7ad0..d6f0c1838f 100644 --- a/internal/store/service_test.go +++ b/internal/store/service_test.go @@ -30,6 +30,8 @@ func TestServiceStore(t *testing.T) { // Fixed metadata on type and help text. We prepend this to every expected // output so we only have to modify a single place when doing adjustments. const metadata = ` + # HELP kube_service_annotations Kubernetes annotations converted to Prometheus labels. + # TYPE kube_service_annotations gauge # HELP kube_service_info Information about service. # TYPE kube_service_info gauge # HELP kube_service_created Unix creation timestamp @@ -60,20 +62,24 @@ func TestServiceStore(t *testing.T) { }, }, Want: ` + # HELP kube_service_annotations Kubernetes annotations converted to Prometheus labels. # HELP kube_service_created Unix creation timestamp # HELP kube_service_info Information about service. # HELP kube_service_labels Kubernetes labels converted to Prometheus labels. # HELP kube_service_spec_type Type about service. + # TYPE kube_service_annotations gauge # TYPE kube_service_created gauge # TYPE kube_service_info gauge # TYPE kube_service_labels gauge # TYPE kube_service_spec_type gauge + kube_service_annotations{namespace="default",service="test-service1"} 1 kube_service_created{namespace="default",service="test-service1"} 1.5e+09 kube_service_info{cluster_ip="1.2.3.4",external_name="",load_balancer_ip="",namespace="default",service="test-service1"} 1 kube_service_labels{namespace="default",service="test-service1"} 1 kube_service_spec_type{namespace="default",service="test-service1",type="ClusterIP"} 1 `, MetricNames: []string{ + "kube_service_annotations", "kube_service_created", "kube_service_info", "kube_service_labels", @@ -97,6 +103,7 @@ func TestServiceStore(t *testing.T) { }, }, Want: metadata + ` + kube_service_annotations{namespace="default",service="test-service2"} 1 kube_service_created{namespace="default",service="test-service2"} 1.5e+09 kube_service_info{cluster_ip="1.2.3.5",external_name="",load_balancer_ip="",namespace="default",service="test-service2"} 1 kube_service_labels{namespace="default",service="test-service2"} 1 @@ -120,6 +127,7 @@ func TestServiceStore(t *testing.T) { }, }, Want: metadata + ` + kube_service_annotations{namespace="default",service="test-service3"} 1 kube_service_created{namespace="default",service="test-service3"} 1.5e+09 kube_service_info{cluster_ip="1.2.3.6",external_name="",load_balancer_ip="1.2.3.7",namespace="default",service="test-service3"} 1 kube_service_labels{namespace="default",service="test-service3"} 1 @@ -142,6 +150,7 @@ func TestServiceStore(t *testing.T) { }, }, Want: metadata + ` + kube_service_annotations{namespace="default",service="test-service4"} 1 kube_service_created{namespace="default",service="test-service4"} 1.5e+09 kube_service_info{cluster_ip="",external_name="www.example.com",load_balancer_ip="",namespace="default",service="test-service4"} 1 kube_service_labels{namespace="default",service="test-service4"} 1 @@ -173,6 +182,7 @@ func TestServiceStore(t *testing.T) { }, }, Want: metadata + ` + kube_service_annotations{namespace="default",service="test-service5"} 1 kube_service_created{namespace="default",service="test-service5"} 1.5e+09 kube_service_info{cluster_ip="",external_name="",load_balancer_ip="",namespace="default",service="test-service5"} 1 kube_service_labels{namespace="default",service="test-service5"} 1 @@ -199,6 +209,7 @@ func TestServiceStore(t *testing.T) { }, }, Want: metadata + ` + kube_service_annotations{namespace="default",service="test-service6"} 1 kube_service_created{namespace="default",service="test-service6"} 1.5e+09 kube_service_info{cluster_ip="",external_name="",load_balancer_ip="",namespace="default",service="test-service6"} 1 kube_service_labels{namespace="default",service="test-service6"} 1 @@ -209,8 +220,8 @@ func TestServiceStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(serviceMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(serviceMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(serviceMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(serviceMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/statefulset.go b/internal/store/statefulset.go index 624e5aba96..9d3f096c42 100644 --- a/internal/store/statefulset.go +++ b/internal/store/statefulset.go @@ -31,12 +31,14 @@ import ( ) var ( + descStatefulSetAnnotationsName = "kube_statefulset_annotations" + descStatefulSetAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descStatefulSetLabelsName = "kube_statefulset_labels" descStatefulSetLabelsHelp = "Kubernetes labels converted to Prometheus labels." descStatefulSetLabelsDefaultLabels = []string{"namespace", "statefulset"} ) -func statefulSetMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func statefulSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_statefulset_created", @@ -166,6 +168,24 @@ func statefulSetMetricFamilies(allowLabelsList []string) []generator.FamilyGener } }), ), + *generator.NewFamilyGenerator( + descStatefulSetAnnotationsName, + descStatefulSetAnnotationsHelp, + metric.Gauge, + "", + wrapStatefulSetFunc(func(s *v1.StatefulSet) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(s.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descStatefulSetLabelsName, descStatefulSetLabelsHelp, diff --git a/internal/store/statefulset_test.go b/internal/store/statefulset_test.go index d9ef6670ee..eabca452d1 100644 --- a/internal/store/statefulset_test.go +++ b/internal/store/statefulset_test.go @@ -241,8 +241,8 @@ func TestStatefulSetStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(statefulSetMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(statefulSetMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(statefulSetMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(statefulSetMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/storageclass.go b/internal/store/storageclass.go index f4c4538e44..439c6737f5 100644 --- a/internal/store/storageclass.go +++ b/internal/store/storageclass.go @@ -29,6 +29,8 @@ import ( ) var ( + descStorageClassAnnotationsName = "kube_storageclass_annotations" + descStorageClassAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descStorageClassLabelsName = "kube_storageclass_labels" descStorageClassLabelsHelp = "Kubernetes labels converted to Prometheus labels." descStorageClassLabelsDefaultLabels = []string{"storageclass"} @@ -36,7 +38,7 @@ var ( defaultVolumeBindingMode = storagev1.VolumeBindingImmediate ) -func storageClassMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func storageClassMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ *generator.NewFamilyGenerator( "kube_storageclass_info", @@ -79,6 +81,24 @@ func storageClassMetricFamilies(allowLabelsList []string) []generator.FamilyGene } }), ), + *generator.NewFamilyGenerator( + descStorageClassAnnotationsName, + descStorageClassAnnotationsHelp, + metric.Gauge, + "", + wrapStorageClassFunc(func(s *storagev1.StorageClass) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(s.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descStorageClassLabelsName, descStorageClassLabelsHelp, diff --git a/internal/store/storageclass_test.go b/internal/store/storageclass_test.go index d3ee764fc8..c1f6866241 100644 --- a/internal/store/storageclass_test.go +++ b/internal/store/storageclass_test.go @@ -108,8 +108,8 @@ func TestStorageClassStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(storageClassMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(storageClassMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(storageClassMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(storageClassMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/internal/store/testutils.go b/internal/store/testutils.go index 86d3526cfb..fcebf167de 100644 --- a/internal/store/testutils.go +++ b/internal/store/testutils.go @@ -30,12 +30,13 @@ import ( ) type generateMetricsTestCase struct { - Obj interface{} - MetricNames []string - AllowLabelsList []string - Want string - Headers []string - Func func(interface{}) []metric.FamilyInterface + Obj interface{} + MetricNames []string + AllowAnnotationsList []string + AllowLabelsList []string + Want string + Headers []string + Func func(interface{}) []metric.FamilyInterface } func (testCase *generateMetricsTestCase) run() error { diff --git a/internal/store/utils.go b/internal/store/utils.go index e00205ea60..c7d405dcc0 100644 --- a/internal/store/utils.go +++ b/internal/store/utils.go @@ -73,6 +73,10 @@ func addConditionMetrics(cs v1.ConditionStatus) []*metric.Metric { return ms } +func kubeAnnotationssToPrometheusLabels(annotations map[string]string) ([]string, []string) { + return mapToPrometheusLabels(annotations, "annotation") +} + func kubeLabelsToPrometheusLabels(labels map[string]string) ([]string, []string) { return mapToPrometheusLabels(labels, "label") } @@ -172,6 +176,26 @@ func isPrefixedNativeResource(name v1.ResourceName) bool { return strings.Contains(string(name), v1.ResourceDefaultNamespacePrefix) } +// createAnnotationKeysValues takes in passed kubernetes annotations and allowed list in kubernetes label format +// it returns only those allowed annotations that exist in the list converting them to Prometheus labels. +func createAnnotationKeysValues(allKubeAnnotations map[string]string, allowList []string) ([]string, []string) { + allowedKubeAnnotations := make(map[string]string) + + if len(allowList) > 0 { + if allowList[0] == options.LabelWildcard { + return kubeAnnotationssToPrometheusLabels(allKubeAnnotations) + } + + for _, l := range allowList { + v, found := allKubeAnnotations[l] + if found { + allowedKubeAnnotations[l] = v + } + } + } + return kubeAnnotationssToPrometheusLabels(allowedKubeAnnotations) +} + // createLabelKeysValues takes in passed kubernetes labels and allowed list in kubernetes label format // it returns only those allowed labels that exist in the list converting them to Prometheus labels. func createLabelKeysValues(allKubeLabels map[string]string, allowList []string) ([]string, []string) { diff --git a/internal/store/verticalpodautoscaler.go b/internal/store/verticalpodautoscaler.go index 3093a2c13b..1783ef0b77 100644 --- a/internal/store/verticalpodautoscaler.go +++ b/internal/store/verticalpodautoscaler.go @@ -34,13 +34,33 @@ import ( ) var ( + descVerticalPodAutoscalerAnnotationsName = "kube_verticalpodautoscaler_annotations" + descVerticalPodAutoscalerAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." descVerticalPodAutoscalerLabelsName = "kube_verticalpodautoscaler_labels" descVerticalPodAutoscalerLabelsHelp = "Kubernetes labels converted to Prometheus labels." descVerticalPodAutoscalerLabelsDefaultLabels = []string{"namespace", "verticalpodautoscaler", "target_api_version", "target_kind", "target_name"} ) -func vpaMetricFamilies(allowLabelsList []string) []generator.FamilyGenerator { +func vpaMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { return []generator.FamilyGenerator{ + *generator.NewFamilyGenerator( + descVerticalPodAutoscalerAnnotationsName, + descVerticalPodAutoscalerAnnotationsHelp, + metric.Gauge, + "", + wrapVPAFunc(func(a *autoscaling.VerticalPodAutoscaler) *metric.Family { + annotationKeys, annotationValues := createAnnotationKeysValues(a.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), *generator.NewFamilyGenerator( descVerticalPodAutoscalerLabelsName, descVerticalPodAutoscalerLabelsHelp, diff --git a/internal/store/verticalpodautoscaler_test.go b/internal/store/verticalpodautoscaler_test.go index f3be025bc9..481c689ff3 100644 --- a/internal/store/verticalpodautoscaler_test.go +++ b/internal/store/verticalpodautoscaler_test.go @@ -133,8 +133,8 @@ func TestVPAStore(t *testing.T) { }, } for i, c := range cases { - c.Func = generator.ComposeMetricGenFuncs(vpaMetricFamilies(nil)) - c.Headers = generator.ExtractMetricFamilyHeaders(vpaMetricFamilies(nil)) + c.Func = generator.ComposeMetricGenFuncs(vpaMetricFamilies(nil, nil)) + c.Headers = generator.ExtractMetricFamilyHeaders(vpaMetricFamilies(nil, nil)) if err := c.run(); err != nil { t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) } diff --git a/main.go b/main.go index df7512fccd..3ba6036c18 100644 --- a/main.go +++ b/main.go @@ -150,6 +150,7 @@ func main() { storeBuilder.WithKubeClient(kubeClient) storeBuilder.WithVPAClient(vpaClient) storeBuilder.WithSharding(opts.Shard, opts.TotalShards) + storeBuilder.WithAllowAnnotations(opts.AnnotationsAllowList) storeBuilder.WithAllowLabels(opts.LabelsAllowList) ksmMetricsRegistry.MustRegister( diff --git a/main_test.go b/main_test.go index 278d283a49..2ce85e7263 100644 --- a/main_test.go +++ b/main_test.go @@ -75,6 +75,7 @@ func BenchmarkKubeStateMetrics(b *testing.B) { } builder.WithAllowDenyList(l) + builder.WithAllowAnnotations(map[string][]string{}) builder.WithAllowLabels(map[string][]string{}) // This test is not suitable to be compared in terms of time, as it includes @@ -165,7 +166,8 @@ func TestFullScrapeCycle(t *testing.T) { body, _ := io.ReadAll(resp.Body) - expected := `# HELP kube_pod_completion_time Completion time in unix timestamp for a pod. + expected := `# HELP kube_pod_annotations Kubernetes annotations converted to Prometheus labels. +# HELP kube_pod_completion_time Completion time in unix timestamp for a pod. # HELP kube_pod_container_info Information about a container in a pod. # HELP kube_pod_container_resource_limits The number of requested limit resource by a container. # HELP kube_pod_container_resource_requests The number of requested request resource by a container. @@ -215,6 +217,7 @@ func TestFullScrapeCycle(t *testing.T) { # HELP kube_pod_status_scheduled Describes the status of the scheduling process for the pod. # HELP kube_pod_status_scheduled_time Unix timestamp when pod moved into scheduled status # HELP kube_pod_status_unschedulable Describes the unschedulable status for the pod. +# TYPE kube_pod_annotations gauge # TYPE kube_pod_completion_time gauge # TYPE kube_pod_container_info gauge # TYPE kube_pod_container_resource_limits gauge @@ -265,6 +268,7 @@ func TestFullScrapeCycle(t *testing.T) { # TYPE kube_pod_status_scheduled gauge # TYPE kube_pod_status_scheduled_time gauge # TYPE kube_pod_status_unschedulable gauge +kube_pod_annotations{namespace="default",pod="pod0",uid="abc-0"} 1 kube_pod_container_info{namespace="default",pod="pod0",uid="abc-0",container="container2",image="k8s.gcr.io/hyperkube2",image_id="docker://sha256:bbb",container_id="docker://cd456"} 1 kube_pod_container_info{namespace="default",pod="pod0",uid="abc-0",container="container3",image="k8s.gcr.io/hyperkube3",image_id="docker://sha256:ccc",container_id="docker://ef789"} 1 kube_pod_container_resource_limits{namespace="default",pod="pod0",uid="abc-0",container="pod1_con1",node="node1",resource="cpu",unit="core"} 0.2 diff --git a/pkg/options/options.go b/pkg/options/options.go index 18d4eb3c15..c77f98761a 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -28,24 +28,25 @@ import ( // Options are the configurable parameters for kube-state-metrics. type Options struct { - Apiserver string - Kubeconfig string - Help bool - Port int - Host string - TelemetryPort int - TelemetryHost string - TLSConfig string - Resources ResourceSet - Namespaces NamespaceList - Shard int32 - TotalShards int - Pod string - Namespace string - MetricDenylist MetricSet - MetricAllowlist MetricSet - Version bool - LabelsAllowList LabelsAllowList + Apiserver string + Kubeconfig string + Help bool + Port int + Host string + TelemetryPort int + TelemetryHost string + TLSConfig string + Resources ResourceSet + Namespaces NamespaceList + Shard int32 + TotalShards int + Pod string + Namespace string + MetricDenylist MetricSet + MetricAllowlist MetricSet + Version bool + AnnotationsAllowList LabelsAllowList + LabelsAllowList LabelsAllowList EnableGZIPEncoding bool @@ -55,10 +56,11 @@ type Options struct { // NewOptions returns a new instance of `Options`. func NewOptions() *Options { return &Options{ - Resources: ResourceSet{}, - MetricAllowlist: MetricSet{}, - MetricDenylist: MetricSet{}, - LabelsAllowList: LabelsAllowList{}, + Resources: ResourceSet{}, + MetricAllowlist: MetricSet{}, + MetricDenylist: MetricSet{}, + AnnotationsAllowList: LabelsAllowList{}, + LabelsAllowList: LabelsAllowList{}, } } @@ -90,6 +92,7 @@ func (o *Options) AddFlags() { o.flags.Var(&o.Namespaces, "namespaces", fmt.Sprintf("Comma-separated list of namespaces to be enabled. Defaults to %q", &DefaultNamespaces)) o.flags.Var(&o.MetricAllowlist, "metric-allowlist", "Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.") o.flags.Var(&o.MetricDenylist, "metric-denylist", "Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.") + o.flags.Var(&o.AnnotationsAllowList, "metric-annotations-allowlist", "Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional annotations provide a list of resource names in their plural form and Kubernetes annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. A single '*' can be provided per resource instead to allow any annotations, but that has severe performance implications (Example: '=pods=[*]').") o.flags.Var(&o.LabelsAllowList, "metric-labels-allowlist", "Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional labels provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]').") o.flags.Int32Var(&o.Shard, "shard", int32(0), "The instances shard nominal (zero indexed) within the total number of shards. (default 0)") o.flags.IntVar(&o.TotalShards, "total-shards", 1, "The total number of shards. Sharding is disabled when total shards is set to 1.")