fix: the panel polls the registry for its own and the nodes' releases [CI]
ci/woodpecker/push/main-amd64-build Pipeline is running
ci/woodpecker/push/main-amd64-build Pipeline is running
Registering a manifest with the panel is best effort in CI (an unreachable panel only warns, the pipeline stays green). The Backend update notice then never appeared, because the host updater only reads the mailbox the panel writes. A background poller now fetches the signed manifests of the agent, the probe and the backend itself from the registry every five minutes, verifies the release signature and registers them, writing the backend one into the host updater's mailbox.
This commit is contained in:
1 parent
0f8f3f9a54
commit
636a784043
6 files changed
+121
-4
No files matched your search
@@ -19,6 +19,8 @@ public sealed class ReleaseFeedTests : IAsyncLifetime
|
||||
private WebApplication _registry = null!;
|
||||
private string _registryUrl = "";
|
||||
private string _label = "";
|
||||
private string _backendLabel = "";
|
||||
private string _backendSignature = "";
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
{
|
||||
@@ -28,6 +30,10 @@ public sealed class ReleaseFeedTests : IAsyncLifetime
|
||||
_registry.MapGet("/v2/moshiro-dev/moshiro-agent-manifest/manifests/dev", () => Results.Text(new JsonObject { ["schemaVersion"] = 2, ["config"] = new JsonObject { ["digest"] = ConfigDigest } }.ToJsonString(), "application/vnd.oci.image.manifest.v1+json"));
|
||||
_registry.MapGet("/v2/moshiro-dev/moshiro-agent-manifest/blobs/" + ConfigDigest, () => Results.Text(
|
||||
new JsonObject { ["config"] = new JsonObject { ["Labels"] = new JsonObject { ["org.moshiro.manifest"] = _label, ["org.moshiro.signature"] = "c2ln" } } }.ToJsonString(), "application/json"));
|
||||
const string BackendConfigDigest = "sha256:0000000000000000000000000000000000000000000000000000000000000002";
|
||||
_registry.MapGet("/v2/moshiro-dev/moshiro-backend-manifest/manifests/dev", () => Results.Text(new JsonObject { ["schemaVersion"] = 2, ["config"] = new JsonObject { ["digest"] = BackendConfigDigest } }.ToJsonString(), "application/vnd.oci.image.manifest.v1+json"));
|
||||
_registry.MapGet("/v2/moshiro-dev/moshiro-backend-manifest/blobs/" + BackendConfigDigest, () => Results.Text(
|
||||
new JsonObject { ["config"] = new JsonObject { ["Labels"] = new JsonObject { ["org.moshiro.manifest"] = _backendLabel, ["org.moshiro.signature"] = _backendSignature } } }.ToJsonString(), "application/json"));
|
||||
await _registry.StartAsync();
|
||||
_registryUrl = _registry.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()!.Addresses.First();
|
||||
}
|
||||
@@ -71,6 +77,56 @@ public sealed class ReleaseFeedTests : IAsyncLifetime
|
||||
Assert.Null(await empty.FetchAsync("agent", "dev", default));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ThePanelFindsItsOwnNewRelease_InTheRegistry_AndLeavesItForTheHostUpdater()
|
||||
{
|
||||
// CI 把清单登记给面板那一步失败了(只警告):面板自己去仓库取,验过签就登记,并写进宿主机更新器的信箱
|
||||
long seq = TimeProvider.System.GetUtcNow().ToUnixTimeSeconds();
|
||||
string manifest = $$"""{"schema":1,"component":"backend","channel":"dev","image":"registry.example/moshiro-dev/moshiro-backend","digest":"sha256:2222222222222222222222222222222222222222222222222222222222222222","tag":"dev-new","version":"0.1.1","seq":{{seq}}}""";
|
||||
_backendLabel = Convert.ToBase64String(Encoding.UTF8.GetBytes(manifest));
|
||||
_backendSignature = TestReleaseKey.Sign(manifest);
|
||||
string directory = Path.Combine(Path.GetTempPath(), "moshiro-feed-" + Guid.NewGuid().ToString("N"));
|
||||
await using WebApplication panel = BackendHost.Build(
|
||||
["--urls", "http://127.0.0.1:0", "--Logging:LogLevel:Default=Warning"],
|
||||
options =>
|
||||
{
|
||||
options.DataDirectory = directory;
|
||||
options.Pbkdf2Iterations = 1000;
|
||||
options.SecureCookies = false;
|
||||
options.NodeImageRegistry = _registryUrl;
|
||||
options.UpdateChannel = "dev";
|
||||
options.GeoLookupUrl = string.Empty;
|
||||
options.RequireOwner = false;
|
||||
},
|
||||
configureServices: services => services.AddSingleton<IReleaseKey>(TestReleaseKey.Instance));
|
||||
await panel.StartAsync();
|
||||
try
|
||||
{
|
||||
ReleaseService releases = panel.Services.GetRequiredService<ReleaseService>();
|
||||
Assert.Null(await releases.GetBackendAsync("dev", default));
|
||||
|
||||
ReleaseOffer? offer = await releases.GetOrFetchAsync("backend", default);
|
||||
|
||||
Assert.Equal(seq, offer!.Seq);
|
||||
Assert.Equal(seq, (await releases.GetBackendAsync("dev", default))!.Seq);
|
||||
Assert.Equal(manifest, await File.ReadAllTextAsync(Path.Combine(directory, "update", "offer.manifest")));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await panel.StopAsync();
|
||||
await panel.DisposeAsync();
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
try
|
||||
{
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// 系统临时目录里的残留
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AForgedManifestInTheRegistry_IsNeverServedToTheInstaller()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user