105 lines
3.7 KiB
C#
105 lines
3.7 KiB
C#
using System.Text.Json;
|
|
|
|
namespace StellaOps.Concelier.WebService.Services;
|
|
|
|
internal static class IncidentFileStore
|
|
{
|
|
private static readonly JsonSerializerOptions SerializerOptions = new()
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
WriteIndented = false,
|
|
};
|
|
|
|
public static string GetIncidentFilePath(ConcelierOptions.EvidenceBundleOptions evidenceOptions, string tenant, string advisoryKey)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(evidenceOptions);
|
|
ArgumentNullException.ThrowIfNull(tenant);
|
|
ArgumentNullException.ThrowIfNull(advisoryKey);
|
|
|
|
var root = evidenceOptions.RootAbsolute ?? evidenceOptions.Root ?? string.Empty;
|
|
return Path.Combine(root, tenant.Trim(), advisoryKey.Trim(), "incident.json");
|
|
}
|
|
|
|
public static async Task WriteAsync(
|
|
ConcelierOptions.EvidenceBundleOptions evidenceOptions,
|
|
string tenant,
|
|
string advisoryKey,
|
|
string reason,
|
|
int cooldownMinutes,
|
|
string? pipelineVersion,
|
|
DateTimeOffset now,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var path = GetIncidentFilePath(evidenceOptions, tenant, advisoryKey);
|
|
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
|
|
|
var activatedAt = now.ToUniversalTime();
|
|
var cooldownUntil = activatedAt.AddMinutes(cooldownMinutes);
|
|
|
|
var payload = new IncidentFile
|
|
{
|
|
AdvisoryKey = advisoryKey.Trim(),
|
|
Tenant = tenant.Trim(),
|
|
Reason = string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason.Trim(),
|
|
ActivatedAt = activatedAt,
|
|
CooldownUntil = cooldownUntil,
|
|
PipelineVersion = pipelineVersion,
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(payload, SerializerOptions);
|
|
await File.WriteAllTextAsync(path, json, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
public static async Task<IncidentStatusResponse?> ReadAsync(
|
|
ConcelierOptions.EvidenceBundleOptions evidenceOptions,
|
|
string tenant,
|
|
string advisoryKey,
|
|
DateTimeOffset now,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var path = GetIncidentFilePath(evidenceOptions, tenant, advisoryKey);
|
|
if (!File.Exists(path))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
await using var stream = File.OpenRead(path);
|
|
var payload = await JsonSerializer.DeserializeAsync<IncidentFile>(stream, SerializerOptions, cancellationToken).ConfigureAwait(false);
|
|
if (payload is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var active = payload.CooldownUntil > now.ToUniversalTime();
|
|
return new IncidentStatusResponse(
|
|
payload.AdvisoryKey,
|
|
payload.Tenant,
|
|
payload.Reason,
|
|
payload.ActivatedAt.ToUniversalTime().ToString("O"),
|
|
payload.CooldownUntil.ToUniversalTime().ToString("O"),
|
|
payload.PipelineVersion,
|
|
active);
|
|
}
|
|
|
|
public static Task DeleteAsync(ConcelierOptions.EvidenceBundleOptions evidenceOptions, string tenant, string advisoryKey, CancellationToken cancellationToken)
|
|
{
|
|
var path = GetIncidentFilePath(evidenceOptions, tenant, advisoryKey);
|
|
if (File.Exists(path))
|
|
{
|
|
File.Delete(path);
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private sealed record IncidentFile
|
|
{
|
|
public string AdvisoryKey { get; init; } = string.Empty;
|
|
public string Tenant { get; init; } = string.Empty;
|
|
public string Reason { get; init; } = "unspecified";
|
|
public DateTimeOffset ActivatedAt { get; init; }
|
|
public DateTimeOffset CooldownUntil { get; init; }
|
|
public string? PipelineVersion { get; init; }
|
|
}
|
|
}
|