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