Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Policy.Exceptions.Models;
|
||||
using StellaOps.Policy.Exceptions.Services;
|
||||
|
||||
namespace StellaOps.Policy.Engine.BuildGate;
|
||||
|
||||
/// <summary>
|
||||
/// Build gate that checks recheck policies before allowing deployment.
|
||||
/// </summary>
|
||||
public sealed class ExceptionRecheckGate : IBuildGate
|
||||
{
|
||||
private readonly IExceptionEvaluator _exceptionEvaluator;
|
||||
private readonly IRecheckEvaluationService _recheckService;
|
||||
private readonly ILogger<ExceptionRecheckGate> _logger;
|
||||
|
||||
public ExceptionRecheckGate(
|
||||
IExceptionEvaluator exceptionEvaluator,
|
||||
IRecheckEvaluationService recheckService,
|
||||
ILogger<ExceptionRecheckGate> logger)
|
||||
{
|
||||
_exceptionEvaluator = exceptionEvaluator ?? throw new ArgumentNullException(nameof(exceptionEvaluator));
|
||||
_recheckService = recheckService ?? throw new ArgumentNullException(nameof(recheckService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public string GateName => "exception-recheck";
|
||||
public int Priority => 100;
|
||||
|
||||
public async Task<BuildGateResult> EvaluateAsync(
|
||||
BuildGateContext context,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Evaluating exception recheck gate for artifact {Artifact}",
|
||||
context.ArtifactDigest);
|
||||
|
||||
var evaluation = await _exceptionEvaluator.EvaluateAsync(new FindingContext
|
||||
{
|
||||
ArtifactDigest = context.ArtifactDigest,
|
||||
Environment = context.Environment,
|
||||
TenantId = context.TenantId
|
||||
}, ct).ConfigureAwait(false);
|
||||
|
||||
var blockers = new List<string>();
|
||||
var warnings = new List<string>();
|
||||
|
||||
foreach (var exception in evaluation.MatchingExceptions)
|
||||
{
|
||||
if (exception.RecheckPolicy is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var evalContext = new RecheckEvaluationContext
|
||||
{
|
||||
ArtifactDigest = context.ArtifactDigest,
|
||||
Environment = context.Environment,
|
||||
EvaluatedAt = context.EvaluatedAt,
|
||||
ReachGraphChanged = context.ReachGraphChanged,
|
||||
EpssScore = context.EpssScore,
|
||||
CvssScore = context.CvssScore,
|
||||
UnknownsCount = context.UnknownsCount,
|
||||
NewCveInPackage = context.NewCveInPackage,
|
||||
KevFlagged = context.KevFlagged,
|
||||
VexStatusChanged = context.VexStatusChanged,
|
||||
PackageVersionChanged = context.PackageVersionChanged
|
||||
};
|
||||
|
||||
var result = await _recheckService.EvaluateAsync(exception, evalContext, ct).ConfigureAwait(false);
|
||||
if (!result.IsTriggered)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var triggered in result.TriggeredConditions)
|
||||
{
|
||||
var message = $"Exception {exception.ExceptionId}: {triggered.Description} ({triggered.Action})";
|
||||
|
||||
if (triggered.Action is RecheckAction.Block or RecheckAction.Revoke or RecheckAction.RequireReapproval)
|
||||
{
|
||||
blockers.Add(message);
|
||||
}
|
||||
else if (triggered.Action == RecheckAction.Warn)
|
||||
{
|
||||
warnings.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blockers.Count > 0)
|
||||
{
|
||||
return new BuildGateResult
|
||||
{
|
||||
Passed = false,
|
||||
GateName = GateName,
|
||||
Message = $"Recheck policy blocking: {string.Join("; ", blockers)}",
|
||||
Blockers = blockers.ToImmutableArray(),
|
||||
Warnings = warnings.ToImmutableArray()
|
||||
};
|
||||
}
|
||||
|
||||
return new BuildGateResult
|
||||
{
|
||||
Passed = true,
|
||||
GateName = GateName,
|
||||
Message = warnings.Count > 0
|
||||
? $"Passed with {warnings.Count} warning(s)"
|
||||
: "All exception recheck policies satisfied",
|
||||
Blockers = [],
|
||||
Warnings = warnings.ToImmutableArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public interface IBuildGate
|
||||
{
|
||||
string GateName { get; }
|
||||
int Priority { get; }
|
||||
Task<BuildGateResult> EvaluateAsync(BuildGateContext context, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
public sealed record BuildGateContext
|
||||
{
|
||||
public required string ArtifactDigest { get; init; }
|
||||
public required string Environment { get; init; }
|
||||
public string? Branch { get; init; }
|
||||
public string? PipelineId { get; init; }
|
||||
public Guid? TenantId { get; init; }
|
||||
public DateTimeOffset EvaluatedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
public bool ReachGraphChanged { get; init; }
|
||||
public decimal? EpssScore { get; init; }
|
||||
public decimal? CvssScore { get; init; }
|
||||
public int? UnknownsCount { get; init; }
|
||||
public bool NewCveInPackage { get; init; }
|
||||
public bool KevFlagged { get; init; }
|
||||
public bool VexStatusChanged { get; init; }
|
||||
public bool PackageVersionChanged { get; init; }
|
||||
}
|
||||
|
||||
public sealed record BuildGateResult
|
||||
{
|
||||
public required bool Passed { get; init; }
|
||||
public required string GateName { get; init; }
|
||||
public required string Message { get; init; }
|
||||
public required ImmutableArray<string> Blockers { get; init; }
|
||||
public required ImmutableArray<string> Warnings { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user