ontoserver-0.4.0
Removed
-
BREAKING: the bundled
nginx-ingresssubchart. The chart no longer installs an ingress
controller. It was pinned at2.1.0and nobody was updating it, which makes a vendored
network-facing controller a liability rather than a convenience.Migration. Install a controller yourself and point the chart at its IngressClass:
helm repo add nginx-stable https://helm.nginx.com/stable helm install nginx-ingress nginx-stable/nginx-ingress \ --namespace nginx-ingress --create-namespace \ --set controller.ingressClass.name=ontoserver-nginx
ontoserver: ingress: enabled: true className: ontoserver-nginx # must match the controller's IngressClass # delete the whole nginx-ingress: block
The chart fails to render if a
nginx-ingress:block is still present — including
enabled: false. That is deliberate: nothing at the top level ofvalues.schema.jsonsets
additionalProperties: false, so the key would otherwise be accepted silently andhelm upgrade
would quietly stop deploying the controller, taking the service offline with no error anywhere.
The presence of the key, not its value, is the signal that a values file has not been migrated.Consequences worth knowing: the chart now has no dependencies at all, so
Chart.lockand the
vendoredcharts/directory are gone andhelm dependency buildis a no-op.
Note: verified against the
ontoserver-v0.3.0tag rather than assembled from commit messages —
18 commits touch this chart since that release, and the values-key diff was used to confirm
nothing user-facing is missing. Changes predating 0.3.0 are covered by the sections below.
Added
-
ontoserver.gateway.allowPlaintextandontoserver.gateway.listenerPortPlain. A plaintext HTTP
listener now has to be requested explicitly: it is off by default so a public Gateway cannot be
left unencrypted by accident. Enable it for local development, or when TLS terminates upstream. -
ontoserver.gateway.closureRequestTimeout(default300s). The$closureHTTPRoute carried no
timeoutsblock at all, so the chart's longest-running operation inherited whatever the Gateway
implementation defaults to. Falls back torequestTimeoutwhen empty. -
ontoserver.metrics.serviceMonitor.labels,.interval,.scrapeTimeoutand
.namespaceSelector. Most Prometheus installations set a non-emptyserviceMonitorSelector, and
a ServiceMonitor carrying no matching label is silently never discovered — nothing errors, the
metrics just never appear. -
Validation of the release-name length, with the limits checked against a live API server. The
binding constraint is Service names (DNS labels, capped at 63), which caps the release name at
33 — Helm's own cap of 53 is not low enough. Without this an over-long name yields a
partially installed release: everything applies except one Service. -
ontoserver.deployment.allowScaledReadWrite— opt in to the unsupported scaled read-write
topology. Scaled deployments must otherwise be read-only: each replica keeps its own Lucene
index on its own PVC, so content written through the round-robin Service is indexed only on
the replica that served the write, and$expand/$validate-codethen fail on the others. -
ontoserver.deployment.podDisruptionBudget.maxUnavailableand.unhealthyPodEvictionPolicy
as real values rather than commented-out suggestions. -
Opt-in security context:
ontoserver.deployment.podSecurityContext,
.containerSecurityContext,.db.containerSecurityContextand.automountServiceAccountToken.
The chart previously set no security context anywhere, so pods ran as root. All four default to
unset, so upgrading an existing release renders a byte-identical pod spec and rolls no pods —
hardening has to be requested. The Postgres sidecar has its own value because it cannot share
the Ontoserver container's: the postgres entrypoint requires uid 999.A verified hardened configuration is documented in the README, along with the two
combinations that cannot be made to work non-root (the Postgres sidecar and Ontoserver's own
HTTPS mode). Those constraints were established by running the shipped images under each
setting, not inferred — each failure is a crash at startup. -
ontoserver.deployment.extraVolumesand.extraVolumeMounts— arbitrary volumes and mounts for
the Ontoserver container, rendered verbatim and appended after the chart's own entries so
chart-managed names always win a collision. Both default to empty, so an existing release
renders a byte-identical pod spec.This makes
readOnlyRootFilesystem: truereachable, which it previously was not: the server
needs a writable/tmpand the chart had no way to supply one. Validated on a cluster (AKS,
external PostgreSQL, Gatekeeper auditing): Ready in ~50s with 0 restarts, root filesystem
genuinely read-only, FHIR served, bothhelm testsuites passing, and zero violations of
readOnlyRootFilesystem,allowedUsersGroupsornoPrivilegeEscalationfor the namespace./tmpturned out to be
load-bearing rather than just a log destination — a running server writesspring.log,
hsperfdata, Tomcat's work directories anddownlaod-*scratch files there. The hardened
README recipe now includes both, asserted together by a test so it cannot ship half-applied. -
Schema validation and test coverage for
ontoserver.existingSecretConfig, a 0.3.0 feature that
shipped with neither. The value was accepted before (the schema has noadditionalProperties
restriction at that level) so this is validation and documentation rather than a functional fix:
a non-string is now rejected with a message naming the key.
Fixed
-
Documented a silent
$closurerouting failure on Traefik. Traefik matchesPathPrefixagainst
the percent-encoded path, so a client sending/fhir/%24closuremisses the dedicated pod-0 route
and is load-balanced across all pods, corrupting the stateful closure table. NGINX decodes before
matching and is unaffected. Both were tested; AWS ALB and Azure AGIC remain unverified and the
README says so. -
Removed the unreachable
existingVolumebranch from the StatefulSet'svolumeClaimTemplates.
validate-values.yamlrejectspersistence.files.existingVolumefor a StatefulSet outright, so
the branch could never render — and the spec it would have produced (volumeNamewith no
accessModesorresources) was not a valid PVC. Renders are byte-identical across all eight
test fixtures. -
Renamed
poddistributionbudget.yamltopoddisruptionbudget.yaml(the resource is a
PodDisruptionBudget). Template filenames are not part of the API, so this changes nothing at
install time. -
A
Deploymentwith persistence on aReadWriteOncevolume now rendersstrategy.type: Recreate
instead of the requestedRollingUpdate.RollingUpdatecannot work there: it starts the
replacement pod before removing the old one, and a RWO disk attaches to one node at a time, so a
replacement scheduled elsewhere waits indefinitely onMulti-Attach errorwhile the old pod is
never torn down. Reproduced on a live cluster; it also blocks PVC expansion until the volume
detaches. Scoped toReadWriteOnce/ReadWriteOncePod, soReadWriteManyusers keep
zero-downtime rolling upgrades, and the sidecar'sdbfilesaccess mode is only consulted when
the sidecar is enabled.Upgrading an existing release: Kubernetes defaults
spec.strategy.rollingUpdateand refuses
to hold it alongsidetype: Recreate.helm upgradeand client-sidekubectl applyhandle the
removal; server-side apply fails withspec.strategy.rollingUpdate: Forbidden. Each row of
that was verified on a cluster, and the README gives the one-line patch that clears it. -
ontoserver.secretConfigno longer fails to render on a non-string value.b64encrejects
anything but a string, so a numeric port or a boolean flag aborted the whole release with
wrong type for value; expected string; got int64— and the error named only<b64enc>, not the
key at fault. Values are now coerced withtoString, matching the siblingontoserver.config,
which has always accepted them via| quote. Note that Helm parses YAML numbers as float64, so
quote any value whose exact form matters; that is true of both keys and is not new. -
envoygateway.envoyProxy.pdbMinAvailable: 0and.replicas: 0are no longer silently coerced to
1 and 2. Both were rendered with| default, and 0 is falsy in Go templates, so the Envoy fleet
could not be scaled to 0 and its PodDisruptionBudget could not be set to permit full eviction.
Same class of bug as the PodDisruptionBudget fix above.minAvailablenow also routes through the
shared IntOrString helper for consistency, though the quoting is not load-bearing in this field. -
podDisruptionBudget.minAvailable/.maxUnavailablenow accept percentages. The previous guard
comparedintagainst 1, andint "25%"is 0, so every percentage — including the25%form
the values file suggested — was rejected. Percentages are emitted quoted and counts unquoted,
as the KubernetesIntOrStringtype requires. Setting both, setting neither,maxUnavailable: 0,
and aminAvailableat or above the replica count are now all rejected with an actionable
message instead of silently producing an unevictable workload. -
Configuration changes now roll the pods.
checksum/secret-configand
checksum/external-secretannotations were added to the pod template, because Ontoserver
resolves its configuration as environment variables at container start, so ahelm upgrade
that changed only a Secret left the running pods on the old value indefinitely. See
"Configuration changes and pod restarts" in the README for what is and is not covered. -
The
<release>-ontoserver-db-filesStorageClass is no longer created when
persistence.dbfiles.storageClass.provided.enabledisfalse. The condition tested the
enclosing map, which is always truthy, leaving an orphaned cluster-scoped object while the PVC
correctly referencedstorageClass.name. -
Registry credentials containing a
"or\no longer corrupt the image pull secret, in both
the chart-managed Secret and the External Secrets variant. The.dockerconfigjsonwas built by
interpolating credentials into a JSON string literal, so either character produced invalid
JSON — which the kubelet reports only as an opaqueImagePullBackOff.