up
This commit is contained in:
148
src/StellaOps.Zastava.Observer/Worker/RuntimeEventFactory.cs
Normal file
148
src/StellaOps.Zastava.Observer/Worker/RuntimeEventFactory.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using StellaOps.Zastava.Core.Contracts;
|
||||
using StellaOps.Zastava.Observer.Configuration;
|
||||
using StellaOps.Zastava.Observer.ContainerRuntime;
|
||||
using StellaOps.Zastava.Observer.ContainerRuntime.Cri;
|
||||
using StellaOps.Zastava.Observer.Runtime;
|
||||
|
||||
namespace StellaOps.Zastava.Observer.Worker;
|
||||
|
||||
internal static class RuntimeEventFactory
|
||||
{
|
||||
public static RuntimeEventEnvelope Create(
|
||||
ContainerLifecycleEvent lifecycleEvent,
|
||||
ContainerRuntimeEndpointOptions endpoint,
|
||||
CriRuntimeIdentity identity,
|
||||
string tenant,
|
||||
string nodeName,
|
||||
RuntimeProcessCapture? capture = null,
|
||||
RuntimePosture? posture = null,
|
||||
IReadOnlyList<RuntimeEvidence>? additionalEvidence = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(lifecycleEvent);
|
||||
ArgumentNullException.ThrowIfNull(endpoint);
|
||||
ArgumentNullException.ThrowIfNull(identity);
|
||||
ArgumentNullException.ThrowIfNull(tenant);
|
||||
ArgumentNullException.ThrowIfNull(nodeName);
|
||||
|
||||
var snapshot = lifecycleEvent.Snapshot;
|
||||
var workloadLabels = snapshot.Labels ?? new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
var annotations = snapshot.Annotations is null
|
||||
? new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
: new Dictionary<string, string>(snapshot.Annotations, StringComparer.Ordinal);
|
||||
|
||||
var platform = ResolvePlatform(workloadLabels, endpoint);
|
||||
var runtimeEvent = new RuntimeEvent
|
||||
{
|
||||
EventId = ComputeEventId(nodeName, lifecycleEvent),
|
||||
When = lifecycleEvent.Timestamp,
|
||||
Kind = lifecycleEvent.Kind == ContainerLifecycleEventKind.Start
|
||||
? RuntimeEventKind.ContainerStart
|
||||
: RuntimeEventKind.ContainerStop,
|
||||
Tenant = tenant,
|
||||
Node = nodeName,
|
||||
Runtime = new RuntimeEngine
|
||||
{
|
||||
Engine = endpoint.Engine.ToEngineString(),
|
||||
Version = identity.RuntimeVersion
|
||||
},
|
||||
Workload = new RuntimeWorkload
|
||||
{
|
||||
Platform = platform,
|
||||
Namespace = TryGet(workloadLabels, CriLabelKeys.PodNamespace),
|
||||
Pod = TryGet(workloadLabels, CriLabelKeys.PodName),
|
||||
Container = TryGet(workloadLabels, CriLabelKeys.ContainerName) ?? snapshot.Name,
|
||||
ContainerId = $"{endpoint.Engine.ToEngineString()}://{snapshot.Id}",
|
||||
ImageRef = ResolveImageRef(snapshot),
|
||||
Owner = null
|
||||
},
|
||||
Process = capture?.Process,
|
||||
LoadedLibraries = capture?.Libraries ?? Array.Empty<RuntimeLoadedLibrary>(),
|
||||
Posture = posture,
|
||||
Evidence = MergeEvidence(capture?.Evidence, additionalEvidence),
|
||||
Annotations = annotations.Count == 0 ? null : new SortedDictionary<string, string>(annotations, StringComparer.Ordinal)
|
||||
};
|
||||
|
||||
return RuntimeEventEnvelope.Create(runtimeEvent, ZastavaContractVersions.RuntimeEvent);
|
||||
}
|
||||
|
||||
private static string ResolvePlatform(IReadOnlyDictionary<string, string> labels, ContainerRuntimeEndpointOptions endpoint)
|
||||
{
|
||||
if (labels.ContainsKey(CriLabelKeys.PodName))
|
||||
{
|
||||
return "kubernetes";
|
||||
}
|
||||
|
||||
return endpoint.Engine.ToEngineString();
|
||||
}
|
||||
|
||||
private static IReadOnlyList<RuntimeEvidence> MergeEvidence(
|
||||
IReadOnlyList<RuntimeEvidence>? primary,
|
||||
IReadOnlyList<RuntimeEvidence>? secondary)
|
||||
{
|
||||
if ((primary is null || primary.Count == 0) && (secondary is null || secondary.Count == 0))
|
||||
{
|
||||
return Array.Empty<RuntimeEvidence>();
|
||||
}
|
||||
|
||||
if (secondary is null || secondary.Count == 0)
|
||||
{
|
||||
return primary ?? Array.Empty<RuntimeEvidence>();
|
||||
}
|
||||
|
||||
if (primary is null || primary.Count == 0)
|
||||
{
|
||||
return secondary;
|
||||
}
|
||||
|
||||
var merged = new List<RuntimeEvidence>(primary.Count + secondary.Count);
|
||||
merged.AddRange(primary);
|
||||
merged.AddRange(secondary);
|
||||
return merged;
|
||||
}
|
||||
|
||||
private static string? ResolveImageRef(CriContainerInfo snapshot)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.ImageRef))
|
||||
{
|
||||
return snapshot.ImageRef;
|
||||
}
|
||||
|
||||
return snapshot.Image;
|
||||
}
|
||||
|
||||
private static string? TryGet(IReadOnlyDictionary<string, string> dictionary, string key)
|
||||
{
|
||||
if (dictionary.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ComputeEventId(string nodeName, ContainerLifecycleEvent lifecycleEvent)
|
||||
{
|
||||
var builder = new StringBuilder()
|
||||
.Append(nodeName)
|
||||
.Append('|')
|
||||
.Append(lifecycleEvent.Snapshot.Id)
|
||||
.Append('|')
|
||||
.Append(lifecycleEvent.Timestamp.ToUniversalTime().Ticks)
|
||||
.Append('|')
|
||||
.Append((int)lifecycleEvent.Kind);
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(builder.ToString());
|
||||
Span<byte> hash = stackalloc byte[16];
|
||||
if (!MD5.TryHashData(bytes, hash, out _))
|
||||
{
|
||||
using var md5 = MD5.Create();
|
||||
hash = md5.ComputeHash(bytes).AsSpan(0, 16);
|
||||
}
|
||||
|
||||
var guid = new Guid(hash);
|
||||
return guid.ToString("N");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user