using System.Collections.Concurrent; using System.Text.Json; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using StellaOps.Zastava.Webhook.Backend; using StellaOps.Zastava.Webhook.Configuration; namespace StellaOps.Zastava.Webhook.Admission; internal sealed class RuntimePolicyCache { private readonly ConcurrentDictionary entries = new(StringComparer.Ordinal); private readonly ILogger logger; private readonly TimeProvider timeProvider; public RuntimePolicyCache(IOptions options, TimeProvider timeProvider, ILogger logger) { this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); ArgumentNullException.ThrowIfNull(options); var admission = options.Value.Admission; if (!string.IsNullOrWhiteSpace(admission.CacheSeedPath) && File.Exists(admission.CacheSeedPath)) { TryLoadSeed(admission.CacheSeedPath!); } } public bool TryGet(string digest, out RuntimePolicyImageResult result) { if (entries.TryGetValue(digest, out var entry)) { if (timeProvider.GetUtcNow() <= entry.ExpiresAtUtc) { result = entry.Result; return true; } entries.TryRemove(digest, out _); } result = default!; return false; } public void Set(string digest, RuntimePolicyImageResult result, DateTimeOffset expiresAtUtc) { entries[digest] = new CacheEntry(result, expiresAtUtc); } private void TryLoadSeed(string path) { try { var payload = File.ReadAllText(path); var seed = JsonSerializer.Deserialize(payload, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); if (seed?.Results is null || seed.Results.Count == 0) { logger.LogDebug("Runtime policy cache seed file {Path} empty or invalid.", path); return; } var ttlSeconds = Math.Max(1, seed.TtlSeconds); var expires = timeProvider.GetUtcNow().AddSeconds(ttlSeconds); foreach (var pair in seed.Results) { Set(pair.Key, pair.Value, expires); } logger.LogInformation("Loaded {Count} runtime policy cache seed entries from {Path}.", seed.Results.Count, path); } catch (Exception ex) { logger.LogWarning(ex, "Failed to load runtime policy cache seed from {Path}.", path); } } private sealed record CacheEntry(RuntimePolicyImageResult Result, DateTimeOffset ExpiresAtUtc); }