feat: Add Scanner CI runner and related artifacts
- 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:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user