Skip to content

feat(otelcollector): render OTel Collector - #5106

Open
tianfeng92 wants to merge 14 commits into
tigera:masterfrom
tianfeng92:EV-6862-otel-collector
Open

feat(otelcollector): render OTel Collector#5106
tianfeng92 wants to merge 14 commits into
tigera:masterfrom
tianfeng92:EV-6862-otel-collector

Conversation

@tianfeng92

@tianfeng92 tianfeng92 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Render the OTel Collector as a StatefulSet in calico-system, configured via LogCollector.spec.openTelemetry.

The collector receives logs from fluent-bit via OTLP and optionally federates Prometheus metrics, forwarding both to user-configured OTLP endpoints. It is added to the LogCollector CR rather than AdditionalStores because it is operator-managed infrastructure (StatefulSet, ConfigMap, RBAC, certs) with its own lifecycle, not a pointer to an external system.

API

spec:
  openTelemetry:
    logs:
      types: [Flows, DNS, Audit]
    metrics:
      state: Enabled
    exporters:
      - name: datadog
        endpoint: https://otlp.datadoghq.com:4318
        protocol: http
        auth:
          headers:
            - name: DD-API-KEY
              valueFrom:
                secretKeyRef: { name: otlp-datadog-auth, key: api-key }
        tls:
          caConfigMapName: corp-ca
          clientCertSecretName: corp-client-mtls

TLS and auth are configured per exporter rather than once for all of them: this fans out to several external backends, which generally expect different client identities and different trust anchors. tls.clientCertSecretName turns on mutual TLS for that exporter alone; tls.caConfigMapName pins that exporter to the named CA alone — the system roots are not trusted as well, since keeping them would let any public authority satisfy the connection, which is what pinning is for. An exporter that names no CA verifies against the system roots. Verification is never disabled. Header credentials are read from Secrets into environment variables and referenced as ${env:...}, so they never appear in the rendered config.

Exporter CAs are deliberately kept out of the operator's trusted bundle — that bundle backs the receiver's client_ca_file, so folding them in would make them valid signers for inbound client certificates. The collector carries its own named bundle rather than the shared calico-system/tigera-ca-bundle, which the Installation controller owns.

Release Note

Add OpenTelemetry Collector component to export logs and metrics via OTLP.

Test plan

  • Unit tests for render, controller, and the fluent-bit pipeline output
  • Deployed to a cluster: all three log types reaching an OTLP backend, Prometheus federation exporting, collector's own ServiceMonitor scraping, clean startup and teardown
  • Verified the collector is not deployed when the license lacks the feature, and that disabling export removes every resource it owns
  • Full lifecycle on a cluster: enable → logs and metrics flowing → disable → all resources removed → re-enable → data resumes

Exercised on a cluster since: a pinned caConfigMapName renders ca_file with no system roots and is mounted and loaded, and a user-supplied otel-collector-tls reaches fluent-bit's trust bundle and is accepted with no verification errors.

Still unit-tested only: clientCertSecretName and auth.headers against a real TLS-terminating backend — no external OTLP endpoint demanding mutual TLS or a credential header was available to test against.

Follow-up: moving Dex and Guardian onto the shared ParseExternalDestination helper (r3717124352) is going into its own PR.

EV-6862

Comment thread internal/controller/otelcollector_controller.go
Comment thread pkg/render/otelcollector/component.go Outdated
Comment thread pkg/render/otelcollector/component.go Outdated
@tianfeng92
tianfeng92 force-pushed the EV-6862-otel-collector branch 7 times, most recently from 8124957 to 222f3f4 Compare July 27, 2026 23:24
@tianfeng92 tianfeng92 changed the title feat(otelcollector): render OTel Collector from LogCollector.spec.otelCollector feat(otelcollector): render OTel Collector Jul 28, 2026
@tianfeng92
tianfeng92 force-pushed the EV-6862-otel-collector branch 2 times, most recently from 6dd36c8 to a8c2d9e Compare July 28, 2026 17:55
Comment thread pkg/controller/otelcollector/controller.go
Comment thread pkg/controller/otelcollector/controller.go
Comment thread pkg/render/otelcollector/component.go
Comment thread pkg/render/otelcollector/component.go Outdated
Comment thread pkg/controller/otelcollector/controller.go Outdated
Comment thread pkg/render/otelcollector/collector-config.yaml.template
Comment thread api/v1/otelcollector_types.go Outdated
Comment thread api/v1/logcollector_types.go Outdated
Comment thread pkg/render/otelcollector/component.go Outdated
Comment thread pkg/render/otelcollector/component.go Outdated
@tianfeng92
tianfeng92 force-pushed the EV-6862-otel-collector branch from c612900 to fca9047 Compare August 6, 2026 22:12
@tianfeng92
tianfeng92 requested a review from pasanw August 6, 2026 23:02

