Skip to content

Commit 1499f88

Browse files
committed
Generate TargetAllocator CR from Collector CR
1 parent c79f185 commit 1499f88

File tree

7 files changed

+303
-21
lines changed

7 files changed

+303
-21
lines changed

apis/v1alpha2/targetallocator_types.go

+51-2
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@
1717
package v1alpha2
1818

1919
import (
20+
"encoding/json"
21+
2022
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2123
)
2224

2325
// TargetAllocatorSpec defines the desired state of TargetAllocator.
2426
type TargetAllocatorSpec struct {
2527
// Common defines fields that are common to all OpenTelemetry CRD workloads.
2628
OpenTelemetryCommonFields `json:",inline"`
29+
// CollectorSelector is the selector for Collector Pods the target allocator will allocate targets to.
30+
CollectorSelector metav1.LabelSelector `json:"collectorSelector,omitempty"`
2731
// AllocationStrategy determines which strategy the target allocator should use for allocation.
2832
// The current options are least-weighted and consistent-hashing. The default option is consistent-hashing
2933
// +optional
@@ -35,8 +39,13 @@ type TargetAllocatorSpec struct {
3539
// +optional
3640
// +kubebuilder:default:=relabel-config
3741
FilterStrategy TargetAllocatorFilterStrategy `json:"filterStrategy,omitempty"`
38-
// ServiceAccount indicates the name of an existing service account to use with this instance. When set,
39-
// the operator will not automatically create a ServiceAccount for the TargetAllocator.
42+
// ScrapeConfigs define static Prometheus scrape configurations for the target allocator.
43+
// To use dynamic configurations from ServiceMonitors and PodMonitors, see the PrometheusCR section.
44+
// For the exact format, see https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config.
45+
// +optional
46+
// +listType=atomic
47+
ScrapeConfigs []ScrapeConfig `json:"scrapeConfigs,omitempty"`
48+
// PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 and podmonitor.monitoring.coreos.com/v1 ).
4049
// +optional
4150
PrometheusCR TargetAllocatorPrometheusCR `json:"prometheusCR,omitempty"`
4251
}
@@ -82,6 +91,46 @@ type TargetAllocatorStatus struct {
8291
Messages []string `json:"messages,omitempty"`
8392
}
8493

94+
// ScrapeConfig is a Prometheus scrape config definition.
95+
type ScrapeConfig map[string]interface{}
96+
97+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
98+
func (in *ScrapeConfig) DeepCopyInto(out *ScrapeConfig) {
99+
*out = make(map[string]interface{}, len(*in))
100+
for key, val := range *in {
101+
(*out)[key] = val
102+
}
103+
}
104+
105+
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScrapeConfig.
106+
func (in *ScrapeConfig) DeepCopy() *ScrapeConfig {
107+
if in == nil {
108+
return nil
109+
}
110+
out := new(ScrapeConfig)
111+
in.DeepCopyInto(out)
112+
return out
113+
}
114+
115+
var _ json.Marshaler = &ScrapeConfig{}
116+
var _ json.Unmarshaler = &ScrapeConfig{}
117+
118+
// UnmarshalJSON implements an alternative parser for this field.
119+
func (c *ScrapeConfig) UnmarshalJSON(b []byte) error {
120+
if err := json.Unmarshal(b, c); err != nil {
121+
return err
122+
}
123+
return nil
124+
}
125+
126+
// MarshalJSON specifies how to convert this object into JSON.
127+
func (c *ScrapeConfig) MarshalJSON() ([]byte, error) {
128+
if c == nil {
129+
return []byte("{}"), nil
130+
}
131+
return json.Marshal(c)
132+
}
133+
85134
//+kubebuilder:object:root=true
86135
//+kubebuilder:subresource:status
87136

apis/v1alpha2/zz_generated.deepcopy.go

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controllers/opentelemetrycollector_controller.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"github.com/open-telemetry/opentelemetry-operator/internal/api/convert"
3636
"github.com/open-telemetry/opentelemetry-operator/internal/config"
3737
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
38+
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector"
3839
collectorStatus "github.com/open-telemetry/opentelemetry-operator/internal/status/collector"
3940
"github.com/open-telemetry/opentelemetry-operator/pkg/featuregate"
4041
)
@@ -62,14 +63,24 @@ func (r *OpenTelemetryCollectorReconciler) getParams(instance v1alpha1.OpenTelem
6263
if err != nil {
6364
return manifests.Params{}, err
6465
}
65-
return manifests.Params{
66+
params := manifests.Params{
6667
Config: r.config,
6768
Client: r.Client,
6869
OtelCol: otelCol,
6970
Log: r.log,
7071
Scheme: r.scheme,
7172
Recorder: r.recorder,
72-
}, nil
73+
}
74+
75+
// generate the target allocator CR from the collector CR
76+
targetAllocator, err := collector.TargetAllocator(params)
77+
if err != nil {
78+
return params, err
79+
}
80+
if targetAllocator != nil {
81+
params.TargetAllocator = *targetAllocator
82+
}
83+
return params, nil
7384
}
7485

