Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions api/v1/logcollector_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ type LogCollectorSpec struct {
// EKSLogForwarderDeployment configures the EKSLogForwarderDeployment Deployment.
// +optional
EKSLogForwarderDeployment *EKSLogForwarderDeployment `json:"eksLogForwarderDeployment,omitempty"`

// OTelCollector configures the OpenTelemetry Collector for exporting logs
// and metrics via OTLP. Unlike AdditionalStores entries (S3, Syslog,
// Splunk), which point at external systems, the OTel Collector is
// operator-managed infrastructure (StatefulSet, ConfigMap, RBAC, certs)
// with its own lifecycle, so it lives at the top level rather than under
// AdditionalStores.
// +optional
OTelCollector *OTelCollectorSpec `json:"otelCollector,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the naming here is a bit off. To me, it implies that this field is configuring the OTel Collector. With that framing, I would expect the config in this struct to match the standard OTel Collector config fields - which it doesn't.

To me this field is configuring OpenTelemetry export in Calico Enterprise/Cloud, which we of course translate into a deployment of an OTel Collector.

I would personally prefer a field/struct name like OpenTelemetry, and leave the comment to explain that it is used to configure OpenTelemetry export, which is powered by a deployment of the OTel Collector

}

type CollectProcessPathOption string
Expand Down Expand Up @@ -260,6 +269,38 @@ type LogCollectorList struct {
Items []LogCollector `json:"items"`
}

// OTelCollectorSpec defines the desired state of the OpenTelemetry Collector.
type OTelCollectorSpec struct {
// Logs configures which log types are exported via OTLP.
// +optional
Logs *OTelLogs `json:"logs,omitempty"`

// Metrics configures whether Calico component metrics are exported via OTLP.
// +optional
Metrics *OTelMetrics `json:"metrics,omitempty"`

// Exporters configures the OTLP export endpoints.
// +optional
Exporters []OTelExporter `json:"exporters,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: A few kubebuilder-marker gaps on the new API surface, grouping them here:

  • This list needs list markers. exporters[] is our first named object list, but it has no +listType=map / +listMapKey=name, and nothing enforces name uniqueness. So duplicate names silently collide (same exporter key), and the list merges as atomic-replace under server-side apply. Add both markers and make name required + unique.
  • Required fields aren't marked. Name and Endpoint (otelcollector_types.go:67,70) have no +optional, so they're effectively required — but the doc wants an explicit // +required.
  • Defaults live in prose. metrics.enabled (otelcollector_types.go:52) and tlsInsecure (:80) state their defaults in the comment but have no +kubebuilder:default, unlike protocol (:74) which does. Add the marker (or drop the prose claim so they don't drift).


// OTelCollectorStatefulSet configures the OTel Collector StatefulSet.
// +optional
OTelCollectorStatefulSet *OTelCollectorStatefulSet `json:"openTelemetryCollectorStatefulSet,omitempty"`
}

func (s *OTelCollectorSpec) HasLogs() bool {
return s != nil && s.Logs != nil && len(s.Logs.Types) > 0
}

func (s *OTelCollectorSpec) MetricsEnabled() bool {
return s != nil && s.Metrics != nil && s.Metrics.Enabled != nil &&
*s.Metrics.Enabled == OTelMetricsEnable
}

func (s *OTelCollectorSpec) HasDataSources() bool {
return s.HasLogs() || s.MetricsEnabled()
}

func init() {
SchemeBuilder.Register(&LogCollector{}, &LogCollectorList{})
}
169 changes: 169 additions & 0 deletions api/v1/otelcollector_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright (c) 2026 Tigera, Inc. All rights reserved.
//
// 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 v1

import (
corev1 "k8s.io/api/core/v1"
)

// OTelLogType represents the allowable log types for OTel export.
// +kubebuilder:validation:Enum=Audit;DNS;Flows
type OTelLogType string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my preference is to replace the naming convention of OTel with OpenTelemetry so that we don't rely on shorthand and have a bit better searchability, WDYT?


const (
OTelAuditLog OTelLogType = "Audit"
OTelDNSLog OTelLogType = "DNS"
OTelFlowLog OTelLogType = "Flows"
)

// OTelLogs configures log export.
type OTelLogs struct {
// Types specifies which log types to export. Supported values: Audit, DNS, Flows.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we only support these 3 types?

// +optional
Types []OTelLogType `json:"types,omitempty"`
}

// OTelMetricsEnabled is the option to enable or disable metrics export.
// +kubebuilder:validation:Enum=Enabled;Disabled
type OTelMetricsEnabled string

const (
OTelMetricsEnable OTelMetricsEnabled = "Enabled"
OTelMetricsDisable OTelMetricsEnabled = "Disabled"
)

// OTelMetrics configures metrics export.
type OTelMetrics struct {
// Enabled specifies whether to scrape and export Calico component metrics via OTLP.
// Default: Disabled
// +optional
Enabled *OTelMetricsEnabled `json:"enabled,omitempty"`
}

// OTelExporterProtocol specifies the OTLP transport protocol.
// +kubebuilder:validation:Enum=grpc;http
type OTelExporterProtocol string

const (
OTelProtocolGRPC OTelExporterProtocol = "grpc"
OTelProtocolHTTP OTelExporterProtocol = "http"
)

// OTelExporter defines an OTLP export endpoint.
type OTelExporter struct {
// Name is a unique identifier for this exporter.
Name string `json:"name"`

// Endpoint is the OTLP endpoint URL.
Endpoint string `json:"endpoint"`

// Protocol specifies the OTLP transport protocol. Default: grpc.
// +optional
// +kubebuilder:default=grpc
Protocol OTelExporterProtocol `json:"protocol,omitempty"`

// TLSInsecure disables TLS verification for this exporter. Only use for trusted in-cluster targets.
// Default: false
// +optional
TLSInsecure *bool `json:"tlsInsecure,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is our overall TLS strategy here? Do we need to support a destination that requires mTLS? Do we need to support a user having a custom CA? There is precedent for this in other components (e.g. syslog forwarding, guardian connection)

}

// OTelCollectorStatefulSet is the configuration for the OTel Collector StatefulSet.
type OTelCollectorStatefulSet struct {
// Metadata is a subset of a Kubernetes object's metadata that is added to the StatefulSet.
// +optional
Metadata *Metadata `json:"metadata,omitempty"`
// Spec is the specification of the OTel Collector StatefulSet.
// +optional
Spec *OTelCollectorStatefulSetSpec `json:"spec,omitempty"`
}

// OTelCollectorStatefulSetSpec defines configuration for the OTel Collector StatefulSet.
type OTelCollectorStatefulSetSpec struct {
// MinReadySeconds is the minimum number of seconds for which a newly created StatefulSet pod should
// be ready without any of its container crashing, for it to be considered available.
// If specified, this overrides any minReadySeconds value that may be set on the OTel Collector StatefulSet.
// If omitted, the OTel Collector StatefulSet will use its default value for minReadySeconds.
// +optional
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=2147483647
MinReadySeconds *int32 `json:"minReadySeconds,omitempty"`

// Template describes the OTel Collector StatefulSet pod that will be created.
// +optional
Template *OTelCollectorStatefulSetPodTemplateSpec `json:"template,omitempty"`
}

// OTelCollectorStatefulSetPodTemplateSpec is the OTel Collector StatefulSet's PodTemplateSpec.
type OTelCollectorStatefulSetPodTemplateSpec struct {
// Metadata is a subset of a Kubernetes object's metadata that is added to the pod's metadata.
// +optional
Metadata *Metadata `json:"metadata,omitempty"`
// Spec is the OTel Collector StatefulSet's PodSpec.
// +optional
Spec *OTelCollectorStatefulSetPodSpec `json:"spec,omitempty"`
}

// OTelCollectorStatefulSetPodSpec is the OTel Collector StatefulSet's PodSpec.
type OTelCollectorStatefulSetPodSpec struct {
// Affinity is a group of affinity scheduling rules for the OTel Collector pods.
// +optional
Affinity *corev1.Affinity `json:"affinity"`
// Containers is a list of OTel Collector containers.
// If specified, this overrides the specified OTel Collector StatefulSet containers.
// If omitted, the OTel Collector StatefulSet will use its default values for its containers.
// +optional
Containers []OTelCollectorStatefulSetContainer `json:"containers,omitempty"`
// NodeSelector gives more control over the nodes where the OTel Collector pods will run on.
// +optional
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// TopologySpreadConstraints describes how a group of pods ought to spread across topology
// domains. Scheduler will schedule pods in a way which abides by the constraints.
// All topologySpreadConstraints are ANDed.
// +optional
TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"`
// Tolerations is the OTel Collector pod's tolerations.
// If specified, this overrides any tolerations that may be set on the OTel Collector StatefulSet.
// If omitted, the OTel Collector StatefulSet will use its default value for tolerations.
// +optional
Tolerations []corev1.Toleration `json:"tolerations"`
// PriorityClassName allows to specify a PriorityClass resource to be used.
// +optional
PriorityClassName string `json:"priorityClassName,omitempty"`
}

// OTelCollectorStatefulSetContainer is an OTel Collector StatefulSet container.
type OTelCollectorStatefulSetContainer struct {
// Name is an enum which identifies the OTel Collector StatefulSet container by name.
// Supported values are: otel-collector
// +kubebuilder:validation:Enum=otel-collector
Name string `json:"name"`

// Resources allows customization of limits and requests for compute resources such as cpu and memory.
// If specified, this overrides the named OTel Collector StatefulSet container's resources.
// If omitted, the OTel Collector StatefulSet will use its default value for this container's resources.
// +optional
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`

// ReadinessProbe allows customization of the readiness probe timing parameters.
// The probe handler is set by the operator and cannot be overridden.
// +optional
ReadinessProbe *ProbeOverride `json:"readinessProbe,omitempty"`

// LivenessProbe allows customization of the liveness probe timing parameters.
// The probe handler is set by the operator and cannot be overridden.
// +optional
LivenessProbe *ProbeOverride `json:"livenessProbe,omitempty"`
}
Loading
Loading