Repository navigation
Conversation
Solr 10 introduced several breaking changes that prevent the operator from successfully starting and managing SolrClouds. This change adds version-conditional behavior for Solr 10 while preserving full backwards compatibility with Solr 9.x. Major changes covered: * solr.xml — Solr 10 removed several `<solrcloud>` parameters (genericCoreNodeNames, hostContext, allowPaths, metricsEnabled). A new `DefaultSolrXMLForSolr10` template, mirroring the stock Solr 10 template, is selected when the image tag indicates Solr 10+. * Host advertise — the `host` system property was renamed to `solr.host.advertise`. A `SOLR_HOST_ADVERTISE` env var is now set on Solr 10 pods. * Modules — Solr 10 removed `/opt/solr/contrib/<module>/lib` and `/opt/solr/dist`. Modules are now loaded via the `SOLR_MODULES` env var, and the operator no longer emits contrib paths in `sharedLib` for Solr 10. * hostPort sysprop — `-DhostPort` is no longer needed in Solr 10 and is skipped. * zkcli.sh removed — `setUrlSchemeClusterPropCmd` (TLS setup) now uses `solr cluster --property` and `solr zk cp` for Solr 10 instead of the removed `cloud-scripts/zkcli.sh`. * `solr api` CLI — `-get URL` was replaced with `--solr-url URL`. Secure probes (`useSecureProbe`) and the e2e helper (`callSolrApiInPod`) emit the new flag for Solr 10. * Basic auth — Solr 10 no longer honors the deprecated `-Dbasicauth=user:pass` JAVA_TOOL_OPTIONS path. The e2e helper now uses the native `--credentials user:pass` flag for Solr 10. Version detection lives on the `SolrCloud` type as `(*SolrCloud).IsSolr10OrLater()`, which parses the major version from the image tag and treats unparseable tags (e.g. "latest", "nightly") and a nil `SolrImage` as pre-10 for backwards compatibility. A package-level `IsSolr10OrLater(imageTag string)` is also exported for callers that only have a raw image string. Unit tests cover the version parser, both `solr.xml` templates, `setUrlSchemeClusterPropCmd` for both branches, the env vars emitted by `GenerateStatefulSet` for Solr 9 vs Solr 10, and both branches of `useSecureProbe`. End-to-end tests have been verified against Solr 10.0.0 across Basic, Scaling (with replica migration), Security JSON (provided + bootstrapped), TLS (Secrets and Mounted Dir, including ClientAuth Need/Want, CheckPeerName, VerifyClientHostname), Local-directory backups (recurring + single), Ingress, and Managed Rolling Upgrades. Solr 9.8.0 Basic was verified as a regression baseline. The Prometheus exporter is not covered: `solr-exporter` was removed from the Solr distribution in 10, and metrics are now expected to be scraped from Solr's built-in endpoint. That work is tracked separately in apache#820. Refs apache#821, apache#826. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove SolrMajorVersion(tag) and standalone IsSolr10OrLater(tag) functions. Version detection is now driven entirely by the required solrMajorVersion CRD field (validated 8–10), eliminating misdetection of custom image tags like '261.162.1' as Solr 261.
Adds a new `solrMajorVersion` value (default 9, matching the default image.tag of 9.10.0) and renders it under spec.solrMajorVersion. Uses Helm's `required` so the chart fails fast with a clear message if a user explicitly unsets it. Documents the new value in the chart README and adds a v0.10.0 upgrade note covering both the CRD field and the new chart value. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ction Drops "Required by the operator" from the chart README row since the table already shows a default of 9, which reads as contradictory at the chart layer. The CRD field is still required at the API layer; the chart just hands it a default. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reframes the solrMajorVersion entry to make the CRD-required vs chart-defaulted distinction explicit (raised in PR apache#827 review). Adds two new entries surfaced by PR apache#827 testing: * SolrPrometheusExporter must be removed before bumping a SolrCloud to Solr 10.x, otherwise the rolling upgrade stalls and requires rollback (apache#820). * On Solr 10 the operator sets SOLR_MODULES on pods (since contrib paths no longer exist in the image). Modules baked into a custom image's Dockerfile via SOLR_MODULES are overridden after upgrade; declare modules via spec.solrModules instead. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* helm/solr-operator/Chart.yaml: append an `added` entry under the existing v0.10.0 changelog covering the new required spec.solrMajorVersion field and Solr 10 support. * helm/solr/Chart.yaml: replace the placeholder entries with one `added` entry covering the new solrMajorVersion chart value. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two CI fixes for the new required SolrMajorVersion CRD field: * gofmt: struct field alignment in tests/e2e/test_utils_test.go and controllers/util/solr_util_test.go broke when SolrMajorVersion was added to several SolrCloudSpec literals. Reformatted via make fmt. * Controller tests: CRD validation now rejects SolrCloud resources that omit spec.solrMajorVersion, which caused 61/71 specs in the smoke-test suite to fail at [JustBeforeEach]. Default the field to 9 in each JustBeforeEach (only when unset, so tests can still override) and add SolrMajorVersion: 9 to the two inline solrRef literals in the SolrPrometheusExporter tests. Verified locally with make lint, make test, and make build. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts: # docs/upgrade-notes.md # helm/solr-operator/Chart.yaml # helm/solr/Chart.yaml
Replaces the required spec.solrMajorVersion field with parsing the Solr
major version from spec.solrImage.tag, as agreed in the PR discussion.
The tag must start with the Solr version (e.g. 10.0.0, 9.10.0-slim, 9.10,
8.11, 10); only majors 8 to 10 are accepted. SolrClouds with other tags
(latest, custom tags, unsupported majors) get an UnsupportedSolrVersion
Warning event and are not reconciled, but can still be deleted. Short
versions are accepted because released operator versions stored their
default tag (8.11) in the SolrCloud spec. The CRDs and the solr Helm
chart are unchanged compared to main.
Further Solr 10 fixes:
- set SOLR_PORT_LISTEN, which Solr 10's bin/solr reads instead of SOLR_PORT
- keep -DhostPort, for custom solr.xml files that still use ${hostPort:}
- load modules from /opt/solr/modules/<module>/lib via sharedLib instead
of SOLR_MODULES, the same mechanism as for Solr 9
- align the Solr 10 solr.xml with Solr 10's stock solr.xml (enforced by a test)
- pass probe credentials via --credentials, -Dbasicauth via
JAVA_TOOL_OPTIONS no longer works on Solr 10
- show the right version in the node status for digest-pinned tags
Tests: version parsing, reconcile skip and deletion with an unsupported
tag, probe auth on Solr 9 and 10, custom podPort, Solr 10 module paths,
an e2e case for probesRequireAuth, exporter e2e tests skipped on Solr 10.
Docs: Solr 10 column in the version matrix, image tag rules, upgrade
notes for tags, custom solr.xml and the Prometheus exporter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Compare node versions with the SolrCloud's tag without a digest, so a tag like 10.0.0@sha256:... no longer leaves the SolrCloud looking stuck mid-upgrade (TargetVersion never cleared) - Correct the comment on why Solr 10 probes use --credentials - Docs: Solr 10 note on the Prometheus exporter page, tag rule in the solr chart README, upgrade notes on the metrics element and on indexes created with Solr 8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- No-restart claim - Custom log4j2.xml and solr.logs.dir
# Conflicts: # docs/modules/upgrade-notes/pages/upgrade-notes.adoc # helm/solr-operator/Chart.yaml # helm/solr/README.md
- Drop the metricsEnabled upgrade note; Solr 10 still supports the metrics element - Mention SOLR_ALLOW_PATHS, which works unchanged on Solr 9.6+ and Solr 10 - Shorten the image.tag row in the solr chart README - Add the image tag requirement to the solr chart changelog - Use a version-agnostic image tag in the SolrPrometheusExporter tests
Version checks are now inlined at each conditional instead of being cached in per-function isSolr10 variables.
Solr 10 removed the Prometheus exporter, so such a Deployment can only crash-loop. Emit an UnsupportedSolrVersion Warning event and log an error instead. Tags that don't start with a Solr version are still allowed for custom exporter images.
…gs on Solr 10 - Don't reconcile a SolrPrometheusExporter that refers to a Solr 10 SolrCloud either, as Solr 10 only serves metrics in Prometheus format, and block Solr 11+ images too - Check before creating any exporter resources, and mark the exporter not ready - Warn (without blocking) when a Solr 10 cloud's custom solr.xml contains hostContext or genericCoreNodeNames - Upgrade notes: SOLR_SECURITY_ALLOW_PATHS on Solr 10, as SOLR_ALLOW_PATHS is deprecated there
# Conflicts: # .github/workflows/e2e-tests.yaml # helm/solr-operator/Chart.yaml
The merge with main took main's e2e matrix, which drives solr-version from the workflow_dispatch input and so tests a single version. Restore the Solr 10 dimension this branch had, keeping main's input-driven manual runs: a pull request tests 9.11.0 and 10.1.0, a manual run tests only the version given by the input. fail-fast is off so that a failure on one Solr version does not cancel the job for the other.
New SolrCloud option `replicaPlacementFactory` sets the cluster-wide replica placement plugin and its config. The operator applies it through Solr's /api/cluster/plugin API once the cloud has a ready pod, and re-applies it when the section changes, tracked by a hash in the status. Removing the section removes the plugin. The config is passed to Solr as arbitrary JSON, so it covers any plugin's settings including the map-valued ones, with no per-option plumbing. Requires Solr 9+, which is gated using the version detection from the Solr 10 compatibility work this builds on. Also fixes a latent panic in CallCollectionsApiV2: a nil *bytes.Buffer was passed as the request body, which is a non-nil io.Reader that http.NewRequestWithContext panics on. No existing caller passed a nil body, so this is the first call to hit it. Closes apache#691
- Keep JSON numbers as their original literal when normalising the config, so a value a float64 cannot hold exactly is neither corrupted on its way to Solr nor collapsed into the same hash as its neighbour. Trailing data after the JSON value is still rejected. - Include the ZooKeeper ensemble and chroot in the applied hash. Cluster plugins live in ZooKeeper, so pointing a SolrCloud at a fresh target has to re-apply the plugin rather than trust the old hash. - Gate the removal path on the Solr version too. A cloud rolled back to an unsupported version with the section removed was calling the missing API every 15 seconds; the hash is now retained until a supported version can remove the plugin. Four regression tests added, one per behaviour.
janhoy
marked this pull request as ready for review
November 9, 2026 09:18
| // last applied to the cluster, used to detect when it needs to be re-applied. Empty when no | ||
| // placement plugin is managed by the operator. | ||
| // +optional | ||
| AppliedReplicaPlacementFactory string `json:"appliedReplicaPlacementFactory,omitempty"` |
Contributor
There was a problem hiding this comment.
Should we instead keep this in the statefulSet annotations, like we do for the other hashes? (I do know they mostly live in the pod annotations, where this would live in the statefulset annotations because we don't want a restart).\
Maybe instead we have a section for Status.appliedSettings and have replicaPlacementFactory as a key in there? Because I imagine that we could have other settings added in the future. Though in the future, hopefully Solr has a Kubernetes plugin that will allow this stuff to just live in Kubernetes configMaps instead of zookeeper...
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 AI assisted 🤖
Closes #691
Problem. Solr picks replica placement with a cluster-wide plugin and defaults to
SimplePlacementFactory. To use anything else, includingAffinityPlacementFactory, you either maintain a full custom solr.xml or POST to/api/cluster/pluginby hand after every cluster rebuild. Neither is declarative, and the custom solr.xml route costs you the operator-generated solr.xml entirely.Solution. A new SolrCloud section:
The operator applies this through
/api/cluster/pluginonce the cloud has a ready pod, under the reserved name.placement-plugin. Removing the section removes the plugin. Changes need no pod restart. Also exposed as the Helm valuesolrOptions.replicaPlacementFactory.Why the API and not solr.xml. The issue proposed rendering
<replicaPlacementFactory>into the generated solr.xml. Two findings moved me off that:solr.cluster.plugin.edit.enabled=false, orNodeConfigthrows at startup and no node comes up. That property then disables the/cluster/pluginedit API cluster-wide, for cluster singletons and custom plugins too. The operator would be imposing a cluster-wide restriction to deliver one feature.AffinityPlacementConfighaslong,Integer,Booleanand threeMap<String,String>fields, so solr.xml would need correct type tags plus nested<lst>rendering. Over the API the config is JSON and passes through untouched.Design notes.
configis arbitrary JSON viax-kubernetes-preserve-unknown-fields, so any plugin's options work with no per-option plumbing. The tradeoff is no schema validation of keys.status.appliedReplicaPlacementFactory, so Solr is called only when the spec changes. The hash normalises the JSON first, so reformatting the config does not trigger a reapply. A cloud configuring no plugin never calls Solr at all.SolrVersionAtLeastfrom Add Solr 10 compatibility support #827. On Solr 8 the setting is ignored with a warning event rather than retried.COLL_EDIT_PERM, which the operator's k8s role already holds throughcollection-admin-edit.Also fixes a latent panic in
CallCollectionsApiV2. It passed a nil*bytes.Bufferas the request body, which is a non-nilio.Readerwrapping a nil pointer, sohttp.NewRequestWithContextpanicked callingLen()on it. No previous caller passed a nil body, so this GET was the first to hit it. The panic aborted the whole reconcile, leaving the SolrCloud stuck at zero ready replicas in its status. Present on main, independent of this feature, and easy to split out if preferred.Testing. Five unit tests cover plugin construction, JSON passthrough including nested objects and non-string types, hash stability against reformatting, and rejection of invalid JSON. The full unit suite passes, as do
check-manifests,check-generated,check-mod,check-format,vetandcheck-helm.Manually verified on a kind cluster with Solr 9.11 and the e2e harness:
GET /api/cluster/pluginReplicaPlacementPluginUnsupportedwarning and no retry loopThat manual run is also what surfaced both bugs above, so the API call is still worth an e2e spec. I have not added one.