Skip to content

Commit 856dc8e

Browse files
committed
Allow setting target allocator via label
1 parent 49ca805 commit 856dc8e

14 files changed

+389
-6
lines changed

.github/workflows/e2e.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ jobs:
5151
kube-version: "1.29"
5252
- group: e2e-targetallocator
5353
setup: "enable-targetallocator-cr prepare-e2e"
54+
- group: e2e-targetallocator-cr
55+
setup: "enable-targetallocator-cr prepare-e2e"
5456
steps:
5557
- name: Check out code into the Go module directory
5658
uses: actions/checkout@v4

Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,11 @@ e2e-prometheuscr: chainsaw
327327
e2e-targetallocator: chainsaw
328328
$(CHAINSAW) test --test-dir ./tests/e2e-targetallocator
329329

330+
# Target allocator CR end-to-tests
331+
.PHONY: e2e-targetallocator-cr
332+
e2e-targetallocator-cr: chainsaw
333+
$(CHAINSAW) test --test-dir ./tests/e2e-targetallocator-cr
334+
330335
.PHONY: add-certmanager-permissions
331336
add-certmanager-permissions:
332337
# Kustomize only allows patches in the folder where the kustomization is located

controllers/opentelemetrycollector_controller.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"sigs.k8s.io/controller-runtime/pkg/client"
3939
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
4040

41+
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
4142
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
4243
"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift"
4344
"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/prometheus"
@@ -50,6 +51,8 @@ import (
5051
"github.com/open-telemetry/opentelemetry-operator/pkg/featuregate"
5152
)
5253

