Skip to content

fix(monitoring): surface missing VictoriaLogs and gate dead-end vlinsert ExternalName - #3215

Merged
scooby87 merged 1 commit into
mainfrom
worktree-fix+3181-monitoring-vlinsert-silent-loss
Jul 8, 2026
Merged

scooby87 merged 1 commit into
mainfrom
worktree-fix+3181-monitoring-vlinsert-silent-loss

Conversation

@scooby87

@scooby87 scooby87 commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Addresses #3181, where platform log ingestion could fail silently: fluent-bit shipped to vlinsert-generic while the monitoring HelmRelease reported Ready, and a vlinsert-generic ExternalName in cozy-monitoring dead-ended at a tenant-root VictoriaLogs that did not exist when the root tenant had monitoring disabled. Logs were dropped with no failing signal.

Three complementary layers make the failure loud instead of silent:

  1. Readiness gate — packages/extra/monitoring/templates/helmrelease.yaml now sets a poller waitStrategy and a healthCheckExprs entry on the VLCluster status.updateStatus. A missing/unhealthy log store makes the monitoring-system release not-ready instead of flipping Ready the moment the CR is applied. Mirrors the existing seaweedfs CNPG pattern.
  2. No empty-log-store hole — packages/system/monitoring/templates/vlogs/vlogs.yaml fails the render on an empty logsStorages. With no entry no VLCluster exists, the readiness gate has nothing to match, and fluent-bit would silently drop logs — so this is now a loud misconfiguration.
  3. No silent fallback — the cozy-monitoring → tenant-root vlinsert/vminsert ExternalName redirects in cozystack-basics are created only when the root tenant hosts monitoring. The template reads the live Tenant/root spec.monitoring via lookup (source of truth, so it cannot drift from the tenant's real state) and falls back to a new platform value monitoring.rootEnabled (via _cluster.monitoring-enabled) when lookup is empty (dry-run / first render). The default preserves the historical always-create behaviour.

Covered by helm-unittest across all four affected charts (141 tests): readiness-gate fields, empty-vs-populated logsStorages contract, ExternalName present/absent fallback contract, and the platform wiring into the cozystack-values Secret. Also adds the missing test: target to packages/extra/monitoring/Makefile so its suite runs in CI.

Known limitation / follow-up: the legacy platform monitoring release installed directly into cozy-monitoring via the cozystack.monitoring PackageSource cannot carry the readiness gate, because the engine's ComponentInstall/HelmRelease builder and the vendored helm-controller API v1.4.3 lack waitStrategy/healthCheckExprs. That source is not referenced by any active bundle; full coverage there needs a helm-controller bump plus engine support.

Release note

fix(monitoring): surface missing platform VictoriaLogs as not-ready and stop the dangling vlinsert-generic ExternalName from silently dropping logs (#3181)

Summary by CodeRabbit

  • New Features
    • Added a platform setting (monitoring.rootEnabled) to control whether root-level monitoring is enabled.
    • Monitoring redirect ExternalName services are now rendered only when root monitoring is enabled (defaulting to enabled).
  • Bug Fixes
    • Ensures an explicitly set monitoring.rootEnabled=false is preserved when generating platform configuration.
    • Prevents incomplete monitoring deployments by failing rendering when logsStorages is empty.
    • Improves VictoriaMetrics readiness by delaying “ready” until the operator reports an operational status.
  • Tests
    • Added/expanded Helm-unittest suites covering monitoring wiring, redirects, readiness gating, and logsStorages requirements.
    • Added a make test target to run chart unittests.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses silent log ingestion failures by implementing stricter readiness checks and conditional resource creation. By gating the monitoring-system HelmRelease on the actual health of the VictoriaLogs cluster and ensuring that log-forwarding services are only created when a valid destination exists, the platform now provides clear feedback on misconfigurations instead of silently dropping data.

Highlights

  • Readiness Gate Implementation: Added a 'poller' waitStrategy and healthCheckExprs to the monitoring-system HelmRelease to ensure the release only reports 'Ready' when the VictoriaLogs cluster is actually operational.
  • Configuration Validation: Updated the monitoring chart to explicitly fail rendering if 'logsStorages' is empty, preventing silent log loss scenarios.
  • Conditional ExternalName Services: Modified 'cozystack-basics' to conditionally create 'ExternalName' services only when the root tenant has monitoring enabled, preventing dead-end redirects.
  • Testing and Verification: Added extensive helm-unittest suites across four charts and enabled the 'test' target in the monitoring Makefile to ensure ongoing reliability.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files area/monitoring Issues or PRs related to the monitoring stack (vlogs, vmstack, grafana, workloadmonitor) kind/bug Categorizes issue or PR as related to a bug labels Jul 6, 2026

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request addresses issue #3181 by ensuring that the cozy-monitoring ExternalName redirects are only created when the root tenant actually hosts the platform monitoring stack. It also adds a readiness gate to the monitoring HelmRelease based on the VLCluster status and fails rendering if logsStorages is empty. The feedback highlights two potential nil pointer dereferences in the Helm templates: one when .Values.monitoring is omitted or null in apps.yaml, and another when .Values._cluster is undefined in monitoring-external-services.yaml.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

and would flip an explicit rootEnabled=false back to true. The
chart values.yaml supplies the true default, so quote the value
as-is. */}}
monitoring-enabled: {{ .Values.monitoring.rootEnabled | quote }}

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.

