Skip to content

Commit

Permalink
Move TA config parsing to configmap manifest builder (#3243)
Browse files Browse the repository at this point in the history
This is in preparation for the Target Allocator CRD. For that CRD, the
Collector instance may be specified via a label or ownership relation.
Therefore, we need to pull the necessary data from the Collector
instance during TA config generation. It's more convenient to do this in
all cases, which is what this change does.
  • Loading branch information
swiatekm authored Aug 30, 2024
1 parent d4780a6 commit ef1306b
Show file tree
Hide file tree
Showing 4 changed files with 467 additions and 239 deletions.
57 changes: 0 additions & 57 deletions internal/manifests/collector/targetallocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@
package collector

import (
"github.com/mitchellh/mapstructure"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters"
)

// TargetAllocator builds the TargetAllocator CR for the given instance.
Expand All @@ -32,19 +30,6 @@ func TargetAllocator(params manifests.Params) (*v1alpha1.TargetAllocator, error)
return nil, nil
}

configStr, err := params.OtelCol.Spec.Config.Yaml()
if err != nil {
return nil, err
}
scrapeConfigs, err := getScrapeConfigs(configStr)
if err != nil {
return nil, err
}
globalConfig, err := getGlobalConfig(params.OtelCol.Spec.Config)
if err != nil {
return nil, err
}

return &v1alpha1.TargetAllocator{
ObjectMeta: metav1.ObjectMeta{
Name: params.OtelCol.Name,
Expand All @@ -70,50 +55,8 @@ func TargetAllocator(params manifests.Params) (*v1alpha1.TargetAllocator, error)
},
AllocationStrategy: taSpec.AllocationStrategy,
FilterStrategy: taSpec.FilterStrategy,
ScrapeConfigs: scrapeConfigs,
GlobalConfig: globalConfig,
PrometheusCR: taSpec.PrometheusCR,
Observability: taSpec.Observability,
},
}, nil
}

func getGlobalConfig(otelConfig v1beta1.Config) (v1beta1.AnyConfig, error) {
// TODO: Eventually we should figure out a way to pull this in to the main specification for the TA
type promReceiverConfig struct {
Prometheus struct {
Config struct {
Global map[string]interface{} `mapstructure:"global"`
} `mapstructure:"config"`
} `mapstructure:"prometheus"`
}
decodedConfig := &promReceiverConfig{}
if err := mapstructure.Decode(otelConfig.Receivers.Object, decodedConfig); err != nil {
return v1beta1.AnyConfig{}, err
}
return v1beta1.AnyConfig{
Object: decodedConfig.Prometheus.Config.Global,
}, nil
}

func getScrapeConfigs(otelcolConfig string) ([]v1beta1.AnyConfig, error) {
// Collector supports environment variable substitution, but the TA does not.
// TA Scrape Configs should have a single "$", as it does not support env var substitution
prometheusReceiverConfig, err := adapters.UnescapeDollarSignsInPromConfig(otelcolConfig)
if err != nil {
return nil, err
}

scrapeConfigs, err := adapters.GetScrapeConfigsFromPromConfig(prometheusReceiverConfig)
if err != nil {
return nil, err
}

v1beta1scrapeConfigs := make([]v1beta1.AnyConfig, len(scrapeConfigs))

for i, config := range scrapeConfigs {
v1beta1scrapeConfigs[i] = v1beta1.AnyConfig{Object: config}
}

return v1beta1scrapeConfigs, nil
}
177 changes: 1 addition & 176 deletions internal/manifests/collector/targetallocator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@
package collector

import (
"fmt"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -89,10 +87,7 @@ func TestTargetAllocator(t *testing.T) {
},
want: &v1alpha1.TargetAllocator{
ObjectMeta: objectMetadata,
Spec: v1alpha1.TargetAllocatorSpec{
ScrapeConfigs: []v1beta1.AnyConfig{},
GlobalConfig: v1beta1.AnyConfig{},
},
Spec: v1alpha1.TargetAllocatorSpec{},
},
},
{
Expand Down Expand Up @@ -291,7 +286,6 @@ func TestTargetAllocator(t *testing.T) {
MatchLabels: map[string]string{"servicemonitorkey": "servicemonitorkey"},
},
},
ScrapeConfigs: []v1beta1.AnyConfig{},
Observability: v1beta1.ObservabilitySpec{
Metrics: v1beta1.MetricsConfigSpec{
EnableMetrics: true,
Expand All @@ -314,172 +308,3 @@ func TestTargetAllocator(t *testing.T) {
})
}
}

