Skip to content

Commit 8eb13f1

Browse files
frzifusjaronoff97
andauthored
Use 0.0.0.0 as otlp receiver default address (#3325)
* operator: expose receivers by default Signed-off-by: Benedikt Bongartz <[email protected]> * singleEndpointConfig: use omitempty Signed-off-by: Benedikt Bongartz <[email protected]> * components: apply MultiAddressDefaulter Signed-off-by: Benedikt Bongartz <[email protected]> * simplify some logic, add tests * config merge: handle nil values Signed-off-by: Benedikt Bongartz <[email protected]> --------- Signed-off-by: Benedikt Bongartz <[email protected]> Co-authored-by: Jacob Aronoff <[email protected]>
1 parent e84193d commit 8eb13f1

File tree

22 files changed

+536
-46
lines changed

22 files changed

+536
-46
lines changed

.chloggen/add_receiver_defaults.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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: operator
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Use 0.0.0.0 as otlp receiver default address
9+
10+
# One or more tracking issues related to the change
11+
issues: [3126]
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:

apis/v1beta1/collector_webhook.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (c CollectorWebhook) Default(_ context.Context, obj runtime.Object) error {
102102
if len(otelcol.Spec.ManagementState) == 0 {
103103
otelcol.Spec.ManagementState = ManagementStateManaged
104104
}
105-
return nil
105+
return otelcol.Spec.Config.ApplyDefaults(c.logger)
106106
}
107107

108108
func (c CollectorWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {

apis/v1beta1/collector_webhook_test.go

+66
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,72 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
144144
expected v1beta1.OpenTelemetryCollector
145145
shouldFailSar bool
146146
}{
147+
{
148+
name: "update config defaults",
149+
otelcol: v1beta1.OpenTelemetryCollector{
150+
Spec: v1beta1.OpenTelemetryCollectorSpec{
151+
Config: func() v1beta1.Config {
152+
const input = `{"receivers":{"otlp":{"protocols":{"grpc":null,"http":null}}},"exporters":{"debug":null},"service":{"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
153+
var cfg v1beta1.Config
154+
require.NoError(t, yaml.Unmarshal([]byte(input), &cfg))
155+
return cfg
156+
}(),
157+
},
158+
},
159+
expected: v1beta1.OpenTelemetryCollector{
160+
ObjectMeta: metav1.ObjectMeta{
161+
Labels: map[string]string{},
162+
},
163+
Spec: v1beta1.OpenTelemetryCollectorSpec{
164+
OpenTelemetryCommonFields: v1beta1.OpenTelemetryCommonFields{
165+
Args: map[string]string{"feature-gates": "-component.UseLocalHostAsDefaultHost"},
166+
ManagementState: v1beta1.ManagementStateManaged,
167+
Replicas: &one,
168+
},
169+
Mode: v1beta1.ModeDeployment,
170+
UpgradeStrategy: v1beta1.UpgradeStrategyAutomatic,
171+
Config: func() v1beta1.Config {
172+
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"endpoint":"0.0.0.0:4317"},"http":{"endpoint":"0.0.0.0:4318"}}}},"exporters":{"debug":null},"service":{"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
173+
var cfg v1beta1.Config
174+
require.NoError(t, yaml.Unmarshal([]byte(input), &cfg))
175+
return cfg
176+
}(),
177+
},
178+
},
179+
},
180+
{
181+
name: "update config defaults, leave other fields alone",
182+
otelcol: v1beta1.OpenTelemetryCollector{
183+
Spec: v1beta1.OpenTelemetryCollectorSpec{
184+
Config: func() v1beta1.Config {
185+
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"headers":{"example":"another"}},"http":{"endpoint":"0.0.0.0:4000"}}}},"exporters":{"debug":null},"service":{"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
186+
var cfg v1beta1.Config
187+
require.NoError(t, yaml.Unmarshal([]byte(input), &cfg))
188+
return cfg
189+
}(),
190+
},
191+
},
192+
expected: v1beta1.OpenTelemetryCollector{
193+
ObjectMeta: metav1.ObjectMeta{
194+
Labels: map[string]string{},
195+
},
196+
Spec: v1beta1.OpenTelemetryCollectorSpec{
197+
OpenTelemetryCommonFields: v1beta1.OpenTelemetryCommonFields{
198+
Args: map[string]string{"feature-gates": "-component.UseLocalHostAsDefaultHost"},
199+
ManagementState: v1beta1.ManagementStateManaged,
200+
Replicas: &one,
201+
},
202+
Mode: v1beta1.ModeDeployment,
203+
UpgradeStrategy: v1beta1.UpgradeStrategyAutomatic,
204+
Config: func() v1beta1.Config {
205+
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"endpoint":"0.0.0.0:4317","headers":{"example":"another"}},"http":{"endpoint":"0.0.0.0:4000"}}}},"exporters":{"debug":null},"service":{"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
206+
var cfg v1beta1.Config
207+
require.NoError(t, yaml.Unmarshal([]byte(input), &cfg))
208+
return cfg
209+
}(),
210+
},
211+
},
212+
},
147213
{
148214
name: "all fields default",
149215
otelcol: v1beta1.OpenTelemetryCollector{},

apis/v1beta1/config.go

+50
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"strconv"
2525
"strings"
2626

27+
"dario.cat/mergo"
2728
"github.com/go-logr/logr"
2829
"gopkg.in/yaml.v3"
2930
corev1 "k8s.io/api/core/v1"
@@ -225,6 +226,51 @@ func (c *Config) getPortsForComponentKinds(logger logr.Logger, componentKinds ..
225226
return ports, nil
226227
}
227228

229+
// applyDefaultForComponentKinds applies defaults to the endpoints for the given ComponentKind(s).
230+
func (c *Config) applyDefaultForComponentKinds(logger logr.Logger, componentKinds ...ComponentKind) error {
231+
enabledComponents := c.GetEnabledComponents()
232+
for _, componentKind := range componentKinds {
233+
var retriever components.ParserRetriever
234+
var cfg AnyConfig
235+
switch componentKind {
236+
case KindReceiver:
237+
retriever = receivers.ReceiverFor
238+
cfg = c.Receivers
239+
case KindExporter:
240+
continue
241+
case KindProcessor:
242+
continue
243+
case KindExtension:
244+
continue
245+
}
246+
for componentName := range enabledComponents[componentKind] {
247+
parser := retriever(componentName)
248+
componentConf := cfg.Object[componentName]
249+
newCfg, err := parser.GetDefaultConfig(logger, componentConf)
250+
if err != nil {
251+
return err
252+
}
253+
254+
// We need to ensure we don't remove any fields in defaulting.
255+
mappedCfg, ok := newCfg.(map[string]interface{})
256+
if !ok || mappedCfg == nil {
257+
logger.V(1).Info("returned default configuration invalid",
258+
"warn", "could not apply component defaults",
259+
"component", componentName,
260+
)
261+
continue
262+
}
263+
264+
if err := mergo.Merge(&mappedCfg, componentConf); err != nil {
265+
return err
266+
}
267+
cfg.Object[componentName] = mappedCfg
268+
}
269+
}
270+
271+
return nil
272+
}
273+
228274
func (c *Config) GetReceiverPorts(logger logr.Logger) ([]corev1.ServicePort, error) {
229275
return c.getPortsForComponentKinds(logger, KindReceiver)
230276
}
@@ -241,6 +287,10 @@ func (c *Config) GetAllRbacRules(logger logr.Logger) ([]rbacv1.PolicyRule, error
241287
return c.getRbacRulesForComponentKinds(logger, KindReceiver, KindExporter, KindProcessor)
242288
}
243289

290+
func (c *Config) ApplyDefaults(logger logr.Logger) error {
291+
return c.applyDefaultForComponentKinds(logger, KindReceiver)
292+
}
293+
244294
// GetLivenessProbe gets the first enabled liveness probe. There should only ever be one extension enabled
245295
// that provides the hinting for the liveness probe.
246296
func (c *Config) GetLivenessProbe(logger logr.Logger) (*corev1.Probe, error) {

internal/components/builder.go

+30-16
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@ import (
2626
type ParserOption[ComponentConfigType any] func(*Settings[ComponentConfigType])
2727

2828
type Settings[ComponentConfigType any] struct {
29-
protocol corev1.Protocol
30-
appProtocol *string
31-
targetPort intstr.IntOrString
32-
nodePort int32
33-
name string
34-
port int32
35-
portParser PortParser[ComponentConfigType]
36-
rbacGen RBACRuleGenerator[ComponentConfigType]
37-
livenessGen ProbeGenerator[ComponentConfigType]
38-
readinessGen ProbeGenerator[ComponentConfigType]
29+
protocol corev1.Protocol
30+
appProtocol *string
31+
targetPort intstr.IntOrString
32+
nodePort int32
33+
name string
34+
port int32
35+
defaultRecAddr string
36+
portParser PortParser[ComponentConfigType]
37+
rbacGen RBACRuleGenerator[ComponentConfigType]
38+
livenessGen ProbeGenerator[ComponentConfigType]
39+
readinessGen ProbeGenerator[ComponentConfigType]
40+
defaultsApplier Defaulter[ComponentConfigType]
3941
}
4042

4143
func NewEmptySettings[ComponentConfigType any]() *Settings[ComponentConfigType] {
@@ -75,6 +77,11 @@ func (b Builder[ComponentConfigType]) WithAppProtocol(appProtocol *string) Build
7577
o.appProtocol = appProtocol
7678
})
7779
}
80+
func (b Builder[ComponentConfigType]) WithDefaultRecAddress(defaultRecAddr string) Builder[ComponentConfigType] {
81+
return append(b, func(o *Settings[ComponentConfigType]) {
82+
o.defaultRecAddr = defaultRecAddr
83+
})
84+
}
7885
func (b Builder[ComponentConfigType]) WithTargetPort(targetPort int32) Builder[ComponentConfigType] {
7986
return append(b, func(o *Settings[ComponentConfigType]) {
8087
o.targetPort = intstr.FromInt32(targetPort)
@@ -118,19 +125,26 @@ func (b Builder[ComponentConfigType]) WithReadinessGen(readinessGen ProbeGenerat
118125
})
119126
}
120127

128+
func (b Builder[ComponentConfigType]) WithDefaultsApplier(defaultsApplier Defaulter[ComponentConfigType]) Builder[ComponentConfigType] {
129+
return append(b, func(o *Settings[ComponentConfigType]) {
130+
o.defaultsApplier = defaultsApplier
131+
})
132+
}
133+
121134
func (b Builder[ComponentConfigType]) Build() (*GenericParser[ComponentConfigType], error) {
122135
o := NewEmptySettings[ComponentConfigType]()
123136
o.Apply(b...)
124137
if len(o.name) == 0 {
125138
return nil, fmt.Errorf("invalid settings struct, no name specified")
126139
}
127140
return &GenericParser[ComponentConfigType]{
128-
name: o.name,
129-
portParser: o.portParser,
130-
rbacGen: o.rbacGen,
131-
livenessGen: o.livenessGen,
132-
readinessGen: o.readinessGen,
133-
settings: o,
141+
name: o.name,
142+
portParser: o.portParser,
143+
rbacGen: o.rbacGen,
144+
livenessGen: o.livenessGen,
145+
readinessGen: o.readinessGen,
146+
defaultsApplier: o.defaultsApplier,
147+
settings: o,
134148
}, nil
135149
}
136150

internal/components/component.go

+10
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type PortRetriever interface {
3838
GetPortNumOrDefault(logr.Logger, int32) int32
3939
}
4040

41+
type AddressProvider = func(name string) (address string, port int32)
42+
4143
// PortParser is a function that returns a list of servicePorts given a config of type Config.
4244
type PortParser[ComponentConfigType any] func(logger logr.Logger, name string, defaultPort *corev1.ServicePort, config ComponentConfigType) ([]corev1.ServicePort, error)
4345

@@ -49,6 +51,10 @@ type RBACRuleGenerator[ComponentConfigType any] func(logger logr.Logger, config
4951
// It's expected that type Config is the configuration used by a parser.
5052
type ProbeGenerator[ComponentConfigType any] func(logger logr.Logger, config ComponentConfigType) (*corev1.Probe, error)
5153

54+
// Defaulter is a function that applies given defaults to the passed Config.
55+
// It's expected that type Config is the configuration used by a parser.
56+
type Defaulter[ComponentConfigType any] func(logger logr.Logger, defaultAddr string, defaultPort int32, config ComponentConfigType) (map[string]interface{}, error)
57+
5258
// ComponentType returns the type for a given component name.
5359
// components have a name like:
5460
// - mycomponent/custom
@@ -87,6 +93,10 @@ func PortFromEndpoint(endpoint string) (int32, error) {
8793
type ParserRetriever func(string) Parser
8894

8995
type Parser interface {
96+
// GetDefaultConfig returns a config with set default values.
97+
// NOTE: Config merging must be done by the caller if desired.
98+
GetDefaultConfig(logger logr.Logger, config interface{}) (interface{}, error)
99+
90100
// Ports returns the service ports parsed based on the component's configuration where name is the component's name
91101
// of the form "name" or "type/name"
92102
Ports(logger logr.Logger, name string, config interface{}) ([]corev1.ServicePort, error)

internal/components/generic_parser.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,29 @@ var (
3030
// GenericParser serves as scaffolding for custom parsing logic by isolating
3131
// functionality to idempotent functions.
3232
type GenericParser[T any] struct {
33-
name string
34-
settings *Settings[T]
35-
portParser PortParser[T]
36-
rbacGen RBACRuleGenerator[T]
37-
livenessGen ProbeGenerator[T]
38-
readinessGen ProbeGenerator[T]
33+
name string
34+
settings *Settings[T]
35+
portParser PortParser[T]
36+
rbacGen RBACRuleGenerator[T]
37+
livenessGen ProbeGenerator[T]
38+
readinessGen ProbeGenerator[T]
39+
defaultsApplier Defaulter[T]
40+
}
41+
42+
func (g *GenericParser[T]) GetDefaultConfig(logger logr.Logger, config interface{}) (interface{}, error) {
43+
if g.settings == nil || g.defaultsApplier == nil {
44+
return config, nil
45+
}
46+
47+
if g.settings.defaultRecAddr == "" {
48+
return config, nil
49+
}
50+
51+
var parsed T
52+
if err := mapstructure.Decode(config, &parsed); err != nil {
53+
return nil, err
54+
}
55+
return g.defaultsApplier(logger, g.settings.defaultRecAddr, g.settings.GetServicePort().Port, parsed)
3956
}
4057

4158
func (g *GenericParser[T]) GetLivenessProbe(logger logr.Logger, config interface{}) (*corev1.Probe, error) {

0 commit comments

Comments
 (0)