Skip to content

Commit b64ed2c

Browse files
author
Israel Blancas
committed
Add automatic RBAC creation for kubeletstats receiver
Signed-off-by: Israel Blancas <[email protected]>
1 parent 4da4f66 commit b64ed2c

17 files changed

+427
-3
lines changed

.chloggen/kubeletstats.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
5+
component: collector
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: "Add automatic RBAC creation for the `kubeletstats` receiver."
9+
10+
# One or more tracking issues related to the change
11+
issues: [3155]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext:

Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,12 @@ add-image-opampbridge:
204204
add-rbac-permissions-to-operator: manifests kustomize
205205
# Kustomize only allows patches in the folder where the kustomization is located
206206
# This folder is ignored by .gitignore
207-
cp -r tests/e2e-automatic-rbac/extra-permissions-operator/ config/rbac/extra-permissions-operator
207+
mkdir -p config/rbac/extra-permissions-operator
208+
cp -r tests/e2e-automatic-rbac/extra-permissions-operator/* config/rbac/extra-permissions-operator
208209
cd config/rbac && $(KUSTOMIZE) edit add patch --kind ClusterRole --name manager-role --path extra-permissions-operator/namespaces.yaml
209210
cd config/rbac && $(KUSTOMIZE) edit add patch --kind ClusterRole --name manager-role --path extra-permissions-operator/nodes.yaml
211+
cd config/rbac && $(KUSTOMIZE) edit add patch --kind ClusterRole --name manager-role --path extra-permissions-operator/nodes-stats.yaml
212+
cd config/rbac && $(KUSTOMIZE) edit add patch --kind ClusterRole --name manager-role --path extra-permissions-operator/nodes-proxy.yaml
210213
cd config/rbac && $(KUSTOMIZE) edit add patch --kind ClusterRole --name manager-role --path extra-permissions-operator/rbac.yaml
211214
cd config/rbac && $(KUSTOMIZE) edit add patch --kind ClusterRole --name manager-role --path extra-permissions-operator/replicaset.yaml
212215

internal/components/receivers/helpers.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ var (
136136
WithProtocol(corev1.ProtocolTCP).
137137
WithTargetPort(3100).
138138
MustBuild(),
139+
components.NewBuilder[kubeletStatsConfig]().WithName("kubeletstats").
140+
WithRbacGen(generateKubeletStatsRbacRules).
141+
MustBuild(),
139142
NewScraperParser("prometheus"),
140-
NewScraperParser("kubeletstats"),
141143
NewScraperParser("sshcheck"),
142144
NewScraperParser("cloudfoundry"),
143145
NewScraperParser("vcenter"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package receivers
16+
17+
import (
18+
"github.com/go-logr/logr"
19+
rbacv1 "k8s.io/api/rbac/v1"
20+
)
21+
22+
type metricConfig struct {
23+
Enabled bool `mapstructure:"enabled"`
24+
}
25+
26+
type metrics struct {
27+
K8sContainerCPULimitUtilization metricConfig `mapstructure:"k8s.container.cpu_limit_utilization"`
28+
K8sContainerCPURequestUtilization metricConfig `mapstructure:"k8s.container.cpu_request_utilization"`
29+
K8sContainerMemoryLimitUtilization metricConfig `mapstructure:"k8s.container.memory_limit_utilization"`
30+
K8sContainerMemoryRequestUtilization metricConfig `mapstructure:"k8s.container.memory_request_utilization"`
31+
K8sPodCPULimitUtilization metricConfig `mapstructure:"k8s.pod.cpu_limit_utilization"`
32+
K8sPodCPURequestUtilization metricConfig `mapstructure:"k8s.pod.cpu_request_utilization"`
33+
K8sPodMemoryLimitUtilization metricConfig `mapstructure:"k8s.pod.memory_limit_utilization"`
34+
K8sPodMemoryRequestUtilization metricConfig `mapstructure:"k8s.pod.memory_request_utilization"`
35+
}
36+
37+
// KubeletStatsConfig is a minimal struct needed for parsing a valid kubeletstats receiver configuration
38+
// This only contains the fields necessary for parsing, other fields can be added in the future.
39+
type kubeletStatsConfig struct {
40+
ExtraMetadataLabels []string `mapstructure:"extra_metadata_labels"`
41+
Metrics metrics `mapstructure:"metrics"`
42+
AuthType string `mapstructure:"auth_type"`
43+
}
44+
45+
func generateKubeletStatsRbacRules(_ logr.Logger, config kubeletStatsConfig) ([]rbacv1.PolicyRule, error) {
46+
// The Kubelet Stats Receiver needs get permissions on the nodes/stats resources always.
47+
prs := []rbacv1.PolicyRule{
48+
{
49+
APIGroups: []string{""},
50+
Resources: []string{"nodes/stats"},
51+
Verbs: []string{"get"},
52+
},
53+
}
54+
55+
// Additionally, when using extra_metadata_labels or any of the {request|limit}_utilization metrics
56+
// the processor also needs get permissions for nodes/proxy resources.
57+
nodesProxyPr := rbacv1.PolicyRule{
58+
APIGroups: []string{""},
59+
Resources: []string{"nodes/proxy"},
60+
Verbs: []string{"get"},
61+
}
62+
63+
if len(config.ExtraMetadataLabels) > 0 {
64+
prs = append(prs, nodesProxyPr)
65+
return prs, nil
66+
}
67+
68+
metrics := []bool{
69+
config.Metrics.K8sContainerCPULimitUtilization.Enabled,
70+
config.Metrics.K8sContainerCPURequestUtilization.Enabled,
71+
config.Metrics.K8sContainerMemoryLimitUtilization.Enabled,
72+
config.Metrics.K8sContainerMemoryRequestUtilization.Enabled,
73+
config.Metrics.K8sPodCPULimitUtilization.Enabled,
74+
config.Metrics.K8sPodCPURequestUtilization.Enabled,
75+
config.Metrics.K8sPodMemoryLimitUtilization.Enabled,
76+
config.Metrics.K8sPodMemoryRequestUtilization.Enabled,
77+
}
78+
for _, metric := range metrics {
79+
if metric {
80+
prs = append(prs, nodesProxyPr)
81+
return prs, nil
82+
}
83+
}
84+
return prs, nil
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package receivers
16+
17+
import (
18+
"testing"
19+
20+
"github.com/go-logr/logr"
21+
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
23+
rbacv1 "k8s.io/api/rbac/v1"
24+
)
25+
26+
func TestGenerateKubeletStatsRbacRules(t *testing.T) {
27+
baseRule := rbacv1.PolicyRule{
28+
APIGroups: []string{""},
29+
Resources: []string{"nodes/stats"},
30+
Verbs: []string{"get"},
31+
}
32+
33+
proxyRule := rbacv1.PolicyRule{
34+
APIGroups: []string{""},
35+
Resources: []string{"nodes/proxy"},
36+
Verbs: []string{"get"},
37+
}
38+
39+
tests := []struct {
40+
name string
41+
config kubeletStatsConfig
42+
expectedRules []rbacv1.PolicyRule
43+
expectedErrMsg string
44+
}{
45+
{
46+
name: "Default config",
47+
config: kubeletStatsConfig{},
48+
expectedRules: []rbacv1.PolicyRule{baseRule},
49+
},
50+
{
51+
name: "Extra metadata labels",
52+
config: kubeletStatsConfig{
53+
ExtraMetadataLabels: []string{"label1", "label2"},
54+
},
55+
expectedRules: []rbacv1.PolicyRule{baseRule, proxyRule},
56+
},
57+
{
58+
name: "CPU limit utilization enabled",
59+
config: kubeletStatsConfig{
60+
Metrics: metrics{
61+
K8sContainerCPULimitUtilization: metricConfig{Enabled: true},
62+
},
63+
},
64+
expectedRules: []rbacv1.PolicyRule{baseRule, proxyRule},
65+
},
66+
{
67+
name: "Memory request utilization enabled",
68+
config: kubeletStatsConfig{
69+
Metrics: metrics{
70+
K8sPodMemoryRequestUtilization: metricConfig{Enabled: true},
71+
},
72+
},
73+
expectedRules: []rbacv1.PolicyRule{baseRule, proxyRule},
74+
},
75+
{
76+
name: "No extra permissions needed",
77+
config: kubeletStatsConfig{
78+
Metrics: metrics{
79+
K8sContainerCPULimitUtilization: metricConfig{Enabled: false},
80+
},
81+
},
82+
expectedRules: []rbacv1.PolicyRule{baseRule},
83+
},
84+
}
85+
86+
for _, tt := range tests {
87+
t.Run(tt.name, func(t *testing.T) {
88+
rules, err := generateKubeletStatsRbacRules(logr.Logger{}, tt.config)
89+
90+
if tt.expectedErrMsg != "" {
91+
require.Error(t, err)
92+
assert.Contains(t, err.Error(), tt.expectedErrMsg)
93+
} else {
94+
require.NoError(t, err)
95+
assert.Equal(t, tt.expectedRules, rules)
96+
}
97+
})
98+
}
99+
}

internal/components/receivers/scraper_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ func TestScraperParsers(t *testing.T) {
2929
defaultPort int
3030
}{
3131
{"prometheus", "__prometheus", 0},
32-
{"kubeletstats", "__kubeletstats", 0},
3332
{"sshcheck", "__sshcheck", 0},
3433
{"cloudfoundry", "__cloudfoundry", 0},
3534
{"vcenter", "__vcenter", 0},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
- op: add
3+
path: /rules/-
4+
value:
5+
apiGroups:
6+
- ""
7+
resources:
8+
- nodes/proxy
9+
verbs:
10+
- get
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
- op: add
3+
path: /rules/-
4+
value:
5+
apiGroups:
6+
- ""
7+
resources:
8+
- nodes/stats
9+
verbs:
10+
- get

tests/e2e-automatic-rbac/extra-permissions-operator/nodes.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,23 @@
1010
- get
1111
- list
1212
- watch
13+
---
14+
- op: add
15+
path: /rules/-
16+
value:
17+
apiGroups:
18+
- ""
19+
resources:
20+
- nodes/proxy
21+
verbs:
22+
- get
23+
---
24+
- op: add
25+
path: /rules/-
26+
value:
27+
apiGroups:
28+
- ""
29+
resources:
30+
- nodes/stats
31+
verbs:
32+
- get
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: chainsaw-kubeletstats
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: ClusterRole
3+
metadata:
4+
name: simplest-chainsaw-kubeletstats-cluster-role
5+
rules:
6+
- apiGroups: [""]
7+
resources: ["nodes/stats"]
8+
verbs: ["get"]
9+
---
10+
apiVersion: rbac.authorization.k8s.io/v1
11+
kind: ClusterRoleBinding
12+
metadata:
13+
labels:
14+
app.kubernetes.io/component: opentelemetry-collector
15+
app.kubernetes.io/instance: chainsaw-kubeletstats.simplest
16+
app.kubernetes.io/managed-by: opentelemetry-operator
17+
app.kubernetes.io/name: simplest-chainsaw-kubeletstats-collector
18+
app.kubernetes.io/part-of: opentelemetry
19+
name: simplest-chainsaw-kubeletstats-collector
20+
roleRef:
21+
apiGroup: rbac.authorization.k8s.io
22+
kind: ClusterRole
23+
name: simplest-chainsaw-kubeletstats-cluster-role
24+
subjects:
25+
- kind: ServiceAccount
26+
name: simplest-collector
27+
namespace: chainsaw-kubeletstats
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: opentelemetry.io/v1alpha1
2+
kind: OpenTelemetryCollector
3+
metadata:
4+
name: simplest
5+
namespace: chainsaw-kubeletstats
6+
spec:
7+
config: |
8+
receivers:
9+
kubeletstats:
10+
auth_type: ""
11+
processors:
12+
exporters:
13+
debug:
14+
service:
15+
pipelines:
16+
traces:
17+
receivers: [kubeletstats]
18+
processors: []
19+
exporters: [debug]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: ClusterRole
3+
metadata:
4+
name: simplest-chainsaw-kubeletstats-cluster-role
5+
rules:
6+
- apiGroups: [""]
7+
resources: ["nodes/stats"]
8+
verbs: ["get"]
9+
- apiGroups: [""]
10+
resources: ["nodes/proxy"]
11+
verbs: ["get"]
12+
---
13+
apiVersion: rbac.authorization.k8s.io/v1
14+
kind: ClusterRoleBinding
15+
metadata:
16+
labels:
17+
app.kubernetes.io/component: opentelemetry-collector
18+
app.kubernetes.io/instance: chainsaw-kubeletstats.simplest
19+
app.kubernetes.io/managed-by: opentelemetry-operator
20+
app.kubernetes.io/name: simplest-chainsaw-kubeletstats-collector
21+
app.kubernetes.io/part-of: opentelemetry
22+
name: simplest-chainsaw-kubeletstats-collector
23+
roleRef:
24+
apiGroup: rbac.authorization.k8s.io
25+
kind: ClusterRole
26+
name: simplest-chainsaw-kubeletstats-cluster-role
27+
subjects:
28+
- kind: ServiceAccount
29+
name: simplest-collector
30+
namespace: chainsaw-kubeletstats
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: opentelemetry.io/v1alpha1
2+
kind: OpenTelemetryCollector
3+
metadata:
4+
name: simplest
5+
namespace: chainsaw-kubeletstats
6+
spec:
7+
config: |
8+
receivers:
9+
kubeletstats:
10+
extra_metadata_labels:
11+
- container.id
12+
processors:
13+
exporters:
14+
debug:
15+
service:
16+
pipelines:
17+
traces:
18+
receivers: [kubeletstats]
19+
processors: []
20+
exporters: [debug]

0 commit comments

Comments
 (0)