Files
git.stella-ops.org/src/Zastava/StellaOps.Zastava.Webhook/Admission/ImageDigestResolver.cs
2025-10-28 15:10:40 +02:00

53 lines
1.9 KiB
C#

using System.Text.RegularExpressions;
namespace StellaOps.Zastava.Webhook.Admission;
internal interface IImageDigestResolver
{
Task<ImageResolutionResult> ResolveAsync(string imageReference, CancellationToken cancellationToken);
}
internal sealed class ImageDigestResolver : IImageDigestResolver
{
private static readonly Regex DigestPattern = new(@"(?<algorithm>[a-z0-9_+.-]+):(?<digest>[a-f0-9]{32,})", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
public Task<ImageResolutionResult> ResolveAsync(string imageReference, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(imageReference))
{
return Task.FromResult(ImageResolutionResult.CreateFailure(imageReference, "image.reference.empty"));
}
if (imageReference.Contains('@', StringComparison.Ordinal))
{
var digest = imageReference[(imageReference.IndexOf('@') + 1)..];
if (DigestPattern.IsMatch(digest))
{
return Task.FromResult(ImageResolutionResult.CreateSuccess(imageReference, digest));
}
return Task.FromResult(ImageResolutionResult.CreateFailure(imageReference, "image.reference.invalid_digest"));
}
if (DigestPattern.IsMatch(imageReference))
{
return Task.FromResult(ImageResolutionResult.CreateSuccess(imageReference, imageReference));
}
return Task.FromResult(ImageResolutionResult.CreateFailure(imageReference, "image.reference.tag_unresolved"));
}
}
internal sealed record ImageResolutionResult(
string Original,
string? ResolvedDigest,
bool Success,
string? FailureReason)
{
public static ImageResolutionResult CreateSuccess(string original, string digest)
=> new(original, digest, true, null);
public static ImageResolutionResult CreateFailure(string original, string reason)
=> new(original, null, false, reason);
}