Add audit API client

- cleanup
- generate PCAP API
This commit is contained in:
Peter 2021-02-20 15:18:08 +01:00
parent bb46b7f58a
commit 15106dbfd6
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
13 changed files with 151 additions and 58 deletions

View file

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Net.Client;
using INetMock.Client.Audit.Serialization;
using INetMock.Client.Rpc;
namespace INetMock.Client.Audit.Client
{
public class AuditApiClient : IAuditApiClient
{
private readonly Rpc.Audit.AuditClient _auditClient;
public AuditApiClient(string address, GrpcChannelOptions? options = null) : this(new Uri(address), options)
{
}
public AuditApiClient(Uri address, GrpcChannelOptions? options = null) : this(
GrpcChannel.ForAddress(address, options ?? new GrpcChannelOptions()))
{
}
public AuditApiClient(ChannelBase channel)
{
_auditClient = new Rpc.Audit.AuditClient(channel);
}
public async Task<IEnumerable<string>> ListSinksAsync(CancellationToken token = default)
{
var sinks = await _auditClient.ListSinksAsync(new ListSinksRequest(), Metadata.Empty, null, token);
return sinks.Sinks;
}
public async Task<string> RegisterFileSinkAsync(string targetPath, CancellationToken token = default)
{
var resp = await _auditClient.RegisterFileSinkAsync(
new RegisterFileSinkRequest {TargetPath = targetPath},
Metadata.Empty,
null,
token
);
return resp.ResolvedPath;
}
public async Task<bool> RemoveFileSinkAsync(string targetPath, CancellationToken token = default)
{
var resp = await _auditClient.RemoveFileSinkAsync(new RemoveFileSinkRequest
{
TargetPath = targetPath
},
Metadata.Empty,
null,
token
);
return resp.SinkGotRemoved;
}
public IProtoEventReader EventStreamAsync(string watcherName, CancellationToken token = default)
{
var stream = _auditClient.WatchEvents(
new WatchEventsRequest {WatcherName = watcherName},
Metadata.Empty,
null,
token
);
return new EventServerStreamReader(stream);
}
}
}

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
@ -9,11 +8,13 @@ using INetMock.Client.Audit.Details;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit
{ {
public abstract record EventDetails; public abstract record EventDetails;
public record EmptyDetails : EventDetails; public record EmptyDetails : EventDetails;
public record GenericDetails : EventDetails public record GenericDetails : EventDetails
{ {
private readonly Any? _detailsAny; private readonly Any? _detailsAny;
public GenericDetails() public GenericDetails()
{ {
_detailsAny = null; _detailsAny = null;
@ -27,14 +28,16 @@ namespace INetMock.Client.Audit
public static implicit operator HttpDetails(GenericDetails gd) public static implicit operator HttpDetails(GenericDetails gd)
{ {
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new(); if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
if(!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName)) throw new InvalidOperationException(); if (!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName))
throw new InvalidOperationException();
return new HttpDetails(gd._detailsAny); return new HttpDetails(gd._detailsAny);
} }
public static implicit operator DnsDetails(GenericDetails gd) public static implicit operator DnsDetails(GenericDetails gd)
{ {
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new(); if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
if(!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName)) throw new InvalidOperationException(); if (!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName))
throw new InvalidOperationException();
return new DnsDetails(gd._detailsAny); return new DnsDetails(gd._detailsAny);
} }
} }
@ -43,7 +46,6 @@ namespace INetMock.Client.Audit
{ {
public HttpDetails() public HttpDetails()
{ {
} }
public HttpDetails(Any? any) public HttpDetails(Any? any)
@ -51,14 +53,14 @@ namespace INetMock.Client.Audit
if (any == null || any.Value == null) return; if (any == null || any.Value == null) return;
var detailsEntity = HTTPDetailsEntity.Parser.ParseFrom(any.Value); var detailsEntity = HTTPDetailsEntity.Parser.ParseFrom(any.Value);
Method = new HttpMethod(detailsEntity.Method.ToString()); Method = new HttpMethod(detailsEntity.Method.ToString());
Host = detailsEntity.Host; Host = detailsEntity.Host;
Uri = detailsEntity.Uri; Uri = detailsEntity.Uri;
Proto = detailsEntity.Proto; Proto = detailsEntity.Proto;
Headers = new INetMockHttpHeaders(detailsEntity.Headers); Headers = new INetMockHttpHeaders(detailsEntity.Headers);
} }
public HttpMethod Method { get; init; } public HttpMethod Method { get; init; }
public string Host { get; init; } public string Host { get; init; }
public string Uri { get; init; } public string Uri { get; init; }
@ -70,16 +72,15 @@ namespace INetMock.Client.Audit
{ {
public DnsDetails() public DnsDetails()
{ {
} }
public DNSOpCode OpCode { get; init; } public DNSOpCode OpCode { get; init; }
public IReadOnlyList<DNSQuestionEntity> Questions { get; init; } public IReadOnlyList<DNSQuestionEntity> Questions { get; init; }
public DnsDetails(Any? any) public DnsDetails(Any? any)
{ {
if(any == null || any.Value == null) return; if (any == null || any.Value == null) return;
var entity = DNSDetailsEntity.Parser.ParseFrom(any.Value); var entity = DNSDetailsEntity.Parser.ParseFrom(any.Value);
OpCode = entity.Opcode; OpCode = entity.Opcode;

View file

@ -7,13 +7,12 @@ namespace INetMock.Client.Audit
{ {
public Event() public Event()
{ {
} }
public Event(EventEntity entity) public Event(EventEntity entity)
{ {
if (entity == null) throw new ArgumentNullException(nameof(entity)); if (entity == null) throw new ArgumentNullException(nameof(entity));
var details = (new T(), entity.Application) switch var details = (new T(), entity.Application) switch
{ {
(GenericDetails, _) => new GenericDetails(entity.ProtocolDetails) as T, (GenericDetails, _) => new GenericDetails(entity.ProtocolDetails) as T,
@ -33,7 +32,7 @@ namespace INetMock.Client.Audit
Application = entity.Application; Application = entity.Application;
Details = details; Details = details;
} }
public long Id { get; init; } public long Id { get; init; }
public DateTimeOffset Timestamp { get; init; } public DateTimeOffset Timestamp { get; init; }
public TransportProtocol Transport { get; init; } public TransportProtocol Transport { get; init; }
@ -69,10 +68,10 @@ namespace INetMock.Client.Audit
_ => new EmptyDetails() as T _ => new EmptyDetails() as T
}; };
} }
if (details == null) return null; if (details == null) return null;
return new() return new()
{ {
Id = ge.Id, Id = ge.Id,

View file

@ -1,28 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
namespace INetMock.Client.Audit
{
public sealed class EventServerStreamReader : IProtoEventReader
{
private readonly IAsyncStreamReader<EventEntity> _asyncEventStream;
public EventServerStreamReader(IAsyncStreamReader<EventEntity> asyncEventStream)
{
_asyncEventStream = asyncEventStream;
}
public async Task<EventEntity?> ReadAsync(CancellationToken token = default)
{
if (!await _asyncEventStream.MoveNext(token)) return null;
return _asyncEventStream.Current;
}
public void Dispose()
{
}
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
}

View file

@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace INetMock.Client.Audit
{
public interface IAuditApiClient
{
IProtoEventReader EventStreamAsync(string watcherName, CancellationToken token = default);
Task<IEnumerable<string>> ListSinksAsync(CancellationToken token = default);
Task<string> RegisterFileSinkAsync(string targetPath, CancellationToken token = default);
Task<bool> RemoveFileSinkAsync(string targetPath, CancellationToken token = default);
}
}

View file

@ -5,10 +5,10 @@ using System.Threading.Tasks;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit
{ {
public interface IEventReader<T>: IDisposable, IAsyncDisposable where T : EventDetails, new() public interface IEventReader<T> : IDisposable, IAsyncDisposable where T : EventDetails, new()
{ {
IAsyncEnumerable<Event<T>> ReadAllAsync(CancellationToken token = default); IAsyncEnumerable<Event<T>> ReadAllAsync(CancellationToken token = default);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>

View file

@ -0,0 +1,30 @@
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
namespace INetMock.Client.Audit.Serialization
{
public sealed class EventServerStreamReader : IProtoEventReader
{
private readonly AsyncServerStreamingCall<EventEntity> _asyncEventStream;
public EventServerStreamReader(AsyncServerStreamingCall<EventEntity> asyncEventStream)
{
_asyncEventStream = asyncEventStream;
}
public async Task<EventEntity?> ReadAsync(CancellationToken token = default)
{
if (!await _asyncEventStream.ResponseStream.MoveNext(token)) return null;
return _asyncEventStream.ResponseStream.Current;
}
public void Dispose() => _asyncEventStream.Dispose();
public ValueTask DisposeAsync()
{
_asyncEventStream.Dispose();
return ValueTask.CompletedTask;
}
}
}

View file

@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit.Serialization
{ {
public sealed class GenericReader : IEventReader<GenericDetails> public sealed class GenericReader : IEventReader<GenericDetails>
{ {
@ -14,7 +14,8 @@ namespace INetMock.Client.Audit
_reader = reader; _reader = reader;
} }
public async IAsyncEnumerable<Event<GenericDetails>> ReadAllAsync([EnumeratorCancellation] CancellationToken token = default) public async IAsyncEnumerable<Event<GenericDetails>> ReadAllAsync(
[EnumeratorCancellation] CancellationToken token = default)
{ {
while (true) while (true)
{ {

View file

@ -5,14 +5,14 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Google.Protobuf; using Google.Protobuf;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit.Serialization
{ {
public sealed class ProtoReader : IProtoEventReader public sealed class ProtoReader : IProtoEventReader
{ {
private readonly MemoryPool<byte> _memoryPool; private readonly MemoryPool<byte> _memoryPool;
private readonly Stream _sourceStream; private readonly Stream _sourceStream;
private readonly bool _keepStreamOpen; private readonly bool _keepStreamOpen;
public ProtoReader(Stream sourceStream, bool keepStreamOpen = false) public ProtoReader(Stream sourceStream, bool keepStreamOpen = false)
{ {
_memoryPool = MemoryPool<byte>.Shared; _memoryPool = MemoryPool<byte>.Shared;
@ -41,10 +41,10 @@ namespace INetMock.Client.Audit
var entity = new EventEntity(); var entity = new EventEntity();
entity.MergeFrom(msgMem.ToArray()); entity.MergeFrom(msgMem.ToArray());
return entity; return entity;
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
if (!_keepStreamOpen) if (!_keepStreamOpen)

View file

@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit.Serialization
{ {
/// <summary> /// <summary>
/// Configures how the TypedReader proceeds with mismatching entities. /// Configures how the TypedReader proceeds with mismatching entities.

View file

@ -19,5 +19,6 @@
<Protobuf Include="proto/audit/details/http_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/audit/details/http_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/rpc/audit.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/rpc/audit.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/rpc/health.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/rpc/health.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/rpc/pcap.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,9 +1,10 @@
using System.IO; using System.IO;
using INetMock.Client.Audit; using INetMock.Client.Audit;
using INetMock.Client.Audit.Serialization;
using INetMock.Client.Test.Hex; using INetMock.Client.Test.Hex;
using Xunit; using Xunit;
namespace INetMock.Client.Test.Audit namespace INetMock.Client.Test.Audit.Serialization
{ {
public class GenericReaderTest public class GenericReaderTest
{ {
@ -23,7 +24,7 @@ namespace INetMock.Client.Test.Audit
public async void TestRead_HttpEvent_Success() public async void TestRead_HttpEvent_Success()
{ {
await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes)); await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes));
await using var reader = new GenericReader(protoReader); await using IEventReader<GenericDetails> reader = new GenericReader(protoReader);
await foreach (var ev in reader.ReadAllAsync()) await foreach (var ev in reader.ReadAllAsync())
{ {
Assert.NotNull(ev); Assert.NotNull(ev);
@ -44,7 +45,7 @@ namespace INetMock.Client.Test.Audit
{ {
await using var memStream = new MemoryStream(_dnsEventPayloadBytes); await using var memStream = new MemoryStream(_dnsEventPayloadBytes);
await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes)); await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes));
await using var reader = new GenericReader(protoReader); await using IEventReader<GenericDetails> reader = new GenericReader(protoReader);
await foreach (var ev in reader.ReadAllAsync()) await foreach (var ev in reader.ReadAllAsync())
{ {
Assert.NotNull(ev); Assert.NotNull(ev);

View file

@ -1,9 +1,10 @@
using System.IO; using System.IO;
using INetMock.Client.Audit; using INetMock.Client.Audit;
using INetMock.Client.Audit.Serialization;
using INetMock.Client.Test.Hex; using INetMock.Client.Test.Hex;
using Xunit; using Xunit;
namespace INetMock.Client.Test.Audit namespace INetMock.Client.Test.Audit.Serialization
{ {
public class TypedReaderTest public class TypedReaderTest
{ {
@ -23,7 +24,7 @@ namespace INetMock.Client.Test.Audit
public async void TestRead_HttpEvent_Success() public async void TestRead_HttpEvent_Success()
{ {
await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes)); await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes));
await using var reader = new TypedReader<HttpDetails>(protoReader, DropMode.DropEntity); await using IEventReader<HttpDetails> reader = new TypedReader<HttpDetails>(protoReader, DropMode.DropEntity);
await foreach (var ev in reader.ReadAllAsync()) await foreach (var ev in reader.ReadAllAsync())
{ {
Assert.NotNull(ev); Assert.NotNull(ev);
@ -34,7 +35,7 @@ namespace INetMock.Client.Test.Audit
public async void TestRead_DnsEvent_Success() public async void TestRead_DnsEvent_Success()
{ {
await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes)); await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes));
await using var reader = new TypedReader<DnsDetails>(protoReader, DropMode.DropEntity); await using IEventReader<DnsDetails> reader = new TypedReader<DnsDetails>(protoReader, DropMode.DropEntity);
await foreach (var ev in reader.ReadAllAsync()) await foreach (var ev in reader.ReadAllAsync())
{ {
Assert.NotNull(ev); Assert.NotNull(ev);