high

If .Values.monitoring is omitted or overridden to null in custom values, accessing .Values.monitoring.rootEnabled directly will cause a template rendering panic (nil pointer dereference). Guard against this by checking if .Values.monitoring is defined before accessing its fields.

      monitoring-enabled: {{ if .Values.monitoring }}{{ .Values.monitoring.rootEnabled | quote }}{{ else }}true{{ end }}
References
  1. Review carefully for logic errors, off-by-one bugs, and nil dereferences. (link)

{{- if $rootTenant -}}
{{- $enabled = toString (dig "spec" "monitoring" false $rootTenant) -}}
{{- else -}}
{{- $enabled = toString (index .Values._cluster "monitoring-enabled" | default "true") -}}

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.

high

If .Values._cluster is nil or not defined (which can happen if the cozystack-values Secret is missing or during certain test/dry-run scenarios), calling index on it will cause a template rendering panic (index of untyped nil). Guard against this by defaulting .Values._cluster to an empty dictionary (dict) before indexing.

{{-   $enabled = toString (index (.Values._cluster | default dict) "monitoring-enabled" | default "true") -}}
References
  1. Review carefully for logic errors, off-by-one bugs, and nil dereferences. (link)

@coderabbitai

coderabbitai Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0f93dab9-34a6-4ec1-a56e-10a95a84d8bc

📥 Commits

Reviewing files that changed from the base of the PR and between a3d688c and f9afc73.

📒 Files selected for processing (10)
  • packages/core/platform/templates/apps.yaml
  • packages/core/platform/tests/apps_monitoring_enabled_wiring_test.yaml
  • packages/core/platform/values.yaml
  • packages/extra/monitoring/Makefile
  • packages/extra/monitoring/templates/helmrelease.yaml
  • packages/extra/monitoring/tests/helmrelease_test.yaml
  • packages/system/cozystack-basics/templates/monitoring-external-services.yaml
  • packages/system/cozystack-basics/tests/monitoring-external-services_test.yaml
  • packages/system/monitoring/templates/vlogs/vlogs.yaml
  • packages/system/monitoring/tests/vlogs_logsstorages_required_test.yaml
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/extra/monitoring/templates/helmrelease.yaml
  • packages/core/platform/tests/apps_monitoring_enabled_wiring_test.yaml
  • packages/system/monitoring/tests/vlogs_logsstorages_required_test.yaml
  • packages/system/cozystack-basics/tests/monitoring-external-services_test.yaml
  • packages/core/platform/templates/apps.yaml
  • packages/extra/monitoring/tests/helmrelease_test.yaml
  • packages/core/platform/values.yaml
  • packages/extra/monitoring/Makefile

