Files
MoShiro/backend/src/MoShiro.Backend.Gateway/FleetGateway.cs
T
justme ad5efc62ff agent: unify listening and dialing with directed mutual measurements
Replace Probe with Agent across node runtime, Fleet contracts, backend state, API and storage. Report incoming sessions and outgoing link health through each Agent uplink, and retain directed link controls and retry.

Update protocol fixtures and end-to-end coverage for the unified model; accept requested cancellation during the Fleet timeout test cleanup.
2026-10-21 11:55:36 +08:00

79 lines
4.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Fleet.Core;
using Fleet.Server;
using Microsoft.Extensions.DependencyInjection;
using MoShiro.Backend.Application;
using MoShiro.Protocol;
namespace MoShiro.Backend.Gateway;
/// <summary>
/// Application 通往 Agent 连接的端口的实现,建立在 Fleet.Server.Link 的在线表之上。
/// Fleet 的连接宿主依赖节点事件处理器,而事件处理器依赖 Application,Application 又依赖这个端口:
/// 这里在调用时才解析 Fleet 的服务,打断构造期的循环依赖。
/// </summary>
public sealed class FleetGateway : IAgentGateway
{
private readonly IServiceProvider _services;
public FleetGateway(IServiceProvider services)
{
_services = services;
}
private IFleetConnections Connections => _services.GetRequiredService<IFleetConnections>();
private IStateNotifier Notifier => _services.GetRequiredService<IStateNotifier>();
public IReadOnlyCollection<NodeId> OnlineAgents => [.. Connections.OnlineNodes.Where(n => n.Value.StartsWith(AgentIds.Prefix, StringComparison.Ordinal))];
public IReadOnlyCollection<NodeId> OnlineNodes => Connections.OnlineNodes;
public bool IsOnline(NodeId agent) => Connections.IsOnline(agent);
public void NotifyTaskSetChanged(NodeId agent) => Notifier.NotifyChanged(agent);
public void NotifyAllTaskSetsChanged() => Notifier.NotifyAllChanged();
public async ValueTask<Result<bool>> RunTestAsync(NodeId agent, TestRunParams run, CancellationToken ct)
{
Result<TestRunResult> result = await Connections.RequestAsync(
agent, MoShiroMethods.TestRun, run, MoShiroJsonContext.Default.TestRunParams, MoShiroJsonContext.Default.TestRunResult, ct);
if (!result.TryGet(out TestRunResult? accepted, out FleetError? error))
{
return Result<bool>.Fail(error.Code == FleetErrors.NodeOffline ? MoShiroErrors.AgentOffline : error.Code, error.Message);
}
return accepted.Accepted ? Result<bool>.Ok(true) : Result<bool>.Fail(MoShiroErrors.TestBusy, "the agent did not accept the test");
}
public async ValueTask CancelTestAsync(NodeId agent, TestId test, CancellationToken ct) =>
await Connections.RequestAsync(
agent, MoShiroMethods.TestCancel, new TestCancelParams { TestId = test }, MoShiroJsonContext.Default.TestCancelParams, MoShiroJsonContext.Default.TestRunResult, ct);
public async ValueTask<Result<bool>> DisconnectDialerAsync(NodeId listener, NodeId dialer, CancellationToken ct)
{
Result<TestRunResult> result = await Connections.RequestAsync(
listener, MoShiroMethods.DialerDisconnect, new DialerDisconnectParams { DialerId = dialer }, MoShiroJsonContext.Default.DialerDisconnectParams, MoShiroJsonContext.Default.TestRunResult, ct);
if (!result.TryGet(out TestRunResult? done, out FleetError? error))
{
return Result<bool>.Fail(error.Code == FleetErrors.NodeOffline ? MoShiroErrors.AgentOffline : error.Code, error.Message);
}
return Result<bool>.Ok(done.Accepted);
}
public ValueTask DisconnectAsync(NodeId agent, string reason, CancellationToken ct) => Connections.DisconnectAsync(agent, reason, ct);
public ValueTask<bool> NotifyLinkRetryAsync(NodeId dialer, NodeId listener, CancellationToken ct) =>
Connections.NotifyAsync(dialer, MoShiroMethods.LinkRetry, new LinkRetryParams { ListenerId = listener }, MoShiroJsonContext.Default.LinkRetryParams, ct);
public async ValueTask NotifyUpdateOfferAsync(NodeId node, string manifest, string signature, CancellationToken ct) =>
await Connections.NotifyAsync(node, MoShiroMethods.UpdateOffer, new UpdateOfferParams { Manifest = manifest, Signature = signature }, MoShiroJsonContext.Default.UpdateOfferParams, ct);
public async ValueTask NotifyScheduleAsync(NodeId node, string? restartAt, CancellationToken ct) =>
await Connections.NotifyAsync(node, MoShiroMethods.Schedule, new ScheduleParams { RestartAt = restartAt }, MoShiroJsonContext.Default.ScheduleParams, ct);
public async ValueTask<bool> NotifyUpdateApplyAsync(NodeId node, CancellationToken ct) =>
await Connections.NotifyAsync(node, MoShiroMethods.UpdateApply, new UpdateOfferParams(), MoShiroJsonContext.Default.UpdateOfferParams, ct);
}