54+
const collectorTargetAllocatorLabelName = "opentelemetry.io/target-allocator"
55+
5356
var (
5457
ownedClusterObjectTypes = []client.Object{
5558
&rbacv1.ClusterRole{},
@@ -168,7 +171,7 @@ func (r *OpenTelemetryCollectorReconciler) getConfigMapsToRemove(configVersionsT
168171
return ownedConfigMaps
169172
}
170173

171-
func (r *OpenTelemetryCollectorReconciler) GetParams(instance v1beta1.OpenTelemetryCollector) (manifests.Params, error) {
174+
func (r *OpenTelemetryCollectorReconciler) GetParams(ctx context.Context, instance v1beta1.OpenTelemetryCollector) (manifests.Params, error) {
172175
p := manifests.Params{
173176
Config: r.config,
174177
Client: r.Client,
@@ -179,14 +182,28 @@ func (r *OpenTelemetryCollectorReconciler) GetParams(instance v1beta1.OpenTeleme
179182
}
180183

181184
// generate the target allocator CR from the collector CR
182-
targetAllocator, err := collector.TargetAllocator(p)
185+
targetAllocator, err := r.getTargetAllocator(ctx, p)
183186
if err != nil {
184187
return p, err
185188
}
186189
p.TargetAllocator = targetAllocator
187190
return p, nil
188191
}
189192

193+
func (r *OpenTelemetryCollectorReconciler) getTargetAllocator(ctx context.Context, params manifests.Params) (*v1alpha1.TargetAllocator, error) {
194+
otelcol := params.OtelCol
195+
if taName, ok := otelcol.GetLabels()[collectorTargetAllocatorLabelName]; ok {
196+
targetAllocator := &v1alpha1.TargetAllocator{}
197+
taKey := client.ObjectKey{Name: taName, Namespace: otelcol.GetNamespace()}
198+
err := r.Client.Get(ctx, taKey, targetAllocator)
199+
if err != nil {
200+
return nil, err
201+
}
202+
return targetAllocator, nil
203+
}
204+
return collector.TargetAllocator(params)
205+
}
206+
190207
// NewReconciler creates a new reconciler for OpenTelemetryCollector objects.
191208
func NewReconciler(p Params) *OpenTelemetryCollectorReconciler {
192209
r := &OpenTelemetryCollectorReconciler{
@@ -230,7 +247,7 @@ func (r *OpenTelemetryCollectorReconciler) Reconcile(ctx context.Context, req ct
230247
return ctrl.Result{}, client.IgnoreNotFound(err)
231248
}
232249

233-
params, err := r.GetParams(instance)
250+
params, err := r.GetParams(ctx, instance)
234251
if err != nil {
235252
log.Error(err, "Failed to create manifest.Params")
236253
return ctrl.Result{}, err

controllers/targetallocator_controller.go

+51-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,24 @@ func (r *TargetAllocatorReconciler) getCollector(ctx context.Context, instance v
9898
return &collector, nil
9999
}
100100

101-
return nil, nil
101+
var collectors v1beta1.OpenTelemetryCollectorList
102+
listOpts := []client.ListOption{
103+
client.InNamespace(instance.GetNamespace()),
104+
client.MatchingLabels{
105+
collectorTargetAllocatorLabelName: instance.GetName(),
106+
},
107+
}
108+
err := r.List(ctx, &collectors, listOpts...)
109+
if err != nil {
110+
return nil, err
111+
}
112+
if len(collectors.Items) == 0 {
113+
return nil, nil
114+
} else if len(collectors.Items) > 1 {
115+
return nil, fmt.Errorf("found multiple OpenTelemetry collectors annotated with the same Target Allocator: %s/%s", instance.GetNamespace(), instance.GetName())
116+
}
117+
118+
return &collectors.Items[0], nil
102119
}
103120

104121
// NewTargetAllocatorReconciler creates a new reconciler for TargetAllocator objects.
@@ -195,6 +212,25 @@ func (r *TargetAllocatorReconciler) SetupWithManager(mgr ctrl.Manager) error {
195212
),
196213
)
197214

215+
// watch collectors which have the target allocator label
216+
collectorSelector := metav1.LabelSelector{
217+
MatchExpressions: []metav1.LabelSelectorRequirement{
218+
{
219+
Key: collectorTargetAllocatorLabelName,
220+
Operator: metav1.LabelSelectorOpExists,
221+
},
222+
},
223+
}
224+
selectorPredicate, err := predicate.LabelSelectorPredicate(collectorSelector)
225+
if err != nil {
226+
return err
227+
}
228+
ctrlBuilder.Watches(
229+
&v1beta1.OpenTelemetryCollector{},
230+
handler.EnqueueRequestsFromMapFunc(getTargetAllocatorRequestsFromLabel),
231+
builder.WithPredicates(selectorPredicate),
232+
)
233+
198234
return ctrlBuilder.Complete(r)
199235
}
200236

@@ -208,3 +244,17 @@ func getTargetAllocatorForCollector(_ context.Context, collector client.Object)
208244
},
209245
}
210246
}
247+
248+
func getTargetAllocatorRequestsFromLabel(_ context.Context, collector client.Object) []reconcile.Request {
249+
if taName, ok := collector.GetLabels()[collectorTargetAllocatorLabelName]; ok {
250+
return []reconcile.Request{
251+
{
252+
NamespacedName: types.NamespacedName{
253+
Name: taName,
254+
Namespace: collector.GetNamespace(),
255+
},
256+
},
257+
}
258+
}
259+
return []reconcile.Request{}
260+
}

controllers/targetallocator_reconciler_test.go

+54-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ func init() {
5555
func TestTargetAllocatorReconciler_GetCollector(t *testing.T) {
5656
testCollector := &v1beta1.OpenTelemetryCollector{
5757
ObjectMeta: metav1.ObjectMeta{
58-
Name: "my-instance-collector",
58+
Name: "test",
59+
Labels: map[string]string{
60+
collectorTargetAllocatorLabelName: "label-ta",
61+
},
5962
},
6063
}
6164
fakeClient := fake.NewFakeClient(testCollector)
@@ -105,6 +108,36 @@ func TestTargetAllocatorReconciler_GetCollector(t *testing.T) {
105108
assert.Nil(t, collector)
106109
assert.Errorf(t, err, "error getting owner for TargetAllocator default/test: opentelemetrycollectors.opentelemetry.io \"non_existent\" not found")
107110
})
111+
t.Run("collector attached by label", func(t *testing.T) {
112+
ta := v1alpha1.TargetAllocator{
113+
ObjectMeta: metav1.ObjectMeta{
114+
Name: "label-ta",
115+
},
116+
}
117+
collector, err := reconciler.getCollector(context.Background(), ta)
118+
require.NoError(t, err)
119+
assert.Equal(t, testCollector, collector)
120+
})
121+
t.Run("multiple collectors attached by label", func(t *testing.T) {
122+
testCollector2 := testCollector.DeepCopy()
123+
testCollector2.SetName("test2")
124+
fakeClient := fake.NewFakeClient(testCollector, testCollector2)
125+
reconciler := NewTargetAllocatorReconciler(
126+
fakeClient,
127+
testScheme,
128+
record.NewFakeRecorder(10),
129+
config.New(),
130+
testLogger,
131+
)
132+
ta := v1alpha1.TargetAllocator{
133+
ObjectMeta: metav1.ObjectMeta{
134+
Name: "label-ta",
135+
},
136+
}
137+
collector, err := reconciler.getCollector(context.Background(), ta)
138+
assert.Nil(t, collector)
139+
assert.Errorf(t, err, "found multiple OpenTelemetry collectors annotated with the same Target Allocator: %s/%s", ta.Namespace, ta.Name)
140+
})
108141
}
109142

110143
func TestGetTargetAllocatorForCollector(t *testing.T) {
@@ -123,3 +156,23 @@ func TestGetTargetAllocatorForCollector(t *testing.T) {
123156
}}
124157
assert.Equal(t, expected, requests)
125158
}
159+
160+
func TestGetTargetAllocatorRequestsFromLabel(t *testing.T) {
161+
testCollector := &v1beta1.OpenTelemetryCollector{
162+
ObjectMeta: metav1.ObjectMeta{
163+
Name: "test",
164+
Namespace: "default",
165+
Labels: map[string]string{
166+
collectorTargetAllocatorLabelName: "label-ta",
167+
},
168+
},
169+
}
170+
requests := getTargetAllocatorRequestsFromLabel(context.Background(), testCollector)
171+
expected := []reconcile.Request{{
172+
NamespacedName: types.NamespacedName{
173+
Name: "label-ta",
174+
Namespace: "default",
175+
},
176+
}}
177+
assert.Equal(t, expected, requests)
178+
}

