Skip to content

Commit 9e1a54e

Browse files
gyanranjanpandaGyan Ranjan Panda
authored andcommitted
Add RBAC support for k8s_leader_elector extension (#4802)
1 parent 88beb29 commit 9e1a54e

12 files changed

Lines changed: 221 additions & 1 deletion

File tree

.chloggen/issue_4802.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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: "Support RBAC generation for `k8s_leader_elector` extension"
9+
10+
# One or more tracking issues related to the change
11+
issues: [4802]
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: |
17+
Automatically generates a ClusterRole with permissions to manage `leases` in the `coordination.k8s.io` API group for leader election among multiple collector replicas.

config/rbac/role.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ rules:
106106
- leases
107107
verbs:
108108
- create
109+
- delete
109110
- get
110111
- list
112+
- patch
111113
- update
114+
- watch
112115
- apiGroups:
113116
- discovery.k8s.io
114117
resources:

internal/components/extensions/helpers.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ var registry = map[string]components.Parser{
2626
MustBuild(),
2727
"jaeger_query": NewJaegerQueryExtensionParserBuilder().
2828
MustBuild(),
29+
"k8s_leader_elector": components.NewBuilder[any]().
30+
WithName("k8s_leader_elector").
31+
WithRbacGen(generatek8sleaderelectorRbacRules).
32+
MustBuild(),
2933
"k8s_observer": components.NewBuilder[k8sobserverConfig]().
3034
WithName("k8s_observer").
3135
WithRbacGen(generatek8sobserverRbacRules).

internal/components/extensions/helpers_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func TestExtensionsComponentParsers(t *testing.T) {
4444
defaultPort int32
4545
}{
4646
{"health_check", "__health_check", 13133},
47+
{"k8s_leader_elector", "__k8s_leader_elector", 0},
4748
} {
4849
t.Run(tt.exporterName, func(t *testing.T) {
4950
t.Run("is registered", func(t *testing.T) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package extensions
5+
6+
import (
7+
"github.com/go-logr/logr"
8+
rbacv1 "k8s.io/api/rbac/v1"
9+
)
10+
11+
// generatek8sleaderelectorRbacRules returns the RBAC policy rules required by the
12+
// k8s_leader_elector extension.
13+
//
14+
// The extension uses Kubernetes Lease objects from the coordination.k8s.io
15+
// API group for leader election among multiple collector replicas, enabling
16+
// HA deployments of receivers that should only run on a single instance
17+
// (e.g. k8sclusterreceiver, k8seventsreceiver).
18+
//
19+
// All seven verbs are required:
20+
// - get/list/watch: observe existing leases and detect leader changes
21+
// - create: create a new Lease if one doesn't yet exist
22+
// - update/patch: renew the lease (heartbeat) while holding leadership
23+
// - delete: release/clean up leases
24+
//
25+
// Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/k8sleaderelector
26+
func generatek8sleaderelectorRbacRules(_ logr.Logger, _ any) ([]rbacv1.PolicyRule, error) {
27+
return []rbacv1.PolicyRule{
28+
{
29+
APIGroups: []string{"coordination.k8s.io"},
30+
Resources: []string{"leases"},
31+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
32+
},
33+
}, nil
34+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package extensions
5+
6+
import (
7+
"testing"
8+
9+
"github.com/go-logr/logr"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
rbacv1 "k8s.io/api/rbac/v1"
13+
)
14+
15+
func TestK8sLeaderElectorRBACRules(t *testing.T) {
16+
rules, err := generatek8sleaderelectorRbacRules(logr.Discard(), nil)
17+
require.NoError(t, err)
18+
19+
require.Len(t, rules, 1, "should return exactly one policy rule")
20+
21+
rule := rules[0]
22+
23+
assert.Equal(t, []string{"coordination.k8s.io"}, rule.APIGroups,
24+
"should target the coordination.k8s.io API group")
25+
assert.Equal(t, []string{"leases"}, rule.Resources,
26+
"should target the leases resource")
27+
assert.Equal(t,
28+
[]string{"get", "list", "watch", "create", "update", "patch", "delete"},
29+
rule.Verbs,
30+
"should include all required verbs for leader election",
31+
)
32+
}
33+
34+
func TestK8sLeaderElectorRBACRulesMatchUpstreamDocs(t *testing.T) {
35+
// Validates the RBAC rules exactly match the upstream k8s_leader_elector
36+
// extension documentation:
37+
// https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/k8sleaderelector
38+
rules, err := generatek8sleaderelectorRbacRules(logr.Discard(), nil)
39+
require.NoError(t, err)
40+
41+
expected := []rbacv1.PolicyRule{
42+
{
43+
APIGroups: []string{"coordination.k8s.io"},
44+
Resources: []string{"leases"},
45+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
46+
},
47+
}
48+
49+
assert.Equal(t, expected, rules)
50+
}

internal/controllers/opentelemetrycollector_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ func NewReconciler(p Params) *OpenTelemetryCollectorReconciler {
212212
// +kubebuilder:rbac:groups=apps,resources=daemonsets;deployments;statefulsets,verbs=get;list;watch;create;update;patch;delete
213213
// +kubebuilder:rbac:groups=autoscaling,resources=horizontalpodautoscalers,verbs=get;list;watch;create;update;patch;delete
214214
// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
215-
// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;update
215+
// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update;patch;delete
216216
// +kubebuilder:rbac:groups=discovery.k8s.io,resources=endpointslices,verbs=get;list
217217
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors;podmonitors,verbs=get;list;watch;create;update;patch;delete
218218
// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete

internal/manifests/collector/rbac_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@ func TestDesiredClusterRoles(t *testing.T) {
7979
},
8080
},
8181
},
82+
{
83+
desc: "k8s_leader_elector extension",
84+
configPath: "testdata/rbac_k8sleaderelector_extension.yaml",
85+
expectedRules: []rbacv1.PolicyRule{
86+
{
87+
APIGroups: []string{"coordination.k8s.io"},
88+
Resources: []string{"leases"},
89+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
90+
},
91+
},
92+
},
8293
{
8394
desc: "k8sattributes processor - service.name metadata",
8495
configPath: "testdata/rbac_k8sattributes_service_name.yaml",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extensions:
2+
k8s_leader_elector:
3+
receivers:
4+
receiver_creator/logs:
5+
watch_observers: []
6+
discovery:
7+
enabled: true
8+
processors: {}
9+
exporters:
10+
debug:
11+
verbosity: basic
12+
service:
13+
pipelines:
14+
logs:
15+
receivers: [receiver_creator/logs]
16+
exporters: [debug]
17+
extensions: [k8s_leader_elector]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# =============================================================================
2+
# File: tests/e2e-automatic-rbac/k8s-leader-elector/00-install.yaml
3+
# =============================================================================
4+
# This e2e test validates that the operator automatically generates the
5+
# correct RBAC rules when the k8s_leader_elector extension is configured
6+
# in the OpenTelemetryCollector CR.
7+
8+
apiVersion: opentelemetry.io/v1beta1
9+
kind: OpenTelemetryCollector
10+
metadata:
11+
name: k8s-leader-elector-test
12+
spec:
13+
mode: deployment
14+
replicas: 2
15+
config:
16+
extensions:
17+
k8s_leader_elector:
18+
auth_type: serviceAccount
19+
lease_name: otel-leader
20+
lease_namespace: default
21+
receivers:
22+
otlp:
23+
protocols:
24+
grpc:
25+
endpoint: 0.0.0.0:4317
26+
exporters:
27+
debug: {}
28+
service:
29+
extensions: [k8s_leader_elector]
30+
pipelines:
31+
traces:
32+
receivers: [otlp]
33+
exporters: [debug]

0 commit comments

Comments
 (0)