7586
// NewReconciler creates a new reconciler for OpenTelemetryCollector objects.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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 collector
16+
17+
import (
18+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+
20+
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha2"
21+
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
22+
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils"
23+
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters"
24+
)
25+
26+
// TargetAllocator builds the TargetAllocator CR for the given instance.
27+
func TargetAllocator(params manifests.Params) (*v1alpha2.TargetAllocator, error) {
28+
29+
taSpec := params.OtelCol.Spec.TargetAllocator
30+
if !taSpec.Enabled {
31+
return nil, nil
32+
}
33+
34+
collectorSelector := metav1.LabelSelector{
35+
MatchLabels: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector),
36+
}
37+
38+
configStr, err := params.OtelCol.Spec.Config.Yaml()
39+
if err != nil {
40+
return nil, err
41+
}
42+
scrapeConfigs, err := getScrapeConfigs(configStr)
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
return &v1alpha2.TargetAllocator{
48+
ObjectMeta: metav1.ObjectMeta{
49+
Name: params.OtelCol.Name,
50+
Namespace: params.OtelCol.Namespace,
51+
Annotations: params.OtelCol.Annotations,
52+
Labels: params.OtelCol.Labels,
53+
},
54+
Spec: v1alpha2.TargetAllocatorSpec{
55+
OpenTelemetryCommonFields: v1alpha2.OpenTelemetryCommonFields{
56+
Replicas: taSpec.Replicas,
57+
NodeSelector: taSpec.NodeSelector,
58+
Resources: taSpec.Resources,
59+
ServiceAccount: taSpec.ServiceAccount,
60+
SecurityContext: taSpec.SecurityContext,
61+
PodSecurityContext: taSpec.PodSecurityContext,
62+
Image: taSpec.Image,
63+
Affinity: taSpec.Affinity,
64+
TopologySpreadConstraints: taSpec.TopologySpreadConstraints,
65+
Tolerations: taSpec.Tolerations,
66+
Env: taSpec.Env,
67+
PodAnnotations: params.OtelCol.Spec.PodAnnotations,
68+
},
69+
CollectorSelector: collectorSelector,
70+
AllocationStrategy: taSpec.AllocationStrategy,
71+
FilterStrategy: taSpec.FilterStrategy,
72+
ScrapeConfigs: scrapeConfigs,
73+
PrometheusCR: taSpec.PrometheusCR,
74+
},
75+
}, nil
76+
}
77+
78+
func getScrapeConfigs(otelcolConfig string) ([]v1alpha2.ScrapeConfig, error) {
79+
// Collector supports environment variable substitution, but the TA does not.
80+
// TA Scrape Configs should have a single "$", as it does not support env var substitution
81+
prometheusReceiverConfig, err := adapters.UnescapeDollarSignsInPromConfig(otelcolConfig)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
scrapeConfigs, err := adapters.GetScrapeConfigsFromPromConfig(prometheusReceiverConfig)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
v1alpha2scrapeConfigs := make([]v1alpha2.ScrapeConfig, len(scrapeConfigs))
92+
93+
for i, config := range scrapeConfigs {
94+
v1alpha2scrapeConfigs[i] = config
95+
}
96+
97+
return v1alpha2scrapeConfigs, nil
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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 collector
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
23+
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha2"
24+
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
25+
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils"
26+
)
27+
28+
func TestTargetAllocator(t *testing.T) {
29+
objectMetadata := metav1.ObjectMeta{
30+
Name: "name",
31+
Namespace: "namespace",
32+
Annotations: map[string]string{
33+
"annotation_key": "annotation_value",
34+
},
35+
Labels: map[string]string{
36+
"label_key": "label_value",
37+
},
38+
}
39+
otelcolConfig := v1alpha2.Config{
40+
Receivers: v1alpha2.AnyConfig{
41+
Object: map[string]interface{}{
42+
"prometheus": map[string]any{
43+
"config": map[string]any{
44+
"scrape_configs": []any{},
45+
},
46+
},
47+
},
48+
},
49+
}
50+
51+
testCases := []struct {
52+
name string
53+
input v1alpha2.OpenTelemetryCollector
54+
want *v1alpha2.TargetAllocator
55+
wantErr error
56+
}{
57+
{
58+
name: "disabled",
59+
input: v1alpha2.OpenTelemetryCollector{
60+
Spec: v1alpha2.OpenTelemetryCollectorSpec{
61+
TargetAllocator: v1alpha2.TargetAllocatorEmbedded{
62+
Enabled: false,
63+
},
64+
},
65+
},
66+
want: nil,
67+
},
68+
{
69+
name: "metadata",
70+
input: v1alpha2.OpenTelemetryCollector{
71+
ObjectMeta: objectMetadata,
72+
Spec: v1alpha2.OpenTelemetryCollectorSpec{
73+
Config: otelcolConfig,
74+
TargetAllocator: v1alpha2.TargetAllocatorEmbedded{
75+
Enabled: true,
76+
},
77+
},
78+
},
79+
want: &v1alpha2.TargetAllocator{
80+
ObjectMeta: objectMetadata,
81+
Spec: v1alpha2.TargetAllocatorSpec{
82+
CollectorSelector: metav1.LabelSelector{
83+
MatchLabels: manifestutils.SelectorLabels(objectMetadata, ComponentOpenTelemetryCollector),
84+
},
85+
ScrapeConfigs: []v1alpha2.ScrapeConfig{},
86+
},
87+
},
88+
},
89+
}
90+
91+
for _, testCase := range testCases {
92+
testCase := testCase
93+
t.Run(testCase.name, func(t *testing.T) {
94+
params := manifests.Params{
95+
OtelCol: testCase.input,
96+
}
97+
actual, err := TargetAllocator(params)
98+
assert.Equal(t, testCase.wantErr, err)
99+
assert.Equal(t, testCase.want, actual)
100+
})
101+
}
102+
}

internal/manifests/params.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ import (
2727

2828
// Params holds the reconciliation-specific parameters.
2929
type Params struct {
30-
Client client.Client
31-
Recorder record.EventRecorder
32-
Scheme *runtime.Scheme
33-
Log logr.Logger
34-
OtelCol v1alpha2.OpenTelemetryCollector
35-
OpAMPBridge v1alpha1.OpAMPBridge
36-
Config config.Config
30+
Client client.Client
31+
Recorder record.EventRecorder
32+
Scheme *runtime.Scheme
33+
Log logr.Logger
34+
OtelCol v1alpha2.OpenTelemetryCollector
35+
TargetAllocator v1alpha2.TargetAllocator
36+
OpAMPBridge v1alpha1.OpAMPBridge
37+
Config config.Config
3738
}

0 commit comments

Comments
 (0)