Skip to content

Chart 1.0.5: working bundled MongoDB, upgrades that keep the release, OPC Router 5.7 - #14

Draft
etoensfeldt wants to merge 40 commits into
OPC-Router:mainfrom
etoensfeldt:feature/1.0.5/#341690-rescue
Draft

etoensfeldt wants to merge 40 commits into
OPC-Router:mainfrom
etoensfeldt:feature/1.0.5/#341690-rescue

Conversation

@etoensfeldt

@etoensfeldt etoensfeldt commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Changes

  • Bundled MongoDB: runs on bitnamilegacy/mongodb:4.4.15, which opens the old 4.4.11 data. The subchart is pinned to 10.31.5.
  • Upgrades: an existing release keeps its selector.
  • Database connection: the router connects with directConnection=true. Without it, OPC Router 5.7 finds no primary whenever the router initiated the replica set before the MongoDB chart did.
  • Default image OPC Router 5.7. The README covers the move: back up first; the embedded database migrates to MongoDB 8; use Recreate with the bundled MongoDB; git projects need project.importMode=overwrite; image.tag=5.3 stays on 5.3.
  • Embedded database: an init container for the log folder.
  • Every router: S6_BEHAVIOUR_IF_STAGE2_FAILS=2. Recreate in every mode, because its ReadWriteOnce volumes stall a rolling update on another node; updateStrategy.type=RollingUpdate stays available.
  • Permissions: a minimal role, only for the project auto-update; no API token otherwise.
  • Managed clusters: resources on every container; pinned helper images.
  • Extension points: service.extraPorts, extraEnv, extraVolumes/extraVolumeMounts, extraInitContainers.
  • Optional: rotation of the embedded MongoDB log.
  • Fixes: NOTES (LoadBalancer, embedded database), the secret volume type, a node-affinity guard for chart-created local volumes, mongodb.replicaCount > 1 refused.
  • CI: PR checks run ct lint, helm-unittest, and rendering validated with kubeconform against Kubernetes 1.25/1.30/1.34, with Helm 3 and 4. The release workflow is updated.
  • README: upgrade guide, requirements, storage, downtime, security, full parameter table.

etoensfeldt and others added 30 commits October 2, 2026 16:40
Windows checkouts with core.autocrlf would otherwise turn the CI scripts
into CRLF files that bash cannot run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every pull request and every push to main now runs chart-testing's lint
on changed charts and renders the chart with each values file under
.github/ci/values for Kubernetes 1.25, 1.30 and 1.34. The rendered
manifests are validated with kubeconform in strict mode, with Helm 3
and Helm 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves to actions/checkout v7, azure/setup-helm v5 with Helm 3.22.0 and
chart-releaser-action v1.7.0, grants the token write access explicitly
and skips chart versions that are already released.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ct lint runs helm dependency build, which refuses a dependency whose
repository is not configured. The check stayed green so far only
because no pull request changed the chart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pull requests now also run helm-unittest against the chart. The tests
live in charts/opc-router/tests and are excluded from the package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bitnami removed the versioned MongoDB tags from docker.io/bitnami, so a
fresh install could no longer pull its database. The chart now pins the
mongodb subchart to 10.31.5, the version every 1.0.x release shipped,
vendors it together with a matching Chart.lock, and pulls MongoDB 4.4.15
from docker.io/bitnamilegacy, the newest 4.4 build kept there.

Releases are packaged from the vendored subchart instead of resolving
it from a third-party chart index at release time, and only versions
that are not tagged yet are released.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chart versions up to 1.0.4 put the chart and app version into the
Deployment's selector. Kubernetes does not allow a selector to change,
so every chart upgrade failed with "field is immutable".

The router's selector is now read from the release's existing
Deployment and reused unchanged. New releases get selector labels
without any version (name, instance and component). The pod labels are
built from the selector labels first, so the selector always matches,
and the Service selects the pods with the same labels. This also removes
the duplicate instance label and the StatefulSet pod-name label that
made the Deployment invalid YAML with mongodb.deploy=false.

lookup returns nothing under helm template, so tools that render
offline see the new labels and need to replace the Deployment once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two routers on one embedded database cannot both start: the second one
finds the data directory locked and, without a stage-2 policy, keeps
running without answering. With mongodb.deploy=false the Deployment now
uses the Recreate strategy, so the old pod is gone before the new one
starts. With a separate MongoDB the strategy stays unset as before, so a
strategy patched onto a live Deployment is kept; updateStrategy sets
any strategy explicitly.

The router container also gets S6_BEHAVIOUR_IF_STAGE2_FAILS=2 unless
envVars already sets it: a failed startup step then stops the container
and Kubernetes restarts it, instead of leaving it running and silent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The chart bound the router's service account to the namespace admin
role whenever it created the account, although only the experimental
project auto-update uses the Kubernetes API, to restart the router pod
after a new commit. Clusters whose rules forbid such a binding had to
turn off the service account.

The admin binding is gone. With the auto-update a Role allows listing
and deleting pods, bound under a new name because a binding's role
reference cannot change. Without the auto-update no role is created and
no API token is mounted into the pod. The restart now selects only this
release's router pods instead of every pod with the chart's name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unanchored "tests/" pattern also excluded templates/tests, so the
chart's own connection test would have been missing from the package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The project clone and auto-update containers used alpine/git:latest and
bitnami/kubectl:latest; Bitnami now publishes only latest tags for free.
They now default to alpine/git v2.47.2 and registry.k8s.io/kubectl
v1.33.0, which has no shell, so the proxy is started through the image's
kubectl entrypoint.

helperResources sets requests and limits for all helper containers, and
the connection test gets a configurable image and resources, so clusters
that reject containers without limits can run every pod of the chart.
The connection test now asks the runtime's health route instead of only
opening the web port.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The data volume defaulted to 100M and the log volume to 1Gi. With the
embedded database both filled up in production: MongoDB stopped when it
could no longer write its log, and the web management timed out.

Capacities now default to empty. A configured capacity is used as is.
Otherwise an existing claim keeps its current size, because a bound
claim cannot shrink and can only grow with a storage class that allows
expansion. A new install gets 10Gi for data and 6Gi for logs with the
embedded database, and 1Gi and 4Gi with a separate MongoDB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the embedded database, the image's startup check writes into
/var/log/opcrouter/mongodb before a later step creates that folder. A new,
empty log volume next to an existing database therefore made every start
fail, for example after the log claim was replaced.

With mongodb.deploy=false an init container from the router image now
creates the folder and hands it to the image's mongodb user. Images
without that user have no embedded database and are left untouched.
prepareLogFolder=false switches the step off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
service.extraPorts opens further ports on the router container and the
Service, for example a project's OPC UA server. extraEnv takes native
EnvVar entries, so a value can come from a Secret; it also counts for the
startup-failure default. extraVolumes, extraVolumeMounts and
extraInitContainers cover what clusters so far patched into the
rendered manifests, such as an odbc.ini for the ODBC plug-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
probes.enabled adds a startup, a readiness and a liveness probe. Startup
and liveness ask /health/runtime/live, readiness /health/runtime/ready,
so a runtime that runs but is not ready is taken out of rotation, not
restarted. The startup budget is ten minutes for a first start that
migrates the embedded database. With HTTPS the probes use the HTTPS web
port, because port 8080 only redirects.

The probes stay off by default: builds before 5.3.6001 and 5.4.5005
answer every address with the start page, so a probe there passes
without checking anything. With probes on, the service publishes a pod
that is not ready, so the web management stays reachable while the
runtime is not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
project.importMode chooses how the project replaces the configuration
at every start. clear, the default, deletes the configuration before the
import, as the chart always did. On 5.5, 5.6, 5.7 and later 5.4 builds
that step stops the web management from starting, a known product
defect; overwrite imports over the existing configuration instead and
works on every build from 5.1.5001 on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A secret volume has no type field; strict schema validation rejects the
Deployment as soon as HTTPS or an SSH key is configured. The CI matrix
now renders the web management over HTTPS with login, and the runtime
image with its embedded database, which renders cleanly since the pod
labels are merged without duplicates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With mongodb.replicaCount above 1 the chart rendered a connection string
the Deployment could not even parse, and the router supports only a
single-member replica set anyway: several hosts stop it from starting,
and losing a member of a larger set stalls it. The chart now says so
when it renders. The connection string of the single member is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a LoadBalancer service the notes sent users to the bundled
MongoDB's external service and port, and with the embedded database
they failed to render at all, which made every such install fail. They
now name the router's service and port, use https when the web
management serves HTTPS, forward a local port to the service instead of
a looked-up pod, and warn while the web management has no login.

The securityContext text in the values now states what the image
measurably needs instead of suggesting settings that stop it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The redundancy mode was never wired up: no template used the helper
for a second router's name, the two older selector helpers or the
redundant project clone, and the values they read do not exist. Every
CI values file renders the same as before. The selector of an existing
release, which may still carry the old redundancy label, is read from
the cluster and stays as it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The router image writes the embedded MongoDB's log without rotating it,
so on a long-running install it grows until the log volume is full and
the database stops. mongodbLogRotation.enabled adds a container from the
router image that renames the log once it exceeds maxSizeMB, asks mongod
to reopen its log path, and keeps the newest keepFiles rotated files.
Off by default; only with the embedded database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README now covers the upgrade to 1.0.5 in its three Helm modes, the
repair of a MongoDB pod stuck on the removed Bitnami image, what tools
that render without the cluster have to do, the rollback trap and the
move to OPC Router 5.7. It states what the router needs from a cluster,
what the two volumes hold and how to size them, how pods are replaced
and what that costs, the health probes and their minimum builds,
security defaults, exact image tags, managed clusters with admission
policies, and the new extension points. The parameter tables list every
value. Corrected: the switch to the runtime image, the key of the SSH
secret, and the MongoDB redundancy mode, which never worked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CI matrix gains a values file for a managed cluster: resources on
every container, no service account permissions, an internal load
balancer with an extra port, a dedicated node pool, probes, the log
rotation and the runtime image with its embedded database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chart-testing lints values.yaml with yamllint, which rejects trailing
spaces and a missing final newline. Whitespace only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The role allows deleting any pod of the namespace, so the restart's
label selector is what keeps the auto-update to its own release. No
test noticed when the release label was dropped from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MongoDB reads the replica set key as a key file of base64 characters;
the hyphens in the placeholder would keep it from starting if the file
were ever installed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With probes on, the service also routed to the new pod while it was
still starting. During a rolling update with the bundled MongoDB, about
one in three requests in that window was refused, up to five seconds in
a row. Without that, no request failed while both routers ran, because
the old one served until its successor was ready.

