From 6dfaae8902701d10e0c1516743d9845643f8736f Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Wed, 7 Aug 2024 14:28:09 -0400 Subject: [PATCH 01/18] begin interface change --- internal/components/component.go | 11 +++++++++-- internal/components/exporters/helpers.go | 8 ++++---- internal/components/multi_endpoint.go | 7 ++++++- internal/components/nop_parser.go | 7 ++++++- internal/components/receivers/helpers.go | 8 ++++---- internal/components/receivers/scraper.go | 7 ++++++- internal/components/single_endpoint.go | 7 ++++++- 7 files changed, 41 insertions(+), 14 deletions(-) diff --git a/internal/components/component.go b/internal/components/component.go index 5bf57a649a..696cd19821 100644 --- a/internal/components/component.go +++ b/internal/components/component.go @@ -22,6 +22,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/util/intstr" ) @@ -37,6 +38,9 @@ type PortRetriever interface { GetPortNumOrDefault(logr.Logger, int32) int32 } +// RBACRuleGenerator is a function that generates a list of RBAC Rules given a configuration of type T +// It's expected that type T is the configuration used by a parser. +type RBACRuleGenerator[T any] func(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) type PortBuilderOption func(*corev1.ServicePort) func WithTargetPort(targetPort int32) PortBuilderOption { @@ -92,13 +96,16 @@ func PortFromEndpoint(endpoint string) (int32, error) { return int32(port), err } -type ParserRetriever func(string) ComponentPortParser +type ParserRetriever func(string) Parser -type ComponentPortParser interface { +type Parser interface { // Ports returns the service ports parsed based on the component's configuration where name is the component's name // of the form "name" or "type/name" Ports(logger logr.Logger, name string, config interface{}) ([]corev1.ServicePort, error) + // GetRBACRules returns the rbac rules for this component + GetRBACRules(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) + // ParserType returns the type of this parser ParserType() string diff --git a/internal/components/exporters/helpers.go b/internal/components/exporters/helpers.go index e8626f73b5..e20e356384 100644 --- a/internal/components/exporters/helpers.go +++ b/internal/components/exporters/helpers.go @@ -19,10 +19,10 @@ import ( ) // registry holds a record of all known receiver parsers. -var registry = make(map[string]components.ComponentPortParser) +var registry = make(map[string]components.Parser) // Register adds a new parser builder to the list of known builders. -func Register(name string, p components.ComponentPortParser) { +func Register(name string, p components.Parser) { registry[name] = p } @@ -33,7 +33,7 @@ func IsRegistered(name string) bool { } // ParserFor returns a parser builder for the given exporter name. -func ParserFor(name string) components.ComponentPortParser { +func ParserFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } @@ -42,7 +42,7 @@ func ParserFor(name string) components.ComponentPortParser { } var ( - componentParsers = []components.ComponentPortParser{ + componentParsers = []components.Parser{ components.NewSinglePortParser("prometheus", 8888), } ) diff --git a/internal/components/multi_endpoint.go b/internal/components/multi_endpoint.go index 0f3294d1b9..a6fc272237 100644 --- a/internal/components/multi_endpoint.go +++ b/internal/components/multi_endpoint.go @@ -20,11 +20,12 @@ import ( "github.com/go-logr/logr" "github.com/mitchellh/mapstructure" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) -var _ ComponentPortParser = &MultiPortReceiver{} +var _ Parser = &MultiPortReceiver{} // MultiProtocolEndpointConfig represents the minimal struct for a given YAML configuration input containing a map to // a struct with either endpoint or listen_address. @@ -71,6 +72,10 @@ func (m *MultiPortReceiver) ParserName() string { return fmt.Sprintf("__%s", m.name) } +func (m *MultiPortReceiver) GetRBACRules(logr.Logger, interface{}) ([]rbacv1.PolicyRule, error) { + return nil, nil +} + func NewMultiPortReceiver(name string, opts ...MultiPortOption) *MultiPortReceiver { multiReceiver := &MultiPortReceiver{ name: name, diff --git a/internal/components/nop_parser.go b/internal/components/nop_parser.go index 4f66c7ec8c..5fa629b983 100644 --- a/internal/components/nop_parser.go +++ b/internal/components/nop_parser.go @@ -19,10 +19,11 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" ) var ( - _ ComponentPortParser = &NopParser{} + _ Parser = &NopParser{} ) // SingleEndpointParser is a special parser for a generic receiver that has an endpoint or listen_address in its @@ -35,6 +36,10 @@ func (n *NopParser) Ports(logger logr.Logger, name string, config interface{}) ( return nil, nil } +func (n *NopParser) GetRBACRules(logr.Logger, interface{}) ([]rbacv1.PolicyRule, error) { + return nil, nil +} + func (n *NopParser) ParserType() string { return ComponentType(n.name) } diff --git a/internal/components/receivers/helpers.go b/internal/components/receivers/helpers.go index 5003e00c24..bdad38a81d 100644 --- a/internal/components/receivers/helpers.go +++ b/internal/components/receivers/helpers.go @@ -21,10 +21,10 @@ import ( ) // registry holds a record of all known receiver parsers. -var registry = make(map[string]components.ComponentPortParser) +var registry = make(map[string]components.Parser) // Register adds a new parser builder to the list of known builders. -func Register(name string, p components.ComponentPortParser) { +func Register(name string, p components.Parser) { registry[name] = p } @@ -35,7 +35,7 @@ func IsRegistered(name string) bool { } // ReceiverFor returns a parser builder for the given exporter name. -func ReceiverFor(name string) components.ComponentPortParser { +func ReceiverFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } @@ -43,7 +43,7 @@ func ReceiverFor(name string) components.ComponentPortParser { } var ( - componentParsers = []components.ComponentPortParser{ + componentParsers = []components.Parser{ components.NewMultiPortReceiver("otlp", components.WithPortMapping( "grpc", diff --git a/internal/components/receivers/scraper.go b/internal/components/receivers/scraper.go index 01f0ad53d9..3282ff559d 100644 --- a/internal/components/receivers/scraper.go +++ b/internal/components/receivers/scraper.go @@ -19,12 +19,13 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" "github.com/open-telemetry/opentelemetry-operator/internal/components" ) var ( - _ components.ComponentPortParser = &ScraperParser{} + _ components.Parser = &ScraperParser{} ) type ScraperParser struct { @@ -43,6 +44,10 @@ func (s *ScraperParser) ParserName() string { return fmt.Sprintf("__%s", s.componentType) } +func (s *ScraperParser) GetRBACRules(logr.Logger, interface{}) ([]rbacv1.PolicyRule, error) { + return nil, nil +} + func NewScraperParser(name string) *ScraperParser { return &ScraperParser{ componentType: components.ComponentType(name), diff --git a/internal/components/single_endpoint.go b/internal/components/single_endpoint.go index cb43850edf..b9f98228e8 100644 --- a/internal/components/single_endpoint.go +++ b/internal/components/single_endpoint.go @@ -20,12 +20,13 @@ import ( "github.com/go-logr/logr" "github.com/mitchellh/mapstructure" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) var ( - _ ComponentPortParser = &SingleEndpointParser{} + _ Parser = &SingleEndpointParser{} ) // SingleEndpointConfig represents the minimal struct for a given YAML configuration input containing either @@ -66,6 +67,10 @@ type SingleEndpointParser struct { failSilently bool } +func (s *SingleEndpointParser) GetRBACRules(logr.Logger, interface{}) ([]rbacv1.PolicyRule, error) { + return nil, nil +} + func (s *SingleEndpointParser) Ports(logger logr.Logger, name string, config interface{}) ([]corev1.ServicePort, error) { singleEndpointConfig := &SingleEndpointConfig{} if err := mapstructure.Decode(config, singleEndpointConfig); err != nil { From b8c8fb16123e72195fdec204259e994c88a739f2 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Wed, 7 Aug 2024 15:36:59 -0400 Subject: [PATCH 02/18] move over processor config --- internal/components/component.go | 2 +- internal/components/generic_config_parser.go | 63 ++++++++ .../components/generic_config_parser_test.go | 112 +++++++++++++ internal/components/nop_parser.go | 3 +- internal/components/processors/helpers.go | 50 ++++++ .../components/processors/helpers_test.go | 58 +++++++ .../components/processors/k8sattribute.go | 81 ++++++++++ .../processors/k8sattribute_test.go | 151 ++++++++++++++++++ .../processors/resourcedetection.go | 52 ++++++ .../processors/resourcedetection_test.go | 121 ++++++++++++++ internal/components/receivers/helpers.go | 2 +- internal/components/single_endpoint.go | 16 +- internal/components/single_endpoint_test.go | 2 +- 13 files changed, 704 insertions(+), 9 deletions(-) create mode 100644 internal/components/generic_config_parser.go create mode 100644 internal/components/generic_config_parser_test.go create mode 100644 internal/components/processors/helpers.go create mode 100644 internal/components/processors/helpers_test.go create mode 100644 internal/components/processors/k8sattribute.go create mode 100644 internal/components/processors/k8sattribute_test.go create mode 100644 internal/components/processors/resourcedetection.go create mode 100644 internal/components/processors/resourcedetection_test.go diff --git a/internal/components/component.go b/internal/components/component.go index 696cd19821..df3a9ef62e 100644 --- a/internal/components/component.go +++ b/internal/components/component.go @@ -40,7 +40,7 @@ type PortRetriever interface { // RBACRuleGenerator is a function that generates a list of RBAC Rules given a configuration of type T // It's expected that type T is the configuration used by a parser. -type RBACRuleGenerator[T any] func(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) +type RBACRuleGenerator[T any] func(logger logr.Logger, config T) ([]rbacv1.PolicyRule, error) type PortBuilderOption func(*corev1.ServicePort) func WithTargetPort(targetPort int32) PortBuilderOption { diff --git a/internal/components/generic_config_parser.go b/internal/components/generic_config_parser.go new file mode 100644 index 0000000000..3ff1e25c86 --- /dev/null +++ b/internal/components/generic_config_parser.go @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package components + +import ( + "fmt" + + "github.com/go-logr/logr" + "github.com/mitchellh/mapstructure" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" +) + +var ( + _ Parser = &GenericParser[SingleEndpointConfig]{} +) + +// GenericParser serves as scaffolding for custom parsing logic by isolating +// functionality to idempotent functions like RBACRuleGenerator +type GenericParser[T any] struct { + name string + config T + rbacGen RBACRuleGenerator[T] +} + +func (g *GenericParser[T]) Ports(logger logr.Logger, name string, config interface{}) ([]corev1.ServicePort, error) { + return nil, nil +} + +func (g *GenericParser[T]) GetRBACRules(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) { + if g.rbacGen == nil { + return nil, nil + } + var parsed T + if err := mapstructure.Decode(config, &parsed); err != nil { + return nil, err + } + return g.rbacGen(logger, parsed) +} + +func (g *GenericParser[T]) ParserType() string { + return ComponentType(g.name) +} + +func (g *GenericParser[T]) ParserName() string { + return fmt.Sprintf("__%s", g.name) +} + +func NewGenericParser[T any](name string, rbacGen RBACRuleGenerator[T]) *GenericParser[T] { + return &GenericParser[T]{name: name, rbacGen: rbacGen} +} diff --git a/internal/components/generic_config_parser_test.go b/internal/components/generic_config_parser_test.go new file mode 100644 index 0000000000..3a3addbe0d --- /dev/null +++ b/internal/components/generic_config_parser_test.go @@ -0,0 +1,112 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package components_test + +import ( + "fmt" + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + rbacv1 "k8s.io/api/rbac/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/components" +) + +func TestGenericParser_GetRBACRules(t *testing.T) { + type args struct { + logger logr.Logger + config interface{} + } + type testCase[T any] struct { + name string + g *components.GenericParser[T] + args args + want []rbacv1.PolicyRule + wantErr assert.ErrorAssertionFunc + } + + rbacGenFunc := func(logger logr.Logger, config components.SingleEndpointConfig) ([]rbacv1.PolicyRule, error) { + if config.Endpoint == "" && config.ListenAddress == "" { + return nil, fmt.Errorf("either endpoint or listen_address must be specified") + } + return []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, nil + } + + tests := []testCase[components.SingleEndpointConfig]{ + { + name: "valid config with endpoint", + g: components.NewGenericParser[components.SingleEndpointConfig]("test", rbacGenFunc), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{ + "endpoint": "http://localhost:8080", + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "valid config with listen_address", + g: components.NewGenericParser[components.SingleEndpointConfig]("test", rbacGenFunc), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{ + "listen_address": "0.0.0.0:9090", + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "invalid config with no endpoint or listen_address", + g: components.NewGenericParser[components.SingleEndpointConfig]("test", rbacGenFunc), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{}, + }, + want: nil, + wantErr: assert.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.g.GetRBACRules(tt.args.logger, tt.args.config) + if !tt.wantErr(t, err, fmt.Sprintf("GetRBACRules(%v, %v)", tt.args.logger, tt.args.config)) { + return + } + assert.Equalf(t, tt.want, got, "GetRBACRules(%v, %v)", tt.args.logger, tt.args.config) + }) + } +} diff --git a/internal/components/nop_parser.go b/internal/components/nop_parser.go index 5fa629b983..2832250172 100644 --- a/internal/components/nop_parser.go +++ b/internal/components/nop_parser.go @@ -26,8 +26,7 @@ var ( _ Parser = &NopParser{} ) -// SingleEndpointParser is a special parser for a generic receiver that has an endpoint or listen_address in its -// configuration. It doesn't self-register and should be created/used directly. +// NopParser is a minimal processor mostly used for testing or coverage. type NopParser struct { name string } diff --git a/internal/components/processors/helpers.go b/internal/components/processors/helpers.go new file mode 100644 index 0000000000..426154c590 --- /dev/null +++ b/internal/components/processors/helpers.go @@ -0,0 +1,50 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import "github.com/open-telemetry/opentelemetry-operator/internal/components" + +// registry holds a record of all known receiver parsers. +var registry = make(map[string]components.Parser) + +// Register adds a new parser builder to the list of known builders. +func Register(name string, p components.Parser) { + registry[name] = p +} + +// IsRegistered checks whether a parser is registered with the given name. +func IsRegistered(name string) bool { + _, ok := registry[components.ComponentType(name)] + return ok +} + +// ProcessorFor returns a parser builder for the given exporter name. +func ProcessorFor(name string) components.Parser { + if parser, ok := registry[components.ComponentType(name)]; ok { + return parser + } + return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort, nil) +} + +var componentParsers = []components.Parser{ + components.NewGenericParser[K8sAttributeConfig]("k8sattributes", GenerateK8SAttrRbacRules), + components.NewGenericParser[ResourceDetectionConfig]("resourcedetection", GenerateResourceDetectionRbacRules), +} + +func init() { + for _, parser := range componentParsers { + Register(parser.ParserType(), parser) + } +} diff --git a/internal/components/processors/helpers_test.go b/internal/components/processors/helpers_test.go new file mode 100644 index 0000000000..09ae674226 --- /dev/null +++ b/internal/components/processors/helpers_test.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/open-telemetry/opentelemetry-operator/internal/components/processors" +) + +var logger = logf.Log.WithName("unit-tests") + +func TestDownstreamParsers(t *testing.T) { + for _, tt := range []struct { + desc string + processorName string + parserName string + }{ + {"k8sattributes", "k8sattributes", "__k8sattributes"}, + {"opencensus", "opencensus", "__opencensus"}, + } { + t.Run(tt.processorName, func(t *testing.T) { + t.Run("builds successfully", func(t *testing.T) { + // test + parser := processors.ProcessorFor(tt.processorName) + + // verify + assert.Equal(t, tt.parserName, parser.ParserName()) + }) + t.Run("bad config errors", func(t *testing.T) { + // prepare + parser := processors.ProcessorFor(tt.processorName) + + // test throwing in pure junk + _, err := parser.Ports(logger, tt.processorName, func() {}) + + // verify + assert.ErrorContains(t, err, "expected a map, got 'func'") + }) + + }) + } +} diff --git a/internal/components/processors/k8sattribute.go b/internal/components/processors/k8sattribute.go new file mode 100644 index 0000000000..f9d5266c60 --- /dev/null +++ b/internal/components/processors/k8sattribute.go @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "fmt" + "strings" + + "github.com/go-logr/logr" + rbacv1 "k8s.io/api/rbac/v1" +) + +type FieldExtractConfig struct { + TagName string `mapstructure:"tag_name"` + Key string `mapstructure:"key"` + KeyRegex string `mapstructure:"key_regex"` + Regex string `mapstructure:"regex"` + From string `mapstructure:"from"` +} + +type Extract struct { + Metadata []string `mapstructure:"metadata"` + Labels []FieldExtractConfig `mapstructure:"labels"` + Annotations []FieldExtractConfig `mapstructure:"annotations"` +} + +// K8sAttributeConfig is a minimal struct needed for parsing a valid k8sattribute processor configuration +// This only contains the fields necessary for parsing, other fields can be added in the future. +type K8sAttributeConfig struct { + Extract Extract `mapstructure:"extract"` +} + +func GenerateK8SAttrRbacRules(_ logr.Logger, config K8sAttributeConfig) ([]rbacv1.PolicyRule, error) { + // These policies need to be added always + var prs = []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods", "namespaces"}, + Verbs: []string{"get", "watch", "list"}, + }, + } + + replicasetPolicy := rbacv1.PolicyRule{ + APIGroups: []string{"apps"}, + Resources: []string{"replicasets"}, + Verbs: []string{"get", "watch", "list"}, + } + + if len(config.Extract.Metadata) == 0 { + prs = append(prs, replicasetPolicy) + } + addedReplicasetPolicy := false + for _, m := range config.Extract.Metadata { + metadataField := fmt.Sprint(m) + if (metadataField == "k8s.deployment.uid" || metadataField == "k8s.deployment.name") && !addedReplicasetPolicy { + prs = append(prs, replicasetPolicy) + addedReplicasetPolicy = true + } else if strings.Contains(metadataField, "k8s.node") { + prs = append(prs, + rbacv1.PolicyRule{ + APIGroups: []string{""}, + Resources: []string{"nodes"}, + Verbs: []string{"get", "watch", "list"}, + }, + ) + } + } + return prs, nil +} diff --git a/internal/components/processors/k8sattribute_test.go b/internal/components/processors/k8sattribute_test.go new file mode 100644 index 0000000000..604656f93a --- /dev/null +++ b/internal/components/processors/k8sattribute_test.go @@ -0,0 +1,151 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + rbacv1 "k8s.io/api/rbac/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/components/processors" +) + +func TestGenerateK8SAttrRbacRules(t *testing.T) { + type args struct { + config interface{} + } + tests := []struct { + name string + args args + want []rbacv1.PolicyRule + wantErr assert.ErrorAssertionFunc + }{ + { + name: "default config with empty metadata", + args: args{ + config: map[string]interface{}{ + "extract": map[string]interface{}{ + "metadata": []string{}, + "labels": []interface{}{}, + "annotations": []interface{}{}, + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods", "namespaces"}, + Verbs: []string{"get", "watch", "list"}, + }, + { + APIGroups: []string{"apps"}, + Resources: []string{"replicasets"}, + Verbs: []string{"get", "watch", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "config with deployment metadata", + args: args{ + config: map[string]interface{}{ + "extract": map[string]interface{}{ + "metadata": []string{"k8s.deployment.uid", "k8s.deployment.name"}, + "labels": []interface{}{}, + "annotations": []interface{}{}, + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods", "namespaces"}, + Verbs: []string{"get", "watch", "list"}, + }, + { + APIGroups: []string{"apps"}, + Resources: []string{"replicasets"}, + Verbs: []string{"get", "watch", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "config with node metadata", + args: args{ + config: map[string]interface{}{ + "extract": map[string]interface{}{ + "metadata": []string{"k8s.node.name"}, + "labels": []interface{}{}, + "annotations": []interface{}{}, + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods", "namespaces"}, + Verbs: []string{"get", "watch", "list"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"nodes"}, + Verbs: []string{"get", "watch", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "invalid config", + args: args{ + config: "hi", + }, + want: nil, + wantErr: assert.Error, + }, + { + name: "config with invalid metadata", + args: args{ + config: map[string]interface{}{ + "extract": map[string]interface{}{ + "metadata": []string{"invalid.metadata"}, + "labels": []interface{}{}, + "annotations": []interface{}{}, + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods", "namespaces"}, + Verbs: []string{"get", "watch", "list"}, + }, + }, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parser := processors.ProcessorFor("k8sattributes") + got, err := parser.GetRBACRules(logger, tt.args.config) + if !tt.wantErr(t, err, fmt.Sprintf("GetRBACRules(%v)", tt.args.config)) { + return + } + assert.Equalf(t, tt.want, got, "GetRBACRules(%v)", tt.args.config) + }) + } +} diff --git a/internal/components/processors/resourcedetection.go b/internal/components/processors/resourcedetection.go new file mode 100644 index 0000000000..5d5af17c11 --- /dev/null +++ b/internal/components/processors/resourcedetection.go @@ -0,0 +1,52 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "fmt" + + "github.com/go-logr/logr" + rbacv1 "k8s.io/api/rbac/v1" +) + +// ResourceDetectionConfig is a minimal struct needed for parsing a valid resourcedetection processor configuration +// This only contains the fields necessary for parsing, other fields can be added in the future. +type ResourceDetectionConfig struct { + Detectors []string `mapstructure:"detectors"` +} + +func GenerateResourceDetectionRbacRules(_ logr.Logger, config ResourceDetectionConfig) ([]rbacv1.PolicyRule, error) { + var prs []rbacv1.PolicyRule + for _, d := range config.Detectors { + detectorName := fmt.Sprint(d) + switch detectorName { + case "k8snode": + policy := rbacv1.PolicyRule{ + APIGroups: []string{""}, + Resources: []string{"nodes"}, + Verbs: []string{"get", "list"}, + } + prs = append(prs, policy) + case "openshift": + policy := rbacv1.PolicyRule{ + APIGroups: []string{"config.openshift.io"}, + Resources: []string{"infrastructures", "infrastructures/status"}, + Verbs: []string{"get", "watch", "list"}, + } + prs = append(prs, policy) + } + } + return prs, nil +} diff --git a/internal/components/processors/resourcedetection_test.go b/internal/components/processors/resourcedetection_test.go new file mode 100644 index 0000000000..4d6b448d40 --- /dev/null +++ b/internal/components/processors/resourcedetection_test.go @@ -0,0 +1,121 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + rbacv1 "k8s.io/api/rbac/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/components/processors" +) + +func TestGenerateResourceDetectionRbacRules(t *testing.T) { + type args struct { + config map[string]interface{} + } + tests := []struct { + name string + args args + want []rbacv1.PolicyRule + wantErr assert.ErrorAssertionFunc + }{ + { + name: "default config with no detectors", + args: args{ + config: map[string]interface{}{ + "detectors": []string{}, + }, + }, + want: nil, + wantErr: assert.NoError, + }, + { + name: "config with k8snode detector", + args: args{ + config: map[string]interface{}{ + "detectors": []string{"k8snode"}, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"nodes"}, + Verbs: []string{"get", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "config with openshift detector", + args: args{ + config: map[string]interface{}{ + "detectors": []string{"openshift"}, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{"config.openshift.io"}, + Resources: []string{"infrastructures", "infrastructures/status"}, + Verbs: []string{"get", "watch", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "config with multiple detectors", + args: args{ + config: map[string]interface{}{ + "detectors": []string{"k8snode", "openshift"}, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"nodes"}, + Verbs: []string{"get", "list"}, + }, + { + APIGroups: []string{"config.openshift.io"}, + Resources: []string{"infrastructures", "infrastructures/status"}, + Verbs: []string{"get", "watch", "list"}, + }, + }, + wantErr: assert.NoError, + }, + { + name: "config with invalid detector", + args: args{ + config: map[string]interface{}{ + "detectors": []string{"invalid"}, + }, + }, + want: nil, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parser := processors.ProcessorFor("resourcedetection") + got, err := parser.GetRBACRules(logger, tt.args.config) + if !tt.wantErr(t, err, fmt.Sprintf("GetRBACRules(%v)", tt.args.config)) { + return + } + assert.Equalf(t, tt.want, got, "GetRBACRules(%v)", tt.args.config) + }) + } +} diff --git a/internal/components/receivers/helpers.go b/internal/components/receivers/helpers.go index bdad38a81d..3f94015ce8 100644 --- a/internal/components/receivers/helpers.go +++ b/internal/components/receivers/helpers.go @@ -39,7 +39,7 @@ func ReceiverFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } - return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort) + return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort, nil) } var ( diff --git a/internal/components/single_endpoint.go b/internal/components/single_endpoint.go index b9f98228e8..288dac1ce0 100644 --- a/internal/components/single_endpoint.go +++ b/internal/components/single_endpoint.go @@ -65,10 +65,18 @@ type SingleEndpointParser struct { // failSilently allows the parser to prevent the propagation of failure if the parser fails to set a port. failSilently bool + rbacGen RBACRuleGenerator[SingleEndpointConfig] } -func (s *SingleEndpointParser) GetRBACRules(logr.Logger, interface{}) ([]rbacv1.PolicyRule, error) { - return nil, nil +func (s *SingleEndpointParser) GetRBACRules(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) { + if s.rbacGen == nil { + return nil, nil + } + var singleEndpointConfig SingleEndpointConfig + if err := mapstructure.Decode(config, &singleEndpointConfig); err != nil { + return nil, err + } + return s.rbacGen(logger, singleEndpointConfig) } func (s *SingleEndpointParser) Ports(logger logr.Logger, name string, config interface{}) ([]corev1.ServicePort, error) { @@ -111,7 +119,7 @@ func NewSinglePortParser(name string, port int32, opts ...PortBuilderOption) *Si } // NewSilentSinglePortParser returns a SingleEndpointParser that errors silently on failure to find a port. -func NewSilentSinglePortParser(name string, port int32, opts ...PortBuilderOption) *SingleEndpointParser { +func NewSilentSinglePortParser(name string, port int32, rbacGen RBACRuleGenerator[SingleEndpointConfig], opts ...PortBuilderOption) *SingleEndpointParser { servicePort := &corev1.ServicePort{ Name: naming.PortName(name, port), Port: port, @@ -119,5 +127,5 @@ func NewSilentSinglePortParser(name string, port int32, opts ...PortBuilderOptio for _, opt := range opts { opt(servicePort) } - return &SingleEndpointParser{name: name, svcPort: servicePort, failSilently: true} + return &SingleEndpointParser{name: name, svcPort: servicePort, rbacGen: rbacGen, failSilently: true} } diff --git a/internal/components/single_endpoint_test.go b/internal/components/single_endpoint_test.go index 9e544d40b9..cf497e02c8 100644 --- a/internal/components/single_endpoint_test.go +++ b/internal/components/single_endpoint_test.go @@ -393,7 +393,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := components.NewSilentSinglePortParser(tt.fields.name, tt.fields.port, tt.fields.opts...) + s := components.NewSilentSinglePortParser(tt.fields.name, tt.fields.port, nil, tt.fields.opts...) got, err := s.Ports(logr.Discard(), tt.fields.name, tt.args.config) if !tt.wantErr(t, err, fmt.Sprintf("Ports(%v)", tt.args.config)) { return From 43c8a4838ef659172579a1c8ce47a666e82b576d Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Wed, 7 Aug 2024 15:44:35 -0400 Subject: [PATCH 03/18] use it --- apis/v1beta1/config.go | 36 +++++++++++++++++++++++++++- internal/manifests/collector/rbac.go | 26 ++++---------------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/apis/v1beta1/config.go b/apis/v1beta1/config.go index 61fecb3ad1..438987a663 100644 --- a/apis/v1beta1/config.go +++ b/apis/v1beta1/config.go @@ -27,6 +27,7 @@ import ( "github.com/go-logr/logr" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" "github.com/open-telemetry/opentelemetry-operator/internal/components" "github.com/open-telemetry/opentelemetry-operator/internal/components/exporters" @@ -139,9 +140,38 @@ type Config struct { Service Service `json:"service" yaml:"service"` } +// getRbacRulesForComponentKinds gets the RBAC Rules for the given ComponentKind(s). +func (c *Config) getRbacRulesForComponentKinds(logger logr.Logger, componentKinds ...ComponentKind) ([]rbacv1.PolicyRule, error) { + var rules []rbacv1.PolicyRule + enabledComponents := c.GetEnabledComponents() + for _, componentKind := range componentKinds { + var retriever components.ParserRetriever + var cfg AnyConfig + switch componentKind { + case KindReceiver: + retriever = receivers.ReceiverFor + cfg = c.Receivers + case KindExporter: + retriever = exporters.ParserFor + cfg = c.Exporters + case KindProcessor: + break + } + for componentName := range enabledComponents[componentKind] { + // TODO: Clean up the naming here and make it simpler to use a retriever. + parser := retriever(componentName) + if parsedRules, err := parser.GetRBACRules(logger, cfg.Object[componentName]); err != nil { + return nil, err + } else { + rules = append(rules, parsedRules...) + } + } + } + return rules, nil +} + // getPortsForComponentKinds gets the ports for the given ComponentKind(s). func (c *Config) getPortsForComponentKinds(logger logr.Logger, componentKinds ...ComponentKind) ([]corev1.ServicePort, error) { - var ports []corev1.ServicePort enabledComponents := c.GetEnabledComponents() for _, componentKind := range componentKinds { @@ -187,6 +217,10 @@ func (c *Config) GetAllPorts(logger logr.Logger) ([]corev1.ServicePort, error) { return c.getPortsForComponentKinds(logger, KindReceiver, KindExporter) } +func (c *Config) GetAllRbacRules(logger logr.Logger) ([]rbacv1.PolicyRule, error) { + return c.getRbacRulesForComponentKinds(logger, KindReceiver, KindExporter, KindProcessor) +} + // Yaml encodes the current object and returns it as a string. func (c *Config) Yaml() (string, error) { var buf bytes.Buffer diff --git a/internal/manifests/collector/rbac.go b/internal/manifests/collector/rbac.go index 70bac31c75..610d948b67 100644 --- a/internal/manifests/collector/rbac.go +++ b/internal/manifests/collector/rbac.go @@ -19,25 +19,15 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/internal/manifests" - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) func ClusterRole(params manifests.Params) (*rbacv1.ClusterRole, error) { - confStr, err := params.OtelCol.Spec.Config.Yaml() + rules, err := params.OtelCol.Spec.Config.GetAllRbacRules(params.Log) if err != nil { return nil, err - } - - configFromString, err := adapters.ConfigFromString(confStr) - if err != nil { - params.Log.Error(err, "couldn't extract the configuration from the context") - return nil, nil - } - rules := adapters.ConfigToRBAC(params.Log, configFromString) - - if len(rules) == 0 { + } else if len(rules) == 0 { return nil, nil } @@ -60,18 +50,10 @@ func ClusterRole(params manifests.Params) (*rbacv1.ClusterRole, error) { } func ClusterRoleBinding(params manifests.Params) (*rbacv1.ClusterRoleBinding, error) { - confStr, err := params.OtelCol.Spec.Config.Yaml() + rules, err := params.OtelCol.Spec.Config.GetAllRbacRules(params.Log) if err != nil { return nil, err - } - configFromString, err := adapters.ConfigFromString(confStr) - if err != nil { - params.Log.Error(err, "couldn't extract the configuration from the context") - return nil, nil - } - rules := adapters.ConfigToRBAC(params.Log, configFromString) - - if len(rules) == 0 { + } else if len(rules) == 0 { return nil, nil } From 91cf3829bf519cf23b2b958c4fa67d4ff9f53701 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Wed, 7 Aug 2024 16:14:27 -0400 Subject: [PATCH 04/18] fix tests --- apis/v1beta1/config.go | 8 +++++++- internal/components/generic_config_parser.go | 3 +-- internal/components/processors/helpers.go | 2 +- internal/components/processors/helpers_test.go | 4 ++-- internal/components/receivers/helpers.go | 2 +- internal/components/single_endpoint.go | 14 +++----------- internal/components/single_endpoint_test.go | 2 +- pkg/instrumentation/sdk_test.go | 10 +++++++++- 8 files changed, 25 insertions(+), 20 deletions(-) diff --git a/apis/v1beta1/config.go b/apis/v1beta1/config.go index 438987a663..fce6a610f4 100644 --- a/apis/v1beta1/config.go +++ b/apis/v1beta1/config.go @@ -31,6 +31,7 @@ import ( "github.com/open-telemetry/opentelemetry-operator/internal/components" "github.com/open-telemetry/opentelemetry-operator/internal/components/exporters" + "github.com/open-telemetry/opentelemetry-operator/internal/components/processors" "github.com/open-telemetry/opentelemetry-operator/internal/components/receivers" ) @@ -155,7 +156,12 @@ func (c *Config) getRbacRulesForComponentKinds(logger logr.Logger, componentKind retriever = exporters.ParserFor cfg = c.Exporters case KindProcessor: - break + retriever = processors.ProcessorFor + if c.Processors == nil { + cfg = AnyConfig{} + } else { + cfg = *c.Processors + } } for componentName := range enabledComponents[componentKind] { // TODO: Clean up the naming here and make it simpler to use a retriever. diff --git a/internal/components/generic_config_parser.go b/internal/components/generic_config_parser.go index 3ff1e25c86..1c1e1deb24 100644 --- a/internal/components/generic_config_parser.go +++ b/internal/components/generic_config_parser.go @@ -28,10 +28,9 @@ var ( ) // GenericParser serves as scaffolding for custom parsing logic by isolating -// functionality to idempotent functions like RBACRuleGenerator +// functionality to idempotent functions like RBACRuleGenerator. type GenericParser[T any] struct { name string - config T rbacGen RBACRuleGenerator[T] } diff --git a/internal/components/processors/helpers.go b/internal/components/processors/helpers.go index 426154c590..83f05eecce 100644 --- a/internal/components/processors/helpers.go +++ b/internal/components/processors/helpers.go @@ -35,7 +35,7 @@ func ProcessorFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } - return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort, nil) + return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort) } var componentParsers = []components.Parser{ diff --git a/internal/components/processors/helpers_test.go b/internal/components/processors/helpers_test.go index 09ae674226..60076faf71 100644 --- a/internal/components/processors/helpers_test.go +++ b/internal/components/processors/helpers_test.go @@ -32,7 +32,7 @@ func TestDownstreamParsers(t *testing.T) { parserName string }{ {"k8sattributes", "k8sattributes", "__k8sattributes"}, - {"opencensus", "opencensus", "__opencensus"}, + {"resourcedetection", "resourcedetection", "__resourcedetection"}, } { t.Run(tt.processorName, func(t *testing.T) { t.Run("builds successfully", func(t *testing.T) { @@ -50,7 +50,7 @@ func TestDownstreamParsers(t *testing.T) { _, err := parser.Ports(logger, tt.processorName, func() {}) // verify - assert.ErrorContains(t, err, "expected a map, got 'func'") + assert.Nil(t, err) }) }) diff --git a/internal/components/receivers/helpers.go b/internal/components/receivers/helpers.go index 3f94015ce8..bdad38a81d 100644 --- a/internal/components/receivers/helpers.go +++ b/internal/components/receivers/helpers.go @@ -39,7 +39,7 @@ func ReceiverFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } - return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort, nil) + return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort) } var ( diff --git a/internal/components/single_endpoint.go b/internal/components/single_endpoint.go index 288dac1ce0..508a2dd681 100644 --- a/internal/components/single_endpoint.go +++ b/internal/components/single_endpoint.go @@ -65,18 +65,10 @@ type SingleEndpointParser struct { // failSilently allows the parser to prevent the propagation of failure if the parser fails to set a port. failSilently bool - rbacGen RBACRuleGenerator[SingleEndpointConfig] } func (s *SingleEndpointParser) GetRBACRules(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) { - if s.rbacGen == nil { - return nil, nil - } - var singleEndpointConfig SingleEndpointConfig - if err := mapstructure.Decode(config, &singleEndpointConfig); err != nil { - return nil, err - } - return s.rbacGen(logger, singleEndpointConfig) + return nil, nil } func (s *SingleEndpointParser) Ports(logger logr.Logger, name string, config interface{}) ([]corev1.ServicePort, error) { @@ -119,7 +111,7 @@ func NewSinglePortParser(name string, port int32, opts ...PortBuilderOption) *Si } // NewSilentSinglePortParser returns a SingleEndpointParser that errors silently on failure to find a port. -func NewSilentSinglePortParser(name string, port int32, rbacGen RBACRuleGenerator[SingleEndpointConfig], opts ...PortBuilderOption) *SingleEndpointParser { +func NewSilentSinglePortParser(name string, port int32, opts ...PortBuilderOption) *SingleEndpointParser { servicePort := &corev1.ServicePort{ Name: naming.PortName(name, port), Port: port, @@ -127,5 +119,5 @@ func NewSilentSinglePortParser(name string, port int32, rbacGen RBACRuleGenerato for _, opt := range opts { opt(servicePort) } - return &SingleEndpointParser{name: name, svcPort: servicePort, rbacGen: rbacGen, failSilently: true} + return &SingleEndpointParser{name: name, svcPort: servicePort, failSilently: true} } diff --git a/internal/components/single_endpoint_test.go b/internal/components/single_endpoint_test.go index cf497e02c8..9e544d40b9 100644 --- a/internal/components/single_endpoint_test.go +++ b/internal/components/single_endpoint_test.go @@ -393,7 +393,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := components.NewSilentSinglePortParser(tt.fields.name, tt.fields.port, nil, tt.fields.opts...) + s := components.NewSilentSinglePortParser(tt.fields.name, tt.fields.port, tt.fields.opts...) got, err := s.Ports(logr.Discard(), tt.fields.name, tt.args.config) if !tt.wantErr(t, err, fmt.Sprintf("Ports(%v)", tt.args.config)) { return diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index 4a42184513..96346963dc 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -596,6 +596,14 @@ func TestSDKInjection(t *testing.T) { Name: "OTEL_TRACES_SAMPLER", Value: "always_on", }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, { Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", ValueFrom: &corev1.EnvVarSource{ @@ -606,7 +614,7 @@ func TestSDKInjection(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "foo=bar,k8s.container.name=other,service.version=explicitly_set,foo=test,fromcr=val,fromtest=val,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=app", + Value: "foo=bar,k8s.container.name=other,service.version=explicitly_set,foo=test,fromcr=val,fromtest=val,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", }, }, }, From a3e0806eae05730c7c9a2fdb3e6eeb2fb79aa51a Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Mon, 19 Aug 2024 16:43:38 -0400 Subject: [PATCH 05/18] Generics --- internal/components/component.go | 40 +++++++---- internal/components/exporters/helpers.go | 2 +- internal/components/generic_parser.go | 10 +-- internal/components/generic_parser_test.go | 14 ++-- internal/components/multi_endpoint.go | 4 +- internal/components/multi_endpoint_test.go | 18 ++--- internal/components/processors/helpers.go | 4 +- internal/components/receivers/helpers.go | 74 ++++++++++----------- internal/components/single_endpoint.go | 28 ++++---- internal/components/single_endpoint_test.go | 32 ++++----- 10 files changed, 120 insertions(+), 106 deletions(-) diff --git a/internal/components/component.go b/internal/components/component.go index cc6459332a..87314a8e7c 100644 --- a/internal/components/component.go +++ b/internal/components/component.go @@ -41,36 +41,38 @@ type PortRetriever interface { } // PortParser is a function that returns a list of servicePorts given a config of type T. -type PortParser[T any] func(logger logr.Logger, name string, o *Option, config T) ([]corev1.ServicePort, error) +type PortParser[T any] func(logger logr.Logger, name string, defaultPort *corev1.ServicePort, config T) ([]corev1.ServicePort, error) // RBACRuleGenerator is a function that generates a list of RBAC Rules given a configuration of type T // It's expected that type T is the configuration used by a parser. type RBACRuleGenerator[T any] func(logger logr.Logger, config T) ([]rbacv1.PolicyRule, error) -type PortBuilderOption func(*Option) +type PortBuilderOption[T any] func(*Option[T]) -type Option struct { +type Option[T any] struct { protocol corev1.Protocol appProtocol *string targetPort intstr.IntOrString nodePort int32 name string port int32 + portParser PortParser[T] + rbacGen RBACRuleGenerator[T] } -func NewOption(name string, port int32) *Option { - return &Option{ +func NewOption[T any](name string, port int32) *Option[T] { + return &Option[T]{ name: name, port: port, } } -func (o *Option) Apply(opts ...PortBuilderOption) { +func (o *Option[T]) Apply(opts ...PortBuilderOption[T]) { for _, opt := range opts { opt(o) } } -func (o *Option) GetServicePort() *corev1.ServicePort { +func (o *Option[T]) GetServicePort() *corev1.ServicePort { return &corev1.ServicePort{ Name: naming.PortName(o.name, o.port), Port: o.port, @@ -81,20 +83,32 @@ func (o *Option) GetServicePort() *corev1.ServicePort { } } -func WithTargetPort(targetPort int32) PortBuilderOption { - return func(opt *Option) { +func WithRBACRuleGenerator[T any](r RBACRuleGenerator[T]) PortBuilderOption[T] { + return func(opt *Option[T]) { + opt.rbacGen = r + } +} + +func WithPortParser[T any](p PortParser[T]) PortBuilderOption[T] { + return func(opt *Option[T]) { + opt.portParser = p + } +} + +func WithTargetPort[T any](targetPort int32) PortBuilderOption[T] { + return func(opt *Option[T]) { opt.targetPort = intstr.FromInt32(targetPort) } } -func WithAppProtocol(proto *string) PortBuilderOption { - return func(opt *Option) { +func WithAppProtocol[T any](proto *string) PortBuilderOption[T] { + return func(opt *Option[T]) { opt.appProtocol = proto } } -func WithProtocol(proto corev1.Protocol) PortBuilderOption { - return func(opt *Option) { +func WithProtocol[T any](proto corev1.Protocol) PortBuilderOption[T] { + return func(opt *Option[T]) { opt.protocol = proto } } diff --git a/internal/components/exporters/helpers.go b/internal/components/exporters/helpers.go index 13b0c0e7c6..be41e039a9 100644 --- a/internal/components/exporters/helpers.go +++ b/internal/components/exporters/helpers.go @@ -38,7 +38,7 @@ func ParserFor(name string) components.Parser { return parser } // We want the default for exporters to fail silently. - return components.NewGenericParser[any](components.ComponentType(name), components.UnsetPort, nil, nil) + return components.NewGenericParser[any](components.ComponentType(name), components.UnsetPort) } var ( diff --git a/internal/components/generic_parser.go b/internal/components/generic_parser.go index 8d4d53c137..22c2c04e68 100644 --- a/internal/components/generic_parser.go +++ b/internal/components/generic_parser.go @@ -31,7 +31,7 @@ var ( // functionality to idempotent functions. type GenericParser[T any] struct { name string - option *Option + option *Option[T] portParser PortParser[T] rbacGen RBACRuleGenerator[T] } @@ -55,7 +55,7 @@ func (g *GenericParser[T]) Ports(logger logr.Logger, name string, config interfa if err := mapstructure.Decode(config, &parsed); err != nil { return nil, err } - return g.portParser(logger, name, g.option, parsed) + return g.portParser(logger, name, g.option.GetServicePort(), parsed) } func (g *GenericParser[T]) ParserType() string { @@ -66,8 +66,8 @@ func (g *GenericParser[T]) ParserName() string { return fmt.Sprintf("__%s", g.name) } -func NewGenericParser[T any](name string, port int32, parser PortParser[T], rbacGen RBACRuleGenerator[T], opts ...PortBuilderOption) *GenericParser[T] { - o := NewOption(name, port) +func NewGenericParser[T any](name string, port int32, opts ...PortBuilderOption[T]) *GenericParser[T] { + o := NewOption[T](name, port) o.Apply(opts...) - return &GenericParser[T]{name: name, portParser: parser, rbacGen: rbacGen, option: o} + return &GenericParser[T]{name: name, portParser: o.portParser, rbacGen: o.rbacGen, option: o} } diff --git a/internal/components/generic_parser_test.go b/internal/components/generic_parser_test.go index c40abca2f1..9e70b77136 100644 --- a/internal/components/generic_parser_test.go +++ b/internal/components/generic_parser_test.go @@ -42,7 +42,7 @@ func TestGenericParser_GetPorts(t *testing.T) { tests := []testCase[*components.SingleEndpointConfig]{ { name: "valid config with endpoint", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.ParseSingleEndpoint, nil), + g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint)), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -59,7 +59,7 @@ func TestGenericParser_GetPorts(t *testing.T) { }, { name: "valid config with listen_address", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.ParseSingleEndpoint, nil), + g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint)), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -76,7 +76,7 @@ func TestGenericParser_GetPorts(t *testing.T) { }, { name: "valid config with listen_address with option", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.ParseSingleEndpoint, nil, components.WithProtocol(corev1.ProtocolUDP)), + g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint), components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolUDP)), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -94,7 +94,7 @@ func TestGenericParser_GetPorts(t *testing.T) { }, { name: "invalid config with no endpoint or listen_address", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.ParseSingleEndpoint, nil), + g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint)), args: args{ logger: logr.Discard(), config: map[string]interface{}{}, @@ -144,7 +144,7 @@ func TestGenericParser_GetRBACRules(t *testing.T) { tests := []testCase[components.SingleEndpointConfig]{ { name: "valid config with endpoint", - g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, nil, rbacGenFunc), + g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, components.WithRBACRuleGenerator(rbacGenFunc)), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -162,7 +162,7 @@ func TestGenericParser_GetRBACRules(t *testing.T) { }, { name: "valid config with listen_address", - g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, nil, rbacGenFunc), + g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, components.WithRBACRuleGenerator(rbacGenFunc)), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -180,7 +180,7 @@ func TestGenericParser_GetRBACRules(t *testing.T) { }, { name: "invalid config with no endpoint or listen_address", - g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, nil, rbacGenFunc), + g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, components.WithRBACRuleGenerator(rbacGenFunc)), args: args{ logger: logr.Discard(), config: map[string]interface{}{}, diff --git a/internal/components/multi_endpoint.go b/internal/components/multi_endpoint.go index 7e41a1fa40..489b6c5dc3 100644 --- a/internal/components/multi_endpoint.go +++ b/internal/components/multi_endpoint.go @@ -87,9 +87,9 @@ func NewMultiPortReceiver(name string, opts ...MultiPortOption) *MultiPortReceiv return multiReceiver } -func WithPortMapping(name string, port int32, opts ...PortBuilderOption) MultiPortOption { +func WithPortMapping(name string, port int32, opts ...PortBuilderOption[*MultiProtocolEndpointConfig]) MultiPortOption { return func(parser *MultiPortReceiver) { - o := NewOption(name, port) + o := NewOption[*MultiProtocolEndpointConfig](name, port) o.Apply(opts...) parser.portMappings[name] = o.GetServicePort() } diff --git a/internal/components/multi_endpoint_test.go b/internal/components/multi_endpoint_test.go index 2aac06a5d1..d4ea5ea926 100644 --- a/internal/components/multi_endpoint_test.go +++ b/internal/components/multi_endpoint_test.go @@ -165,7 +165,7 @@ func TestMultiPortReceiver_Ports(t *testing.T) { fields: fields{ name: "receiver3", opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80, components.WithTargetPort(8080)), + components.WithPortMapping("http", 80, components.WithTargetPort[*components.MultiProtocolEndpointConfig](8080)), }, }, args: args{ @@ -185,7 +185,7 @@ func TestMultiPortReceiver_Ports(t *testing.T) { fields: fields{ name: "receiver4", opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80, components.WithAppProtocol(&components.HttpProtocol)), + components.WithPortMapping("http", 80, components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol)), }, }, args: args{ @@ -205,7 +205,7 @@ func TestMultiPortReceiver_Ports(t *testing.T) { fields: fields{ name: "receiver5", opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80, components.WithProtocol(corev1.ProtocolTCP)), + components.WithPortMapping("http", 80, components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP)), }, }, args: args{ @@ -227,9 +227,9 @@ func TestMultiPortReceiver_Ports(t *testing.T) { opts: []components.MultiPortOption{ components.WithPortMapping("http", 80), components.WithPortMapping("grpc", 4317, - components.WithTargetPort(4317), - components.WithProtocol(corev1.ProtocolTCP), - components.WithAppProtocol(&components.GrpcProtocol)), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](4317), + components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol)), }, }, args: args{ @@ -257,9 +257,9 @@ func TestMultiPortReceiver_Ports(t *testing.T) { opts: []components.MultiPortOption{ components.WithPortMapping("http", 80), components.WithPortMapping("grpc", 4317, - components.WithTargetPort(4317), - components.WithProtocol(corev1.ProtocolTCP), - components.WithAppProtocol(&components.GrpcProtocol)), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](4317), + components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol)), }, }, args: args{ diff --git a/internal/components/processors/helpers.go b/internal/components/processors/helpers.go index ca5e5bb9fe..cf0bd58352 100644 --- a/internal/components/processors/helpers.go +++ b/internal/components/processors/helpers.go @@ -39,8 +39,8 @@ func ProcessorFor(name string) components.Parser { } var componentParsers = []components.Parser{ - components.NewGenericParser[K8sAttributeConfig]("k8sattributes", components.UnsetPort, nil, GenerateK8SAttrRbacRules), - components.NewGenericParser[ResourceDetectionConfig]("resourcedetection", components.UnsetPort, nil, GenerateResourceDetectionRbacRules), + components.NewGenericParser[K8sAttributeConfig]("k8sattributes", components.UnsetPort, components.WithRBACRuleGenerator(GenerateK8SAttrRbacRules)), + components.NewGenericParser[ResourceDetectionConfig]("resourcedetection", components.UnsetPort, components.WithRBACRuleGenerator(GenerateResourceDetectionRbacRules)), } func init() { diff --git a/internal/components/receivers/helpers.go b/internal/components/receivers/helpers.go index 273f0dad31..d8c45c388a 100644 --- a/internal/components/receivers/helpers.go +++ b/internal/components/receivers/helpers.go @@ -44,7 +44,7 @@ func ReceiverFor(name string) components.Parser { // NewScraperParser is an instance of a generic parser that returns nothing when called and never fails. func NewScraperParser(name string) *components.GenericParser[any] { - return components.NewGenericParser[any](name, components.UnsetPort, nil, nil) + return components.NewGenericParser[any](name, components.UnsetPort) } var ( @@ -53,68 +53,68 @@ var ( components.WithPortMapping( "grpc", 4317, - components.WithAppProtocol(&components.GrpcProtocol), - components.WithTargetPort(4317), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](4317), ), components.WithPortMapping( "http", 4318, - components.WithAppProtocol(&components.HttpProtocol), - components.WithTargetPort(4318), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](4318), ), ), components.NewMultiPortReceiver("skywalking", components.WithPortMapping(components.GrpcProtocol, 11800, - components.WithTargetPort(11800), - components.WithAppProtocol(&components.GrpcProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](11800), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), ), components.WithPortMapping(components.HttpProtocol, 12800, - components.WithTargetPort(12800), - components.WithAppProtocol(&components.HttpProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](12800), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), )), components.NewMultiPortReceiver("jaeger", components.WithPortMapping(components.GrpcProtocol, 14250, - components.WithTargetPort(14250), - components.WithProtocol(corev1.ProtocolTCP), - components.WithAppProtocol(&components.GrpcProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](14250), + components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), ), components.WithPortMapping("thrift_http", 14268, - components.WithTargetPort(14268), - components.WithProtocol(corev1.ProtocolTCP), - components.WithAppProtocol(&components.HttpProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](14268), + components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), ), components.WithPortMapping("thrift_compact", 6831, - components.WithTargetPort(6831), - components.WithProtocol(corev1.ProtocolUDP), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](6831), + components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolUDP), ), components.WithPortMapping("thrift_binary", 6832, - components.WithTargetPort(6832), - components.WithProtocol(corev1.ProtocolUDP), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](6832), + components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolUDP), ), ), components.NewMultiPortReceiver("loki", components.WithPortMapping(components.GrpcProtocol, 9095, - components.WithTargetPort(9095), - components.WithAppProtocol(&components.GrpcProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](9095), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), ), components.WithPortMapping(components.HttpProtocol, 3100, - components.WithTargetPort(3100), - components.WithAppProtocol(&components.HttpProtocol), + components.WithTargetPort[*components.MultiProtocolEndpointConfig](3100), + components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), ), ), - components.NewSinglePortParser("awsxray", 2000, components.WithTargetPort(2000)), - components.NewSinglePortParser("carbon", 2003, components.WithTargetPort(2003)), - components.NewSinglePortParser("collectd", 8081, components.WithTargetPort(8081)), - components.NewSinglePortParser("fluentforward", 8006, components.WithTargetPort(8006)), - components.NewSinglePortParser("influxdb", 8086, components.WithTargetPort(8086)), - components.NewSinglePortParser("opencensus", 55678, components.WithAppProtocol(nil), components.WithTargetPort(55678)), - components.NewSinglePortParser("sapm", 7276, components.WithTargetPort(7276)), - components.NewSinglePortParser("signalfx", 9943, components.WithTargetPort(9943)), - components.NewSinglePortParser("splunk_hec", 8088, components.WithTargetPort(8088)), - components.NewSinglePortParser("statsd", 8125, components.WithProtocol(corev1.ProtocolUDP), components.WithTargetPort(8125)), - components.NewSinglePortParser("tcplog", components.UnsetPort, components.WithProtocol(corev1.ProtocolTCP)), - components.NewSinglePortParser("udplog", components.UnsetPort, components.WithProtocol(corev1.ProtocolUDP)), - components.NewSinglePortParser("wavefront", 2003, components.WithTargetPort(2003)), - components.NewSinglePortParser("zipkin", 9411, components.WithAppProtocol(&components.HttpProtocol), components.WithProtocol(corev1.ProtocolTCP), components.WithTargetPort(3100)), + components.NewSinglePortParser("awsxray", 2000, components.WithTargetPort[*components.SingleEndpointConfig](2000)), + components.NewSinglePortParser("carbon", 2003, components.WithTargetPort[*components.SingleEndpointConfig](2003)), + components.NewSinglePortParser("collectd", 8081, components.WithTargetPort[*components.SingleEndpointConfig](8081)), + components.NewSinglePortParser("fluentforward", 8006, components.WithTargetPort[*components.SingleEndpointConfig](8006)), + components.NewSinglePortParser("influxdb", 8086, components.WithTargetPort[*components.SingleEndpointConfig](8086)), + components.NewSinglePortParser("opencensus", 55678, components.WithAppProtocol[*components.SingleEndpointConfig](nil), components.WithTargetPort[*components.SingleEndpointConfig](55678)), + components.NewSinglePortParser("sapm", 7276, components.WithTargetPort[*components.SingleEndpointConfig](7276)), + components.NewSinglePortParser("signalfx", 9943, components.WithTargetPort[*components.SingleEndpointConfig](9943)), + components.NewSinglePortParser("splunk_hec", 8088, components.WithTargetPort[*components.SingleEndpointConfig](8088)), + components.NewSinglePortParser("statsd", 8125, components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolUDP), components.WithTargetPort[*components.SingleEndpointConfig](8125)), + components.NewSinglePortParser("tcplog", components.UnsetPort, components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP)), + components.NewSinglePortParser("udplog", components.UnsetPort, components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolUDP)), + components.NewSinglePortParser("wavefront", 2003, components.WithTargetPort[*components.SingleEndpointConfig](2003)), + components.NewSinglePortParser("zipkin", 9411, components.WithAppProtocol[*components.SingleEndpointConfig](&components.HttpProtocol), components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), components.WithTargetPort[*components.SingleEndpointConfig](3100)), NewScraperParser("prometheus"), NewScraperParser("kubeletstats"), NewScraperParser("sshcheck"), diff --git a/internal/components/single_endpoint.go b/internal/components/single_endpoint.go index 1b8e1efada..9f7b78e787 100644 --- a/internal/components/single_endpoint.go +++ b/internal/components/single_endpoint.go @@ -52,38 +52,38 @@ func (g *SingleEndpointConfig) GetPortNum() (int32, error) { return UnsetPort, PortNotFoundErr } -func ParseSingleEndpointSilent(logger logr.Logger, name string, o *Option, singleEndpointConfig *SingleEndpointConfig) ([]corev1.ServicePort, error) { - return internalParseSingleEndpoint(logger, name, true, o, singleEndpointConfig) +func ParseSingleEndpointSilent(logger logr.Logger, name string, defaultPort *corev1.ServicePort, singleEndpointConfig *SingleEndpointConfig) ([]corev1.ServicePort, error) { + return internalParseSingleEndpoint(logger, name, true, defaultPort, singleEndpointConfig) } -func ParseSingleEndpoint(logger logr.Logger, name string, o *Option, singleEndpointConfig *SingleEndpointConfig) ([]corev1.ServicePort, error) { - return internalParseSingleEndpoint(logger, name, false, o, singleEndpointConfig) +func ParseSingleEndpoint(logger logr.Logger, name string, defaultPort *corev1.ServicePort, singleEndpointConfig *SingleEndpointConfig) ([]corev1.ServicePort, error) { + return internalParseSingleEndpoint(logger, name, false, defaultPort, singleEndpointConfig) } -func internalParseSingleEndpoint(logger logr.Logger, name string, failSilently bool, o *Option, singleEndpointConfig *SingleEndpointConfig) ([]corev1.ServicePort, error) { +func internalParseSingleEndpoint(logger logr.Logger, name string, failSilently bool, defaultPort *corev1.ServicePort, singleEndpointConfig *SingleEndpointConfig) ([]corev1.ServicePort, error) { if singleEndpointConfig == nil { return nil, nil } - if _, err := singleEndpointConfig.GetPortNum(); err != nil && o.port == UnsetPort { + if _, err := singleEndpointConfig.GetPortNum(); err != nil && defaultPort.Port == UnsetPort { if failSilently { - logger.WithValues("receiver", o.name).V(4).Info("couldn't parse the endpoint's port and no default port set", "error", err) + logger.WithValues("receiver", defaultPort.Name).V(4).Info("couldn't parse the endpoint's port and no default port set", "error", err) err = nil } else { - logger.WithValues("receiver", o.name).Error(err, "couldn't parse the endpoint's port and no default port set") + logger.WithValues("receiver", defaultPort.Name).Error(err, "couldn't parse the endpoint's port and no default port set") } return []corev1.ServicePort{}, err } - port := singleEndpointConfig.GetPortNumOrDefault(logger, o.port) - svcPort := o.GetServicePort() + port := singleEndpointConfig.GetPortNumOrDefault(logger, defaultPort.Port) + svcPort := defaultPort svcPort.Name = naming.PortName(name, port) return []corev1.ServicePort{ConstructServicePort(svcPort, port)}, nil } -func NewSinglePortParser(name string, port int32, opts ...PortBuilderOption) *GenericParser[*SingleEndpointConfig] { - return NewGenericParser(name, port, ParseSingleEndpoint, nil, opts...) +func NewSinglePortParser(name string, port int32, opts ...PortBuilderOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { + return NewGenericParser(name, port, append(opts, WithPortParser(ParseSingleEndpoint))...) } // NewSilentSinglePortParser returns a ParseSingleEndpoint that errors silently on failure to find a port. -func NewSilentSinglePortParser(name string, port int32, opts ...PortBuilderOption) *GenericParser[*SingleEndpointConfig] { - return NewGenericParser(name, port, ParseSingleEndpointSilent, nil, opts...) +func NewSilentSinglePortParser(name string, port int32, opts ...PortBuilderOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { + return NewGenericParser(name, port, append(opts, WithPortParser(ParseSingleEndpointSilent))...) } diff --git a/internal/components/single_endpoint_test.go b/internal/components/single_endpoint_test.go index 9e544d40b9..5ec1e4f015 100644 --- a/internal/components/single_endpoint_test.go +++ b/internal/components/single_endpoint_test.go @@ -111,7 +111,7 @@ func TestSingleEndpointParser_ParserName(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption + opts []components.PortBuilderOption[*components.SingleEndpointConfig] } tests := []struct { name string @@ -130,8 +130,8 @@ func TestSingleEndpointParser_ParserName(t *testing.T) { name: "with port mapping without builder options", fields: fields{ name: "receiver2", - opts: []components.PortBuilderOption{ - components.WithTargetPort(8080), + opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + components.WithTargetPort[*components.SingleEndpointConfig](8080), }, }, want: "__receiver2", @@ -149,7 +149,7 @@ func TestSingleEndpointParser_ParserType(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption + opts []components.PortBuilderOption[*components.SingleEndpointConfig] } tests := []struct { name string @@ -168,8 +168,8 @@ func TestSingleEndpointParser_ParserType(t *testing.T) { name: "with port mapping without builder options", fields: fields{ name: "receiver2/test", - opts: []components.PortBuilderOption{ - components.WithTargetPort(80), + opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + components.WithTargetPort[*components.SingleEndpointConfig](80), }, }, want: "receiver2", @@ -187,7 +187,7 @@ func TestSingleEndpointParser_Ports(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption + opts []components.PortBuilderOption[*components.SingleEndpointConfig] } type args struct { config interface{} @@ -234,10 +234,10 @@ func TestSingleEndpointParser_Ports(t *testing.T) { fields: fields{ name: "testparser", port: 8080, - opts: []components.PortBuilderOption{ - components.WithTargetPort(4317), - components.WithProtocol(corev1.ProtocolTCP), - components.WithAppProtocol(&components.GrpcProtocol), + opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + components.WithTargetPort[*components.SingleEndpointConfig](4317), + components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), + components.WithAppProtocol[*components.SingleEndpointConfig](&components.GrpcProtocol), }, }, args: args{ @@ -297,7 +297,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption + opts []components.PortBuilderOption[*components.SingleEndpointConfig] } type args struct { config interface{} @@ -344,10 +344,10 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { fields: fields{ name: "testparser", port: 8080, - opts: []components.PortBuilderOption{ - components.WithTargetPort(4317), - components.WithProtocol(corev1.ProtocolTCP), - components.WithAppProtocol(&components.GrpcProtocol), + opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + components.WithTargetPort[*components.SingleEndpointConfig](4317), + components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), + components.WithAppProtocol[*components.SingleEndpointConfig](&components.GrpcProtocol), }, }, args: args{ From 21ffd38b07ad2493ff675ac33bee8a810f779ebe Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Tue, 20 Aug 2024 17:48:10 -0400 Subject: [PATCH 06/18] rename --- internal/components/component.go | 14 +++++++------- internal/components/generic_parser.go | 2 +- internal/components/multi_endpoint.go | 2 +- internal/components/single_endpoint.go | 4 ++-- internal/components/single_endpoint_test.go | 16 ++++++++-------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/internal/components/component.go b/internal/components/component.go index 87314a8e7c..30535ec35e 100644 --- a/internal/components/component.go +++ b/internal/components/component.go @@ -46,7 +46,7 @@ type PortParser[T any] func(logger logr.Logger, name string, defaultPort *corev1 // RBACRuleGenerator is a function that generates a list of RBAC Rules given a configuration of type T // It's expected that type T is the configuration used by a parser. type RBACRuleGenerator[T any] func(logger logr.Logger, config T) ([]rbacv1.PolicyRule, error) -type PortBuilderOption[T any] func(*Option[T]) +type ParserOption[T any] func(*Option[T]) type Option[T any] struct { protocol corev1.Protocol @@ -66,7 +66,7 @@ func NewOption[T any](name string, port int32) *Option[T] { } } -func (o *Option[T]) Apply(opts ...PortBuilderOption[T]) { +func (o *Option[T]) Apply(opts ...ParserOption[T]) { for _, opt := range opts { opt(o) } @@ -83,31 +83,31 @@ func (o *Option[T]) GetServicePort() *corev1.ServicePort { } } -func WithRBACRuleGenerator[T any](r RBACRuleGenerator[T]) PortBuilderOption[T] { +func WithRBACRuleGenerator[T any](r RBACRuleGenerator[T]) ParserOption[T] { return func(opt *Option[T]) { opt.rbacGen = r } } -func WithPortParser[T any](p PortParser[T]) PortBuilderOption[T] { +func WithPortParser[T any](p PortParser[T]) ParserOption[T] { return func(opt *Option[T]) { opt.portParser = p } } -func WithTargetPort[T any](targetPort int32) PortBuilderOption[T] { +func WithTargetPort[T any](targetPort int32) ParserOption[T] { return func(opt *Option[T]) { opt.targetPort = intstr.FromInt32(targetPort) } } -func WithAppProtocol[T any](proto *string) PortBuilderOption[T] { +func WithAppProtocol[T any](proto *string) ParserOption[T] { return func(opt *Option[T]) { opt.appProtocol = proto } } -func WithProtocol[T any](proto corev1.Protocol) PortBuilderOption[T] { +func WithProtocol[T any](proto corev1.Protocol) ParserOption[T] { return func(opt *Option[T]) { opt.protocol = proto } diff --git a/internal/components/generic_parser.go b/internal/components/generic_parser.go index 22c2c04e68..77a82a08eb 100644 --- a/internal/components/generic_parser.go +++ b/internal/components/generic_parser.go @@ -66,7 +66,7 @@ func (g *GenericParser[T]) ParserName() string { return fmt.Sprintf("__%s", g.name) } -func NewGenericParser[T any](name string, port int32, opts ...PortBuilderOption[T]) *GenericParser[T] { +func NewGenericParser[T any](name string, port int32, opts ...ParserOption[T]) *GenericParser[T] { o := NewOption[T](name, port) o.Apply(opts...) return &GenericParser[T]{name: name, portParser: o.portParser, rbacGen: o.rbacGen, option: o} diff --git a/internal/components/multi_endpoint.go b/internal/components/multi_endpoint.go index 489b6c5dc3..89db7e6047 100644 --- a/internal/components/multi_endpoint.go +++ b/internal/components/multi_endpoint.go @@ -87,7 +87,7 @@ func NewMultiPortReceiver(name string, opts ...MultiPortOption) *MultiPortReceiv return multiReceiver } -func WithPortMapping(name string, port int32, opts ...PortBuilderOption[*MultiProtocolEndpointConfig]) MultiPortOption { +func WithPortMapping(name string, port int32, opts ...ParserOption[*MultiProtocolEndpointConfig]) MultiPortOption { return func(parser *MultiPortReceiver) { o := NewOption[*MultiProtocolEndpointConfig](name, port) o.Apply(opts...) diff --git a/internal/components/single_endpoint.go b/internal/components/single_endpoint.go index 9f7b78e787..5da4d18e1e 100644 --- a/internal/components/single_endpoint.go +++ b/internal/components/single_endpoint.go @@ -79,11 +79,11 @@ func internalParseSingleEndpoint(logger logr.Logger, name string, failSilently b return []corev1.ServicePort{ConstructServicePort(svcPort, port)}, nil } -func NewSinglePortParser(name string, port int32, opts ...PortBuilderOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { +func NewSinglePortParser(name string, port int32, opts ...ParserOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { return NewGenericParser(name, port, append(opts, WithPortParser(ParseSingleEndpoint))...) } // NewSilentSinglePortParser returns a ParseSingleEndpoint that errors silently on failure to find a port. -func NewSilentSinglePortParser(name string, port int32, opts ...PortBuilderOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { +func NewSilentSinglePortParser(name string, port int32, opts ...ParserOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { return NewGenericParser(name, port, append(opts, WithPortParser(ParseSingleEndpointSilent))...) } diff --git a/internal/components/single_endpoint_test.go b/internal/components/single_endpoint_test.go index 5ec1e4f015..6b952e7eb7 100644 --- a/internal/components/single_endpoint_test.go +++ b/internal/components/single_endpoint_test.go @@ -111,7 +111,7 @@ func TestSingleEndpointParser_ParserName(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption[*components.SingleEndpointConfig] + opts []components.ParserOption[*components.SingleEndpointConfig] } tests := []struct { name string @@ -130,7 +130,7 @@ func TestSingleEndpointParser_ParserName(t *testing.T) { name: "with port mapping without builder options", fields: fields{ name: "receiver2", - opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + opts: []components.ParserOption[*components.SingleEndpointConfig]{ components.WithTargetPort[*components.SingleEndpointConfig](8080), }, }, @@ -149,7 +149,7 @@ func TestSingleEndpointParser_ParserType(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption[*components.SingleEndpointConfig] + opts []components.ParserOption[*components.SingleEndpointConfig] } tests := []struct { name string @@ -168,7 +168,7 @@ func TestSingleEndpointParser_ParserType(t *testing.T) { name: "with port mapping without builder options", fields: fields{ name: "receiver2/test", - opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + opts: []components.ParserOption[*components.SingleEndpointConfig]{ components.WithTargetPort[*components.SingleEndpointConfig](80), }, }, @@ -187,7 +187,7 @@ func TestSingleEndpointParser_Ports(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption[*components.SingleEndpointConfig] + opts []components.ParserOption[*components.SingleEndpointConfig] } type args struct { config interface{} @@ -234,7 +234,7 @@ func TestSingleEndpointParser_Ports(t *testing.T) { fields: fields{ name: "testparser", port: 8080, - opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + opts: []components.ParserOption[*components.SingleEndpointConfig]{ components.WithTargetPort[*components.SingleEndpointConfig](4317), components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), components.WithAppProtocol[*components.SingleEndpointConfig](&components.GrpcProtocol), @@ -297,7 +297,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { type fields struct { name string port int32 - opts []components.PortBuilderOption[*components.SingleEndpointConfig] + opts []components.ParserOption[*components.SingleEndpointConfig] } type args struct { config interface{} @@ -344,7 +344,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { fields: fields{ name: "testparser", port: 8080, - opts: []components.PortBuilderOption[*components.SingleEndpointConfig]{ + opts: []components.ParserOption[*components.SingleEndpointConfig]{ components.WithTargetPort[*components.SingleEndpointConfig](4317), components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), components.WithAppProtocol[*components.SingleEndpointConfig](&components.GrpcProtocol), From 5c498fc2c020a6d71c9760f260fffb5bf052e3a8 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Wed, 21 Aug 2024 10:27:19 -0400 Subject: [PATCH 07/18] builder --- internal/components/builder.go | 86 ++++++++++++++++++++++++ internal/components/component.go | 4 ++ internal/components/receivers/helpers.go | 62 ++++++++++++----- internal/components/single_endpoint.go | 8 +++ 4 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 internal/components/builder.go diff --git a/internal/components/builder.go b/internal/components/builder.go new file mode 100644 index 0000000000..bbe6030e5f --- /dev/null +++ b/internal/components/builder.go @@ -0,0 +1,86 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package components + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +type Builder[T any] []ParserOption[T] + +func NewBuilder[T any]() Builder[T] { + return make([]ParserOption[T], 8) +} + +func (b Builder[T]) WithProtocol(protocol corev1.Protocol) Builder[T] { + return append(b, func(o *Option[T]) { + o.protocol = protocol + }) +} +func (b Builder[T]) WithAppProtocol(appProtocol *string) Builder[T] { + return append(b, func(o *Option[T]) { + o.appProtocol = appProtocol + }) +} +func (b Builder[T]) WithTargetPort(targetPort int32) Builder[T] { + return append(b, func(o *Option[T]) { + o.targetPort = intstr.FromInt32(targetPort) + }) +} +func (b Builder[T]) WithNodePort(nodePort int32) Builder[T] { + return append(b, func(o *Option[T]) { + o.nodePort = nodePort + }) +} +func (b Builder[T]) WithName(name string) Builder[T] { + return append(b, func(o *Option[T]) { + o.name = name + }) +} +func (b Builder[T]) WithPort(port int32) Builder[T] { + return append(b, func(o *Option[T]) { + o.port = port + }) +} +func (b Builder[T]) WithPortParser(portParser PortParser[T]) Builder[T] { + return append(b, func(o *Option[T]) { + o.portParser = portParser + }) +} +func (b Builder[T]) WithRbacGen(rbacGen RBACRuleGenerator[T]) Builder[T] { + return append(b, func(o *Option[T]) { + o.rbacGen = rbacGen + }) +} + +func (b Builder[T]) Build() (*GenericParser[T], error) { + o := NewEmptyOption[T]() + o.Apply(b...) + if len(o.name) == 0 { + return nil, fmt.Errorf("invalid option struct, no name specified") + } + return &GenericParser[T]{name: o.name, portParser: o.portParser, rbacGen: o.rbacGen, option: o}, nil +} + +func (b Builder[T]) MustBuild() *GenericParser[T] { + if p, err := b.Build(); err != nil { + panic(err) + } else { + return p + } +} diff --git a/internal/components/component.go b/internal/components/component.go index 30535ec35e..3e4c1804a1 100644 --- a/internal/components/component.go +++ b/internal/components/component.go @@ -59,6 +59,10 @@ type Option[T any] struct { rbacGen RBACRuleGenerator[T] } +func NewEmptyOption[T any]() *Option[T] { + return &Option[T]{} +} + func NewOption[T any](name string, port int32) *Option[T] { return &Option[T]{ name: name, diff --git a/internal/components/receivers/helpers.go b/internal/components/receivers/helpers.go index d8c45c388a..a67def9771 100644 --- a/internal/components/receivers/helpers.go +++ b/internal/components/receivers/helpers.go @@ -44,7 +44,7 @@ func ReceiverFor(name string) components.Parser { // NewScraperParser is an instance of a generic parser that returns nothing when called and never fails. func NewScraperParser(name string) *components.GenericParser[any] { - return components.NewGenericParser[any](name, components.UnsetPort) + return components.NewBuilder[any]().WithName(name).WithPort(components.UnsetPort).MustBuild() } var ( @@ -101,20 +101,52 @@ var ( components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), ), ), - components.NewSinglePortParser("awsxray", 2000, components.WithTargetPort[*components.SingleEndpointConfig](2000)), - components.NewSinglePortParser("carbon", 2003, components.WithTargetPort[*components.SingleEndpointConfig](2003)), - components.NewSinglePortParser("collectd", 8081, components.WithTargetPort[*components.SingleEndpointConfig](8081)), - components.NewSinglePortParser("fluentforward", 8006, components.WithTargetPort[*components.SingleEndpointConfig](8006)), - components.NewSinglePortParser("influxdb", 8086, components.WithTargetPort[*components.SingleEndpointConfig](8086)), - components.NewSinglePortParser("opencensus", 55678, components.WithAppProtocol[*components.SingleEndpointConfig](nil), components.WithTargetPort[*components.SingleEndpointConfig](55678)), - components.NewSinglePortParser("sapm", 7276, components.WithTargetPort[*components.SingleEndpointConfig](7276)), - components.NewSinglePortParser("signalfx", 9943, components.WithTargetPort[*components.SingleEndpointConfig](9943)), - components.NewSinglePortParser("splunk_hec", 8088, components.WithTargetPort[*components.SingleEndpointConfig](8088)), - components.NewSinglePortParser("statsd", 8125, components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolUDP), components.WithTargetPort[*components.SingleEndpointConfig](8125)), - components.NewSinglePortParser("tcplog", components.UnsetPort, components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP)), - components.NewSinglePortParser("udplog", components.UnsetPort, components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolUDP)), - components.NewSinglePortParser("wavefront", 2003, components.WithTargetPort[*components.SingleEndpointConfig](2003)), - components.NewSinglePortParser("zipkin", 9411, components.WithAppProtocol[*components.SingleEndpointConfig](&components.HttpProtocol), components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), components.WithTargetPort[*components.SingleEndpointConfig](3100)), + components.NewSinglePortParserBuilder("awsxray", 2000). + WithTargetPort(2000). + MustBuild(), + components.NewSinglePortParserBuilder("carbon", 2003). + WithTargetPort(2003). + MustBuild(), + components.NewSinglePortParserBuilder("collectd", 8081). + WithTargetPort(8081). + MustBuild(), + components.NewSinglePortParserBuilder("fluentforward", 8006). + WithTargetPort(8006). + MustBuild(), + components.NewSinglePortParserBuilder("influxdb", 8086). + WithTargetPort(8086). + MustBuild(), + components.NewSinglePortParserBuilder("opencensus", 55678). + WithAppProtocol(nil). + WithTargetPort(55678). + MustBuild(), + components.NewSinglePortParserBuilder("sapm", 7276). + WithTargetPort(7276). + MustBuild(), + components.NewSinglePortParserBuilder("signalfx", 9943). + WithTargetPort(9943). + MustBuild(), + components.NewSinglePortParserBuilder("splunk_hec", 8088). + WithTargetPort(8088). + MustBuild(), + components.NewSinglePortParserBuilder("statsd", 8125). + WithProtocol(corev1.ProtocolUDP). + WithTargetPort(8125). + MustBuild(), + components.NewSinglePortParserBuilder("tcplog", components.UnsetPort). + WithProtocol(corev1.ProtocolTCP). + MustBuild(), + components.NewSinglePortParserBuilder("udplog", components.UnsetPort). + WithProtocol(corev1.ProtocolUDP). + MustBuild(), + components.NewSinglePortParserBuilder("wavefront", 2003). + WithTargetPort(2003). + MustBuild(), + components.NewSinglePortParserBuilder("zipkin", 9411). + WithAppProtocol(&components.HttpProtocol). + WithProtocol(corev1.ProtocolTCP). + WithTargetPort(3100). + MustBuild(), NewScraperParser("prometheus"), NewScraperParser("kubeletstats"), NewScraperParser("sshcheck"), diff --git a/internal/components/single_endpoint.go b/internal/components/single_endpoint.go index 5da4d18e1e..7c20e39cbf 100644 --- a/internal/components/single_endpoint.go +++ b/internal/components/single_endpoint.go @@ -87,3 +87,11 @@ func NewSinglePortParser(name string, port int32, opts ...ParserOption[*SingleEn func NewSilentSinglePortParser(name string, port int32, opts ...ParserOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { return NewGenericParser(name, port, append(opts, WithPortParser(ParseSingleEndpointSilent))...) } + +func NewSinglePortParserBuilder(name string, port int32) Builder[*SingleEndpointConfig] { + return NewBuilder[*SingleEndpointConfig]().WithPort(port).WithName(name).WithPortParser(ParseSingleEndpoint) +} + +func NewSilentSinglePortParserBuilder(name string, port int32) Builder[*SingleEndpointConfig] { + return NewBuilder[*SingleEndpointConfig]().WithPort(port).WithName(name).WithPortParser(ParseSingleEndpointSilent) +} From c8c5d47862d026c6e1f8a70957e2cdfc1bdb5f19 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Wed, 21 Aug 2024 13:57:13 -0400 Subject: [PATCH 08/18] Change to use the builder pattern instead of the option pattern --- internal/components/builder.go | 45 ++++- internal/components/builder_test.go | 156 +++++++++++++++ internal/components/component.go | 72 ------- internal/components/exporters/helpers.go | 2 +- internal/components/exporters/helpers_test.go | 2 +- internal/components/generic_parser.go | 3 +- internal/components/generic_parser_test.go | 38 +++- internal/components/multi_endpoint.go | 41 +++- internal/components/multi_endpoint_test.go | 189 +++++++++++------- internal/components/processors/helpers.go | 6 +- .../components/processors/helpers_test.go | 27 +++ internal/components/receivers/helpers.go | 94 ++++----- internal/components/single_endpoint.go | 9 - internal/components/single_endpoint_test.go | 106 +++++----- 14 files changed, 495 insertions(+), 295 deletions(-) create mode 100644 internal/components/builder_test.go diff --git a/internal/components/builder.go b/internal/components/builder.go index bbe6030e5f..98ba217010 100644 --- a/internal/components/builder.go +++ b/internal/components/builder.go @@ -19,12 +19,55 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) +type ParserOption[T any] func(*Option[T]) + +type Option[T any] struct { + protocol corev1.Protocol + appProtocol *string + targetPort intstr.IntOrString + nodePort int32 + name string + port int32 + portParser PortParser[T] + rbacGen RBACRuleGenerator[T] +} + +func NewEmptyOption[T any]() *Option[T] { + return &Option[T]{} +} + +func NewOption[T any](name string, port int32) *Option[T] { + return &Option[T]{ + name: name, + port: port, + } +} + +func (o *Option[T]) Apply(opts ...ParserOption[T]) { + for _, opt := range opts { + opt(o) + } +} + +func (o *Option[T]) GetServicePort() *corev1.ServicePort { + return &corev1.ServicePort{ + Name: naming.PortName(o.name, o.port), + Port: o.port, + Protocol: o.protocol, + AppProtocol: o.appProtocol, + TargetPort: o.targetPort, + NodePort: o.nodePort, + } +} + type Builder[T any] []ParserOption[T] func NewBuilder[T any]() Builder[T] { - return make([]ParserOption[T], 8) + return []ParserOption[T]{} } func (b Builder[T]) WithProtocol(protocol corev1.Protocol) Builder[T] { diff --git a/internal/components/builder_test.go b/internal/components/builder_test.go new file mode 100644 index 0000000000..7a1c19d1ff --- /dev/null +++ b/internal/components/builder_test.go @@ -0,0 +1,156 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package components_test + +import ( + "fmt" + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/components" +) + +func TestBuilder_Build(t *testing.T) { + type sampleConfig struct { + example string + number int + m map[string]interface{} + } + type want struct { + name string + ports []corev1.ServicePort + rules []rbacv1.PolicyRule + } + type fields[T any] struct { + b components.Builder[T] + } + type params struct { + conf interface{} + } + type testCase[T any] struct { + name string + fields fields[T] + params params + want want + wantErr assert.ErrorAssertionFunc + } + examplePortParser := func(logger logr.Logger, name string, defaultPort *corev1.ServicePort, config sampleConfig) ([]corev1.ServicePort, error) { + if defaultPort != nil { + return []corev1.ServicePort{*defaultPort}, nil + } + return nil, nil + } + tests := []testCase[sampleConfig]{ + { + name: "basic valid configuration", + fields: fields[sampleConfig]{ + b: components.NewBuilder[sampleConfig](). + WithPortParser(examplePortParser). + WithName("test-service"). + WithPort(80). + WithNodePort(80). + WithProtocol(corev1.ProtocolTCP), + }, + params: params{ + conf: sampleConfig{}, + }, + want: want{ + name: "__test-service", + ports: []corev1.ServicePort{ + { + Name: "test-service", + Port: 80, + NodePort: 80, + Protocol: corev1.ProtocolTCP, + }, + }, + rules: nil, + }, + wantErr: assert.NoError, + }, + { + name: "missing name", + fields: fields[sampleConfig]{ + b: components.NewBuilder[sampleConfig](). + WithPort(8080). + WithProtocol(corev1.ProtocolUDP), + }, + params: params{ + conf: sampleConfig{}, + }, + want: want{}, + wantErr: assert.Error, + }, + { + name: "complete configuration with RBAC rules", + fields: fields[sampleConfig]{ + b: components.NewBuilder[sampleConfig](). + WithName("secure-service"). + WithPort(443). + WithProtocol(corev1.ProtocolTCP). + WithRbacGen(func(logger logr.Logger, config sampleConfig) ([]rbacv1.PolicyRule, error) { + return []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, nil + }), + }, + params: params{ + conf: sampleConfig{}, + }, + want: want{ + name: "__secure-service", + ports: nil, + rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, + }, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.fields.b.Build() + if tt.wantErr(t, err, fmt.Sprintf("WantErr()")) && err != nil { + return + } + assert.Equalf(t, tt.want.name, got.ParserName(), "ParserName()") + ports, err := got.Ports(logr.Discard(), got.ParserType(), tt.params.conf) + assert.NoError(t, err) + assert.Equalf(t, tt.want.ports, ports, "Ports()") + rules, err := got.GetRBACRules(logr.Discard(), tt.params.conf) + assert.NoError(t, err) + assert.Equalf(t, tt.want.rules, rules, "GetRBACRules()") + }) + } +} + +func TestMustBuildPanics(t *testing.T) { + b := components.Builder[*components.SingleEndpointConfig]{} + assert.Panics(t, func() { + b.MustBuild() + }) +} diff --git a/internal/components/component.go b/internal/components/component.go index 3e4c1804a1..d51690b8ae 100644 --- a/internal/components/component.go +++ b/internal/components/component.go @@ -24,8 +24,6 @@ import ( corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/util/intstr" - - "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) var ( @@ -46,76 +44,6 @@ type PortParser[T any] func(logger logr.Logger, name string, defaultPort *corev1 // RBACRuleGenerator is a function that generates a list of RBAC Rules given a configuration of type T // It's expected that type T is the configuration used by a parser. type RBACRuleGenerator[T any] func(logger logr.Logger, config T) ([]rbacv1.PolicyRule, error) -type ParserOption[T any] func(*Option[T]) - -type Option[T any] struct { - protocol corev1.Protocol - appProtocol *string - targetPort intstr.IntOrString - nodePort int32 - name string - port int32 - portParser PortParser[T] - rbacGen RBACRuleGenerator[T] -} - -func NewEmptyOption[T any]() *Option[T] { - return &Option[T]{} -} - -func NewOption[T any](name string, port int32) *Option[T] { - return &Option[T]{ - name: name, - port: port, - } -} - -func (o *Option[T]) Apply(opts ...ParserOption[T]) { - for _, opt := range opts { - opt(o) - } -} - -func (o *Option[T]) GetServicePort() *corev1.ServicePort { - return &corev1.ServicePort{ - Name: naming.PortName(o.name, o.port), - Port: o.port, - Protocol: o.protocol, - AppProtocol: o.appProtocol, - TargetPort: o.targetPort, - NodePort: o.nodePort, - } -} - -func WithRBACRuleGenerator[T any](r RBACRuleGenerator[T]) ParserOption[T] { - return func(opt *Option[T]) { - opt.rbacGen = r - } -} - -func WithPortParser[T any](p PortParser[T]) ParserOption[T] { - return func(opt *Option[T]) { - opt.portParser = p - } -} - -func WithTargetPort[T any](targetPort int32) ParserOption[T] { - return func(opt *Option[T]) { - opt.targetPort = intstr.FromInt32(targetPort) - } -} - -func WithAppProtocol[T any](proto *string) ParserOption[T] { - return func(opt *Option[T]) { - opt.appProtocol = proto - } -} - -func WithProtocol[T any](proto corev1.Protocol) ParserOption[T] { - return func(opt *Option[T]) { - opt.protocol = proto - } -} // ComponentType returns the type for a given component name. // components have a name like: diff --git a/internal/components/exporters/helpers.go b/internal/components/exporters/helpers.go index be41e039a9..bf11f1dc1e 100644 --- a/internal/components/exporters/helpers.go +++ b/internal/components/exporters/helpers.go @@ -43,7 +43,7 @@ func ParserFor(name string) components.Parser { var ( componentParsers = []components.Parser{ - components.NewSinglePortParser("prometheus", 8888), + components.NewSinglePortParserBuilder("prometheus", 8888).MustBuild(), } ) diff --git a/internal/components/exporters/helpers_test.go b/internal/components/exporters/helpers_test.go index 80dfed3a20..77fb67aced 100644 --- a/internal/components/exporters/helpers_test.go +++ b/internal/components/exporters/helpers_test.go @@ -39,7 +39,7 @@ func TestParserForReturns(t *testing.T) { func TestCanRegister(t *testing.T) { const testComponentName = "test" - exporters.Register(testComponentName, components.NewSinglePortParser(testComponentName, 9000)) + exporters.Register(testComponentName, components.NewSinglePortParserBuilder(testComponentName, 9000).MustBuild()) assert.True(t, exporters.IsRegistered(testComponentName)) parser := exporters.ParserFor(testComponentName) assert.Equal(t, "test", parser.ParserType()) diff --git a/internal/components/generic_parser.go b/internal/components/generic_parser.go index 77a82a08eb..f0a33be21f 100644 --- a/internal/components/generic_parser.go +++ b/internal/components/generic_parser.go @@ -66,8 +66,7 @@ func (g *GenericParser[T]) ParserName() string { return fmt.Sprintf("__%s", g.name) } -func NewGenericParser[T any](name string, port int32, opts ...ParserOption[T]) *GenericParser[T] { +func NewGenericParser[T any](name string, port int32) *GenericParser[T] { o := NewOption[T](name, port) - o.Apply(opts...) return &GenericParser[T]{name: name, portParser: o.portParser, rbacGen: o.rbacGen, option: o} } diff --git a/internal/components/generic_parser_test.go b/internal/components/generic_parser_test.go index 9e70b77136..20c553d057 100644 --- a/internal/components/generic_parser_test.go +++ b/internal/components/generic_parser_test.go @@ -42,7 +42,7 @@ func TestGenericParser_GetPorts(t *testing.T) { tests := []testCase[*components.SingleEndpointConfig]{ { name: "valid config with endpoint", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint)), + g: components.NewSinglePortParserBuilder("test", 0).MustBuild(), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -59,7 +59,7 @@ func TestGenericParser_GetPorts(t *testing.T) { }, { name: "valid config with listen_address", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint)), + g: components.NewSinglePortParserBuilder("test", 0).MustBuild(), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -76,7 +76,7 @@ func TestGenericParser_GetPorts(t *testing.T) { }, { name: "valid config with listen_address with option", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint), components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolUDP)), + g: components.NewSinglePortParserBuilder("test", 0).WithProtocol(corev1.ProtocolUDP).MustBuild(), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -94,7 +94,7 @@ func TestGenericParser_GetPorts(t *testing.T) { }, { name: "invalid config with no endpoint or listen_address", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0, components.WithPortParser(components.ParseSingleEndpoint)), + g: components.NewSinglePortParserBuilder("test", 0).MustBuild(), args: args{ logger: logr.Discard(), config: map[string]interface{}{}, @@ -128,7 +128,7 @@ func TestGenericParser_GetRBACRules(t *testing.T) { wantErr assert.ErrorAssertionFunc } - rbacGenFunc := func(logger logr.Logger, config components.SingleEndpointConfig) ([]rbacv1.PolicyRule, error) { + rbacGenFunc := func(logger logr.Logger, config *components.SingleEndpointConfig) ([]rbacv1.PolicyRule, error) { if config.Endpoint == "" && config.ListenAddress == "" { return nil, fmt.Errorf("either endpoint or listen_address must be specified") } @@ -141,10 +141,10 @@ func TestGenericParser_GetRBACRules(t *testing.T) { }, nil } - tests := []testCase[components.SingleEndpointConfig]{ + tests := []testCase[*components.SingleEndpointConfig]{ { name: "valid config with endpoint", - g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, components.WithRBACRuleGenerator(rbacGenFunc)), + g: components.NewSinglePortParserBuilder("test", 0).WithRbacGen(rbacGenFunc).MustBuild(), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -162,7 +162,7 @@ func TestGenericParser_GetRBACRules(t *testing.T) { }, { name: "valid config with listen_address", - g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, components.WithRBACRuleGenerator(rbacGenFunc)), + g: components.NewSinglePortParserBuilder("test", 0).WithRbacGen(rbacGenFunc).MustBuild(), args: args{ logger: logr.Discard(), config: map[string]interface{}{ @@ -180,12 +180,32 @@ func TestGenericParser_GetRBACRules(t *testing.T) { }, { name: "invalid config with no endpoint or listen_address", - g: components.NewGenericParser[components.SingleEndpointConfig]("test", components.UnsetPort, components.WithRBACRuleGenerator(rbacGenFunc)), + g: components.NewSinglePortParserBuilder("test", 0).WithRbacGen(rbacGenFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{}, + }, + want: nil, + wantErr: assert.Error, + }, + { + name: "Generic works", + g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0), args: args{ logger: logr.Discard(), config: map[string]interface{}{}, }, want: nil, + wantErr: assert.NoError, + }, + { + name: "failed to parse config", + g: components.NewSinglePortParserBuilder("test", 0).WithRbacGen(rbacGenFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: func() {}, + }, + want: nil, wantErr: assert.Error, }, } diff --git a/internal/components/multi_endpoint.go b/internal/components/multi_endpoint.go index 89db7e6047..c7a818c431 100644 --- a/internal/components/multi_endpoint.go +++ b/internal/components/multi_endpoint.go @@ -76,21 +76,42 @@ func (m *MultiPortReceiver) GetRBACRules(logr.Logger, interface{}) ([]rbacv1.Pol return nil, nil } -func NewMultiPortReceiver(name string, opts ...MultiPortOption) *MultiPortReceiver { +type MultiPortBuilder[T any] []Builder[T] + +func NewMultiPortReceiverBuilder(name string) MultiPortBuilder[*MultiProtocolEndpointConfig] { + return append(MultiPortBuilder[*MultiProtocolEndpointConfig]{}, NewBuilder[*MultiProtocolEndpointConfig]().WithName(name)) +} + +func NewProtocolBuilder(name string, port int32) Builder[*MultiProtocolEndpointConfig] { + return NewBuilder[*MultiProtocolEndpointConfig]().WithName(name).WithPort(port) +} + +func (mp MultiPortBuilder[T]) AddPortMapping(builder Builder[T]) MultiPortBuilder[T] { + return append(mp, builder) +} + +func (mp MultiPortBuilder[T]) Build() (*MultiPortReceiver, error) { + if len(mp) < 1 { + return nil, fmt.Errorf("must provide at least one port mapping") + } multiReceiver := &MultiPortReceiver{ - name: name, + name: mp[0].MustBuild().name, portMappings: map[string]*corev1.ServicePort{}, } - for _, opt := range opts { - opt(multiReceiver) + for _, bu := range mp[1:] { + built, err := bu.Build() + if err != nil { + return nil, err + } + multiReceiver.portMappings[built.name] = built.option.GetServicePort() } - return multiReceiver + return multiReceiver, nil } -func WithPortMapping(name string, port int32, opts ...ParserOption[*MultiProtocolEndpointConfig]) MultiPortOption { - return func(parser *MultiPortReceiver) { - o := NewOption[*MultiProtocolEndpointConfig](name, port) - o.Apply(opts...) - parser.portMappings[name] = o.GetServicePort() +func (mp MultiPortBuilder[T]) MustBuild() *MultiPortReceiver { + if p, err := mp.Build(); err != nil { + panic(err) + } else { + return p } } diff --git a/internal/components/multi_endpoint_test.go b/internal/components/multi_endpoint_test.go index d4ea5ea926..deabf377a4 100644 --- a/internal/components/multi_endpoint_test.go +++ b/internal/components/multi_endpoint_test.go @@ -42,8 +42,7 @@ var ( func TestMultiPortReceiver_ParserName(t *testing.T) { type fields struct { - name string - opts []components.MultiPortOption + b components.MultiPortBuilder[*components.MultiProtocolEndpointConfig] } tests := []struct { name string @@ -53,34 +52,32 @@ func TestMultiPortReceiver_ParserName(t *testing.T) { { name: "no options", fields: fields{ - name: "receiver1", - opts: nil, + b: components.NewMultiPortReceiverBuilder("receiver1"), }, want: "__receiver1", }, { name: "with port mapping without builder options", fields: fields{ - name: "receiver2", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80), - }, + b: components.NewMultiPortReceiverBuilder("receiver2").AddPortMapping( + components.NewProtocolBuilder("http", 80), + ), }, want: "__receiver2", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - m := components.NewMultiPortReceiver(tt.fields.name, tt.fields.opts...) - assert.Equalf(t, tt.want, m.ParserName(), "ParserName()") + s, err := tt.fields.b.Build() + assert.NoError(t, err) + assert.Equalf(t, tt.want, s.ParserName(), "ParserName()") }) } } func TestMultiPortReceiver_ParserType(t *testing.T) { type fields struct { - name string - opts []components.MultiPortOption + b components.MultiPortBuilder[*components.MultiProtocolEndpointConfig] } tests := []struct { name string @@ -90,26 +87,25 @@ func TestMultiPortReceiver_ParserType(t *testing.T) { { name: "no options", fields: fields{ - name: "receiver1", - opts: nil, + b: components.NewMultiPortReceiverBuilder("receiver1"), }, want: "receiver1", }, { name: "with port mapping without builder options", fields: fields{ - name: "receiver2/test", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80), - }, + b: components.NewMultiPortReceiverBuilder("receiver2").AddPortMapping( + components.NewProtocolBuilder("http", 80), + ), }, want: "receiver2", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - m := components.NewMultiPortReceiver(tt.fields.name, tt.fields.opts...) - assert.Equalf(t, tt.want, m.ParserType(), "ParserType()") + s, err := tt.fields.b.Build() + assert.NoError(t, err) + assert.Equalf(t, tt.want, s.ParserType(), "ParserType()") }) } } @@ -117,37 +113,37 @@ func TestMultiPortReceiver_ParserType(t *testing.T) { func TestMultiPortReceiver_Ports(t *testing.T) { type fields struct { name string - opts []components.MultiPortOption + b components.MultiPortBuilder[*components.MultiProtocolEndpointConfig] } type args struct { config interface{} } tests := []struct { - name string - fields fields - args args - want []corev1.ServicePort - wantErr assert.ErrorAssertionFunc + name string + fields fields + args args + want []corev1.ServicePort + wantErr assert.ErrorAssertionFunc + wantBuildErr assert.ErrorAssertionFunc }{ { name: "no options", fields: fields{ name: "receiver1", - opts: nil, + b: components.NewMultiPortReceiverBuilder("receiver1"), }, args: args{ config: nil, }, - want: nil, - wantErr: assert.NoError, + want: nil, + wantBuildErr: assert.NoError, + wantErr: assert.NoError, }, { name: "single port mapping without builder options", fields: fields{ name: "receiver2", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80), - }, + b: components.NewMultiPortReceiverBuilder("receiver2").AddPortMapping(components.NewProtocolBuilder("http", 80)), }, args: args{ config: httpConfig, @@ -158,15 +154,16 @@ func TestMultiPortReceiver_Ports(t *testing.T) { Port: 80, }, }, - wantErr: assert.NoError, + wantBuildErr: assert.NoError, + wantErr: assert.NoError, }, { name: "port mapping with target port", fields: fields{ name: "receiver3", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80, components.WithTargetPort[*components.MultiProtocolEndpointConfig](8080)), - }, + b: components.NewMultiPortReceiverBuilder("receiver3"). + AddPortMapping(components.NewProtocolBuilder("http", 80). + WithTargetPort(8080)), }, args: args{ config: httpConfig, @@ -178,15 +175,16 @@ func TestMultiPortReceiver_Ports(t *testing.T) { TargetPort: intstr.FromInt(80), }, }, - wantErr: assert.NoError, + wantBuildErr: assert.NoError, + wantErr: assert.NoError, }, { name: "port mapping with app protocol", fields: fields{ name: "receiver4", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80, components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol)), - }, + b: components.NewMultiPortReceiverBuilder("receiver4"). + AddPortMapping(components.NewProtocolBuilder("http", 80). + WithAppProtocol(&components.HttpProtocol)), }, args: args{ config: httpConfig, @@ -198,15 +196,16 @@ func TestMultiPortReceiver_Ports(t *testing.T) { AppProtocol: &components.HttpProtocol, }, }, - wantErr: assert.NoError, + wantBuildErr: assert.NoError, + wantErr: assert.NoError, }, { name: "port mapping with protocol", fields: fields{ name: "receiver5", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80, components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP)), - }, + b: components.NewMultiPortReceiverBuilder("receiver2"). + AddPortMapping(components.NewProtocolBuilder("http", 80). + WithProtocol(corev1.ProtocolTCP)), }, args: args{ config: httpConfig, @@ -218,19 +217,20 @@ func TestMultiPortReceiver_Ports(t *testing.T) { Protocol: corev1.ProtocolTCP, }, }, - wantErr: assert.NoError, + wantBuildErr: assert.NoError, + wantErr: assert.NoError, }, { name: "multiple port mappings", fields: fields{ name: "receiver6", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80), - components.WithPortMapping("grpc", 4317, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](4317), - components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol)), - }, + b: components.NewMultiPortReceiverBuilder("receiver6"). + AddPortMapping(components.NewProtocolBuilder("http", 80)). + AddPortMapping(components.NewProtocolBuilder("grpc", 4317). + WithTargetPort(4317). + WithProtocol(corev1.ProtocolTCP). + WithAppProtocol(&components.GrpcProtocol), + ), }, args: args{ config: httpAndGrpcConfig, @@ -248,19 +248,20 @@ func TestMultiPortReceiver_Ports(t *testing.T) { Port: 80, }, }, - wantErr: assert.NoError, + wantBuildErr: assert.NoError, + wantErr: assert.NoError, }, { name: "multiple port mappings only one enabled", fields: fields{ name: "receiver6", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80), - components.WithPortMapping("grpc", 4317, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](4317), - components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol)), - }, + b: components.NewMultiPortReceiverBuilder("receiver6"). + AddPortMapping(components.NewProtocolBuilder("http", 80)). + AddPortMapping(components.NewProtocolBuilder("grpc", 4317). + WithTargetPort(4317). + WithProtocol(corev1.ProtocolTCP). + WithAppProtocol(&components.GrpcProtocol), + ), }, args: args{ config: httpConfig, @@ -271,39 +272,40 @@ func TestMultiPortReceiver_Ports(t *testing.T) { Port: 80, }, }, - wantErr: assert.NoError, + wantBuildErr: assert.NoError, + wantErr: assert.NoError, }, { name: "error unmarshalling configuration", fields: fields{ name: "receiver1", - opts: nil, + b: components.NewMultiPortReceiverBuilder("receiver1"), }, args: args{ config: "invalid config", // Simulate an invalid config that causes LoadMap to fail }, - want: nil, - wantErr: assert.Error, + want: nil, + wantBuildErr: assert.NoError, + wantErr: assert.Error, }, { name: "error marshaling configuration", fields: fields{ name: "receiver1", - opts: nil, + b: components.NewMultiPortReceiverBuilder("receiver1"), }, args: args{ config: func() {}, // Simulate an invalid config that causes LoadMap to fail }, - want: nil, - wantErr: assert.Error, + want: nil, + wantBuildErr: assert.NoError, + wantErr: assert.Error, }, { name: "unknown protocol", fields: fields{ name: "receiver2", - opts: []components.MultiPortOption{ - components.WithPortMapping("http", 80), - }, + b: components.NewMultiPortReceiverBuilder("receiver2").AddPortMapping(components.NewProtocolBuilder("http", 80)), }, args: args{ config: map[string]interface{}{ @@ -312,18 +314,57 @@ func TestMultiPortReceiver_Ports(t *testing.T) { }, }, }, - want: nil, - wantErr: assert.Error, + want: nil, + wantBuildErr: assert.NoError, + wantErr: assert.Error, + }, + { + name: "no name set", + fields: fields{ + name: "receiver2", + b: components.MultiPortBuilder[*components.MultiProtocolEndpointConfig]{}, + }, + args: args{ + config: map[string]interface{}{}, + }, + want: nil, + wantBuildErr: assert.Error, + }, + { + name: "bad builder", + fields: fields{ + name: "receiver2", + b: components.NewMultiPortReceiverBuilder("receiver2").AddPortMapping(components.NewBuilder[*components.MultiProtocolEndpointConfig]()), + }, + args: args{ + config: map[string]interface{}{}, + }, + want: nil, + wantErr: assert.NoError, + wantBuildErr: assert.Error, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - m := components.NewMultiPortReceiver(tt.fields.name, tt.fields.opts...) - got, err := m.Ports(logr.Discard(), tt.fields.name, tt.args.config) + s, err := tt.fields.b.Build() + if tt.wantBuildErr(t, err, fmt.Sprintf("Ports(%v)", tt.args.config)) && err != nil { + return + } + got, err := s.Ports(logr.Discard(), tt.fields.name, tt.args.config) if !tt.wantErr(t, err, fmt.Sprintf("Ports(%v)", tt.args.config)) { return } assert.ElementsMatchf(t, tt.want, got, "Ports(%v)", tt.args.config) + rbacGen, err := s.GetRBACRules(logr.Discard(), tt.args.config) + assert.NoError(t, err) + assert.Nil(t, rbacGen) }) } } + +func TestMultiMustBuildPanics(t *testing.T) { + b := components.MultiPortBuilder[*components.MultiProtocolEndpointConfig]{} + assert.Panics(t, func() { + b.MustBuild() + }) +} diff --git a/internal/components/processors/helpers.go b/internal/components/processors/helpers.go index cf0bd58352..4f3c82c238 100644 --- a/internal/components/processors/helpers.go +++ b/internal/components/processors/helpers.go @@ -35,12 +35,12 @@ func ProcessorFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } - return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort) + return components.NewGenericParser[any](components.ComponentType(name), components.UnsetPort) } var componentParsers = []components.Parser{ - components.NewGenericParser[K8sAttributeConfig]("k8sattributes", components.UnsetPort, components.WithRBACRuleGenerator(GenerateK8SAttrRbacRules)), - components.NewGenericParser[ResourceDetectionConfig]("resourcedetection", components.UnsetPort, components.WithRBACRuleGenerator(GenerateResourceDetectionRbacRules)), + components.NewBuilder[K8sAttributeConfig]().WithName("k8sattributes").WithRbacGen(GenerateK8SAttrRbacRules).MustBuild(), + components.NewBuilder[ResourceDetectionConfig]().WithName("resourcedetection").WithRbacGen(GenerateResourceDetectionRbacRules).MustBuild(), } func init() { diff --git a/internal/components/processors/helpers_test.go b/internal/components/processors/helpers_test.go index 60076faf71..5ef0b40a8f 100644 --- a/internal/components/processors/helpers_test.go +++ b/internal/components/processors/helpers_test.go @@ -17,14 +17,41 @@ package processors_test import ( "testing" + "github.com/go-logr/logr" "github.com/stretchr/testify/assert" logf "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/open-telemetry/opentelemetry-operator/internal/components" "github.com/open-telemetry/opentelemetry-operator/internal/components/processors" ) var logger = logf.Log.WithName("unit-tests") +func TestParserForReturns(t *testing.T) { + const testComponentName = "test" + parser := processors.ProcessorFor(testComponentName) + assert.Equal(t, "test", parser.ParserType()) + assert.Equal(t, "__test", parser.ParserName()) + ports, err := parser.Ports(logr.Discard(), testComponentName, map[string]interface{}{ + "endpoint": "localhost:9000", + }) + assert.NoError(t, err) + assert.Len(t, ports, 0) // Should use the nop parser +} + +func TestCanRegister(t *testing.T) { + const testComponentName = "test" + processors.Register(testComponentName, components.NewSinglePortParserBuilder(testComponentName, 9000).MustBuild()) + assert.True(t, processors.IsRegistered(testComponentName)) + parser := processors.ProcessorFor(testComponentName) + assert.Equal(t, "test", parser.ParserType()) + assert.Equal(t, "__test", parser.ParserName()) + ports, err := parser.Ports(logr.Discard(), testComponentName, map[string]interface{}{}) + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.Equal(t, ports[0].Port, int32(9000)) +} + func TestDownstreamParsers(t *testing.T) { for _, tt := range []struct { desc string diff --git a/internal/components/receivers/helpers.go b/internal/components/receivers/helpers.go index a67def9771..50a7610243 100644 --- a/internal/components/receivers/helpers.go +++ b/internal/components/receivers/helpers.go @@ -39,7 +39,7 @@ func ReceiverFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } - return components.NewSilentSinglePortParser(components.ComponentType(name), components.UnsetPort) + return components.NewSilentSinglePortParserBuilder(components.ComponentType(name), components.UnsetPort).MustBuild() } // NewScraperParser is an instance of a generic parser that returns nothing when called and never fails. @@ -49,58 +49,46 @@ func NewScraperParser(name string) *components.GenericParser[any] { var ( componentParsers = []components.Parser{ - components.NewMultiPortReceiver("otlp", - components.WithPortMapping( - "grpc", - 4317, - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), - components.WithTargetPort[*components.MultiProtocolEndpointConfig](4317), - ), components.WithPortMapping( - "http", - 4318, - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), - components.WithTargetPort[*components.MultiProtocolEndpointConfig](4318), - ), - ), - components.NewMultiPortReceiver("skywalking", - components.WithPortMapping(components.GrpcProtocol, 11800, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](11800), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), - ), - components.WithPortMapping(components.HttpProtocol, 12800, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](12800), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), - )), - components.NewMultiPortReceiver("jaeger", - components.WithPortMapping(components.GrpcProtocol, 14250, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](14250), - components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), - ), - components.WithPortMapping("thrift_http", 14268, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](14268), - components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolTCP), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), - ), - components.WithPortMapping("thrift_compact", 6831, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](6831), - components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolUDP), - ), - components.WithPortMapping("thrift_binary", 6832, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](6832), - components.WithProtocol[*components.MultiProtocolEndpointConfig](corev1.ProtocolUDP), - ), - ), - components.NewMultiPortReceiver("loki", - components.WithPortMapping(components.GrpcProtocol, 9095, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](9095), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.GrpcProtocol), - ), - components.WithPortMapping(components.HttpProtocol, 3100, - components.WithTargetPort[*components.MultiProtocolEndpointConfig](3100), - components.WithAppProtocol[*components.MultiProtocolEndpointConfig](&components.HttpProtocol), - ), - ), + components.NewMultiPortReceiverBuilder("otlp"). + AddPortMapping(components.NewProtocolBuilder("grpc", 4317). + WithAppProtocol(&components.GrpcProtocol). + WithTargetPort(4317)). + AddPortMapping(components.NewProtocolBuilder("http", 4318). + WithAppProtocol(&components.HttpProtocol). + WithTargetPort(4318)). + MustBuild(), + components.NewMultiPortReceiverBuilder("skywalking"). + AddPortMapping(components.NewProtocolBuilder(components.GrpcProtocol, 11800). + WithTargetPort(11800). + WithAppProtocol(&components.GrpcProtocol)). + AddPortMapping(components.NewProtocolBuilder(components.HttpProtocol, 12800). + WithTargetPort(12800). + WithAppProtocol(&components.HttpProtocol)). + MustBuild(), + components.NewMultiPortReceiverBuilder("jaeger"). + AddPortMapping(components.NewProtocolBuilder(components.GrpcProtocol, 14250). + WithTargetPort(14250). + WithProtocol(corev1.ProtocolTCP). + WithAppProtocol(&components.GrpcProtocol)). + AddPortMapping(components.NewProtocolBuilder("thrift_http", 14268). + WithTargetPort(14268). + WithProtocol(corev1.ProtocolTCP). + WithAppProtocol(&components.HttpProtocol)). + AddPortMapping(components.NewProtocolBuilder("thrift_compact", 6831). + WithTargetPort(6831). + WithProtocol(corev1.ProtocolUDP)). + AddPortMapping(components.NewProtocolBuilder("thrift_binary", 6832). + WithTargetPort(6832). + WithProtocol(corev1.ProtocolUDP)). + MustBuild(), + components.NewMultiPortReceiverBuilder("loki"). + AddPortMapping(components.NewProtocolBuilder(components.GrpcProtocol, 9095). + WithTargetPort(9095). + WithAppProtocol(&components.GrpcProtocol)). + AddPortMapping(components.NewProtocolBuilder(components.HttpProtocol, 3100). + WithTargetPort(3100). + WithAppProtocol(&components.HttpProtocol)). + MustBuild(), components.NewSinglePortParserBuilder("awsxray", 2000). WithTargetPort(2000). MustBuild(), diff --git a/internal/components/single_endpoint.go b/internal/components/single_endpoint.go index 7c20e39cbf..914136b568 100644 --- a/internal/components/single_endpoint.go +++ b/internal/components/single_endpoint.go @@ -79,15 +79,6 @@ func internalParseSingleEndpoint(logger logr.Logger, name string, failSilently b return []corev1.ServicePort{ConstructServicePort(svcPort, port)}, nil } -func NewSinglePortParser(name string, port int32, opts ...ParserOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { - return NewGenericParser(name, port, append(opts, WithPortParser(ParseSingleEndpoint))...) -} - -// NewSilentSinglePortParser returns a ParseSingleEndpoint that errors silently on failure to find a port. -func NewSilentSinglePortParser(name string, port int32, opts ...ParserOption[*SingleEndpointConfig]) *GenericParser[*SingleEndpointConfig] { - return NewGenericParser(name, port, append(opts, WithPortParser(ParseSingleEndpointSilent))...) -} - func NewSinglePortParserBuilder(name string, port int32) Builder[*SingleEndpointConfig] { return NewBuilder[*SingleEndpointConfig]().WithPort(port).WithName(name).WithPortParser(ParseSingleEndpoint) } diff --git a/internal/components/single_endpoint_test.go b/internal/components/single_endpoint_test.go index 6b952e7eb7..bf0757d835 100644 --- a/internal/components/single_endpoint_test.go +++ b/internal/components/single_endpoint_test.go @@ -109,9 +109,7 @@ func TestSingleEndpointConfig_GetPortNumOrDefault(t *testing.T) { func TestSingleEndpointParser_ParserName(t *testing.T) { type fields struct { - name string - port int32 - opts []components.ParserOption[*components.SingleEndpointConfig] + b components.Builder[*components.SingleEndpointConfig] } tests := []struct { name string @@ -121,25 +119,22 @@ func TestSingleEndpointParser_ParserName(t *testing.T) { { name: "no options", fields: fields{ - name: "receiver1", - opts: nil, + b: components.NewSinglePortParserBuilder("receiver1", components.UnsetPort), }, want: "__receiver1", }, { name: "with port mapping without builder options", fields: fields{ - name: "receiver2", - opts: []components.ParserOption[*components.SingleEndpointConfig]{ - components.WithTargetPort[*components.SingleEndpointConfig](8080), - }, + b: components.NewSinglePortParserBuilder("receiver2", components.UnsetPort).WithTargetPort(8080), }, want: "__receiver2", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := components.NewSinglePortParser(tt.fields.name, tt.fields.port, tt.fields.opts...) + s, err := tt.fields.b.Build() + assert.NoError(t, err) assert.Equalf(t, tt.want, s.ParserName(), "ParserName()") }) } @@ -147,9 +142,7 @@ func TestSingleEndpointParser_ParserName(t *testing.T) { func TestSingleEndpointParser_ParserType(t *testing.T) { type fields struct { - name string - port int32 - opts []components.ParserOption[*components.SingleEndpointConfig] + b components.Builder[*components.SingleEndpointConfig] } tests := []struct { name string @@ -159,25 +152,22 @@ func TestSingleEndpointParser_ParserType(t *testing.T) { { name: "no options", fields: fields{ - name: "receiver1", - opts: nil, + b: components.NewSinglePortParserBuilder("receiver1", components.UnsetPort), }, want: "receiver1", }, { name: "with port mapping without builder options", fields: fields{ - name: "receiver2/test", - opts: []components.ParserOption[*components.SingleEndpointConfig]{ - components.WithTargetPort[*components.SingleEndpointConfig](80), - }, + b: components.NewSinglePortParserBuilder("receiver2", components.UnsetPort).WithTargetPort(8080), }, want: "receiver2", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := components.NewSinglePortParser(tt.fields.name, tt.fields.port, tt.fields.opts...) + s, err := tt.fields.b.Build() + assert.NoError(t, err) assert.Equalf(t, tt.want, s.ParserType(), "ParserType()") }) } @@ -185,9 +175,7 @@ func TestSingleEndpointParser_ParserType(t *testing.T) { func TestSingleEndpointParser_Ports(t *testing.T) { type fields struct { - name string - port int32 - opts []components.ParserOption[*components.SingleEndpointConfig] + b components.Builder[*components.SingleEndpointConfig] } type args struct { config interface{} @@ -202,8 +190,7 @@ func TestSingleEndpointParser_Ports(t *testing.T) { { name: "ValidConfigWithPort", fields: fields{ - name: "testparser", - port: 8080, + b: components.NewSinglePortParserBuilder("testparser", 8080), }, args: args{ config: map[string]interface{}{ @@ -215,11 +202,21 @@ func TestSingleEndpointParser_Ports(t *testing.T) { }, wantErr: assert.NoError, }, + { + name: "ValidConfigWithPort nil config", + fields: fields{ + b: components.NewSinglePortParserBuilder("testparser", 8080), + }, + args: args{ + config: nil, + }, + want: nil, + wantErr: assert.NoError, + }, { name: "ValidConfigWithDefaultPort", fields: fields{ - name: "testparser", - port: 8080, + b: components.NewSinglePortParserBuilder("testparser", 8080), }, args: args{ config: map[string]interface{}{}, @@ -232,13 +229,10 @@ func TestSingleEndpointParser_Ports(t *testing.T) { { name: "ConfigWithFixins", fields: fields{ - name: "testparser", - port: 8080, - opts: []components.ParserOption[*components.SingleEndpointConfig]{ - components.WithTargetPort[*components.SingleEndpointConfig](4317), - components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), - components.WithAppProtocol[*components.SingleEndpointConfig](&components.GrpcProtocol), - }, + b: components.NewSinglePortParserBuilder("testparser", 8080). + WithTargetPort(4317). + WithProtocol(corev1.ProtocolTCP). + WithAppProtocol(&components.GrpcProtocol), }, args: args{ config: map[string]interface{}{}, @@ -257,8 +251,7 @@ func TestSingleEndpointParser_Ports(t *testing.T) { { name: "InvalidConfigMissingPort", fields: fields{ - name: "testparser", - port: 0, + b: components.NewSinglePortParserBuilder("testparser", components.UnsetPort), }, args: args{ config: map[string]interface{}{ @@ -271,8 +264,7 @@ func TestSingleEndpointParser_Ports(t *testing.T) { { name: "ErrorParsingConfig", fields: fields{ - name: "testparser", - port: 8080, + b: components.NewSinglePortParserBuilder("testparser", components.UnsetPort), }, args: args{ config: "invalid config", @@ -283,8 +275,9 @@ func TestSingleEndpointParser_Ports(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := components.NewSinglePortParser(tt.fields.name, tt.fields.port, tt.fields.opts...) - got, err := s.Ports(logr.Discard(), tt.fields.name, tt.args.config) + s, err := tt.fields.b.Build() + assert.NoError(t, err) + got, err := s.Ports(logr.Discard(), s.ParserType(), tt.args.config) if !tt.wantErr(t, err, fmt.Sprintf("Ports(%v)", tt.args.config)) { return } @@ -294,10 +287,9 @@ func TestSingleEndpointParser_Ports(t *testing.T) { } func TestNewSilentSinglePortParser_Ports(t *testing.T) { + type fields struct { - name string - port int32 - opts []components.ParserOption[*components.SingleEndpointConfig] + b components.Builder[*components.SingleEndpointConfig] } type args struct { config interface{} @@ -312,8 +304,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { { name: "ValidConfigWithPort", fields: fields{ - name: "testparser", - port: 8080, + b: components.NewSilentSinglePortParserBuilder("testparser", 8080), }, args: args{ config: map[string]interface{}{ @@ -328,8 +319,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { { name: "ValidConfigWithDefaultPort", fields: fields{ - name: "testparser", - port: 8080, + b: components.NewSilentSinglePortParserBuilder("testparser", 8080), }, args: args{ config: map[string]interface{}{}, @@ -342,13 +332,10 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { { name: "ConfigWithFixins", fields: fields{ - name: "testparser", - port: 8080, - opts: []components.ParserOption[*components.SingleEndpointConfig]{ - components.WithTargetPort[*components.SingleEndpointConfig](4317), - components.WithProtocol[*components.SingleEndpointConfig](corev1.ProtocolTCP), - components.WithAppProtocol[*components.SingleEndpointConfig](&components.GrpcProtocol), - }, + b: components.NewSilentSinglePortParserBuilder("testparser", 8080). + WithTargetPort(4317). + WithProtocol(corev1.ProtocolTCP). + WithAppProtocol(&components.GrpcProtocol), }, args: args{ config: map[string]interface{}{}, @@ -367,8 +354,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { { name: "InvalidConfigMissingPort", fields: fields{ - name: "testparser", - port: 0, + b: components.NewSilentSinglePortParserBuilder("testparser", components.UnsetPort), }, args: args{ config: map[string]interface{}{ @@ -381,8 +367,7 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { { name: "ErrorParsingConfig", fields: fields{ - name: "testparser", - port: 8080, + b: components.NewSilentSinglePortParserBuilder("testparser", 8080), }, args: args{ config: "invalid config", @@ -393,8 +378,9 @@ func TestNewSilentSinglePortParser_Ports(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := components.NewSilentSinglePortParser(tt.fields.name, tt.fields.port, tt.fields.opts...) - got, err := s.Ports(logr.Discard(), tt.fields.name, tt.args.config) + s, err := tt.fields.b.Build() + assert.NoError(t, err) + got, err := s.Ports(logr.Discard(), s.ParserType(), tt.args.config) if !tt.wantErr(t, err, fmt.Sprintf("Ports(%v)", tt.args.config)) { return } From b6f8027f270847ba824cc8bb054c756810d4ef51 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Wed, 21 Aug 2024 16:10:36 -0400 Subject: [PATCH 09/18] remove unused methods --- internal/components/builder.go | 7 ------- internal/components/exporters/helpers.go | 2 +- internal/components/generic_parser.go | 5 ----- internal/components/generic_parser_test.go | 2 +- internal/components/processors/helpers.go | 2 +- 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/internal/components/builder.go b/internal/components/builder.go index 98ba217010..9ffb49e840 100644 --- a/internal/components/builder.go +++ b/internal/components/builder.go @@ -40,13 +40,6 @@ func NewEmptyOption[T any]() *Option[T] { return &Option[T]{} } -func NewOption[T any](name string, port int32) *Option[T] { - return &Option[T]{ - name: name, - port: port, - } -} - func (o *Option[T]) Apply(opts ...ParserOption[T]) { for _, opt := range opts { opt(o) diff --git a/internal/components/exporters/helpers.go b/internal/components/exporters/helpers.go index bf11f1dc1e..82108c5fc0 100644 --- a/internal/components/exporters/helpers.go +++ b/internal/components/exporters/helpers.go @@ -38,7 +38,7 @@ func ParserFor(name string) components.Parser { return parser } // We want the default for exporters to fail silently. - return components.NewGenericParser[any](components.ComponentType(name), components.UnsetPort) + return components.NewBuilder[any]().WithName(name).MustBuild() } var ( diff --git a/internal/components/generic_parser.go b/internal/components/generic_parser.go index f0a33be21f..9f0eed609c 100644 --- a/internal/components/generic_parser.go +++ b/internal/components/generic_parser.go @@ -65,8 +65,3 @@ func (g *GenericParser[T]) ParserType() string { func (g *GenericParser[T]) ParserName() string { return fmt.Sprintf("__%s", g.name) } - -func NewGenericParser[T any](name string, port int32) *GenericParser[T] { - o := NewOption[T](name, port) - return &GenericParser[T]{name: name, portParser: o.portParser, rbacGen: o.rbacGen, option: o} -} diff --git a/internal/components/generic_parser_test.go b/internal/components/generic_parser_test.go index 20c553d057..549771ff8c 100644 --- a/internal/components/generic_parser_test.go +++ b/internal/components/generic_parser_test.go @@ -190,7 +190,7 @@ func TestGenericParser_GetRBACRules(t *testing.T) { }, { name: "Generic works", - g: components.NewGenericParser[*components.SingleEndpointConfig]("test", 0), + g: components.NewBuilder[*components.SingleEndpointConfig]().WithName("test").MustBuild(), args: args{ logger: logr.Discard(), config: map[string]interface{}{}, diff --git a/internal/components/processors/helpers.go b/internal/components/processors/helpers.go index 4f3c82c238..ab1277b186 100644 --- a/internal/components/processors/helpers.go +++ b/internal/components/processors/helpers.go @@ -35,7 +35,7 @@ func ProcessorFor(name string) components.Parser { if parser, ok := registry[components.ComponentType(name)]; ok { return parser } - return components.NewGenericParser[any](components.ComponentType(name), components.UnsetPort) + return components.NewBuilder[any]().WithName(name).MustBuild() } var componentParsers = []components.Parser{ From cd909d0b05eb6826c6d114afb5ae47c6838c38f6 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Mon, 26 Aug 2024 12:37:35 -0400 Subject: [PATCH 10/18] fix unit tests --- internal/components/builder_test.go | 101 ++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 20 deletions(-) diff --git a/internal/components/builder_test.go b/internal/components/builder_test.go index 7a1c19d1ff..d600db4a70 100644 --- a/internal/components/builder_test.go +++ b/internal/components/builder_test.go @@ -44,11 +44,12 @@ func TestBuilder_Build(t *testing.T) { conf interface{} } type testCase[T any] struct { - name string - fields fields[T] - params params - want want - wantErr assert.ErrorAssertionFunc + name string + fields fields[T] + params params + want want + wantErr assert.ErrorAssertionFunc + wantRbacErr assert.ErrorAssertionFunc } examplePortParser := func(logger logr.Logger, name string, defaultPort *corev1.ServicePort, config sampleConfig) ([]corev1.ServicePort, error) { if defaultPort != nil { @@ -82,7 +83,8 @@ func TestBuilder_Build(t *testing.T) { }, rules: nil, }, - wantErr: assert.NoError, + wantErr: assert.NoError, + wantRbacErr: assert.NoError, }, { name: "missing name", @@ -94,8 +96,9 @@ func TestBuilder_Build(t *testing.T) { params: params{ conf: sampleConfig{}, }, - want: want{}, - wantErr: assert.Error, + want: want{}, + wantErr: assert.Error, + wantRbacErr: assert.NoError, }, { name: "complete configuration with RBAC rules", @@ -105,44 +108,102 @@ func TestBuilder_Build(t *testing.T) { WithPort(443). WithProtocol(corev1.ProtocolTCP). WithRbacGen(func(logger logr.Logger, config sampleConfig) ([]rbacv1.PolicyRule, error) { - return []rbacv1.PolicyRule{ + rules := []rbacv1.PolicyRule{ { + NonResourceURLs: []string{config.example}, + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + } + if config.number > 100 { + rules = append(rules, rbacv1.PolicyRule{ APIGroups: []string{""}, - Resources: []string{"pods"}, + Resources: []string{"nodes"}, Verbs: []string{"get", "list"}, - }, - }, nil + }) + } + return rules, nil }), }, params: params{ - conf: sampleConfig{}, + conf: sampleConfig{ + example: "test", + number: 100, + m: map[string]interface{}{ + "key": "value", + }, + }, }, want: want{ name: "__secure-service", ports: nil, rules: []rbacv1.PolicyRule{ { - APIGroups: []string{""}, - Resources: []string{"pods"}, - Verbs: []string{"get", "list"}, + NonResourceURLs: []string{"test"}, + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, }, }, }, - wantErr: assert.NoError, + wantErr: assert.NoError, + wantRbacErr: assert.NoError, + }, + { + name: "complete configuration with RBAC rules errors", + fields: fields[sampleConfig]{ + b: components.NewBuilder[sampleConfig](). + WithName("secure-service"). + WithPort(443). + WithProtocol(corev1.ProtocolTCP). + WithRbacGen(func(logger logr.Logger, config sampleConfig) ([]rbacv1.PolicyRule, error) { + rules := []rbacv1.PolicyRule{ + { + NonResourceURLs: []string{config.example}, + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + } + if v, ok := config.m["key"]; ok && v == "value" { + return nil, fmt.Errorf("errors from function") + } + return rules, nil + }), + }, + params: params{ + conf: sampleConfig{ + example: "test", + number: 100, + m: map[string]interface{}{ + "key": "value", + }, + }, + }, + want: want{ + name: "__secure-service", + ports: nil, + rules: nil, + }, + wantErr: assert.NoError, + wantRbacErr: assert.Error, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := tt.fields.b.Build() - if tt.wantErr(t, err, fmt.Sprintf("WantErr()")) && err != nil { + if tt.wantErr(t, err, "WantErr()") && err != nil { return } assert.Equalf(t, tt.want.name, got.ParserName(), "ParserName()") ports, err := got.Ports(logr.Discard(), got.ParserType(), tt.params.conf) assert.NoError(t, err) assert.Equalf(t, tt.want.ports, ports, "Ports()") - rules, err := got.GetRBACRules(logr.Discard(), tt.params.conf) - assert.NoError(t, err) + rules, rbacErr := got.GetRBACRules(logr.Discard(), tt.params.conf) + if tt.wantRbacErr(t, rbacErr, "WantRbacErr()") && rbacErr != nil { + return + } assert.Equalf(t, tt.want.rules, rules, "GetRBACRules()") }) } From 882947dc8fdd86f104f884aa278a409f3925afad Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 30 Aug 2024 15:43:46 -0400 Subject: [PATCH 11/18] Delete dead code --- .../collector/adapters/config_to_rbac.go | 63 ------ .../collector/adapters/config_to_rbac_test.go | 100 ---------- .../collector/parser/exporter/exporter.go | 128 ------------- .../parser/exporter/exporter_prometheus.go | 77 -------- .../parser/exporter/exporter_test.go | 79 -------- internal/manifests/collector/parser/parser.go | 31 --- .../collector/parser/processor/processor.go | 73 ------- .../processor/processor_k8sattributes.go | 106 ----------- .../processor/processor_k8sattributes_test.go | 144 -------------- .../processor/processor_resourcedetection.go | 87 --------- .../collector/parser/receiver/receiver.go | 180 ------------------ .../parser/receiver/receiver_aws-xray.go | 38 ---- .../parser/receiver/receiver_aws-xray_test.go | 17 -- .../parser/receiver/receiver_carbon.go | 38 ---- .../parser/receiver/receiver_carbon_test.go | 17 -- .../parser/receiver/receiver_collectd.go | 38 ---- .../parser/receiver/receiver_collectd_test.go | 17 -- .../receiver/receiver_fluent-forward.go | 38 ---- .../receiver/receiver_fluent-forward_test.go | 17 -- .../parser/receiver/receiver_generic.go | 77 -------- .../parser/receiver/receiver_generic_test.go | 125 ------------ .../parser/receiver/receiver_influxdb.go | 38 ---- .../parser/receiver/receiver_influxdb_test.go | 17 -- .../parser/receiver/receiver_jaeger.go | 138 -------------- .../parser/receiver/receiver_jaeger_test.go | 115 ----------- .../parser/receiver/receiver_loki.go | 131 ------------- .../parser/receiver/receiver_loki_test.go | 109 ----------- .../collector/parser/receiver/receiver_oc.go | 40 ---- .../parser/receiver/receiver_oc_test.go | 17 -- .../parser/receiver/receiver_otlp.go | 136 ------------- .../parser/receiver/receiver_otlp_test.go | 109 ----------- .../parser/receiver/receiver_sapm.go | 38 ---- .../parser/receiver/receiver_sapm_test.go | 17 -- .../parser/receiver/receiver_signalfx.go | 38 ---- .../parser/receiver/receiver_signalfx_test.go | 17 -- .../parser/receiver/receiver_skywalking.go | 131 ------------- .../receiver/receiver_skywalking_test.go | 109 ----------- .../parser/receiver/receiver_splunk-hec.go | 38 ---- .../receiver/receiver_splunk-hec_test.go | 17 -- .../parser/receiver/receiver_statsd.go | 40 ---- .../parser/receiver/receiver_statsd_test.go | 17 -- .../parser/receiver/receiver_syslog.go | 98 ---------- .../parser/receiver/receiver_syslog_test.go | 64 ------- .../parser/receiver/receiver_tcplog.go | 79 -------- .../parser/receiver/receiver_tcplog_test.go | 61 ------ .../parser/receiver/receiver_test.go | 164 ---------------- .../parser/receiver/receiver_udplog.go | 79 -------- .../parser/receiver/receiver_udplog_test.go | 61 ------ .../parser/receiver/receiver_wavefront.go | 38 ---- .../receiver/receiver_wavefront_test.go | 17 -- .../parser/receiver/receiver_zipkin-scribe.go | 38 ---- .../receiver/receiver_zipkin-scribe_test.go | 17 -- .../parser/receiver/receiver_zipkin.go | 42 ---- .../parser/receiver/receiver_zipkin_test.go | 17 -- 54 files changed, 3577 deletions(-) delete mode 100644 internal/manifests/collector/adapters/config_to_rbac.go delete mode 100644 internal/manifests/collector/adapters/config_to_rbac_test.go delete mode 100644 internal/manifests/collector/parser/exporter/exporter.go delete mode 100644 internal/manifests/collector/parser/exporter/exporter_prometheus.go delete mode 100644 internal/manifests/collector/parser/exporter/exporter_test.go delete mode 100644 internal/manifests/collector/parser/parser.go delete mode 100644 internal/manifests/collector/parser/processor/processor.go delete mode 100644 internal/manifests/collector/parser/processor/processor_k8sattributes.go delete mode 100644 internal/manifests/collector/parser/processor/processor_k8sattributes_test.go delete mode 100644 internal/manifests/collector/parser/processor/processor_resourcedetection.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_aws-xray.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_aws-xray_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_carbon.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_carbon_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_collectd.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_collectd_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_fluent-forward.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_fluent-forward_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_generic.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_generic_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_influxdb.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_influxdb_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_jaeger.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_jaeger_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_loki.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_loki_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_oc.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_oc_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_otlp.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_otlp_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_sapm.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_sapm_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_signalfx.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_signalfx_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_skywalking.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_skywalking_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_splunk-hec.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_splunk-hec_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_statsd.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_statsd_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_syslog.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_syslog_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_tcplog.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_tcplog_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_udplog.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_udplog_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_wavefront.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_wavefront_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_zipkin-scribe.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_zipkin-scribe_test.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_zipkin.go delete mode 100644 internal/manifests/collector/parser/receiver/receiver_zipkin_test.go diff --git a/internal/manifests/collector/adapters/config_to_rbac.go b/internal/manifests/collector/adapters/config_to_rbac.go deleted file mode 100644 index 55fd2d9bef..0000000000 --- a/internal/manifests/collector/adapters/config_to_rbac.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adapters - -import ( - "github.com/go-logr/logr" - rbacv1 "k8s.io/api/rbac/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/processor" -) - -// ConfigToRBAC parses the OpenTelemetry Collector configuration and checks what RBAC resources are needed to be created. -func ConfigToRBAC(logger logr.Logger, config map[interface{}]interface{}) []rbacv1.PolicyRule { - var policyRules []rbacv1.PolicyRule - processorsRaw, ok := config["processors"] - if !ok { - logger.V(2).Info("no processors available as part of the configuration") - return policyRules - } - - processors, ok := processorsRaw.(map[interface{}]interface{}) - if !ok { - logger.V(2).Info("processors doesn't contain valid components") - return policyRules - } - - enabledProcessors := getEnabledComponents(config, ComponentTypeProcessor) - - for key, val := range processors { - if !enabledProcessors[key] { - continue - } - - processorCfg, ok := val.(map[interface{}]interface{}) - if !ok { - logger.V(2).Info("processor doesn't seem to be a map of properties", "processor", key) - processorCfg = map[interface{}]interface{}{} - } - - processorName := key.(string) - processorParser, err := processor.For(logger, processorName, processorCfg) - if err != nil { - logger.V(2).Info("no parser found for", "processor", processorName) - continue - } - - policyRules = append(policyRules, processorParser.GetRBACRules()...) - } - - return policyRules -} diff --git a/internal/manifests/collector/adapters/config_to_rbac_test.go b/internal/manifests/collector/adapters/config_to_rbac_test.go deleted file mode 100644 index 8260d23648..0000000000 --- a/internal/manifests/collector/adapters/config_to_rbac_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adapters - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - rbacv1 "k8s.io/api/rbac/v1" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -func TestConfigRBAC(t *testing.T) { - tests := []struct { - desc string - config string - expectedRules []rbacv1.PolicyRule - }{ - { - desc: "No processors", - config: `processors: -service: - traces: - processors:`, - expectedRules: ([]rbacv1.PolicyRule)(nil), - }, - { - desc: "processors no rbac", - config: `processors: - batch: -service: - pipelines: - traces: - processors: [batch]`, - expectedRules: ([]rbacv1.PolicyRule)(nil), - }, - { - desc: "resourcedetection-processor k8s", - config: `processors: - resourcedetection: - detectors: [k8snode] -service: - pipelines: - traces: - processors: [resourcedetection]`, - expectedRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"nodes"}, - Verbs: []string{"get", "list"}, - }, - }, - }, - { - desc: "resourcedetection-processor openshift", - config: `processors: - resourcedetection: - detectors: [openshift] -service: - pipelines: - traces: - processors: [resourcedetection]`, - expectedRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"config.openshift.io"}, - Resources: []string{"infrastructures", "infrastructures/status"}, - Verbs: []string{"get", "watch", "list"}, - }, - }, - }, - } - - var logger = logf.Log.WithName("collector-unit-tests") - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - config, err := ConfigFromString(tt.config) - require.NoError(t, err, tt.desc) - require.NotEmpty(t, config, tt.desc) - - // test - rules := ConfigToRBAC(logger, config) - assert.NoError(t, err) - assert.Equal(t, tt.expectedRules, rules, tt.desc) - }) - } -} diff --git a/internal/manifests/collector/parser/exporter/exporter.go b/internal/manifests/collector/parser/exporter/exporter.go deleted file mode 100644 index 93c66b8599..0000000000 --- a/internal/manifests/collector/parser/exporter/exporter.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package parser is for parsing the OpenTelemetry Collector configuration. -package exporter - -import ( - "errors" - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -// registry holds a record of all known exporter parsers. -var registry = make(map[string]parser.Builder) - -// BuilderFor returns a parser builder for the given exporter name. -func BuilderFor(name string) parser.Builder { - return registry[exporterType(name)] -} - -// For returns a new parser for the given exporter name + config. -func For(logger logr.Logger, name string, config map[interface{}]interface{}) (parser.ComponentPortParser, error) { - builder := BuilderFor(name) - if builder == nil { - return nil, fmt.Errorf("no builders for %s", name) - } - return builder(logger, name, config), nil -} - -// Register adds a new parser builder to the list of known builders. -func Register(name string, builder parser.Builder) { - registry[name] = builder -} - -// IsRegistered checks whether a parser is registered with the given name. -func IsRegistered(name string) bool { - _, ok := registry[name] - return ok -} - -var ( - endpointKey = "endpoint" -) - -func singlePortFromConfigEndpoint(logger logr.Logger, name string, config map[interface{}]interface{}) *corev1.ServicePort { - endpoint := getAddressFromConfig(logger, name, endpointKey, config) - - switch e := endpoint.(type) { - case nil: - break - case string: - port, err := portFromEndpoint(e) - if err != nil { - logger.WithValues(endpointKey, e).Error(err, "couldn't parse the endpoint's port") - return nil - } - - return &corev1.ServicePort{ - Name: naming.PortName(name, port), - Port: port, - } - default: - logger.WithValues(endpointKey, endpoint).Error(fmt.Errorf("unrecognized type %T", endpoint), "exporter's endpoint isn't a string") - } - - return nil -} - -func getAddressFromConfig(logger logr.Logger, name, key string, config map[interface{}]interface{}) interface{} { - endpoint, ok := config[key] - if !ok { - logger.V(2).Info("%s exporter doesn't have an %s", name, key) - return nil - } - return endpoint -} - -func portFromEndpoint(endpoint string) (int32, error) { - var err error - var port int64 - - r := regexp.MustCompile(":[0-9]+") - - if r.MatchString(endpoint) { - port, err = strconv.ParseInt(strings.Replace(r.FindString(endpoint), ":", "", -1), 10, 32) - - if err != nil { - return 0, err - } - } - - if port == 0 { - return 0, errors.New("port should not be empty") - } - - return int32(port), err -} - -func exporterType(name string) string { - // exporters have a name like: - // - myexporter/custom - // - myexporter - // we extract the "myexporter" part and see if we have a parser for the exporter - if strings.Contains(name, "/") { - return name[:strings.Index(name, "/")] - } - - return name -} diff --git a/internal/manifests/collector/parser/exporter/exporter_prometheus.go b/internal/manifests/collector/parser/exporter/exporter_prometheus.go deleted file mode 100644 index 30047de70e..0000000000 --- a/internal/manifests/collector/parser/exporter/exporter_prometheus.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package exporter - -import ( - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &PrometheusExporterParser{} - -const ( - parserNamePrometheus = "__prometheus" - defaultPrometheusPort = 8888 -) - -// PrometheusExporterParser parses the configuration for OTLP receivers. -type PrometheusExporterParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewPrometheusExporterParser builds a new parser for OTLP receivers. -func NewPrometheusExporterParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &PrometheusExporterParser{ - logger: logger, - name: name, - config: config, - } -} - -// Ports returns all the service ports for all protocols in this parser. -func (o *PrometheusExporterParser) Ports() ([]corev1.ServicePort, error) { - ports := []corev1.ServicePort{} - if o.config == nil { - ports = append(ports, - corev1.ServicePort{ - Name: naming.PortName(o.name, defaultPrometheusPort), - Port: defaultPrometheusPort, - TargetPort: intstr.FromInt(int(defaultPrometheusPort)), - Protocol: corev1.ProtocolTCP, - }, - ) - } else { - if port := singlePortFromConfigEndpoint(o.logger, o.name, o.config); port != nil { - ports = append(ports, *port) - } - } - - return ports, nil -} - -// ParserName returns the name of this parser. -func (o *PrometheusExporterParser) ParserName() string { - return parserNamePrometheus -} - -func init() { - Register("prometheus", NewPrometheusExporterParser) -} diff --git a/internal/manifests/collector/parser/exporter/exporter_test.go b/internal/manifests/collector/parser/exporter/exporter_test.go deleted file mode 100644 index bc468be110..0000000000 --- a/internal/manifests/collector/parser/exporter/exporter_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package exporter - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -func TestPorts(t *testing.T) { - tests := []struct { - testName string - parser *PrometheusExporterParser - want []v1.ServicePort - }{ - { - testName: "Valid Configuration", - parser: &PrometheusExporterParser{ - name: "test-exporter", - config: map[interface{}]interface{}{ - "endpoint": "http://myprometheus.io:9090", - }, - }, - want: []v1.ServicePort{ - { - Name: "test-exporter", - Port: 9090, - }, - }, - }, - { - testName: "Empty Configuration", - parser: &PrometheusExporterParser{ - name: "test-exporter", - config: nil, // Simulate no configuration provided - }, - want: []v1.ServicePort{ - { - Name: "test-exporter", - Port: defaultPrometheusPort, - TargetPort: intstr.FromInt(int(defaultPrometheusPort)), - Protocol: v1.ProtocolTCP, - }, - }, - }, - { - testName: "Invalid Endpoint No Port", - parser: &PrometheusExporterParser{ - name: "test-exporter", - config: map[interface{}]interface{}{ - "endpoint": "invalidendpoint", - }, - }, - want: []v1.ServicePort{}, - }, - } - - for _, tt := range tests { - t.Run(tt.testName, func(t *testing.T) { - ports, _ := tt.parser.Ports() - assert.Equal(t, tt.want, ports) - }) - } -} diff --git a/internal/manifests/collector/parser/parser.go b/internal/manifests/collector/parser/parser.go deleted file mode 100644 index 62de283b15..0000000000 --- a/internal/manifests/collector/parser/parser.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package parser - -import ( - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" -) - -type ComponentPortParser interface { - // Ports returns the service ports parsed based on the exporter's configuration - Ports() ([]corev1.ServicePort, error) - - // ParserName returns the name of this parser - ParserName() string -} - -// Builder specifies the signature required for parser builders. -type Builder func(logr.Logger, string, map[interface{}]interface{}) ComponentPortParser diff --git a/internal/manifests/collector/parser/processor/processor.go b/internal/manifests/collector/parser/processor/processor.go deleted file mode 100644 index 784fffeade..0000000000 --- a/internal/manifests/collector/parser/processor/processor.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package parser is for parsing the OpenTelemetry Collector configuration. -package processor - -import ( - "fmt" - "strings" - - "github.com/go-logr/logr" - rbacv1 "k8s.io/api/rbac/v1" -) - -// ProcessorParser specifies the methods to implement to parse a processor. -type ProcessorParser interface { - ParserName() string - GetRBACRules() []rbacv1.PolicyRule -} - -// Builder specifies the signature required for parser builders. -type Builder func(logr.Logger, string, map[interface{}]interface{}) ProcessorParser - -// registry holds a record of all known processor parsers. -var registry = make(map[string]Builder) - -// BuilderFor returns a parser builder for the given processor name. -func BuilderFor(name string) Builder { - return registry[processorType(name)] -} - -// For returns a new parser for the given processor name + config. -func For(logger logr.Logger, name string, config map[interface{}]interface{}) (ProcessorParser, error) { - builder := BuilderFor(name) - if builder == nil { - return nil, fmt.Errorf("no builders for %s", name) - } - return builder(logger, name, config), nil -} - -// Register adds a new parser builder to the list of known builders. -func Register(name string, builder Builder) { - registry[name] = builder -} - -// IsRegistered checks whether a parser is registered with the given name. -func IsRegistered(name string) bool { - _, ok := registry[name] - return ok -} - -func processorType(name string) string { - // processors have a name like: - // - myprocessor/custom - // - myprocessor - // we extract the "myprocessor" part and see if we have a parser for the processor - if strings.Contains(name, "/") { - return name[:strings.Index(name, "/")] - } - - return name -} diff --git a/internal/manifests/collector/parser/processor/processor_k8sattributes.go b/internal/manifests/collector/parser/processor/processor_k8sattributes.go deleted file mode 100644 index 76fb45e08f..0000000000 --- a/internal/manifests/collector/parser/processor/processor_k8sattributes.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package processor - -import ( - "fmt" - "strings" - - "github.com/go-logr/logr" - rbacv1 "k8s.io/api/rbac/v1" -) - -var _ ProcessorParser = &K8sAttributesParser{} - -const ( - parserNameK8sAttributes = "__k8sattributes" -) - -// PrometheusExporterParser parses the configuration for k8sattributes processor. -type K8sAttributesParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewK8sAttributesParser builds a new parser k8sattributes processor. -func NewK8sAttributesParser(logger logr.Logger, name string, config map[interface{}]interface{}) ProcessorParser { - return &K8sAttributesParser{ - logger: logger, - name: name, - config: config, - } -} - -// ParserName returns the name of this parser. -func (o *K8sAttributesParser) ParserName() string { - return parserNameK8sAttributes -} - -func (o *K8sAttributesParser) GetRBACRules() []rbacv1.PolicyRule { - // These policies need to be added always - var prs []rbacv1.PolicyRule = []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods", "namespaces"}, - Verbs: []string{"get", "watch", "list"}, - }, - } - - replicasetPolicy := rbacv1.PolicyRule{ - APIGroups: []string{"apps"}, - Resources: []string{"replicasets"}, - Verbs: []string{"get", "watch", "list"}, - } - - extractCfg, ok := o.config["extract"] - if !ok { - // k8s.deployment.name is enabled by default so, replicasets permissions are needed - // https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/32248#discussion_r1560077826 - prs = append(prs, replicasetPolicy) - return prs - } - - metadataCfg, ok := extractCfg.(map[interface{}]interface{})["metadata"] - if !ok { - return prs - } - - metadata, ok := metadataCfg.([]interface{}) - if !ok { - return prs - } - - for _, m := range metadata { - metadataField := fmt.Sprint(m) - if metadataField == "k8s.deployment.uid" || metadataField == "k8s.deployment.name" { - prs = append(prs, replicasetPolicy) - } else if strings.Contains(metadataField, "k8s.node") { - prs = append(prs, - rbacv1.PolicyRule{ - APIGroups: []string{""}, - Resources: []string{"nodes"}, - Verbs: []string{"get", "watch", "list"}, - }, - ) - } - } - - return prs -} - -func init() { - Register("k8sattributes", NewK8sAttributesParser) -} diff --git a/internal/manifests/collector/parser/processor/processor_k8sattributes_test.go b/internal/manifests/collector/parser/processor/processor_k8sattributes_test.go deleted file mode 100644 index 7274ffaa3d..0000000000 --- a/internal/manifests/collector/parser/processor/processor_k8sattributes_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package processor - -import ( - "testing" - - "github.com/stretchr/testify/assert" - rbacv1 "k8s.io/api/rbac/v1" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -var logger = logf.Log.WithName("unit-tests") - -func TestK8sAttributesRBAC(t *testing.T) { - - tests := []struct { - name string - config map[interface{}]interface{} - expectedRules []rbacv1.PolicyRule - }{ - { - name: "no extra parameters", - config: nil, - expectedRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods", "namespaces"}, - Verbs: []string{"get", "watch", "list"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"replicasets"}, - Verbs: []string{"get", "watch", "list"}, - }, - }, - }, - { - name: "extract k8s.deployment.name", - config: map[interface{}]interface{}{ - "extract": map[interface{}]interface{}{ - "metadata": []interface{}{ - "k8s.deployment.name", - }, - }, - }, - expectedRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods", "namespaces"}, - Verbs: []string{"get", "watch", "list"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"replicasets"}, - Verbs: []string{"get", "watch", "list"}, - }, - }, - }, - { - name: "extract k8s.deployment.uid", - config: map[interface{}]interface{}{ - "extract": map[interface{}]interface{}{ - "metadata": []interface{}{ - "k8s.deployment.uid", - }, - }, - }, - expectedRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods", "namespaces"}, - Verbs: []string{"get", "watch", "list"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"replicasets"}, - Verbs: []string{"get", "watch", "list"}, - }, - }, - }, - { - name: "extract k8s.pod.name", - config: map[interface{}]interface{}{ - "extract": map[interface{}]interface{}{ - "metadata": []interface{}{ - "k8s.pod.name", - }, - }, - }, - expectedRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods", "namespaces"}, - Verbs: []string{"get", "watch", "list"}, - }, - }, - }, - { - name: "extract k8s.node", - config: map[interface{}]interface{}{ - "extract": map[interface{}]interface{}{ - "metadata": []interface{}{ - "k8s.node", - }, - }, - }, - expectedRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods", "namespaces"}, - Verbs: []string{"get", "watch", "list"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"nodes"}, - Verbs: []string{"get", "watch", "list"}, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p := NewK8sAttributesParser(logger, "test", tt.config) - rules := p.GetRBACRules() - assert.Equal(t, tt.expectedRules, rules) - }) - - } - -} diff --git a/internal/manifests/collector/parser/processor/processor_resourcedetection.go b/internal/manifests/collector/parser/processor/processor_resourcedetection.go deleted file mode 100644 index 4145f6baa6..0000000000 --- a/internal/manifests/collector/parser/processor/processor_resourcedetection.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package processor - -import ( - "fmt" - - "github.com/go-logr/logr" - rbacv1 "k8s.io/api/rbac/v1" -) - -var _ ProcessorParser = &ResourceDetectionParser{} - -const ( - parserNameResourceDetection = "__resourcedetection" -) - -// PrometheusExporterParser parses the configuration for OTLP receivers. -type ResourceDetectionParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewPrometheusExporterParser builds a new parser for OTLP receivers. -func NewResourceDetectionParser(logger logr.Logger, name string, config map[interface{}]interface{}) ProcessorParser { - return &ResourceDetectionParser{ - logger: logger, - name: name, - config: config, - } -} - -// ParserName returns the name of this parser. -func (o *ResourceDetectionParser) ParserName() string { - return parserNameResourceDetection -} - -func (o *ResourceDetectionParser) GetRBACRules() []rbacv1.PolicyRule { - var prs []rbacv1.PolicyRule - - detectorsCfg, ok := o.config["detectors"] - if !ok { - return prs - } - - detectors, ok := detectorsCfg.([]interface{}) - if !ok { - return prs - } - for _, d := range detectors { - detectorName := fmt.Sprint(d) - switch detectorName { - case "k8snode": - policy := rbacv1.PolicyRule{ - APIGroups: []string{""}, - Resources: []string{"nodes"}, - Verbs: []string{"get", "list"}, - } - prs = append(prs, policy) - case "openshift": - policy := rbacv1.PolicyRule{ - APIGroups: []string{"config.openshift.io"}, - Resources: []string{"infrastructures", "infrastructures/status"}, - Verbs: []string{"get", "watch", "list"}, - } - prs = append(prs, policy) - } - } - return prs -} - -func init() { - Register("resourcedetection", NewResourceDetectionParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver.go b/internal/manifests/collector/parser/receiver/receiver.go deleted file mode 100644 index 127891747b..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package parser is for parsing the OpenTelemetry Collector configuration. -package receiver - -import ( - "errors" - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -// registry holds a record of all known receiver parsers. -var registry = make(map[string]parser.Builder) - -// BuilderFor returns a parser builder for the given receiver name. -func BuilderFor(name string) parser.Builder { - builder := registry[receiverType(name)] - if builder == nil { - builder = NewGenericReceiverParser - } - - return builder -} - -// For returns a new parser for the given receiver name + config. -func For(logger logr.Logger, name string, config map[interface{}]interface{}) (parser.ComponentPortParser, error) { - builder := BuilderFor(name) - return builder(logger, name, config), nil -} - -// Register adds a new parser builder to the list of known builders. -func Register(name string, builder parser.Builder) { - registry[name] = builder -} - -// IsRegistered checks whether a parser is registered with the given name. -func IsRegistered(name string) bool { - _, ok := registry[name] - return ok -} - -var ( - endpointKey = "endpoint" - listenAddressKey = "listen_address" - scraperReceivers = map[string]struct{}{ - "prometheus": {}, - "kubeletstats": {}, - "sshcheck": {}, - "cloudfoundry": {}, - "vcenter": {}, - "oracledb": {}, - "snmp": {}, - "googlecloudpubsub": {}, - "chrony": {}, - "jmx": {}, - "podman_stats": {}, - "pulsar": {}, - "docker_stats": {}, - "aerospike": {}, - "zookeeper": {}, - "prometheus_simple": {}, - "saphana": {}, - "riak": {}, - "redis": {}, - "rabbitmq": {}, - "purefb": {}, - "postgresql": {}, - "nsxt": {}, - "nginx": {}, - "mysql": {}, - "memcached": {}, - "httpcheck": {}, - "haproxy": {}, - "flinkmetrics": {}, - "couchdb": {}, - } -) - -func isScraperReceiver(name string) bool { - _, exists := scraperReceivers[name] - return exists -} - -func singlePortFromConfigEndpoint(logger logr.Logger, name string, config map[interface{}]interface{}) *v1.ServicePort { - var endpoint interface{} - var receiverType = receiverType(name) - switch { - // ignore the receiver as it holds the field key endpoint, and it - // is a scraper, we only expose endpoint through k8s service objects for - // receivers that aren't scrapers. - case isScraperReceiver(receiverType): - return nil - - default: - endpoint = getAddressFromConfig(logger, name, endpointKey, config) - } - - switch e := endpoint.(type) { - case nil: - break - case string: - port, err := portFromEndpoint(e) - if err != nil { - logger.WithValues(endpointKey, e).Error(err, "couldn't parse the endpoint's port") - return nil - } - - return &corev1.ServicePort{ - Name: naming.PortName(name, port), - Port: port, - } - default: - logger.WithValues(endpointKey, endpoint).Error(fmt.Errorf("unrecognized type %T", endpoint), "receiver's endpoint isn't a string") - } - - return nil -} - -func getAddressFromConfig(logger logr.Logger, name, key string, config map[interface{}]interface{}) interface{} { - endpoint, ok := config[key] - if !ok { - logger.V(2).Info("%s receiver doesn't have an %s", name, key) - return nil - } - return endpoint -} - -func portFromEndpoint(endpoint string) (int32, error) { - var err error - var port int64 - - r := regexp.MustCompile(":[0-9]+") - - if r.MatchString(endpoint) { - port, err = strconv.ParseInt(strings.Replace(r.FindString(endpoint), ":", "", -1), 10, 32) - - if err != nil { - return 0, err - } - } - - if port == 0 { - return 0, errors.New("port should not be empty") - } - - return int32(port), err -} - -func receiverType(name string) string { - // receivers have a name like: - // - myreceiver/custom - // - myreceiver - // we extract the "myreceiver" part and see if we have a parser for the receiver - if strings.Contains(name, "/") { - return name[:strings.Index(name, "/")] - } - - return name -} diff --git a/internal/manifests/collector/parser/receiver/receiver_aws-xray.go b/internal/manifests/collector/parser/receiver/receiver_aws-xray.go deleted file mode 100644 index 44618b3aa5..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_aws-xray.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameAWSXRAY = "__awsxray" - -// NewAWSXrayReceiverParser builds a new parser for AWS xray receivers, from the contrib repository. -func NewAWSXrayReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 2000, - parserName: parserNameAWSXRAY, - } -} - -func init() { - Register("awsxray", NewAWSXrayReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_aws-xray_test.go b/internal/manifests/collector/parser/receiver/receiver_aws-xray_test.go deleted file mode 100644 index 5a99f9e893..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_aws-xray_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the AWS XRAY parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_carbon.go b/internal/manifests/collector/parser/receiver/receiver_carbon.go deleted file mode 100644 index b1b0aaa4ce..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_carbon.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameCarbon = "__carbon" - -// NewCarbonReceiverParser builds a new parser for Carbon receivers, from the contrib repository. -func NewCarbonReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 2003, - parserName: parserNameCarbon, - } -} - -func init() { - Register("carbon", NewCarbonReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_carbon_test.go b/internal/manifests/collector/parser/receiver/receiver_carbon_test.go deleted file mode 100644 index 370732182d..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_carbon_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the Carbon parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_collectd.go b/internal/manifests/collector/parser/receiver/receiver_collectd.go deleted file mode 100644 index aa42ab0397..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_collectd.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameCollectd = "__collectd" - -// NewCollectdReceiverParser builds a new parser for Collectd receivers, from the contrib repository. -func NewCollectdReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 8081, - parserName: parserNameCollectd, - } -} - -func init() { - Register("collectd", NewCollectdReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_collectd_test.go b/internal/manifests/collector/parser/receiver/receiver_collectd_test.go deleted file mode 100644 index 5e0e6d0121..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_collectd_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the Collectd parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_fluent-forward.go b/internal/manifests/collector/parser/receiver/receiver_fluent-forward.go deleted file mode 100644 index 88881923fa..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_fluent-forward.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameFluentForward = "__fluentforward" - -// NewFluentForwardReceiverParser builds a new parser for FluentForward receivers, from the contrib repository. -func NewFluentForwardReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 8006, - parserName: parserNameFluentForward, - } -} - -func init() { - Register("fluentforward", NewFluentForwardReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_fluent-forward_test.go b/internal/manifests/collector/parser/receiver/receiver_fluent-forward_test.go deleted file mode 100644 index af15006983..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_fluent-forward_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the FluentForward parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_generic.go b/internal/manifests/collector/parser/receiver/receiver_generic.go deleted file mode 100644 index 864b5bb111..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_generic.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -const parserNameGeneric = "__generic" - -var _ parser.ComponentPortParser = &GenericReceiver{} - -// GenericReceiver is a special parser for generic receivers. It doesn't self-register and should be created/used directly. -type GenericReceiver struct { - config map[interface{}]interface{} - defaultAppProtocol *string - logger logr.Logger - name string - defaultProtocol corev1.Protocol - parserName string - defaultPort int32 -} - -// NOTE: Operator will sync with only receivers that aren't scrapers. Operator sync up receivers -// so that it can expose the required port based on the receiver's config. Receiver scrapers are ignored. - -// NewGenericReceiverParser builds a new parser for generic receivers. -func NewGenericReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - parserName: parserNameGeneric, - } -} - -// Ports returns all the service ports for all protocols in this parser. -func (g *GenericReceiver) Ports() ([]corev1.ServicePort, error) { - port := singlePortFromConfigEndpoint(g.logger, g.name, g.config) - if port != nil { - port.Protocol = g.defaultProtocol - port.AppProtocol = g.defaultAppProtocol - return []corev1.ServicePort{*port}, nil - } - - if g.defaultPort > 0 { - return []corev1.ServicePort{{ - Port: g.defaultPort, - Name: naming.PortName(g.name, g.defaultPort), - Protocol: g.defaultProtocol, - AppProtocol: g.defaultAppProtocol, - }}, nil - } - - return []corev1.ServicePort{}, nil -} - -// ParserName returns the name of this parser. -func (g *GenericReceiver) ParserName() string { - return g.parserName -} diff --git a/internal/manifests/collector/parser/receiver/receiver_generic_test.go b/internal/manifests/collector/parser/receiver/receiver_generic_test.go deleted file mode 100644 index 1adfcf12df..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_generic_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver_test - -import ( - "testing" - - "github.com/go-logr/logr" - "github.com/stretchr/testify/assert" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/receiver" -) - -var logger = logf.Log.WithName("unit-tests") - -func TestParseEndpoint(t *testing.T) { - // prepare - // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser - builder := receiver.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1234", - }) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 1) - assert.EqualValues(t, 1234, ports[0].Port) -} - -func TestFailedToParseEndpoint(t *testing.T) { - // prepare - // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser - builder := receiver.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ - "endpoint": "0.0.0.0", - }) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 0) -} - -func TestDownstreamParsers(t *testing.T) { - for _, tt := range []struct { - builder func(logr.Logger, string, map[interface{}]interface{}) parser.ComponentPortParser - desc string - receiverName string - parserName string - defaultPort int - }{ - {receiver.NewZipkinReceiverParser, "zipkin", "zipkin", "__zipkin", 9411}, - {receiver.NewOpenCensusReceiverParser, "opencensus", "opencensus", "__opencensus", 55678}, - - // contrib receivers - {receiver.NewCarbonReceiverParser, "carbon", "carbon", "__carbon", 2003}, - {receiver.NewCollectdReceiverParser, "collectd", "collectd", "__collectd", 8081}, - {receiver.NewSAPMReceiverParser, "sapm", "sapm", "__sapm", 7276}, - {receiver.NewSignalFxReceiverParser, "signalfx", "signalfx", "__signalfx", 9943}, - {receiver.NewWavefrontReceiverParser, "wavefront", "wavefront", "__wavefront", 2003}, - {receiver.NewZipkinScribeReceiverParser, "zipkin-scribe", "zipkin-scribe", "__zipkinscribe", 9410}, - {receiver.NewFluentForwardReceiverParser, "fluentforward", "fluentforward", "__fluentforward", 8006}, - {receiver.NewStatsdReceiverParser, "statsd", "statsd", "__statsd", 8125}, - {receiver.NewInfluxdbReceiverParser, "influxdb", "influxdb", "__influxdb", 8086}, - {receiver.NewSplunkHecReceiverParser, "splunk-hec", "splunk-hec", "__splunk_hec", 8088}, - {receiver.NewAWSXrayReceiverParser, "awsxray", "awsxray", "__awsxray", 2000}, - } { - t.Run(tt.receiverName, func(t *testing.T) { - t.Run("builds successfully", func(t *testing.T) { - // test - builder := tt.builder(logger, tt.receiverName, map[interface{}]interface{}{}) - - // verify - assert.Equal(t, tt.parserName, builder.ParserName()) - }) - - t.Run("assigns the expected port", func(t *testing.T) { - // prepare - builder := tt.builder(logger, tt.receiverName, map[interface{}]interface{}{}) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 1) - assert.EqualValues(t, tt.defaultPort, ports[0].Port) - assert.Equal(t, tt.receiverName, ports[0].Name) - }) - - t.Run("allows port to be overridden", func(t *testing.T) { - // prepare - builder := tt.builder(logger, tt.receiverName, map[interface{}]interface{}{ - "endpoint": "0.0.0.0:65535", - }) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 1) - assert.EqualValues(t, 65535, ports[0].Port) - assert.Equal(t, tt.receiverName, ports[0].Name) - }) - }) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_influxdb.go b/internal/manifests/collector/parser/receiver/receiver_influxdb.go deleted file mode 100644 index 0930a29f73..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_influxdb.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameInfluxdb = "__influxdb" - -// NewInfluxdbReceiverParser builds a new parser for Influxdb receivers, from the contrib repository. -func NewInfluxdbReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 8086, - parserName: parserNameInfluxdb, - } -} - -func init() { - Register("influxdb", NewInfluxdbReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_influxdb_test.go b/internal/manifests/collector/parser/receiver/receiver_influxdb_test.go deleted file mode 100644 index 94f596ee06..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_influxdb_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the Influxdb parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_jaeger.go b/internal/manifests/collector/parser/receiver/receiver_jaeger.go deleted file mode 100644 index dcc99d8bd9..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_jaeger.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &JaegerReceiverParser{} - -const ( - parserNameJaeger = "__jaeger" - - defaultGRPCPort int32 = 14250 - defaultThriftHTTPPort int32 = 14268 - defaultThriftCompactPort int32 = 6831 - defaultThriftBinaryPort int32 = 6832 -) - -// JaegerReceiverParser parses the configuration for Jaeger-specific receivers. -type JaegerReceiverParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewJaegerReceiverParser builds a new parser for Jaeger receivers. -func NewJaegerReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - if protocols, ok := config["protocols"].(map[interface{}]interface{}); ok { - return &JaegerReceiverParser{ - logger: logger, - name: name, - config: protocols, - } - } - - return &JaegerReceiverParser{ - name: name, - config: map[interface{}]interface{}{}, - } -} - -// Ports returns all the service ports for all protocols in this parser. -func (j *JaegerReceiverParser) Ports() ([]corev1.ServicePort, error) { - ports := []corev1.ServicePort{} - - for _, protocol := range []struct { - name string - transportProtocol corev1.Protocol - appProtocol string - defaultPort int32 - }{ - { - name: "grpc", - defaultPort: defaultGRPCPort, - transportProtocol: corev1.ProtocolTCP, - appProtocol: "grpc", - }, - { - name: "thrift_http", - defaultPort: defaultThriftHTTPPort, - transportProtocol: corev1.ProtocolTCP, - appProtocol: "http", - }, - { - name: "thrift_compact", - defaultPort: defaultThriftCompactPort, - transportProtocol: corev1.ProtocolUDP, - }, - { - name: "thrift_binary", - defaultPort: defaultThriftBinaryPort, - transportProtocol: corev1.ProtocolUDP, - }, - } { - // do we have the protocol specified at all? - if receiverProtocol, ok := j.config[protocol.name]; ok { - // we have the specified protocol, we definitely need a service port - nameWithProtocol := fmt.Sprintf("%s-%s", j.name, protocol.name) - var protocolPort *corev1.ServicePort - - // do we have a configuration block for the protocol? - settings, ok := receiverProtocol.(map[interface{}]interface{}) - if ok { - protocolPort = singlePortFromConfigEndpoint(j.logger, nameWithProtocol, settings) - } - - // have we parsed a port based on the configuration block? - // if not, we use the default port - if protocolPort == nil { - protocolPort = &corev1.ServicePort{ - Name: naming.PortName(nameWithProtocol, protocol.defaultPort), - Port: protocol.defaultPort, - } - } - - // set the appropriate transport protocol (i.e. TCP/UDP) for this kind of receiver protocol - protocolPort.Protocol = protocol.transportProtocol - - if protocol.appProtocol != "" { - c := protocol.appProtocol - protocolPort.AppProtocol = &c - } - - // at this point, we *have* a port specified, add it to the list of ports - ports = append(ports, *protocolPort) - } - } - - return ports, nil -} - -// ParserName returns the name of this parser. -func (j *JaegerReceiverParser) ParserName() string { - return parserNameJaeger -} - -func init() { - Register("jaeger", NewJaegerReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_jaeger_test.go b/internal/manifests/collector/parser/receiver/receiver_jaeger_test.go deleted file mode 100644 index dadf3e0cb5..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_jaeger_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" -) - -func TestJaegerSelfRegisters(t *testing.T) { - // verify - assert.True(t, IsRegistered("jaeger")) -} - -func TestJaegerIsFoundByName(t *testing.T) { - // test - p, err := For(logger, "jaeger", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // verify - assert.Equal(t, "__jaeger", p.ParserName()) -} - -func TestJaegerMinimalConfiguration(t *testing.T) { - // prepare - builder := NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{}, - }, - }) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 1) - assert.EqualValues(t, 14250, ports[0].Port) - assert.EqualValues(t, corev1.ProtocolTCP, ports[0].Protocol) -} - -func TestJaegerPortsOverridden(t *testing.T) { - // prepare - builder := NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1234", - }, - }, - }) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 1) - assert.EqualValues(t, 1234, ports[0].Port) - assert.EqualValues(t, corev1.ProtocolTCP, ports[0].Protocol) -} - -func TestJaegerExposeDefaultPorts(t *testing.T) { - // prepare - builder := NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{}, - "thrift_http": map[interface{}]interface{}{}, - "thrift_compact": map[interface{}]interface{}{}, - "thrift_binary": map[interface{}]interface{}{}, - }, - }) - - expectedResults := map[string]struct { - transportProtocol corev1.Protocol - portNumber int32 - seen bool - }{ - "jaeger-grpc": {portNumber: 14250, transportProtocol: corev1.ProtocolTCP}, - "port-14268": {portNumber: 14268, transportProtocol: corev1.ProtocolTCP}, - "port-6831": {portNumber: 6831, transportProtocol: corev1.ProtocolUDP}, - "port-6832": {portNumber: 6832, transportProtocol: corev1.ProtocolUDP}, - } - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 4) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - assert.EqualValues(t, r.portNumber, port.Port) - assert.EqualValues(t, r.transportProtocol, port.Protocol) - } - for k, v := range expectedResults { - assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_loki.go b/internal/manifests/collector/parser/receiver/receiver_loki.go deleted file mode 100644 index 6cdac81356..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_loki.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &LokiReceiverParser{} - -const ( - parserNameLoki = "__loki" - - defaultLokiGRPCPort int32 = 9095 - defaultLokiHTTPPort int32 = 3100 -) - -// LokiReceiverParser parses the configuration for Loki receivers. -type LokiReceiverParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewLokiReceiverParser builds a new parser for Loki receivers. -func NewLokiReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - if protocols, ok := config["protocols"].(map[interface{}]interface{}); ok { - return &LokiReceiverParser{ - logger: logger, - name: name, - config: protocols, - } - } - - return &LokiReceiverParser{ - name: name, - config: map[interface{}]interface{}{}, - } -} - -// Ports returns all the service ports for all protocols in this parser. -func (o *LokiReceiverParser) Ports() ([]corev1.ServicePort, error) { - ports := []corev1.ServicePort{} - - for _, protocol := range []struct { - name string - defaultPorts []corev1.ServicePort - }{ - { - name: grpc, - defaultPorts: []corev1.ServicePort{ - { - Name: naming.PortName(fmt.Sprintf("%s-grpc", o.name), defaultLokiGRPCPort), - Port: defaultLokiGRPCPort, - TargetPort: intstr.FromInt(int(defaultLokiGRPCPort)), - AppProtocol: &grpc, - }, - }, - }, - { - name: http, - defaultPorts: []corev1.ServicePort{ - { - Name: naming.PortName(fmt.Sprintf("%s-http", o.name), defaultLokiHTTPPort), - Port: defaultLokiHTTPPort, - TargetPort: intstr.FromInt(int(defaultLokiHTTPPort)), - AppProtocol: &http, - }, - }, - }, - } { - // do we have the protocol specified at all? - if receiverProtocol, ok := o.config[protocol.name]; ok { - // we have the specified protocol, we definitely need a service port - nameWithProtocol := fmt.Sprintf("%s-%s", o.name, protocol.name) - var protocolPort *corev1.ServicePort - - // do we have a configuration block for the protocol? - settings, ok := receiverProtocol.(map[interface{}]interface{}) - if ok { - protocolPort = singlePortFromConfigEndpoint(o.logger, nameWithProtocol, settings) - } - - // have we parsed a port based on the configuration block? - // if not, we use the default port - if protocolPort == nil { - ports = append(ports, protocol.defaultPorts...) - } else { - // infer protocol and appProtocol from protocol.name - if protocol.name == grpc { - protocolPort.Protocol = corev1.ProtocolTCP - protocolPort.AppProtocol = &grpc - } else if protocol.name == http { - protocolPort.Protocol = corev1.ProtocolTCP - protocolPort.AppProtocol = &http - } - ports = append(ports, *protocolPort) - } - } - } - - return ports, nil -} - -// ParserName returns the name of this parser. -func (o *LokiReceiverParser) ParserName() string { - return parserNameLoki -} - -func init() { - Register("loki", NewLokiReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_loki_test.go b/internal/manifests/collector/parser/receiver/receiver_loki_test.go deleted file mode 100644 index cc10edc9f1..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_loki_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLokiSelfRegisters(t *testing.T) { - // verify - assert.True(t, IsRegistered("loki")) -} - -func TestLokiIsFoundByName(t *testing.T) { - // test - p, err := For(logger, "loki", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // verify - assert.Equal(t, "__loki", p.ParserName()) -} - -func TestLokiPortsOverridden(t *testing.T) { - // prepare - builder := NewLokiReceiverParser(logger, "loki", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1234", - }, - "http": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1235", - }, - }, - }) - - expectedResults := map[string]struct { - portNumber int32 - seen bool - }{ - "loki-grpc": {portNumber: 1234}, - "loki-http": {portNumber: 1235}, - } - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, len(expectedResults)) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - assert.EqualValues(t, r.portNumber, port.Port) - } - for k, v := range expectedResults { - assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) - } -} - -func TestLokiExposeDefaultPorts(t *testing.T) { - // prepare - builder := NewLokiReceiverParser(logger, "loki", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{}, - "http": map[interface{}]interface{}{}, - }, - }) - - expectedResults := map[string]struct { - portNumber int32 - seen bool - }{ - "loki-grpc": {portNumber: 9095}, - "loki-http": {portNumber: 3100}, - } - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, len(expectedResults)) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - assert.EqualValues(t, r.portNumber, port.Port) - } - for k, v := range expectedResults { - assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_oc.go b/internal/manifests/collector/parser/receiver/receiver_oc.go deleted file mode 100644 index 6619439a82..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_oc.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameOpenCensus = "__opencensus" - -// NewOpenCensusReceiverParser builds a new parser for OpenCensus receivers. -func NewOpenCensusReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - httpAppProtocol := "http" - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 55678, - parserName: parserNameOpenCensus, - defaultAppProtocol: &httpAppProtocol, - } -} - -func init() { - Register("opencensus", NewOpenCensusReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_oc_test.go b/internal/manifests/collector/parser/receiver/receiver_oc_test.go deleted file mode 100644 index 74b6062427..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_oc_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the OpenCensus parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_otlp.go b/internal/manifests/collector/parser/receiver/receiver_otlp.go deleted file mode 100644 index 68972fd950..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_otlp.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &OTLPReceiverParser{} - -const ( - parserNameOTLP = "__otlp" - - defaultOTLPGRPCPort int32 = 4317 - defaultOTLPHTTPPort int32 = 4318 -) - -var ( - grpc = "grpc" - http = "http" -) - -// OTLPReceiverParser parses the configuration for OTLP receivers. -type OTLPReceiverParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewOTLPReceiverParser builds a new parser for OTLP receivers. -func NewOTLPReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - if protocols, ok := config["protocols"].(map[interface{}]interface{}); ok { - return &OTLPReceiverParser{ - logger: logger, - name: name, - config: protocols, - } - } - - return &OTLPReceiverParser{ - name: name, - config: map[interface{}]interface{}{}, - } -} - -// Ports returns all the service ports for all protocols in this parser. -func (o *OTLPReceiverParser) Ports() ([]corev1.ServicePort, error) { - ports := []corev1.ServicePort{} - - for _, protocol := range []struct { - name string - defaultPorts []corev1.ServicePort - }{ - { - name: grpc, - defaultPorts: []corev1.ServicePort{ - { - Name: naming.PortName(fmt.Sprintf("%s-grpc", o.name), defaultOTLPGRPCPort), - Port: defaultOTLPGRPCPort, - TargetPort: intstr.FromInt(int(defaultOTLPGRPCPort)), - AppProtocol: &grpc, - }, - }, - }, - { - name: http, - defaultPorts: []corev1.ServicePort{ - { - Name: naming.PortName(fmt.Sprintf("%s-http", o.name), defaultOTLPHTTPPort), - Port: defaultOTLPHTTPPort, - TargetPort: intstr.FromInt(int(defaultOTLPHTTPPort)), - AppProtocol: &http, - }, - }, - }, - } { - // do we have the protocol specified at all? - if receiverProtocol, ok := o.config[protocol.name]; ok { - // we have the specified protocol, we definitely need a service port - nameWithProtocol := fmt.Sprintf("%s-%s", o.name, protocol.name) - var protocolPort *corev1.ServicePort - - // do we have a configuration block for the protocol? - settings, ok := receiverProtocol.(map[interface{}]interface{}) - if ok { - protocolPort = singlePortFromConfigEndpoint(o.logger, nameWithProtocol, settings) - } - - // have we parsed a port based on the configuration block? - // if not, we use the default port - if protocolPort == nil { - ports = append(ports, protocol.defaultPorts...) - } else { - // infer protocol and appProtocol from protocol.name - if protocol.name == grpc { - protocolPort.Protocol = corev1.ProtocolTCP - protocolPort.AppProtocol = &grpc - } else if protocol.name == http { - protocolPort.Protocol = corev1.ProtocolTCP - protocolPort.AppProtocol = &http - } - ports = append(ports, *protocolPort) - } - } - } - - return ports, nil -} - -// ParserName returns the name of this parser. -func (o *OTLPReceiverParser) ParserName() string { - return parserNameOTLP -} - -func init() { - Register("otlp", NewOTLPReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_otlp_test.go b/internal/manifests/collector/parser/receiver/receiver_otlp_test.go deleted file mode 100644 index 7165153dde..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_otlp_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestOTLPSelfRegisters(t *testing.T) { - // verify - assert.True(t, IsRegistered("otlp")) -} - -func TestOTLPIsFoundByName(t *testing.T) { - // test - p, err := For(logger, "otlp", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // verify - assert.Equal(t, "__otlp", p.ParserName()) -} - -func TestOTLPPortsOverridden(t *testing.T) { - // prepare - builder := NewOTLPReceiverParser(logger, "otlp", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1234", - }, - "http": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1235", - }, - }, - }) - - expectedResults := map[string]struct { - portNumber int32 - seen bool - }{ - "otlp-grpc": {portNumber: 1234}, - "otlp-http": {portNumber: 1235}, - } - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, len(expectedResults)) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - assert.EqualValues(t, r.portNumber, port.Port) - } - for k, v := range expectedResults { - assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) - } -} - -func TestOTLPExposeDefaultPorts(t *testing.T) { - // prepare - builder := NewOTLPReceiverParser(logger, "otlp", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{}, - "http": map[interface{}]interface{}{}, - }, - }) - - expectedResults := map[string]struct { - portNumber int32 - seen bool - }{ - "otlp-grpc": {portNumber: 4317}, - "otlp-http": {portNumber: 4318}, - } - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, len(expectedResults)) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - assert.EqualValues(t, r.portNumber, port.Port) - } - for k, v := range expectedResults { - assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_sapm.go b/internal/manifests/collector/parser/receiver/receiver_sapm.go deleted file mode 100644 index 924b88bff5..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_sapm.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameSAPM = "__sapm" - -// NewSAPMReceiverParser builds a new parser for SAPM receivers, from the contrib repository. -func NewSAPMReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 7276, - parserName: parserNameSAPM, - } -} - -func init() { - Register("sapm", NewSAPMReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_sapm_test.go b/internal/manifests/collector/parser/receiver/receiver_sapm_test.go deleted file mode 100644 index 0940086266..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_sapm_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the SAPM parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_signalfx.go b/internal/manifests/collector/parser/receiver/receiver_signalfx.go deleted file mode 100644 index 549e802453..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_signalfx.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameSignalFx = "__signalfx" - -// NewSignalFxReceiverParser builds a new parser for SignalFx receivers, from the contrib repository. -func NewSignalFxReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 9943, - parserName: parserNameSignalFx, - } -} - -func init() { - Register("signalfx", NewSignalFxReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_signalfx_test.go b/internal/manifests/collector/parser/receiver/receiver_signalfx_test.go deleted file mode 100644 index eaee10189f..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_signalfx_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the SignalFx parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_skywalking.go b/internal/manifests/collector/parser/receiver/receiver_skywalking.go deleted file mode 100644 index 9b72847ee9..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_skywalking.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &SkywalkingReceiverParser{} - -const ( - parserNameSkywalking = "__skywalking" - - defaultSkywalkingGRPCPort int32 = 11800 - defaultSkywalkingHTTPPort int32 = 12800 -) - -// SkywalkingReceiverParser parses the configuration for Skywalking receivers. -type SkywalkingReceiverParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewSkywalkingReceiverParser builds a new parser for Skywalking receivers. -func NewSkywalkingReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - if protocols, ok := config["protocols"].(map[interface{}]interface{}); ok { - return &SkywalkingReceiverParser{ - logger: logger, - name: name, - config: protocols, - } - } - - return &SkywalkingReceiverParser{ - name: name, - config: map[interface{}]interface{}{}, - } -} - -// Ports returns all the service ports for all protocols in this parser. -func (o *SkywalkingReceiverParser) Ports() ([]corev1.ServicePort, error) { - ports := []corev1.ServicePort{} - - for _, protocol := range []struct { - name string - defaultPorts []corev1.ServicePort - }{ - { - name: grpc, - defaultPorts: []corev1.ServicePort{ - { - Name: naming.PortName(fmt.Sprintf("%s-grpc", o.name), defaultSkywalkingGRPCPort), - Port: defaultSkywalkingGRPCPort, - TargetPort: intstr.FromInt(int(defaultSkywalkingGRPCPort)), - AppProtocol: &grpc, - }, - }, - }, - { - name: http, - defaultPorts: []corev1.ServicePort{ - { - Name: naming.PortName(fmt.Sprintf("%s-http", o.name), defaultSkywalkingHTTPPort), - Port: defaultSkywalkingHTTPPort, - TargetPort: intstr.FromInt(int(defaultSkywalkingHTTPPort)), - AppProtocol: &http, - }, - }, - }, - } { - // do we have the protocol specified at all? - if receiverProtocol, ok := o.config[protocol.name]; ok { - // we have the specified protocol, we definitely need a service port - nameWithProtocol := fmt.Sprintf("%s-%s", o.name, protocol.name) - var protocolPort *corev1.ServicePort - - // do we have a configuration block for the protocol? - settings, ok := receiverProtocol.(map[interface{}]interface{}) - if ok { - protocolPort = singlePortFromConfigEndpoint(o.logger, nameWithProtocol, settings) - } - - // have we parsed a port based on the configuration block? - // if not, we use the default port - if protocolPort == nil { - ports = append(ports, protocol.defaultPorts...) - } else { - // infer protocol and appProtocol from protocol.name - if protocol.name == grpc { - protocolPort.Protocol = corev1.ProtocolTCP - protocolPort.AppProtocol = &grpc - } else if protocol.name == http { - protocolPort.Protocol = corev1.ProtocolTCP - protocolPort.AppProtocol = &http - } - ports = append(ports, *protocolPort) - } - } - } - - return ports, nil -} - -// ParserName returns the name of this parser. -func (o *SkywalkingReceiverParser) ParserName() string { - return parserNameSkywalking -} - -func init() { - Register("skywalking", NewSkywalkingReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_skywalking_test.go b/internal/manifests/collector/parser/receiver/receiver_skywalking_test.go deleted file mode 100644 index ab00c852c2..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_skywalking_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSkywalkingSelfRegisters(t *testing.T) { - // verify - assert.True(t, IsRegistered("skywalking")) -} - -func TestSkywalkingIsFoundByName(t *testing.T) { - // test - p, err := For(logger, "skywalking", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // verify - assert.Equal(t, "__skywalking", p.ParserName()) -} - -func TestSkywalkingPortsOverridden(t *testing.T) { - // prepare - builder := NewSkywalkingReceiverParser(logger, "skywalking", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1234", - }, - "http": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1235", - }, - }, - }) - - expectedResults := map[string]struct { - portNumber int32 - seen bool - }{ - "skywalking-grpc": {portNumber: 1234}, - "skywalking-http": {portNumber: 1235}, - } - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, len(expectedResults)) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - assert.EqualValues(t, r.portNumber, port.Port) - } - for k, v := range expectedResults { - assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) - } -} - -func TestSkywalkingExposeDefaultPorts(t *testing.T) { - // prepare - builder := NewSkywalkingReceiverParser(logger, "skywalking", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{}, - "http": map[interface{}]interface{}{}, - }, - }) - - expectedResults := map[string]struct { - portNumber int32 - seen bool - }{ - "skywalking-grpc": {portNumber: 11800}, - "skywalking-http": {portNumber: 12800}, - } - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, len(expectedResults)) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - assert.EqualValues(t, r.portNumber, port.Port) - } - for k, v := range expectedResults { - assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_splunk-hec.go b/internal/manifests/collector/parser/receiver/receiver_splunk-hec.go deleted file mode 100644 index 676415334b..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_splunk-hec.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameSplunkHec = "__splunk_hec" - -// NewSplunkHecReceiverParser builds a new parser for Splunk Hec receivers, from the contrib repository. -func NewSplunkHecReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 8088, - parserName: parserNameSplunkHec, - } -} - -func init() { - Register("splunk_hec", NewSplunkHecReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_splunk-hec_test.go b/internal/manifests/collector/parser/receiver/receiver_splunk-hec_test.go deleted file mode 100644 index e6f99f7852..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_splunk-hec_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the Splunk Hec parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_statsd.go b/internal/manifests/collector/parser/receiver/receiver_statsd.go deleted file mode 100644 index 0f41520e22..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_statsd.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameStatsd = "__statsd" - -// NewStatsdReceiverParser builds a new parser for Statsd receivers, from the contrib repository. -func NewStatsdReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 8125, - defaultProtocol: corev1.ProtocolUDP, - parserName: parserNameStatsd, - } -} - -func init() { - Register("statsd", NewStatsdReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_statsd_test.go b/internal/manifests/collector/parser/receiver/receiver_statsd_test.go deleted file mode 100644 index 338971ba44..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_statsd_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the Statsd parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_syslog.go b/internal/manifests/collector/parser/receiver/receiver_syslog.go deleted file mode 100644 index 582cb5c2be..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_syslog.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &SyslogReceiverParser{} - -const parserNameSyslog = "__syslog" - -// SyslogReceiverParser parses the configuration for TCP log receivers. -type SyslogReceiverParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewSyslogReceiverParser builds a new parser for TCP log receivers. -func NewSyslogReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &SyslogReceiverParser{ - logger: logger, - name: name, - config: config, - } -} - -func (o *SyslogReceiverParser) Ports() ([]corev1.ServicePort, error) { - var endpoint interface{} - var endpointName string - var protocol corev1.Protocol - var c map[interface{}]interface{} - - // syslog receiver contains the endpoint - // that needs to be exposed one level down inside config - // i.e. either in tcp or udp section with field key - // as `listen_address` - if tcp, isTCP := o.config["tcp"]; isTCP && tcp != nil { - c = tcp.(map[interface{}]interface{}) - endpointName = "tcp" - endpoint = getAddressFromConfig(o.logger, o.name, listenAddressKey, c) - protocol = corev1.ProtocolTCP - } else if udp, isUDP := o.config["udp"]; isUDP && udp != nil { - c = udp.(map[interface{}]interface{}) - endpointName = "udp" - endpoint = getAddressFromConfig(o.logger, o.name, listenAddressKey, c) - protocol = corev1.ProtocolUDP - } - - switch e := endpoint.(type) { - case nil: - break - case string: - port, err := portFromEndpoint(e) - if err != nil { - o.logger.WithValues(listenAddressKey, e).Error(err, fmt.Sprintf("couldn't parse the %s endpoint's port", endpointName)) - return nil, nil - } - - return []corev1.ServicePort{{ - Port: port, - Name: naming.PortName(o.name, port), - Protocol: protocol, - }}, nil - default: - o.logger.WithValues(listenAddressKey, endpoint).Error(fmt.Errorf("unrecognized type %T of %s endpoint", endpoint, endpointName), - "receiver's endpoint isn't a string") - } - - return []corev1.ServicePort{}, nil -} - -func (o *SyslogReceiverParser) ParserName() string { - return parserNameSyslog -} - -func init() { - Register("syslog", NewSyslogReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_syslog_test.go b/internal/manifests/collector/parser/receiver/receiver_syslog_test.go deleted file mode 100644 index fd7ea9daf5..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_syslog_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" -) - -func TestSyslogSelfRegisters(t *testing.T) { - // verify - assert.True(t, IsRegistered("syslog")) -} - -func TestSyslogIsFoundByName(t *testing.T) { - // test - p, err := For(logger, "syslog", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // verify - assert.Equal(t, "__syslog", p.ParserName()) -} - -func TestSyslogConfiguration(t *testing.T) { - for _, tt := range []struct { - desc string - config map[interface{}]interface{} - expected []corev1.ServicePort - }{ - {"Empty configuration", map[interface{}]interface{}{}, []corev1.ServicePort{}}, - {"UDP port configuration", - map[interface{}]interface{}{"udp": map[interface{}]interface{}{"listen_address": "0.0.0.0:1234"}}, - []corev1.ServicePort{{Name: "syslog", Port: 1234, Protocol: corev1.ProtocolUDP}}}, - {"TCP port configuration", - map[interface{}]interface{}{"tcp": map[interface{}]interface{}{"listen_address": "0.0.0.0:1234"}}, - []corev1.ServicePort{{Name: "syslog", Port: 1234, Protocol: corev1.ProtocolTCP}}}, - } { - t.Run(tt.desc, func(t *testing.T) { - // prepare - builder := NewSyslogReceiverParser(logger, "syslog", tt.config) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Equal(t, ports, tt.expected) - }) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_tcplog.go b/internal/manifests/collector/parser/receiver/receiver_tcplog.go deleted file mode 100644 index c0232415a9..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_tcplog.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &TcpLogReceiverParser{} - -const parserNameTcpLog = "__tcplog" - -// TcpLogReceiverParser parses the configuration for TCP log receivers. -type TcpLogReceiverParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewTcpLogReceiverParser builds a new parser for TCP log receivers. -func NewTcpLogReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &TcpLogReceiverParser{ - logger: logger, - name: name, - config: config, - } -} - -func (o *TcpLogReceiverParser) Ports() ([]corev1.ServicePort, error) { - // tcplog receiver hold the endpoint value in `listen_address` field - var endpoint = getAddressFromConfig(o.logger, o.name, listenAddressKey, o.config) - - switch e := endpoint.(type) { - case nil: - break - case string: - port, err := portFromEndpoint(e) - if err != nil { - o.logger.WithValues(listenAddressKey, e).Error(err, "couldn't parse the endpoint's port") - return nil, nil - } - - return []corev1.ServicePort{{ - Port: port, - Name: naming.PortName(o.name, port), - Protocol: corev1.ProtocolTCP, - }}, nil - default: - o.logger.WithValues(listenAddressKey, endpoint).Error(fmt.Errorf("unrecognized type %T", endpoint), "receiver's endpoint isn't a string") - } - - return []corev1.ServicePort{}, nil -} - -func (o *TcpLogReceiverParser) ParserName() string { - return parserNameTcpLog -} - -func init() { - Register("tcplog", NewTcpLogReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_tcplog_test.go b/internal/manifests/collector/parser/receiver/receiver_tcplog_test.go deleted file mode 100644 index 04b4eb03be..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_tcplog_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" -) - -func TestTcpLogSelfRegisters(t *testing.T) { - // verify - assert.True(t, IsRegistered("tcplog")) -} - -func TestTcpLogIsFoundByName(t *testing.T) { - // test - p, err := For(logger, "tcplog", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // verify - assert.Equal(t, "__tcplog", p.ParserName()) -} - -func TestTcpLogConfiguration(t *testing.T) { - for _, tt := range []struct { - desc string - config map[interface{}]interface{} - expected []corev1.ServicePort - }{ - {"Empty configuration", map[interface{}]interface{}{}, []corev1.ServicePort{}}, - {"TCP port configuration", - map[interface{}]interface{}{"listen_address": "0.0.0.0:1234"}, - []corev1.ServicePort{{Name: "tcplog", Port: 1234, Protocol: corev1.ProtocolTCP}}}, - } { - t.Run(tt.desc, func(t *testing.T) { - // prepare - builder := NewTcpLogReceiverParser(logger, "tcplog", tt.config) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Equal(t, ports, tt.expected) - }) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_test.go b/internal/manifests/collector/parser/receiver/receiver_test.go deleted file mode 100644 index 44cb04519d..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_test.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/go-logr/logr" - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var logger = logf.Log.WithName("unit-tests") - -func TestReceiverPortNames(t *testing.T) { - for _, tt := range []struct { - desc string - candidate string - expected string - port int - }{ - {"regular case", "my-receiver", "my-receiver", 123}, - {"name too long", "long-name-long-name-long-name-long-name-long-name-long-name-long-name-long-name", "port-123", 123}, - {"name with invalid chars", "my-🦄-receiver", "port-123", 123}, - {"name starting with invalid char", "-my-receiver", "port-123", 123}, - } { - t.Run(tt.desc, func(t *testing.T) { - assert.Equal(t, tt.expected, naming.PortName(tt.candidate, int32(tt.port))) - }) - } -} - -func TestReceiverType(t *testing.T) { - for _, tt := range []struct { - desc string - name string - expected string - }{ - {"regular case", "myreceiver", "myreceiver"}, - {"named instance", "myreceiver/custom", "myreceiver"}, - } { - t.Run(tt.desc, func(t *testing.T) { - // test and verify - assert.Equal(t, tt.expected, receiverType(tt.name)) - }) - } -} - -func TestReceiverParsePortFromEndpoint(t *testing.T) { - for _, tt := range []struct { - desc string - endpoint string - expected int - errorExpected bool - }{ - {"regular case", "http://localhost:1234", 1234, false}, - {"absolute with path", "http://localhost:1234/server-status?auto", 1234, false}, - {"no protocol", "0.0.0.0:1234", 1234, false}, - {"just port", ":1234", 1234, false}, - {"no port at all", "http://localhost", 0, true}, - } { - t.Run(tt.desc, func(t *testing.T) { - // test - val, err := portFromEndpoint(tt.endpoint) - if tt.errorExpected { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - - assert.EqualValues(t, tt.expected, val, "wrong port from endpoint %s: %d", tt.endpoint, val) - }) - } -} - -func TestReceiverFailsWhenPortIsntString(t *testing.T) { - // prepare - config := map[interface{}]interface{}{ - "endpoint": 123, - } - - // test - p := singlePortFromConfigEndpoint(logger, "myreceiver", config) - - // verify - assert.Nil(t, p) -} - -func TestIgnorekubeletstatsEndpoint(t *testing.T) { - // ignore "kubeletstats" receiver endpoint field, this is special case - // as this receiver gets parsed by generic receiver parser - builder := NewGenericReceiverParser(logger, "kubeletstats", map[interface{}]interface{}{ - "endpoint": "0.0.0.0:9000", - }) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Len(t, ports, 0) -} - -func TestReceiverFallbackWhenNotRegistered(t *testing.T) { - // test - p, err := For(logger, "myreceiver", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // test - assert.Equal(t, "__generic", p.ParserName()) -} - -func TestReceiverShouldFindRegisteredParser(t *testing.T) { - // prepare - builderCalled := false - Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - builderCalled = true - return &mockParser{} - }) - - // test - _, _ = For(logger, "mock", map[interface{}]interface{}{}) - - // verify - assert.True(t, builderCalled) -} - -type mockParser struct { -} - -func (m *mockParser) Ports() ([]corev1.ServicePort, error) { - return nil, nil -} - -func (m *mockParser) ParserName() string { - return "__mock" -} - -func TestSkipPortsForScrapers(t *testing.T) { - for receiver := range scraperReceivers { - builder := NewGenericReceiverParser(logger, receiver, map[interface{}]interface{}{ - "endpoint": "0.0.0.0:42069", - }) - ports, err := builder.Ports() - assert.NoError(t, err) - assert.Len(t, ports, 0) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_udplog.go b/internal/manifests/collector/parser/receiver/receiver_udplog.go deleted file mode 100644 index 4f55e5b492..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_udplog.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" - "github.com/open-telemetry/opentelemetry-operator/internal/naming" -) - -var _ parser.ComponentPortParser = &UdpLogReceiverParser{} - -const parserNameUdpLog = "__udplog" - -// UdpLogReceiverParser parses the configuration for UDP log receivers. -type UdpLogReceiverParser struct { - config map[interface{}]interface{} - logger logr.Logger - name string -} - -// NewUdpLogReceiverParser builds a new parser for UDP log receivers. -func NewUdpLogReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &UdpLogReceiverParser{ - logger: logger, - name: name, - config: config, - } -} - -func (o *UdpLogReceiverParser) Ports() ([]corev1.ServicePort, error) { - // udplog receiver hold the endpoint value in `listen_address` field - var endpoint = getAddressFromConfig(o.logger, o.name, listenAddressKey, o.config) - - switch e := endpoint.(type) { - case nil: - break - case string: - port, err := portFromEndpoint(e) - if err != nil { - o.logger.WithValues(listenAddressKey, e).Error(err, "couldn't parse the endpoint's port") - return nil, nil - } - - return []corev1.ServicePort{{ - Port: port, - Name: naming.PortName(o.name, port), - Protocol: corev1.ProtocolUDP, - }}, nil - default: - o.logger.WithValues(listenAddressKey, endpoint).Error(fmt.Errorf("unrecognized type %T", endpoint), "receiver's endpoint isn't a string") - } - - return []corev1.ServicePort{}, nil -} - -func (o *UdpLogReceiverParser) ParserName() string { - return parserNameUdpLog -} - -func init() { - Register("udplog", NewUdpLogReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_udplog_test.go b/internal/manifests/collector/parser/receiver/receiver_udplog_test.go deleted file mode 100644 index 1b12191ed5..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_udplog_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "testing" - - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" -) - -func TestUdpLogSelfRegisters(t *testing.T) { - // verify - assert.True(t, IsRegistered("udplog")) -} - -func TestUdpLogIsFoundByName(t *testing.T) { - // test - p, err := For(logger, "udplog", map[interface{}]interface{}{}) - assert.NoError(t, err) - - // verify - assert.Equal(t, "__udplog", p.ParserName()) -} - -func TestUdpLogConfiguration(t *testing.T) { - for _, tt := range []struct { - desc string - config map[interface{}]interface{} - expected []corev1.ServicePort - }{ - {"Empty configuration", map[interface{}]interface{}{}, []corev1.ServicePort{}}, - {"UDP port configuration", - map[interface{}]interface{}{"listen_address": "0.0.0.0:1234"}, - []corev1.ServicePort{{Name: "udplog", Port: 1234, Protocol: corev1.ProtocolUDP}}}, - } { - t.Run(tt.desc, func(t *testing.T) { - // prepare - builder := NewUdpLogReceiverParser(logger, "udplog", tt.config) - - // test - ports, err := builder.Ports() - - // verify - assert.NoError(t, err) - assert.Equal(t, ports, tt.expected) - }) - } -} diff --git a/internal/manifests/collector/parser/receiver/receiver_wavefront.go b/internal/manifests/collector/parser/receiver/receiver_wavefront.go deleted file mode 100644 index f2eafb8556..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_wavefront.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameWavefront = "__wavefront" - -// NewWavefrontReceiverParser builds a new parser for Wavefront receivers, from the contrib repository. -func NewWavefrontReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 2003, - parserName: parserNameWavefront, - } -} - -func init() { - Register("wavefront", NewWavefrontReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_wavefront_test.go b/internal/manifests/collector/parser/receiver/receiver_wavefront_test.go deleted file mode 100644 index 0dd31b9470..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_wavefront_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the Wavefront parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe.go b/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe.go deleted file mode 100644 index 8f8d9fe210..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameZipkinScribe = "__zipkinscribe" - -// NewZipkinScribeReceiverParser builds a new parser for ZipkinScribe receivers. -func NewZipkinScribeReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 9410, - parserName: parserNameZipkinScribe, - } -} - -func init() { - Register("zipkin-scribe", NewZipkinScribeReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe_test.go b/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe_test.go deleted file mode 100644 index 6475eb02fc..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the ZipkinScribe parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_zipkin.go b/internal/manifests/collector/parser/receiver/receiver_zipkin.go deleted file mode 100644 index debbf8e9a4..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_zipkin.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -import ( - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" -) - -const parserNameZipkin = "__zipkin" - -// NewZipkinReceiverParser builds a new parser for Zipkin receivers. -func NewZipkinReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { - http := "http" - return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 9411, - defaultProtocol: corev1.ProtocolTCP, - defaultAppProtocol: &http, - parserName: parserNameZipkin, - } -} - -func init() { - Register("zipkin", NewZipkinReceiverParser) -} diff --git a/internal/manifests/collector/parser/receiver/receiver_zipkin_test.go b/internal/manifests/collector/parser/receiver/receiver_zipkin_test.go deleted file mode 100644 index 8f1af43c5e..0000000000 --- a/internal/manifests/collector/parser/receiver/receiver_zipkin_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package receiver - -// all tests for the Zipkin parser are currently part of the test TestDownstreamParsers From 09ad83709c7e06d8e64a6dc77fc93a8410ea98e2 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 30 Aug 2024 15:45:27 -0400 Subject: [PATCH 12/18] Delete more --- .../collector/adapters/config_to_ports.go | 27 ----- .../collector/adapters/config_validate.go | 110 ------------------ .../adapters/config_validate_test.go | 103 ---------------- 3 files changed, 240 deletions(-) delete mode 100644 internal/manifests/collector/adapters/config_to_ports.go delete mode 100644 internal/manifests/collector/adapters/config_validate.go delete mode 100644 internal/manifests/collector/adapters/config_validate_test.go diff --git a/internal/manifests/collector/adapters/config_to_ports.go b/internal/manifests/collector/adapters/config_to_ports.go deleted file mode 100644 index 444eb0f9fe..0000000000 --- a/internal/manifests/collector/adapters/config_to_ports.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adapters - -type ComponentType int - -const ( - ComponentTypeReceiver ComponentType = iota - ComponentTypeExporter - ComponentTypeProcessor -) - -func (c ComponentType) String() string { - return [...]string{"receiver", "exporter", "processor"}[c] -} diff --git a/internal/manifests/collector/adapters/config_validate.go b/internal/manifests/collector/adapters/config_validate.go deleted file mode 100644 index ff0c86c9b8..0000000000 --- a/internal/manifests/collector/adapters/config_validate.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adapters - -import "fmt" - -// Following Otel Doc: Configuring a receiver does not enable it. The receivers are enabled via pipelines within the service section. -// getEnabledComponents returns all enabled components as a true flag set. If it can't find any receiver, it will return a nil interface. -func getEnabledComponents(config map[interface{}]interface{}, componentType ComponentType) map[interface{}]bool { - componentTypePlural := fmt.Sprintf("%ss", componentType.String()) - cfgComponents, ok := config[componentTypePlural] - if !ok { - return nil - } - components, ok := cfgComponents.(map[interface{}]interface{}) - if !ok { - return nil - } - availableComponents := map[interface{}]bool{} - - for compID := range components { - - //Safe Cast - componentID, withComponent := compID.(string) - if !withComponent { - return nil - } - //Getting all components present in the components (exporters,receivers...) section and setting them to false. - availableComponents[componentID] = false - } - - cfgService, withService := config["service"].(map[interface{}]interface{}) - if !withService { - return nil - } - - pipeline, withPipeline := cfgService["pipelines"].(map[interface{}]interface{}) - if !withPipeline { - return nil - } - availablePipelines := map[string]bool{} - - for pipID := range pipeline { - //Safe Cast - pipelineID, existsPipeline := pipID.(string) - if !existsPipeline { - return nil - } - //Getting all the available pipelines. - availablePipelines[pipelineID] = true - } - - if len(pipeline) > 0 { - for pipelineID, pipelineCfg := range pipeline { - //Safe Cast - pipelineV, withPipelineCfg := pipelineID.(string) - if !withPipelineCfg { - continue - } - //Condition will get information if there are multiple configured pipelines. - if len(pipelineV) > 0 { - pipelineDesc, ok := pipelineCfg.(map[interface{}]interface{}) - if !ok { - return nil - } - for pipSpecID, pipSpecCfg := range pipelineDesc { - if pipSpecID.(string) == componentTypePlural { - receiversList, ok := pipSpecCfg.([]interface{}) - if !ok { - continue - } - // If receiversList is empty means that we haven't any enabled Receiver. - if len(receiversList) == 0 { - availableComponents = nil - } else { - // All enabled receivers will be set as true - for _, comKey := range receiversList { - //Safe Cast - receiverKey, ok := comKey.(string) - if !ok { - return nil - } - availableComponents[receiverKey] = true - } - } - //Removing all non-enabled receivers - for comID, comKey := range availableComponents { - if !(comKey) { - delete(availableComponents, comID) - } - } - } - } - } - } - } - return availableComponents -} diff --git a/internal/manifests/collector/adapters/config_validate_test.go b/internal/manifests/collector/adapters/config_validate_test.go deleted file mode 100644 index 7003235fed..0000000000 --- a/internal/manifests/collector/adapters/config_validate_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adapters - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestConfigValidate(t *testing.T) { - // prepare - - // First Test - Exporters - configStr := ` -receivers: - httpd/mtls: - protocols: - http: - endpoint: mysite.local:55690 - jaeger: - protocols: - grpc: - prometheus: - protocols: - grpc: - -processors: - -exporters: - debug: - -service: - pipelines: - metrics: - receivers: [httpd/mtls, jaeger] - exporters: [debug] - metrics/1: - receivers: [httpd/mtls, jaeger] - exporters: [debug] -` - // // prepare - config, err := ConfigFromString(configStr) - require.NoError(t, err) - require.NotEmpty(t, config) - - // test - check := getEnabledComponents(config, ComponentTypeReceiver) - require.NotEmpty(t, check) -} - -func TestEmptyEnabledReceivers(t *testing.T) { - // prepare - - // First Test - Exporters - configStr := ` -receivers: - httpd/mtls: - protocols: - http: - endpoint: mysite.local:55690 - jaeger: - protocols: - grpc: - prometheus: - protocols: - grpc: - -processors: - -exporters: - debug: - -service: - pipelines: - metrics: - receivers: [] - exporters: [] - metrics/1: - receivers: [] - exporters: [] -` - // // prepare - config, err := ConfigFromString(configStr) - require.NoError(t, err) - require.NotEmpty(t, config) - - // test - check := getEnabledComponents(config, ComponentTypeReceiver) - require.Empty(t, check) -} From 0defa34a7b6748efd5892571fb52d2662de9aaec Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 30 Aug 2024 17:25:11 -0400 Subject: [PATCH 13/18] Add parsing for extensions --- internal/components/builder.go | 39 +++- internal/components/builder_test.go | 134 +++++++++++-- internal/components/component.go | 10 + .../components/extensions/healthcheckv1.go | 43 ++++ .../extensions/healthcheckv1_test.go | 186 ++++++++++++++++++ internal/components/extensions/helpers.go | 65 ++++++ .../components/extensions/helpers_test.go | 111 +++++++++++ internal/components/generic_parser.go | 32 ++- internal/components/generic_parser_test.go | 152 ++++++++++++++ internal/components/multi_endpoint.go | 8 + internal/components/multi_endpoint_test.go | 6 + 11 files changed, 754 insertions(+), 32 deletions(-) create mode 100644 internal/components/extensions/healthcheckv1.go create mode 100644 internal/components/extensions/healthcheckv1_test.go create mode 100644 internal/components/extensions/helpers.go create mode 100644 internal/components/extensions/helpers_test.go diff --git a/internal/components/builder.go b/internal/components/builder.go index 9ffb49e840..4b9e9dde89 100644 --- a/internal/components/builder.go +++ b/internal/components/builder.go @@ -26,14 +26,16 @@ import ( type ParserOption[T any] func(*Option[T]) type Option[T any] struct { - protocol corev1.Protocol - appProtocol *string - targetPort intstr.IntOrString - nodePort int32 - name string - port int32 - portParser PortParser[T] - rbacGen RBACRuleGenerator[T] + protocol corev1.Protocol + appProtocol *string + targetPort intstr.IntOrString + nodePort int32 + name string + port int32 + portParser PortParser[T] + rbacGen RBACRuleGenerator[T] + livenessGen ProbeGenerator[T] + readinessGen ProbeGenerator[T] } func NewEmptyOption[T any]() *Option[T] { @@ -103,14 +105,31 @@ func (b Builder[T]) WithRbacGen(rbacGen RBACRuleGenerator[T]) Builder[T] { o.rbacGen = rbacGen }) } +func (b Builder[T]) WithLivenessGen(livenessGen ProbeGenerator[T]) Builder[T] { + return append(b, func(o *Option[T]) { + o.livenessGen = livenessGen + }) +} +func (b Builder[T]) WithReadinessGen(readinessGen ProbeGenerator[T]) Builder[T] { + return append(b, func(o *Option[T]) { + o.readinessGen = readinessGen + }) +} func (b Builder[T]) Build() (*GenericParser[T], error) { o := NewEmptyOption[T]() o.Apply(b...) - if len(o.name) == 0 { + if len(o.name) == 0 || o == nil { return nil, fmt.Errorf("invalid option struct, no name specified") } - return &GenericParser[T]{name: o.name, portParser: o.portParser, rbacGen: o.rbacGen, option: o}, nil + return &GenericParser[T]{ + name: o.name, + portParser: o.portParser, + rbacGen: o.rbacGen, + livenessGen: o.livenessGen, + readinessGen: o.readinessGen, + option: o, + }, nil } func (b Builder[T]) MustBuild() *GenericParser[T] { diff --git a/internal/components/builder_test.go b/internal/components/builder_test.go index d600db4a70..0e82f6bde3 100644 --- a/internal/components/builder_test.go +++ b/internal/components/builder_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/util/intstr" "github.com/open-telemetry/opentelemetry-operator/internal/components" ) @@ -33,9 +34,11 @@ func TestBuilder_Build(t *testing.T) { m map[string]interface{} } type want struct { - name string - ports []corev1.ServicePort - rules []rbacv1.PolicyRule + name string + ports []corev1.ServicePort + rules []rbacv1.PolicyRule + livenessProbe *corev1.Probe + readinessProbe *corev1.Probe } type fields[T any] struct { b components.Builder[T] @@ -44,12 +47,13 @@ func TestBuilder_Build(t *testing.T) { conf interface{} } type testCase[T any] struct { - name string - fields fields[T] - params params - want want - wantErr assert.ErrorAssertionFunc - wantRbacErr assert.ErrorAssertionFunc + name string + fields fields[T] + params params + want want + wantErr assert.ErrorAssertionFunc + wantRbacErr assert.ErrorAssertionFunc + wantLivenessErr assert.ErrorAssertionFunc } examplePortParser := func(logger logr.Logger, name string, defaultPort *corev1.ServicePort, config sampleConfig) ([]corev1.ServicePort, error) { if defaultPort != nil { @@ -57,6 +61,16 @@ func TestBuilder_Build(t *testing.T) { } return nil, nil } + exampleProbeGen := func(logger logr.Logger, config sampleConfig) (*corev1.Probe, error) { + return &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, nil + } tests := []testCase[sampleConfig]{ { name: "basic valid configuration", @@ -83,8 +97,9 @@ func TestBuilder_Build(t *testing.T) { }, rules: nil, }, - wantErr: assert.NoError, - wantRbacErr: assert.NoError, + wantErr: assert.NoError, + wantRbacErr: assert.NoError, + wantLivenessErr: assert.NoError, }, { name: "missing name", @@ -96,9 +111,10 @@ func TestBuilder_Build(t *testing.T) { params: params{ conf: sampleConfig{}, }, - want: want{}, - wantErr: assert.Error, - wantRbacErr: assert.NoError, + want: want{}, + wantErr: assert.Error, + wantRbacErr: assert.NoError, + wantLivenessErr: assert.NoError, }, { name: "complete configuration with RBAC rules", @@ -147,8 +163,9 @@ func TestBuilder_Build(t *testing.T) { }, }, }, - wantErr: assert.NoError, - wantRbacErr: assert.NoError, + wantErr: assert.NoError, + wantRbacErr: assert.NoError, + wantLivenessErr: assert.NoError, }, { name: "complete configuration with RBAC rules errors", @@ -186,8 +203,81 @@ func TestBuilder_Build(t *testing.T) { ports: nil, rules: nil, }, - wantErr: assert.NoError, - wantRbacErr: assert.Error, + wantErr: assert.NoError, + wantRbacErr: assert.Error, + wantLivenessErr: assert.NoError, + }, + { + name: "complete configuration with probe gen", + fields: fields[sampleConfig]{ + b: components.NewBuilder[sampleConfig](). + WithName("secure-service"). + WithPort(443). + WithProtocol(corev1.ProtocolTCP). + WithLivenessGen(exampleProbeGen). + WithReadinessGen(exampleProbeGen), + }, + params: params{ + conf: sampleConfig{ + example: "test", + number: 100, + m: map[string]interface{}{ + "key": "value", + }, + }, + }, + want: want{ + name: "__secure-service", + ports: nil, + livenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, + readinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, + }, + wantErr: assert.NoError, + wantRbacErr: assert.NoError, + wantLivenessErr: assert.NoError, + }, + { + name: "complete configuration with probe gen errors", + fields: fields[sampleConfig]{ + b: components.NewBuilder[sampleConfig](). + WithName("secure-service"). + WithPort(443). + WithProtocol(corev1.ProtocolTCP). + WithLivenessGen(func(logger logr.Logger, config sampleConfig) (*corev1.Probe, error) { + return nil, fmt.Errorf("no probe") + }), + }, + params: params{ + conf: sampleConfig{ + example: "test", + number: 100, + m: map[string]interface{}{ + "key": "value", + }, + }, + }, + want: want{ + name: "__secure-service", + ports: nil, + rules: nil, + }, + wantErr: assert.NoError, + wantRbacErr: assert.NoError, + wantLivenessErr: assert.Error, }, } for _, tt := range tests { @@ -205,6 +295,14 @@ func TestBuilder_Build(t *testing.T) { return } assert.Equalf(t, tt.want.rules, rules, "GetRBACRules()") + livenessProbe, livenessErr := got.GetLivenessProbe(logr.Discard(), tt.params.conf) + if tt.wantLivenessErr(t, livenessErr, "wantLivenessErr()") && livenessErr != nil { + return + } + assert.Equalf(t, tt.want.livenessProbe, livenessProbe, "GetLivenessProbe()") + readinessProbe, readinessErr := got.GetReadinessProbe(logr.Discard(), tt.params.conf) + assert.NoError(t, readinessErr) + assert.Equalf(t, tt.want.readinessProbe, readinessProbe, "GetReadinessProbe()") }) } } diff --git a/internal/components/component.go b/internal/components/component.go index d51690b8ae..9d5232740f 100644 --- a/internal/components/component.go +++ b/internal/components/component.go @@ -45,6 +45,10 @@ type PortParser[T any] func(logger logr.Logger, name string, defaultPort *corev1 // It's expected that type T is the configuration used by a parser. type RBACRuleGenerator[T any] func(logger logr.Logger, config T) ([]rbacv1.PolicyRule, error) +// ProbeGenerator is a function that generates a valid probe for a container given T +// It's expected that type T is the configuration used by a parser. +type ProbeGenerator[T any] func(logger logr.Logger, config T) (*corev1.Probe, error) + // ComponentType returns the type for a given component name. // components have a name like: // - mycomponent/custom @@ -90,6 +94,12 @@ type Parser interface { // GetRBACRules returns the rbac rules for this component GetRBACRules(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) + // GetLivenessProbe returns a liveness probe set for the collector + GetLivenessProbe(logger logr.Logger, config interface{}) (*corev1.Probe, error) + + // GetReadinessProbe returns a readiness probe set for the collector + GetReadinessProbe(logger logr.Logger, config interface{}) (*corev1.Probe, error) + // ParserType returns the type of this parser ParserType() string diff --git a/internal/components/extensions/healthcheckv1.go b/internal/components/extensions/healthcheckv1.go new file mode 100644 index 0000000000..3c517899d7 --- /dev/null +++ b/internal/components/extensions/healthcheckv1.go @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions + +import ( + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/components" +) + +const DefaultHealthcheckV1Port = 13133 + +type healthcheckV1Config struct { + components.SingleEndpointConfig `mapstructure:",squash"` + Path string `mapstructure:"path"` +} + +// HealthCheckV1Probe returns the probe configuration for the healthcheck v1 extension. +// Right now no TLS config is parsed. +func HealthCheckV1Probe(logger logr.Logger, config healthcheckV1Config) (*corev1.Probe, error) { + return &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: config.Path, + Port: intstr.FromInt32(config.GetPortNumOrDefault(logger, DefaultHealthcheckV1Port)), + }, + }, + }, nil +} diff --git a/internal/components/extensions/healthcheckv1_test.go b/internal/components/extensions/healthcheckv1_test.go new file mode 100644 index 0000000000..db10a9f71c --- /dev/null +++ b/internal/components/extensions/healthcheckv1_test.go @@ -0,0 +1,186 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions_test + +import ( + "fmt" + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/components/extensions" +) + +func TestHealthCheckV1Probe(t *testing.T) { + type args struct { + config interface{} + } + tests := []struct { + name string + args args + want *corev1.Probe + wantErr assert.ErrorAssertionFunc + }{ + { + name: "Valid path and custom port", + args: args{ + config: map[string]interface{}{ + "endpoint": "127.0.0.1:8080", + "path": "/healthz", + }, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt32(8080), + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "Valid path and default port", + args: args{ + config: map[string]interface{}{ + "endpoint": "127.0.0.1", + "path": "/healthz", + }, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt32(extensions.DefaultHealthcheckV1Port), + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "Empty path and custom port", + args: args{ + config: map[string]interface{}{ + "endpoint": "127.0.0.1:9090", + "path": "", + }, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "", + Port: intstr.FromInt32(9090), + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "Empty path and default port", + args: args{ + config: map[string]interface{}{ + "endpoint": "127.0.0.1", + "path": "", + }, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "", + Port: intstr.FromInt32(extensions.DefaultHealthcheckV1Port), + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "Nil path and custom port", + args: args{ + config: map[string]interface{}{ + "endpoint": "127.0.0.1:7070", + }, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "", + Port: intstr.FromInt32(7070), + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "Nil path and default port", + args: args{ + config: map[string]interface{}{ + "endpoint": "127.0.0.1", + }, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "", + Port: intstr.FromInt32(extensions.DefaultHealthcheckV1Port), + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "Invalid endpoint", + args: args{ + config: map[string]interface{}{ + "endpoint": 123, + "path": "/healthz", + }, + }, + want: nil, + wantErr: assert.Error, + }, + { + name: "Zero custom port, default port fallback", + args: args{ + config: map[string]interface{}{ + "endpoint": "127.0.0.1:0", + "path": "/healthz", + }, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt32(extensions.DefaultHealthcheckV1Port), + }, + }, + }, + wantErr: assert.NoError, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parser := extensions.ParserFor("health_check") + got, err := parser.GetLivenessProbe(logr.Discard(), tt.args.config) + if !tt.wantErr(t, err, fmt.Sprintf("GetLivenessProbe(%v)", tt.args.config)) { + return + } + assert.Equalf(t, tt.want, got, "GetLivenessProbe(%v)", tt.args.config) + }) + } +} diff --git a/internal/components/extensions/helpers.go b/internal/components/extensions/helpers.go new file mode 100644 index 0000000000..d05a04f3d9 --- /dev/null +++ b/internal/components/extensions/helpers.go @@ -0,0 +1,65 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions + +import ( + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/components" +) + +// registry holds a record of all known receiver parsers. +var registry = make(map[string]components.Parser) + +// Register adds a new parser builder to the list of known builders. +func Register(name string, p components.Parser) { + registry[name] = p +} + +// IsRegistered checks whether a parser is registered with the given name. +func IsRegistered(name string) bool { + _, ok := registry[name] + return ok +} + +// ParserFor returns a parser builder for the given exporter name. +func ParserFor(name string) components.Parser { + if parser, ok := registry[components.ComponentType(name)]; ok { + return parser + } + // We want the default for exporters to fail silently. + return components.NewBuilder[any]().WithName(name).MustBuild() +} + +var ( + componentParsers = []components.Parser{ + components.NewBuilder[healthcheckV1Config](). + WithName("health_check"). + WithPort(13133). + WithReadinessGen(HealthCheckV1Probe). + WithLivenessGen(HealthCheckV1Probe). + WithPortParser(func(logger logr.Logger, name string, defaultPort *corev1.ServicePort, config healthcheckV1Config) ([]corev1.ServicePort, error) { + return components.ParseSingleEndpointSilent(logger, name, defaultPort, &config.SingleEndpointConfig) + }). + MustBuild(), + } +) + +func init() { + for _, parser := range componentParsers { + Register(parser.ParserType(), parser) + } +} diff --git a/internal/components/extensions/helpers_test.go b/internal/components/extensions/helpers_test.go new file mode 100644 index 0000000000..826072aef9 --- /dev/null +++ b/internal/components/extensions/helpers_test.go @@ -0,0 +1,111 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions_test + +import ( + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + + "github.com/open-telemetry/opentelemetry-operator/internal/components" + "github.com/open-telemetry/opentelemetry-operator/internal/components/extensions" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func TestParserForReturns(t *testing.T) { + const testComponentName = "test" + parser := extensions.ParserFor(testComponentName) + assert.Equal(t, "test", parser.ParserType()) + assert.Equal(t, "__test", parser.ParserName()) + ports, err := parser.Ports(logr.Discard(), testComponentName, map[string]interface{}{ + "endpoint": "localhost:9000", + }) + assert.NoError(t, err) + assert.Len(t, ports, 0) // Should use the nop parser +} + +func TestCanRegister(t *testing.T) { + const testComponentName = "test" + extensions.Register(testComponentName, components.NewSinglePortParserBuilder(testComponentName, 9000).MustBuild()) + assert.True(t, extensions.IsRegistered(testComponentName)) + parser := extensions.ParserFor(testComponentName) + assert.Equal(t, "test", parser.ParserType()) + assert.Equal(t, "__test", parser.ParserName()) + ports, err := parser.Ports(logr.Discard(), testComponentName, map[string]interface{}{}) + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.Equal(t, ports[0].Port, int32(9000)) +} + +func TestExtensionsComponentParsers(t *testing.T) { + for _, tt := range []struct { + exporterName string + parserName string + defaultPort int + }{ + {"health_check", "__health_check", 13133}, + } { + t.Run(tt.exporterName, func(t *testing.T) { + t.Run("is registered", func(t *testing.T) { + assert.True(t, extensions.IsRegistered(tt.exporterName)) + }) + t.Run("bad config errors", func(t *testing.T) { + // prepare + parser := extensions.ParserFor(tt.exporterName) + + // test throwing in pure junk + _, err := parser.Ports(logr.Discard(), tt.exporterName, func() {}) + + // verify + assert.ErrorContains(t, err, "expected a map, got ") + }) + + t.Run("assigns the expected port", func(t *testing.T) { + // prepare + parser := extensions.ParserFor(tt.exporterName) + + // test + ports, err := parser.Ports(logr.Discard(), tt.exporterName, map[string]interface{}{}) + + if tt.defaultPort == 0 { + assert.Len(t, ports, 0) + return + } + // verify + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.EqualValues(t, tt.defaultPort, ports[0].Port) + assert.Equal(t, naming.PortName(tt.exporterName, int32(tt.defaultPort)), ports[0].Name) + }) + + t.Run("allows port to be overridden", func(t *testing.T) { + // prepare + parser := extensions.ParserFor(tt.exporterName) + + // test + ports, err := parser.Ports(logr.Discard(), tt.exporterName, map[string]interface{}{ + "endpoint": "0.0.0.0:65535", + }) + + // verify + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.EqualValues(t, 65535, ports[0].Port) + assert.Equal(t, naming.PortName(tt.exporterName, int32(tt.defaultPort)), ports[0].Name) + }) + }) + } +} diff --git a/internal/components/generic_parser.go b/internal/components/generic_parser.go index 9f0eed609c..ea5f334cca 100644 --- a/internal/components/generic_parser.go +++ b/internal/components/generic_parser.go @@ -30,10 +30,34 @@ var ( // GenericParser serves as scaffolding for custom parsing logic by isolating // functionality to idempotent functions. type GenericParser[T any] struct { - name string - option *Option[T] - portParser PortParser[T] - rbacGen RBACRuleGenerator[T] + name string + option *Option[T] + portParser PortParser[T] + rbacGen RBACRuleGenerator[T] + livenessGen ProbeGenerator[T] + readinessGen ProbeGenerator[T] +} + +func (g *GenericParser[T]) GetLivenessProbe(logger logr.Logger, config interface{}) (*corev1.Probe, error) { + if g.livenessGen == nil { + return nil, nil + } + var parsed T + if err := mapstructure.Decode(config, &parsed); err != nil { + return nil, err + } + return g.livenessGen(logger, parsed) +} + +func (g *GenericParser[T]) GetReadinessProbe(logger logr.Logger, config interface{}) (*corev1.Probe, error) { + if g.readinessGen == nil { + return nil, nil + } + var parsed T + if err := mapstructure.Decode(config, &parsed); err != nil { + return nil, err + } + return g.readinessGen(logger, parsed) } func (g *GenericParser[T]) GetRBACRules(logger logr.Logger, config interface{}) ([]rbacv1.PolicyRule, error) { diff --git a/internal/components/generic_parser_test.go b/internal/components/generic_parser_test.go index 549771ff8c..3fadd853e3 100644 --- a/internal/components/generic_parser_test.go +++ b/internal/components/generic_parser_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/util/intstr" "github.com/open-telemetry/opentelemetry-operator/internal/components" ) @@ -220,3 +221,154 @@ func TestGenericParser_GetRBACRules(t *testing.T) { }) } } + +func TestGenericParser_GetProbe(t *testing.T) { + type args struct { + logger logr.Logger + config interface{} + } + type testCase[T any] struct { + name string + g *components.GenericParser[T] + args args + livenessProbe *corev1.Probe + readinessProbe *corev1.Probe + wantLivenessErr assert.ErrorAssertionFunc + wantReadinessErr assert.ErrorAssertionFunc + } + probeFunc := func(logger logr.Logger, config *components.SingleEndpointConfig) (*corev1.Probe, error) { + if config.Endpoint == "" && config.ListenAddress == "" { + return nil, fmt.Errorf("either endpoint or listen_address must be specified") + } + return &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, nil + } + + tests := []testCase[*components.SingleEndpointConfig]{ + { + name: "valid config with endpoint", + g: components.NewSinglePortParserBuilder("test", 0).WithReadinessGen(probeFunc).WithLivenessGen(probeFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{ + "endpoint": "http://localhost:8080", + }, + }, + livenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, + readinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, + wantLivenessErr: assert.NoError, + wantReadinessErr: assert.NoError, + }, + { + name: "valid config with listen_address", + g: components.NewSinglePortParserBuilder("test", 0).WithReadinessGen(probeFunc).WithLivenessGen(probeFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{ + "listen_address": "0.0.0.0:9090", + }, + }, + livenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, + readinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/hello", + Port: intstr.FromInt32(8080), + }, + }, + }, + wantLivenessErr: assert.NoError, + wantReadinessErr: assert.NoError, + }, + { + name: "readiness invalid config with no endpoint or listen_address", + g: components.NewSinglePortParserBuilder("test", 0).WithReadinessGen(probeFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{}, + }, + readinessProbe: nil, + livenessProbe: nil, + wantReadinessErr: assert.Error, + wantLivenessErr: assert.NoError, + }, + { + name: "liveness invalid config with no endpoint or listen_address", + g: components.NewSinglePortParserBuilder("test", 0).WithLivenessGen(probeFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: map[string]interface{}{}, + }, + readinessProbe: nil, + livenessProbe: nil, + wantReadinessErr: assert.NoError, + wantLivenessErr: assert.Error, + }, + { + name: "liveness failed to parse config", + g: components.NewSinglePortParserBuilder("test", 0).WithLivenessGen(probeFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: func() {}, + }, + livenessProbe: nil, + readinessProbe: nil, + wantLivenessErr: assert.Error, + wantReadinessErr: assert.NoError, + }, + { + name: "readiness failed to parse config", + g: components.NewSinglePortParserBuilder("test", 0).WithReadinessGen(probeFunc).MustBuild(), + args: args{ + logger: logr.Discard(), + config: func() {}, + }, + livenessProbe: nil, + readinessProbe: nil, + wantLivenessErr: assert.NoError, + wantReadinessErr: assert.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + livenessProbe, err := tt.g.GetLivenessProbe(tt.args.logger, tt.args.config) + if !tt.wantLivenessErr(t, err, fmt.Sprintf("GetLivenessProbe(%v, %v)", tt.args.logger, tt.args.config)) { + return + } + assert.Equalf(t, tt.livenessProbe, livenessProbe, "GetLivenessProbe(%v, %v)", tt.args.logger, tt.args.config) + readinessProbe, err := tt.g.GetReadinessProbe(tt.args.logger, tt.args.config) + if !tt.wantReadinessErr(t, err, fmt.Sprintf("GetReadinessProbe(%v, %v)", tt.args.logger, tt.args.config)) { + return + } + assert.Equalf(t, tt.readinessProbe, readinessProbe, "GetReadinessProbe(%v, %v)", tt.args.logger, tt.args.config) + }) + } +} diff --git a/internal/components/multi_endpoint.go b/internal/components/multi_endpoint.go index c7a818c431..a7f3c2580c 100644 --- a/internal/components/multi_endpoint.go +++ b/internal/components/multi_endpoint.go @@ -72,6 +72,14 @@ func (m *MultiPortReceiver) ParserName() string { return fmt.Sprintf("__%s", m.name) } +func (m *MultiPortReceiver) GetLivenessProbe(logger logr.Logger, config interface{}) (*corev1.Probe, error) { + return nil, nil +} + +func (m *MultiPortReceiver) GetReadinessProbe(logger logr.Logger, config interface{}) (*corev1.Probe, error) { + return nil, nil +} + func (m *MultiPortReceiver) GetRBACRules(logr.Logger, interface{}) ([]rbacv1.PolicyRule, error) { return nil, nil } diff --git a/internal/components/multi_endpoint_test.go b/internal/components/multi_endpoint_test.go index deabf377a4..83baf24d5c 100644 --- a/internal/components/multi_endpoint_test.go +++ b/internal/components/multi_endpoint_test.go @@ -358,6 +358,12 @@ func TestMultiPortReceiver_Ports(t *testing.T) { rbacGen, err := s.GetRBACRules(logr.Discard(), tt.args.config) assert.NoError(t, err) assert.Nil(t, rbacGen) + livenessProbe, livenessErr := s.GetLivenessProbe(logr.Discard(), tt.args.config) + assert.NoError(t, livenessErr) + assert.Nil(t, livenessProbe) + readinessProbe, readinessErr := s.GetReadinessProbe(logr.Discard(), tt.args.config) + assert.NoError(t, readinessErr) + assert.Nil(t, readinessProbe) }) } } From c96376dbbe8460b1f3d0b2c26e9387aa1e3994c7 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 30 Aug 2024 17:40:32 -0400 Subject: [PATCH 14/18] Add parser for liveness and readiness probes --- apis/v1beta1/config.go | 42 +++- apis/v1beta1/config_test.go | 2 +- apis/v1beta1/zz_generated.deepcopy.go | 8 +- .../collector/adapters/config_to_probe.go | 158 --------------- .../adapters/config_to_probe_test.go | 187 ------------------ internal/manifests/collector/container.go | 63 +----- .../manifestutils/annotations_test.go | 10 +- 7 files changed, 52 insertions(+), 418 deletions(-) delete mode 100644 internal/manifests/collector/adapters/config_to_probe.go delete mode 100644 internal/manifests/collector/adapters/config_to_probe_test.go diff --git a/apis/v1beta1/config.go b/apis/v1beta1/config.go index fce6a610f4..397b1b831f 100644 --- a/apis/v1beta1/config.go +++ b/apis/v1beta1/config.go @@ -31,6 +31,7 @@ import ( "github.com/open-telemetry/opentelemetry-operator/internal/components" "github.com/open-telemetry/opentelemetry-operator/internal/components/exporters" + "github.com/open-telemetry/opentelemetry-operator/internal/components/extensions" "github.com/open-telemetry/opentelemetry-operator/internal/components/processors" "github.com/open-telemetry/opentelemetry-operator/internal/components/receivers" ) @@ -41,10 +42,11 @@ const ( KindReceiver ComponentKind = iota KindExporter KindProcessor + KindExtension ) func (c ComponentKind) String() string { - return [...]string{"receiver", "exporter", "processor"}[c] + return [...]string{"receiver", "exporter", "processor", "extension"}[c] } // AnyConfig represent parts of the config. @@ -108,6 +110,7 @@ func (c *Config) GetEnabledComponents() map[ComponentKind]map[string]interface{} KindReceiver: {}, KindProcessor: {}, KindExporter: {}, + KindExtension: {}, } for _, pipeline := range c.Service.Pipelines { if pipeline == nil { @@ -123,6 +126,9 @@ func (c *Config) GetEnabledComponents() map[ComponentKind]map[string]interface{} toReturn[KindProcessor][componentId] = struct{}{} } } + for _, componentId := range c.Service.Extensions { + toReturn[KindExtension][componentId] = struct{}{} + } return toReturn } @@ -227,6 +233,38 @@ func (c *Config) GetAllRbacRules(logger logr.Logger) ([]rbacv1.PolicyRule, error return c.getRbacRulesForComponentKinds(logger, KindReceiver, KindExporter, KindProcessor) } +// GetLivenessProbe gets the first enabled liveness probe. There should only ever be one extension enabled +// that provides the hinting for the liveness probe. +func (c *Config) GetLivenessProbe(logger logr.Logger) (*corev1.Probe, error) { + enabledComponents := c.GetEnabledComponents() + for componentName := range enabledComponents[KindExtension] { + // TODO: Clean up the naming here and make it simpler to use a retriever. + parser := extensions.ParserFor(componentName) + if probe, err := parser.GetLivenessProbe(logger, c.Extensions.Object[componentName]); err != nil { + return nil, err + } else if probe != nil { + return probe, nil + } + } + return nil, nil +} + +// GetReadinessProbe gets the first enabled readiness probe. There should only ever be one extension enabled +// that provides the hinting for the readiness probe. +func (c *Config) GetReadinessProbe(logger logr.Logger) (*corev1.Probe, error) { + enabledComponents := c.GetEnabledComponents() + for componentName := range enabledComponents[KindExtension] { + // TODO: Clean up the naming here and make it simpler to use a retriever. + parser := extensions.ParserFor(componentName) + if probe, err := parser.GetReadinessProbe(logger, c.Extensions.Object[componentName]); err != nil { + return nil, err + } else if probe != nil { + return probe, nil + } + } + return nil, nil +} + // Yaml encodes the current object and returns it as a string. func (c *Config) Yaml() (string, error) { var buf bytes.Buffer @@ -268,7 +306,7 @@ func (c *Config) nullObjects() []string { } type Service struct { - Extensions *[]string `json:"extensions,omitempty" yaml:"extensions,omitempty"` + Extensions []string `json:"extensions,omitempty" yaml:"extensions,omitempty"` // +kubebuilder:pruning:PreserveUnknownFields Telemetry *AnyConfig `json:"telemetry,omitempty" yaml:"telemetry,omitempty"` // +kubebuilder:pruning:PreserveUnknownFields diff --git a/apis/v1beta1/config_test.go b/apis/v1beta1/config_test.go index 6f36d18a93..0bfe96d7c3 100644 --- a/apis/v1beta1/config_test.go +++ b/apis/v1beta1/config_test.go @@ -143,7 +143,7 @@ func TestConfigYaml(t *testing.T) { }, }, Service: Service{ - Extensions: &[]string{"addon"}, + Extensions: []string{"addon"}, Telemetry: &AnyConfig{ Object: map[string]interface{}{ "insights": "yeah!", diff --git a/apis/v1beta1/zz_generated.deepcopy.go b/apis/v1beta1/zz_generated.deepcopy.go index d0334a8051..eaf24ed0ba 100644 --- a/apis/v1beta1/zz_generated.deepcopy.go +++ b/apis/v1beta1/zz_generated.deepcopy.go @@ -635,12 +635,8 @@ func (in *Service) DeepCopyInto(out *Service) { *out = *in if in.Extensions != nil { in, out := &in.Extensions, &out.Extensions - *out = new([]string) - if **in != nil { - in, out := *in, *out - *out = make([]string, len(*in)) - copy(*out, *in) - } + *out = make([]string, len(*in)) + copy(*out, *in) } if in.Telemetry != nil { in, out := &in.Telemetry, &out.Telemetry diff --git a/internal/manifests/collector/adapters/config_to_probe.go b/internal/manifests/collector/adapters/config_to_probe.go deleted file mode 100644 index 897b7db068..0000000000 --- a/internal/manifests/collector/adapters/config_to_probe.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adapters - -import ( - "errors" - "strings" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -var ( - errNoService = errors.New("no service available as part of the configuration") - errNoExtensions = errors.New("no extensions available as part of the configuration") - - errServiceNotAMap = errors.New("service property in the configuration doesn't contain valid services") - errExtensionsNotAMap = errors.New("extensions property in the configuration doesn't contain valid extensions") - - errNoExtensionHealthCheck = errors.New("extensions property in the configuration does not contain the expected health_check extension") - - ErrNoServiceExtensions = errors.New("service property in the configuration doesn't contain extensions") - - errServiceExtensionsNotSlice = errors.New("service extensions property in the configuration does not contain valid extensions") - ErrNoServiceExtensionHealthCheck = errors.New("no healthcheck extension available in service extension configuration") -) - -type probeConfiguration struct { - path string - port intstr.IntOrString -} - -const ( - defaultHealthCheckPath = "/" - defaultHealthCheckPort = 13133 -) - -// ConfigToContainerProbe converts the incoming configuration object into a container probe or returns an error. -func ConfigToContainerProbe(config map[interface{}]interface{}) (*corev1.Probe, error) { - serviceProperty, withService := config["service"] - if !withService { - return nil, errNoService - } - service, withSvcProperty := serviceProperty.(map[interface{}]interface{}) - if !withSvcProperty { - return nil, errServiceNotAMap - } - - serviceExtensionsProperty, withExtension := service["extensions"] - if !withExtension { - return nil, ErrNoServiceExtensions - } - - serviceExtensions, withExtProperty := serviceExtensionsProperty.([]interface{}) - if !withExtProperty { - return nil, errServiceExtensionsNotSlice - } - healthCheckServiceExtensions := make([]string, 0) - for _, ext := range serviceExtensions { - parsedExt, ok := ext.(string) - if ok && strings.HasPrefix(parsedExt, "health_check") { - healthCheckServiceExtensions = append(healthCheckServiceExtensions, parsedExt) - } - } - - if len(healthCheckServiceExtensions) == 0 { - return nil, ErrNoServiceExtensionHealthCheck - } - - extensionsProperty, ok := config["extensions"] - if !ok { - return nil, errNoExtensions - } - extensions, ok := extensionsProperty.(map[interface{}]interface{}) - if !ok { - return nil, errExtensionsNotAMap - } - // in the event of multiple health_check service extensions defined, we arbitrarily take the first one found - for _, healthCheckForProbe := range healthCheckServiceExtensions { - healthCheckExtension, ok := extensions[healthCheckForProbe] - if ok { - return createProbeFromExtension(healthCheckExtension) - } - } - - return nil, errNoExtensionHealthCheck -} - -func createProbeFromExtension(extension interface{}) (*corev1.Probe, error) { - probeCfg := extractProbeConfigurationFromExtension(extension) - return &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: probeCfg.path, - Port: probeCfg.port, - }, - }, - }, nil -} - -func extractProbeConfigurationFromExtension(ext interface{}) probeConfiguration { - extensionCfg, ok := ext.(map[interface{}]interface{}) - if !ok { - return defaultProbeConfiguration() - } - return probeConfiguration{ - path: extractPathFromExtensionConfig(extensionCfg), - port: extractPortFromExtensionConfig(extensionCfg), - } -} - -func defaultProbeConfiguration() probeConfiguration { - return probeConfiguration{ - path: defaultHealthCheckPath, - port: intstr.FromInt(defaultHealthCheckPort), - } -} - -func extractPathFromExtensionConfig(cfg map[interface{}]interface{}) string { - if path, ok := cfg["path"]; ok { - if parsedPath, ok := path.(string); ok { - return parsedPath - } - } - return defaultHealthCheckPath -} - -func extractPortFromExtensionConfig(cfg map[interface{}]interface{}) intstr.IntOrString { - endpoint, ok := cfg["endpoint"] - if !ok { - return defaultHealthCheckEndpoint() - } - parsedEndpoint, ok := endpoint.(string) - if !ok { - return defaultHealthCheckEndpoint() - } - endpointComponents := strings.Split(parsedEndpoint, ":") - if len(endpointComponents) != 2 { - return defaultHealthCheckEndpoint() - } - return intstr.Parse(endpointComponents[1]) -} - -func defaultHealthCheckEndpoint() intstr.IntOrString { - return intstr.FromInt(defaultHealthCheckPort) -} diff --git a/internal/manifests/collector/adapters/config_to_probe_test.go b/internal/manifests/collector/adapters/config_to_probe_test.go deleted file mode 100644 index 89e1f97349..0000000000 --- a/internal/manifests/collector/adapters/config_to_probe_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adapters - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestConfigToProbeShouldCreateProbeFor(t *testing.T) { - tests := []struct { - desc string - config string - expectedPath string - expectedPort int32 - }{ - { - desc: "SimpleHappyPath", - expectedPort: int32(13133), - expectedPath: "/", - config: `extensions: - health_check: -service: - extensions: [health_check]`, - }, { - desc: "CustomEndpointAndPath", - expectedPort: int32(1234), - expectedPath: "/checkit", - config: `extensions: - health_check: - endpoint: localhost:1234 - path: /checkit -service: - extensions: [health_check]`, - }, { - desc: "CustomEndpointAndDefaultPath", - expectedPort: int32(1234), - expectedPath: "/", - config: `extensions: - health_check: - endpoint: localhost:1234 -service: - extensions: [health_check]`, - }, { - desc: "CustomEndpointWithJustPortAndDefaultPath", - expectedPort: int32(1234), - expectedPath: "/", - config: `extensions: - health_check: - endpoint: :1234 -service: - extensions: [health_check]`, - }, { - desc: "DefaultEndpointAndCustomPath", - expectedPort: int32(13133), - expectedPath: "/checkit", - config: `extensions: - health_check: - path: /checkit -service: - extensions: [health_check]`, - }, { - desc: "DefaultEndpointForUnexpectedEndpoint", - expectedPort: int32(13133), - expectedPath: "/", - config: `extensions: - health_check: - endpoint: 0:0:0" -service: - extensions: [health_check]`, - }, { - desc: "DefaultEndpointForUnparseablendpoint", - expectedPort: int32(13133), - expectedPath: "/", - config: `extensions: - health_check: - endpoint: - this: should-not-be-a-map" -service: - extensions: [health_check]`, - }, { - desc: "WillUseSecondServiceExtension", - config: `extensions: - health_check: -service: - extensions: [health_check/1, health_check]`, - expectedPort: int32(13133), - expectedPath: "/", - }, - } - - for _, test := range tests { - // prepare - config, err := ConfigFromString(test.config) - require.NoError(t, err, test.desc) - require.NotEmpty(t, config, test.desc) - - // test - actualProbe, err := ConfigToContainerProbe(config) - assert.NoError(t, err) - assert.Equal(t, test.expectedPath, actualProbe.HTTPGet.Path, test.desc) - assert.Equal(t, test.expectedPort, actualProbe.HTTPGet.Port.IntVal, test.desc) - assert.Equal(t, "", actualProbe.HTTPGet.Host, test.desc) - } -} - -func TestConfigToProbeShouldErrorIf(t *testing.T) { - tests := []struct { - expectedErr error - desc string - config string - }{ - { - desc: "NoHealthCheckExtension", - config: `extensions: - pprof: -service: - extensions: [health_check]`, - expectedErr: errNoExtensionHealthCheck, - }, { - desc: "BadlyFormattedExtensions", - config: `extensions: [hi] -service: - extensions: [health_check]`, - expectedErr: errExtensionsNotAMap, - }, { - desc: "NoExtensions", - config: `service: - extensions: [health_check]`, - expectedErr: errNoExtensions, - }, { - desc: "NoHealthCheckInServiceExtensions", - config: `service: - extensions: [pprof]`, - expectedErr: ErrNoServiceExtensionHealthCheck, - }, { - desc: "BadlyFormattedServiceExtensions", - config: `service: - extensions: - this: should-not-be-a-map`, - expectedErr: errServiceExtensionsNotSlice, - }, { - desc: "NoServiceExtensions", - config: `service: - pipelines: - traces: - receivers: [otlp]`, - expectedErr: ErrNoServiceExtensions, - }, { - desc: "BadlyFormattedService", - config: `extensions: - health_check: -service: [hi]`, - expectedErr: errServiceNotAMap, - }, { - desc: "NoService", - config: `extensions: - health_check:`, - expectedErr: errNoService, - }, - } - - for _, test := range tests { - // prepare - config, err := ConfigFromString(test.config) - require.NoError(t, err, test.desc) - require.NotEmpty(t, config, test.desc) - - // test - _, err = ConfigToContainerProbe(config) - assert.Equal(t, test.expectedErr, err, test.desc) - } -} diff --git a/internal/manifests/collector/container.go b/internal/manifests/collector/container.go index de53314ffb..39d6b63e28 100644 --- a/internal/manifests/collector/container.go +++ b/internal/manifests/collector/container.go @@ -15,7 +15,6 @@ package collector import ( - "errors" "fmt" "path" "sort" @@ -27,7 +26,6 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1beta1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" "github.com/open-telemetry/opentelemetry-operator/internal/naming" "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" ) @@ -43,12 +41,6 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1beta1.OpenTeleme image = cfg.CollectorImage() } - configYaml, err := otelcol.Spec.Config.Yaml() - if err != nil { - logger.Error(err, "could not convert json to yaml") - return corev1.Container{} - } - // build container ports from service ports ports, err := getConfigContainerPorts(logger, otelcol.Spec.Config) if err != nil { @@ -140,28 +132,13 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1beta1.OpenTeleme }) } - var livenessProbe *corev1.Probe - var readinessProbe *corev1.Probe - if configFromString, err := adapters.ConfigFromString(configYaml); err == nil { - if probe, err := getProbe(configFromString, otelcol.Spec.LivenessProbe); err == nil { - livenessProbe = probe - } else if errors.Is(err, adapters.ErrNoServiceExtensions) { - logger.V(4).Info("extensions not configured, skipping liveness probe creation") - } else if errors.Is(err, adapters.ErrNoServiceExtensionHealthCheck) { - logger.V(4).Info("healthcheck extension not configured, skipping liveness probe creation") - } else { - logger.Error(err, "cannot create liveness probe.") - } - - if probe, err := getProbe(configFromString, otelcol.Spec.ReadinessProbe); err == nil { - readinessProbe = probe - } else if errors.Is(err, adapters.ErrNoServiceExtensions) { - logger.V(4).Info("extensions not configured, skipping readiness probe creation") - } else if errors.Is(err, adapters.ErrNoServiceExtensionHealthCheck) { - logger.V(4).Info("healthcheck extension not configured, skipping readiness probe creation") - } else { - logger.Error(err, "cannot create readiness probe.") - } + livenessProbe, livenessProbeErr := otelcol.Spec.Config.GetLivenessProbe(logger) + if livenessProbeErr != nil { + logger.Error(livenessProbeErr, "cannot create liveness probe.") + } + readinessProbe, readinessProbeErr := otelcol.Spec.Config.GetReadinessProbe(logger) + if readinessProbeErr != nil { + logger.Error(readinessProbeErr, "cannot create readiness probe.") } if featuregate.SetGolangFlags.IsEnabled() { @@ -256,29 +233,3 @@ func portMapToList(portMap map[string]corev1.ContainerPort) []corev1.ContainerPo }) return ports } - -func getProbe(config map[interface{}]interface{}, probeConfig *v1beta1.Probe) (*corev1.Probe, error) { - probe, err := adapters.ConfigToContainerProbe(config) - if err != nil { - return nil, err - } - if probeConfig != nil { - if probeConfig.InitialDelaySeconds != nil { - probe.InitialDelaySeconds = *probeConfig.InitialDelaySeconds - } - if probeConfig.PeriodSeconds != nil { - probe.PeriodSeconds = *probeConfig.PeriodSeconds - } - if probeConfig.FailureThreshold != nil { - probe.FailureThreshold = *probeConfig.FailureThreshold - } - if probeConfig.SuccessThreshold != nil { - probe.SuccessThreshold = *probeConfig.SuccessThreshold - } - if probeConfig.TimeoutSeconds != nil { - probe.TimeoutSeconds = *probeConfig.TimeoutSeconds - } - probe.TerminationGracePeriodSeconds = probeConfig.TerminationGracePeriodSeconds - } - return probe, nil -} diff --git a/internal/manifests/manifestutils/annotations_test.go b/internal/manifests/manifestutils/annotations_test.go index 818adff3f7..7ae4673a8e 100644 --- a/internal/manifests/manifestutils/annotations_test.go +++ b/internal/manifests/manifestutils/annotations_test.go @@ -34,10 +34,7 @@ func TestDefaultAnnotations(t *testing.T) { Spec: v1beta1.OpenTelemetryCollectorSpec{ Config: v1beta1.Config{ Service: v1beta1.Service{ - Extensions: func() *[]string { - res := []string{"test"} - return &res - }(), + Extensions: []string{"test"}, }, }, }, @@ -101,10 +98,7 @@ func TestUserAnnotations(t *testing.T) { Spec: v1beta1.OpenTelemetryCollectorSpec{ Config: v1beta1.Config{ Service: v1beta1.Service{ - Extensions: func() *[]string { - res := []string{"test2"} - return &res - }(), + Extensions: []string{"test2"}, }, }, }, From 440855ebd9b60fdf5fc56d02ac44f28b050c4ccf Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 30 Aug 2024 17:44:30 -0400 Subject: [PATCH 15/18] Set defaults --- internal/manifests/collector/container.go | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/internal/manifests/collector/container.go b/internal/manifests/collector/container.go index 39d6b63e28..3cf0b1a2e4 100644 --- a/internal/manifests/collector/container.go +++ b/internal/manifests/collector/container.go @@ -135,10 +135,14 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1beta1.OpenTeleme livenessProbe, livenessProbeErr := otelcol.Spec.Config.GetLivenessProbe(logger) if livenessProbeErr != nil { logger.Error(livenessProbeErr, "cannot create liveness probe.") + } else { + defaultProbeSettings(livenessProbe, otelcol.Spec.LivenessProbe) } readinessProbe, readinessProbeErr := otelcol.Spec.Config.GetReadinessProbe(logger) if readinessProbeErr != nil { logger.Error(readinessProbeErr, "cannot create readiness probe.") + } else { + defaultProbeSettings(readinessProbe, otelcol.Spec.ReadinessProbe) } if featuregate.SetGolangFlags.IsEnabled() { @@ -233,3 +237,24 @@ func portMapToList(portMap map[string]corev1.ContainerPort) []corev1.ContainerPo }) return ports } + +func defaultProbeSettings(probe *corev1.Probe, probeConfig *v1beta1.Probe) { + if probe != nil && probeConfig != nil { + if probeConfig.InitialDelaySeconds != nil { + probe.InitialDelaySeconds = *probeConfig.InitialDelaySeconds + } + if probeConfig.PeriodSeconds != nil { + probe.PeriodSeconds = *probeConfig.PeriodSeconds + } + if probeConfig.FailureThreshold != nil { + probe.FailureThreshold = *probeConfig.FailureThreshold + } + if probeConfig.SuccessThreshold != nil { + probe.SuccessThreshold = *probeConfig.SuccessThreshold + } + if probeConfig.TimeoutSeconds != nil { + probe.TimeoutSeconds = *probeConfig.TimeoutSeconds + } + probe.TerminationGracePeriodSeconds = probeConfig.TerminationGracePeriodSeconds + } +} From 913d8f6b7e11927fa582006a72761c7896047367 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 30 Aug 2024 17:45:24 -0400 Subject: [PATCH 16/18] chlog --- .chloggen/improve-probe-parsing.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 .chloggen/improve-probe-parsing.yaml diff --git a/.chloggen/improve-probe-parsing.yaml b/.chloggen/improve-probe-parsing.yaml new file mode 100755 index 0000000000..ec9b3fe8c2 --- /dev/null +++ b/.chloggen/improve-probe-parsing.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) +component: collector + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Improves healthcheck parsing capabilities, allowing for future extensions to configure a healthcheck other than the v1 healthcheck extension. + +# One or more tracking issues related to the change +issues: [3184] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: From 1cb601b269aedec2f142d1df207998bdfa24773e Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 30 Aug 2024 17:58:24 -0400 Subject: [PATCH 17/18] fix tests --- apis/v1beta1/config.go | 6 +++++- apis/v1beta1/config_test.go | 12 ++++++++++++ internal/components/extensions/healthcheckv1.go | 11 +++++++++-- internal/components/extensions/healthcheckv1_test.go | 8 ++++---- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/apis/v1beta1/config.go b/apis/v1beta1/config.go index 397b1b831f..b34601bf05 100644 --- a/apis/v1beta1/config.go +++ b/apis/v1beta1/config.go @@ -168,6 +168,8 @@ func (c *Config) getRbacRulesForComponentKinds(logger logr.Logger, componentKind } else { cfg = *c.Processors } + case KindExtension: + continue } for componentName := range enabledComponents[componentKind] { // TODO: Clean up the naming here and make it simpler to use a retriever. @@ -197,7 +199,9 @@ func (c *Config) getPortsForComponentKinds(logger logr.Logger, componentKinds .. retriever = exporters.ParserFor cfg = c.Exporters case KindProcessor: - break + continue + case KindExtension: + continue } for componentName := range enabledComponents[componentKind] { // TODO: Clean up the naming here and make it simpler to use a retriever. diff --git a/apis/v1beta1/config_test.go b/apis/v1beta1/config_test.go index 0bfe96d7c3..31895b3252 100644 --- a/apis/v1beta1/config_test.go +++ b/apis/v1beta1/config_test.go @@ -304,6 +304,7 @@ func TestConfig_GetEnabledComponents(t *testing.T) { "bar": struct{}{}, "count": struct{}{}, }, + KindExtension: {}, }, }, { @@ -321,6 +322,7 @@ func TestConfig_GetEnabledComponents(t *testing.T) { KindExporter: { "prometheus": struct{}{}, }, + KindExtension: {}, }, }, { @@ -339,6 +341,11 @@ func TestConfig_GetEnabledComponents(t *testing.T) { "otlp": struct{}{}, "prometheus": struct{}{}, }, + KindExtension: { + "health_check": struct{}{}, + "pprof": struct{}{}, + "zpages": struct{}{}, + }, }, }, { @@ -352,6 +359,9 @@ func TestConfig_GetEnabledComponents(t *testing.T) { KindExporter: { "otlp/auth": struct{}{}, }, + KindExtension: { + "oauth2client": struct{}{}, + }, }, }, { @@ -365,6 +375,7 @@ func TestConfig_GetEnabledComponents(t *testing.T) { KindExporter: { "debug": struct{}{}, }, + KindExtension: {}, }, }, { @@ -374,6 +385,7 @@ func TestConfig_GetEnabledComponents(t *testing.T) { KindReceiver: {}, KindProcessor: {}, KindExporter: {}, + KindExtension: {}, }, }, } diff --git a/internal/components/extensions/healthcheckv1.go b/internal/components/extensions/healthcheckv1.go index 3c517899d7..49769f2f8b 100644 --- a/internal/components/extensions/healthcheckv1.go +++ b/internal/components/extensions/healthcheckv1.go @@ -22,7 +22,10 @@ import ( "github.com/open-telemetry/opentelemetry-operator/internal/components" ) -const DefaultHealthcheckV1Port = 13133 +const ( + DefaultHealthcheckV1Path = "/" + DefaultHealthcheckV1Port = 13133 +) type healthcheckV1Config struct { components.SingleEndpointConfig `mapstructure:",squash"` @@ -32,10 +35,14 @@ type healthcheckV1Config struct { // HealthCheckV1Probe returns the probe configuration for the healthcheck v1 extension. // Right now no TLS config is parsed. func HealthCheckV1Probe(logger logr.Logger, config healthcheckV1Config) (*corev1.Probe, error) { + path := config.Path + if len(path) == 0 { + path = DefaultHealthcheckV1Path + } return &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: config.Path, + Path: path, Port: intstr.FromInt32(config.GetPortNumOrDefault(logger, DefaultHealthcheckV1Port)), }, }, diff --git a/internal/components/extensions/healthcheckv1_test.go b/internal/components/extensions/healthcheckv1_test.go index db10a9f71c..52f857f864 100644 --- a/internal/components/extensions/healthcheckv1_test.go +++ b/internal/components/extensions/healthcheckv1_test.go @@ -83,7 +83,7 @@ func TestHealthCheckV1Probe(t *testing.T) { want: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: "", + Path: "/", Port: intstr.FromInt32(9090), }, }, @@ -101,7 +101,7 @@ func TestHealthCheckV1Probe(t *testing.T) { want: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: "", + Path: "/", Port: intstr.FromInt32(extensions.DefaultHealthcheckV1Port), }, }, @@ -118,7 +118,7 @@ func TestHealthCheckV1Probe(t *testing.T) { want: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: "", + Path: "/", Port: intstr.FromInt32(7070), }, }, @@ -135,7 +135,7 @@ func TestHealthCheckV1Probe(t *testing.T) { want: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: "", + Path: "/", Port: intstr.FromInt32(extensions.DefaultHealthcheckV1Port), }, }, From b9a93689e5442424a017d7dab2074cee10d4e6e5 Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Mon, 23 Sep 2024 11:23:35 -0400 Subject: [PATCH 18/18] fix merge --- internal/components/builder.go | 28 +++++++++++++-------------- internal/components/generic_parser.go | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/internal/components/builder.go b/internal/components/builder.go index 7b85653ce7..be459cc513 100644 --- a/internal/components/builder.go +++ b/internal/components/builder.go @@ -26,14 +26,14 @@ import ( type ParserOption[ComponentConfigType any] func(*Settings[ComponentConfigType]) type Settings[ComponentConfigType any] struct { - protocol corev1.Protocol - appProtocol *string - targetPort intstr.IntOrString - nodePort int32 - name string - port int32 - portParser PortParser[ComponentConfigType] - rbacGen RBACRuleGenerator[ComponentConfigType] + protocol corev1.Protocol + appProtocol *string + targetPort intstr.IntOrString + nodePort int32 + name string + port int32 + portParser PortParser[ComponentConfigType] + rbacGen RBACRuleGenerator[ComponentConfigType] livenessGen ProbeGenerator[ComponentConfigType] readinessGen ProbeGenerator[ComponentConfigType] } @@ -105,14 +105,15 @@ func (b Builder[ComponentConfigType]) WithRbacGen(rbacGen RBACRuleGenerator[Comp o.rbacGen = rbacGen }) } - + func (b Builder[ComponentConfigType]) WithLivenessGen(livenessGen ProbeGenerator[ComponentConfigType]) Builder[ComponentConfigType] { - return append(b, func(o *Option[ComponentConfigType]) { + return append(b, func(o *Settings[ComponentConfigType]) { o.livenessGen = livenessGen }) } + func (b Builder[ComponentConfigType]) WithReadinessGen(readinessGen ProbeGenerator[ComponentConfigType]) Builder[ComponentConfigType] { - return append(b, func(o *Option[ComponentConfigType]) { + return append(b, func(o *Settings[ComponentConfigType]) { o.readinessGen = readinessGen }) } @@ -129,9 +130,8 @@ func (b Builder[ComponentConfigType]) Build() (*GenericParser[ComponentConfigTyp rbacGen: o.rbacGen, livenessGen: o.livenessGen, readinessGen: o.readinessGen, - settings: o, - - }, nil + settings: o, + }, nil } func (b Builder[ComponentConfigType]) MustBuild() *GenericParser[ComponentConfigType] { diff --git a/internal/components/generic_parser.go b/internal/components/generic_parser.go index 044c328ed6..6bead9442c 100644 --- a/internal/components/generic_parser.go +++ b/internal/components/generic_parser.go @@ -31,7 +31,7 @@ var ( // functionality to idempotent functions. type GenericParser[T any] struct { name string - settings *Settings[T] + settings *Settings[T] portParser PortParser[T] rbacGen RBACRuleGenerator[T] livenessGen ProbeGenerator[T]