You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f5a5137
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: deploy/aks/scripts/check-chart-invariants.sh
+52Lines changed: 52 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -154,6 +154,12 @@ COMBOS=(
154
154
# opt-out shape is here too, because "no issuer" must be a statement, never an omission.
155
155
"a public host issued by cert-manager (fixture)|deploy/helm/values.yaml:deploy/aks/scripts/testdata/values.ingress-issuer.yaml"
156
156
"a host whose TLS Secret is pre-provisioned (fixture)|deploy/helm/values.yaml:deploy/aks/scripts/testdata/values.ingress-preprovisioned.yaml"
157
+
# 🚨 Cookie session affinity as the RECORD renders it (MeshWeaver#6825): HelmValues writes the typed
158
+
# block under ingress.sessionAffinity.*, which the chart did not read, so a record-only render
159
+
# dropped affinity from every multi-replica Blazor portal. The evidence check below the loop reads
160
+
# both renders by name: enabled merges in order, disabled renders none of it.
161
+
"cookie session affinity from the record's typed block (fixture)|deploy/helm/values.yaml:deploy/aks/scripts/testdata/values.ingress-session-affinity.yaml"
162
+
"cookie session affinity switched off (fixture)|deploy/helm/values.yaml:deploy/aks/scripts/testdata/values.ingress-session-affinity-disabled.yaml"
157
163
# 🚨 The instance's OWN database release (Doc/Architecture/InClusterDatabases): a CloudNativePG
158
164
# Cluster beside the portal release, named by database.release. The only combination whose
159
165
# connection strings are COMPOSED in the containers' env from a Secret the chart does not render.
@@ -336,6 +342,52 @@ else
336
342
report "plugin source credentials: the fixture must render exactly Sources__1__TokenKey=\"PluginCatalog:SourceTokens:Extensions\" and Sources__2__InstallationOwner=\"fabrikam\" (trimmed), Sources__3__TokenKey=\"\" (a blank tokenKey is a DECLARATION the portal refuses — dropping it reads the repository with the instance's App or anonymously), NO credential key for the undeclared source 0 and the null tokenKey of source 4, and the chart defaults must render none"
337
343
fi
338
344
345
+
# The session-affinity evidence (MeshWeaver#6825). Read by NAME: the record's typed block must reach
346
+
# the portal Ingress — `affinity: cookie` and the cookie name derived from the typed fields,
347
+
# nginxAnnotations over both, ingress.annotations over everything, the AGIC map NOT rendered — and a
348
+
# disabled block must render none of it. The AKS overlay states the same block, so its render must
349
+
# carry the affinity too. The plugin-registry identity rides the affinity fixture:
350
+
# pluginCatalog.instanceId renders PluginCatalog__InstanceId and a HomeUrl derived from the host.
351
+
affinity_annotations() {
352
+
python3 - "$1"<<'PY'
353
+
import sys, yaml, json
354
+
for doc in yaml.safe_load_all(open(sys.argv[1])):
355
+
if doc and doc.get("kind") == "Ingress" and doc["metadata"]["name"] == "memex-portal":
356
+
print(json.dumps(doc["metadata"].get("annotations") or {}, sort_keys=True))
357
+
break
358
+
else:
359
+
print("NO-INGRESS")
360
+
PY
361
+
}
362
+
affinity_on="$(affinity_annotations "$(render_of "cookie session affinity from the record's typed block (fixture)")")"
363
+
affinity_off="$(affinity_annotations "$(render_of "cookie session affinity switched off (fixture)")")"
364
+
affinity_aks="$(affinity_annotations "$(render_of "AKS overlay (the layer every AKS install shares)")")"
365
+
affinity_render="$(render_of "cookie session affinity from the record's typed block (fixture)")"
366
+
if python3 - "$affinity_on""$affinity_off""$affinity_aks"<<'PY'
367
+
import sys, json
368
+
on, off, aks = (json.loads(a) for a in sys.argv[1:4])
369
+
p = "nginx.ingress.kubernetes.io/"
370
+
assert on.get(p + "affinity") == "cookie", on
371
+
assert on.get(p + "session-cookie-name") == "MEMEX_AFFINITY", on
372
+
assert on.get(p + "affinity-mode") == "persistent", on
373
+
assert on.get(p + "session-cookie-max-age") == "3600", on # ingress.annotations wins
374
+
assert on.get("cert-manager.io/cluster-issuer") == "letsencrypt-prod", on
375
+
assert not any(k.startswith("appgw.") for k in on), on # AGIC is not rendered
376
+
assert not any(k in off for k in (p + "affinity", p + "session-cookie-name", p + "affinity-mode")), off
377
+
assert aks.get(p + "affinity") == "cookie" and aks.get(p + "session-cookie-name") == "MEMEX_AFFINITY", aks
ok "cookie session affinity reaches the portal Ingress from ingress.sessionAffinity (typed, then nginxAnnotations, then ingress.annotations; AGIC not rendered; disabled renders none), and pluginCatalog.instanceId renders PluginCatalog__InstanceId and HomeUrl"
384
+
else
385
+
report "pluginCatalog.instanceId: the session-affinity fixture must render PluginCatalog__InstanceId=\"instance-at-registry\" and PluginCatalog__HomeUrl=\"https://instance.example.com\" — a record-driven deploy otherwise loses the deployment-config principal"
386
+
fi
387
+
else
388
+
report "ingress.sessionAffinity: the portal Ingress must carry affinity=cookie and the cookie name when the block is enabled, nginxAnnotations over them, ingress.annotations over all, no AGIC annotation, and NONE of it when disabled — a record-only render otherwise drops cookie affinity (MeshWeaver#6825)"
389
+
fi
390
+
339
391
# The bake-gate evidence (MeshWeaver#4588, #5544). Invariant 10b — an armed PreWarm__GateReadiness
340
392
# is read by the readinessProbe on /ready, and the rollout deadline covers a cold bake — is
341
393
# CONDITIONAL, so it is satisfied just as well by a set of renders where nothing ever arms the gate.
Copy file name to clipboardExpand all lines: src/MeshWeaver.Deployment.Contract/InstanceShape.cs
+56-2Lines changed: 56 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -135,6 +135,53 @@ public sealed record RegistryKeyVaultSpec
135
135
publicstring?NotificationSecretObject{get;init;}
136
136
}
137
137
138
+
/// <summary>
139
+
/// Where this instance pulls its SEALED NodeType publications from before the portal starts — the
140
+
/// chart's <c>bundles</c> section, which renders the <c>bundle-identity</c> and <c>bundle-fetch</c>
141
+
/// init containers (Doc/Architecture/PluginBundlesInTheRegistry). Without it every pod compiles
142
+
/// the shipped NodeTypes itself, and a multi-pod instance can name an assembly in the shared
143
+
/// records that only the compiling pod holds.
144
+
///
145
+
/// <para>Stated on the record so an estate outside the fleet's own subscription can declare its
146
+
/// bundle source and still roll from the record alone: before this block existed the source lived
147
+
/// only in a hand-written values layer, and a record-driven roll either dropped it or was refused
148
+
/// for dropping it (MeshWeaver#6825).</para>
149
+
///
150
+
/// <para>The init container authenticates with the pod's <see cref="DeploymentContent.ImagePullSecret"/>:
151
+
/// the key that pulls the platform image pulls the bundles, so a record that states a registry here
152
+
/// states that Secret too. Names only — no credential.</para>
153
+
/// </summary>
154
+
publicsealedrecordBundlesSpec
155
+
{
156
+
/// <summary>The fleet registry host the publications are pulled from (e.g. <c>cr.meshweaver.cloud</c>). Blank → no bundle fetch renders.</summary>
157
+
[Description("Registry host the sealed bundles are pulled from (e.g. cr.meshweaver.cloud)")]
158
+
publicstring?Registry{get;init;}
159
+
160
+
/// <summary>The source names to materialise, as the publisher named them (e.g. <c>plugins</c>, <c>reinsurance</c>). Required when <see cref="Registry"/> is set.</summary>
161
+
[Description("Source names to materialise (e.g. plugins)")]
0 commit comments