feat: Add Scanner CI runner and related artifacts
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Airgap Sealed CI Smoke / sealed-smoke (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled

- Implemented `run-scanner-ci.sh` to build and run tests for the Scanner solution with a warmed NuGet cache.
- Created `excititor-vex-traces.json` dashboard for monitoring Excititor VEX observations.
- Added Docker Compose configuration for the OTLP span sink in `docker-compose.spansink.yml`.
- Configured OpenTelemetry collector in `otel-spansink.yaml` to receive and process traces.
- Developed `run-spansink.sh` script to run the OTLP span sink for Excititor traces.
- Introduced `FileSystemRiskBundleObjectStore` for storing risk bundle artifacts in the filesystem.
- Built `RiskBundleBuilder` for creating risk bundles with associated metadata and providers.
- Established `RiskBundleJob` to execute the risk bundle creation and storage process.
- Defined models for risk bundle inputs, entries, and manifests in `RiskBundleModels.cs`.
- Implemented signing functionality for risk bundle manifests with `HmacRiskBundleManifestSigner`.
- Created unit tests for `RiskBundleBuilder`, `RiskBundleJob`, and signing functionality to ensure correctness.
- Added filesystem artifact reader tests to validate manifest parsing and artifact listing.
- Included test manifests for egress scenarios in the task runner tests.
- Developed timeline query service tests to verify tenant and event ID handling.
This commit is contained in:
StellaOps Bot
2025-11-30 19:12:35 +02:00
parent 17d45a6d30
commit 71e9a56cfd
92 changed files with 2596 additions and 387 deletions

View File

@@ -0,0 +1,92 @@
using System.Security.Cryptography;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TaskRunner.Worker.Services;
namespace StellaOps.TaskRunner.Infrastructure.Execution;
/// <summary>
/// Executes built-in bundle ingestion helpers: validates checksums and stages bundles to a deterministic destination.
/// </summary>
public sealed class BundleIngestionStepExecutor : IPackRunStepExecutor
{
private const string BuiltInUses = "bundle.ingest";
private readonly string stagingRoot;
private readonly ILogger<BundleIngestionStepExecutor> logger;
public BundleIngestionStepExecutor(IOptions<PackRunWorkerOptions> options, ILogger<BundleIngestionStepExecutor> logger)
{
ArgumentNullException.ThrowIfNull(options);
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
stagingRoot = Path.Combine(options.Value.ArtifactsPath, "bundles");
Directory.CreateDirectory(stagingRoot);
}
public Task<PackRunStepExecutionResult> ExecuteAsync(
PackRunExecutionStep step,
IReadOnlyDictionary<string, TaskPackPlanParameterValue> parameters,
CancellationToken cancellationToken)
{
// Non-bundle helpers are treated as no-op success for now.
if (!IsBundleIngestStep(step))
{
return Task.FromResult(PackRunStepExecutionResult.Success());
}
var sourcePath = GetString(parameters, "path");
if (string.IsNullOrWhiteSpace(sourcePath) || !File.Exists(sourcePath))
{
return Task.FromResult(PackRunStepExecutionResult.Failure("Bundle path missing or not found."));
}
var checksum = GetString(parameters, "checksum") ?? GetString(parameters, "checksumSha256");
if (!string.IsNullOrWhiteSpace(checksum))
{
var actual = ComputeSha256(sourcePath);
if (!checksum.Equals(actual, StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(PackRunStepExecutionResult.Failure($"Checksum mismatch: expected {checksum}, actual {actual}."));
}
}
var destination = GetString(parameters, "destinationPath")
?? Path.Combine(stagingRoot, Path.GetFileName(sourcePath));
try
{
Directory.CreateDirectory(Path.GetDirectoryName(destination)!);
File.Copy(sourcePath, destination, overwrite: true);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to stage bundle to {Destination}.", destination);
return Task.FromResult(PackRunStepExecutionResult.Failure("Failed to stage bundle."));
}
return Task.FromResult(PackRunStepExecutionResult.Success());
}
private static string? GetString(IReadOnlyDictionary<string, TaskPackPlanParameterValue> parameters, string key)
{
if (!parameters.TryGetValue(key, out var value) || value.Value is not JsonValue jsonValue)
{
return null;
}
return jsonValue.TryGetValue<string>(out var result) ? result : null;
}
private static bool IsBundleIngestStep(PackRunExecutionStep step)
=> !string.IsNullOrWhiteSpace(step.Uses) &&
step.Kind == PackRunStepKind.Run &&
step.Uses.Contains(BuiltInUses, StringComparison.OrdinalIgnoreCase);
private static string ComputeSha256(string path)
{
using var stream = File.OpenRead(path);
var hash = SHA256.HashData(stream);
return Convert.ToHexString(hash).ToLowerInvariant();
}
}

View File

@@ -1,3 +1,5 @@
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Driver;
using StellaOps.TaskRunner.Core.Configuration;
using StellaOps.TaskRunner.Core.Execution;
@@ -36,7 +38,7 @@ public sealed class MongoPackRunArtifactReader : IPackRunArtifactReader
doc.Status,
doc.Notes,
new DateTimeOffset(doc.CapturedAt, TimeSpan.Zero),
doc.Expression?.ToJson()))
doc.Expression?.ToJson(new JsonWriterSettings())))
.ToList();
}
}