Cluster:Roles has no OTOPCUA_ROLES fallback, despite docs claiming one — ClusterId resolves null #532
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found while fixing the
ConfigDbApiKeyStorecross-cluster over-grant (see the companion issue).AkkaClusterOptions.Roles' own XML doc andSplitTopologyTransportValidator(lines ~63-68) both state that"when empty the role list comes from
OTOPCUA_ROLESviaRoleParser". No such fallback exists in code.AddOtOpcUaClusteronly doesAddValidatedOptions<AkkaClusterOptions>(configuration, "Cluster"); thevalidator cross-reads the env var itself, and
Program.csparses it separately for role gating.Consequence. On a node configured only via
OTOPCUA_ROLESwith noCluster:Roles, anything deriving aClusterId from
AkkaClusterOptionsgets null. For the over-grant fix that means it fails open — the nodekeeps the old cross-cluster behaviour. The same derivation backs
IClusterRoleInfo.ClusterIdand the Phase-6per-cluster redundancy singleton scope, so this is not confined to ACLs.
docker-dev sets both, so the rig does not exhibit it; a real deployment setting only the env var would.
Decide one of: implement the documented fallback, or delete the claim from both doc sites and make the
env-only configuration a validation failure. Silently disagreeing docs are the worst of the three.
Related, same area:
RoleParser.IsClusterRoleuses an ordinal prefix test, so a mixed-caseCluster-SITE-AinCluster:Rolesyields a null ClusterId too.SplitTopologyTransportValidatornormalizesfor this;
ClusterRoleInfodoes not.There is also no shared
AkkaClusterOptions → ClusterIdhelper — four call sites now open-codeRoles.FirstOrDefault(RoleParser.IsClusterRole)+ClusterIdFromRole(ClusterRoleInfo,ControlPlane/ServiceCollectionExtensions,SplitTopologyTransportValidator,ConfigDbApiKeyStore).A
RoleParser.ClusterIdFromRoles(string[])would collapse all four and give the fallback one home.Fixed in
06c56d77(branchfix/gitea-532-533-roles-and-string-identity).Decision: implement the documented fallback. The issue framed this as a three-way choice and warned that silently disagreeing docs were the worst option. What settled it was
docs/deployments/wonder-app-vd03-makino-z-34184.md, which records this defect happening in the field — that node joined role-less, central reported "no driver-role nodes are Up", no deployment could complete, and the fix was hand-addingCluster__Roles__0/1to a Windows service's registry environment. That doc's own product follow-up asked for exactly this. Deleting the claim would have left a real deployment shape broken on purpose.What shipped
AddOtOpcUaClusterpost-configuresAkkaClusterOptionsthrough a new sharedRoleParser.ResolveEffectiveRoles. One place, soBuildClusterOptions(Akka's own member roles),ClusterRoleInfo, the ControlPlane singleton scope andConfigDbApiKeyStore's ClusterId all get the same answer. Configured roles still win outright.RoleParser.ClusterIdFromRoles, andIsClusterRoleis case-insensitive. That closes the second half of the over-grant fail-open: three of the four classified ordinally, so a verbatim-boundCluster-SITE-Aproduced a null ClusterId, and a null ClusterId applies no filter at all.Two things found while fixing it, both included
ConfigSourceOptionsValidatorandTelemetryOptionsValidatoralso readCluster:Rolesdirectly with no fallback — the issue named onlySplitTopologyTransportValidator. The ConfigSource one is the sharper of the two: it exempted precisely the node it exists for, since an env-only driver-only node has no ConfigDb (Program.cs gates that on the same env var) yet saw an empty role list and could sit onMode=Directunchallenged, reading its configuration from nothing. Both resolve through the shared helper now. Note the consequence: such a node now fails host start instead of booting into that state.OTOPCUA_ROLESis process-global and xUnit runs test classes in parallel, so once those two validators read it, their no-roles cases could observe another class's env var. The three suites share a non-parallel collection.Deliberately not done: normalizing the ClusterId suffix.
Cluster:Rolesbinds verbatim andRoleParser.Parselower-cases the env path, so the two sources produceSITE-Aandsite-a. Folding either would rename the Akka role of a running deployment — the docker-dev rig configurescluster-MAIN/cluster-SITE-Averbatim today, and a mid-upgrade role rename splits a singleton. Consumers compare ClusterIds case-insensitively instead, which #533 makes the stated rule rather than an accident. The residual is documented indocs/Configuration.md: configure both nodes of a pair through the same source.Falsified by reverting the PostConfigure (3 tests red) and by reverting
IsClusterRoleto ordinal (3 tests red), each time only its own guard.