Skip to content

Commit 6ae647a

Browse files
authored
Create service for extensions (#3403)
* feat: create service for extensions Signed-off-by: Ankit152 <[email protected]> * chore: added extension service in manifest factories Signed-off-by: Ankit152 <[email protected]> * chore: added unit test for extension service function Signed-off-by: Ankit152 <[email protected]> * chore: added e2e tests for extensions Signed-off-by: Ankit152 <[email protected]> --------- Signed-off-by: Ankit152 <[email protected]>
1 parent 54caa0e commit 6ae647a

File tree

10 files changed

+461
-4
lines changed

10 files changed

+461
-4
lines changed

.chloggen/service-extension.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: collector
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: support for creating a service for extensions when ports are specified.
9+
10+
# One or more tracking issues related to the change
11+
issues: [3460]
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/config.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,12 @@ func (c *Config) getPortsForComponentKinds(logger logr.Logger, componentKinds ..
206206
case KindProcessor:
207207
continue
208208
case KindExtension:
209-
continue
209+
retriever = extensions.ParserFor
210+
if c.Extensions == nil {
211+
cfg = AnyConfig{}
212+
} else {
213+
cfg = *c.Extensions
214+
}
210215
}
211216
for componentName := range enabledComponents[componentKind] {
212217
// TODO: Clean up the naming here and make it simpler to use a retriever.
@@ -318,10 +323,18 @@ func (c *Config) GetExporterPorts(logger logr.Logger) ([]corev1.ServicePort, err
318323
return c.getPortsForComponentKinds(logger, KindExporter)
319324
}
320325

321-
func (c *Config) GetAllPorts(logger logr.Logger) ([]corev1.ServicePort, error) {
326+
func (c *Config) GetExtensionPorts(logger logr.Logger) ([]corev1.ServicePort, error) {
327+
return c.getPortsForComponentKinds(logger, KindExtension)
328+
}
329+
330+
func (c *Config) GetReceiverAndExporterPorts(logger logr.Logger) ([]corev1.ServicePort, error) {
322331
return c.getPortsForComponentKinds(logger, KindReceiver, KindExporter)
323332
}
324333

334+
func (c *Config) GetAllPorts(logger logr.Logger) ([]corev1.ServicePort, error) {
335+
return c.getPortsForComponentKinds(logger, KindReceiver, KindExporter, KindExtension)
336+
}
337+
325338
func (c *Config) GetEnvironmentVariables(logger logr.Logger) ([]corev1.EnvVar, error) {
326339
return c.getEnvironmentVariablesForComponentKinds(logger, KindReceiver)
327340
}

internal/components/extensions/helpers.go

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ var (
5555
return components.ParseSingleEndpointSilent(logger, name, defaultPort, &config.SingleEndpointConfig)
5656
}).
5757
MustBuild(),
58+
components.NewSinglePortParserBuilder("jaeger_query", 16686).
59+
WithTargetPort(16686).
60+
MustBuild(),
5861
}
5962
)
6063

internal/manifests/collector/collector.go

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func Build(params manifests.Params) ([]client.Object, error) {
5353
manifests.Factory(Service),
5454
manifests.Factory(HeadlessService),
5555
manifests.Factory(MonitoringService),
56+
manifests.Factory(ExtensionService),
5657
manifests.Factory(Ingress),
5758
}...)
5859

internal/manifests/collector/service.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ const (
4242
BaseServiceType ServiceType = iota
4343
HeadlessServiceType
4444
MonitoringServiceType
45+
ExtensionServiceType
4546
)
4647

4748
func (s ServiceType) String() string {
48-
return [...]string{"base", "headless", "monitoring"}[s]
49+
return [...]string{"base", "headless", "monitoring", "extension"}[s]
4950
}
5051

5152
func HeadlessService(params manifests.Params) (*corev1.Service, error) {
@@ -108,6 +109,39 @@ func MonitoringService(params manifests.Params) (*corev1.Service, error) {
108109
}, nil
109110
}
110111

112+
func ExtensionService(params manifests.Params) (*corev1.Service, error) {
113+
name := naming.ExtensionService(params.OtelCol.Name)
114+
labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{})
115+
labels[serviceTypeLabel] = ExtensionServiceType.String()
116+
117+
annotations, err := manifestutils.Annotations(params.OtelCol, params.Config.AnnotationsFilter())
118+
if err != nil {
119+
return nil, err
120+
}
121+
122+
ports, err := params.OtelCol.Spec.Config.GetExtensionPorts(params.Log)
123+
if err != nil {
124+
return nil, err
125+
}
126+
127+
if len(ports) == 0 {
128+
return nil, nil
129+
}
130+
131+
return &corev1.Service{
132+
ObjectMeta: metav1.ObjectMeta{
133+
Name: name,
134+
Namespace: params.OtelCol.Namespace,
135+
Labels: labels,
136+
Annotations: annotations,
137+
},
138+
Spec: corev1.ServiceSpec{
139+
Ports: ports,
140+
Selector: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector),
141+
},
142+
}, nil
143+
}
144+
111145
func Service(params manifests.Params) (*corev1.Service, error) {
112146
name := naming.Service(params.OtelCol.Name)
113147
labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{})
@@ -118,7 +152,7 @@ func Service(params manifests.Params) (*corev1.Service, error) {
118152
return nil, err
119153
}
120154

121-
ports, err := params.OtelCol.Spec.Config.GetAllPorts(params.Log)
155+
ports, err := params.OtelCol.Spec.Config.GetReceiverAndExporterPorts(params.Log)
122156
if err != nil {
123157
return nil, err
124158
}

internal/manifests/collector/service_test.go

+201
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/open-telemetry/opentelemetry-operator/internal/config"
2727
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
2828
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils"
29+
"github.com/open-telemetry/opentelemetry-operator/internal/naming"
2930
)
3031

3132
func TestExtractPortNumbersAndNames(t *testing.T) {
@@ -321,6 +322,206 @@ func TestMonitoringService(t *testing.T) {
321322
})
322323
}
323324

325+
func TestExtensionService(t *testing.T) {
326+
testCases := []struct {
327+
name string
328+
params manifests.Params
329+
expectedPorts []v1.ServicePort
330+
}{
331+
{
332+
name: "when the extension has http endpoint",
333+
params: manifests.Params{
334+
Config: config.Config{},
335+
Log: logger,
336+
OtelCol: v1beta1.OpenTelemetryCollector{
337+
ObjectMeta: metav1.ObjectMeta{
338+
Name: "test",
339+
},
340+
Spec: v1beta1.OpenTelemetryCollectorSpec{
341+
Config: v1beta1.Config{
342+
Service: v1beta1.Service{
343+
Extensions: []string{"jaeger_query"},
344+
},
345+
Extensions: &v1beta1.AnyConfig{
346+
Object: map[string]interface{}{
347+
"jaeger_query": map[string]interface{}{
348+
"http": map[string]interface{}{
349+
"endpoint": "0.0.0.0:16686",
350+
},
351+
},
352+
},
353+
},
354+
},
355+
},
356+
},
357+
},
358+
expectedPorts: []v1.ServicePort{
359+
{
360+
Name: "jaeger-query",
361+
Port: 16686,
362+
TargetPort: intstr.IntOrString{
363+
IntVal: 16686,
364+
},
365+
},
366+
},
367+
},
368+
{
369+
name: "when the extension has grpc endpoint",
370+
params: manifests.Params{
371+
Config: config.Config{},
372+
Log: logger,
373+
OtelCol: v1beta1.OpenTelemetryCollector{
374+
ObjectMeta: metav1.ObjectMeta{
375+
Name: "test",
376+
},
377+
Spec: v1beta1.OpenTelemetryCollectorSpec{
378+
Config: v1beta1.Config{
379+
Service: v1beta1.Service{
380+
Extensions: []string{"jaeger_query"},
381+
},
382+
Extensions: &v1beta1.AnyConfig{
383+
Object: map[string]interface{}{
384+
"jaeger_query": map[string]interface{}{
385+
"http": map[string]interface{}{
386+
"endpoint": "0.0.0.0:16686",
387+
},
388+
},
389+
},
390+
},
391+
},
392+
},
393+
},
394+
},
395+
expectedPorts: []v1.ServicePort{
396+
{
397+
Name: "jaeger-query",
398+
Port: 16686,
399+
TargetPort: intstr.IntOrString{
400+
IntVal: 16686,
401+
},
402+
},
403+
},
404+
},
405+
{
406+
name: "when the extension has both http and grpc endpoint",
407+
params: manifests.Params{
408+
Config: config.Config{},
409+
Log: logger,
410+
OtelCol: v1beta1.OpenTelemetryCollector{
411+
ObjectMeta: metav1.ObjectMeta{
412+
Name: "test",
413+
},
414+
Spec: v1beta1.OpenTelemetryCollectorSpec{
415+
Config: v1beta1.Config{
416+
Service: v1beta1.Service{
417+
Extensions: []string{"jaeger_query"},
418+
},
419+
Extensions: &v1beta1.AnyConfig{
420+
Object: map[string]interface{}{
421+
"jaeger_query": map[string]interface{}{
422+
"http": map[string]interface{}{
423+
"endpoint": "0.0.0.0:16686",
424+
},
425+
"grpc": map[string]interface{}{
426+
"endpoint": "0.0.0.0:16686",
427+
},
428+
},
429+
},
430+
},
431+
},
432+
},
433+
},
434+
},
435+
expectedPorts: []v1.ServicePort{
436+
{
437+
Name: "jaeger-query",
438+
Port: 16686,
439+
TargetPort: intstr.IntOrString{
440+
IntVal: 16686,
441+
},
442+
},
443+
},
444+
},
445+
{
446+
name: "when the extension has no extensions defined",
447+
params: manifests.Params{
448+
Config: config.Config{},
449+
Log: logger,
450+
OtelCol: v1beta1.OpenTelemetryCollector{
451+
ObjectMeta: metav1.ObjectMeta{
452+
Name: "test",
453+
},
454+
Spec: v1beta1.OpenTelemetryCollectorSpec{
455+
Config: v1beta1.Config{
456+
Service: v1beta1.Service{
457+
Extensions: []string{"jaeger_query"},
458+
},
459+
Extensions: &v1beta1.AnyConfig{
460+
Object: map[string]interface{}{},
461+
},
462+
},
463+
},
464+
},
465+
},
466+
expectedPorts: []v1.ServicePort{},
467+
},
468+
{
469+
name: "when the extension has no endpoint defined",
470+
params: manifests.Params{
471+
Config: config.Config{},
472+
Log: logger,
473+
OtelCol: v1beta1.OpenTelemetryCollector{
474+
ObjectMeta: metav1.ObjectMeta{
475+
Name: "test",
476+
},
477+
Spec: v1beta1.OpenTelemetryCollectorSpec{
478+
Config: v1beta1.Config{
479+
Service: v1beta1.Service{
480+
Extensions: []string{"jaeger_query"},
481+
},
482+
Extensions: &v1beta1.AnyConfig{
483+
Object: map[string]interface{}{
484+
"jaeger_query": map[string]interface{}{},
485+
},
486+
},
487+
},
488+
},
489+
},
490+
},
491+
expectedPorts: []v1.ServicePort{
492+
{
493+
Name: "jaeger-query",
494+
Port: 16686,
495+
TargetPort: intstr.IntOrString{
496+
IntVal: 16686,
497+
},
498+
},
499+
},
500+
},
501+
}
502+
503+
for _, tc := range testCases {
504+
tc := tc
505+
t.Run(tc.name, func(t *testing.T) {
506+
actual, err := ExtensionService(tc.params)
507+
assert.NoError(t, err)
508+
509+
if len(tc.expectedPorts) > 0 {
510+
assert.NotNil(t, actual)
511+
assert.Equal(t, actual.Name, naming.ExtensionService(tc.params.OtelCol.Name))
512+
// ports assertion
513+
assert.Equal(t, len(tc.expectedPorts), len(actual.Spec.Ports))
514+
assert.Equal(t, tc.expectedPorts[0].Name, actual.Spec.Ports[0].Name)
515+
assert.Equal(t, tc.expectedPorts[0].Port, actual.Spec.Ports[0].Port)
516+
assert.Equal(t, tc.expectedPorts[0].TargetPort.IntVal, actual.Spec.Ports[0].TargetPort.IntVal)
517+
} else {
518+
// no ports, no service
519+
assert.Nil(t, actual)
520+
}
521+
})
522+
}
523+
}
524+
324525
func service(name string, ports []v1beta1.PortsSpec) v1.Service {
325526
return serviceWithInternalTrafficPolicy(name, ports, v1.ServiceInternalTrafficPolicyCluster)
326527
}

internal/naming/main.go

+5
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ func MonitoringService(otelcol string) string {
116116
return DNSName(Truncate("%s-monitoring", 63, Service(otelcol)))
117117
}
118118

119+
// ExtensionService builds the name for the extension service based on the instance.
120+
func ExtensionService(otelcol string) string {
121+
return DNSName(Truncate("%s-extension", 63, Service(otelcol)))
122+
}
123+
119124
// Service builds the service name based on the instance.
120125
func Service(otelcol string) string {
121126
return DNSName(Truncate("%s-collector", 63, otelcol))

0 commit comments

Comments
 (0)