using Microsoft.Extensions.Logging;
namespace StellaOps.Policy.Unknowns;
///
/// Unknowns budget configuration for policy evaluation.
///
public sealed record UnknownsBudgetConfig
{
///
/// Maximum allowed critical severity unknowns.
///
public int MaxCriticalUnknowns { get; init; } = 0;
///
/// Maximum allowed high severity unknowns.
///
public int MaxHighUnknowns { get; init; } = 5;
///
/// Maximum allowed medium severity unknowns.
///
public int MaxMediumUnknowns { get; init; } = 20;
///
/// Maximum allowed low severity unknowns.
///
public int MaxLowUnknowns { get; init; } = 50;
///
/// Maximum total unknowns across all severities.
///
public int? MaxTotalUnknowns { get; init; }
///
/// Action to take when budget is exceeded.
///
public UnknownsBudgetAction Action { get; init; } = UnknownsBudgetAction.Block;
///
/// Environment-specific overrides.
///
public Dictionary? EnvironmentOverrides { get; init; }
}
///
/// Action to take when unknowns budget is exceeded.
///
public enum UnknownsBudgetAction
{
///
/// Block deployment/approval.
///
Block,
///
/// Warn but allow deployment.
///
Warn,
///
/// Log only, no enforcement.
///
Log
}
///
/// Counts of unknowns by severity.
///
public sealed record UnknownsCounts
{
public int Critical { get; init; }
public int High { get; init; }
public int Medium { get; init; }
public int Low { get; init; }
public int Total => Critical + High + Medium + Low;
}
///
/// Result of unknowns budget enforcement.
///
public sealed record UnknownsBudgetResult
{
public required bool WithinBudget { get; init; }
public required UnknownsCounts Counts { get; init; }
public required UnknownsBudgetConfig Budget { get; init; }
public required UnknownsBudgetAction Action { get; init; }
public IReadOnlyList? Violations { get; init; }
}
///
/// Enforces unknowns budget for policy decisions.
///
public sealed class UnknownsBudgetEnforcer
{
private readonly ILogger _logger;
public UnknownsBudgetEnforcer(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
///
/// Evaluate unknowns counts against budget.
///
public UnknownsBudgetResult Evaluate(
UnknownsCounts counts,
UnknownsBudgetConfig budget,
string? environment = null)
{
ArgumentNullException.ThrowIfNull(counts);
ArgumentNullException.ThrowIfNull(budget);
var effectiveBudget = GetEffectiveBudget(budget, environment);
var violations = new List();
if (counts.Critical > effectiveBudget.MaxCriticalUnknowns)
{
violations.Add($"Critical unknowns ({counts.Critical}) exceeds budget ({effectiveBudget.MaxCriticalUnknowns})");
}
if (counts.High > effectiveBudget.MaxHighUnknowns)
{
violations.Add($"High unknowns ({counts.High}) exceeds budget ({effectiveBudget.MaxHighUnknowns})");
}
if (counts.Medium > effectiveBudget.MaxMediumUnknowns)
{
violations.Add($"Medium unknowns ({counts.Medium}) exceeds budget ({effectiveBudget.MaxMediumUnknowns})");
}
if (counts.Low > effectiveBudget.MaxLowUnknowns)
{
violations.Add($"Low unknowns ({counts.Low}) exceeds budget ({effectiveBudget.MaxLowUnknowns})");
}
if (effectiveBudget.MaxTotalUnknowns.HasValue &&
counts.Total > effectiveBudget.MaxTotalUnknowns.Value)
{
violations.Add($"Total unknowns ({counts.Total}) exceeds budget ({effectiveBudget.MaxTotalUnknowns.Value})");
}
var withinBudget = violations.Count == 0;
if (!withinBudget)
{
LogViolations(violations, effectiveBudget.Action, environment);
}
return new UnknownsBudgetResult
{
WithinBudget = withinBudget,
Counts = counts,
Budget = effectiveBudget,
Action = effectiveBudget.Action,
Violations = violations
};
}
///
/// Check if deployment should be blocked based on budget result.
///
public bool ShouldBlock(UnknownsBudgetResult result)
{
ArgumentNullException.ThrowIfNull(result);
return !result.WithinBudget && result.Action == UnknownsBudgetAction.Block;
}
private static UnknownsBudgetConfig GetEffectiveBudget(
UnknownsBudgetConfig budget,
string? environment)
{
if (string.IsNullOrWhiteSpace(environment) ||
budget.EnvironmentOverrides is null ||
!budget.EnvironmentOverrides.TryGetValue(environment, out var override_))
{
return budget;
}
return override_;
}
private void LogViolations(
List violations,
UnknownsBudgetAction action,
string? environment)
{
var envStr = string.IsNullOrWhiteSpace(environment) ? "" : $" (env: {environment})";
switch (action)
{
case UnknownsBudgetAction.Block:
_logger.LogError(
"Unknowns budget exceeded{Env}. Blocking deployment. Violations: {Violations}",
envStr,
string.Join("; ", violations));
break;
case UnknownsBudgetAction.Warn:
_logger.LogWarning(
"Unknowns budget exceeded{Env}. Allowing deployment with warning. Violations: {Violations}",
envStr,
string.Join("; ", violations));
break;
case UnknownsBudgetAction.Log:
_logger.LogInformation(
"Unknowns budget exceeded{Env}. Logging only. Violations: {Violations}",
envStr,
string.Join("; ", violations));
break;
}
}
}