save progress

This commit is contained in:
StellaOps Bot
2026-01-04 19:08:47 +02:00
parent f7d27c6fda
commit 75611a505f
97 changed files with 4531 additions and 293 deletions

View File

@@ -2,6 +2,7 @@ using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Zastava.Agent.Configuration;
using StellaOps.Zastava.Core.Contracts;
@@ -34,15 +35,18 @@ internal sealed class RuntimeEventsClient : IRuntimeEventsClient
private readonly HttpClient _httpClient;
private readonly IOptionsMonitor<ZastavaAgentOptions> _options;
private readonly ILogger<RuntimeEventsClient> _logger;
private readonly IGuidProvider _guidProvider;
public RuntimeEventsClient(
HttpClient httpClient,
IOptionsMonitor<ZastavaAgentOptions> options,
ILogger<RuntimeEventsClient> logger)
ILogger<RuntimeEventsClient> logger,
IGuidProvider? guidProvider = null)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_guidProvider = guidProvider ?? new SystemGuidProvider();
}
public async Task<RuntimeEventsSubmitResult> SubmitAsync(
@@ -63,7 +67,7 @@ internal sealed class RuntimeEventsClient : IRuntimeEventsClient
{
var request = new RuntimeEventsSubmitRequest
{
BatchId = Guid.NewGuid().ToString("N"),
BatchId = _guidProvider.NewGuid().ToString("N"),
Events = envelopes.ToArray()
};

View File

@@ -21,5 +21,6 @@
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Surface.Env/StellaOps.Scanner.Surface.Env.csproj" />
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets/StellaOps.Scanner.Surface.Secrets.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -4,6 +4,7 @@ using System.Text.Json;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Zastava.Agent.Configuration;
using StellaOps.Zastava.Agent.Docker;
@@ -22,16 +23,22 @@ internal sealed class HealthCheckHostedService : BackgroundService
private readonly IDockerSocketClient _dockerClient;
private readonly IOptionsMonitor<ZastavaAgentOptions> _options;
private readonly ILogger<HealthCheckHostedService> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
private HttpListener? _listener;
public HealthCheckHostedService(
IDockerSocketClient dockerClient,
IOptionsMonitor<ZastavaAgentOptions> options,
ILogger<HealthCheckHostedService> logger)
ILogger<HealthCheckHostedService> logger,
TimeProvider? timeProvider = null,
IGuidProvider? guidProvider = null)
{
_dockerClient = dockerClient ?? throw new ArgumentNullException(nameof(dockerClient));
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? new SystemGuidProvider();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -184,7 +191,7 @@ internal sealed class HealthCheckHostedService : BackgroundService
{
Status = overallHealthy ? "healthy" : "unhealthy",
Checks = checks,
Timestamp = DateTimeOffset.UtcNow
Timestamp = _timeProvider.GetUtcNow()
};
return (overallHealthy ? 200 : 503, response);
@@ -203,7 +210,7 @@ internal sealed class HealthCheckHostedService : BackgroundService
{
Status = "ready",
Message = "Agent ready to process container events",
Timestamp = DateTimeOffset.UtcNow
Timestamp = _timeProvider.GetUtcNow()
});
}
@@ -211,7 +218,7 @@ internal sealed class HealthCheckHostedService : BackgroundService
{
Status = "not_ready",
Message = "Docker daemon not reachable",
Timestamp = DateTimeOffset.UtcNow
Timestamp = _timeProvider.GetUtcNow()
});
}
catch (Exception ex)
@@ -220,16 +227,16 @@ internal sealed class HealthCheckHostedService : BackgroundService
{
Status = "not_ready",
Message = $"Ready check failed: {ex.Message}",
Timestamp = DateTimeOffset.UtcNow
Timestamp = _timeProvider.GetUtcNow()
});
}
}
private static bool IsDirectoryWritable(string path)
private bool IsDirectoryWritable(string path)
{
try
{
var testFile = Path.Combine(path, $".healthcheck-{Guid.NewGuid():N}");
var testFile = Path.Combine(path, $".healthcheck-{_guidProvider.NewGuid():N}");
File.WriteAllText(testFile, "test");
File.Delete(testFile);
return true;

View File

@@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
using System.Threading.Channels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Zastava.Agent.Configuration;
using StellaOps.Zastava.Core.Contracts;
using StellaOps.Zastava.Core.Serialization;
@@ -31,6 +32,7 @@ internal sealed class RuntimeEventBuffer : IRuntimeEventBuffer
private readonly string _spoolPath;
private readonly ILogger<RuntimeEventBuffer> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
private readonly long _maxDiskBytes;
private readonly int _capacity;
@@ -39,11 +41,13 @@ internal sealed class RuntimeEventBuffer : IRuntimeEventBuffer
public RuntimeEventBuffer(
IOptions<ZastavaAgentOptions> agentOptions,
TimeProvider timeProvider,
ILogger<RuntimeEventBuffer> logger)
ILogger<RuntimeEventBuffer> logger,
IGuidProvider? guidProvider = null)
{
ArgumentNullException.ThrowIfNull(agentOptions);
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_guidProvider = guidProvider ?? new SystemGuidProvider();
var options = agentOptions.Value ?? throw new ArgumentNullException(nameof(agentOptions));
@@ -178,7 +182,7 @@ internal sealed class RuntimeEventBuffer : IRuntimeEventBuffer
private async Task<string> PersistAsync(byte[] payload, CancellationToken cancellationToken)
{
var timestamp = _timeProvider.GetUtcNow().UtcTicks;
var fileName = $"{timestamp:D20}-{Guid.NewGuid():N}{FileExtension}";
var fileName = $"{timestamp:D20}-{_guidProvider.NewGuid():N}{FileExtension}";
var filePath = Path.Combine(_spoolPath, fileName);
Directory.CreateDirectory(_spoolPath);

View File

@@ -16,18 +16,21 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
private readonly IRuntimeEventsClient _eventsClient;
private readonly IOptionsMonitor<ZastavaAgentOptions> _options;
private readonly ILogger<RuntimeEventDispatchService> _logger;
private readonly TimeProvider _timeProvider;
private readonly Random _jitterRandom = new();
public RuntimeEventDispatchService(
IRuntimeEventBuffer eventBuffer,
IRuntimeEventsClient eventsClient,
IOptionsMonitor<ZastavaAgentOptions> options,
ILogger<RuntimeEventDispatchService> logger)
ILogger<RuntimeEventDispatchService> logger,
TimeProvider? timeProvider = null)
{
_eventBuffer = eventBuffer ?? throw new ArgumentNullException(nameof(eventBuffer));
_eventsClient = eventsClient ?? throw new ArgumentNullException(nameof(eventsClient));
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timeProvider = timeProvider ?? TimeProvider.System;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -43,7 +46,7 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
flushInterval);
var batch = new List<RuntimeEventBufferItem>(batchSize);
var lastFlush = DateTimeOffset.UtcNow;
var lastFlush = _timeProvider.GetUtcNow();
var failureCount = 0;
try
@@ -53,7 +56,7 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
batch.Add(item);
var shouldFlush = batch.Count >= batchSize ||
(batch.Count > 0 && DateTimeOffset.UtcNow - lastFlush >= flushInterval);
(batch.Count > 0 && _timeProvider.GetUtcNow() - lastFlush >= flushInterval);
if (shouldFlush)
{
@@ -68,7 +71,7 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
}
batch.Clear();
lastFlush = DateTimeOffset.UtcNow;
lastFlush = _timeProvider.GetUtcNow();
}
}
}

View File

@@ -23,6 +23,7 @@ public sealed class EbpfProbeManager : IProbeManager, IAsyncDisposable
private readonly IRuntimeSignalCollector _signalCollector;
private readonly ISignalPublisher _signalPublisher;
private readonly EbpfProbeManagerOptions _options;
private readonly TimeProvider _timeProvider;
private readonly ConcurrentDictionary<string, SignalCollectionHandle> _activeHandles;
private bool _disposed;
@@ -30,12 +31,14 @@ public sealed class EbpfProbeManager : IProbeManager, IAsyncDisposable
ILogger<EbpfProbeManager> logger,
IRuntimeSignalCollector signalCollector,
ISignalPublisher signalPublisher,
IOptions<EbpfProbeManagerOptions> options)
IOptions<EbpfProbeManagerOptions> options,
TimeProvider? timeProvider = null)
{
_logger = logger;
_signalCollector = signalCollector;
_signalPublisher = signalPublisher;
_options = options.Value;
_timeProvider = timeProvider ?? TimeProvider.System;
_activeHandles = new ConcurrentDictionary<string, SignalCollectionHandle>();
}
@@ -277,7 +280,7 @@ public sealed class EbpfProbeManager : IProbeManager, IAsyncDisposable
Namespace = evt.Labels.GetValueOrDefault("io.kubernetes.pod.namespace"),
PodName = evt.Labels.GetValueOrDefault("io.kubernetes.pod.name"),
Summary = summary,
CollectedAt = DateTimeOffset.UtcNow,
CollectedAt = _timeProvider.GetUtcNow(),
};
await _signalPublisher.PublishAsync(message, ct);

View File

@@ -30,10 +30,12 @@ internal sealed class ProcSnapshotCollector : IProcSnapshotCollector
private readonly DotNetAssemblyCollector _dotnetCollector;
private readonly PhpAutoloadCollector _phpCollector;
private readonly ILogger<ProcSnapshotCollector> _logger;
private readonly TimeProvider _timeProvider;
private readonly string _procRoot;
public ProcSnapshotCollector(
IOptions<ZastavaObserverOptions> options,
TimeProvider? timeProvider,
ILoggerFactory loggerFactory)
{
ArgumentNullException.ThrowIfNull(options);
@@ -41,6 +43,7 @@ internal sealed class ProcSnapshotCollector : IProcSnapshotCollector
_procRoot = options.Value.ProcRootPath;
_logger = loggerFactory.CreateLogger<ProcSnapshotCollector>();
_timeProvider = timeProvider ?? TimeProvider.System;
_javaCollector = new JavaClasspathCollector(
_procRoot,
@@ -82,7 +85,7 @@ internal sealed class ProcSnapshotCollector : IProcSnapshotCollector
var document = new ProcSnapshotDocument
{
Id = $"{tenant}:{imageDigest}:{pid}:{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}",
Id = $"{tenant}:{imageDigest}:{pid}:{_timeProvider.GetUtcNow().ToUnixTimeMilliseconds()}",
Tenant = tenant,
ImageDigest = imageDigest,
ContainerId = container.Id,
@@ -91,7 +94,7 @@ internal sealed class ProcSnapshotCollector : IProcSnapshotCollector
Classpath = snapshot.Classpath,
LoadedAssemblies = snapshot.LoadedAssemblies,
AutoloadPaths = snapshot.AutoloadPaths,
CapturedAt = DateTimeOffset.UtcNow
CapturedAt = _timeProvider.GetUtcNow()
};
_logger.LogDebug(

View File

@@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
using System.Threading.Channels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Zastava.Core.Contracts;
using StellaOps.Zastava.Core.Serialization;
using StellaOps.Zastava.Observer.Configuration;
@@ -32,6 +33,7 @@ internal sealed class RuntimeEventBuffer : IRuntimeEventBuffer
private readonly string spoolPath;
private readonly ILogger<RuntimeEventBuffer> logger;
private readonly TimeProvider timeProvider;
private readonly IGuidProvider guidProvider;
private readonly long maxDiskBytes;
private long currentBytes;
@@ -40,11 +42,13 @@ internal sealed class RuntimeEventBuffer : IRuntimeEventBuffer
public RuntimeEventBuffer(
IOptions<ZastavaObserverOptions> observerOptions,
TimeProvider timeProvider,
ILogger<RuntimeEventBuffer> logger)
ILogger<RuntimeEventBuffer> logger,
IGuidProvider? guidProvider = null)
{
ArgumentNullException.ThrowIfNull(observerOptions);
this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.guidProvider = guidProvider ?? SystemGuidProvider.Instance;
var options = observerOptions.Value ?? throw new ArgumentNullException(nameof(observerOptions));
@@ -178,7 +182,7 @@ internal sealed class RuntimeEventBuffer : IRuntimeEventBuffer
private async Task<string> PersistAsync(byte[] payload, CancellationToken cancellationToken)
{
var timestamp = timeProvider.GetUtcNow().UtcTicks;
var fileName = $"{timestamp:D20}-{Guid.NewGuid():N}{FileExtension}";
var fileName = $"{timestamp:D20}-{guidProvider.NewGuid():N}{FileExtension}";
var filePath = Path.Combine(spoolPath, fileName);
Directory.CreateDirectory(spoolPath);

View File

@@ -28,6 +28,7 @@
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets/StellaOps.Scanner.Surface.Secrets.csproj" />
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Surface.Validation/StellaOps.Scanner.Surface.Validation.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos/runtime/v1/runtime.proto" GrpcServices="Client" />

View File

@@ -3,6 +3,7 @@ using System.Net;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Zastava.Core.Contracts;
using StellaOps.Zastava.Observer.Backend;
using StellaOps.Zastava.Observer.Configuration;
@@ -17,6 +18,7 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
private readonly IRuntimeFactsClient runtimeFactsClient;
private readonly IOptionsMonitor<ZastavaObserverOptions> observerOptions;
private readonly TimeProvider timeProvider;
private readonly IGuidProvider guidProvider;
private readonly ILogger<RuntimeEventDispatchService> logger;
public RuntimeEventDispatchService(
@@ -25,6 +27,7 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
IRuntimeFactsClient runtimeFactsClient,
IOptionsMonitor<ZastavaObserverOptions> observerOptions,
TimeProvider timeProvider,
IGuidProvider? guidProvider,
ILogger<RuntimeEventDispatchService> logger)
{
this.buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
@@ -32,6 +35,7 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
this.runtimeFactsClient = runtimeFactsClient ?? throw new ArgumentNullException(nameof(runtimeFactsClient));
this.observerOptions = observerOptions ?? throw new ArgumentNullException(nameof(observerOptions));
this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
this.guidProvider = guidProvider ?? SystemGuidProvider.Instance;
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -127,7 +131,7 @@ internal sealed class RuntimeEventDispatchService : BackgroundService
var request = new RuntimeEventsIngestRequest
{
BatchId = $"obs-{timeProvider.GetUtcNow():yyyyMMddTHHmmssfff}-{Guid.NewGuid():N}",
BatchId = $"obs-{timeProvider.GetUtcNow():yyyyMMddTHHmmssfff}-{guidProvider.NewGuid():N}",
Events = envelopes
};

View File

@@ -6,14 +6,17 @@ public sealed class WebhookCertificateHealthCheck : IHealthCheck
{
private readonly IWebhookCertificateProvider _certificateProvider;
private readonly ILogger<WebhookCertificateHealthCheck> _logger;
private readonly TimeProvider _timeProvider;
private readonly TimeSpan _expiryThreshold = TimeSpan.FromDays(7);
public WebhookCertificateHealthCheck(
IWebhookCertificateProvider certificateProvider,
ILogger<WebhookCertificateHealthCheck> logger)
ILogger<WebhookCertificateHealthCheck> logger,
TimeProvider? timeProvider = null)
{
_certificateProvider = certificateProvider;
_logger = logger;
_timeProvider = timeProvider ?? TimeProvider.System;
}
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
@@ -22,7 +25,7 @@ public sealed class WebhookCertificateHealthCheck : IHealthCheck
{
var certificate = _certificateProvider.GetCertificate();
var expires = certificate.NotAfter.ToUniversalTime();
var remaining = expires - DateTimeOffset.UtcNow;
var remaining = expires - _timeProvider.GetUtcNow();
if (remaining <= TimeSpan.Zero)
{