feat(admin): headless POST /api/deployments REST endpoint (API-key gated)
v2-ci / build (push) Failing after 50s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
v2-ci / build (push) Failing after 50s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
A thin gateway over the admin-operations cluster singleton so CI/scripts can trigger a deployment without the Blazor button. Forwards to the same IAdminOperationsClient. StartDeploymentAsync; mounted on admin-role nodes. Auth is a fixed-time X-Api-Key check against Security:DeployApiKey (orthogonal to the cookie-only web auth); AllowAnonymous so the auth fallback doesn't 401 it, self-disabling (503) until the key is set. Outcome->status: 202/200/409/422. Unit tests for the key check + outcome mapping; HTTP E2E (real auth + real deploy via the 2-node harness). Documented in docs/security.md.
This commit is contained in:
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Api;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Clients;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
|
||||
using ZB.MOM.WW.OtOpcUa.Cluster;
|
||||
@@ -48,9 +49,23 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
|
||||
public sealed class TwoNodeClusterHarness : IAsyncDisposable
|
||||
{
|
||||
public const string TestRoles = "admin,driver";
|
||||
/// <summary>The deploy-API key both harness nodes are configured with (Security:DeployApiKey).</summary>
|
||||
public const string HarnessDeployApiKey = "test-deploy-key";
|
||||
/// <summary>Gets the shared database name for both cluster nodes.</summary>
|
||||
public string SharedDbName { get; } = $"two-node-cluster-{Guid.NewGuid():N}";
|
||||
|
||||
/// <summary>Resolves node A's bound HTTP base address (Kestrel binds an ephemeral port), for
|
||||
/// driving the REST surface over real HTTP in tests.</summary>
|
||||
public string NodeABaseAddress => ResolveBaseAddress(NodeA);
|
||||
|
||||
private static string ResolveBaseAddress(WebApplication app)
|
||||
{
|
||||
var feature = app.Services.GetRequiredService<Microsoft.AspNetCore.Hosting.Server.IServer>()
|
||||
.Features.Get<Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature>();
|
||||
return feature?.Addresses.FirstOrDefault()
|
||||
?? throw new InvalidOperationException("Node A has no bound HTTP address.");
|
||||
}
|
||||
|
||||
/// <summary>Gets the harness mode configuration from environment variables.</summary>
|
||||
public HarnessMode Mode { get; } = HarnessMode.FromEnvironment();
|
||||
private string? _sqlDbName;
|
||||
@@ -173,6 +188,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
|
||||
["Cluster:Roles:1"] = "driver",
|
||||
["Security:Jwt:SigningKey"] = "two-node-harness-test-signing-key-with-enough-bytes-for-hs256",
|
||||
["Security:Jwt:Issuer"] = "otopcua-test",
|
||||
["Security:DeployApiKey"] = HarnessDeployApiKey,
|
||||
["Security:Jwt:Audience"] = "otopcua-test",
|
||||
};
|
||||
|
||||
@@ -227,6 +243,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
|
||||
app.MapOtOpcUaAuth();
|
||||
app.MapOtOpcUaHubs();
|
||||
app.MapOtOpcUaHealth();
|
||||
app.MapOtOpcUaDeployApi(app.Configuration);
|
||||
|
||||
await app.StartAsync();
|
||||
return app;
|
||||
|
||||
Reference in New Issue
Block a user