Namespaces, assemblies, projects and directories, the solution, binaries, images, systemd units, config paths, environment variables, protocol method names, the media type and the raw chunk magic all move to MoShiro/moshiro. Existing data and deployments are not compatible and must be recreated. Also fix the architecture tests after the solution layout change and make the update tests independent of their order.
88 lines
2.8 KiB
C#
88 lines
2.8 KiB
C#
using System.Globalization;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
using Fleet.Core;
|
||
|
||
namespace MoShiro.Protocol;
|
||
|
||
/// <summary>Task = { id, scope, kind, target, params, schedule, enabled }。参数已经按 ParamSpec 校验并补全默认值。</summary>
|
||
public sealed class TaskSpec
|
||
{
|
||
[JsonPropertyName("id")]
|
||
public TaskId Id { get; set; }
|
||
|
||
/// <summary>line | target</summary>
|
||
[JsonPropertyName("scope")]
|
||
public string Scope { get; set; } = "line";
|
||
|
||
[JsonPropertyName("kind")]
|
||
public string Kind { get; set; } = "";
|
||
|
||
[JsonPropertyName("target")]
|
||
public string? Target { get; set; }
|
||
|
||
[JsonPropertyName("params")]
|
||
public Dictionary<string, JsonElement> Params { get; set; } = [];
|
||
|
||
/// <summary>continuous | every:<分钟> | once</summary>
|
||
[JsonPropertyName("schedule")]
|
||
public string Schedule { get; set; } = TaskSchedule.Continuous;
|
||
|
||
[JsonPropertyName("enabled")]
|
||
public bool Enabled { get; set; } = true;
|
||
}
|
||
|
||
public sealed class PeerSpec
|
||
{
|
||
[JsonPropertyName("probe_id")]
|
||
public ProbeId ProbeId { get; set; }
|
||
|
||
/// <summary>Probe 的身份公钥(SPKI,base64)。</summary>
|
||
[JsonPropertyName("pubkey")]
|
||
public string PublicKey { get; set; } = "";
|
||
}
|
||
|
||
/// <summary>期望状态文档(Fleet state type = <c>task_set</c>):Backend 为每个 Agent 计算的有效任务集。</summary>
|
||
public sealed class TaskSet
|
||
{
|
||
public const string StateType = "task_set";
|
||
|
||
[JsonPropertyName("version")]
|
||
public long Version { get; set; }
|
||
|
||
[JsonPropertyName("tasks")]
|
||
public List<TaskSpec> Tasks { get; set; } = [];
|
||
|
||
/// <summary>允许连接到这个 Agent 的 Probe 公钥。</summary>
|
||
[JsonPropertyName("peers")]
|
||
public List<PeerSpec> Peers { get; set; } = [];
|
||
}
|
||
|
||
public static class TaskSchedule
|
||
{
|
||
public const string Continuous = "continuous";
|
||
public const string Once = "once";
|
||
public const string EveryPrefix = "every:";
|
||
|
||
public static string Every(int minutes) => EveryPrefix + minutes.ToString(CultureInfo.InvariantCulture);
|
||
|
||
/// <summary>校验调度字符串:<c>every:N</c> 的 N ≥ 60(适用于重负载 Kind)。</summary>
|
||
public static Result<int?> Validate(string schedule)
|
||
{
|
||
if (schedule is Continuous or Once)
|
||
{
|
||
return Result<int?>.Ok(null);
|
||
}
|
||
|
||
if (schedule.StartsWith(EveryPrefix, StringComparison.Ordinal)
|
||
&& int.TryParse(schedule.AsSpan(EveryPrefix.Length), NumberStyles.None, CultureInfo.InvariantCulture, out int minutes))
|
||
{
|
||
return minutes >= 60
|
||
? Result<int?>.Ok(minutes)
|
||
: Result<int?>.Fail(MoShiroErrors.ValidationFailed, "every:N requires N >= 60 minutes");
|
||
}
|
||
|
||
return Result<int?>.Fail(MoShiroErrors.ValidationFailed, "schedule must be continuous, once or every:<minutes>");
|
||
}
|
||
}
|