diff --git a/docs/README.md b/docs/README.md index 19c979fe28..ddf69681bf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,6 +30,7 @@ As of v2.3.0, kube-state-metrics supports additional opt-in metrics via the CLI Per group of metrics there is one file for each metrics. See each file for specific documentation about the exposed metrics: - [CertificateSigningRequest Metrics](certificatesigningrequest-metrics.md) +- [ClusterRole Metrics](clusterrole-metrics.md) - [ConfigMap Metrics](configmap-metrics.md) - [CronJob Metrics](cronjob-metrics.md) - [DaemonSet Metrics](daemonset-metrics.md) @@ -51,6 +52,7 @@ Per group of metrics there is one file for each metrics. See each file for speci - [ReplicaSet Metrics](replicaset-metrics.md) - [ReplicationController Metrics](replicationcontroller-metrics.md) - [ResourceQuota Metrics](resourcequota-metrics.md) +- [Role Metrics](role-metrics.md) - [Secret Metrics](secret-metrics.md) - [Service Metrics](service-metrics.md) - [ServiceAccount Metrics](serviceaccount-metrics.md) diff --git a/docs/cli-arguments.md b/docs/cli-arguments.md index fe4bc91ffa..f5cf56ded7 100644 --- a/docs/cli-arguments.md +++ b/docs/cli-arguments.md @@ -50,7 +50,7 @@ Usage of ./kube-state-metrics: --pod string Name of the pod that contains the kube-state-metrics container. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice. --pod-namespace string Name of the namespace of the pod specified by --pod. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice. --port int Port to expose metrics on. (default 8080) - --resources string Comma-separated list of Resources to be enabled. Defaults to "certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,serviceaccounts,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments" + --resources string Comma-separated list of Resources to be enabled. Defaults to "certificatesigningrequests,clusterroles,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,roles,secrets,serviceaccounts,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments" --shard int32 The instances shard nominal (zero indexed) within the total number of shards. (default 0) --skip_headers If true, avoid header prefixes in the log messages --skip_log_headers If true, avoid headers when opening log files diff --git a/docs/clusterrole-metrics.md b/docs/clusterrole-metrics.md new file mode 100644 index 0000000000..c60aee035a --- /dev/null +++ b/docs/clusterrole-metrics.md @@ -0,0 +1,9 @@ +# ClusterRole Metrics + +| Metric name| Metric type | Labels/tags | Status | +| ---------- | ----------- | ----------- | ----------- | +| kube_clusterrole_annotations | Gauge | `clusterrole`=<clusterrole-name> | EXPERIMENTAL +| kube_clusterrole_labels | Gauge | `clusterrole`=<clusterrole-name> | EXPERIMENTAL +| kube_clusterrole_info | Gauge | `clusterrole`=<clusterrole-name> | EXPERIMENTAL | +| kube_clusterrole_created | Gauge | `clusterrole`=<clusterrole-name> | EXPERIMENTAL | +| kube_clusterrole_metadata_resource_version | Gauge | `clusterrole`=<clusterrole-name> | EXPERIMENTAL | diff --git a/docs/role-metrics.md b/docs/role-metrics.md new file mode 100644 index 0000000000..fc52087c10 --- /dev/null +++ b/docs/role-metrics.md @@ -0,0 +1,9 @@ +# Role Metrics + +| Metric name| Metric type | Labels/tags | Status | +| ---------- | ----------- | ----------- | ----------- | +| kube_role_annotations | Gauge | `role`=<role-name>
`namespace`=<role-namespace> | EXPERIMENTAL +| kube_role_labels | Gauge | `role`=<role-name>
`namespace`=<role-namespace> | EXPERIMENTAL +| kube_role_info | Gauge | `role`=<role-name>
`namespace`=<role-namespace> | EXPERIMENTAL +| kube_role_created | Gauge | `role`=<role-name>
`namespace`=<role-namespace> | EXPERIMENTAL | +| kube_role_metadata_resource_version | Gauge | `role`=<role-name>
`namespace`=<role-namespace> | EXPERIMENTAL | diff --git a/examples/autosharding/cluster-role.yaml b/examples/autosharding/cluster-role.yaml index c82473384d..574452e9e1 100644 --- a/examples/autosharding/cluster-role.yaml +++ b/examples/autosharding/cluster-role.yaml @@ -108,3 +108,11 @@ rules: verbs: - list - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - roles + verbs: + - list + - watch diff --git a/examples/standard/cluster-role.yaml b/examples/standard/cluster-role.yaml index c82473384d..574452e9e1 100644 --- a/examples/standard/cluster-role.yaml +++ b/examples/standard/cluster-role.yaml @@ -108,3 +108,11 @@ rules: verbs: - list - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - roles + verbs: + - list + - watch diff --git a/internal/store/builder.go b/internal/store/builder.go index 1707ebcc7b..783cfeea87 100644 --- a/internal/store/builder.go +++ b/internal/store/builder.go @@ -34,6 +34,7 @@ import ( v1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" policyv1 "k8s.io/api/policy/v1" + rbacv1 "k8s.io/api/rbac/v1" storagev1 "k8s.io/api/storage/v1" vpaautoscaling "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1beta2" vpaclientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned" @@ -261,6 +262,7 @@ func (b *Builder) BuildStores() [][]cache.Store { var availableStores = map[string]func(f *Builder) []cache.Store{ "certificatesigningrequests": func(b *Builder) []cache.Store { return b.buildCsrStores() }, + "clusterroles": func(b *Builder) []cache.Store { return b.buildClusterRoleStores() }, "configmaps": func(b *Builder) []cache.Store { return b.buildConfigMapStores() }, "cronjobs": func(b *Builder) []cache.Store { return b.buildCronJobStores() }, "daemonsets": func(b *Builder) []cache.Store { return b.buildDaemonSetStores() }, @@ -282,6 +284,7 @@ var availableStores = map[string]func(f *Builder) []cache.Store{ "replicasets": func(b *Builder) []cache.Store { return b.buildReplicaSetStores() }, "replicationcontrollers": func(b *Builder) []cache.Store { return b.buildReplicationControllerStores() }, "resourcequotas": func(b *Builder) []cache.Store { return b.buildResourceQuotaStores() }, + "roles": func(b *Builder) []cache.Store { return b.buildRoleStores() }, "secrets": func(b *Builder) []cache.Store { return b.buildSecretStores() }, "serviceaccounts": func(b *Builder) []cache.Store { return b.buildServiceAccountStores() }, "services": func(b *Builder) []cache.Store { return b.buildServiceStores() }, @@ -425,6 +428,14 @@ func (b *Builder) buildLeasesStores() []cache.Store { return b.buildStoresFunc(leaseMetricFamilies, &coordinationv1.Lease{}, createLeaseListWatch, b.useAPIServerCache) } +func (b *Builder) buildClusterRoleStores() []cache.Store { + return b.buildStoresFunc(clusterRoleMetricFamilies(b.allowAnnotationsList["clusterroles"], b.allowLabelsList["clusterroles"]), &rbacv1.ClusterRole{}, createClusterRoleListWatch, b.useAPIServerCache) +} + +func (b *Builder) buildRoleStores() []cache.Store { + return b.buildStoresFunc(roleMetricFamilies(b.allowAnnotationsList["roles"], b.allowLabelsList["roles"]), &rbacv1.Role{}, createRoleListWatch, b.useAPIServerCache) +} + func (b *Builder) buildStores( metricFamilies []generator.FamilyGenerator, expectedType interface{}, diff --git a/internal/store/clusterrole.go b/internal/store/clusterrole.go new file mode 100644 index 0000000000..b6def1628c --- /dev/null +++ b/internal/store/clusterrole.go @@ -0,0 +1,154 @@ +/* +Copyright 2018 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package store + +import ( + "context" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + + "k8s.io/kube-state-metrics/v2/pkg/metric" + generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" +) + +var ( + descClusterRoleAnnotationsName = "kube_clusterrole_annotations" + descClusterRoleAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." + descClusterRoleLabelsName = "kube_clusterrole_labels" + descClusterRoleLabelsHelp = "Kubernetes labels converted to Prometheus labels." + descClusterRoleLabelsDefaultLabels = []string{"clusterrole"} +) + +func clusterRoleMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { + return []generator.FamilyGenerator{ + *generator.NewFamilyGenerator( + descClusterRoleAnnotationsName, + descClusterRoleAnnotationsHelp, + metric.Gauge, + "", + wrapClusterRoleFunc(func(r *rbacv1.ClusterRole) *metric.Family { + annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", r.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), + *generator.NewFamilyGenerator( + descClusterRoleLabelsName, + descClusterRoleLabelsHelp, + metric.Gauge, + "", + wrapClusterRoleFunc(func(r *rbacv1.ClusterRole) *metric.Family { + labelKeys, labelValues := createPrometheusLabelKeysValues("label", r.Labels, allowLabelsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: labelKeys, + LabelValues: labelValues, + Value: 1, + }, + }, + } + }), + ), + *generator.NewFamilyGenerator( + "kube_clusterrole_info", + "Information about cluster role.", + metric.Gauge, + "", + wrapClusterRoleFunc(func(r *rbacv1.ClusterRole) *metric.Family { + return &metric.Family{ + Metrics: []*metric.Metric{{ + LabelKeys: []string{}, + LabelValues: []string{}, + Value: 1, + }}, + } + }), + ), + *generator.NewFamilyGenerator( + "kube_clusterrole_created", + "Unix creation timestamp", + metric.Gauge, + "", + wrapClusterRoleFunc(func(r *rbacv1.ClusterRole) *metric.Family { + ms := []*metric.Metric{} + + if !r.CreationTimestamp.IsZero() { + ms = append(ms, &metric.Metric{ + LabelKeys: []string{}, + LabelValues: []string{}, + Value: float64(r.CreationTimestamp.Unix()), + }) + } + + return &metric.Family{ + Metrics: ms, + } + }), + ), + *generator.NewFamilyGenerator( + "kube_clusterrole_metadata_resource_version", + "Resource version representing a specific version of the cluster role.", + metric.Gauge, + "", + wrapClusterRoleFunc(func(r *rbacv1.ClusterRole) *metric.Family { + return &metric.Family{ + Metrics: resourceVersionMetric(r.ObjectMeta.ResourceVersion), + } + }), + ), + } +} + +func createClusterRoleListWatch(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher { + return &cache.ListWatch{ + ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { + opts.FieldSelector = fieldSelector + return kubeClient.RbacV1().ClusterRoles().List(context.TODO(), opts) + }, + WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { + opts.FieldSelector = fieldSelector + return kubeClient.RbacV1().ClusterRoles().Watch(context.TODO(), opts) + }, + } +} + +func wrapClusterRoleFunc(f func(*rbacv1.ClusterRole) *metric.Family) func(interface{}) *metric.Family { + return func(obj interface{}) *metric.Family { + clusterrole := obj.(*rbacv1.ClusterRole) + + metricFamily := f(clusterrole) + + for _, m := range metricFamily.Metrics { + m.LabelKeys, m.LabelValues = mergeKeyValues(descClusterRoleLabelsDefaultLabels, []string{clusterrole.Name}, m.LabelKeys, m.LabelValues) + } + + return metricFamily + } +} diff --git a/internal/store/clusterrole_test.go b/internal/store/clusterrole_test.go new file mode 100644 index 0000000000..aef2d2659d --- /dev/null +++ b/internal/store/clusterrole_test.go @@ -0,0 +1,103 @@ +/* +Copyright 2012 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package store + +import ( + "testing" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" +) + +func TestClusterRoleStore(t *testing.T) { + startTime := 1501569018 + metav1StartTime := metav1.Unix(int64(startTime), 0) + + cases := []generateMetricsTestCase{ + { + AllowAnnotationsList: []string{ + "app.k8s.io/owner", + }, + AllowLabelsList: []string{ + "app", + }, + Obj: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "role1", + ResourceVersion: "BBBBB", + Annotations: map[string]string{ + "app": "mysql-server", + "app.k8s.io/owner": "@foo", + }, + Labels: map[string]string{ + "excluded": "me", + "app": "mysql-server", + }, + }, + }, + Want: ` + # HELP kube_clusterrole_annotations Kubernetes annotations converted to Prometheus labels. + # HELP kube_clusterrole_labels Kubernetes labels converted to Prometheus labels. + # HELP kube_clusterrole_info Information about cluster role. + # HELP kube_clusterrole_metadata_resource_version Resource version representing a specific version of the cluster role. + # TYPE kube_clusterrole_annotations gauge + # TYPE kube_clusterrole_labels gauge + # TYPE kube_clusterrole_info gauge + # TYPE kube_clusterrole_metadata_resource_version gauge + kube_clusterrole_annotations{annotation_app_k8s_io_owner="@foo",clusterrole="role1"} 1 + kube_clusterrole_labels{clusterrole="role1",label_app="mysql-server"} 1 + kube_clusterrole_info{clusterrole="role1"} 1 +`, + MetricNames: []string{ + "kube_clusterrole_annotations", + "kube_clusterrole_labels", + "kube_clusterrole_info", + "kube_clusterrole_metadata_resource_version", + }, + }, + { + Obj: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "role2", + CreationTimestamp: metav1StartTime, + ResourceVersion: "10596", + }, + }, + Want: ` + # HELP kube_clusterrole_created Unix creation timestamp + # HELP kube_clusterrole_info Information about cluster role. + # HELP kube_clusterrole_metadata_resource_version Resource version representing a specific version of the cluster role. + # TYPE kube_clusterrole_created gauge + # TYPE kube_clusterrole_info gauge + # TYPE kube_clusterrole_metadata_resource_version gauge + kube_clusterrole_info{clusterrole="role2"} 1 + kube_clusterrole_created{clusterrole="role2"} 1.501569018e+09 + kube_clusterrole_metadata_resource_version{clusterrole="role2"} 10596 + `, + MetricNames: []string{"kube_clusterrole_info", "kube_clusterrole_created", "kube_clusterrole_metadata_resource_version"}, + }, + } + for i, c := range cases { + c.Func = generator.ComposeMetricGenFuncs(clusterRoleMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) + c.Headers = generator.ExtractMetricFamilyHeaders(clusterRoleMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) + if err := c.run(); err != nil { + t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) + } + } +} diff --git a/internal/store/role.go b/internal/store/role.go new file mode 100644 index 0000000000..944ce5e29d --- /dev/null +++ b/internal/store/role.go @@ -0,0 +1,154 @@ +/* +Copyright 2018 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package store + +import ( + "context" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + + "k8s.io/kube-state-metrics/v2/pkg/metric" + generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" +) + +var ( + descRoleAnnotationsName = "kube_role_annotations" + descRoleAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." + descRoleLabelsName = "kube_role_labels" + descRoleLabelsHelp = "Kubernetes labels converted to Prometheus labels." + descRoleLabelsDefaultLabels = []string{"namespace", "role"} +) + +func roleMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { + return []generator.FamilyGenerator{ + *generator.NewFamilyGenerator( + descRoleAnnotationsName, + descRoleAnnotationsHelp, + metric.Gauge, + "", + wrapRoleFunc(func(r *rbacv1.Role) *metric.Family { + annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", r.Annotations, allowAnnotationsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: annotationKeys, + LabelValues: annotationValues, + Value: 1, + }, + }, + } + }), + ), + *generator.NewFamilyGenerator( + descRoleLabelsName, + descRoleLabelsHelp, + metric.Gauge, + "", + wrapRoleFunc(func(r *rbacv1.Role) *metric.Family { + labelKeys, labelValues := createPrometheusLabelKeysValues("label", r.Labels, allowLabelsList) + return &metric.Family{ + Metrics: []*metric.Metric{ + { + LabelKeys: labelKeys, + LabelValues: labelValues, + Value: 1, + }, + }, + } + }), + ), + *generator.NewFamilyGenerator( + "kube_role_info", + "Information about role.", + metric.Gauge, + "", + wrapRoleFunc(func(r *rbacv1.Role) *metric.Family { + return &metric.Family{ + Metrics: []*metric.Metric{{ + LabelKeys: []string{}, + LabelValues: []string{}, + Value: 1, + }}, + } + }), + ), + *generator.NewFamilyGenerator( + "kube_role_created", + "Unix creation timestamp", + metric.Gauge, + "", + wrapRoleFunc(func(r *rbacv1.Role) *metric.Family { + ms := []*metric.Metric{} + + if !r.CreationTimestamp.IsZero() { + ms = append(ms, &metric.Metric{ + LabelKeys: []string{}, + LabelValues: []string{}, + Value: float64(r.CreationTimestamp.Unix()), + }) + } + + return &metric.Family{ + Metrics: ms, + } + }), + ), + *generator.NewFamilyGenerator( + "kube_role_metadata_resource_version", + "Resource version representing a specific version of the role.", + metric.Gauge, + "", + wrapRoleFunc(func(r *rbacv1.Role) *metric.Family { + return &metric.Family{ + Metrics: resourceVersionMetric(r.ObjectMeta.ResourceVersion), + } + }), + ), + } +} + +func createRoleListWatch(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher { + return &cache.ListWatch{ + ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { + opts.FieldSelector = fieldSelector + return kubeClient.RbacV1().Roles(ns).List(context.TODO(), opts) + }, + WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { + opts.FieldSelector = fieldSelector + return kubeClient.RbacV1().Roles(ns).Watch(context.TODO(), opts) + }, + } +} + +func wrapRoleFunc(f func(*rbacv1.Role) *metric.Family) func(interface{}) *metric.Family { + return func(obj interface{}) *metric.Family { + role := obj.(*rbacv1.Role) + + metricFamily := f(role) + + for _, m := range metricFamily.Metrics { + m.LabelKeys, m.LabelValues = mergeKeyValues(descRoleLabelsDefaultLabels, []string{role.Namespace, role.Name}, m.LabelKeys, m.LabelValues) + } + + return metricFamily + } +} diff --git a/internal/store/role_test.go b/internal/store/role_test.go new file mode 100644 index 0000000000..499028aea2 --- /dev/null +++ b/internal/store/role_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2012 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package store + +import ( + "testing" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" +) + +func TestRoleStore(t *testing.T) { + startTime := 1501569018 + metav1StartTime := metav1.Unix(int64(startTime), 0) + + cases := []generateMetricsTestCase{ + { + AllowAnnotationsList: []string{ + "app.k8s.io/owner", + }, + AllowLabelsList: []string{ + "app", + }, + Obj: &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "role1", + Namespace: "ns1", + ResourceVersion: "BBBBB", + Annotations: map[string]string{ + "app": "mysql-server", + "app.k8s.io/owner": "@foo", + }, + Labels: map[string]string{ + "excluded": "me", + "app": "mysql-server", + }, + }, + }, + Want: ` + # HELP kube_role_annotations Kubernetes annotations converted to Prometheus labels. + # HELP kube_role_labels Kubernetes labels converted to Prometheus labels. + # HELP kube_role_info Information about role. + # HELP kube_role_metadata_resource_version Resource version representing a specific version of the role. + # TYPE kube_role_annotations gauge + # TYPE kube_role_labels gauge + # TYPE kube_role_info gauge + # TYPE kube_role_metadata_resource_version gauge + kube_role_annotations{annotation_app_k8s_io_owner="@foo",role="role1",namespace="ns1"} 1 + kube_role_labels{role="role1",label_app="mysql-server",namespace="ns1"} 1 + kube_role_info{role="role1",namespace="ns1"} 1 +`, + MetricNames: []string{ + "kube_role_annotations", + "kube_role_labels", + "kube_role_info", + "kube_role_metadata_resource_version", + }, + }, + { + Obj: &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "role2", + Namespace: "ns2", + CreationTimestamp: metav1StartTime, + ResourceVersion: "10596", + }, + }, + Want: ` + # HELP kube_role_created Unix creation timestamp + # HELP kube_role_info Information about role. + # HELP kube_role_metadata_resource_version Resource version representing a specific version of the role. + # TYPE kube_role_created gauge + # TYPE kube_role_info gauge + # TYPE kube_role_metadata_resource_version gauge + kube_role_info{role="role2",namespace="ns2"} 1 + kube_role_created{role="role2",namespace="ns2"} 1.501569018e+09 + kube_role_metadata_resource_version{role="role2",namespace="ns2"} 10596 + `, + MetricNames: []string{"kube_role_info", "kube_role_created", "kube_role_metadata_resource_version"}, + }, + } + for i, c := range cases { + c.Func = generator.ComposeMetricGenFuncs(roleMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) + c.Headers = generator.ExtractMetricFamilyHeaders(roleMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) + if err := c.run(); err != nil { + t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) + } + } +} diff --git a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet index e4867afee7..80e259dc51 100644 --- a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet +++ b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet @@ -145,7 +145,15 @@ ], verbs: ['list', 'watch'], }, - ]; + { + apiGroups: ['rbac.authorization.k8s.io'], + resources: [ + 'clusterroles', + 'roles', + ], + verbs: ['list', 'watch'], + }, + ]; { apiVersion: 'rbac.authorization.k8s.io/v1', @@ -164,9 +172,9 @@ { name: 'http-metrics', containerPort: 8080 }, { name: 'telemetry', containerPort: 8081 }, ], - securityContext: { - runAsUser: 65534, - allowPrivilegeEscalation: false, + securityContext: { + runAsUser: 65534, + allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities: { drop: ['ALL'] }, }, diff --git a/pkg/options/resource.go b/pkg/options/resource.go index 8c4a170bbb..3ca7cd5fd2 100644 --- a/pkg/options/resource.go +++ b/pkg/options/resource.go @@ -27,6 +27,7 @@ var ( // DefaultResources represents the default set of resources in kube-state-metrics. DefaultResources = ResourceSet{ "certificatesigningrequests": struct{}{}, + "clusterroles": struct{}{}, "configmaps": struct{}{}, "cronjobs": struct{}{}, "daemonsets": struct{}{}, @@ -48,6 +49,7 @@ var ( "replicasets": struct{}{}, "replicationcontrollers": struct{}{}, "resourcequotas": struct{}{}, + "roles": struct{}{}, "secrets": struct{}{}, "serviceaccounts": struct{}{}, "services": struct{}{}, diff --git a/tests/manifests/clusterole.yaml b/tests/manifests/clusterole.yaml new file mode 100644 index 0000000000..c7c6fea1fa --- /dev/null +++ b/tests/manifests/clusterole.yaml @@ -0,0 +1,8 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: clusterrole +rules: +- apiGroups: [""] + resources: [""] + verbs: ["get", "watch", "list"] \ No newline at end of file diff --git a/tests/manifests/role.yaml b/tests/manifests/role.yaml new file mode 100644 index 0000000000..266e4e656a --- /dev/null +++ b/tests/manifests/role.yaml @@ -0,0 +1,8 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: role +rules: +- apiGroups: [""] + resources: [""] + verbs: ["get", "watch", "list"] \ No newline at end of file