probe: add native OpenWrt deployment and signed automatic updates [CI]
This commit is contained in:
1 parent
24ba05fbb6
commit
cd193378a6
26 files changed
+987
-9
No files matched your search
@@ -0,0 +1,76 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Fleet.Core;
|
||||
using Fleet.Hosting;
|
||||
using MoShiro.Measurement;
|
||||
using MoShiro.Protocol;
|
||||
|
||||
namespace MoShiro.Probe;
|
||||
|
||||
/// <summary>
|
||||
/// Native supervisor bridge: writes only fixed wake-up flags and a health beacon in tmpfs.
|
||||
/// Never downloads, installs, verifies releases, or executes commands. The root-owned host
|
||||
/// updater independently fetches and verifies its locally configured release feed.
|
||||
/// </summary>
|
||||
public sealed class OpenWrtUpdater
|
||||
{
|
||||
private readonly ConfigStore _runtime;
|
||||
private readonly string _statusPath;
|
||||
private readonly TimeProvider _time;
|
||||
private readonly INodeLog _log;
|
||||
|
||||
public OpenWrtUpdater(string runtimeDirectory, string statusPath, TimeProvider time, INodeLog log)
|
||||
{
|
||||
_runtime = new ConfigStore(runtimeDirectory);
|
||||
_statusPath = statusPath;
|
||||
_time = time;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
public static OpenWrtUpdater? FromEnvironment(TimeProvider time, INodeLog log) =>
|
||||
Environment.GetEnvironmentVariable(RestartUpdater.EnvironmentVariable) == "openwrt"
|
||||
? new OpenWrtUpdater("/var/run/moshiro-probe", "/var/run/moshiro-update/status.json", time, log)
|
||||
: null;
|
||||
|
||||
public void Offer() => Signal("check.request");
|
||||
|
||||
public void RequestApply() => Signal("apply.request");
|
||||
|
||||
private void Signal(string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
_runtime.Write(fileName, "check"u8.ToArray());
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
_log.Warn("could not signal the native updater", ("err", ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Called by the Fleet heartbeat: evidence that this version reached Backend.</summary>
|
||||
public UpdateStatus? Status()
|
||||
{
|
||||
try
|
||||
{
|
||||
var health = new OpenWrtHealth(BuildInfo.Version, Environment.ProcessId, _time.GetUtcNow().ToUnixTimeSeconds());
|
||||
_runtime.Write("health.json", JsonSerializer.SerializeToUtf8Bytes(health, ProbeConfigContext.Default.OpenWrtHealth));
|
||||
if (!File.Exists(_statusPath) || new FileInfo(_statusPath).Length > 8192)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return JsonSerializer.Deserialize(File.ReadAllBytes(_statusPath), MoShiroJsonContext.Default.UpdateStatus);
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException)
|
||||
{
|
||||
_log.Warn("could not read native update status", ("err", ex.Message));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record OpenWrtHealth(
|
||||
[property: JsonPropertyName("version")] string Version,
|
||||
[property: JsonPropertyName("pid")] int Pid,
|
||||
[property: JsonPropertyName("at")] long At);
|
||||
@@ -41,6 +41,7 @@ public sealed class ProbeApp : IDisposable
|
||||
directory.Changed += () => _changed.Release();
|
||||
|
||||
RestartUpdater? update = RestartUpdater.FromEnvironment("probe", reason => restart?.RequestNow(reason), time, log);
|
||||
OpenWrtUpdater? nativeUpdate = OpenWrtUpdater.FromEnvironment(time, log);
|
||||
var options = new FleetLinkOptions
|
||||
{
|
||||
Server = new Uri(file.Fleet.Server),
|
||||
@@ -52,7 +53,7 @@ public sealed class ProbeApp : IDisposable
|
||||
ShutdownReason = () => restart?.NowReason == "update" ? "updating" : "stopping",
|
||||
HeartbeatExtra = () =>
|
||||
{
|
||||
UpdateStatus? updateStatus = update?.Status();
|
||||
UpdateStatus? updateStatus = nativeUpdate?.Status() ?? update?.Status();
|
||||
NodeRuntimeStatus runtimeStatus = updateStatus?.State == "applying" ? NodeRuntimeStatus.Updating : _env.Directory.Agents.Count == 0 ? NodeRuntimeStatus.ProbePaused : NodeRuntimeStatus.Running;
|
||||
NodeLogBatch logBatch = _logBatchSource?.TakeBatch(16) ?? new NodeLogBatch([], 0);
|
||||
return JsonSerializer.SerializeToElement(new ProbeHeartbeatExtra
|
||||
@@ -84,11 +85,13 @@ public sealed class ProbeApp : IDisposable
|
||||
.OnNotify(MoShiroMethods.UpdateOffer, MoShiroJsonContext.Default.UpdateOfferParams, (_, offer, _) =>
|
||||
{
|
||||
update?.Offer(offer);
|
||||
nativeUpdate?.Offer();
|
||||
return ValueTask.CompletedTask;
|
||||
})
|
||||
.OnNotify(MoShiroMethods.UpdateApply, MoShiroJsonContext.Default.UpdateOfferParams, (_, _, _) =>
|
||||
{
|
||||
update?.RequestApply();
|
||||
nativeUpdate?.RequestApply();
|
||||
return ValueTask.CompletedTask;
|
||||
})
|
||||
.OnNotify(MoShiroMethods.Schedule, MoShiroJsonContext.Default.ScheduleParams, (_, schedule, _) =>
|
||||
|
||||
@@ -84,6 +84,7 @@ public sealed class ProbeLimits
|
||||
|
||||
[JsonSerializable(typeof(ProbeFile))]
|
||||
[JsonSerializable(typeof(AgentsCache))]
|
||||
[JsonSerializable(typeof(OpenWrtHealth))]
|
||||
public sealed partial class ProbeConfigContext : JsonSerializerContext;
|
||||
|
||||
/// <summary><c>agents.json</c>:最近一次从面板同步到的 Agent 列表(配置,不是测量数据);面板连不上时 Probe 靠它继续测量。</summary>
|
||||
|
||||
Reference in new issue
Block a user