func TestGetScrapeConfigs(t *testing.T) {
testCases := []struct {
name string
input v1beta1.Config
want []v1beta1.AnyConfig
wantErr error
}{
{
name: "empty scrape configs list",
input: v1beta1.Config{
Receivers: v1beta1.AnyConfig{
Object: map[string]interface{}{
"prometheus": map[string]any{
"config": map[string]any{
"scrape_configs": []any{},
},
},
},
},
},
want: []v1beta1.AnyConfig{},
},
{
name: "no scrape configs key",
input: v1beta1.Config{
Receivers: v1beta1.AnyConfig{
Object: map[string]interface{}{
"prometheus": map[string]any{
"config": map[string]any{},
},
},
},
},
wantErr: fmt.Errorf("no scrape_configs available as part of the configuration"),
},
{
name: "one scrape config",
input: v1beta1.Config{
Receivers: v1beta1.AnyConfig{
Object: map[string]interface{}{
"prometheus": map[string]any{
"config": map[string]any{
"scrape_configs": []any{
map[string]any{
"job": "somejob",
},
},
},
},
},
},
},
want: []v1beta1.AnyConfig{
{Object: map[string]interface{}{"job": "somejob"}},
},
},
{
name: "regex substitution",
input: v1beta1.Config{
Receivers: v1beta1.AnyConfig{
Object: map[string]interface{}{
"prometheus": map[string]any{
"config": map[string]any{
"scrape_configs": []any{
map[string]any{
"job": "somejob",
"metric_relabel_configs": []map[string]any{
{
"action": "labelmap",
"regex": "label_(.+)",
"replacement": "$$1",
},
},
},
},
},
},
},
},
},
want: []v1beta1.AnyConfig{
{Object: map[string]interface{}{
"job": "somejob",
"metric_relabel_configs": []any{
map[any]any{
"action": "labelmap",
"regex": "label_(.+)",
"replacement": "$1",
},
},
}},
},
},
}

for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
configStr, err := testCase.input.Yaml()
require.NoError(t, err)
actual, err := getScrapeConfigs(configStr)
assert.Equal(t, testCase.wantErr, err)
assert.Equal(t, testCase.want, actual)
})
}
}

func Test_getGlobalConfig(t *testing.T) {
type args struct {
otelConfig v1beta1.Config
}
tests := []struct {
name string
args args
want v1beta1.AnyConfig
wantErr error
}{
{
name: "Valid Global Config",
args: args{
otelConfig: v1beta1.Config{
Receivers: v1beta1.AnyConfig{
Object: map[string]interface{}{
"prometheus": map[string]interface{}{
"config": map[string]interface{}{
"global": map[string]interface{}{
"scrape_interval": "15s",
"scrape_protocols": []string{"PrometheusProto", "OpenMetricsText1.0.0", "OpenMetricsText0.0.1", "PrometheusText0.0.4"},
},
},
},
},
},
},
},
want: v1beta1.AnyConfig{
Object: map[string]interface{}{
"scrape_interval": "15s",
"scrape_protocols": []string{"PrometheusProto", "OpenMetricsText1.0.0", "OpenMetricsText0.0.1", "PrometheusText0.0.4"},
},
},
wantErr: nil,
},
{
name: "Invalid Global Config - Missing Global",
args: args{
otelConfig: v1beta1.Config{
Receivers: v1beta1.AnyConfig{
Object: map[string]interface{}{
"prometheus": map[string]interface{}{
"config": map[string]interface{}{},
},
},
},
},
},
want: v1beta1.AnyConfig{},
wantErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getGlobalConfig(tt.args.otelConfig)
assert.Equal(t, tt.wantErr, err)
assert.Equal(t, tt.want, got)
})
}
}
Loading

0 comments on commit ef1306b

Please sign in to comment.