Repository navigation
fix(opensearch-operator): align DNS_BASE with the platform cluster domain - #3357
Alekperov Fuad (fuad00) wants to merge 1 commit into
Conversation
Summary of ChangesHello, 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 a service resolution issue where the OpenSearch operator was failing to connect to internal services due to a mismatch between the default Highlights
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 AssistThe 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
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 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
|
📝 WalkthroughWalkthroughThe platform now propagates ChangesOpenSearch DNS configuration
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Platform
participant HelmValues
participant ControllerManager
Platform->>HelmValues: Emit global.clusterDomain
HelmValues->>ControllerManager: Render DNS_BASE
ControllerManager-->>ControllerManager: Fall back to cluster.local when unset
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request configures the dnsBase parameter to cozy.local in the opensearch-operator values and adds a corresponding test suite to ensure DNS_BASE matches the platform's cluster domain instead of the upstream default cluster.local. The reviewer noted that the PR description is missing the required release-note block as specified in the repository guidelines.
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.
| # The operator builds in-cluster service FQDNs from DNS_BASE — the | ||
| # securityadmin bootstrap job (`-h <svc>.<ns>.svc.<DNS_BASE>`) and the | ||
| # OpenSearch Dashboards OPENSEARCH_HOSTS. The upstream chart defaults | ||
| # DNS_BASE to cluster.local, but Cozystack's cluster domain is | ||
| # cozy.local, so those names fail to resolve: the securityconfig job | ||
| # can never reach the cluster (no `.opendistro_security` index → nodes | ||
| # stay NotReady) and Dashboards crash-loops on ENOTFOUND. Align | ||
| # DNS_BASE with the platform's default cluster domain, matching what | ||
| # the opensearch app chart already uses everywhere | ||
| # (`.Values._cluster.cluster-domain | default "cozy.local"`). | ||
| dnsBase: cozy.local |
There was a problem hiding this comment.
The PR body is missing the required release-note block. According to the repository guidelines, every PR must contain a release note block in the following format:
```release-note
type(scope): human-readable changelog entry
Please update the PR description to include this block.
<details>
<summary>References</summary>
1. PR body must contain a release note block: ```release-note ... ```. Flag any PR whose body has no release-note block. <sup>([link](https://github.com/cozystack/cozystack/blob/main/.gemini/styleguide.md))</sup>
</details>
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
The bug you are fixing is real, and I want to start by confirming that properly, because the obvious objection is "if DNS_BASE were wrong, CI would be red".
CI is not red because there is no opensearch end-to-end suite at all. The chainsaw directory has 21 suites and opensearch is not among them, and the string does not appear anywhere under the CI configuration. The operator is deployed, but DNS_BASE has only two consumers and both are per-cluster: the -h argument of the securityconfig-update Job, and OPENSEARCH_HOSTS for Dashboards. No OpenSearchCluster is ever created in CI, so neither is reached. The wait loop in the securityconfig job also has no attempt limit, which is exactly the symptom you describe. So the fix is warranted and the absence of a red check proves nothing.
The override is also in the right place: it lands in the parent values.yaml, leaves charts/ alone, and survives make update.
What blocks it is the other direction of the same bug. Replacing cluster.local with a hardcoded cozy.local fixes the default install and breaks a non-default one. networking.clusterDomain is configurable, and a Cozystack layered on an existing kubeadm, k3s or RKE2 cluster keeps cluster.local, where OpenSearch works today precisely because the upstream default happens to match. After this change those installs get the identical failure, mirrored. The new test then pins that behaviour, which is the part I would most like to avoid: a test asserting the hardcoded value makes the regression look intentional to whoever reads it next.
To be fair to you, and this is why I am not simply saying no: four sibling packages already hardcode the same domain in the same way, and the same defect is already tracked in #2348 for the mariadb operator. So this PR is not out of line with the codebase, it is consistent with a pattern that is itself the problem. I also confirmed the constraint you were up against: _cluster is not visible to subcharts and values.yaml is not templated, so there is no values-only fix here. That rules out the easy answer rather than the goal.
The direction that actually resolves it is to emit global.clusterDomain from the platform templates, since Helm propagates global into subcharts, and extend the existing patch so the vendored template falls back to it. A test case with a non-cozy.local domain would then pin the property you want, which is that the operator follows the platform, rather than pinning a specific string.
If a maintainer would rather take this as-is and fold it into #2348 with the four siblings, that is a defensible call and I would not stand in the way. My preference is to fix it once here, because you have already done the investigation.
Two mechanical things regardless of which way that goes. The commit has no Signed-off-by, which blocks merge, and the PR is missing the release-note block the template asks for. Also worth knowing: none of the workflows have actually run on this, they are all sitting in action_required because it is from a fork, so the green you and I both saw is absence of results rather than success. I ran the chart's unit tests locally and they pass, and the template does render the intended value.
…domain The operator builds in-cluster service FQDNs from its DNS_BASE env var: the securityadmin bootstrap job connects to `<svc>.<ns>.svc.<DNS_BASE>` and the OpenSearch Dashboards Deployment points OPENSEARCH_HOSTS at the same suffix. The upstream chart defaults DNS_BASE to cluster.local, and the vendored subchart has no way to learn the real domain: `_cluster` is a plain value that Helm does not propagate into subcharts, and the wrapper's values.yaml is not templated. On a cozy.local cluster the securityconfig job then never reaches the cluster (the `.opendistro_security` index is never created, so OpenSearch nodes stay NotReady) and Dashboards crash-loop on ENOTFOUND. Emit the configurable cluster domain as `global.clusterDomain` from the platform values Secret -- Helm propagates `global` into every package's subcharts -- and extend the vendored operator patch to read DNS_BASE from it, falling back to the upstream `manager.dnsBase` default. This follows networking.clusterDomain instead of hardcoding a value, so a cluster that keeps cluster.local (e.g. Cozystack layered on an existing kubeadm/k3s/RKE2) keeps working. The unit test pins that the operator tracks the platform domain rather than any specific string. Signed-off-by: fuad00 <[email protected]>
467665f to
d37e295
Compare
|
Thank you — this is one of the most useful reviews I've had, and you're right on every point. I went with fixing it properly here rather than folding into #2348. What changed:
Verified locally, since the fork workflows are stuck in
Both mechanical items are handled too: the commit is now One nice side effect: now that the platform exposes |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/system/opensearch-operator/tests/dns_base_test.yaml (1)
21-24: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winCover the platform-to-operator propagation boundary.
This test sets
global.clusterDomaindirectly, so it cannot catch a regression that stopsnetworking.clusterDomainfrom being emitted asglobal.clusterDomain. Add a platform-template or integration assertion for that mapping.The PR objective spans both propagation and consumption, so both boundaries should be covered.
🤖 Prompt for 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. In `@packages/system/opensearch-operator/tests/dns_base_test.yaml` around lines 21 - 24, Add a platform-template or integration assertion that verifies networking.clusterDomain is emitted as global.clusterDomain before the DNS operator consumes it. Keep the existing dns_base_test.yaml consumption coverage, and ensure tests cover both the platform-to-operator propagation and operator behavior boundaries.
🤖 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/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml`:
- Line 79: Revert the direct template change in the generated deployment
manifest and restore the vendored chart file to its upstream verbatim content.
Apply or preserve the DNS_BASE customization through
packages/system/opensearch-operator/patches/leaderElection.diff instead, keeping
the generated chart unmodified so make update does not overwrite the fix.
In `@packages/system/opensearch-operator/patches/leaderElection.diff`:
- Around line 34-35: Quote the rendered DNS_BASE value in the Helm template by
applying the quote function to the existing global.clusterDomain fallback
expression, preserving .Values.manager.dnsBase as its default. Ensure the
resulting EnvVar.value is always emitted as a string.
---
Nitpick comments:
In `@packages/system/opensearch-operator/tests/dns_base_test.yaml`:
- Around line 21-24: Add a platform-template or integration assertion that
verifies networking.clusterDomain is emitted as global.clusterDomain before the
DNS operator consumes it. Keep the existing dns_base_test.yaml consumption
coverage, and ensure tests cover both the platform-to-operator propagation and
operator behavior boundaries.
🪄 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 Plus
Run ID: 6cd4836b-2ce1-421b-986e-75b20761afd5
📒 Files selected for processing (4)
packages/core/platform/templates/apps.yamlpackages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yamlpackages/system/opensearch-operator/patches/leaderElection.diffpackages/system/opensearch-operator/tests/dns_base_test.yaml
| env: | ||
| - name: DNS_BASE | ||
| value: {{ .Values.manager.dnsBase }} | ||
| value: {{ .Values.global.clusterDomain | default .Values.manager.dnsBase }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep the vendored chart template verbatim.
This file is regenerated from upstream; editing it directly means make update can overwrite the DNS_BASE fix. Revert this generated-file change and keep the customization in packages/system/opensearch-operator/patches/leaderElection.diff.
As per coding guidelines, files under packages/**/charts/** must remain verbatim because make update regenerates them. Based on learnings, vendored Helm templates under packages/system/*/charts/**/templates/ are upstream-owned and should be changed through patches or overrides.
🤖 Prompt for 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.
In
`@packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml`
at line 79, Revert the direct template change in the generated deployment
manifest and restore the vendored chart file to its upstream verbatim content.
Apply or preserve the DNS_BASE customization through
packages/system/opensearch-operator/patches/leaderElection.diff instead, keeping
the generated chart unmodified so make update does not overwrite the fix.
Sources: Coding guidelines, Learnings
| - value: {{ .Values.manager.dnsBase }} | ||
| + value: {{ .Values.global.clusterDomain | default .Values.manager.dnsBase }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg 'packages/system/opensearch-operator/patches/leaderElection\.diff|dnsBase|clusterDomain|EnvVar|manager\(' || true
echo
echo "== patch context =="
if [ -f packages/system/opensearch-operator/patches/leaderElection.diff ]; then
nl -ba packages/system/opensearch-operator/patches/leaderElection.diff | sed -n '1,120p'
fi
echo
echo "== search occurrences =="
rg -n "value:.*DNS_BASE|DNS_BASE|clusterDomain|dnsBase" packages/system/opensearch-operator || true
echo
echo "== helm/quote behavior probe outside repo =="
python3 - <<'PY'
# Standalone YAML-like YAML parser for common string values,
# without importing/requiring any repository dependencies.
import re
def parse_yaml_like_value(s):
s = s.strip()
if not s: return ''
if (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"')):
if s.startswith("'"):
return s[1:-1].replace("''", "'")
return s[1:-1].replace('\\"', '"').replace('\\\\', '\\')
if s in ('true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'):
return True if s in ('true', 'yes', 'y', 'on') else False
try:
return int(s)
except ValueError:
try:
return float(s)
except ValueError:
return s
samples = ["true", "123", "example.com", "local.", "none", "null"]
for s in samples:
print(f"{s!r:12} -> {parse_yaml_like_value(s)!r} ({type(parse_yaml_like_value(s)).__name__})")
PYRepository: cozystack/cozystack
Length of output: 295
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg 'packages/system/opensearch-operator/patches/leaderElection\.diff|dnsBase|clusterDomain|EnvVar|manager\(' || true
echo
echo "== patch context =="
if [ -f packages/system/opensearch-operator/patches/leaderElection.diff ]; then
nl -ba packages/system/opensearch-operator/patches/leaderElection.diff | sed -n '1,120p'
fi
echo
echo "== search occurrences =="
rg -n "value:.*DNS_BASE|DNS_BASE|clusterDomain|dnsBase" packages/system/opensearch-operator || true
echo
echo "== helm/quote behavior probe outside repo =="
python3 - <<'PY'
# Standalone YAML-like YAML parser for common strict scalar values,
# without importing/requiring any repository dependencies.
def parse_yaml_like_value(s):
s = s.strip()
if not s:
return ''
if (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"')):
if s.startswith("'"):
return s[1:-1].replace("''", "'")
return s[1:-1].replace('\\"', '"').replace('\\\\', '\\')
if s in ('true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'):
return True if s in ('true', 'yes', 'y', 'on') else False
try:
return int(s)
except ValueError:
try:
return float(s)
except ValueError:
return s
samples = ["true", "123", "example.com", "local.", "none", "null"]
for s in samples:
print(f"{s!r:12} -> {parse_yaml_like_value(s)!r} ({type(parse_yaml_like_value(s)).__name__})")
PYRepository: cozystack/cozystack
Length of output: 295
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | grep -E 'packages/system/opensearch-operator/patches/leaderElection\.diff|dnsBase|clusterDomain|EnvVar|manager\(' || true
echo
echo "== patch header and lines 1-120 =="
if [ -f packages/system/opensearch-operator/patches/leaderElection.diff ]; then
awk '{printf "%6d %s\n", NR, $0}' packages/system/opensearch-operator/patches/leaderElection.diff | sed -n '1,140p'
fi
echo
echo "== search occurrences =="
grep -RInE 'value:.*DNS_BASE|DNS_BASE|clusterDomain|dnsBase|type: string|type: bool|type: integer' packages/system/opensearch-operator || true
echo
echo "== deterministic YAML scalar typing probe =="
python3 - <<'PY'
def as_yaml_scalar(s):
s = s.strip()
if not s:
return ''
if (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"')):
return s
if s in ('true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'):
return True if s in ('true', 'yes', 'y', 'on') else False
try:
return int(s)
except ValueError:
try:
return float(s)
except ValueError:
return s
samples = ["true", "false", "123", "0", "example.com", "local.", "none", "null", "false.example.com"]
for s in samples:
v = as_yaml_scalar(s)
print(f"{s!r:22} -> {v!r:12} ({type(v).__name__})")
PY
echo
echo "== deterministic Helm quote template probe =="
python3 - <<'PY'
def quote(s):
if s == '':
return '""'
import re
s = re.sub(r'(?<!\\)\\', r'\\\\', s)
s = re.sub(r'(?<!\\)"', r'\"', s)
return f'"{s}"'
samples = ["true", "123", "example.com", "local.", "false.example.com"]
for s in samples:
print(f"quote({s!r}) -> {quote(s)}")
PYRepository: cozystack/cozystack
Length of output: 50376
Quote the rendered DNS_BASE value.
Unquoted Helm output can render YAML-sensitive values like true or 123 as non-strings, but EnvVar.value must be a string in Kubernetes. Add | quote after the fallback expression.
Proposed fix
- value: {{ .Values.global.clusterDomain | default .Values.manager.dnsBase }}
+ value: {{ .Values.global.clusterDomain | default .Values.manager.dnsBase | quote }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - value: {{ .Values.manager.dnsBase }} | |
| + value: {{ .Values.global.clusterDomain | default .Values.manager.dnsBase }} | |
| value: {{ .Values.global.clusterDomain | default .Values.manager.dnsBase | quote }} |
🤖 Prompt for 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.
In `@packages/system/opensearch-operator/patches/leaderElection.diff` around lines
34 - 35, Quote the rendered DNS_BASE value in the Helm template by applying the
quote function to the existing global.clusterDomain fallback expression,
preserving .Values.manager.dnsBase as its default. Ensure the resulting
EnvVar.value is always emitted as a string.
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM. The direction I asked for last round is in and it works, but the delivery channel takes down a different package, and the platform half of the wiring is untested.
I rebased d37e295 onto current main before reading it, since the branch forked 1232 commits back and I wanted the findings to be about today's tree. The rebase is clean; apps.yaml has moved a lot since July, but not where this lands. Everything below is measured against current main.
The things I blocked on last time are resolved. The test pins the property rather than a string, and it is not decorative: I reverted the template to .Values.manager.dnsBase, hardcoded cozy.local, swapped the default operands, and renamed the env var, and each of those turns the suite red while the unmutated tree stays green. The cozy.local rung is exactly what I refused last round. The chart edit reproduces too. I pulled upstream 2.8.0 fresh and ran the package's own patch --no-backup-if-mismatch -p1 < patches/leaderElection.diff: no fuzz, no offset, and the DNS_BASE line comes out identical to the committed one, so make update will not eat it. Sign-off and the release-note block are both there.
What blocks it now is that global is not a private channel to one subchart. internal/operator/package_reconciler.go:266-273 attaches the cozystack-values Secret as valuesFrom to every Package's HelmRelease unless its PackageSource sets operator.cozystack.io/skip-cozystack-values, and packages/core/platform/sources/bootbox.yaml does not. Then packages/system/bootbox/charts/smee/templates/role.yaml:3-5 guards on bare {{- if .Values.global }} and reads .Values.global.rbac.type inside. An empty map is falsy, so the guard is skipped today; a global carrying only clusterDomain is truthy and .Values.global.rbac is nil. helm template packages/system/bootbox --set global.clusterDomain=cozy.local fails with role.yaml:4:32 ... nil pointer evaluating interface {}.type, and the same render without the flag succeeds. role-binding.yaml:3 and deployment.yaml:6 carry the same guard. I rendered all 169 package charts both ways and this is the only outcome that changes: 133 render bare, and for the 36 that already need values the error text is byte-identical before and after. So any cluster with cozystack.bootbox in bundles.enabledPackages gets a HelmRelease that cannot render.
There is a mechanism for this that does not reach every package. Package.spec.components[<name>].values (api/v1alpha1/package_types.go:68-81, applied at package_reconciler.go:301-302) is already used in templates/bundles/system.yaml:25-36 to push platform values into the vendored kube-ovn and cilium subcharts. And helm template packages/system/opensearch-operator --set opensearch-operator.manager.dnsBase=example.test already renders DNS_BASE: example.test, so going that way needs no chart patch at all: the leaderElection.diff hunk and the vendored chart edit both come back out, and the "keep in sync with _cluster.cluster-domain" comment goes with them, which I would rather not carry anyway. Patching smee's guard instead would also work, but that puts a patch file in a package this PR has no other reason to touch, and leaves the next key added to global to find the next chart with the same shape.
The other blocker is the platform side of the test. The new suite sets global.clusterDomain itself, so it stays green if apps.yaml stops emitting the key and the original failure comes back. packages/core/platform/tests/ holds 36 suites and has a per-key convention for exactly this: apps_monitoring_enabled_wiring_test.yaml, apps_gateway_class_wiring_test.yaml and apps_load_balancer_class_wiring_test.yaml each matchRegex the rendered stringData["values.yaml"] for one key. This change adds a key to that Secret and adds nothing there. Whichever mechanism the fix lands on, it wants a wiring test there with the default and an override.
Ignore the red E2E Tests check. Its status text is "Pull Request workflow concluded 'failure'; e2e not run", and the run it points at has zero jobs, so nothing ever executed; the fork runs sat unapproved since July and got reaped. There is still no CI evidence either way on this head.
Three smaller things.
On a cluster that already has OpenSearchClusters, this does not take effect by itself. At 2.8.0 the manager reads DNS_BASE on each call and defaults to cluster.local, so a restart switches the domain, but the reconciler keeps an existing <cluster>-securityconfig-update Job while its securityconfig/checksum annotation matches, and the target hostname is not part of that checksum. backoffLimit is 0 and the wait loop has no attempt limit, so a Job that was already stuck stays stuck and has to be deleted by hand. That is worth a line in the release note, otherwise someone upgrades, sees nothing change, and reports the fix as broken. Certificates are a milder version of the same thing: they are only generated when the Secret GET fails, so existing SANs keep svc.cluster.local, which nothing in-cluster verifies but which stays stale until those Secrets go.
The value is unquoted, the same as upstream has it. EnvVar.value is a string and the PARALLEL_RECOVERY_ENABLED line right below quotes its own, so | quote would be consistent and costs nothing. It only bites on a domain that YAML reads as a non-string, which is not a valid domain, so this is tidiness rather than a defect.
The body has no Downstream repositories section. I walked the trigger map against the diff and nothing matches as far as I can see: the website row keys on packages/core/platform/values.yaml, which this PR does not touch, and the ansible row keys on changing what Cozystack requires from the distribution, which this does the opposite of. So "No downstream repository is affected", with those two lines as the reason.
One bot suggestion I would not act on: reverting the vendored chart file and keeping only the patch. charts/ is the post-patch artifact this repo commits, so both files moving together is correct, and I checked that the pair reproduces.
This is the DNS side of #4073 and it closes it for a fresh install. On a cluster already carrying the failure it is partial until that Job is deleted.
| # each package's own templates, so vendored operator subcharts that build | ||
| # in-cluster FQDNs from it (e.g. opensearch-operator's DNS_BASE) cannot see | ||
| # it. Keep in sync with _cluster.cluster-domain. | ||
| clusterDomain: {{ .Values.networking.clusterDomain | quote }} |
There was a problem hiding this comment.
This key reaches every package whose PackageSource does not set operator.cozystack.io/skip-cozystack-values, not only this one subchart. packages/system/bootbox/charts/smee/templates/role.yaml:3-5 guards on bare {{- if .Values.global }} and then reads .Values.global.rbac.type, so a global that carries only clusterDomain flips that guard on and the render dies on a nil .Values.global.rbac. Details and the alternative in the review body.
|
This is what I asked for in July, and I need it on main now: the opensearch TLS PR (#2682) cannot pass its e2e while the securityconfig Job dials cluster.local on a cozy.local cluster (#4073). I don't know when you will be back on this, so I'm taking your commit as is, with your authorship, into a branch on the main repo and adding one commit of my own on top: a platform test that pins networking.clusterDomain reaching global.clusterDomain in the cozystack-values Secret. Checked on today's main: rebases clean, make update reproduces the patched DNS_BASE line from the patch, package and platform suites green. The coderabbit thread about editing the vendored template is wrong, charts/ carries the patch result by design. I'll link the new PR here and close this one after it merges. If you'd rather rebase and add the test yourself in the next day or two, say so and I'll review that instead. |
|
New PR: #4185. It carries your commit with your authorship and both sign-offs; one clause of the message is reworded and marked as such in the message, because a per-component values route does exist and the global route is a choice, not the only way. On top of it: a platform test that pins the wiring, and a fix in bootbox, whose vendored smee chart dies on any non-empty global map. I will close this one when it merges. |
…main (#4185) ## What this PR does The opensearch-operator resolves every in-cluster name it dials as `<svc>.<ns>.svc.<DNS_BASE>`, and nothing set `DNS_BASE`, so it stayed on the vendored chart default `cluster.local` while the platform runs on a configurable domain. On a cluster that is not on `cluster.local` the securityconfig bootstrap job waits forever on a name that never resolves, the security configuration is never applied, and OpenSearch nodes stay unready. That is issue #4073. The platform now carries the domain to the operator in the `cozystack-values` Secret, under the subchart key the vendored chart already reads: ```yaml opensearch-operator: manager: dnsBase: "cozy.local" ``` `charts/opensearch-operator` reads that as `.Values.manager.dnsBase` and hands it to the manager as `DNS_BASE`, so nothing under `charts/` changes and the package needs no patch. The value renders from `networking.clusterDomain` rather than a literal, so a cluster that keeps `cluster.local` keeps working, and a package rendered without the Secret keeps the upstream `cluster.local` default. This is the route the KEDA cluster domain already travels through the same Secret. The whole change is one key in `packages/core/platform/templates/apps.yaml` plus two test suites: one pinning that `networking.clusterDomain` reaches the Secret under that key, one pinning that the key reaches the Deployment env in the package. ### Why this is not the `global` route Earlier revisions of this branch, and #3357 before it, emitted the domain as `global.clusterDomain` and patched the vendored template to read it. `global` is the one key Helm copies into every subchart, which is why it looked right. That is also the problem. The `cozystack-values` Secret is `valuesFrom` on every HelmRelease the operator builds unless the PackageSource opts out with `operator.cozystack.io/skip-cozystack-values` (`internal/operator/package_reconciler.go:265-273`), so a new key under `global` lands in the values of nearly every release on the cluster. One of them broke on it. The vendored smee chart in `packages/system/bootbox` gates on the truthiness of the whole `global` map and then indexes `global.rbac`, so a single unrelated key under `global` ends its render. This still reproduces on `main`: ```console $ helm template bootbox packages/system/bootbox # exit 0 $ helm template bootbox packages/system/bootbox --set global.clusterDomain=cozy.local Error: cozy-bootbox/charts/smee/templates/role.yaml:4:32 executing "cozy-bootbox/charts/smee/templates/role.yaml" at <.Values.global.rbac.type>: nil pointer evaluating interface {}.type ``` The subchart key reaches the operator with no patch, no change to any other release, and nothing to harden in bootbox. I measured that rather than reasoning about it: rendering all 170 charts under `packages/*/*` plain and with `--set opensearch-operator.manager.dnsBase=cozy.local`, exactly one chart's output changes, opensearch-operator, by the one `DNS_BASE` line this PR is about. Nine other charts differ between the two runs by exactly as much as two identical runs of the same command differ, which is the random material Helm regenerates per render, and thirty-five fail in both modes for reasons unrelated to the flag, with byte-identical errors either way. The smee hardening is still worth having, because the next key anyone puts under `global` hits the same gate, and that gate is still on the default branch of tinkerbell/charts. It is not carried here. It is available as a separate change against its own package. ### On having more than one way to say the cluster domain Review feedback on the earlier revision asked for the opposite of this: fold the `keda:` block into `global.clusterDomain` so the platform has one answer rather than two added a fortnight apart for the same reason. The instinct is right and I am not doing it here, because `global` is the channel that carries the cost. A key under it lands in the values of nearly every release, one vendored chart already ends its render on it, and the next chart with the same gate breaks on the day someone adds a second key. The subchart key has no reach: the chart that declares it reads it and everything else ignores it, which is how the KEDA domain already travels. That does leave the repo with several ways to tell a chart its domain, and that complaint stands. What it avoids is a shared channel whose blast radius grows with every new user of it. Whether the platform should grow one general mechanism here, and which one, is worth settling on its own rather than inside a bug fix. ### Why the stuck Job is a manual step and not a migration The release note asks operators to delete a wedged `<release>-securityconfig-update` Job once. On an existing OpenSearch installation that Job is already stuck, so this is a step for everyone the fix is for, and `packages/core/platform/images/migrations/` is the mechanism built for exactly that. It is not in this PR because the migration deletes Jobs in tenant namespaces, which is a wider grant and a wider blast radius than the one key this change adds, and the fix works without it. If it should ride the same release, it is a separate commit and I will add it. ### Upgrade behaviour on a running cluster Read from the operator source at v2.8.0. `DNS_BASE` is read on every call through `helpers.ClusterDnsBase` (`pkg/helpers/constants.go:35-41`), so the manager restart is the whole switch. On a cluster that already runs OpenSearch clusters: - Certificates are not re-issued. Transport and http certs are generated only when their Secret is missing (`pkg/reconcilers/tls.go:272-295`, `490-518`), per-node transport certs only for a pod with no entry in the Secret (`tls.go:373-377`), the admin cert only when missing, expiring within five days or not signed by the CA (`tls.go:176-211`). Existing SANs keep the old suffix. Nothing in-cluster verifies them: the securityadmin job runs `curl -k` and `securityadmin.sh -nhnv` (`pkg/reconcilers/securityconfig.go:34,40,46`), the operator client sets `InsecureSkipVerify` (`opensearch-gateway/services/os_client.go:77`), Dashboards run with `opensearch.ssl.verificationMode: none` (`pkg/builders/dashboards.go:180`). - OpenSearch node pods do not roll, because no StatefulSet field derives from the DNS base. Dashboards roll, since `OPENSEARCH_HOSTS` changes (`pkg/builders/dashboards.go:46-47`). - A securityconfig Job already stuck in its wait loop is not recreated. The reconciler keeps an existing `<release>-securityconfig-update` Job while its checksum annotation matches the securityconfig Secret (`securityconfig.go:140-146`), and the target hostname is not part of that checksum. Delete that Job once by hand. The next reconcile recreates it against the new name, and users and roles start applying from there. ### Known and not addressed here The upstream `manager.dnsBase` knob stays usable. The platform writes it through `spec.valuesFrom`, and a per-package override in `Package.spec.components[].values` lands in `spec.values`, which Flux merges on top of the references, so a package that needs a different domain can still say so and win. The fallback differs from its neighbours and that is deliberate. `packages/apps/opensearch` falls back to `cozy.local`, `packages/system/keda` declares `cozy.local` as its package default, and this package stays on the upstream `cluster.local`. Inside the bundle all three receive the same value, so nothing diverges in practice, and `cluster.local` is the right standalone default for a Cozystack installed over an existing kubeadm or k3s cluster. A cluster domain that YAML reads as a number does not survive the round trip. The platform quotes the value into the Secret, so `networking.clusterDomain: 123` arrives as the string `123`, but the upstream template renders it as a bare `value: 123` in the container env, and an `EnvVar` value has to be a string. Quoting it means patching the vendored template, which is the thing this approach removes, and an all-numeric cluster domain is not a usable DNS name to begin with. Before this change the key was never set from the platform, so the path is new here even though nothing reaches it. The vendored `opensearch-operator` Deployment template does not reproduce from `make update` and has not for a while: a fresh pull plus the patch differs in three places, and one of them is a live defect. The committed `nindent 10` on `manager.extraEnv` renders invalid YAML, so that knob cannot be used at all, while upstream's `nindent 8` works. Nothing in the tree sets `extraEnv`, so it is latent, and the drift predates this branch. This PR does not touch that patch at all and does not carry a fix for it. Nothing in CI renders a package chart with the values the platform injects, which is why the smee breakage on the earlier approach had to be found by hand. That is a gap in the harness. ### Screenshots Not a UI change. ### Downstream repositories Walked the trigger map against the diff, which is one template key in `packages/core/platform/templates/apps.yaml` and two test files. `packages/core/platform/values.yaml` is untouched and gains no key, so the hand-written platform-package table on the website is not affected. No package, app schema, version enum, CRD, release prefix or naming convention moves, so the Terraform provider is not reached. The `networking.clusterDomain` key already exists and its default does not change, so ansible-cozystack, which passes `--cluster-domain=cozy.local` to k3s and sets only the networking CIDRs in its platform-package template, needs nothing: the operator now follows the domain that repo already configures. talm sets the Talos `cluster.network.dnsDomain` from its own value and does not write the platform value either. Nothing under `hack/`, no namespace and no package-tree layout moves, so ccp is not reached. - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: - [ ] [cozystack/community](https://github.com/cozystack/community) - follow-up: ### Release note ```release-note fix(opensearch-operator): the operator's DNS_BASE now follows the platform cluster domain (`networking.clusterDomain`) instead of the upstream `cluster.local` default, so the securityconfig job and OpenSearch Dashboards reach the cluster on installations that use `cozy.local`. On upgrade, an existing `<release>-securityconfig-update` Job that is already stuck waiting on the old hostname is not recreated by the operator: delete it once by hand and the next reconcile recreates it against the new hostname. Existing certificates keep their SANs and keep working, nothing in-cluster verifies them. ```
|
#4185 is merged (e89617d), so I'm closing this one. The merged shape is not your commit though. The route moved from |
Problem
The OpenSearch operator builds in-cluster service FQDNs from its
DNS_BASEenv var:securityadminbootstrap job connects to<svc>.<ns>.svc.<DNS_BASE>OPENSEARCH_HOSTSat the same suffixThe vendored upstream chart defaults
DNS_BASEtocluster.local, and the vendored subchart has no way to learn the actual cluster domain:_clusteris a plain value that Helm does not propagate into subcharts, and the wrapper chart'svalues.yamlis not templated.On a
cozy.localcluster those names never resolve:*-securityconfig-updatejob loops onWaiting to connect to the cluster(getaddrinfo ENOTFOUND ...svc.cluster.local), so the.opendistro_securityindex is never created. OpenSearch nodes then fail readiness withNot yet initialized (you may need to run securityadmin)and the StatefulSet never reaches Ready.[ConnectionError]: getaddrinfo ENOTFOUND ...svc.cluster.local.Fix
Rather than hardcode
cozy.local(which would mirror the same failure onto clusters that keepcluster.local, e.g. Cozystack layered on an existing kubeadm/k3s/RKE2), make the operator follow the platform's configurable cluster domain:global.clusterDomain(fromnetworking.clusterDomain) in thecozystack-valuesSecret. Unlike_cluster, Helm's reservedglobalkey propagates into subcharts.leaderElection.diff) readsDNS_BASEfrom.Values.global.clusterDomain, falling back to the upstreammanager.dnsBasedefault (cluster.local) when unset, so a barehelm installis unchanged.Testing
helm templaterendersDNS_BASE: cozy.localon the default install,cluster.localwhenglobal.clusterDomain=cluster.local, andcluster.localwith noglobal(bare install).tests/dns_base_test.yaml) pins that the operator follows the platform domain (an arbitraryexample.testpropagates) and falls back to the upstream default — not that it equals any specific string.helm unittest .→ 2 suites / 4 tests pass.Summary by CodeRabbit
Bug Fixes
cluster.localwhen no custom cluster domain is configured.Tests