diff --git a/README.md b/README.md index b20504c..656bc78 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ spec: spec: exporters: # OTLP HTTP exporter settings - otlphttp: + otlp_http: enabled: true endpoint: "https://opentelemetry-receiver.example.org" ``` @@ -106,7 +106,7 @@ spec: spec: exporters: # OTLP HTTP exporter settings - otlphttp: + otlp_http: enabled: true endpoint: "https://opentelemetry-receiver.example.org" token: @@ -144,6 +144,41 @@ the example above to the extension, you should first create the respective secrets in the shoot project namespace, which can then be referenced via [Gardener Referenced Resources](https://gardener.cloud/docs/gardener/extensions/referenced-resources/#referenced-resources). +This example snippet enables the extension to forward the signals of the +control-plane components to a remote collector using the [OTLP gRPC exporter](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter). + +``` yaml + extensions: + - type: otelcol + providerConfig: + apiVersion: otelcol.extensions.gardener.cloud/v1alpha1 + kind: CollectorConfig + spec: + # Exporters settings + exporters: + # OTLP gRPC exporter settings + otlp_grpc: + enabled: true + endpoint: "https://opentelemetry-receiver.default.svc.cluster.local:4317" + token: + resourceRef: + name: otelcol-bearer-token + dataKey: token + tls: + ca: + resourceRef: + name: otelcol-tls + dataKey: ca.crt + cert: + resourceRef: + name: otelcol-tls + dataKey: client.crt + key: + resourceRef: + name: otelcol-tls + dataKey: client.key +``` + For additional configuration settings, which can be provided to the extension, please make sure to check the [OTel Extension API spec documentation](./docs/api-reference/otelcol.extensions.gardener.cloud.md). diff --git a/cmd/extension/main.go b/cmd/extension/main.go index 1f99198..1634b79 100644 --- a/cmd/extension/main.go +++ b/cmd/extension/main.go @@ -11,7 +11,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" - managercmd "github.com/gardener/gardener-extension-otelcol/cmd/extension/internal/manager" "github.com/gardener/gardener-extension-otelcol/pkg/version" ) @@ -22,7 +21,7 @@ func main() { EnableShellCompletion: true, Usage: "Gardener Extension for OpenTelemetry Collector", Commands: []*cli.Command{ - managercmd.New(), + NewManagerCommand(), }, } diff --git a/cmd/extension/internal/manager/manager.go b/cmd/extension/manager.go similarity index 98% rename from cmd/extension/internal/manager/manager.go rename to cmd/extension/manager.go index 90e3b8f..596d87f 100644 --- a/cmd/extension/internal/manager/manager.go +++ b/cmd/extension/manager.go @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package manager +package main import ( "context" @@ -123,8 +123,8 @@ func getFlags(ctx context.Context) *flags { return conf } -// New creates a new [cli.Command] for running the controller manager. -func New() *cli.Command { +// NewManagerCommand creates a new [cli.Command] for running the controller manager. +func NewManagerCommand() *cli.Command { flags := flags{ gardenletFeatureGates: make(map[featuregate.Feature]bool), } diff --git a/docs/api-reference/otelcol.extensions.gardener.cloud.md b/docs/api-reference/otelcol.extensions.gardener.cloud.md index 409e247..cb36914 100644 --- a/docs/api-reference/otelcol.extensions.gardener.cloud.md +++ b/docs/api-reference/otelcol.extensions.gardener.cloud.md @@ -43,7 +43,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `otlphttp` _[OTLPHTTPExporterConfig](#otlphttpexporterconfig)_ | HTTPExporter provides the OTLP HTTP Exporter settings. | | | +| `otlp_grpc` _[OTLPGRPCExporterConfig](#otlpgrpcexporterconfig)_ | OTLPGRPCExporter provides the OTLP gRPC Exporter settings. | | | +| `otlp_http` _[OTLPHTTPExporterConfig](#otlphttpexporterconfig)_ | HTTPExporter provides the OTLP HTTP Exporter settings. | | | | `debug` _[DebugExporterConfig](#debugexporterconfig)_ | DebugExporter provides the settings for the debug exporter. | | | @@ -98,6 +99,7 @@ Compression specifies the compression used by the collector. _Appears in:_ +- [OTLPGRPCExporterConfig](#otlpgrpcexporterconfig) - [OTLPHTTPExporterConfig](#otlphttpexporterconfig) | Field | Description | @@ -228,6 +230,34 @@ _Appears in:_ | `detailed` | MetricsVerbosityLevelDetailed configures the collector with the most
verbose level, which includes dimensions and views.
| +#### OTLPGRPCExporterConfig + + + +OTLPGRPCExporterConfig provides the OTLP gRPC Exporter config settings. + +See [OTLP gRPC Exporter] for more details. + +[OTLP gRPC Exporter]: https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter + + + +_Appears in:_ +- [CollectorExportersConfig](#collectorexportersconfig) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `enabled` _boolean_ | Enabled specifies whether the OTLP gRPC exporter is enabled or not. | | | +| `endpoint` _string_ | Endpoint specifies the gRPC endpoint to which signals will be exported.
Check the link below for more details about the format of this field.
https://github.com/grpc/grpc/blob/master/doc/naming.md | | | +| `tls` _[TLSConfig](#tlsconfig)_ | TLS specifies the TLS configuration settings for the exporter. | | | +| `token` _[ResourceReference](#resourcereference)_ | Token references a bearer token for authentication. | | | +| `timeout` _[Duration](#duration)_ | Timeout specifies the time to wait per individual attempt to send
data to the backend. | | | +| `read_buffer_size` _integer_ | ReadBufferSize specifies the ReadBufferSize for the gRPC
client. Default value is [DefaultGRPCExporterClientReadBufferSize]. | | | +| `write_buffer_size` _integer_ | WriteBufferSize specifies the WriteBufferSize for the gRPC
client. Default value is [DefaultGRPCExporterClientWriteBufferSize]. | | | +| `retry_on_failure` _[RetryOnFailureConfig](#retryonfailureconfig)_ | RetryOnFailure specifies the retry policy of the exporter. | | | +| `compression` _[Compression](#compression)_ | Compression specifies the compression to use. The default value is
[CompressionGzip]. | | | + + #### OTLPHTTPExporterConfig @@ -253,9 +283,9 @@ _Appears in:_ | `profiles_endpoint` _string_ | ProfilesEndpoint specifies the target URL to send profile data to, e.g. https://example.com:4318/v1development/profiles.
When this setting is present the endpoint setting is ignored for
profile data. | | | | `tls` _[TLSConfig](#tlsconfig)_ | TLS specifies the TLS configuration settings for the exporter. | | | | `token` _[ResourceReference](#resourcereference)_ | Token references a bearer token for authentication. | | | -| `timeout` _[Duration](#duration)_ | Timeout specifies the HTTP request time limit. Default value is
[DefaultExporterClientTimeout]. | | | -| `read_buffer_size` _integer_ | ReadBufferSize specifies the ReadBufferSize for the HTTP
client. Default value is [DefaultExporterClientReadBufferSize]. | | | -| `write_buffer_size` _integer_ | WriteBufferSize specifies the WriteBufferSize for the HTTP
client. Default value is [DefaultExporterClientWriteBufferSize]. | | | +| `timeout` _[Duration](#duration)_ | Timeout specifies the HTTP request time limit. Default value is
[DefaultHTTPExporterClientTimeout]. | | | +| `read_buffer_size` _integer_ | ReadBufferSize specifies the ReadBufferSize for the HTTP
client. Default value is [DefaultHTTPExporterClientReadBufferSize]. | | | +| `write_buffer_size` _integer_ | WriteBufferSize specifies the WriteBufferSize for the HTTP
client. Default value is [DefaultHTTPExporterClientWriteBufferSize]. | | | | `encoding` _[MessageEncoding](#messageencoding)_ | Encoding specifies the encoding to use for the messages. The default
value is [MessageEncodingProto]. | | | | `retry_on_failure` _[RetryOnFailureConfig](#retryonfailureconfig)_ | RetryOnFailure specifies the retry policy of the exporter. | | | | `compression` _[Compression](#compression)_ | Compression specifies the compression to use. The default value is
[CompressionGzip]. | | | @@ -270,6 +300,7 @@ ResourceReference references data from a Secret. _Appears in:_ +- [OTLPGRPCExporterConfig](#otlpgrpcexporterconfig) - [OTLPHTTPExporterConfig](#otlphttpexporterconfig) - [TLSConfig](#tlsconfig) @@ -304,6 +335,7 @@ RetryOnFailureConfig provides the retry policy for an exporter. _Appears in:_ +- [OTLPGRPCExporterConfig](#otlpgrpcexporterconfig) - [OTLPHTTPExporterConfig](#otlphttpexporterconfig) | Field | Description | Default | Validation | @@ -324,6 +356,7 @@ TLSConfig provides the TLS settings used by exporters. _Appears in:_ +- [OTLPGRPCExporterConfig](#otlpgrpcexporterconfig) - [OTLPHTTPExporterConfig](#otlphttpexporterconfig) | Field | Description | Default | Validation | diff --git a/examples/opentelemetry-receiver.yaml b/examples/opentelemetry-receiver.yaml index c176801..7fb2ebc 100644 --- a/examples/opentelemetry-receiver.yaml +++ b/examples/opentelemetry-receiver.yaml @@ -163,8 +163,7 @@ data: protocols: http: include_metadata: true - endpoint: 0.0.0.0:55690 - compression_algorithms: ["", "gzip"] + endpoint: 0.0.0.0:4318 # Enable TLS with client certificate verification tls: cert_file: /etc/otel/tls/tls.crt @@ -173,6 +172,17 @@ data: # Enable authentication for HTTP endpoint auth: authenticator: bearertokenauth/server + grpc: + include_metadata: true + endpoint: 0.0.0.0:4317 + # Enable TLS with client certificate verification + tls: + cert_file: /etc/otel/tls/tls.crt + key_file: /etc/otel/tls/tls.key + client_ca_file: /etc/otel/tls/ca.crt + # Enable authentication for gRPC endpoint + auth: + authenticator: bearertokenauth/server exporters: debug: @@ -207,9 +217,14 @@ metadata: namespace: default spec: ports: - - port: 8443 + - name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - name: otlp-http + port: 4318 protocol: TCP - targetPort: 55690 + targetPort: 4318 selector: app.kubernetes.io/component: opentelemetry-receiver type: ClusterIP @@ -242,7 +257,7 @@ spec: secretKeyRef: name: opentelemetry-receiver-auth key: bearertoken - image: otel/opentelemetry-collector-contrib:0.141.0 + image: otel/opentelemetry-collector-contrib:0.144.0 imagePullPolicy: IfNotPresent name: otc-container resources: diff --git a/examples/shoot.yaml b/examples/shoot.yaml index 459f245..ea8eff2 100644 --- a/examples/shoot.yaml +++ b/examples/shoot.yaml @@ -30,9 +30,31 @@ spec: verbosity: basic # basic, normal or detailed # OTLP HTTP exporter settings - otlphttp: + otlp_http: enabled: true - endpoint: "https://opentelemetry-receiver.default.svc.cluster.local:8443" + endpoint: "https://opentelemetry-receiver.default.svc.cluster.local:4318" + token: + resourceRef: + name: otelcol-bearer-token + dataKey: token + tls: + ca: + resourceRef: + name: otelcol-tls + dataKey: ca.crt + cert: + resourceRef: + name: otelcol-tls + dataKey: client.crt + key: + resourceRef: + name: otelcol-tls + dataKey: client.key + + # OTLP gRPC exporter settings + otlp_grpc: + enabled: true + endpoint: "https://opentelemetry-receiver.default.svc.cluster.local:4317" token: resourceRef: name: otelcol-bearer-token diff --git a/pkg/actuator/actuator.go b/pkg/actuator/actuator.go index 0db27a4..ab5bcd0 100644 --- a/pkg/actuator/actuator.go +++ b/pkg/actuator/actuator.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "maps" + "path/filepath" "slices" "strconv" "time" @@ -329,6 +330,15 @@ func (a *Actuator) Reconcile(ctx context.Context, logger logr.Logger, ex *extens return err } + otelCollector := a.getOtelCollector( + ex.Namespace, + caBundleSecret, + clientSecret, + cfg, + cluster.Shoot.Spec.Resources, + collectorImage, + ) + data, err := registry.AddAllAndSerialize( taConfigMap, a.getTargetAllocatorServiceAccount(ex.Namespace), @@ -337,7 +347,7 @@ func (a *Actuator) Reconcile(ctx context.Context, logger logr.Logger, ex *extens a.getTargetAllocatorHTTPSService(ex.Namespace), a.getTargetAllocatorDeployment(ex.Namespace, caBundleSecret, serverSecret, taImage), a.getOtelCollectorServiceAccount(ex.Namespace), - a.getOtelCollector(ex.Namespace, caBundleSecret, clientSecret, cfg, cluster.Shoot.Spec.Resources, collectorImage), + otelCollector, ) if err != nil { return err @@ -718,10 +728,20 @@ func (a *Actuator) getOtelCollectorServiceAccount(namespace string) *corev1.Serv } const ( - bearerTokenAuthName = "bearertokenauth" - - volumeNameTLS = "tls" - volumeMountPathTLS = "/etc/ssl/tls" + // bearertokenauthextension names used by the exporters. + baseBearerTokenAuthName = "bearertokenauth" + httpExporterBearerTokenAuthName = baseBearerTokenAuthName + "/exporter-otlp-http" + grpcExporterBearerTokenAuthName = baseBearerTokenAuthName + "/exporter-otlp-grpc" + + // TLS volume names for the exporters. + baseVolumeNameTLS = "tls" + httpExporterVolumeNameTLS = baseVolumeNameTLS + "-exporter-otlp-http" + grpcExporterVolumeNameTLS = baseVolumeNameTLS + "-exporter-otlp-grpc" + + // TLS volume mounts for the exporters. + baseVolumeMountPathTLS = "/etc/ssl/tls" + httpExporterVolumeMountPathTLS = baseVolumeMountPathTLS + "-exporter-otlp-http" + grpcExporterVolumeMountPathTLS = baseVolumeMountPathTLS + "-exporter-otlp-grpc" ) // getDebugExporterConfig returns the OTel settings for the debug exporter. @@ -729,7 +749,7 @@ func (a *Actuator) getDebugExporterConfig(cfg config.DebugExporterConfig) map[st // See the link below for more details about each config setting for the // debug exporter. // - // https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter + // https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/debugexporter exporter := map[string]any{ "verbosity": cfg.Verbosity, } @@ -790,13 +810,13 @@ func (a *Actuator) getOTLPHTTPExporterConfig(cfg config.OTLPHTTPExporterConfig) tlsConfig["insecure_skip_verify"] = *tls.InsecureSkipVerify } if tls.CA != nil { - tlsConfig["ca_file"] = volumeMountPathTLS + "/" + tls.CA.ResourceRef.DataKey + tlsConfig["ca_file"] = filepath.Join(httpExporterVolumeMountPathTLS, tls.CA.ResourceRef.DataKey) } if tls.Cert != nil { - tlsConfig["cert_file"] = volumeMountPathTLS + "/" + tls.Cert.ResourceRef.DataKey + tlsConfig["cert_file"] = filepath.Join(httpExporterVolumeMountPathTLS, tls.Cert.ResourceRef.DataKey) } if tls.Key != nil { - tlsConfig["key_file"] = volumeMountPathTLS + "/" + tls.Key.ResourceRef.DataKey + tlsConfig["key_file"] = filepath.Join(httpExporterVolumeMountPathTLS, tls.Key.ResourceRef.DataKey) } exporter["tls"] = tlsConfig @@ -805,7 +825,62 @@ func (a *Actuator) getOTLPHTTPExporterConfig(cfg config.OTLPHTTPExporterConfig) // Bearer Token Authentication settings if cfg.Token != nil { exporter["auth"] = map[string]any{ - "authenticator": bearerTokenAuthName, + "authenticator": httpExporterBearerTokenAuthName, + } + } + + return exporter +} + +// getOTLPGRPCExporterConfig returns the OTel settings for the OTLP gRPC +// exporter. +func (a *Actuator) getOTLPGRPCExporterConfig(cfg config.OTLPGRPCExporterConfig) map[string]any { + // See the link below for more details about each config setting of the + // OTLP gRPC exporter. + // + // https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter + exporter := map[string]any{ + "endpoint": cfg.Endpoint, + "read_buffer_size": cfg.ReadBufferSize, + "write_buffer_size": cfg.WriteBufferSize, + "timeout": cfg.Timeout.String(), + "compression": string(cfg.Compression), + } + + // Retry on Failure settings + if cfg.RetryOnFailure.Enabled != nil { + exporter["retry_on_failure"] = map[string]any{ + "enabled": *cfg.RetryOnFailure.Enabled, + "initial_interval": cfg.RetryOnFailure.InitialInterval.String(), + "max_interval": cfg.RetryOnFailure.MaxInterval.String(), + "max_elapsed_time": cfg.RetryOnFailure.MaxElapsedTime.String(), + "multiplier": cfg.RetryOnFailure.Multiplier, + } + } + + // TLS settings + if tls := cfg.TLS; tls != nil { + tlsConfig := map[string]any{} + if tls.InsecureSkipVerify != nil { + tlsConfig["insecure_skip_verify"] = *tls.InsecureSkipVerify + } + if tls.CA != nil { + tlsConfig["ca_file"] = filepath.Join(grpcExporterVolumeMountPathTLS, tls.CA.ResourceRef.DataKey) + } + if tls.Cert != nil { + tlsConfig["cert_file"] = filepath.Join(grpcExporterVolumeMountPathTLS, tls.Cert.ResourceRef.DataKey) + } + if tls.Key != nil { + tlsConfig["key_file"] = filepath.Join(grpcExporterVolumeMountPathTLS, tls.Key.ResourceRef.DataKey) + } + + exporter["tls"] = tlsConfig + } + + // Bearer Token Authentication settings + if cfg.Token != nil { + exporter["auth"] = map[string]any{ + "authenticator": grpcExporterBearerTokenAuthName, } } @@ -820,11 +895,14 @@ func (a *Actuator) getOtelExporters(cfg config.CollectorConfig) map[string]any { if cfg.Spec.Exporters.DebugExporter.IsEnabled() { exporters["debug"] = a.getDebugExporterConfig(cfg.Spec.Exporters.DebugExporter) } + if cfg.Spec.Exporters.OTLPHTTPExporter.IsEnabled() { - exporters["otlphttp"] = a.getOTLPHTTPExporterConfig(cfg.Spec.Exporters.OTLPHTTPExporter) + exporters["otlp_http"] = a.getOTLPHTTPExporterConfig(cfg.Spec.Exporters.OTLPHTTPExporter) } - // TODO(dnaeon): add OTLP gRPC exporter + if cfg.Spec.Exporters.OTLPGRPCExporter.IsEnabled() { + exporters["otlp_grpc"] = a.getOTLPGRPCExporterConfig(cfg.Spec.Exporters.OTLPGRPCExporter) + } return exporters } @@ -845,16 +923,19 @@ func (a *Actuator) getOtelCollector( volumeNameClientCertificate = "client-cert" volumeMountPathClientCertificate = "/etc/ssl/certs/client" - volumeNameBearerToken = "bearer-token-auth" // #nosec: G101 - volumeMountPathBearerTokenFile = "/etc/auth/bearer" // #nosec: G101 - ) + baseVolumeNameBearerToken = "bearer-token-auth" // #nosec: G101 + httpExporterVolumeNameBearerToken = baseVolumeNameBearerToken + "-exporter-otlp-http" // #nosec: G101 + grpcExporterVolumeNameBearerToken = baseVolumeNameBearerToken + "-exporter-otlp-grpc" // #nosec: G101 - var ( - exporters = a.getOtelExporters(cfg) - exporterNames = slices.Sorted(maps.Keys(exporters)) - allLabels = utils.MergeStringMaps(a.getCommonLabels(), a.getNetworkLabels()) + baseVolumeMountPathBearerTokenFile = "/etc/auth/bearer" // #nosec: G101 + httpExporterVolumeMountPathBearerTokenFile = baseVolumeMountPathBearerTokenFile + "-exporter-otlp-http" // #nosec: G101 + grpcExporterVolumeMountPathBearerTokenFile = baseVolumeMountPathBearerTokenFile + "-exporter-otlp-grpc" // #nosec: G101 ) + exporters := a.getOtelExporters(cfg) + exporterNames := slices.Sorted(maps.Keys(exporters)) + allLabels := utils.MergeStringMaps(a.getCommonLabels(), a.getNetworkLabels()) + obj := &otelv1beta1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: otelCollectorName, @@ -960,7 +1041,7 @@ func (a *Actuator) getOtelCollector( }, }, Pipelines: map[string]*otelv1beta1.Pipeline{ - // TODO(dnaeon): add a pipeline for logs, once we have them enabled + // TODO(dnaeon): add the otlp grpc receiver "metrics": { Receivers: []string{"prometheus"}, Processors: []string{"batch"}, @@ -972,45 +1053,47 @@ func (a *Actuator) getOtelCollector( }, } - // TLS - if tls := cfg.Spec.Exporters.OTLPHTTPExporter.TLS; tls != nil { - volume := corev1.Volume{Name: volumeNameTLS, VolumeSource: corev1.VolumeSource{Projected: &corev1.ProjectedVolumeSource{}}} - addSecretToProjectedVolume := func(resourceRef config.ResourceReferenceDetails) { - volume.Projected.Sources = append(volume.Projected.Sources, corev1.VolumeProjection{Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{Name: secretNameForResource(resourceRef.Name, resources)}, - Items: []corev1.KeyToPath{{Key: resourceRef.DataKey, Path: resourceRef.DataKey}}, - }}) - } - - if tls.CA != nil { - addSecretToProjectedVolume(tls.CA.ResourceRef) - } - if tls.Cert != nil { - addSecretToProjectedVolume(tls.Cert.ResourceRef) - } - if tls.Key != nil { - addSecretToProjectedVolume(tls.Key.ResourceRef) - } - - obj.Spec.Volumes = append(obj.Spec.Volumes, volume) - obj.Spec.VolumeMounts = append(obj.Spec.VolumeMounts, corev1.VolumeMount{Name: volumeNameTLS, MountPath: volumeMountPathTLS}) - } + // OTLP HTTP exporter TLS settings + a.configureVolumeForTLS( + obj, + cfg.Spec.Exporters.OTLPHTTPExporter.TLS, + httpExporterVolumeNameTLS, + httpExporterVolumeMountPathTLS, + resources, + ) - // Bearer Token Authentication - if token := cfg.Spec.Exporters.OTLPHTTPExporter.Token; token != nil { - if obj.Spec.Config.Extensions == nil { - obj.Spec.Config.Extensions = &otelv1beta1.AnyConfig{} - } + // OTLP HTTP exporter Bearer Token Authentication settings + // + // https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/bearertokenauthextension + a.configureVolumeForBearerTokenAuthExtension( + obj, + cfg.Spec.Exporters.OTLPHTTPExporter.Token, + httpExporterBearerTokenAuthName, + httpExporterVolumeMountPathBearerTokenFile, + httpExporterVolumeNameBearerToken, + httpExporterVolumeMountPathBearerTokenFile, + resources, + ) - if obj.Spec.Config.Extensions.Object == nil { - obj.Spec.Config.Extensions.Object = make(map[string]any) - } + // OTLP gRPC exporter TLS settings + a.configureVolumeForTLS( + obj, + cfg.Spec.Exporters.OTLPGRPCExporter.TLS, + grpcExporterVolumeNameTLS, + grpcExporterVolumeMountPathTLS, + resources, + ) - obj.Spec.Config.Extensions.Object[bearerTokenAuthName] = map[string]any{"filename": volumeMountPathBearerTokenFile + "/" + token.ResourceRef.DataKey} - obj.Spec.Config.Service.Extensions = append(obj.Spec.Config.Service.Extensions, bearerTokenAuthName) - obj.Spec.VolumeMounts = append(obj.Spec.VolumeMounts, corev1.VolumeMount{Name: volumeNameBearerToken, MountPath: volumeMountPathBearerTokenFile}) - obj.Spec.Volumes = append(obj.Spec.Volumes, corev1.Volume{Name: volumeNameBearerToken, VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: secretNameForResource(token.ResourceRef.Name, resources)}}}) - } + // OTLP gRPC exporter Bearer Token Authentication settings + a.configureVolumeForBearerTokenAuthExtension( + obj, + cfg.Spec.Exporters.OTLPGRPCExporter.Token, + grpcExporterBearerTokenAuthName, + grpcExporterVolumeMountPathBearerTokenFile, + grpcExporterVolumeNameBearerToken, + grpcExporterVolumeMountPathBearerTokenFile, + resources, + ) return obj } @@ -1025,3 +1108,107 @@ func secretNameForResource(resourceName string, resources []gardencorev1beta1.Na return "" } + +// configureVolumeForTLS configures a volume for the OpenTelemetry collector for +// TLS secrets. +func (a *Actuator) configureVolumeForTLS( + obj *otelv1beta1.OpenTelemetryCollector, + tls *config.TLSConfig, + volumeName string, + volumeMount string, + resources []gardencorev1beta1.NamedResourceReference, +) { + if obj == nil || tls == nil { + return + } + + volume := corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{}, + }, + } + + addSecretToProjectedVolume := func(resourceRef config.ResourceReferenceDetails) { + volume.Projected.Sources = append( + volume.Projected.Sources, + corev1.VolumeProjection{ + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretNameForResource(resourceRef.Name, resources), + }, + Items: []corev1.KeyToPath{{Key: resourceRef.DataKey, Path: resourceRef.DataKey}}, + }, + }, + ) + } + + if tls.CA != nil { + addSecretToProjectedVolume(tls.CA.ResourceRef) + } + if tls.Cert != nil { + addSecretToProjectedVolume(tls.Cert.ResourceRef) + } + if tls.Key != nil { + addSecretToProjectedVolume(tls.Key.ResourceRef) + } + + obj.Spec.Volumes = append(obj.Spec.Volumes, volume) + obj.Spec.VolumeMounts = append( + obj.Spec.VolumeMounts, + corev1.VolumeMount{ + Name: volumeName, + MountPath: volumeMount, + }, + ) +} + +// configureVolumeForBearerTokenAuthExtension configures a volume for the +// OpenTelemetry collector for the bearertokenauth extension. +func (a *Actuator) configureVolumeForBearerTokenAuthExtension( + obj *otelv1beta1.OpenTelemetryCollector, + ref *config.ResourceReference, + authExtensionName string, + tokenBasePath string, + volumeName string, + volumeMount string, + resources []gardencorev1beta1.NamedResourceReference, +) { + if obj == nil || ref == nil { + return + } + + if obj.Spec.Config.Extensions == nil { + obj.Spec.Config.Extensions = &otelv1beta1.AnyConfig{} + } + + if obj.Spec.Config.Extensions.Object == nil { + obj.Spec.Config.Extensions.Object = make(map[string]any) + } + + obj.Spec.Config.Extensions.Object[authExtensionName] = map[string]any{ + "filename": filepath.Join(tokenBasePath, ref.ResourceRef.DataKey), + } + + obj.Spec.Config.Service.Extensions = append(obj.Spec.Config.Service.Extensions, authExtensionName) + + obj.Spec.Volumes = append( + obj.Spec.Volumes, + corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: secretNameForResource(ref.ResourceRef.Name, resources), + }, + }, + }, + ) + + obj.Spec.VolumeMounts = append( + obj.Spec.VolumeMounts, + corev1.VolumeMount{ + Name: volumeName, + MountPath: volumeMount, + }, + ) +} diff --git a/pkg/apis/config/generated.deepcopy.go b/pkg/apis/config/generated.deepcopy.go index 01c9cf0..96d6518 100644 --- a/pkg/apis/config/generated.deepcopy.go +++ b/pkg/apis/config/generated.deepcopy.go @@ -57,6 +57,7 @@ func (in *CollectorConfigSpec) DeepCopy() *CollectorConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CollectorExportersConfig) DeepCopyInto(out *CollectorExportersConfig) { *out = *in + in.OTLPGRPCExporter.DeepCopyInto(&out.OTLPGRPCExporter) in.OTLPHTTPExporter.DeepCopyInto(&out.OTLPHTTPExporter) in.DebugExporter.DeepCopyInto(&out.DebugExporter) return @@ -125,6 +126,38 @@ func (in *DebugExporterConfig) DeepCopy() *DebugExporterConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OTLPGRPCExporterConfig) DeepCopyInto(out *OTLPGRPCExporterConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } + if in.Token != nil { + in, out := &in.Token, &out.Token + *out = new(ResourceReference) + **out = **in + } + in.RetryOnFailure.DeepCopyInto(&out.RetryOnFailure) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OTLPGRPCExporterConfig. +func (in *OTLPGRPCExporterConfig) DeepCopy() *OTLPGRPCExporterConfig { + if in == nil { + return nil + } + out := new(OTLPGRPCExporterConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OTLPHTTPExporterConfig) DeepCopyInto(out *OTLPHTTPExporterConfig) { *out = *in diff --git a/pkg/apis/config/types.go b/pkg/apis/config/types.go index 5dd425b..b2a951e 100644 --- a/pkg/apis/config/types.go +++ b/pkg/apis/config/types.go @@ -185,7 +185,7 @@ type OTLPHTTPExporterConfig struct { // IsEnabled is a predicate which returns whether the exporter is enabled or // not. -func (cfg *OTLPHTTPExporterConfig) IsEnabled() bool { +func (cfg OTLPHTTPExporterConfig) IsEnabled() bool { if cfg.Enabled != nil { return *cfg.Enabled } @@ -216,7 +216,59 @@ type DebugExporterConfig struct { // IsEnabled is a predicate which returns whether the exporter is enabled or // not. -func (cfg *DebugExporterConfig) IsEnabled() bool { +func (cfg DebugExporterConfig) IsEnabled() bool { + if cfg.Enabled != nil { + return *cfg.Enabled + } + + return false +} + +// OTLPGRPCExporterConfig provides the OTLP gRPC Exporter config settings. +// +// See [OTLP gRPC Exporter] for more details. +// +// [OTLP gRPC Exporter]: https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter +type OTLPGRPCExporterConfig struct { + // Enabled specifies whether the OTLP gRPC exporter is enabled or not. + Enabled *bool + + // Endpoint specifies the gRPC endpoint to which signals will be exported. + // + // Check the link below for more details about the format of this field. + // + // https://github.com/grpc/grpc/blob/master/doc/naming.md + Endpoint string + + // TLS specifies the TLS configuration settings for the exporter. + TLS *TLSConfig + + // Token references a bearer token for authentication. + Token *ResourceReference + + // Timeout specifies the time to wait per individual attempt to send + // data to the backend. + Timeout time.Duration + + // ReadBufferSize specifies the ReadBufferSize for the gRPC + // client. Default value is [DefaultGRPCExporterClientReadBufferSize]. + ReadBufferSize int + + // WriteBufferSize specifies the WriteBufferSize for the gRPC + // client. Default value is [DefaultGRPCExporterClientWriteBufferSize]. + WriteBufferSize int + + // RetryOnFailure specifies the retry policy of the exporter. + RetryOnFailure RetryOnFailureConfig + + // Compression specifies the compression to use. The default value is + // [CompressionGzip]. + Compression Compression +} + +// IsEnabled is a predicate which returns whether the exporter is enabled or +// not. +func (cfg OTLPGRPCExporterConfig) IsEnabled() bool { if cfg.Enabled != nil { return *cfg.Enabled } @@ -226,6 +278,9 @@ func (cfg *DebugExporterConfig) IsEnabled() bool { // CollectorExportersConfig provides the OTLP exporter settings. type CollectorExportersConfig struct { + // OTLPGRPCExporter provides the OTLP gRPC Exporter settings. + OTLPGRPCExporter OTLPGRPCExporterConfig + // HTTPExporter provides the OTLP HTTP Exporter settings. OTLPHTTPExporter OTLPHTTPExporterConfig diff --git a/pkg/apis/config/v1alpha1/generated.conversion.go b/pkg/apis/config/v1alpha1/generated.conversion.go index 58e4a2a..4eda236 100644 --- a/pkg/apis/config/v1alpha1/generated.conversion.go +++ b/pkg/apis/config/v1alpha1/generated.conversion.go @@ -81,6 +81,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*OTLPGRPCExporterConfig)(nil), (*config.OTLPGRPCExporterConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_OTLPGRPCExporterConfig_To_config_OTLPGRPCExporterConfig(a.(*OTLPGRPCExporterConfig), b.(*config.OTLPGRPCExporterConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*config.OTLPGRPCExporterConfig)(nil), (*OTLPGRPCExporterConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_config_OTLPGRPCExporterConfig_To_v1alpha1_OTLPGRPCExporterConfig(a.(*config.OTLPGRPCExporterConfig), b.(*OTLPGRPCExporterConfig), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*OTLPHTTPExporterConfig)(nil), (*config.OTLPHTTPExporterConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_OTLPHTTPExporterConfig_To_config_OTLPHTTPExporterConfig(a.(*OTLPHTTPExporterConfig), b.(*config.OTLPHTTPExporterConfig), scope) }); err != nil { @@ -195,6 +205,9 @@ func Convert_config_CollectorConfigSpec_To_v1alpha1_CollectorConfigSpec(in *conf } func autoConvert_v1alpha1_CollectorExportersConfig_To_config_CollectorExportersConfig(in *CollectorExportersConfig, out *config.CollectorExportersConfig, s conversion.Scope) error { + if err := Convert_v1alpha1_OTLPGRPCExporterConfig_To_config_OTLPGRPCExporterConfig(&in.OTLPGRPCExporter, &out.OTLPGRPCExporter, s); err != nil { + return err + } if err := Convert_v1alpha1_OTLPHTTPExporterConfig_To_config_OTLPHTTPExporterConfig(&in.OTLPHTTPExporter, &out.OTLPHTTPExporter, s); err != nil { return err } @@ -210,6 +223,9 @@ func Convert_v1alpha1_CollectorExportersConfig_To_config_CollectorExportersConfi } func autoConvert_config_CollectorExportersConfig_To_v1alpha1_CollectorExportersConfig(in *config.CollectorExportersConfig, out *CollectorExportersConfig, s conversion.Scope) error { + if err := Convert_config_OTLPGRPCExporterConfig_To_v1alpha1_OTLPGRPCExporterConfig(&in.OTLPGRPCExporter, &out.OTLPGRPCExporter, s); err != nil { + return err + } if err := Convert_config_OTLPHTTPExporterConfig_To_v1alpha1_OTLPHTTPExporterConfig(&in.OTLPHTTPExporter, &out.OTLPHTTPExporter, s); err != nil { return err } @@ -288,6 +304,46 @@ func Convert_config_DebugExporterConfig_To_v1alpha1_DebugExporterConfig(in *conf return autoConvert_config_DebugExporterConfig_To_v1alpha1_DebugExporterConfig(in, out, s) } +func autoConvert_v1alpha1_OTLPGRPCExporterConfig_To_config_OTLPGRPCExporterConfig(in *OTLPGRPCExporterConfig, out *config.OTLPGRPCExporterConfig, s conversion.Scope) error { + out.Enabled = (*bool)(unsafe.Pointer(in.Enabled)) + out.Endpoint = in.Endpoint + out.TLS = (*config.TLSConfig)(unsafe.Pointer(in.TLS)) + out.Token = (*config.ResourceReference)(unsafe.Pointer(in.Token)) + out.Timeout = time.Duration(in.Timeout) + out.ReadBufferSize = in.ReadBufferSize + out.WriteBufferSize = in.WriteBufferSize + if err := Convert_v1alpha1_RetryOnFailureConfig_To_config_RetryOnFailureConfig(&in.RetryOnFailure, &out.RetryOnFailure, s); err != nil { + return err + } + out.Compression = config.Compression(in.Compression) + return nil +} + +// Convert_v1alpha1_OTLPGRPCExporterConfig_To_config_OTLPGRPCExporterConfig is an autogenerated conversion function. +func Convert_v1alpha1_OTLPGRPCExporterConfig_To_config_OTLPGRPCExporterConfig(in *OTLPGRPCExporterConfig, out *config.OTLPGRPCExporterConfig, s conversion.Scope) error { + return autoConvert_v1alpha1_OTLPGRPCExporterConfig_To_config_OTLPGRPCExporterConfig(in, out, s) +} + +func autoConvert_config_OTLPGRPCExporterConfig_To_v1alpha1_OTLPGRPCExporterConfig(in *config.OTLPGRPCExporterConfig, out *OTLPGRPCExporterConfig, s conversion.Scope) error { + out.Enabled = (*bool)(unsafe.Pointer(in.Enabled)) + out.Endpoint = in.Endpoint + out.TLS = (*TLSConfig)(unsafe.Pointer(in.TLS)) + out.Token = (*ResourceReference)(unsafe.Pointer(in.Token)) + out.Timeout = time.Duration(in.Timeout) + out.ReadBufferSize = in.ReadBufferSize + out.WriteBufferSize = in.WriteBufferSize + if err := Convert_config_RetryOnFailureConfig_To_v1alpha1_RetryOnFailureConfig(&in.RetryOnFailure, &out.RetryOnFailure, s); err != nil { + return err + } + out.Compression = Compression(in.Compression) + return nil +} + +// Convert_config_OTLPGRPCExporterConfig_To_v1alpha1_OTLPGRPCExporterConfig is an autogenerated conversion function. +func Convert_config_OTLPGRPCExporterConfig_To_v1alpha1_OTLPGRPCExporterConfig(in *config.OTLPGRPCExporterConfig, out *OTLPGRPCExporterConfig, s conversion.Scope) error { + return autoConvert_config_OTLPGRPCExporterConfig_To_v1alpha1_OTLPGRPCExporterConfig(in, out, s) +} + func autoConvert_v1alpha1_OTLPHTTPExporterConfig_To_config_OTLPHTTPExporterConfig(in *OTLPHTTPExporterConfig, out *config.OTLPHTTPExporterConfig, s conversion.Scope) error { out.Enabled = (*bool)(unsafe.Pointer(in.Enabled)) out.Endpoint = in.Endpoint diff --git a/pkg/apis/config/v1alpha1/generated.deepcopy.go b/pkg/apis/config/v1alpha1/generated.deepcopy.go index 448bc32..a348010 100644 --- a/pkg/apis/config/v1alpha1/generated.deepcopy.go +++ b/pkg/apis/config/v1alpha1/generated.deepcopy.go @@ -57,6 +57,7 @@ func (in *CollectorConfigSpec) DeepCopy() *CollectorConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CollectorExportersConfig) DeepCopyInto(out *CollectorExportersConfig) { *out = *in + in.OTLPGRPCExporter.DeepCopyInto(&out.OTLPGRPCExporter) in.OTLPHTTPExporter.DeepCopyInto(&out.OTLPHTTPExporter) in.DebugExporter.DeepCopyInto(&out.DebugExporter) return @@ -125,6 +126,38 @@ func (in *DebugExporterConfig) DeepCopy() *DebugExporterConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OTLPGRPCExporterConfig) DeepCopyInto(out *OTLPGRPCExporterConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } + if in.Token != nil { + in, out := &in.Token, &out.Token + *out = new(ResourceReference) + **out = **in + } + in.RetryOnFailure.DeepCopyInto(&out.RetryOnFailure) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OTLPGRPCExporterConfig. +func (in *OTLPGRPCExporterConfig) DeepCopy() *OTLPGRPCExporterConfig { + if in == nil { + return nil + } + out := new(OTLPGRPCExporterConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OTLPHTTPExporterConfig) DeepCopyInto(out *OTLPHTTPExporterConfig) { *out = *in diff --git a/pkg/apis/config/v1alpha1/generated.defaults.go b/pkg/apis/config/v1alpha1/generated.defaults.go index 88298e3..3123e57 100644 --- a/pkg/apis/config/v1alpha1/generated.defaults.go +++ b/pkg/apis/config/v1alpha1/generated.defaults.go @@ -20,6 +20,44 @@ func RegisterDefaults(scheme *runtime.Scheme) error { } func SetObjectDefaults_CollectorConfig(in *CollectorConfig) { + if in.Spec.Exporters.OTLPGRPCExporter.Enabled == nil { + var ptrVar1 bool = false + in.Spec.Exporters.OTLPGRPCExporter.Enabled = &ptrVar1 + } + if in.Spec.Exporters.OTLPGRPCExporter.TLS != nil { + if in.Spec.Exporters.OTLPGRPCExporter.TLS.InsecureSkipVerify == nil { + var ptrVar1 bool = false + in.Spec.Exporters.OTLPGRPCExporter.TLS.InsecureSkipVerify = &ptrVar1 + } + } + if in.Spec.Exporters.OTLPGRPCExporter.Timeout == 0 { + in.Spec.Exporters.OTLPGRPCExporter.Timeout = time.Duration(DefaultGRPCExporterClientTimeout) + } + if in.Spec.Exporters.OTLPGRPCExporter.ReadBufferSize == 0 { + in.Spec.Exporters.OTLPGRPCExporter.ReadBufferSize = int(DefaultGRPCExporterClientReadBufferSize) + } + if in.Spec.Exporters.OTLPGRPCExporter.WriteBufferSize == 0 { + in.Spec.Exporters.OTLPGRPCExporter.WriteBufferSize = int(DefaultGRPCExporterClientWriteBufferSize) + } + if in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.Enabled == nil { + var ptrVar1 bool = true + in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.Enabled = &ptrVar1 + } + if in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.InitialInterval == 0 { + in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.InitialInterval = time.Duration(DefaultRetryInitialInterval) + } + if in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.MaxInterval == 0 { + in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.MaxInterval = time.Duration(DefaultRetryMaxInterval) + } + if in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.MaxElapsedTime == 0 { + in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.MaxElapsedTime = time.Duration(DefaultRetryMaxElapsedTime) + } + if in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.Multiplier == 0 { + in.Spec.Exporters.OTLPGRPCExporter.RetryOnFailure.Multiplier = float64(DefaultRetryMultiplier) + } + if in.Spec.Exporters.OTLPGRPCExporter.Compression == "" { + in.Spec.Exporters.OTLPGRPCExporter.Compression = Compression(CompressionGzip) + } if in.Spec.Exporters.OTLPHTTPExporter.Enabled == nil { var ptrVar1 bool = false in.Spec.Exporters.OTLPHTTPExporter.Enabled = &ptrVar1 @@ -31,13 +69,13 @@ func SetObjectDefaults_CollectorConfig(in *CollectorConfig) { } } if in.Spec.Exporters.OTLPHTTPExporter.Timeout == 0 { - in.Spec.Exporters.OTLPHTTPExporter.Timeout = time.Duration(DefaultExporterClientTimeout) + in.Spec.Exporters.OTLPHTTPExporter.Timeout = time.Duration(DefaultHTTPExporterClientTimeout) } if in.Spec.Exporters.OTLPHTTPExporter.ReadBufferSize == 0 { - in.Spec.Exporters.OTLPHTTPExporter.ReadBufferSize = int(DefaultExporterClientReadBufferSize) + in.Spec.Exporters.OTLPHTTPExporter.ReadBufferSize = int(DefaultHTTPExporterClientReadBufferSize) } if in.Spec.Exporters.OTLPHTTPExporter.WriteBufferSize == 0 { - in.Spec.Exporters.OTLPHTTPExporter.WriteBufferSize = int(DefaultExporterClientWriteBufferSize) + in.Spec.Exporters.OTLPHTTPExporter.WriteBufferSize = int(DefaultHTTPExporterClientWriteBufferSize) } if in.Spec.Exporters.OTLPHTTPExporter.Encoding == "" { in.Spec.Exporters.OTLPHTTPExporter.Encoding = MessageEncoding(MessageEncodingProto) diff --git a/pkg/apis/config/v1alpha1/types.go b/pkg/apis/config/v1alpha1/types.go index 0f7a30d..3f7a73d 100644 --- a/pkg/apis/config/v1alpha1/types.go +++ b/pkg/apis/config/v1alpha1/types.go @@ -115,15 +115,25 @@ const ( // retry interval is multiplied on each attempt. DefaultRetryMultiplier = 1.5 - // DefaultExporterClientTimeout specifies the default client timeout for + // DefaultHTTPExporterClientTimeout specifies the default client timeout for // HTTP requests made by exporters. - DefaultExporterClientTimeout = 30 * time.Second - // DefaultExporterClientReadBufferSize specifies the default + DefaultHTTPExporterClientTimeout = 30 * time.Second + // DefaultHTTPExporterClientReadBufferSize specifies the default // ReadBufferSize for the HTTP client used by exporters. - DefaultExporterClientReadBufferSize = 0 - // DefaultExporterClientWriteBufferSize specifies the default + DefaultHTTPExporterClientReadBufferSize = 0 + // DefaultHTTPExporterClientWriteBufferSize specifies the default // WriteBufferSize for the HTTP client used by the exporters. - DefaultExporterClientWriteBufferSize = 512 * 1024 + DefaultHTTPExporterClientWriteBufferSize = 512 * 1024 + + // DefaultGRPCExporterClientTimeout specifies the default client timeout + // of the OTLP gRPC exporter. + DefaultGRPCExporterClientTimeout = 5 * time.Second + // DefaultGRPCExporterClientReadBufferSize specifies the default + // ReadBufferSize for the gRPC client used by exporters. + DefaultGRPCExporterClientReadBufferSize = 32 * 1024 + // DefaultGRPCExporterClientWriteBufferSize specifies the default + // WriteBufferSize for the gRPC client used by the exporters. + DefaultGRPCExporterClientWriteBufferSize = 32 * 1024 ) // RetryOnFailureConfig provides the retry policy for an exporter. @@ -222,31 +232,32 @@ type OTLPHTTPExporterConfig struct { // TLS specifies the TLS configuration settings for the exporter. // // +k8s:optional - TLS *TLSConfig `json:"tls,omitempty"` + TLS *TLSConfig `json:"tls,omitzero"` + // Token references a bearer token for authentication. // // +k8s:optional Token *ResourceReference `json:"token,omitempty"` // Timeout specifies the HTTP request time limit. Default value is - // [DefaultExporterClientTimeout]. + // [DefaultHTTPExporterClientTimeout]. // // +k8s:optional - // +default=ref(DefaultExporterClientTimeout) + // +default=ref(DefaultHTTPExporterClientTimeout) Timeout time.Duration `json:"timeout,omitzero"` // ReadBufferSize specifies the ReadBufferSize for the HTTP - // client. Default value is [DefaultExporterClientReadBufferSize]. + // client. Default value is [DefaultHTTPExporterClientReadBufferSize]. // // +k8s:optional - // +default=ref(DefaultExporterClientReadBufferSize) + // +default=ref(DefaultHTTPExporterClientReadBufferSize) ReadBufferSize int `json:"read_buffer_size,omitzero"` // WriteBufferSize specifies the WriteBufferSize for the HTTP - // client. Default value is [DefaultExporterClientWriteBufferSize]. + // client. Default value is [DefaultHTTPExporterClientWriteBufferSize]. // // +k8s:optional - // +default=ref(DefaultExporterClientWriteBufferSize) + // +default=ref(DefaultHTTPExporterClientWriteBufferSize) WriteBufferSize int `json:"write_buffer_size,omitzero"` // Encoding specifies the encoding to use for the messages. The default @@ -298,12 +309,80 @@ type DebugExporterConfig struct { Verbosity DebugExporterVerbosity `json:"verbosity,omitzero"` } +// OTLPGRPCExporterConfig provides the OTLP gRPC Exporter config settings. +// +// See [OTLP gRPC Exporter] for more details. +// +// [OTLP gRPC Exporter]: https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter +type OTLPGRPCExporterConfig struct { + // Enabled specifies whether the OTLP gRPC exporter is enabled or not. + // + // +k8s:optional + // +default=false + Enabled *bool `json:"enabled,omitzero"` + + // Endpoint specifies the gRPC endpoint to which signals will be exported. + // + // Check the link below for more details about the format of this field. + // + // https://github.com/grpc/grpc/blob/master/doc/naming.md + // + // +k8s:required + Endpoint string `json:"endpoint,omitzero"` + + // TLS specifies the TLS configuration settings for the exporter. + // + // +k8s:optional + TLS *TLSConfig `json:"tls,omitzero"` + + // Token references a bearer token for authentication. + Token *ResourceReference `json:"token,omitzero"` + + // Timeout specifies the time to wait per individual attempt to send + // data to the backend. + // + // +k8s:optional + // +default=ref(DefaultGRPCExporterClientTimeout) + Timeout time.Duration `json:"timeout,omitzero"` + + // ReadBufferSize specifies the ReadBufferSize for the gRPC + // client. Default value is [DefaultGRPCExporterClientReadBufferSize]. + // + // +k8s:optional + // +default=ref(DefaultGRPCExporterClientReadBufferSize) + ReadBufferSize int `json:"read_buffer_size,omitzero"` + + // WriteBufferSize specifies the WriteBufferSize for the gRPC + // client. Default value is [DefaultGRPCExporterClientWriteBufferSize]. + // + // +k8s:optional + // +default=ref(DefaultGRPCExporterClientWriteBufferSize) + WriteBufferSize int `json:"write_buffer_size,omitzero"` + + // RetryOnFailure specifies the retry policy of the exporter. + // + // +k8s:optional + RetryOnFailure RetryOnFailureConfig `json:"retry_on_failure,omitzero"` + + // Compression specifies the compression to use. The default value is + // [CompressionGzip]. + // + // +k8s:optional + // +default=ref(CompressionGzip) + Compression Compression `json:"compression,omitzero"` +} + // CollectorExportersConfig provides the OTLP exporter settings. type CollectorExportersConfig struct { + // OTLPGRPCExporter provides the OTLP gRPC Exporter settings. + // + // +k8s:optional + OTLPGRPCExporter OTLPGRPCExporterConfig `json:"otlp_grpc,omitzero"` + // HTTPExporter provides the OTLP HTTP Exporter settings. // // +k8s:optional - OTLPHTTPExporter OTLPHTTPExporterConfig `json:"otlphttp,omitzero"` + OTLPHTTPExporter OTLPHTTPExporterConfig `json:"otlp_http,omitzero"` // DebugExporter provides the settings for the debug exporter. // diff --git a/pkg/apis/config/validation/validation.go b/pkg/apis/config/validation/validation.go index 97c2299..9b09654 100644 --- a/pkg/apis/config/validation/validation.go +++ b/pkg/apis/config/validation/validation.go @@ -21,6 +21,7 @@ func Validate(cfg config.CollectorConfig) error { anyExporterEnabled := []bool{ cfg.Spec.Exporters.DebugExporter.IsEnabled(), cfg.Spec.Exporters.OTLPHTTPExporter.IsEnabled(), + cfg.Spec.Exporters.OTLPGRPCExporter.IsEnabled(), } if !cmp.Or(anyExporterEnabled...) { @@ -36,23 +37,23 @@ func Validate(cfg config.CollectorConfig) error { value string }{ { - path: "spec.exporters.otlphttp.endpoint", + path: "spec.exporters.otlp_http.endpoint", value: cfg.Spec.Exporters.OTLPHTTPExporter.Endpoint, }, { - path: "spec.exporters.otlphttp.traces_endpoint", + path: "spec.exporters.otlp_http.traces_endpoint", value: cfg.Spec.Exporters.OTLPHTTPExporter.TracesEndpoint, }, { - path: "spec.exporters.otlphttp.metrics_endpoint", + path: "spec.exporters.otlp_http.metrics_endpoint", value: cfg.Spec.Exporters.OTLPHTTPExporter.MetricsEndpoint, }, { - path: "spec.exporters.otlphttp.logs_endpoint", + path: "spec.exporters.otlp_http.logs_endpoint", value: cfg.Spec.Exporters.OTLPHTTPExporter.LogsEndpoint, }, { - path: "spec.exporters.otlphttp.profiles_endpoint", + path: "spec.exporters.otlp_http.profiles_endpoint", value: cfg.Spec.Exporters.OTLPHTTPExporter.ProfilesEndpoint, }, } @@ -69,18 +70,28 @@ func Validate(cfg config.CollectorConfig) error { } // Make sure that the HTTP client read/write buffers are good - nonNegativeFields := []struct { + type nonNegativeField struct { path string value int - }{ + } + + nonNegativeFields := []nonNegativeField{ { - path: "spec.exporters.otlphttp.read_buffer_size", + path: "spec.exporters.otlp_http.read_buffer_size", value: cfg.Spec.Exporters.OTLPHTTPExporter.ReadBufferSize, }, { - path: "spec.exporters.otlphttp.write_buffer_size", + path: "spec.exporters.otlp_http.write_buffer_size", value: cfg.Spec.Exporters.OTLPHTTPExporter.WriteBufferSize, }, + { + path: "spec.exporters.otlp_grpc.read_buffer_size", + value: cfg.Spec.Exporters.OTLPGRPCExporter.ReadBufferSize, + }, + { + path: "spec.exporters.otlp_grpc.write_buffer_size", + value: cfg.Spec.Exporters.OTLPGRPCExporter.WriteBufferSize, + }, } for _, f := range nonNegativeFields { @@ -100,29 +111,53 @@ func Validate(cfg config.CollectorConfig) error { resourceRefs := []resourceRef{ { - path: "spec.exporters.otlphttp.token", + path: "spec.exporters.otlp_http.token", ref: cfg.Spec.Exporters.OTLPHTTPExporter.Token, }, + { + path: "spec.exporters.otlp_grpc.token", + ref: cfg.Spec.Exporters.OTLPGRPCExporter.Token, + }, } + // Referenced resources from the OTLP HTTP exporter if cfg.Spec.Exporters.OTLPHTTPExporter.TLS != nil { resourceRefs = append( resourceRefs, resourceRef{ - path: "spec.exporters.otlphttp.tls.ca", + path: "spec.exporters.otlp_http.tls.ca", ref: cfg.Spec.Exporters.OTLPHTTPExporter.TLS.CA, }, resourceRef{ - path: "spec.exporters.otlphttp.tls.cert", + path: "spec.exporters.otlp_http.tls.cert", ref: cfg.Spec.Exporters.OTLPHTTPExporter.TLS.Cert, }, resourceRef{ - path: "spec.exporters.otlphttp.tls.key", + path: "spec.exporters.otlp_http.tls.key", ref: cfg.Spec.Exporters.OTLPHTTPExporter.TLS.Key, }, ) } + // Referenced resources from the OTLP gRPC exporter + if cfg.Spec.Exporters.OTLPGRPCExporter.TLS != nil { + resourceRefs = append( + resourceRefs, + resourceRef{ + path: "spec.exporters.otlp_grpc.tls.ca", + ref: cfg.Spec.Exporters.OTLPGRPCExporter.TLS.CA, + }, + resourceRef{ + path: "spec.exporters.otlp_grpc.tls.cert", + ref: cfg.Spec.Exporters.OTLPGRPCExporter.TLS.Cert, + }, + resourceRef{ + path: "spec.exporters.otlp_grpc.tls.key", + ref: cfg.Spec.Exporters.OTLPGRPCExporter.TLS.Key, + }, + ) + } + for _, f := range resourceRefs { if f.ref != nil { if f.ref.ResourceRef.Name == "" || f.ref.ResourceRef.DataKey == "" { @@ -134,5 +169,31 @@ func Validate(cfg config.CollectorConfig) error { } } + // Validate expected string values are not empty + type nonEmptyString struct { + path string + value string + } + + nonEmptyStrings := make([]nonEmptyString, 0) + if cfg.Spec.Exporters.OTLPGRPCExporter.IsEnabled() { + nonEmptyStrings = append( + nonEmptyStrings, + nonEmptyString{ + path: "spec.exporters.otlp_grpc.endpoint", + value: cfg.Spec.Exporters.OTLPGRPCExporter.Endpoint, + }, + ) + } + + for _, f := range nonEmptyStrings { + if f.value == "" { + allErrs = append( + allErrs, + field.Invalid(field.NewPath(f.path), f.path, "empty value specified"), + ) + } + } + return allErrs.ToAggregate() } diff --git a/pkg/imagevector/images.yaml b/pkg/imagevector/images.yaml index ffe15f2..e39adb2 100644 --- a/pkg/imagevector/images.yaml +++ b/pkg/imagevector/images.yaml @@ -7,8 +7,8 @@ images: - name: otel-collector sourceRepository: github.com/open-telemetry/opentelemetry-collector-contrib repository: europe-docker.pkg.dev/gardener-project/releases/3rd/opentelemetry-collector-releases/opentelemetry-collector-contrib - tag: "0.141.0" + tag: "0.144.0" - name: otel-targetallocator sourceRepository: https://github.com/open-telemetry/opentelemetry-operator repository: europe-docker.pkg.dev/gardener-project/releases/3rd/opentelemetry-operator/target-allocator - tag: "v0.141.0" + tag: "v0.144.0"