# overlays/dc-ha-scaleup.yaml
#
# D-121 (ADOPTED IN PART, 2026-07-15) VR1 HA scale-up overlay -- makes the
# decorative single-unit control plane REAL by scaling 14 services from
# num_units: 1 to 3. VR1-SPECIFIC: VR0/testcloud stays single-unit BY DESIGN
# (D-009), so this is an overlay, NOT a bundle.yaml edit -- same pattern as
# overlays/dc-dc-ipv6-family-matrix.yaml.
#
# APPLIED SEPARATELY, AFTER the per-DC set -- NOT in the same command as the VIP
# overlay. R6 (RULED 2026-07-27, docs/design-decisions.md): operator utterance "Close
# the two VIP gaps first, then apply the overlay whole", and the ruling's own sequencing
# line, "R11 is therefore a hard Stage-5 precondition, sequenced BEFORE the overlay, not
# a parallel item". The two gaps are vault and designate; R11 landed their VIPs (.61 /
# .62) in the per-DC vips overlays. Merged into ONE command the scale-up and the VIPs
# arrive together and the ruled ordering is not observable, which is exactly the shape
# R6 refused (option (b): a 3-node pacemaker cluster for the CA with no VIP to manage).
#
#   Step 1 -- deploy the per-DC set, let it settle:
#     juju deploy ./bundle.yaml \
#       --overlay "overlays/${DC}-vips.yaml" \
#       --overlay "overlays/${DC}-machines.yaml" \
#       --overlay "overlays/${DC}-octavia-pki.yaml" \
#       -m "${DC_MODEL}"
#   Step 2 -- only then, this overlay, whole:
#     juju deploy ./bundle.yaml \
#       --overlay "overlays/${DC}-vips.yaml" \
#       --overlay "overlays/${DC}-machines.yaml" \
#       --overlay "overlays/${DC}-octavia-pki.yaml" \
#       --overlay overlays/dc-ha-scaleup.yaml \
#       -m "${DC_MODEL}"
#
# FILENAMES CORRECTED 2026-07-29 (NEW-4 / NEW-6). The command above previously named
# two files that cannot be passed as written:
#   - `overlays/octavia-pki.yaml` -- RENAMED PER-DC under the D-109 amendment to
#     `overlays/${DC}-octavia-pki.yaml` (.gitignore:41-46 documents the rename and
#     covers it by GLOB precisely so each per-DC file stays ignored).
#   - `overlays/${DC}-hostnames.yaml` -- HAS NEVER BEEN AUTHORED. It is proposed D-106
#     hostname work (bundle.yaml:13-18 records it as proposed, not built) and
#     `os-public-hostname` remains unset everywhere; bundle.yaml's B5 IP-ONLY posture
#     stands. juju errors out on a missing overlay file, so a phantom name inside a
#     runnable command is a live failure, not a documentation nit. Removed rather than
#     renamed -- there is nothing to point it at.
#
# NOT YET APPLIED to any live model -- authored prep-only (STAGE 5 prep; the
# per-DC bundle deploy is two stages out). No live cloud reached this session.
#
# =====================================================================
# ##### RENDERED 2026-07-24 -- tokens bound to logical control ids 0/1/2 #####
# The three {{VR1_DC0_CONTROL_0N}} tokens are RENDERED to the bundle.yaml machines
# block's LOGICAL control ids (0,1,2 -- the 3 control nodes, D-121 Option C). These
# ids are DC-neutral (both dc0 and dc1 use the same logical machines block; the DC
# tag is applied per-DC via overlays/vr1-dc1-machines.yaml), so this overlay is now
# DC-agnostic. The positional 3-entry `to:` lists give one-per-control-node
# anti-affinity (unit0->0, unit1->1, unit2->2); provider-bundle-check.py enforces the
# 3-distinct-machine spread on the MERGED bundle, so a token/id collision is caught.
# =====================================================================
#
# PLACEMENT / ANTI-AFFINITY (D-121 balance rule). Juju bundles have NO dedicated
# anti-affinity primitive; one-per-node is expressed POSITIONALLY -- a 3-entry
# `to:` list binds unit0->entry0, unit1->entry1, unit2->entry2. Every scaled app
# below uses the SAME three-control-node spread, so:
#   - each app lands exactly one unit per control node (14 apps x 1 = ~14 API
#     LXD containers per control node -- the even ~14/node density the 64 GiB
#     control-node sizing was validated for, D-121); and
#   - the RAM-heavy stateful trio (mysql-innodb-cluster already 3 in the base
#     bundle, rabbitmq-server + vault scaled here) auto-balances to one replica
#     per control node -- never two stacked on one node.
#
# THREE ITEMS BELOW GO BEYOND A LITERAL `num_units: 3` BUMP. They are NOT
# scope-creep: each is the same class of "mechanical 1->3 hides a gotcha" that
# D-121 itself flags (D-121 caught the placement rewrite; these are the two it
# under-states plus the one its own wording got wrong), and an overlay titled
# "make HA real" that shipped decorative HA would not meet its stated purpose.
# All three are called out in the delivery changelog, not slipped in:
#   (A) rabbitmq-server min-cluster-size: 3. Per D-009 AMENDMENT (2026-07-02):
#       "rabbitmq scale-up is NOT mechanical without min-cluster-size" -- without
#       it the charm accepts client relations BEFORE the cluster forms (same race
#       as the D-062 mysql formation failure). Roosevelt/VR1 delta = set it
#       alongside num_units: 3. rabbitmq clusters natively (quorum), NO hacluster.
#   (B) hacluster cluster_count: 3 (all 12 active hacluster subs + the re-added
#       vault-hacluster). The base bundle pins cluster_count: 1 -- explicitly
#       "decorative on single-unit testcloud" (D-009 / BUNDLEFIX-003). At 3 units
#       with cluster_count still 1, pacemaker can bootstrap the VIP off a single
#       node = the SAME decorative behavior D-121 exists to eliminate. 3 makes
#       the charm wait for a real 3-node quorum before configuring resources.
#       VERIFY at deploy (not fetched this session): confirm the hacluster
#       2.4/stable charm treats cluster_count as "peers required before the
#       cluster forms / VIP binds" -- the base bundle explicitly pinned it to 1
#       as decorative, and real 3-node quorum needs 3 regardless of the charm
#       default. Flag if `juju config hacluster` disagrees.
#   (C) vault-hacluster is DEFINED here, not just related. D-121's prose says it
#       "exists among the hacluster subs" -- it does NOT: bundle.yaml line ~923
#       has it COMMENTED OUT (removed by BUNDLEFIX-002 when vault was de-HA'd,
#       C1). So the re-added `[vault:ha, vault-hacluster:ha]` relation would
#       reference a non-existent app (provider-bundle-check rule 4 would FAIL).
#       This overlay re-declares it, matching the other haclusters' shape.
#
# VAULT BACKEND (D-121 sub-ruling = v-a, RESOLVED 2026-07-15): vault stays
# MYSQL-backed (on the already-3-unit mysql-innodb-cluster via vault-mysql-router,
# unchanged) and scales to 3 -- NO etcd, NO Raft here. GATE (Stage 5, before
# commit): verify the deployed vault 1.8/stable charm actually does HA
# leader-election on the MySQL storage backend at 3 units. If it does NOT: do
# NOT fall back to etcd -- evaluate Raft integrated storage and route to D-068.
# 3-unit vault ALSO triples the manual-unseal burden (intersects OPEN SEC-003).
#
# NOT changed here (deliberately, per D-121): mysql-innodb-cluster / ovn-central
# / ceph-mon (already 3 in the base bundle); ceph-osd / nova-compute (scale-out,
# sized to node count -- Option C = ceph-osd 4 (D-121 R-3 amendment), nova-compute 2, on the storage /
# compute nodes, NOT control-node containers); designate-bind /
# glance-simplestreams-sync unchanged.
#
# memcached WAS in that "unchanged" list and is now SCALED (see its block below,
# added 2026-07-31 by operator direction). This comment is RE-POINTED rather than
# left stale -- it is the only place the exclusion set is written down.
#
# >>> ceph-rbd-mirror STAYS 1, AND THAT IS PINNED, NOT SETTLED. <<<
# D-108 rules it: "one rbd-mirror daemon per DC ... daemon HA is single-unit for VR1
# (matches the D-104 controller economy), NOTED FOR ROOSEVELT SCALE-UP". So the
# single unit is a ruled VR1 scope decision, not an oversight -- but D-108 itself
# flags that it must be revisited, and operator direction 2026-07-31 is that it be
# brought into line with the rest of the HA posture rather than quietly left behind.
# It is NOT a num_units edit: see docs/dc-dc-deployment-workflow.md gap register
# item 22 for the options, the recommendation, and why this one needs a D-108
# amendment before anything is built. DO NOT scale it here without that ruling.
# =====================================================================

applications:

  # ---- Clustered API charms (hacluster + dual VIP already wired in base) ----
  # Each: 1 -> 3 units, one per control node (positional anti-affinity).
  keystone:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  glance:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  neutron-api:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  nova-cloud-controller:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  placement:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  cinder:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  openstack-dashboard:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  octavia:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  barbican:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  magnum:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  designate:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  ceph-radosgw:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  # ---- RabbitMQ: native quorum clustering, NO hacluster. min-cluster-size is
  #      MANDATORY at multi-unit (D-009 AMENDMENT 2026-07-02, item A above). ----
  rabbitmq-server:
    num_units: 3
    options:
      min-cluster-size: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  # ---- memcached: 1 -> 3, ADDED 2026-07-31 by operator direction. It was left at 1
  #      by D-121's sizing table, which listed it as "memcached=1 (cache; optional 3)"
  #      -- CONSIDERED and marked optional, but with no recorded reason for choosing 1.
  #      MEASURED consumers in bundle.yaml: nova-cloud-controller:memcache and
  #      designate:coordinator-memcached. (An earlier claim in-session that keystone
  #      consumes it was WRONG -- keystone holds no memcache relation in this bundle.)
  #      NO hacluster and NO vip, deliberately: memcached is not clustered. Three units
  #      are three INDEPENDENT caches; clients receive the full server list over the
  #      relation and hash keys across it, so losing one costs that shard's hit rate,
  #      not availability. That is the standard model, not a compromise.
  #      VERIFY AT DEPLOY: the consumers pick up all three servers (the relation should
  #      yield three memcached_servers entries, not one).
  memcached:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  # ---- Vault: MySQL-backed (v-a), scale to 3, re-HA'd via vault-hacluster
  #      (defined below) + the relation re-added at the foot of this file. ----
  vault:
    num_units: 3
    to: ["lxd:0", "lxd:1", "lxd:2"]

  # ---- vault-hacluster: RE-DECLARED (item C above). Removed by BUNDLEFIX-002
  #      when vault was de-HA'd; D-121 (v-a) brings it back. Shape matches the
  #      other hacluster subs in bundle.yaml, with cluster_count: 3 (real HA).
  #      It carries an explicit `base` for the same reason all 56 applications in
  #      bundle.yaml do (RULED 2026-07-31, GA-R5 option D; provider-bundle-check
  #      invariant 12): this is the ONLY application any overlay INTRODUCES, so
  #      without it this later deploy step re-opens the once-only-deployable trap
  #      that invariant 12 exists to close. Found by that invariant on its first
  #      run, in a file the fix was not looking at. ----
  vault-hacluster: {base: ubuntu@22.04/stable, charm: hacluster, channel: 2.4/stable, options: {cluster_count: 3}, bindings: {'': metal-admin, ha: metal-internal, hanode: metal-internal, pacemaker-remote: metal-internal, peer-availability: metal-internal}}

  # ---- cluster_count: 1 -> 3 for the 12 active hacluster subs (item B above).
  #      Overlay apply deep-merges options per-key -- the SAME mechanism
  #      dc-dc-ipv6-family-matrix.yaml already relies on (it supplies only
  #      `options:` for keystone et al. and depends on charm/channel/bindings
  #      surviving from base). So ONLY cluster_count is overridden here; each
  #      sub's base bindings/charm/channel are preserved. ----
  keystone-hacluster: {options: {cluster_count: 3}}
  glance-hacluster: {options: {cluster_count: 3}}
  neutron-api-hacluster: {options: {cluster_count: 3}}
  nova-cloud-controller-hacluster: {options: {cluster_count: 3}}
  placement-hacluster: {options: {cluster_count: 3}}
  openstack-dashboard-hacluster: {options: {cluster_count: 3}}
  cinder-hacluster: {options: {cluster_count: 3}}
  octavia-hacluster: {options: {cluster_count: 3}}
  barbican-hacluster: {options: {cluster_count: 3}}
  magnum-hacluster: {options: {cluster_count: 3}}
  ceph-radosgw-hacluster: {options: {cluster_count: 3}}
  designate-hacluster: {options: {cluster_count: 3}}

relations:
  # ---- Re-add vault HA (removed by BUNDLEFIX-002; D-121 v-a re-adds it).
  #      vault already carries the `ha: metal-internal` binding in the base
  #      bundle, and vault-hacluster is declared above. ----
  - [vault:ha, vault-hacluster:ha]
