143 lines
4.1 KiB
C#
143 lines
4.1 KiB
C#
using StellaOps.Cli.Services.Models;
|
|
|
|
namespace StellaOps.Cli.Services;
|
|
|
|
internal static class OciImageReferenceParser
|
|
{
|
|
public static OciImageReference Parse(string reference)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(reference))
|
|
{
|
|
throw new ArgumentException("Image reference is required.", nameof(reference));
|
|
}
|
|
|
|
reference = reference.Trim();
|
|
if (reference.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
|
|
reference.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
|
|
reference.StartsWith("docker://", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return ParseUri(reference);
|
|
}
|
|
|
|
var registry = string.Empty;
|
|
var remainder = reference;
|
|
var parts = reference.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
if (parts.Length > 1 && LooksLikeRegistry(parts[0]))
|
|
{
|
|
registry = parts[0];
|
|
remainder = string.Join('/', parts.Skip(1));
|
|
}
|
|
else
|
|
{
|
|
registry = "docker.io";
|
|
}
|
|
|
|
var repository = remainder;
|
|
string? tag = null;
|
|
string? digest = null;
|
|
|
|
var atIndex = remainder.LastIndexOf('@');
|
|
if (atIndex >= 0)
|
|
{
|
|
repository = remainder[..atIndex];
|
|
digest = remainder[(atIndex + 1)..];
|
|
}
|
|
else
|
|
{
|
|
var lastColon = remainder.LastIndexOf(':');
|
|
var lastSlash = remainder.LastIndexOf('/');
|
|
if (lastColon > lastSlash)
|
|
{
|
|
repository = remainder[..lastColon];
|
|
tag = remainder[(lastColon + 1)..];
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(repository))
|
|
{
|
|
throw new ArgumentException("Image repository is required.", nameof(reference));
|
|
}
|
|
|
|
if (string.Equals(registry, "docker.io", StringComparison.OrdinalIgnoreCase) &&
|
|
!repository.Contains('/', StringComparison.Ordinal))
|
|
{
|
|
repository = $"library/{repository}";
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(tag) && string.IsNullOrWhiteSpace(digest))
|
|
{
|
|
tag = "latest";
|
|
}
|
|
|
|
return new OciImageReference
|
|
{
|
|
Registry = registry,
|
|
Repository = repository,
|
|
Tag = tag,
|
|
Digest = digest,
|
|
Original = reference
|
|
};
|
|
}
|
|
|
|
private static OciImageReference ParseUri(string reference)
|
|
{
|
|
if (!Uri.TryCreate(reference, UriKind.Absolute, out var uri))
|
|
{
|
|
throw new ArgumentException("Invalid image reference URI.", nameof(reference));
|
|
}
|
|
|
|
var registry = uri.Authority;
|
|
var remainder = uri.AbsolutePath.Trim('/');
|
|
|
|
string? tag = null;
|
|
string? digest = null;
|
|
|
|
var atIndex = remainder.LastIndexOf('@');
|
|
if (atIndex >= 0)
|
|
{
|
|
digest = remainder[(atIndex + 1)..];
|
|
remainder = remainder[..atIndex];
|
|
}
|
|
else
|
|
{
|
|
var lastColon = remainder.LastIndexOf(':');
|
|
if (lastColon > remainder.LastIndexOf('/'))
|
|
{
|
|
tag = remainder[(lastColon + 1)..];
|
|
remainder = remainder[..lastColon];
|
|
}
|
|
}
|
|
|
|
if (string.Equals(registry, "docker.io", StringComparison.OrdinalIgnoreCase) &&
|
|
!remainder.Contains('/', StringComparison.Ordinal))
|
|
{
|
|
remainder = $"library/{remainder}";
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(tag) && string.IsNullOrWhiteSpace(digest))
|
|
{
|
|
tag = "latest";
|
|
}
|
|
|
|
return new OciImageReference
|
|
{
|
|
Registry = registry,
|
|
Repository = remainder,
|
|
Tag = tag,
|
|
Digest = digest,
|
|
Original = reference
|
|
};
|
|
}
|
|
|
|
private static bool LooksLikeRegistry(string value)
|
|
{
|
|
if (string.Equals(value, "localhost", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return value.Contains('.', StringComparison.Ordinal) || value.Contains(':', StringComparison.Ordinal);
|
|
}
|
|
}
|