main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ func main() {
440440

441441
bv := func(collector otelv1beta1.OpenTelemetryCollector) admission.Warnings {
442442
var warnings admission.Warnings
443-
params, newErr := collectorReconciler.GetParams(collector)
443+
params, newErr := collectorReconciler.GetParams(context.Background(), collector)
444444
if err != nil {
445445
warnings = append(warnings, newErr.Error())
446446
return warnings
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
apiVersion: v1
3+
kind: ConfigMap
4+
metadata:
5+
labels:
6+
app.kubernetes.io/name: ta-collector
7+
data:
8+
collector.yaml: |
9+
receivers:
10+
prometheus:
11+
config:
12+
scrape_configs:
13+
- job_name: otel-collector
14+
scrape_interval: 10s
15+
static_configs:
16+
- targets:
17+
- 0.0.0.0:8888
18+
exporters:
19+
debug: {}
20+
service:
21+
telemetry:
22+
metrics:
23+
address: 0.0.0.0:8888
24+
pipelines:
25+
metrics:
26+
exporters:
27+
- debug
28+
receivers:
29+
- prometheus
30+
31+
---
32+
apiVersion: v1
33+
data:
34+
targetallocator.yaml: |
35+
allocation_strategy: consistent-hashing
36+
collector_selector: null
37+
filter_strategy: ""
38+
kind: ConfigMap
39+
metadata:
40+
name: ta-targetallocator
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
apiVersion: opentelemetry.io/v1alpha1
3+
kind: TargetAllocator
4+
metadata:
5+
name: ta
6+
spec:
7+
---
8+
apiVersion: opentelemetry.io/v1beta1
9+
kind: OpenTelemetryCollector
10+
metadata:
11+
name: ta
12+
spec:
13+
mode: statefulset
14+
config:
15+
receivers:
16+
prometheus:
17+
config:
18+
scrape_configs:
19+
- job_name: 'otel-collector'
20+
scrape_interval: 10s
21+
static_configs:
22+
- targets: [ '0.0.0.0:8888' ]
23+
exporters:
24+
debug: {}
25+
service:
26+
pipelines:
27+
metrics:
28+
receivers: [prometheus]
29+
exporters: [debug]
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
apiVersion: opentelemetry.io/v1beta1
3+
kind: OpenTelemetryCollector
4+
metadata:
5+
name: ta
6+
labels:
7+
opentelemetry.io/target-allocator: ta
8+
spec:
9+
mode: statefulset
10+
config:
11+
receivers:
12+
prometheus:
13+
config:
14+
scrape_configs:
15+
- job_name: 'otel-collector'
16+
scrape_interval: 10s
17+
static_configs:
18+
- targets: [ '0.0.0.0:8888' ]
19+
exporters:
20+
debug: {}
21+
service:
22+
pipelines:
23+
metrics:
24+
receivers: [prometheus]
25+
exporters: [debug]
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
apiVersion: v1
3+
kind: ConfigMap
4+
metadata:
5+
labels:
6+
app.kubernetes.io/name: ta-collector
7+
data:
8+
collector.yaml: |
9+
exporters:
10+
debug: {}
11+
receivers:
12+
prometheus:
13+
config: {}
14+
target_allocator:
15+
collector_id: ${POD_NAME}
16+
endpoint: http://ta-targetallocator:80
17+
interval: 30s
18+
service:
19+
pipelines:
20+
metrics:
21+
exporters:
22+
- debug
23+
receivers:
24+
- prometheus
25+
telemetry:
26+
metrics:
27+
address: 0.0.0.0:8888
28+
---
29+
apiVersion: v1
30+
data:
31+
targetallocator.yaml:
32+
( contains(@, join(':', ['app.kubernetes.io/component', ' opentelemetry-collector'])) ): true
33+
( contains(@, join('', ['app.kubernetes.io/instance:', ' ', $namespace, '.ta'])) ): true
34+
( contains(@, join(':', ['app.kubernetes.io/managed-by', ' opentelemetry-operator'])) ): true
35+
( contains(@, join(':', ['app.kubernetes.io/part-of', ' opentelemetry'])) ): true
36+
( contains(@, join(':', ['job_name', ' otel-collector'])) ): true
37+
kind: ConfigMap
38+
metadata:
39+
name: ta-targetallocator

0 commit comments

Comments
 (0)