feat(controlplane): deploy-gate TagConfig strictness — Warn default, Error opt-in at the draft gate (R2-11, 05/CONV-2)
This commit is contained in:
@@ -25,31 +25,37 @@ public sealed class AdminOperationsActor : ReceiveActor
|
||||
private readonly IDbContextFactory<OtOpcUaConfigDbContext> _dbFactory;
|
||||
private readonly IActorRef _coordinator;
|
||||
private readonly IReadOnlyDictionary<string, IDriverProbe> _probesByType;
|
||||
private readonly TagConfigValidationMode _tagConfigValidationMode;
|
||||
private readonly ILoggingAdapter _log = Context.GetLogger();
|
||||
|
||||
/// <summary>Creates actor props for the admin operations actor.</summary>
|
||||
/// <param name="dbFactory">Factory for creating config database contexts.</param>
|
||||
/// <param name="coordinator">Reference to the deployment coordinator actor.</param>
|
||||
/// <param name="probes">Driver probes registered in DI; keyed by DriverType (case-insensitive).</param>
|
||||
/// <param name="tagConfigValidationMode">Deploy-gate TagConfig strictness mode (Warn default | Error opt-in).</param>
|
||||
/// <returns>Props configured to create an AdminOperationsActor.</returns>
|
||||
public static Props Props(
|
||||
IDbContextFactory<OtOpcUaConfigDbContext> dbFactory,
|
||||
IActorRef coordinator,
|
||||
IEnumerable<IDriverProbe> probes) =>
|
||||
Akka.Actor.Props.Create(() => new AdminOperationsActor(dbFactory, coordinator, probes));
|
||||
IEnumerable<IDriverProbe> probes,
|
||||
TagConfigValidationMode tagConfigValidationMode = TagConfigValidationMode.Warn) =>
|
||||
Akka.Actor.Props.Create(() => new AdminOperationsActor(dbFactory, coordinator, probes, tagConfigValidationMode));
|
||||
|
||||
/// <summary>Initializes a new instance of the AdminOperationsActor.</summary>
|
||||
/// <param name="dbFactory">Factory for creating config database contexts.</param>
|
||||
/// <param name="coordinator">Reference to the deployment coordinator actor.</param>
|
||||
/// <param name="probes">Driver probes registered in DI; keyed by DriverType (case-insensitive).</param>
|
||||
/// <param name="tagConfigValidationMode">Deploy-gate TagConfig strictness mode (Warn default | Error opt-in).</param>
|
||||
public AdminOperationsActor(
|
||||
IDbContextFactory<OtOpcUaConfigDbContext> dbFactory,
|
||||
IActorRef coordinator,
|
||||
IEnumerable<IDriverProbe> probes)
|
||||
IEnumerable<IDriverProbe> probes,
|
||||
TagConfigValidationMode tagConfigValidationMode = TagConfigValidationMode.Warn)
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
_coordinator = coordinator;
|
||||
_probesByType = probes.ToDictionary(p => p.DriverType, StringComparer.OrdinalIgnoreCase);
|
||||
_tagConfigValidationMode = tagConfigValidationMode;
|
||||
|
||||
ReceiveAsync<StartDeployment>(HandleStartDeploymentAsync);
|
||||
ReceiveAsync<TestDriverConnect>(HandleTestDriverConnectAsync);
|
||||
@@ -202,6 +208,31 @@ public sealed class AdminOperationsActor : ReceiveActor
|
||||
errors.AddRange(DraftValidator.ValidateClusterTopology(cluster, nodes));
|
||||
}
|
||||
|
||||
// R2-11 (05/CONV-2): deploy-time TagConfig strictness inspection over equipment tags. The
|
||||
// driver-typed EquipmentTagConfigInspector surfaces present-but-invalid enum values (which the
|
||||
// lenient runtime silently defaults) + structurally-unparseable TagConfig. In Warn mode
|
||||
// (default) these are non-blocking (logged + appended to the accepted result Message); in Error
|
||||
// mode they fold into the reject list so a typo'd config cannot be re-deployed until fixed.
|
||||
// Running servers are untouched either way — the gate only sees re-deploys.
|
||||
var driverTypeById = draft.DriverInstances
|
||||
.GroupBy(d => d.DriverInstanceId, StringComparer.Ordinal)
|
||||
.ToDictionary(g => g.Key, g => g.First().DriverType, StringComparer.Ordinal);
|
||||
var tagConfigWarnings = new List<(string TagId, string Text)>();
|
||||
foreach (var t in draft.Tags.Where(t => t.EquipmentId is not null))
|
||||
{
|
||||
if (!driverTypeById.TryGetValue(t.DriverInstanceId, out var driverType)) continue;
|
||||
foreach (var w in EquipmentTagConfigInspector.Inspect(driverType, t.TagConfig))
|
||||
tagConfigWarnings.Add((t.TagId, $"tag '{t.TagId}' (equipment '{t.EquipmentId}'): {w}"));
|
||||
}
|
||||
if (tagConfigWarnings.Count > 0)
|
||||
{
|
||||
if (_tagConfigValidationMode == TagConfigValidationMode.Error)
|
||||
errors.AddRange(tagConfigWarnings.Select(x => new ValidationError("TagConfigInvalid", x.Text, x.TagId)));
|
||||
else
|
||||
foreach (var x in tagConfigWarnings)
|
||||
_log.Warning("StartDeployment: TagConfig warning (Warn mode, non-blocking): {Warning}", x.Text);
|
||||
}
|
||||
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
var summary = string.Join("; ", errors.Select(e => $"[{e.Code}] {e.Message}"));
|
||||
@@ -277,11 +308,18 @@ public sealed class AdminOperationsActor : ReceiveActor
|
||||
|
||||
_coordinator.Tell(new DispatchDeployment(deploymentId, revHash, msg.CorrelationId));
|
||||
|
||||
// Accepted message = the compile-cost advisory (if any) + any Warn-mode TagConfig warnings, so
|
||||
// the operator sees the non-blocking strictness findings on a successful deploy.
|
||||
var messageParts = new List<string>();
|
||||
if (advisory is not null) messageParts.Add(advisory);
|
||||
messageParts.AddRange(tagConfigWarnings.Select(x => x.Text));
|
||||
var acceptedMessage = messageParts.Count > 0 ? string.Join("; ", messageParts) : null;
|
||||
|
||||
replyTo.Tell(new StartDeploymentResult(
|
||||
StartDeploymentOutcome.Accepted,
|
||||
deploymentId,
|
||||
revHash,
|
||||
Message: advisory,
|
||||
Message: acceptedMessage,
|
||||
msg.CorrelationId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations;
|
||||
|
||||
/// <summary>
|
||||
/// How the deploy gate treats TagConfig strictness inspection results (R2-11, 05/CONV-2). Sourced from
|
||||
/// <c>Deployment:TagConfigValidationMode</c> in appsettings; defaults to <see cref="Warn"/> so existing
|
||||
/// deployments are never blocked by a previously-tolerated typo.
|
||||
/// </summary>
|
||||
public enum TagConfigValidationMode
|
||||
{
|
||||
/// <summary>Non-blocking: inspection warnings are logged and appended to the accepted
|
||||
/// <c>StartDeploymentResult.Message</c>, but the deploy still succeeds. The default.</summary>
|
||||
Warn,
|
||||
|
||||
/// <summary>Blocking: inspection warnings fold into the draft-gate reject list, so a config with a
|
||||
/// typo'd enum (or unparseable TagConfig) cannot be re-deployed until fixed. Opt-in per environment
|
||||
/// once the fleet deploys warning-clean.</summary>
|
||||
Error,
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using Akka.Actor;
|
||||
using Akka.Cluster.Hosting;
|
||||
using Akka.Hosting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations;
|
||||
@@ -61,7 +62,12 @@ public static class ServiceCollectionExtensions
|
||||
var dbFactory = resolver.GetService<IDbContextFactory<OtOpcUaConfigDbContext>>();
|
||||
var coordinator = registry.Get<ConfigPublishCoordinatorKey>();
|
||||
var probes = resolver.GetService<IEnumerable<IDriverProbe>>() ?? Enumerable.Empty<IDriverProbe>();
|
||||
return AdminOperationsActor.Props(dbFactory, coordinator, probes);
|
||||
// R2-11 (05/CONV-2): deploy-gate TagConfig strictness mode from appsettings
|
||||
// (Deployment:TagConfigValidationMode = Warn default | Error opt-in).
|
||||
var mode = Enum.TryParse<TagConfigValidationMode>(
|
||||
resolver.GetService<IConfiguration>()?["Deployment:TagConfigValidationMode"],
|
||||
ignoreCase: true, out var m) ? m : TagConfigValidationMode.Warn;
|
||||
return AdminOperationsActor.Props(dbFactory, coordinator, probes, mode);
|
||||
},
|
||||
singletonOptions);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user