📝 Walkthrough

Walkthrough

Adds a root-monitoring toggle into platform values and Secret wiring, gates redirect Services on that flag, adds HelmRelease readiness checks for VLCluster status, and fails rendering when logsStorages is empty. Tests cover the wiring, gating, readiness, and failure cases.

Changes

Monitoring root-enabled propagation and readiness

Layer / File(s) Summary
Add monitoring.rootEnabled configuration
packages/core/platform/values.yaml, packages/core/platform/templates/apps.yaml, packages/core/platform/tests/apps_monitoring_enabled_wiring_test.yaml
Adds monitoring.rootEnabled, writes it into monitoring-enabled in the platform Secret, and tests default, explicit false, and null monitoring cases.
Gate external redirect services
packages/system/cozystack-basics/templates/monitoring-external-services.yaml, packages/system/cozystack-basics/tests/monitoring-external-services_test.yaml
Uses _cluster.monitoring-enabled with a true default and renders ExternalName redirects only when enabled, with tests for enabled, disabled, missing, and null cases.
VLCluster readiness gating in HelmRelease
packages/extra/monitoring/templates/helmrelease.yaml, packages/extra/monitoring/Makefile, packages/extra/monitoring/tests/helmrelease_test.yaml
Adds poller wait strategy and VLCluster health expressions based on status.updateStatus, plus a unit-test target and rendered HelmRelease checks.
Require non-empty logsStorages
packages/system/monitoring/templates/vlogs/vlogs.yaml, packages/system/monitoring/tests/vlogs_logsstorages_required_test.yaml
Stops rendering when logsStorages is empty and tests the default, multi-entry, and empty-list behaviors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Suggested reviewers: kvaps, lllamnyp, IvanHunters, lexfrei

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main monitoring fix: surfacing missing VictoriaLogs and gating the dead-end vlinsert ExternalName.
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 worktree-fix+3181-monitoring-vlinsert-silent-loss

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@scooby87
scooby87 force-pushed the worktree-fix+3181-monitoring-vlinsert-silent-loss branch 2 times, most recently from ea2cf15 to d301a57 Compare July 6, 2026 14:45
@IvanHunters

Copy link
Copy Markdown
Collaborator

Verdict

LGTM with non-blocking notes

The three-layer fix for the silent-log-loss path (#3181) is coherent, idiomatic (mirrors the seaweedfs CNPG readiness-gate precedent), and verified safe on both upgrade and fresh install; the only issue is one inaccurate code comment.

Findings

[MINOR] packages/extra/monitoring/templates/helmrelease.yaml:28 — code comment states a falsehood about the VLCluster status shape

The comment reads "VLCluster has no status.conditions; it exposes status.updateStatus". The vendored CRD (packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/crd.yaml, VLCluster v1) actually defines both status.conditions (a standard []metav1.Condition-shaped array) and status.updateStatus. The chosen health signal (updateStatus) is still the correct one to key on — it is the operator's documented aggregate rollout status (StatusMetadata.UpdateStatus, enum expanding|operational|failed|paused, verified in VictoriaMetrics/operator v0.68.4 api/operator/v1beta1/vmextra_types.go:27-34, matching the vendored appVersion) — but the justifying comment is wrong and will mislead a future maintainer who might otherwise consider the more conventional conditions[type=Ready] shape. Recommend rewording to "VLCluster's status.conditions carry no stable Ready-type contract for this operator version; key on status.updateStatus instead" or similar. Not blocking.

Caveats

  • Phase 5b — existing-customer upgrade: no breakage, verified. (1) monitoring.rootEnabled: true default (packages/core/platform/values.yaml:376) preserves the historical always-create behaviour for the cozy-monitoring → tenant-root ExternalName redirects; the cozystack-basics template (monitoring-external-services.yaml:31-33) additionally falls back to _cluster.monitoring-enabled | default "true", so cozystack-values Secrets written before this key existed keep the redirects. (2) The new empty-logsStorages fail guard (packages/system/monitoring/templates/vlogs/vlogs.yaml:8-9) cannot break existing installs: the default logsStorages is non-empty in both packages/system/monitoring/values.yaml:43 and packages/extra/monitoring/values.yaml:89 (a generic entry); it only fires on an explicit logsStorages: [] override, which is the intended loud misconfiguration. (3) No migration script is needed — no state migration, no resource rename/split, no CRD schema change, no RBAC contraction. Confirmed envelope.repo_invariants and cozy_invariants are both empty.
  • Phase 5b — fresh install: no breakage, verified. The readiness gate keys on waitStrategy: poller + healthCheckExprs, which the running Flux (packages/system/fluxcd/values.yaml:8 → distribution 2.8.x, well past the helm-controller v1.1.0 / Flux 2.4 introduction) supports; the sibling packages/extra/seaweedfs/templates/seaweedfs-db.yaml:24-35 already ships the identical shape. Cold-start dep is satisfied: cozystack.monitoring-application PackageSource already lists cozystack.victoria-metrics-operator in dependsOn. During the brief install window before the operator first writes updateStatus, all three CEL expressions evaluate false, and per Flux docs the resource is then treated as waiting/in-progress until timeout: 10m (the intended not-ready-then-timeout behaviour, not a false-fail or false-ready). All four touched charts have runnable test: Makefile targets (so hack/helm-unit-tests.sh runs every new suite in CI), and all suites pass locally (81 + 3 + 50 + 7 tests).
  • No generated-artifact obligation. packages/core/platform has no values.schema.json, no README, and no cozyvalues-gen step in its Makefile, so the monitoring.rootEnabled addition owes no make generate output. packages/extra/monitoring's values.yaml/Chart.yaml are untouched (only the Makefile test: target was added), so no schema regen is owed there either.
  • PR-body "legacy source" limitation claim is accurate. The cozystack.monitoring PackageSource (which installs system/monitoring directly into cozy-monitoring) is confirmed absent from every bundle template — only cozystack.monitoring-application and cozystack.monitoring-agents are referenced in packages/core/platform/templates/bundles/system.yaml:195,208. The new fail guard in vlogs.yaml does apply to that legacy release too, but it is harmless there for the same reason as above (non-empty default logsStorages).
  • sprig default-on-bool trap correctly avoided. The apps.yaml:135 line intentionally omits | default on monitoring-enabled, because sprig default treats boolean false as empty and would flip an explicit rootEnabled: false back to true; the values.yaml default supplies true instead. Verified correct.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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
`@packages/system/cozystack-basics/templates/monitoring-external-services.yaml`:
- Line 31: The monitoring template logic currently uses default on
_cluster.monitoring-enabled, which converts an explicit false into "true" and
enables fallback Services unintentionally. Update the value resolution in
monitoring-external-services.yaml to explicitly check whether the key exists in
.Values._cluster before falling back, and only use "true" when the key is
missing; keep the existing $enabled handling and the surrounding template flow
intact.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e0052780-0250-4a34-9400-570a18e6d319

📥 Commits

Reviewing files that changed from the base of the PR and between d301a57 and 5a7fc41.

📒 Files selected for processing (10)
  • packages/core/platform/templates/apps.yaml
  • packages/core/platform/tests/apps_monitoring_enabled_wiring_test.yaml
  • packages/core/platform/values.yaml
  • packages/extra/monitoring/Makefile
  • packages/extra/monitoring/templates/helmrelease.yaml
  • packages/extra/monitoring/tests/helmrelease_test.yaml
  • packages/system/cozystack-basics/templates/monitoring-external-services.yaml
  • packages/system/cozystack-basics/tests/monitoring-external-services_test.yaml
  • packages/system/monitoring/templates/vlogs/vlogs.yaml
  • packages/system/monitoring/tests/vlogs_logsstorages_required_test.yaml
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/extra/monitoring/tests/helmrelease_test.yaml
  • packages/system/monitoring/tests/vlogs_logsstorages_required_test.yaml
  • packages/core/platform/tests/apps_monitoring_enabled_wiring_test.yaml
  • packages/extra/monitoring/Makefile
  • packages/extra/monitoring/templates/helmrelease.yaml
  • packages/core/platform/values.yaml
  • packages/system/cozystack-basics/tests/monitoring-external-services_test.yaml
  • packages/core/platform/templates/apps.yaml

Comment thread packages/system/cozystack-basics/templates/monitoring-external-services.yaml Outdated

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict

NOT LGTM

The empty-logsStorages fail and the readiness gate are sound, but the third layer (lookup-gated ExternalName render on an engine-generated HelmRelease) rests on an incorrect assumption about helm-controller re-render behaviour and creates a stale-state hole in both directions.

Findings

[MAJOR] packages/system/cozystack-basics/templates/monitoring-external-services.yaml:26-33 — lookup-gated render will go stale; the "cannot drift" claim is false

The template gates the three vlinsert/vminsert ExternalName Services on lookup of Tenant/root spec.monitoring, and the comment (line 15-16) plus the commit body assert that "the ExternalNames and the tenant's actual monitoring can never drift." That is not how helm-controller works. helm-controller re-renders a chart only on install/upgrade/rollback (chart-source change or HR values change) — never on a reconcile tick, and never when a lookup result changes. cozystack-basics is an operator-generated HelmRelease; your own in-tree note at packages/core/platform/sources/cozystack-basics.yaml:32-36 spells this out ("Drift detection is off on operator-generated HelmReleases, so a capability gate in the templates would render the policy out at first install and never add it back").

Tenant/root spec.monitoring is a runtime bool on the Tenant CR (packages/apps/tenant/values.yaml:12), toggled out-of-band and independent of the platform monitoring.rootEnabled value. So the two divergence scenarios are:

  • Root monitoring ON → later turned OFF via Tenant/root spec.monitoring: no re-render is triggered, the three ExternalNames stay, and the exact dead-end redirect / silent log-drop this PR set out to eliminate persists until an unrelated chart bump forces a re-render.
  • Root monitoring OFF at install → later turned ON via Tenant/root spec.monitoring: the redirects were never created and are never added back, so ingestion into a now-valid VictoriaLogs never routes — the fix silently fails to activate.

The only reliable re-render trigger is a change to the HR's valuesFrom cozystack-values Secret, i.e. flipping monitoring.rootEnabled in platform values (that mutates the Secret, which helm-controller watches). Toggling the Tenant CR alone does nothing. Either (a) make monitoring.rootEnabled the single source of truth and drop the lookup branch entirely (the lookup adds the drift risk without a reliable refresh path), or (b) keep the lookup but document, in the template comment and PR body, that the redirects only refresh when monitoring.rootEnabled is changed, and ship an operator recipe for forcing the refresh after a Tenant/root spec.monitoring toggle. As written, the code comment and commit message make a correctness claim that is false and will mislead the next maintainer.

Claim mismatches

[PARTIAL] "reads the live Tenant/root spec.monitoring via lookup (source of truth, so it cannot drift from the tenant's real state)" — the lookup is only re-evaluated on a Helm action, not on Tenant CR change; on an engine-generated HR with drift detection off, the rendered ExternalNames drift from Tenant/root spec.monitoring in both directions (see Findings). The claim is true only at the instant of an install/upgrade/values-change, not continuously.

[PARTIAL] "Covered by helm-unittest across all four affected charts (141 tests)" — the four suites run and pass (verified: 51 + 7 + 3 + 82 = 143 tests total, including pre-existing), but the new tests exercise only the fallback branch; the comment at monitoring-external-services_test.yaml:19-21 correctly notes the lookup branch is unit-untested. The drift behaviour in Findings is therefore not covered by any test. Not blocking on its own, but it means the failure mode ships unverified.

Operational risks

  • Stale-lookup on Tenant/root spec.monitoring toggle (monitoring-external-services.yaml:26-33): an operator who disables/enables root monitoring the documented way (editing the Tenant CR, the PR's stated "source of truth") gets no redirect update and no failing signal — a silent regression of the very behaviour #3181 tracks. At 3am this surfaces as "logs stopped / never started" with a Ready monitoring release and correct-looking Tenant spec, with nothing in Flux events pointing at the stale ExternalName set.

Caveats

Phase 5b — existing-customer upgrade:

  • packages/system/monitoring/templates/vlogs/vlogs.yaml:8-10 — the new fail on empty logsStorages is a deliberate behaviour change. logsStorages is user-facing (exposed in packages/system/monitoring-rd/cozyrds/monitoring.yaml openAPISchema, default one generic entry, no minItems). A customer running today with logsStorages: [] (currently a silent-drop config) will, on upgrade, have the monitoring-system HR fail to render and loop on retries: -1. That is the intended "loud" outcome, but it is a hard upgrade break for that config — acceptable given the rationale, noted so it is not a surprise.
  • packages/core/platform/values.yaml:80-92 — new monitoring.rootEnabled: true default preserves the historical always-create behaviour for the fallback branch, so existing clusters whose cozystack-values Secret predates the key are unaffected (dig ... default true and index ... | default "true" both resolve to the create-redirects path). Verified via the three wiring tests and by reading apps.yaml:20.
  • No migration script needed: no resource rename/move/adoption, no CRD change, no RBAC contraction. packages/core/platform has no values.schema.json/README.md, so no make generate artifact is owed for the values.yaml addition (verified: files absent).

Phase 5b — fresh install:

  • waitStrategy/healthCheckExprs on packages/extra/monitoring/templates/helmrelease.yaml:26-48 are supported: the identical shape is already in production at packages/extra/seaweedfs/templates/seaweedfs-db.yaml:24-35 (same helm-controller). The <release>-system HR targets chartRef: ExternalArtifact (a real Flux HR), so it can carry these fields — distinct from the legacy cozystack.monitoring source. The PR's "Known limitation" is accurate: cozystack.monitoring is referenced by no active bundle (only cozystack.monitoring-application and cozystack.monitoring-agents appear in packages/core/platform/templates/bundles/system.yaml:195,208).
  • CEL initial-empty-status is safe: on a freshly-applied VLCluster with no status.updateStatus, all of current/inProgress/failed are false, so helm-controller holds the release not-Ready and polls until timeout: 10m — the intended gate behaviour, not a spurious failure.

Recommended follow-ups

  • Add minItems: 1 to the logsStorages schema in packages/system/monitoring values.schema.json / cozyvalues-gen source so the dashboard/API rejects an empty list up front, instead of the operator discovering it only as a stuck HR at render time. This complements the fail guard rather than replacing it.

@scooby87
scooby87 force-pushed the worktree-fix+3181-monitoring-vlinsert-silent-loss branch from 5a7fc41 to a3d688c Compare July 7, 2026 14:50

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict

LGTM with non-blocking notes

The prior drift hole is resolved — the vlinsert/vminsert ExternalName gate no longer uses lookup; it now reads _cluster.monitoring-enabled (fed from the new monitoring.rootEnabled platform value), which lands in the cozystack-values Secret that cozystack-basics consumes via valuesFrom, so flipping it re-renders the chart. Remaining items are a stale PR-body description and two edge-case UX notes, none blocking.

Findings

[MINOR] packages/system/monitoring/templates/vlogs/vlogs.yaml:8-10 — empty-logsStorages hard-fail is reachable by user input with no schema guard

logsStorages is a user-settable array in the monitoring ApplicationDefinition openAPISchema (packages/system/monitoring-rd/cozyrds/monitoring.yaml:11) and carries no minItems constraint, so the dashboard/API accepts an explicit logsStorages: []. With this PR that configuration now hard-fails the HelmRelease render (helm fail) instead of rendering nothing. For an existing monitoring app CR that was deliberately set to an empty list, an upgrade flips it from "silently renders no VLCluster" to "HR stuck in render failure". The loud-fail is the intended behaviour and the default ([{generic...}]) is non-empty, so this is not a regression for standard installs — but the failure would be more legible if minItems: 1 were added to the logsStorages schema in the RD so the API rejects the empty list up front with a validation message, rather than surfacing as an HR reconcile failure. Consider adding minItems: 1 (regenerated via make generate) as a follow-up.

Claim mismatches

[PARTIAL] "The template reads the live Tenant/root spec.monitoring via lookup (source of truth, so it cannot drift from the tenant's real state)" (PR body, layer 3) — the shipped code does NOT use lookup. packages/system/cozystack-basics/templates/monitoring-external-services.yaml:26 gates on index (.Values._cluster | default dict) "monitoring-enabled", and the in-file comment (lines 13-20) explicitly explains why lookup was rejected (operator-generated HR, drift detection off, re-renders only on install/upgrade/values-change). The PR body still describes the earlier lookup-based revision and repeats the "cannot drift" framing, which is now inaccurate — the value can go stale if an operator changes Tenant/root spec.monitoring out-of-band without updating monitoring.rootEnabled. The CodeRabbit summary and the git commit body correctly describe the value-driven approach; only the top "What this PR does" section is stale. Update the PR body layer-3 text to match the shipped mechanism before merge so reviewers and operators do not carry a false mental model.

Caveats

  • Upgrade impact (existing customer): verified no migration is needed and no breakage for the default path. Clusters on N-1 carry a cozystack-values Secret without the monitoring-enabled key; the template's | default "true" (line 26) keeps all three ExternalName redirects, and once packages/core/platform re-renders the Secret the key is emitted as "true" by default (apps.yaml:140, dig "rootEnabled" true verified: default→"true", explicit false→"false", null→"true", no panic). So existing behaviour is preserved unless an operator explicitly sets rootEnabled: false. repo_invariants and cozy_invariants both empty; no numbered migration required.
  • Fresh install: verified safe. system/monitoring default logsStorages is non-empty (packages/system/monitoring/values.yaml:43, generic), so the new render-fail does not fire on cold install. The waitStrategy: poller + healthCheckExprs shape is an established precedent (packages/extra/seaweedfs/templates/seaweedfs-db.yaml:24-35, identical structure), confirming the in-use helm-controller supports these HelmRelease v2 fields. packages/core/platform has no values.schema.json/cozyvalues-gen target, so the monitoring.rootEnabled addition needs no make generate artifacts.
  • CI wiring: verified the added test: target in packages/extra/monitoring/Makefile does wire the suite into CI — hack/helm-unit-tests.sh iterates chart dirs and runs make -C <dir> test only where the target exists (make -C "$dir" -n test guard), so a chart with no test: target was silently skipped before. All four charts' suites pass locally (143 tests: 51 + 7 + 3 + 82).
  • The functional correctness of the CEL healthCheckExprs (operational/expanding/catch-all-failed mapping on status.updateStatus) is not exercised by helm-unittest (the tests only pin the string values); it can only be validated live against victoria-metrics-operator. The mapping is defensible on paper (unknown status can never read as Ready), but end-to-end confirmation on a dev cluster is recommended before relying on it as the log-store backstop.
  • The residual out-of-band drift (operator toggles Tenant/root spec.monitoring without updating monitoring.rootEnabled) is an accepted, documented tradeoff — surfaced in both the template comment (lines 13-20) and values.yaml (packages/core/platform/values.yaml:88-95, "operators MUST set it to match their actual Tenant/root spec.monitoring"). This is the correct honest framing; the only gap is the stale PR-body text noted under Claim mismatches.

…ert ExternalName (#3181)

Platform log ingestion could fail silently: fluent-bit shipped to
vlinsert-generic while the monitoring HelmRelease reported Ready, and a
vlinsert-generic ExternalName in cozy-monitoring dead-ended at a
tenant-root VictoriaLogs that did not exist when the root tenant had
monitoring disabled. Logs were dropped with no failing signal.

Three complementary changes:

- Readiness (do not stay silently Ready): the monitoring-system
  HelmRelease now uses a `poller` waitStrategy and a healthCheckExpr on
  its VLCluster (status.updateStatus), so a missing/unhealthy log store
  makes the release not-ready instead of flipping Ready as soon as the
  CR is applied. Mirrors the seaweedfs CNPG pattern.

- No empty-log-store hole: system/monitoring rejects an empty
  logsStorages at render time (helm fail). With no entry no VLCluster is
  created, the readiness gate has nothing to match, and fluent-bit would
  silently drop logs — so this is made a loud misconfiguration rather
  than a documented footgun.

- No silent fallback: the cozy-monitoring -> tenant-root
  vlinsert/vminsert ExternalName redirects in cozystack-basics are now
  created only when the root tenant hosts monitoring, gated on
  _cluster.monitoring-enabled — fed from a new platform value
  monitoring.rootEnabled (default true, preserving the historical
  always-create behaviour). monitoring.rootEnabled is the single
  render-time source of truth: changing it re-renders cozystack-basics
  (helm-controller watches the cozystack-values Secret it flows into). A
  live `lookup` of Tenant/root spec.monitoring is deliberately NOT used:
  cozystack-basics is an operator-generated HelmRelease with drift
  detection off, so it re-renders only on install/upgrade/values-change
  — never on a reconcile tick or when a lookup result changes — and a
  lookup would go stale in both directions. Operators must keep
  monitoring.rootEnabled in sync with Tenant/root spec.monitoring.

Covered by helm-unittest across all four charts: readiness gate fields,
empty-vs-populated logsStorages contract, ExternalName present/absent
and null-guard contract, and the platform wiring into the
cozystack-values Secret. The CEL health predicates are exercised live,
not in helm-unittest (which cannot evaluate CEL). Adds the missing
`test:` target to packages/extra/monitoring/Makefile so its suite runs
in CI.

Known limitation (follow-up): the legacy cozystack.monitoring
PackageSource that installs system/monitoring directly into
cozy-monitoring cannot carry the readiness gate, because the engine's
ComponentInstall/HelmRelease builder and the vendored helm-controller
API v1.4.3 lack waitStrategy/healthCheckExprs. That source is not
referenced by any active bundle; full coverage needs a helm-controller
bump plus engine support.

Signed-off-by: Alexey Artamonov <[email protected]>
@scooby87
scooby87 force-pushed the worktree-fix+3181-monitoring-vlinsert-silent-loss branch from a3d688c to f9afc73 Compare July 8, 2026 19:33
@scooby87
scooby87 enabled auto-merge July 8, 2026 21:06
@scooby87
scooby87 merged commit f91841f into main Jul 8, 2026
16 checks passed
@scooby87
scooby87 deleted the worktree-fix+3181-monitoring-vlinsert-silent-loss branch July 8, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/monitoring Issues or PRs related to the monitoring stack (vlogs, vmstack, grafana, workloadmonitor) kind/bug Categorizes issue or PR as related to a bug size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants