Skip to content

feat: single-replica ovn-central + PVC + LoadBalancer + Kamaji split-cluster deployment#6793

Open
oilbeater wants to merge 20 commits into
masterfrom
feat/kamaji-install-mode
Open

feat: single-replica ovn-central + PVC + LoadBalancer + Kamaji split-cluster deployment#6793
oilbeater wants to merge 20 commits into
masterfrom
feat/kamaji-install-mode

Conversation

@oilbeater
Copy link
Copy Markdown
Collaborator

@oilbeater oilbeater commented May 28, 2026

End goal: deploy kube-ovn on a Kamaji-style split-cluster topology.

This PR replaces #6782 (single-replica + LoadBalancer exposure) and bundles
the Kamaji installMode work originally split out, plus three follow-up
fixes from the first round of Codex review. Reviewing as one PR is easier
than coordinating two — the features are useless individually for the
target deployment shape.

What you get

  1. OVN_CENTRAL_MODE=single — ovn-central as a single-replica Deployment
    backed by a PVC. Pod can drift to another node on host failure (provided
    the StorageClass supports cross-node attach). New central-pvc.yaml,
    central-deploy.yaml strategy/affinity/volume branching, leaderless
    Service selectors, start-db.sh standalone enhancements,
    pkg/ovn_leader_checker short-circuits raft queries.
  2. ovn-central.service.type=LoadBalancer\|NodePort — expose the three
    OVN DB Services for external clients (e.g. tenant clusters).
  3. installMode=full\|controlPlaneOnly\|dataPlaneOnly — same chart serves
    three deployment shapes from one source of truth. Default full renders
    identically to master (verified by helm template diff).
  4. externalOvnCentral.{endpoint,nbPort,sbPort} — tenant data-plane
    clusters point at the management cluster's exposed ovn-nb / ovn-sb via a
    LoadBalancer / NodePort VIP, with full port-remap support.

How it composes for Kamaji

# Management cluster: ovn-central single-replica, OVN DB exposed via LB
helm install --kube-context=mgmt kube-ovn ./charts/kube-ovn \
  --set installMode=controlPlaneOnly \
  --set OVN_CENTRAL_MODE=single \
  --set 'ovn-central.storage.storageClassName=my-csi' \
  --set 'ovn-central.service.type=LoadBalancer' \
  --set 'ovn-central.service.loadBalancerIP=10.99.99.99'

# Tenant data-plane: CRDs + controller + agents pointing back at mgmt LB
helm install --kube-context=tenant kube-ovn ./charts/kube-ovn \
  --set installMode=dataPlaneOnly \
  --set externalOvnCentral.endpoint=10.99.99.99

Full walkthrough including SSL Secret sync, version lockstep, and a
failover drill is in docs/kamaji-deployment.md (with the
single-replica details in docs/single-replica-deployment.md).

Codex review fixes (last commit)

  1. Honor externalOvnCentral.{nbPort,sbPort}: previously declared but
    ignored — start-controller.sh / start-ovs.sh hardcoded 6641 / 6642
    when OVN_DB_IPS was set, so any LoadBalancer/NodePort that remapped
    ports could not connect. Scripts now read OVN_NB_PORT / OVN_SB_PORT
    env vars wired through new chart helpers.
  2. Drop ovn-central.storage.enabled footgun: replace with explicit
    existingClaim so toggling away from in-chart PVC creation doesn't
    leave the Deployment Pending forever.
  3. Gate ovs-ovn restart in restore-ovn-nb-db.sh: skip the
    kubectl rollout restart ds ovs-ovn step on controlPlaneOnly
    installs (no DS there) — print a hint to restart on the data-plane
    cluster instead.

Test plan

  • CI: existing cluster-mode e2e stays green (helm template diff shows
    default full mode unchanged vs master).
  • Kind smoke test of single-replica + LB (done locally; reported in
    the original PR thread).
  • helm template --set installMode=controlPlaneOnly renders only
    ovn-central + 3 Services + ovn-ovs SA/RBAC (+ optional TLS Secret).
  • helm template --set installMode=dataPlaneOnly --set externalOvnCentral.endpoint=<lb> --set externalOvnCentral.nbPort=31641 --set externalOvnCentral.sbPort=31642
    injects OVN_NB_PORT=31641 / OVN_SB_PORT=31642 on both controller
    and ovs-ovn DS.
  • helm template --set OVN_CENTRAL_MODE=single --set ovn-central.storage.existingClaim=foo
    skips in-chart PVC creation and claimName becomes foo.
  • bash dist/images/restore-ovn-nb-db.sh single backup.db on a
    controlPlaneOnly cluster completes 0 with the ovs-ovn restart
    cleanly skipped.

Replaces

Supersedes #6782. That PR will be closed in favor of this one.

🤖 Generated with Claude Code

oilbeater added 5 commits May 25, 2026 07:05
Introduce OVN_CENTRAL_MODE=single in the Helm chart and rework the
chart templates, startup scripts and leader-checker so ovn-central can
run as a single-replica Deployment with the OVN DB on a PVC. The pod
can drift to another node on host failure as long as the StorageClass
supports cross-node attach.

- chart: new OVN_CENTRAL_MODE value and ovn-central.storage block;
  ovnCentralReplicas / ovnCentralNodeIPs helpers; new central-pvc.yaml
  manifest; central-deploy.yaml branches replicas, strategy,
  podAntiAffinity and the host-config-ovn volume; the three ovn-nb /
  ovn-sb / ovn-northd Services drop the leader-label selector in
  single mode so the lone pod is always the endpoint;
  controller-deploy.yaml and ovsovn-ds.yaml route OVN_DB_IPS through
  the new helper so clients fall back to the Service ClusterIPs.
- start-db.sh: the existing NODE_IPS="" standalone path now also gets
  --ovn-northd-n-threads honoured and cleans stale sockets/pids so a
  drifted pod can start cleanly on a new node.
- ovn-is-leader.sh / ovn-healthcheck.sh: short-circuit raft-only logic
  in single mode (no cluster/status query, no ovn_northd lock steal);
  keep DB storage-status check + compaction.
- pkg/ovn_leader_checker: filter empty entries out of remoteAddresses
  and add a single-replica branch that just keeps the pod labelled as
  leader for nb/sb/northd and runs compaction.

Default OVN_CENTRAL_MODE remains cluster; `helm template` of the
cluster mode renders identically to before.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…e script

Wire the single-replica deployment mode into install.sh so users who
do not use Helm can opt in via ENABLE_SINGLE_REPLICA_OVN=true with
optional OVN_CENTRAL_STORAGE_CLASS / OVN_CENTRAL_PVC_SIZE overrides.

- install.sh: when ENABLE_SINGLE_REPLICA_OVN=true, override
  addresses="" / count=1 so the rest of the script naturally emits an
  empty NODE_IPS / OVN_DB_IPS. The ovn.yaml HEREDOC is parameterised
  via pre-computed YAML fragments (PVC block, leader-label selectors,
  Deployment strategy, podAntiAffinity, host-config-ovn volume), so
  the cluster-mode output is identical to before modulo one harmless
  empty line.
- restore-ovn-nb-db.sh: add a `single <backup.db>` subcommand that
  scales ovn-central to 0, spins up a busybox helper pod mounting the
  same PVC, kubectl-cp's the backup in, swaps it into place (keeping
  the previous file as .bak.<ts>) and scales back up. The existing
  cluster mode is preserved unchanged behind `cluster` (default).

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
- docs/single-replica-deployment.md: user-facing guide covering when
  to pick the new mode, StorageClass requirements (cross-node attach,
  WaitForFirstConsumer), Helm and install.sh install paths, a
  failover drill, backup/restore via restore-ovn-nb-db.sh, migration
  between modes and known limitations.
- test/e2e/single-replica: new ginkgo suite that auto-skips when the
  cluster is not in single-replica mode (detected via the ovn-nb
  Service selector) and otherwise verifies replicas=1 + Recreate +
  PVC volume, leaderless Service selectors, the pod's leader labels,
  basic subnet+pod networking, and recovery after the ovn-central
  pod is deleted.
- makefiles: register the new e2e package in e2e-build, add
  kube-ovn-single-replica-e2e and a kind-install-single-replica
  shortcut that installs against kind's default `standard`
  StorageClass.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…r NodePort

Add a `service.{type,loadBalancerIP,externalTrafficPolicy}` block under
`ovn-central` in the chart values (and the matching `OVN_CENTRAL_SERVICE_TYPE`
/ `OVN_CENTRAL_LB_IP` / `OVN_CENTRAL_EXTERNAL_TRAFFIC_POLICY` envs in
install.sh). The three Services for ovn-nb / ovn-sb / ovn-northd now honour
the chosen type so the OVN DB can be reached from data-plane components
that live outside the management cluster — for example a Kamaji-style
topology where ovn-controller / kube-ovn-cni / kube-ovn-controller run in
a separate tenant cluster and need a stable VIP to connect back to.

Default stays ClusterIP, so cluster-mode helm rendering and the existing
install.sh output are unchanged.

docs: add an "Exposing the OVN DB to data planes outside the cluster"
section to docs/single-replica-deployment.md, covering Helm values,
install.sh envs, how tenant clusters point ovs-ovn / kube-ovn-cni at the
LB via OVN_DB_IPS, and a note that SSL should be enabled before exposing
the DB outside the cluster.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Introduce a single value, `installMode`, that lets the same Helm chart serve
three different scenarios from one source of truth:

- `full` (default): everything in one cluster, identical to today.
- `controlPlaneOnly`: only ovn-central + ovn-nb/ovn-sb/ovn-northd Services and
  their RBAC. Use on the management cluster of a Kamaji-style split.
- `dataPlaneOnly`: CRDs + kube-ovn-controller + ovs-ovn + kube-ovn-cni +
  pinger + monitor and their RBAC. Use on tenant data-plane clusters; the
  agents and controller connect to the management cluster's exposed
  ovn-nb / ovn-sb via the new `externalOvnCentral.endpoint` value.

Implementation:

- `values.yaml`: new `installMode` and `externalOvnCentral.{endpoint,nbPort,
  sbPort}` keys.
- `_helpers.tpl`: new `kubeovn.renderControlPlane` / `kubeovn.renderDataPlane`
  gates; `kubeovn.ovnCentralNodeIPs` now emits `externalOvnCentral.endpoint`
  in dataPlaneOnly mode so kube-ovn-controller and ovs-ovn get
  `OVN_DB_IPS=<lb-ip>` for free.
- Templates wrapped with the appropriate gate:
  - controlPlaneOnly: central-deploy, central-pvc, nb-svc, sb-svc, northd-svc.
  - dataPlaneOnly: kube-ovn-crd, controller-deploy/svc, monitor-deploy/svc,
    ovsovn-ds, ovncni-ds/svc, pinger-ds/svc, ovn-dpdk-ds, ic-controller,
    vpc-nat-config, post-delete-hook, pre-upgrade-ovs-ovn,
    upgrade-ovs-ovn.
  - Both planes: `ovn-ovs` ServiceAccount + `system:ovn-ovs` ClusterRole +
    binding (used by ovn-central in mgmt and ovs-ovn in tenant), plus the
    optional `kube-ovn-tls` Secret manifest.
- `hack/gen-crd.sh`: wrap the generated CRD bundle with the data-plane gate
  when copying into the v1 chart so the wrapper survives regenerations.
  Idempotent (re-running gen-crd does not stack wrappers).
- `docs/kamaji-deployment.md`: full walkthrough of the two-install pattern,
  including LoadBalancer requirements, TLS Secret sync between clusters and
  version-lockstep guidance.

Default `installMode=full` renders identically to before — verified via
`helm template` diff against the previous PR's output (zero diff).

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. chart Helm Chart docs feature New network feature labels May 28, 2026
…ovs-ovn restart

Address three issues raised in code review of the Kamaji split + single-
replica work:

- externalOvnCentral.{nbPort,sbPort} were declared in values.yaml but never
  reached the agents. start-controller.sh / start-ovs.sh hardcoded 6641 /
  6642 when OVN_DB_IPS was set, so any LoadBalancer or NodePort install that
  remapped ports could not connect. The scripts now read OVN_NB_PORT /
  OVN_SB_PORT env vars (defaulting to the in-cluster Service ports), and the
  chart wires the externalOvnCentral ports into both kube-ovn-controller and
  ovs-ovn DaemonSet env via new kubeovn.ovnNbPort / kubeovn.ovnSbPort
  helpers.

- The previous `ovn-central.storage.enabled` toggle could be flipped off
  while the Deployment still mounted a PVC named "ovn-central-data", leaving
  ovn-central permanently Pending. Replace the boolean with an explicit
  `existingClaim` field: when empty (default) the chart creates the PVC;
  when set, in-chart PVC creation is skipped and the Deployment mounts the
  named claim instead. Removes the silent footgun and gives operators a
  clean handoff to externally-managed storage.

- restore-ovn-nb-db.sh's single-mode flow ran `kubectl rollout restart ds
  ovs-ovn` unconditionally. In a controlPlaneOnly management-cluster install
  there is no ovs-ovn DaemonSet, so the restore exited non-zero after a
  successful DB restore. Gate the restart on `kubectl get ds ovs-ovn` and
  print a hint to restart ovs-ovn on the data-plane cluster instead.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
@oilbeater oilbeater changed the base branch from feat/ovn-central-single-replica to master May 28, 2026 02:56
@oilbeater oilbeater changed the title feat(chart): add installMode for Kamaji split-cluster deployment feat: single-replica ovn-central + PVC + LoadBalancer + Kamaji split-cluster deployment May 28, 2026
@coveralls
Copy link
Copy Markdown

coveralls commented May 28, 2026

Coverage Report for CI Build 26581686879

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage increased (+0.2%) to 25.488%

Details

  • Coverage increased (+0.2%) from the base build.
  • Patch coverage: 38 uncovered changes across 1 file (0 of 38 lines covered, 0.0%).
  • 174 coverage regressions across 1 file.

Uncovered Changes

File Changed Covered %
pkg/ovn_leader_checker/ovn.go 38 0 0.0%

Coverage Regressions

174 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
pkg/controller/vpc_egress_gateway.go 174 10.25%

Coverage Stats

Coverage Status
Relevant Lines: 57556
Covered Lines: 14670
Line Coverage: 25.49%
Coverage Strength: 0.3 hits per line

💛 - Coveralls

Second round of review fixes for the Kamaji split / single-replica work:

- upgrade-ovs.sh and start-ovs-dpdk-v2.sh both still assume a local
  ovn-central (the former waits on `deploy/ovn-central`, the latter pulls
  OVN_SB_SERVICE_HOST from Kubernetes env injection). Rendering them in a
  dataPlaneOnly tenant install would crashloop or block upgrades. Add a new
  `kubeovn.renderFullOnly` helper and gate the two ovs-ovn upgrade hooks
  and the ovn-dpdk DaemonSet behind it. controlPlaneOnly installs naturally
  skip them already; dataPlaneOnly users who need DPDK or in-place OVS
  upgrades will need a follow-up that teaches those scripts to honor
  externalOvnCentral.

- Tighten the docs/values comments around `externalOvnCentral.endpoint`:
  the OVN connection string format `tcp:[host]:port` is fragile against
  older OVN parsers when `host` is a DNS hostname, so recommend an IP
  literal (recent OVN 22.03+ tolerates hostnames). Note the DPDK / upgrade-
  hook follow-up explicitly in docs/kamaji-deployment.md.

Note: ovs-healthcheck.sh's `gen_conn_str 6642` reference is dead code
(only present inside a commented-out echo). The probe actually talks to
local OVS sockets, so it does not need port remapping changes.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Copilot AI review requested due to automatic review settings May 28, 2026 03:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the kube-ovn Helm chart and supporting scripts to support Kamaji-style split-cluster deployments by (1) enabling a single-replica ovn-central mode backed by a PVC, (2) optionally exposing OVN DB Services via LoadBalancer/NodePort, and (3) introducing installMode to render control-plane-only vs data-plane-only subsets from the same chart.

Changes:

  • Add OVN_CENTRAL_MODE=single with PVC-backed ovn-central (Recreate strategy, leader-less Service selectors, standalone-aware health/leader logic).
  • Add installMode=full|controlPlaneOnly|dataPlaneOnly with Helm render gates and CRD gating for tenant clusters.
  • Wire externalOvnCentral.{endpoint,nbPort,sbPort} through chart helpers and scripts so external NB/SB port remaps work; add docs + a dedicated single-replica e2e suite/targets.

Reviewed changes

Copilot reviewed 33 out of 40 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test/e2e/single-replica/single_replica_test.go Adds e2e coverage for single-replica ovn-central + PVC + leader-less Services + recovery.
pkg/ovn_leader_checker/ovn.go Skips raft-only leader checks in single-replica (no peers) mode; filters empty remote addresses.
makefiles/kind.mk Adds a kind install target for single-replica mode.
makefiles/e2e.mk Builds/runs new single-replica e2e suite.
hack/gen-crd.sh Wraps v1 chart CRD bundle with a data-plane render gate for installMode.
docs/single-replica-deployment.md Documents single-replica mode and external exposure patterns.
docs/kamaji-deployment.md Documents split-cluster (management/control-plane vs tenant/data-plane) deployment flow.
dist/images/start-ovs.sh Honors OVN_SB_PORT when generating SB connection strings for external ovn-central.
dist/images/start-db.sh Adds standalone-mode cleanup for drifted pods; threads flag for northd restart.
dist/images/start-controller.sh Honors OVN_NB_PORT/OVN_SB_PORT for external ovn-central connections.
dist/images/restore-ovn-nb-db.sh Adds single restore mode that writes a standalone backup into the PVC; skips ovs-ovn restart when absent.
dist/images/ovn-is-leader.sh Adds standalone-mode labeling + storage-status checks and compaction.
dist/images/ovn-healthcheck.sh Adds standalone-mode storage-status readiness checks (no raft cluster/status).
dist/images/install.sh Adds single-replica mode + Service exposure knobs; injects PVC + strategy/selector variants.
charts/kube-ovn/values.yaml Introduces OVN_CENTRAL_MODE, installMode, externalOvnCentral, ovn-central storage/service settings.
charts/kube-ovn/templates/vpc-nat-config.yaml Gates data-plane-only resource rendering via installMode.
charts/kube-ovn/templates/upgrade-ovs-ovn.yaml Gates full-only upgrade hook rendering.
charts/kube-ovn/templates/sb-svc.yaml Adds control-plane render gate; supports LB/NodePort; drops leader selector in single mode.
charts/kube-ovn/templates/pre-upgrade-ovs-ovn.yaml Gates full-only pre-upgrade hook rendering.
charts/kube-ovn/templates/post-delete-hook.yaml Gates data-plane render for post-delete hook resources.
charts/kube-ovn/templates/pinger-svc.yaml Gates data-plane rendering for pinger Service.
charts/kube-ovn/templates/pinger-ds.yaml Gates data-plane rendering for pinger DaemonSet.
charts/kube-ovn/templates/ovsovn-ds.yaml Gates data-plane rendering; wires external ovn-central endpoint + SB port.
charts/kube-ovn/templates/ovncni-svc.yaml Gates data-plane rendering for CNI Service.
charts/kube-ovn/templates/ovncni-ds.yaml Gates data-plane rendering for CNI DaemonSet.
charts/kube-ovn/templates/ovn-sa.yaml Partitions ServiceAccounts by installMode (data-plane vs both-planes).
charts/kube-ovn/templates/ovn-dpdk-ds.yaml Gates full-only DPDK DaemonSet rendering.
charts/kube-ovn/templates/ovn-CRB.yaml Partitions ClusterRoleBindings/RoleBindings by installMode.
charts/kube-ovn/templates/ovn-CR.yaml Partitions ClusterRoles by installMode.
charts/kube-ovn/templates/northd-svc.yaml Adds control-plane render gate; LB/NodePort support; drops leader selector in single mode.
charts/kube-ovn/templates/nb-svc.yaml Adds control-plane render gate; LB/NodePort support; drops leader selector in single mode.
charts/kube-ovn/templates/monitor-svc.yaml Gates data-plane rendering for monitor Service.
charts/kube-ovn/templates/monitor-deploy.yaml Gates data-plane rendering for monitor Deployment.
charts/kube-ovn/templates/kube-ovn-crd.yaml Gates CRDs to render only in data-plane installs.
charts/kube-ovn/templates/ic-controller-deploy.yaml Gates IC controller to data-plane rendering.
charts/kube-ovn/templates/controller-svc.yaml Gates controller Service to data-plane rendering.
charts/kube-ovn/templates/controller-deploy.yaml Gates controller Deployment; wires external ovn-central endpoint + NB/SB ports.
charts/kube-ovn/templates/central-pvc.yaml Adds PVC template for single-replica ovn-central with optional existingClaim.
charts/kube-ovn/templates/central-deploy.yaml Adds control-plane render gate; single-mode replicas/strategy/affinity/volume branching.
charts/kube-ovn/templates/_helpers.tpl Adds helpers for installMode gating, ovn-central replicas/endpoints, and external NB/SB ports.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +47 to +54
ovn-central:
storage:
enabled: true
storageClassName: my-csi # leave empty to use the cluster default
size: 10Gi
accessModes:
- ReadWriteOnce
```
Comment on lines +118 to +123
ovn-central:
storage:
enabled: true
storageClassName: my-csi
size: 10Gi
service:
Comment thread docs/single-replica-deployment.md Outdated
Comment on lines +160 to +162
A full Kamaji integration also needs a "data-plane only" rendering of the
chart that skips `ovn-central` and its PVC — that's tracked separately and
not part of this change.
Comment thread docs/kamaji-deployment.md
Comment on lines +65 to +70
ovn-central:
storage:
enabled: true
storageClassName: my-csi
size: 10Gi
service:
Comment thread charts/kube-ovn/values.yaml Outdated
Comment on lines +204 to +208
# Set existingClaim to skip in-chart PVC creation and point the
# Deployment at a PVC the operator manages out-of-band (e.g. via a
# storage operator). The Deployment always mounts claim "ovn-central-data"
# so the value here must equal "ovn-central-data" — leaving the field
# empty means the chart creates the PVC itself.
oilbeater added 12 commits May 28, 2026 03:41
… name

Third round of review fixes for the Kamaji split:

- `kubeovn.ovnCentralNodeIPs` now uses Helm's `required` to fail at
  template-time when `installMode=dataPlaneOnly` but `externalOvnCentral.
  endpoint` is empty. Without that guard, agents fell back to
  `OVN_NB_SERVICE_HOST` (which is not injected when the local ovn-nb
  Service is not rendered) and crashed under `set -u`.

- `ic-controller-deploy.yaml` was still injecting `OVN_DB_IPS` from the
  tenant cluster's own master nodes, and `start-ic-controller.sh` was
  hardcoding ports 6641/6642. Route both through the new
  `kubeovn.ovnCentralNodeIPs` / `kubeovn.ovnNbPort` / `kubeovn.ovnSbPort`
  helpers and have the script honor `OVN_NB_PORT` / `OVN_SB_PORT`. With
  this, `installMode=dataPlaneOnly` + `ENABLE_IC=true` actually points
  the ic-controller at the management cluster's exposed OVN DB.

- `restore-ovn-nb-db.sh single` discovers the PVC name from the
  ovn-central Deployment's `host-config-ovn` volume rather than
  hardcoding `ovn-central-data`. Operators using
  `ovn-central.storage.existingClaim=<custom-name>` can now run the
  bundled restore flow against the right PVC.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…talLB annotation

Two more review fixes:

- When `installMode=dataPlaneOnly` + `networking.ENABLE_SSL=true` the
  chart would self-sign a fresh kube-ovn-tls Secret on the tenant
  cluster, producing certs that the management cluster's ovn-central
  does not trust. Fail at template-time with a clear message telling
  the operator to sync the Secret from the management cluster first.
- The three OVN Services share `ovn-central.service.loadBalancerIP`
  but cloud LB providers without VIP sharing assign each Service its
  own IP, breaking the assumption that one externalOvnCentral.endpoint
  can reach all three databases. Emit the MetalLB
  `allow-shared-ip: kube-ovn-central` annotation on all three Services
  when serviceType=LoadBalancer; this is a no-op on non-MetalLB
  providers but lets the common MetalLB case work without extra
  configuration. Document the constraint and the cloud-LB workaround
  (NodePort fronted by an external LB) in docs/kamaji-deployment.md.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…own on PVC

Three more review fixes for the combined PR:

- `dist/images/install.sh`: when `ENABLE_SINGLE_REPLICA_OVN=true` the
  precompute step intentionally sets `addresses=""`, but the generated
  ovn.yaml emitted unquoted `value: $addresses` for `NODE_IPS` and
  `OVN_DB_IPS`. The empty expansion produces YAML `value:` (null), which
  Kubernetes rejects for string-typed env vars and the apply step fails.
  Wrap all five sites in `"$addresses"` so empty becomes `""`.

- `install.sh` also did not emit the MetalLB `allow-shared-ip` annotation
  that the chart now adds in `OVN_CENTRAL_SERVICE_TYPE=LoadBalancer`
  installs. Without it, only one of ovn-nb/sb/northd could claim the
  same `loadBalancerIP`. Wire a new `${OVN_CENTRAL_SVC_ANNOTATIONS}`
  precompute block into the three Service manifests.

- `charts/kube-ovn/templates/central-deploy.yaml`: the `hostpath-init`
  init container `chown -R nobody: ... /etc/ovn ...` fails on PVC backends
  that disallow chown (notably root-squashed NFS, which the docs
  recommend for single-replica drift). In single-replica mode, run the
  chown on `/var/run/ovn` and `/var/log/ovn` strictly and make the
  `/etc/ovn` chown best-effort: if the backend refuses, log a hint and
  continue. The main container runs as `nobody` and will create new
  files with the correct owner regardless.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
externalOvnCentral.endpoint is a single IP, so the three OVN Services
(ovn-nb / ovn-sb / ovn-northd) must land on the same VIP for the tenant
cluster to reach all of NB/SB/northd. With type=LoadBalancer but no
explicit loadBalancerIP, cloud LB controllers allocate three different
external IPs and the design silently breaks.

Add a kubeovn.validateService helper that fails at template time when
serviceType=LoadBalancer and loadBalancerIP is empty. Include it in the
three svc templates. The fail message tells the operator to pick a VIP
and notes that the MetalLB allow-shared-ip annotation is emitted
automatically once they do.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…ener

Two install.sh-side parity fixes matching what the Helm chart already does:

- Reject OVN_CENTRAL_SERVICE_TYPE=LoadBalancer without OVN_CENTRAL_LB_IP.
  externalOvnCentral.endpoint is a single IP, so the three OVN Services
  must share one VIP; without an explicit IP cloud LB controllers allocate
  three different ones and tenants can only reach NB or SB. The chart
  already fails in this case via `kubeovn.validateService`; install.sh
  now matches.

- In ENABLE_SINGLE_REPLICA_OVN mode, soften the hostpath-init container's
  chown of /etc/ovn. Some PVC backends (notably root-squashed NFS, which
  the docs recommend for cross-node drift) reject root chown, leaving the
  pod stuck in Init:CrashLoopBackOff. Run the chown on /var/run/ovn and
  /var/log/ovn strictly and make /etc/ovn best-effort, mirroring the
  branch already added to charts/kube-ovn/templates/central-deploy.yaml.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…elper

Final round of review fixes:

- Inject \`helm.sh/resource-policy: keep\` on every CRD so flipping
  \`installMode\` (e.g. \`full\` -> \`controlPlaneOnly\`) does not let
  Helm cascade-delete the live custom resources. The annotation is
  emitted by \`hack/gen-crd.sh\` when wrapping the bundle, so it
  survives every regeneration.

- \`central-deploy.yaml\` now \`lookup\`s the existing ovn-central
  Deployment and fails the upgrade if its host-config-ovn volume type
  disagrees with the rendered \`OVN_CENTRAL_MODE\` (raft hostPath <->
  single PVC). In-place mode switches would otherwise start ovn-central
  against an empty target and silently lose all live OVN state. Lookup
  returns empty during dry-run / template, so this only fires against
  real clusters.

- \`restore-ovn-nb-db.sh\` single-mode helper pod now adds
  ovn-central's tolerations so it can schedule onto the same
  control-plane / master node that hosts the PVC; without them
  \`kubectl wait --for=condition=Ready\` would deadlock on
  single-replica installs whose only viable node is tainted.

- The helper pod also chowns the restored DB to uid/gid 65534
  (\`nobody\`) before exiting. \`kubectl cp\` extracts the tarball as
  the container's uid (root in busybox), and on PVC backends where the
  init-container chown is intentionally best-effort (root-squashed
  NFS), ovn-central came back as nobody unable to write its own DB.
  The chown is itself best-effort with a clear log line on backends
  that map root to nobody (NFS root_squash).

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Two follow-ups from code review:

- kube-ovn-monitor reads ovn-central's local Unix sockets at
  /run/ovn/ovn{nb,sb}_db.sock and the DB files at /etc/ovn/*.db, and
  TryClientConnection exits the process after 5 retries. In
  installMode=dataPlaneOnly there is no local ovn-central, so the
  Deployment would crashloop on every tenant cluster. Move monitor-
  deploy.yaml and monitor-svc.yaml to the renderFullOnly gate. The
  kube-ovn-app SA / ClusterRole / RoleBinding stay in renderDataPlane
  because kube-ovn-pinger also uses them. Add a Kamaji-doc note + a
  follow-up in the limitations section.

- hack/gen-crd.sh injected a NEW `metadata.annotations:` block for each
  CRD, which produced two annotations mappings per CRD. YAML loaders
  keep one of those, so the helm.sh/resource-policy: keep annotation
  was getting dropped during render. Merge the new key into the
  existing controller-gen annotations block instead, so the rendered
  YAML carries both keys on every CRD. Verified with yaml.safe_load on
  the helm template output: 24/24 CRDs have keep: yes.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…make restore image configurable

Three follow-up review fixes:

- Add helm.sh/resource-policy: keep to the ovn-central-data PVC. Without
  it, an installMode switch (e.g. controlPlaneOnly with
  OVN_CENTRAL_MODE=single -> dataPlaneOnly) drops the PVC out of the
  rendered manifest and Helm deletes the only copy of the standalone
  OVN DB. Same data-loss class as the central-deploy guard already
  added.

- pkg/ovn_leader_checker classified a one-node raft cluster as
  "single-replica" because `remoteAddresses` collapses to an empty
  slice once the local address is filtered out, which silently skipped
  raft-header backup, isDBLeader queries, and ovn_northd lock
  stealing. Compute singleReplica from the raw --remoteAddresses flag
  (true only when it was passed explicitly empty) and store on
  Configuration. One-node raft installs now keep their full
  cluster-mode behaviour.

- restore-ovn-nb-db.sh's single-mode helper pod hardcoded
  busybox:1.36, which breaks in air-gapped or private-registry
  environments. Read an optional RESTORE_HELPER_IMAGE env override and
  default to docker.io/library/busybox:1.36 so the operator can swap
  in any minimal Linux image they already have mirrored.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…l.sh mode switch

Two final review fixes:

- ovn-tls-secret.yaml previously rendered the Secret manifest in all SSL
  installs. In installMode=dataPlaneOnly the operator pre-seeds the
  Secret out-of-band (synced from the management cluster), so Helm tries
  to create or adopt it and fails with "resource already exists". Skip
  the manifest entirely in dataPlaneOnly mode and keep only the
  fail-fast guard that requires the pre-seeded Secret to exist before
  install. Other install modes still render the Secret (auto-generated
  CA in cluster mode, reused from existing in full mode).

- install.sh now mirrors the chart's Deployment-level guard against
  in-place OVN_CENTRAL_MODE switches. It looks up the existing
  ovn-central Deployment's host-config-ovn volume and rejects flipping
  ENABLE_SINGLE_REPLICA_OVN if that would swap hostPath <-> PVC
  underneath a running release. Operators are told to back up, delete
  the existing Deployment, and re-run install.sh.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Address Copilot review on PR #6793:

- docs/single-replica-deployment.md (both Helm examples) and
  docs/kamaji-deployment.md (management-cluster values example): drop
  the obsolete `ovn-central.storage.enabled` field. That toggle was
  removed when existingClaim was introduced; the docs still referenced
  it, so any user copy-pasting would get a non-functional values file.
- docs/single-replica-deployment.md "follow-up" paragraph claiming a
  data-plane-only chart rendering is tracked separately is no longer
  true — this PR ships it. Replace the paragraph with a pointer to
  docs/kamaji-deployment.md.
- values.yaml comment on `ovn-central.storage.existingClaim`
  contradicted itself (claimed the value must be "ovn-central-data"
  while the Deployment template actually mounts whatever name is set).
  Reword to match the template behaviour: the field is the name of an
  external PVC; empty means the chart creates a default-named one.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Discovered while bringing up a real Kamaji-style local environment
(kind + Kamaji + tenant docker worker via kubeadm join). On the tenant
cluster, controller-deploy.yaml's `replicas: {{ include "kubeovn.
nodeCount" . }}` triggered the helper's `fail` because tenant nodes
don't carry the `kube-ovn/role=master` label (Kamaji moves the control
plane into the management cluster as pods).

Add a kubeovn.controllerReplicas helper:
- dataPlaneOnly: default to 1 (active/standby HA is by leader election,
  not horizontal scale)
- everywhere else: keep nodeCount behaviour
- operators can override via `kube-ovn-controller.replicas`

controller-deploy.yaml now uses this helper. Verified end-to-end:
`helm install --kubeconfig=tenant-kc -f tenant-values.yaml` now
succeeds, the controller Deployment + ovs-ovn DS + kube-ovn-cni DS land
on the tenant worker, and the tenant worker's TCP path to the
management cluster's MetalLB VIP `172.18.255.210:6641/:6642` is open.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Turn the manual Kamaji-on-kind walkthrough into a reproducible automated
e2e. The local validation covered the only flow that exercises both
halves of installMode together (controlPlaneOnly + dataPlaneOnly +
externalOvnCentral.endpoint), and CI/contributors need the same path.

- hack/kamaji-e2e.sh: subcommands `setup`, `teardown`, `kubeconfig`,
  `vars`. Brings up the mgmt kind cluster, installs cert-manager,
  MetalLB and Kamaji, installs kube-ovn `installMode=controlPlaneOnly`
  with single-replica + LoadBalancer + storage, creates a
  TenantControlPlane via Kamaji, spawns a privileged docker container
  for the tenant worker (with containerd's native snapshotter to dodge
  nested-overlayfs whiteout issues), pre-pulls the kube-ovn image via a
  local registry, joins the worker via kubeadm and installs
  `installMode=dataPlaneOnly` pointed at the management cluster's
  MetalLB VIP.

- test/e2e/kamaji/kamaji_test.go: a small Ginkgo suite that auto-skips
  when not running against a dataPlaneOnly tenant cluster. It verifies:
  (1) kube-ovn-controller defaults to replicas=1 (covers the
  kubeovn.controllerReplicas helper); (2) ovs-ovn really keeps an
  ESTAB connection to the external ovn-sb VIP via `ss` exec;
  (3) tenant Pods get IPs from the OVN subnet through the cross-cluster
  control path; (4) leader-election Lease lives on the tenant
  apiserver.

- makefiles/kind.mk: new `kind-install-kamaji` / `kind-clean-kamaji`
  targets that wrap the shell script.

- makefiles/e2e.mk: register `./test/e2e/kamaji` in `e2e-build` and add
  `kube-ovn-kamaji-e2e` target. The target sources KUBECONFIG and the
  KUBE_OVN_KAMAJI_MGMT_VIP env from `hack/kamaji-e2e.sh vars` so the
  Ginkgo suite can identify the external VIP without rediscovering it.

Reproducing locally:
  make build-dev
  make kind-install-kamaji
  make kube-ovn-kamaji-e2e
  make kind-clean-kamaji

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
…lash

CI is failing on every job at the kube-ovn-cni daemonset rollout. Root
cause: Kubernetes auto-injects environment variables for in-namespace
Services using the pattern `<SVCNAME>_PORT=tcp://<ClusterIP>:<port>`.
The Service named `ovn-nb` therefore implicitly creates
`OVN_NB_PORT=tcp://10.x.x.x:6641` in every kube-system pod's env, which
collided with the new variable I introduced in this PR for the
external-endpoint port. kube-ovn-controller then built a garbage
connection string like

    --ovn-nb-addr=tcp:[172.18.0.3]:tcp://10.110.227.251:6641

so the OVN agents never came up.

Rename the variable everywhere to `KUBE_OVN_NB_PORT` /
`KUBE_OVN_SB_PORT` — the kube-ovn project prefix is unique and avoids
the kubelet's auto-injected names. Touch points:

- dist/images/start-controller.sh
- dist/images/start-ic-controller.sh
- dist/images/start-ovs.sh
- charts/kube-ovn/templates/controller-deploy.yaml
- charts/kube-ovn/templates/ic-controller-deploy.yaml
- charts/kube-ovn/templates/ovsovn-ds.yaml

The Helm-internal helper names (`kubeovn.ovnNbPort` /
`kubeovn.ovnSbPort`) stay unchanged; only the env vars they generate
into pods got renamed. The defaults (6641/6642) still mean
in-cluster Service mode for non-Kamaji installs.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chart Helm Chart docs feature New network feature size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants