mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 21:19:38 +00:00
97 lines
4.0 KiB
C#
97 lines
4.0 KiB
C#
using System;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace AutopilotMonitor.Functions.Services
|
|
{
|
|
/// <summary>
|
|
/// Exposes the latest published agent/bootstrap versions as read from the
|
|
/// public <c>version.json</c> blob. Caches the result in memory for 12h
|
|
/// (short TTL on transient failures). Admin endpoint can force-refresh.
|
|
/// </summary>
|
|
public interface ILatestVersionsService
|
|
{
|
|
Task<LatestVersions?> GetAsync(bool forceRefresh, CancellationToken ct);
|
|
}
|
|
|
|
public sealed record LatestVersions(
|
|
string? AgentVersion,
|
|
string? BootstrapVersion,
|
|
string? AgentSha256,
|
|
DateTimeOffset FetchedAtUtc,
|
|
bool FromCache);
|
|
|
|
public sealed class LatestVersionsService : ILatestVersionsService
|
|
{
|
|
public const string VersionJsonUrl = "https://autopilotmonitor.blob.core.windows.net/agent/version.json";
|
|
private const string CacheKey = "latest-versions";
|
|
private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(12);
|
|
private static readonly TimeSpan FailureCacheDuration = TimeSpan.FromMinutes(5);
|
|
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly IMemoryCache _cache;
|
|
private readonly ILogger<LatestVersionsService> _logger;
|
|
|
|
public LatestVersionsService(
|
|
IHttpClientFactory httpClientFactory,
|
|
IMemoryCache cache,
|
|
ILogger<LatestVersionsService> logger)
|
|
{
|
|
_httpClientFactory = httpClientFactory;
|
|
_cache = cache;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<LatestVersions?> GetAsync(bool forceRefresh, CancellationToken ct)
|
|
{
|
|
if (!forceRefresh && _cache.TryGetValue(CacheKey, out LatestVersions? cached))
|
|
{
|
|
if (cached != null)
|
|
{
|
|
return cached with { FromCache = true };
|
|
}
|
|
// Cached "null" (transient failure marker) — return null without another blob call
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
using var client = _httpClientFactory.CreateClient();
|
|
client.Timeout = TimeSpan.FromSeconds(10);
|
|
using var response = await client.GetAsync(VersionJsonUrl, ct);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var json = await response.Content.ReadAsStringAsync(ct);
|
|
using var doc = JsonDocument.Parse(json);
|
|
var root = doc.RootElement;
|
|
|
|
string? version = root.TryGetProperty("version", out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null;
|
|
string? bootstrapVersion = root.TryGetProperty("bootstrapVersion", out var bv) && bv.ValueKind == JsonValueKind.String ? bv.GetString() : null;
|
|
string? sha256 = root.TryGetProperty("sha256", out var sh) && sh.ValueKind == JsonValueKind.String ? sh.GetString() : null;
|
|
|
|
var result = new LatestVersions(
|
|
AgentVersion: version,
|
|
BootstrapVersion: bootstrapVersion,
|
|
AgentSha256: sha256,
|
|
FetchedAtUtc: DateTimeOffset.UtcNow,
|
|
FromCache: false);
|
|
|
|
_cache.Set(CacheKey, result, CacheDuration);
|
|
_logger.LogInformation("LatestVersions refreshed: agent={AgentVersion}, bootstrap={BootstrapVersion}", version, bootstrapVersion);
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to fetch {Url}; caching null for {Minutes}m", VersionJsonUrl, FailureCacheDuration.TotalMinutes);
|
|
// Cache failure briefly to avoid hammering blob on repeated calls
|
|
_cache.Set(CacheKey, (LatestVersions?)null, FailureCacheDuration);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|