service.publishNotReadyAddresses now defaults to empty: the service
routes to a router that is not ready only when the pod is replaced with
Recreate, where a rollout has a gap anyway and reaching the web
management of a runtime that is not ready matters more. An explicit
true or false still wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A rollback replaces a running MongoDB pod with one that cannot start.
Tools that render without the cluster also have to recreate the Service,
whose merged selector otherwise selects no pod. Releases from 1.0.0 and
1.0.1 move to OPC Router 5.3 unless they set image.tag. The replica set
key takes only base64 characters. The probe and update sections give the
measured behaviour of a rolling update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An upgrade adds the log folder init container to every installation
with the embedded database. Without resources it was rejected on
clusters whose admission policy demands limits, after Recreate had
already stopped the running router. It now takes the router's security
context and, unless helperResources is set, the router's resources; as
an init container it does not raise the pod's requests.

The probe comment no longer claims behaviour after a license expiry,
which was not measured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With serviceAccount.create=false the role was bound to the namespace's
default account, which every other pod without an account of its own
shares, so all of them could delete the namespace's pods. The role is
now bound only to the chart's account or to the one named in
serviceAccount.name, as chart 1.0.4 bound nothing in that case either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
etoensfeldt and others added 9 commits October 5, 2026 10:00
The README now lists every effect of the upgrade on a running release:
the replaced router and MongoDB pods, the pinned helper images, the
init container's resources, the stricter helm test and the visible
failure of a broken import. It warns against --atomic, names the
deployed revision for the repair, orders the move to 5.7 so the backup
and the import mode come first, describes Recreate and the multi-node
stall as measured, fixes the du command and the seconds of the overlap,
and explains which account the auto-update's role is bound to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With MongoDB authentication and generated passwords the MongoDB chart
refuses every upgrade until the current passwords are passed. Since no
upgrade between 1.0.x versions got past the selector before, this
message will be new to most users; the README shows how to read the
values from the release's Secret and under which keys to pass them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Service takes the kept selector from the same helper as the
Deployment, and a volume the chart creates takes its size from the
same helper as its claim. No test noticed when either second caller
stopped using its helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With claims.*.storageVolume.createVolume and the default volume type
local, the API rejected the volume, because a local volume lives on one
node and needs node affinity; the install failed after it had started.
The chart now stops before that and names the value to set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on three nodes: a volume pinned to one node brings every new
pod back to it, so rolling updates and Recreate work as on one node,
but the router cannot start while that node is unavailable. Storage
that is local to a node without node affinity hands a moved pod an
empty /data. The multi-node stall applies to volumes that move between
nodes, such as cloud disks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The probes, the service setting for pods that are not ready and their
helpers, tests and documentation leave 1.0.5; chart 2.0 brings them back
together with its other changes to how the router pod is run. Reverting
this commit restores them as they were tested. helm test keeps asking
the runtime's readiness route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chart 1.0.5 installs OPC Router 5.7, so a release without an image tag
of its own moves from 5.3 to 5.7 with the upgrade. The README now
covers the move as part of the upgrade: the backup, the embedded
database's one-way migration to MongoDB 8, Recreate so that 5.3 and 5.7
do not share the bundled database, and how to stay on 5.3.

The project import keeps its default clear, which stops the web
management of 5.5 to 5.7. The installation notes now warn whenever a
project is imported that way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The router initiates the MongoDB replica set itself when it gets there
first, and the set's only member then carries the pod's short host name,
which resolves only inside the MongoDB pod. Who wins that race is a
matter of timing. OPC Router 5.3 did not mind; 5.7 follows the member
names, finds no primary and its web management never starts, also on a
fresh install with the defaults. With directConnection=true the router
talks to the one member it was given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion

With the clear import on 5.5 to 5.7 the pod keeps running and nothing
answers; it does not restart, because the failing web management is
restarted inside the container. The upgrade notes now also say why the
router connects to the bundled MongoDB directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@etoensfeldt
etoensfeldt requested a review from BoBiene October 5, 2026 14:37
@etoensfeldt etoensfeldt self-assigned this Oct 5, 2026
The router mounts its two volumes as ReadWriteOnce whether MongoDB is
bundled, external or embedded. A rolling update starts the new pod next
to the old one: on another node it waits for volumes the old pod still
holds, and the rollout stalls for good on node-attached disks such as
Azure Disk or EBS. Until now the chart used Recreate only with the
embedded database.

Recreate is now the default in every mode. A rolling update is safe
only with a separate MongoDB and on a single node, with volumes pinned
to a node, or with volumes not attached to nodes; updateStrategy still
selects it there. An existing release switches without an API error.

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant