b037cc7ac4
Four-minor jump 0.2.3 → 0.6.2. The release line exists because a deploy
renaming the app directory took the MxGateway credential store with it
(2026-08-09, two silent days): Secrets:SqlitePath must now be absolute AND
outside the content root, rejected at boot rather than relocated. 0.6.2 over
0.6.1 is message-only (the rejection text now teaches the mounted-volume
case and forecloses the override hunt) — the live rig evidence below was
gathered at 0.6.1 and the validator behavior is identical.
Adoption changes beyond the pins:
- appsettings.json drops the relative "otopcua-secrets.db" (now boot-fatal);
dev gets the library's absolute per-user default, deployments supply their
own (wonder already pins an absolute out-of-root path in the service env).
- Program.cs's pre-host ${secret:} expander now calls the FOUR-argument
AddZbSecrets with builder.Environment.ContentRootPath. This call site is
the actual fix, not the pin: the throwaway container has no
IHostEnvironment, and without the explicit root the under-content-root
rule is skipped at exactly the moment the migrator creates the store file
(the 0.6.0 defect). Do NOT simplify back to the 3-argument overload — it
compiles and silently reopens the gap. Pinned by a Program.cs source-scan
test AND a runtime falsification control that RUNS the migration and
demonstrably creates the store at the rejected path under the 3-arg form.
- docker-dev: Secrets__SqlitePath moves /app/data → /data via a SECOND mount
of the same per-node volume (same file at the volume root, so seeded
stores carry over) because /app is the container's content root and the
new rule correctly rejects it. LocalDb stays on /app/data (secrets-only
rule).
- SecretsStorePathValidationTests: positive-control probes, artifact-first
(relative and in-content-root paths must FAIL leaving no file; outside-
root passes; the no-explicit-root control proves the file assertions can
fail) — a clean boot is equally consistent with the validator never being
wired.
Live-verified on the six-node docker-dev rig (at 0.6.1): all nodes boot
under the new validation with stores at /data; an AdminUI-written secret
converged into BOTH central nodes' independent stores; site-a held only its
own pair's secrets (Phase 6 pair-local isolation intact). Replicator wire
behavior unchanged 0.3.0–0.6.x per CHANGELOG; registration-graph pins and
the full suite pass.
49 lines
2.4 KiB
C#
49 lines
2.4 KiB
C#
using System.Text.RegularExpressions;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Host.Tests.Configuration;
|
|
|
|
/// <summary>
|
|
/// Pins the one <c>AddZbSecrets</c> call that no executable test can reach: the pre-host
|
|
/// <c>${secret:}</c> expander in <c>Program.cs</c> (top-level statements). That call composes the
|
|
/// secrets subsystem in a throwaway <c>ServiceCollection</c> with no <c>IHostEnvironment</c>, so
|
|
/// it MUST pass the content root explicitly (the Secrets 0.6.1 four-argument overload) — dropped
|
|
/// back to the three-argument form, the under-content-root store rule silently vanishes at
|
|
/// exactly the moment the migrator creates the store file, and a rejected path gains an empty
|
|
/// database before the real host aborts (the 0.6.0 defect; the runtime half of this pin is
|
|
/// <c>SecretsStorePathValidationTests.Without_the_explicit_content_root_the_prehost_hole_reopens</c>
|
|
/// in Host.IntegrationTests).
|
|
/// </summary>
|
|
public sealed class SecretsPreHostContentRootSourceTests
|
|
{
|
|
[Fact]
|
|
public void The_prehost_expander_passes_the_content_root_explicitly()
|
|
{
|
|
var source = ReadProgramSource();
|
|
|
|
// The throwaway container's AddZbSecrets call, allowing for formatting drift but requiring
|
|
// the third argument to be the builder's content root.
|
|
Regex.IsMatch(
|
|
source,
|
|
@"new\s+ServiceCollection\(\)\s*\.\s*AddZbSecrets\(\s*builder\.Configuration\s*,\s*""Secrets""\s*,\s*builder\.Environment\.ContentRootPath\s*\)",
|
|
RegexOptions.Singleline)
|
|
.ShouldBeTrue(
|
|
"Program.cs's pre-host secrets expander must call the four-argument AddZbSecrets "
|
|
+ "overload with builder.Environment.ContentRootPath — the throwaway container has no "
|
|
+ "IHostEnvironment, and without the explicit root the under-content-root store rule "
|
|
+ "is skipped while the migrator creates the store file");
|
|
}
|
|
|
|
private static string ReadProgramSource()
|
|
{
|
|
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "ZB.MOM.WW.OtOpcUa.slnx")))
|
|
dir = dir.Parent!;
|
|
dir.ShouldNotBeNull();
|
|
|
|
return File.ReadAllText(Path.Combine(
|
|
dir.FullName, "src", "Server", "ZB.MOM.WW.OtOpcUa.Host", "Program.cs"));
|
|
}
|
|
}
|