@pasanw pasanw left a comment

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.

Thanks for addressing my comments! I let Claude lead the re-review and reviewed everything it said was addressed along with what it flagged - it helped flag some things that weren't fully addressed but it also flagged some new issues (sorry about that!)

I'll let you and @rene-dekker work through them together and decide the best way to proceed.

Comment thread pkg/render/common/networkpolicy/networkpolicy.go
Comment thread pkg/render/otelcollector/component.go
Comment thread pkg/render/logcollector/pipeline.go Outdated
Comment thread pkg/render/monitor/monitor.go
Comment thread pkg/controller/otelcollector/controller.go
Comment thread api/v1/opentelemetry_types.go Outdated
@caseydavenport caseydavenport self-assigned this Aug 10, 2026
Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
tianfeng92 and others added 3 commits August 11, 2026 09:37
…lCollector

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Renames the API surface from OTel to OpenTelemetry, and reworks exporter
TLS, certificate rotation, validation and network policy.

API:
- OTel* types become OpenTelemetry*; LogCollector.spec.otelCollector becomes
  spec.openTelemetry. The file moves to api/v1/opentelemetry_types.go.
- Replace tlsInsecure with mutualTLS. tlsInsecure rendered tls.insecure,
  which disables TLS entirely rather than skipping verification, so it could
  never do what it documented. A private CA is now supplied out of band via
  the otel-collector-ca ConfigMap and a client keypair via the
  otel-collector-client-certs Secret, following the syslog/Splunk and
  external Elasticsearch conventions. Plaintext is selected by an http://
  endpoint, as with Splunk. No input disables verification.
- Add listType/listMapKey, required and default markers on exporters.

Certificate rotation:
- Roll the pod on certificate change via HashAnnotations. The config hash
  alone never changes on rotation because the config holds paths, not PEM,
  so a rotated CA would silently break fluent-bit ingest until something
  else restarted the collector.
- Watch tigera-ca-private, and reload the receiver's CA and keypair in place
  so rotation usually costs no restart.

Validation:
- Reject specs the collector cannot start from (no exporters, no data
  sources, duplicate exporter names) instead of rendering a config it
  rejects at boot and leaving status on Progressing with no reason.
- Degrade when the exporter CA or client keypair exists but is empty.
- Degrade rather than return silently when the Installation is unreadable.

Network policy:
- Pin egress to each exporter's destination and drop the blanket rule that
  allowed any host on the OTLP ports. In-cluster destinations match by
  Service, since Calico resolves Domains rules from observed DNS answers
  and those never match a ClusterIP reached via the cluster domain.
- Set Source on both ingress rules. The internal metrics port serves
  without TLS or authentication and was reachable by any pod.

Lifecycle:
- Remove the collector's resources when the feature is disabled, the
  license loses the feature, or the license expires. Nothing owns them, so
  they previously lingered, and the expiry message claimed forwarding had
  stopped while the collector kept exporting.
- Pin to a single replica. Each replica federates the same Prometheus
  targets under its own service.instance.id, duplicating every series.

Also register the receiver keypair's key usages, give the probes a boot
grace, derive the memory limiter from the container's effective memory
limit so overrides take effect, and use the current otlp_grpc/otlp_http
exporter type names.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds a ServiceMonitor for the collector so a silently failing export
pipeline is visible: queue depth, export failures and dropped records are
otherwise only in the pod's logs.

Gated on the collector actually being configured, and removed when it is
not. The ServiceMonitor selects a Service that only exists while the
collector does, so rendering it unconditionally leaves one behind matching
nothing once OpenTelemetry export is switched off. The monitor controller
reads LogCollector to decide, mirroring how LicenseExpired already gates
the other ServiceMonitors.

The collector's identity constants move to pkg/render so both packages can
share them: pkg/render/otelcollector imports pkg/render/monitor for the
Prometheus federation target, so monitor cannot import it back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tianfeng92 and others added 7 commits August 11, 2026 09:37
…e Collector

The field name was changed to OpenTelemetry but its comment still opened
with "configures the OpenTelemetry Collector", which is the framing the
review pushed back on: it implies the struct mirrors upstream Collector
configuration, which it does not. Describe it as configuring OpenTelemetry
export, which the operator happens to implement with a Collector.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Hash exporter CA and client TLS in pod annotations so cert rotation
  triggers a StatefulSet roll
- Gate OTel fluent-bit output on osType==Linux, matching other non-Linseed
  outputs
- Mirror IsFeatureActive check in monitor controller so ServiceMonitor is
  deleted when the license lacks the otel-collector feature
- Reject mutualTLS on http:// endpoints instead of silently ignoring
  the client cert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ServiceMonitor rendered a target the tigera-prometheus default-deny
blocked, so the scrape reported up=0 with nothing naming the cause. Add the
matching egress rule under the same gate.

Also track the collector's real deploy conditions: requeue on an unreadable
LogCollector rather than treating it as disabled, which flapped both
resources on an apiserver blip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Trusted bundle: the collector rendered the shared calico-system/tigera-ca-bundle
with a CA-only set, which the Installation controller owns and fills with a
different certificate set. The component handler replaces ConfigMap data
wholesale, so the two overwrote each other every reconcile. Carry our own named
bundle, as fluent-bit already does, and add the Prometheus serving certificate
so the federation scrape verifies a BYO cert. System roots stay out: this bundle
backs the receiver's client_ca_file.

Gating: fluent-bit shipped to the collector whenever spec.openTelemetry was set,
even when the license or an invalid spec meant it was never deployed. All three
controllers now share OpenTelemetrySpec.Deployable, and the monitor controller
watches LogCollector so enabling export actually wakes it.

Egress: an endpoint with no parseable port emitted no rule at all, so the
default-deny silently dropped every export. Fall back to the protocol's default
OTLP port.

Non-cluster-host flow and DNS records carry their own tags and were never
matched, so they reached Linseed but never the OTLP backend.

Teardown probed only the StatefulSet, stranding the RBAC, ConfigMap, Service and
policy if it had already gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One shared client certificate meant one identity for every backend, and a
single CA for all of them. This feature fans out to several external backends
that generally want different client certs and different trust anchors, so
both move onto the exporter.

Each exporter now takes its own tls.caConfigMapName and tls.clientCertSecretName,
plus auth.headers for backends that authenticate with a token or a
vendor-specific header instead. Header values are read from Secrets and reach
the collector as environment variables, so credentials never land in the
rendered ConfigMap.

The operator gathers the user's per-exporter material into one object per kind,
keyed by exporter. The teardown cannot enumerate the spec once the feature is
off, so the names it has to delete stay fixed.

Drops the mutualTLS boolean, which is not the repo idiom for a toggle, and
renames metrics.enabled to metrics.state so the value no longer reads
"enabled: Disabled".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review of the per-exporter TLS/auth restructure turned up several ways a
credential could go to the wrong place or be silently dropped.

Header credentials reach the collector as environment variables whose names
were derived by collapsing anything outside [A-Z0-9_] to an underscore. Two
exporters differing only in such a character produced the same variable, so one
backend was sent the other's token. Exporter names are now restricted to a DNS
label - which they had to be anyway, since they key ConfigMap and Secret data
the API server would otherwise reject - and Validate rejects any header pair
that still collides. The derivation moves to api/v1 so validation and rendering
cannot drift apart.

Auth headers on an http:// endpoint were accepted and put the credential on the
wire in the clear; the equivalent mutualTLS case was already rejected. They are
now rejected too.

Exporter Secrets were deduped by name before validation, so a second header
reading a different key of the same Secret was never checked and rendered an
empty credential. Dedupe the fetch, not the validation.

fluent-bit verified the collector against a bundle that never received its
serving certificate. An operator-minted cert is covered by the CA, but a
user-supplied one is honoured as-is and every connection would fail with an
unknown authority while both statuses reported Available.

Also: delete the exporter aggregates when a spec no longer names any credential
rather than leaving them behind; name the certificatemanagement component's
objects in the teardown, which only rendered the otel component; apply the
StatefulSet after the volumes it mounts; and share the collector's port instead
of hardcoding it in the fluent-bit output.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
calicoLibHostPath was committed out of alphabetical order, so controller-gen
relocates it and dirty-check fails for any PR that rebases onto master. Not
related to this PR's changes; included so CI can pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tianfeng92
tianfeng92 force-pushed the EV-6862-otel-collector branch from ba5aeb9 to e8a86d5 Compare August 11, 2026 17:00
@rene-dekker

Copy link
Copy Markdown
Member

Ran Claude Code over this branch as a review pass; it flagged one thing that looks like it would stop the collector from starting at all, so raising it ahead of the smaller notes.

Exporter component IDs may not be valid otelcol types

pkg/render/otelcollector/component.go:76-77:

exporterPrefixHTTP  = "otlp_http"
exporterPrefixGRPC  = "otlp_grpc"

These are used as the component-ID type in two places — the exporters block ({{.Prefix}}/{{.Name}}:, collector-config.yaml.template:50) and every pipeline's exporters: [...] list (ExporterNames, component.go:511) — producing:

exporters:
  otlp_grpc/backend:
    endpoint: ...
service:
  pipelines:
    logs:
      exporters: [otlp_grpc/backend]

In an otelcol config the part before the / has to match a registered component factory type. Upstream those are otlp for the gRPC exporter and otlphttp for the HTTP one — otlp_grpc and otlp_http aren't factory types in any upstream distro. otelcol resolves the type at config-load time and a miss is fatal:

error decoding 'exporters': unknown type: "otlp_grpc" for id "otlp_grpc/backend"

If that applies here, the StatefulSet crash-loops on every install with an exporter configured, the health check never passes, and the TigeraStatus sits on Progressing with nothing pointing at the generated config as the cause.

The render tests (component_test.go:342, :382-404) assert these same prefixes, so they pass either way — they check that the operator renders what the operator intends, not that otelcol accepts it.

What I couldn't verify: the container runs /usr/bin/otelcol from the combined calico image, and the current tigera/calico:latest doesn't contain that binary yet (only calico, calico-ipam, calicoctl, csi-node-driver-registrar), so I couldn't run otelcol components against it. Two questions:

  1. Does the companion build register custom factory types under these names? A factory's type comes from the factory itself, so otlp_grpc/otlp_http would need custom wrapper factories rather than a builder-manifest rename — if that's what's happening, worth a comment in the template saying so, because it reads as a bug.
  2. If not, these want to be otlphttp and otlp.

Either way it'd be good to have one test that feeds a rendered config to the actual collector binary — the current tests can't distinguish the two cases.

Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/logcollector_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
Comment thread api/v1/opentelemetry_types.go Outdated
"github.com/tigera/operator/pkg/render/common/networkpolicy"
rlogcollector "github.com/tigera/operator/pkg/render/logcollector"
"github.com/tigera/operator/pkg/render/monitor"
"github.com/tigera/operator/pkg/render/otelcollector"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe we need a watch in this controller for the new secret otel-collector-tls

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added to the secrets watch loop in add():

// The collector's serving certificate goes into fluent-bit's trusted
// bundle, so creating or rotating it has to rebuild that bundle.
otelcollector.OpenTelemetryCollectorServerTLSSecretName,

and consumed in Reconcile, where the cert joins fluent-bit's trusted bundle when logs are exported:

if instance.Spec.OpenTelemetry.HasLogs() {
otelCertificate, err := certificateManager.GetCertificate(r.client, otelcollector.OpenTelemetryCollectorServerTLSSecretName, common.OperatorNamespace())
if err != nil {
r.status.SetDegraded(operatorv1.ResourceReadError, "Failed to get the OpenTelemetry Collector certificate", err, reqLogger)
return reconcile.Result{}, err
}
// Absent simply means the otel controller has not minted it yet; it will
// trigger another reconcile here when it does.
if otelCertificate != nil {
extraCerts = append(extraCerts, otelCertificate)
}

@rene-dekker

Copy link
Copy Markdown
Member

Another one from the same Claude Code review pass, much smaller than the last:

Logs.Types accepts duplicates, which render duplicate fluent-bit outputs

api/v1/opentelemetry_types.go:34-38 declares the field with an item-level enum but no uniqueness constraint:

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

The generated CRD (operator.tigera.io_logcollectors.yaml:1110-1123) is a plain type: array with an item enum and no x-kubernetes-list-type: set, so the API server accepts types: [Flows, Flows]. Nothing downstream dedupes it either — otelLogTypes() (logcollector_controller.go:952-957) passes the slice straight through, and pipeline.go:255 iterates it appending one output per entry:

outputs:
  - name: opentelemetry
    match: flows
    ...
  - name: opentelemetry
    match: flows
    ...

fluent-bit runs both instances, so every flow record is shipped to the collector — and on to the customer's OTLP backend — twice. For a usage-priced backend that is a duplicated bill as well as duplicated data. With NonClusterHost configured it's four outputs, since each type already renders a second output for the non-cluster tag.

Fix is a marker:

// +optional
// +listType=set
Types []OpenTelemetryLogType `json:"types,omitempty"`

Exporters already has +listType=map + +listMapKey=name for the same reason, so this looks like an oversight rather than a decision. Note Validate() guards against duplicate exporter names in Go as well, with a comment about the CRD being out of date — if that concern applies here too, the same belt-and-braces dedupe would be worth adding for log types.

tianfeng92 and others added 4 commits August 12, 2026 10:02
Trust: a pinned CA is now the only thing trusted for that exporter. Loading it
alongside the system roots meant any public authority still satisfied the
connection, which is what pinning is meant to prevent.

The plaintext check was case-sensitive, so HTTP:// was treated as encrypted and
credentials on it were accepted. Schemes are case-insensitive; the check now
lives on the exporter so validation and rendering cannot diverge, and the
endpoint carries a Pattern requiring a scheme.

fluent-bit's controller now watches otel-collector-tls. Its serving certificate
goes into fluent-bit's trusted bundle, so creating or rotating it has to rebuild
that bundle rather than wait for an unrelated reconcile.

Documents that a header value is sent verbatim, so a bearer token needs the
"Bearer " prefix in the Secret - an easy configuration error to make.

Merges opentelemetry_types.go into logcollector_types.go; the split was
arbitrary. Trims the field comments to what an end user needs, rather than
rationale aimed at a reviewer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Turning logs off while keeping metrics stops the keypair being rendered, but
nothing removed the copy already in calico-system, leaving a server key nothing
serves with. Only a full teardown collected it.

Also drops OpenTelemetryExporter.MutualTLS(), which nothing called;
ClientCertSecret() is what the controller and render actually use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A valid, licensed spec was enough to render the ServiceMonitor and the
Prometheus egress rule, but the otel controller stops short of rendering when
material an exporter names is missing, leaving a ServiceMonitor selecting a
Service that was never created. Follow the Service instead, watched so it does
not wait for the periodic reconcile.

Hash the exporter client keypair and header credentials with SHA-256 rather
than the SHA-1 in rmeta.AnnotationHash; the digest lands in a pod annotation
anyone who can read pods can read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rene's review asked for API docs that explain the field rather than motivate the
choice behind it. Drop the internal rationale from exporters, exporter name and
caConfigMapName, and regenerate the CRD descriptions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@caseydavenport caseydavenport left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Most of these comments are just me scolding Claude for being way too verbose in its code comments....

// (StatefulSet, ConfigMap, RBAC, certs) with its own lifecycle, so this
// lives at the top level rather than under AdditionalStores.
// +optional
OpenTelemetry *OpenTelemetrySpec `json:"openTelemetry,omitempty"`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A bit of a high level question here, but is LogCollector really the right place for this?

We're exporting more than just logs ( e.g., prometheus metrics ) - so I wonder if this really wants its own CRD?

// See the License for the specific language governing permissions and
// limitations under the License.

package otelcollector

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit, but I think we have a new enterprise subdir in a few places for utilities - we should try to keep any utils we're adding within those directories where appropriate (this is sort of more general for the whole PR).

Comment on lines +136 to +137
// The collector's serving certificate goes into fluent-bit's trusted
// bundle, so creating or rotating it has to rebuild that bundle.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AI overcommenting here.

Comment on lines 480 to +489
// Fluent Bit needs to mount system certificates in the case where Splunk, Syslog or AWS are used.
// The bundle carries fluent-bit's own name: calico-system's shared tigera-ca-bundle is rendered by
// the core Installation controller with a different certificate set, and the component handler
// replaces ConfigMap data wholesale — an unnamed bundle here would fight it, and additions like
// the syslog user CA would be lost to whichever controller wrote last.
trustedBundle := certificatemanagement.CreateNamedTrustedBundle(render.FluentBitNodeName, certificateManager.KeyPair(), true, prometheusCertificate, linseedCertificate)
// The collector's serving certificate has to be trusted for the OTLP output to
// verify it. The operator CA covers an operator-minted cert, but a
// user-supplied otel-collector-tls is honoured as-is, and fluent-bit would then
// reject every connection with an unknown-authority error while both statuses
// still reported Available.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This massive comment block also looks like AI over-commenting to me.

I think this should be as simple as:

// FluentBit needs system certificates to talk to external tools like Splunk, Syslog, or AWS. We give 
// it its own trusted bundle as a result to avoid contamination with other services in this namespace. 
//
// We must also trust the OTEL collector's server certificate, if enabled.

Comment on lines +693 to +697
// Same predicate the otel controller deploys on. Pointing fluent-bit at a
// collector that is never rendered — unlicensed, or an invalid spec — just
// fails every chunk and fills the storage buffer.
OpenTelemetryCollectorEnabled: instance.Spec.OpenTelemetry.Deployable(
utils.IsFeatureActive(license, common.OpenTelemetryCollectorFeature)),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Same predicate the otel controller deploys on. Pointing fluent-bit at a
// collector that is never rendered — unlicensed, or an invalid spec — just
// fails every chunk and fills the storage buffer.
OpenTelemetryCollectorEnabled: instance.Spec.OpenTelemetry.Deployable(
utils.IsFeatureActive(license, common.OpenTelemetryCollectorFeature)),
OpenTelemetryCollectorEnabled: instance.Spec.OpenTelemetry.Deployable(utils.IsFeatureActive(license, common.OpenTelemetryCollectorFeature)),

}

// fetchExporterMaterial reads the CA, client keypair and header credentials each
// exporter names, keyed for the render. The second return reports that the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this comment is wrong / out of date? Second return is a map of Secrets.


// clusterServiceHost matches a Kubernetes service DNS name, with or without the
// cluster domain suffix: <service>.<namespace>.svc[.cluster.local]
var clusterServiceHost = regexp.MustCompile(`^([a-z0-9]([-a-z0-9]*[a-z0-9])?)\.([a-z0-9]([-a-z0-9]*[a-z0-9])?)\.svc(\.[a-z0-9.-]+)?$`)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, is there not an upstream regex we could import for this?

Comment on lines +171 to +174
// OpenTelemetryEnabled reports whether the OTel Collector is configured. Its
// ServiceMonitor is created and removed with the collector; without this the
// monitor render cannot tell, and would leave one behind selecting a Service
// that no longer exists.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Again, self-explanatory from the code, Claude.

Suggested change
// OpenTelemetryEnabled reports whether the OTel Collector is configured. Its
// ServiceMonitor is created and removed with the collector; without this the
// monitor render cannot tell, and would leave one behind selecting a Service
// that no longer exists.


func (c *component) Objects() ([]client.Object, []client.Object) {
if c.cfg.Disabled {
// Nothing is owned by a CR we can rely on being deleted, so switching the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Nothing is owned by a CR we can rely on being deleted, so switching the
// Objects are owned by a sub-struct of the LogCollector CR rather than the CR as a whole, so switching the

Comment on lines +462 to +466
// Plaintext is set for http:// endpoints. OTLP backends are commonly exposed
// without TLS in-cluster, and the scheme is how the user asks for that —
// mirroring the Splunk output, where a plain-http endpoint does no TLS. There
// is deliberately no field for "TLS on, verification off".
Plaintext bool

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, this is sketchy - we have a policy of disallowing HTTP in our product. I don't think we should allow this - it's a bad security posture that we are enabling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants