Skip to content

feat(heimdall): OTel Collector log-shipper DaemonSet#10

Merged
Cervator merged 3 commits into
mainfrom
feat/otel-collector-log-shipper
Jul 3, 2026
Merged

feat(heimdall): OTel Collector log-shipper DaemonSet#10
Cervator merged 3 commits into
mainfrom
feat/otel-collector-log-shipper

Conversation

@agent-refr

Copy link
Copy Markdown

AI-assisted change proposal. Filed by agent driven by @Cervator via GDD.

Summary

  • Adds a deploy-otel-collector step to the heimdall Crossplane Composition: a provider-helm Release of the upstream opentelemetry-collector chart (pinned 0.159.0, k8s distro) as a DaemonSet that tails every node's pod stdout (filelog receiver via the logsCollection preset), enriches with pod/namespace/node metadata (k8sattributes processor via the kubernetesAttributes preset), and ships to the existing in-cluster Loki over native OTLP (auth_enabled: false, no tenant header). Closes the platform's missing-log-collector gap.
  • Placed after deploy-loki and before auto-ready (which stays last); no nidavellir change — the selfHeal ArgoCD app reconciles it.
  • Two kuttl e2e tests: otel-collector-running (DaemonSet Ready on all nodes) and otel-loki-logs (a throwaway pod's stdout marker reaches Loki via LogQL — proves real pod-log shipping). The collector-running test uses {.items[*]...} jsonpath so the graceful "no DaemonSet" message is reachable under set -e.
  • Docs: architecture pipeline six→seven steps + the ServiceMonitor release: <name>-kube-prometheus discovery fact; README "Logs" guidance corrected to the accurate OTel-DaemonSet model.
  • Also carries a small prior local commit correcting the OTel plan doc (argo namespace + an environment-readiness precondition).

Test plan

  • helm template render-validation: the rendered logs pipeline keeps filelog + k8sattributes and adds the otlphttp/loki exporter at :3100/otlp; DaemonSet + ClusterRole/Binding/ServiceAccount present.
  • Live deploy (homelab k3d via Gitea hydration): Release heimdall-<xr>-otel-collector Ready, DaemonSet 3/3, collector logs error-free.
  • bash test.sh --test otel-collector-running → PASS; bash test.sh --test otel-loki-logs → PASS (a fresh pod's stdout marker queryable in Loki with k8s_namespace_name enrichment).

Related

Cervator and others added 2 commits June 28, 2026 23:02
…dition to OTel plan

The merged OTel Collector log-shipper plan hardcoded the upstream-default
`argocd` control-plane namespace in Task 6's deploy/verify commands, but every
cluster has used `argo` since the 2026-03-21 namespace rename. Correct the
`kubectl -n argocd ...` invocations and the Task 6 interface note to `argo`,
and add a parenthetical clarifying that this is the namespace — not the
`argocd.argoproj.io/*` annotation domain, which is unchanged.

Also add an "environment readiness (precondition)" Global Constraint: the plan
assumes a target cluster where heimdall is already deployed (live `heimdall`
namespace + Loki sink + the `heimdall` ArgoCD Application). If the target
predates heimdall's activation — e.g. an older k3d whose ArgoCD app-of-apps is
synced from a stale Gitea mirror that omits `heimdall-app.yaml` — the agent must
first work through the relevant nordri/nidavellir setup so ArgoCD reconciles
heimdall, then resume Task 1. Notes that the cluster-independent code work
(Tasks 2-5, 7) can proceed without a live heimdall; only Task 1 preflight and
Task 6 deploy/verify are gated on environment readiness.

Surfaced while resuming the tafl-game-hosting arc against local k3d, where
heimdall is not yet deployed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close the platform's missing-log-collector gap. Add a `deploy-otel-collector` step to the Crossplane Composition — a provider-helm Release of the upstream `opentelemetry-collector` chart (pinned 0.159.0, k8s distro) as a DaemonSet that tails every node's pod stdout via the filelog receiver (logsCollection preset), enriches with pod/namespace/node metadata via the k8sattributes processor (kubernetesAttributes preset), and ships to the existing in-cluster Loki over native OTLP (`auth_enabled: false`, no tenant header). Placed after deploy-loki and before auto-ready (which stays last); no nidavellir change — the selfHeal ArgoCD app reconciles it.

Two kuttl e2e tests: `otel-collector-running` (DaemonSet Ready on all nodes) and `otel-loki-logs` (a throwaway pod's stdout marker reaches Loki via LogQL — proves real pod-log shipping, not just the collector's own logs). The collector-running test uses `{.items[*]...}` jsonpath (not `[0]`) so the graceful "no DaemonSet" message is reachable under `set -e`.

Docs: architecture.md pipeline list six→seven steps with the OTel bullet + the ServiceMonitor `release: <name>-kube-prometheus` discovery fact; README "Logs" guidance corrected to the accurate OTel-DaemonSet model with a `{k8s_namespace_name=...}` LogQL example.

Verified live: Release Ready, DaemonSet 3/3, both kuttl tests green (real pod stdout queryable in Loki).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 19 seconds

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 27e123cb-890a-4dd5-8f80-c587fad68a76

📥 Commits

Reviewing files that changed from the base of the PR and between 352afd8 and ac423d6.

📒 Files selected for processing (3)
  • crossplane/composition.yaml
  • tests/e2e/otel-collector-running/00-cmd.yaml
  • tests/e2e/otel-loki-logs/00-cmd.yaml
📝 Walkthrough

Walkthrough

This PR adds an OpenTelemetry Collector DaemonSet step to the Crossplane composition pipeline for shipping pod logs to Loki via OTLP, updates architecture/plan/README documentation to match, and adds two KUTTL e2e tests validating collector deployment health and end-to-end log ingestion into Loki.

Changes

OpenTelemetry Collector log shipping

Layer / File(s) Summary
Composition pipeline step for OTel Collector
crossplane/composition.yaml
Adds a deploy-otel-collector step installing the opentelemetry-collector Helm chart as a DaemonSet with log collection, Kubernetes attribute enrichment, an otlphttp exporter to Loki, and resource requests/limits.
Architecture, plan, and README documentation updates
docs/architecture.md, docs/plans/2026-06-28-otel-collector-log-shipper-plan.md, README.md
Renumbers pipeline steps and adds ServiceMonitor labeling note, adds a heimdall-deployed precondition and corrects ArgoCD namespace to argo in the plan, and updates README log guidance to OTLP-derived labels.
E2E test: OTel Collector DaemonSet readiness
tests/e2e/otel-collector-running/00-cmd.yaml
Adds a KUTTL test step verifying the DaemonSet exists and ready pod count matches desired scheduled count.
E2E test: Loki log ingestion verification
tests/e2e/otel-loki-logs/00-cmd.yaml
Adds a KUTTL test step that launches a marker pod, queries Loki via LogQL with retries, asserts the marker was ingested, and cleans up test resources.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Pod
  participant OtelCollectorDaemonSet
  participant LokiService

  Pod->>OtelCollectorDaemonSet: stdout logs in /var/log/pods
  OtelCollectorDaemonSet->>OtelCollectorDaemonSet: enrich with k8s attributes
  OtelCollectorDaemonSet->>LokiService: export via otlphttp to /otlp
Loading
sequenceDiagram
  participant TestStep
  participant MarkerPod
  participant OtelCollectorDaemonSet
  participant LokiService
  participant CurlQueryPod

  TestStep->>MarkerPod: create and echo unique marker
  MarkerPod->>OtelCollectorDaemonSet: emit stdout logs
  OtelCollectorDaemonSet->>LokiService: export via OTLP
  TestStep->>CurlQueryPod: run LogQL query against LokiService
  CurlQueryPod->>LokiService: query for marker
  LokiService-->>CurlQueryPod: JSON result
  CurlQueryPod-->>TestStep: PASS/FAIL based on result
  TestStep->>TestStep: cleanup pod and namespace
Loading

Possibly related PRs

  • SiliconSaga/heimdall#4: Inserts the same deploy-otel-collector pipeline step into crossplane/composition.yaml alongside the existing self-health step ordering.
  • SiliconSaga/heimdall#9: Updates the same OTel Collector log-shipper plan document with related deploy-otel-collector step and verification/task instructions.

Suggested reviewers: Cervator

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding an OTel Collector log-shipper DaemonSet for heimdall.
Description check ✅ Passed The description matches the changeset and summarizes the collector, tests, docs, and plan updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/otel-collector-log-shipper

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crossplane/composition.yaml`:
- Around line 354-360: The current resource settings for the collector DaemonSet
are likely too low for the filelog and k8sattributes workload, which can lead to
restarts or OOMKills under heavy log volume. Update the resources block in the
collector-related manifest to raise the memory request/limit or add a matching
self-health alert for collector restarts/OOM, using the existing filelog and
k8sattributes DaemonSet configuration as the place to make the change.
- Around line 337-344: The DaemonSet log collection config is too broad and
captures stdout from every namespace, not just heimdall. Update the
OpenTelemetry Collector setup in the composition to scope or filter pod log
ingestion using the logsCollection/filelog receiver or a filter processor, and
explicitly exclude or document sensitive namespaces/content. Keep the change
localized to the `presets.logsCollection` / `kubernetesAttributes` configuration
so cluster-wide log aggregation is intentional.

In `@tests/e2e/otel-collector-running/00-cmd.yaml`:
- Around line 13-27: The test logic in the DaemonSet lookup is assuming a single
result from the label query, but `DS` is populated from `kubectl get daemonset
... jsonpath='{.items[*].metadata.name}'`, which can return multiple names and
break later `kubectl get daemonset "$DS"` calls. Update the command sequence in
`00-cmd.yaml` to explicitly guard against multiple matches by validating the
number of returned DaemonSets before using `DS`, and emit a clear failure if
more than one is found so the later `DESIRED` and `READY` lookups always operate
on exactly one daemonset.
- Around line 5-42: The otel-collector-running command script does a single
readiness check with no retry, which can fail during a fresh rollout before the
DaemonSet settles. Update the shell logic in the command step to poll for
readiness with a bounded retry/backoff loop around the kubectl reads for the
collector DaemonSet, using the existing DS/READY/DESIRED checks and failure
messages. Keep the final pass/fail output, but only exit once the collector
stays unready after the retries are exhausted.

In `@tests/e2e/otel-loki-logs/00-cmd.yaml`:
- Around line 51-62: The query pod launch in the test script does not enforce a
client-side timeout and the polling loop can fall through without clearly
failing when the pod never reaches a terminal state. Update the kubectl run/curl
flow in the query-pod handling block to add a curl timeout and introduce an
explicit completion check after the wait loop, using the existing QUERY_POD and
the sibling test’s DONE-style pattern as a guide so a non-Succeeded/Failed pod
is treated as a test failure instead of silently continuing.
- Around line 15-19: The cleanup in the test script leaves the fixed TEST_NS
namespace deletion asynchronous, which can cause reruns to collide with a
still-terminating namespace. Update the cleanup logic in the test YAML’s cleanup
function to ensure the namespace is fully removed before the next run starts,
and make the namespace recreation path in the same script tolerate or wait for
deletion completion. Use the TEST_NS and cleanup symbols to locate the affected
teardown flow.
- Around line 22-28: The Loki service lookup in the shell setup can fail before
the empty-check runs because `kubectl get svc` with the `jsonpath` selector may
exit non-zero under `set -e`. Update the service discovery logic around the
`LOKI_SVC` assignment and subsequent `if [ -z "$LOKI_SVC" ]` check to tolerate a
missing match and still print the intended friendly failure message, following
the same hardening pattern used in `tests/e2e/loki-ingestion/00-cmd.yaml`.
- Around line 7-9: The shell step in this e2e test needs an explicit per-command
timeout because the retry loop can exceed the suite’s 300s limit. Add a timeout
to the script step in the otel-loki-logs YAML so the command is allowed to run
for the full retry budget, and keep the existing retry behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a72f3a64-58b6-41f1-85ba-5a8b907ae0f1

📥 Commits

Reviewing files that changed from the base of the PR and between a05d6a7 and 352afd8.

📒 Files selected for processing (6)
  • README.md
  • crossplane/composition.yaml
  • docs/architecture.md
  • docs/plans/2026-06-28-otel-collector-log-shipper-plan.md
  • tests/e2e/otel-collector-running/00-cmd.yaml
  • tests/e2e/otel-loki-logs/00-cmd.yaml

Comment thread crossplane/composition.yaml
Comment thread crossplane/composition.yaml Outdated
Comment thread tests/e2e/otel-collector-running/00-cmd.yaml Outdated
Comment thread tests/e2e/otel-collector-running/00-cmd.yaml Outdated
Comment thread tests/e2e/otel-loki-logs/00-cmd.yaml Outdated
Comment thread tests/e2e/otel-loki-logs/00-cmd.yaml
Comment thread tests/e2e/otel-loki-logs/00-cmd.yaml
Comment thread tests/e2e/otel-loki-logs/00-cmd.yaml

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an OpenTelemetry Collector Helm release to the Heimdall Crossplane Composition to run a DaemonSet that tails /var/log/pods, enriches records with Kubernetes metadata, and exports logs to in-cluster Loki via OTLP. This fills the “no cluster log shipper” gap in Heimdall’s observability stack and updates docs/tests accordingly.

Changes:

  • Adds a deploy-otel-collector pipeline step in crossplane/composition.yaml using the upstream opentelemetry-collector chart (DaemonSet + Loki OTLP export).
  • Introduces two kuttl e2e tests to validate the collector is Ready on all nodes and that real pod stdout reaches Loki.
  • Updates documentation (README + architecture + plan) to reflect the new log shipping architecture and discovery labels.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
crossplane/composition.yaml Adds a new pipeline step deploying the OTel Collector as a log-shipping DaemonSet exporting to Loki over OTLP.
tests/e2e/otel-collector-running/00-cmd.yaml New e2e test ensuring the collector DaemonSet exists and is Ready on all nodes.
tests/e2e/otel-loki-logs/00-cmd.yaml New e2e test proving real workload stdout is ingested into Loki via the collector.
README.md Updates user guidance for logs to match the OTel-Collector-to-Loki pipeline and label usage.
docs/plans/2026-06-28-otel-collector-log-shipper-plan.md Clarifies environment readiness preconditions and corrects ArgoCD namespace references.
docs/architecture.md Expands the architecture pipeline description to include the new OTel Collector step and ServiceMonitor label note.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/e2e/otel-loki-logs/00-cmd.yaml
Comment thread tests/e2e/otel-loki-logs/00-cmd.yaml Outdated
Comment thread tests/e2e/otel-loki-logs/00-cmd.yaml Outdated
Comment thread tests/e2e/otel-collector-running/00-cmd.yaml
…#10)

Address the #10 review (CodeRabbit + Copilot):

Composition:
- Document the cluster-wide log-collection scope as an accepted trade-off — tailing every namespace's pod stdout is the point (it closes the platform log-collector gap) and the sink is the private in-cluster Loki; per-namespace excludes / a redaction processor are noted as future work.
- Bump the collector memory (request 128Mi→256Mi, limit 256Mi→512Mi) for headroom on the filelog buffers + k8sattributes metadata cache under real log volume; 256Mi risked silent OOM log-shipping gaps.

otel-collector-running test:
- Retry the readiness check (18×10s) so a fresh rollout mid-image-pull doesn't spuriously fail.
- Assert the label selects exactly one DaemonSet (word-split + count) so a multi-match can't feed a bad name into the per-name `kubectl get` calls.

otel-loki-logs test:
- Add an explicit `timeout: 300` to the kuttl command and shrink the over-provisioned retry loop (10 attempts) so kuttl no longer kills the async-ingestion poll mid-loop.
- Use a per-run unique namespace + marker (`$(date +%s)` suffix) so a rerun can't collide with a still-Terminating namespace and stale Loki data can't false-positive.
- Resolve the Loki service with `{.items[*]...}` + first token (the `[0]` index aborts under `set -e` before the friendly error) and add `curl --max-time` so a hung request can't stall the poll silently.
- Single-quote the marker in the container command to avoid word-splitting.

Both tests re-verified green against the live collector (otel-collector-running + otel-loki-logs pass; marker ingested and queryable in Loki on attempt 1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Cervator
Cervator merged commit cdc84e8 into main Jul 3, 2026
1 check passed
@Cervator
Cervator deleted the feat/otel-collector-log-shipper branch July 8, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants