feat(mesh): CentralCommandService on the ConfigServe listener
This commit is contained in:
@@ -94,9 +94,10 @@ public sealed class MeshTransportOptionsValidator : IValidateOptions<MeshTranspo
|
||||
}
|
||||
}
|
||||
|
||||
// The Grpc surface below is node-side: the PSK a driver presents (and requires), the central
|
||||
// endpoints it posts its ack to, and the two call deadlines. Scoped to the driver role for the
|
||||
// same reason the ClusterClient block above is scoped to its mode — an admin-only central node
|
||||
// The first Grpc block below is node-side: the PSK a driver presents (and requires), the
|
||||
// central endpoints it posts its ack to, and the two call deadlines (the admin-side rule that
|
||||
// follows it is a separate scope). Scoped to the driver role for the same reason the
|
||||
// ClusterClient block above is scoped to its mode — an admin-only central node
|
||||
// dials nodes rather than being dialled and resolves per-cluster keys at runtime through
|
||||
// MeshTransportOptions.ClusterPsks / the Secrets store, so requiring a single GrpcPsk and an
|
||||
// endpoint list of it would demand configuration it never reads. Roles come from
|
||||
@@ -107,6 +108,7 @@ public sealed class MeshTransportOptionsValidator : IValidateOptions<MeshTranspo
|
||||
var roles = RoleParser.ResolveEffectiveRoles(
|
||||
_configuration.GetSection("Cluster:Roles").Get<string[]>());
|
||||
var isDriver = Array.IndexOf(roles, RoleParser.Driver) >= 0;
|
||||
var isAdmin = Array.IndexOf(roles, RoleParser.Admin) >= 0;
|
||||
|
||||
if (isGrpc && isDriver)
|
||||
{
|
||||
@@ -166,6 +168,37 @@ public sealed class MeshTransportOptionsValidator : IValidateOptions<MeshTranspo
|
||||
}
|
||||
}
|
||||
|
||||
// The one admin-side obligation, and it is cross-read rather than a member of this options
|
||||
// class: central hosts CentralCommandService on the SAME dedicated h2c listener as the
|
||||
// config-serve endpoint (ConfigServe:GrpcListenPort) — a second cleartext HTTP/2 port would
|
||||
// buy nothing but another firewall rule. With that port unset the service is never mapped, so
|
||||
// every node's deployment ack has nowhere to land, and nothing errors: the deployment simply
|
||||
// times out naming nodes that applied perfectly. Owned here rather than by ConfigServe's own
|
||||
// validation because the requirement comes from MeshTransport:Mode, not from config-serve — a
|
||||
// Dps or ClusterClient central node is legitimately portless.
|
||||
if (isGrpc && isAdmin)
|
||||
{
|
||||
// Read as a string, the way SplitTopologyTransportValidator reads the modes:
|
||||
// GetValue<int> THROWS on an unparseable value, and a validator whose whole job is to
|
||||
// produce a readable failure must not be the thing that crashes on a typo. Missing,
|
||||
// unparseable and non-positive all mean the same thing — nothing is bound — so they share
|
||||
// one message.
|
||||
var configServePort = _configuration[
|
||||
$"{ConfigServeOptions.SectionName}:{nameof(ConfigServeOptions.GrpcListenPort)}"];
|
||||
if (!int.TryParse(configServePort, out var port) || port <= 0)
|
||||
{
|
||||
errors.Add(
|
||||
$"{ConfigServeOptions.SectionName}:"
|
||||
+ $"{nameof(ConfigServeOptions.GrpcListenPort)} is not set to a positive port. "
|
||||
+ $"Under {MeshTransportOptions.ModeGrpc} an admin node serves the central "
|
||||
+ "command endpoint on that listener, and without it every node's deployment ack "
|
||||
+ "has nowhere to land — the deploy would time out naming nodes that applied "
|
||||
+ "perfectly. Supply it via the environment "
|
||||
+ $"({ConfigServeOptions.SectionName}__"
|
||||
+ $"{nameof(ConfigServeOptions.GrpcListenPort)}).");
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Count == 0
|
||||
? ValidateOptionsResult.Success
|
||||
: ValidateOptionsResult.Fail(string.Join(" ", errors));
|
||||
|
||||
Reference in New Issue
Block a user