Skip to content

Commit 0e5d90f

Browse files
committed
feat(ta): allowNamespaces and denyNamespaces
When the target allocator is configured to watch Prometheus custom resources in the cluster to discover targets, it is currently hard-coded to require a ClusterRole with a policy rule of listing namespaces. This prevents usage in environments where configuring ClusterRoles is not permitted i.e. in namespace-as-a-service setups where only Roles can be created. This change introduces two fields in the prometheusCR specification to allow configuring the namespaces that can be interacted with by the target allocator. - allowNamespaces is a comma-separated list of namespaces for the target allocator to watch. If set to an empty string, it will list all list all namespaces in the cluster. This is mutually exclusive with denyNamespaces. Defaults to an empty string. - denyNamespaces is a comma-separated list of namespaces for the target allocator to not watch. If set to an empty string, it will not exclude any namespaces. This is mutually exclusive with allowNamespaces. Defaults to an empty string. Fixes: #3086 Signed-off-by: Charlie Le <[email protected]>
1 parent d7291dd commit 0e5d90f

31 files changed

+744
-65
lines changed

.chloggen/namespace-ta.yaml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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: target allocator
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: |
9+
Add support for setting the allowNamespaces and denyNamespaces in the target allocator.
10+
11+
# One or more tracking issues related to the change
12+
issues: [3086]
13+
14+
# (Optional) One or more lines of additional information to render under the primary note.
15+
# These lines will be padded with 2 spaces and then inserted directly into the document.
16+
# Use pipe (|) for multiline entries.
17+
subtext: |
18+
allowNamespaces can be set to an empty string to watch all namespaces (default) or to a comma-separated list of namespaces to watch.
19+
denyNamespaces can be set to an empty string to deny watching any namespaces (default) or to a comma-separated list of namespaces to deny watching.

apis/v1beta1/targetallocator_types.go

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ type TargetAllocatorPrometheusCR struct {
1212
// Enabled indicates whether to use a PrometheusOperator custom resources as targets or not.
1313
// +optional
1414
Enabled bool `json:"enabled,omitempty"`
15+
// AllowNamespaces Namespaces to scope the interaction of the Target Allocator and the apiserver (allow list). This is mutually exclusive with DenyNamespaces.
16+
// +optional
17+
AllowNamespaces string `json:"allowNamespaces,omitempty"`
18+
// DenyNamespaces Namespaces to scope the interaction of the Target Allocator and the apiserver (allow list). This is mutually exclusive with AllowNamespaces.
19+
// +optional
20+
DenyNamespaces string `json:"denyNamespaces,omitempty"`
1521
// Default interval between consecutive scrapes. Intervals set in ServiceMonitors and PodMonitors override it.
1622
//Equivalent to the same setting on the Prometheus CR.
1723
//

bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -7889,6 +7889,10 @@ spec:
78897889
type: object
78907890
prometheusCR:
78917891
properties:
7892+
allowNamespaces:
7893+
type: string
7894+
denyNamespaces:
7895+
type: string
78927896
enabled:
78937897
type: boolean
78947898
podMonitorSelector:

bundle/community/manifests/opentelemetry.io_targetallocators.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -2257,6 +2257,10 @@ spec:
22572257
type: string
22582258
prometheusCR:
22592259
properties:
2260+
allowNamespaces:
2261+
type: string
2262+
denyNamespaces:
2263+
type: string
22602264
enabled:
22612265
type: boolean
22622266
podMonitorSelector:

bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -7889,6 +7889,10 @@ spec:
78897889
type: object
78907890
prometheusCR:
78917891
properties:
7892+
allowNamespaces:
7893+
type: string
7894+
denyNamespaces:
7895+
type: string
78927896
enabled:
78937897
type: boolean
78947898
podMonitorSelector:

bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -2257,6 +2257,10 @@ spec:
22572257
type: string
22582258
prometheusCR:
22592259
properties:
2260+
allowNamespaces:
2261+
type: string
2262+
denyNamespaces:
2263+
type: string
22602264
enabled:
22612265
type: boolean
22622266
podMonitorSelector:

cmd/otel-allocator/README.md

+82-8
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,11 @@ Upstream documentation here: [PrometheusReceiver](https://github.com/open-teleme
191191

192192
### RBAC
193193

194-
Before the TargetAllocator can start scraping, you need to set up Kubernetes RBAC (role-based access controls) resources. This means that you need to have a `ServiceAccount` and corresponding cluster roles so that the TargetAllocator has access to all of the necessary resources to pull metrics from.
194+
Before the TargetAllocator can start scraping, you need to set up Kubernetes RBAC (role-based access controls) resources. This means that you need to have a `ServiceAccount` and corresponding ClusterRoles/Roles so that the TargetAllocator has access to all the necessary resources to pull metrics from.
195195

196-
You can create your own `ServiceAccount`, and reference it in `spec.targetAllocator.serviceAccount` in your `OpenTelemetryCollector` CR. You’ll then need to configure the `ClusterRole` and `ClusterRoleBinding` for this `ServiceAccount`, as per below.
196+
You can create your own `ServiceAccount`, and reference it in `spec.targetAllocator.serviceAccount` in your `OpenTelemetryCollector` CR. You’ll then need to configure the `ClusterRole` and `ClusterRoleBinding` or `Role` and `RoleBinding` for this `ServiceAccount`, as per below.
197+
198+
#### Cluster-scoped RBAC
197199

198200
```yaml
199201
targetAllocator:
@@ -204,11 +206,11 @@ You can create your own `ServiceAccount`, and reference it in `spec.targetAlloca
204206
```
205207

206208
> 🚨 **Note**: The Collector part of this same CR *also* has a serviceAccount key which only affects the collector and *not*
207-
the TargetAllocator.
209+
> the TargetAllocator.
208210

209-
If you omit the `ServiceAccount` name, the TargetAllocator creates a `ServiceAccount` for you. The `ServiceAccount`’s default name is a concatenation of the Collector name and the `-targetallocator` suffix. By default, this `ServiceAccount` has no defined policy, so you’ll need to create your own `ClusterRole` and `ClusterRoleBinding` for it, as per below.
211+
If you omit the `ServiceAccount` name, the TargetAllocator creates a `ServiceAccount` for you. The `ServiceAccount`’s default name is a concatenation of the Collector name and the `-targetallocator` suffix. By default, this `ServiceAccount` has no defined policy, so you’ll need to create your own `ClusterRole` and `ClusterRoleBinding` or `Role` and `RoleBinding` for it, as per below.
210212

211-
The role below will provide the minimum access required for the Target Allocator to query all the targets it needs based on any Prometheus configurations:
213+
The ClusterRole below will provide the minimum access required for the Target Allocator to query all the targets it needs based on any Prometheus configurations:
212214

213215
```yaml
214216
apiVersion: rbac.authorization.k8s.io/v1
@@ -242,7 +244,7 @@ rules:
242244
verbs: ["get"]
243245
```
244246

245-
If you enable the the `prometheusCR` (set `spec.targetAllocator.prometheusCR.enabled` to `true`) in the `OpenTelemetryCollector` CR, you will also need to define the following roles. These give the TargetAllocator access to the `PodMonitor` and `ServiceMonitor` CRs. It also gives namespace access to the `PodMonitor` and `ServiceMonitor`.
247+
If you enable the `prometheusCR` (set `spec.targetAllocator.prometheusCR.enabled` to `true`) in the `OpenTelemetryCollector` CR, you will also need to define the following ClusterRoles. These give the TargetAllocator access to the `PodMonitor` and `ServiceMonitor` CRs. It also gives namespace access to the `PodMonitor` and `ServiceMonitor`.
246248

247249
```yaml
248250
apiVersion: rbac.authorization.k8s.io/v1
@@ -263,8 +265,81 @@ rules:
263265
verbs: ["get", "list", "watch"]
264266
```
265267

266-
> ✨ The above roles can be combined into a single role.
268+
> ✨ The above ClusterRoles can be combined into a single ClusterRole.
269+
270+
#### Namespace-scoped RBAC
271+
272+
If you want to have the TargetAllocator watch a specific namespace, you can set the allowNamespaces field
273+
in the TargetAllocator's prometheusCR configuration. This is useful if you want to restrict the TargetAllocator to only watch Prometheus
274+
CRs in a specific namespace, and not have cluster-wide access.
275+
276+
```yaml
277+
targetAllocator:
278+
enabled: true
279+
serviceAccount: opentelemetry-targetallocator-sa
280+
prometheusCR:
281+
enabled: true
282+
allowNamespaces: foo
283+
```
284+
285+
In this case, you will need to create a Role and RoleBinding instead of a ClusterRole and ClusterRoleBinding. The Role
286+
and RoleBinding should be created in the namespace specified by the allowNamespaces field.
267287

288+
```yaml
289+
apiVersion: rbac.authorization.k8s.io/v1
290+
kind: Role
291+
metadata:
292+
name: opentelemetry-targetallocator-role
293+
rules:
294+
- apiGroups:
295+
- ""
296+
resources:
297+
- pods
298+
- services
299+
- endpoints
300+
- configmaps
301+
- secrets
302+
- namespaces
303+
verbs:
304+
- get
305+
- watch
306+
- list
307+
- apiGroups:
308+
- apps
309+
resources:
310+
- statefulsets
311+
verbs:
312+
- get
313+
- watch
314+
- list
315+
- apiGroups:
316+
- discovery.k8s.io
317+
resources:
318+
- endpointslices
319+
verbs:
320+
- get
321+
- watch
322+
- list
323+
- apiGroups:
324+
- networking.k8s.io
325+
resources:
326+
- ingresses
327+
verbs:
328+
- get
329+
- watch
330+
- list
331+
- apiGroups:
332+
- monitoring.coreos.com
333+
resources:
334+
- servicemonitors
335+
- podmonitors
336+
- scrapeconfigs
337+
- probes
338+
verbs:
339+
- get
340+
- watch
341+
- list
342+
```
268343

269344
### Service / Pod monitor endpoint credentials
270345

@@ -420,4 +495,3 @@ Shards the received targets based on the discovered Collector instances
420495

421496
### Collector
422497
Client to watch for deployed Collector instances which will then provided to the Allocator.
423-

cmd/otel-allocator/internal/config/config.go

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ type Config struct {
5353

5454
type PrometheusCRConfig struct {
5555
Enabled bool `yaml:"enabled,omitempty"`
56+
AllowNamespaces string `yaml:"allow_namespaces,omitempty"`
57+
DenyNamespaces string `yaml:"deny_namespaces,omitempty"`
5658
PodMonitorSelector *metav1.LabelSelector `yaml:"pod_monitor_selector,omitempty"`
5759
PodMonitorNamespaceSelector *metav1.LabelSelector `yaml:"pod_monitor_namespace_selector,omitempty"`
5860
ServiceMonitorSelector *metav1.LabelSelector `yaml:"service_monitor_selector,omitempty"`

cmd/otel-allocator/internal/watcher/promOperator.go

+28-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"log/slog"
1010
"os"
11+
"strings"
1112
"time"
1213

1314
"github.com/blang/semver/v4"
@@ -53,7 +54,30 @@ func NewPrometheusCRWatcher(ctx context.Context, logger logr.Logger, cfg allocat
5354
return nil, err
5455
}
5556

56-
factory := informers.NewMonitoringInformerFactories(map[string]struct{}{v1.NamespaceAll: {}}, map[string]struct{}{}, mClient, allocatorconfig.DefaultResyncTime, nil) //TODO decide what strategy to use regarding namespaces
57+
if cfg.PrometheusCR.AllowNamespaces != "" && cfg.PrometheusCR.DenyNamespaces != "" {
58+
return nil, fmt.Errorf("both allow and deny namespaces are set, only one should be set")
59+
}
60+
61+
allowList := map[string]struct{}{}
62+
if cfg.PrometheusCR.AllowNamespaces != "" {
63+
logger.Info("watching namespace(s)", "namespaces", cfg.PrometheusCR.AllowNamespaces)
64+
for _, ns := range strings.Split(cfg.PrometheusCR.AllowNamespaces, ",") {
65+
allowList[ns] = struct{}{}
66+
}
67+
} else {
68+
logger.Info("cfg.PrometheusCR.AllowNamespaces is unset, watching all namespaces")
69+
allowList = map[string]struct{}{v1.NamespaceAll: {}}
70+
}
71+
72+
denyList := map[string]struct{}{}
73+
if cfg.PrometheusCR.DenyNamespaces != "" {
74+
logger.Info("excluding namespace(s)", "namespaces", cfg.PrometheusCR.DenyNamespaces)
75+
for _, ns := range strings.Split(cfg.PrometheusCR.DenyNamespaces, ",") {
76+
denyList[ns] = struct{}{}
77+
}
78+
}
79+
80+
factory := informers.NewMonitoringInformerFactories(allowList, denyList, mClient, allocatorconfig.DefaultResyncTime, nil)
5781

5882
monitoringInformers, err := getInformers(factory)
5983
if err != nil {
@@ -99,7 +123,7 @@ func NewPrometheusCRWatcher(ctx context.Context, logger logr.Logger, cfg allocat
99123
logger.Error(err, "Retrying namespace informer creation in promOperator CRD watcher")
100124
return true
101125
}, func() error {
102-
nsMonInf, err = getNamespaceInformer(ctx, map[string]struct{}{v1.NamespaceAll: {}}, promLogger, clientset, operatorMetrics)
126+
nsMonInf, err = getNamespaceInformer(ctx, allowList, denyList, promLogger, clientset, operatorMetrics)
103127
return err
104128
})
105129
if getNamespaceInformerErr != nil {
@@ -149,7 +173,7 @@ type PrometheusCRWatcher struct {
149173
store *assets.StoreBuilder
150174
}
151175

152-
func getNamespaceInformer(ctx context.Context, allowList map[string]struct{}, promOperatorLogger *slog.Logger, clientset kubernetes.Interface, operatorMetrics *operator.Metrics) (cache.SharedIndexInformer, error) {
176+
func getNamespaceInformer(ctx context.Context, allowList, denyList map[string]struct{}, promOperatorLogger *slog.Logger, clientset kubernetes.Interface, operatorMetrics *operator.Metrics) (cache.SharedIndexInformer, error) {
153177
kubernetesVersion, err := clientset.Discovery().ServerVersion()
154178
if err != nil {
155179
return nil, err
@@ -165,7 +189,7 @@ func getNamespaceInformer(ctx context.Context, allowList map[string]struct{}, pr
165189
clientset.CoreV1(),
166190
clientset.AuthorizationV1().SelfSubjectAccessReviews(),
167191
allowList,
168-
map[string]struct{}{},
192+
denyList,
169193
)
170194
if err != nil {
171195
return nil, err

config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -7875,6 +7875,10 @@ spec:
78757875
type: object
78767876
prometheusCR:
78777877
properties:
7878+
allowNamespaces:
7879+
type: string
7880+
denyNamespaces:
7881+
type: string
78787882
enabled:
78797883
type: boolean
78807884
podMonitorSelector:

config/crd/bases/opentelemetry.io_targetallocators.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -2254,6 +2254,10 @@ spec:
22542254
type: string
22552255
prometheusCR:
22562256
properties:
2257+
allowNamespaces:
2258+
type: string
2259+
denyNamespaces:
2260+
type: string
22572261
enabled:
22582262
type: boolean
22592263
podMonitorSelector:

0 commit comments

Comments
 (0)