Update to .NET 6 and latest InetMock API

This commit is contained in:
Peter 2022-01-26 15:19:04 +01:00
parent 794159e137
commit 56b77353f2
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
45 changed files with 1000 additions and 835 deletions

View file

@ -3,13 +3,13 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"dotnet-grpc": { "dotnet-grpc": {
"version": "2.40.0", "version": "2.42.0",
"commands": [ "commands": [
"dotnet-grpc" "dotnet-grpc"
] ]
}, },
"nuke.globaltool": { "nuke.globaltool": {
"version": "5.3.0", "version": "6.0.1",
"commands": [ "commands": [
"nuke" "nuke"
] ]

View file

@ -12,6 +12,8 @@ indent_size = 4
[*.cs] [*.cs]
indent_size = 4 indent_size = 4
csharp_style_namespace_declarations = file_scoped:warning
csharp_prefer_braces = true:warning
[*.json] [*.json]
indent_size = 2 indent_size = 2

View file

@ -14,13 +14,30 @@ test:
stage: test stage: test
services: services:
- docker:dind - docker:dind
before_script:
- |
curl https://download.docker.com/linux/static/stable/x86_64/docker-20.10.9.tgz | tar -xzv -C /usr/local/
docker run --rm -d \
--cap-add CAP_NET_RAW \
--cap-add CAP_NET_ADMIN \
--cap-add CAP_NET_BIND_SERVICE \
-u root \
-p 6767:6767 \
-e INETMOCK_API_LISTEN=tcp://0.0.0.0:6767 \
--name inetmock \
registry.gitlab.com/inetmock/inetmock:latest
for i in `seq 1 10`
do
docker exec -i inetmock /usr/lib/inetmock/bin/imctl health container 2>&1 > /dev/null || sleep 1;
done;
after_script:
- docker stop inetmock
variables: variables:
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/docker PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/docker
INETMOCK_SOCKET: http://docker:6767
script: script:
- curl https://download.docker.com/linux/static/stable/x86_64/docker-20.10.4.tgz | tar -xzv -C /usr/local/
- mkdir /usr/local/share/ca-certificates/docker-ca
- cp "${DOCKER_CERT_PATH}/ca.pem" /usr/local/share/ca-certificates/docker-ca/
- update-ca-certificates --fresh
- dotnet tool restore - dotnet tool restore
- dotnet nuke Test - dotnet nuke Test
@ -30,7 +47,7 @@ protobuf-lint:
name: docker.io/bufbuild/buf:latest name: docker.io/bufbuild/buf:latest
entrypoint: [""] entrypoint: [""]
script: script:
- cd api/ - cd api/proto/
- buf ls-files - buf ls-files
- buf lint - buf lint
@ -39,6 +56,7 @@ nuget-publish:
only: only:
refs: refs:
- tags - tags
- main
script: script:
- dotnet tool restore - dotnet tool restore
- dotnet nuke NuGetPush - dotnet nuke NuGetPush

1
.nuke
View file

@ -1 +0,0 @@
INetMock.sln

127
.nuke/build.schema.json Normal file
View file

@ -0,0 +1,127 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Build Schema",
"$ref": "#/definitions/build",
"definitions": {
"build": {
"type": "object",
"properties": {
"Configuration": {
"type": "string",
"description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
"enum": [
"Debug",
"Release"
]
},
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
"type": "string",
"description": "Host for execution. Default is 'automatic'",
"enum": [
"AppVeyor",
"AzurePipelines",
"Bamboo",
"Bitrise",
"GitHubActions",
"GitLab",
"Jenkins",
"Rider",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI",
"VisualStudio",
"VSCode"
]
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
"nuget-password": {
"type": "string",
"description": "Password to use for publishing NuGet packages"
},
"nuget-username": {
"type": "string",
"description": "Username to use for publishing NuGet packages"
},
"Partition": {
"type": "string",
"description": "Partition to use on CI"
},
"Plan": {
"type": "boolean",
"description": "Shows the execution plan (HTML)"
},
"Profile": {
"type": "array",
"description": "Defines the profiles to load",
"items": {
"type": "string"
}
},
"Root": {
"type": "string",
"description": "Root directory during build execution"
},
"Skip": {
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
"type": "string",
"enum": [
"AddNugetSource",
"Clean",
"Compile",
"Format",
"NuGetPush",
"Pack",
"Restore",
"Test"
]
}
},
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
},
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
"type": "string",
"enum": [
"AddNugetSource",
"Clean",
"Compile",
"Format",
"NuGetPush",
"Pack",
"Restore",
"Test"
]
}
},
"Verbosity": {
"type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",
"enum": [
"Minimal",
"Normal",
"Quiet",
"Verbose"
]
}
}
}
}
}

4
.nuke/parameters.json Normal file
View file

@ -0,0 +1,4 @@
{
"$schema": "./build.schema.json",
"Solution": "INetMock.sln"
}

13
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,13 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/dotnet/format
rev: "v5.1.225507" # Specify a tag or sha here, or run "pre-commit autoupdate"
hooks:
- id: dotnet-format
args:
- ""
- --folder
- --check
- --verbosity=detailed
- --include

View file

@ -8,6 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
.editorconfig = .editorconfig .editorconfig = .editorconfig
.gitignore = .gitignore .gitignore = .gitignore
.gitlab-ci.yml = .gitlab-ci.yml .gitlab-ci.yml = .gitlab-ci.yml
.pre-commit-config.yaml = .pre-commit-config.yaml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{07100561-E3C0-4B95-92E1-D2D3BA12C3A6}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{07100561-E3C0-4B95-92E1-D2D3BA12C3A6}"

View file

@ -4,4 +4,4 @@
:; exit $? :; exit $?
@ECHO OFF @ECHO OFF
powershell -ExecutionPolicy ByPass -NoProfile "%~dp0build.ps1" %* powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %*

View file

@ -14,7 +14,7 @@ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
########################################################################### ###########################################################################
$BuildProjectFile = "$PSScriptRoot\build\_build.csproj" $BuildProjectFile = "$PSScriptRoot\build\_build.csproj"
$TempDirectory = "$PSScriptRoot\\.tmp" $TempDirectory = "$PSScriptRoot\\.nuke\temp"
$DotNetGlobalFile = "$PSScriptRoot\\global.json" $DotNetGlobalFile = "$PSScriptRoot\\global.json"
$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
@ -56,9 +56,9 @@ else {
# Install by channel or version # Install by channel or version
$DotNetDirectory = "$TempDirectory\dotnet-win" $DotNetDirectory = "$TempDirectory\dotnet-win"
if (!(Test-Path variable:DotNetVersion)) { if (!(Test-Path variable:DotNetVersion)) {
ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
} else { } else {
ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
} }
$env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
} }

View file

@ -10,7 +10,7 @@ SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
########################################################################### ###########################################################################
BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj" BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj"
TEMP_DIRECTORY="$SCRIPT_DIR//.tmp" TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp"
DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"

View file

@ -1,4 +1,4 @@
using System.IO; using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using Nuke.Common; using Nuke.Common;
using Nuke.Common.CI; using Nuke.Common.CI;
@ -8,7 +8,6 @@ using Nuke.Common.Git;
using Nuke.Common.IO; using Nuke.Common.IO;
using Nuke.Common.ProjectModel; using Nuke.Common.ProjectModel;
using Nuke.Common.Tooling; using Nuke.Common.Tooling;
using Nuke.Common.Tools.Docker;
using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.GitVersion; using Nuke.Common.Tools.GitVersion;
using Nuke.Common.Utilities.Collections; using Nuke.Common.Utilities.Collections;
@ -20,38 +19,29 @@ using static Nuke.Common.Tools.DotNet.DotNetTasks;
class Build : NukeBuild class Build : NukeBuild
{ {
private const string NuGetSourceName = "GitLab"; private const string NuGetSourceName = "GitLab";
public static int Main() => Execute<Build>(x => x.Compile); public static int Main() => Execute<Build>(x => x.Test);
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
[Parameter("Username to use for publishing NuGet packages", Name = "nuget-username")]
string NuGetUsername { get; } = Environment.GetEnvironmentVariable("NUGET_USERNAME") ?? string.Empty;
[Parameter("Password to use for publishing NuGet packages", Name = "nuget-password")]
string NuGetPassword { get; } = Environment.GetEnvironmentVariable("NUGET_PASSWORD") ?? string.Empty;
[Solution] readonly Solution Solution; [Solution] readonly Solution Solution;
[GitRepository] readonly GitRepository GitRepository; [GitRepository] readonly GitRepository GitRepository;
[GitVersion(NoFetch = true, Framework = "net5.0")] readonly GitVersion GitVersion; [GitVersion(NoFetch = true)] readonly GitVersion GitVersion;
[CanBeNull] GitLab CI => GitLab.Instance; [CanBeNull]
GitLab CI => GitLab.Instance;
AbsolutePath SourceDirectory => RootDirectory / "src"; AbsolutePath SourceDirectory => RootDirectory / "src";
AbsolutePath TestsDirectory => RootDirectory / "tests"; AbsolutePath TestsDirectory => RootDirectory / "tests";
AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts";
Target PrintEnv => _ => _
.Executes(() =>
{
if (CI == null)
{
Logger.Info("Running in local environment");
Logger.Info($"Git commit: {GitRepository.Commit}");
}
else
{
Logger.Info("Running in GitLab CI");
Logger.Info($"Git commit: {CI.CommitSha}");
Logger.Info($"Pipeline ID: {CI.PipelineId}");
}
});
Target Clean => _ => _ Target Clean => _ => _
.Before(Restore) .Before(Restore)
.Executes(() => .Executes(() =>
@ -69,7 +59,7 @@ class Build : NukeBuild
}); });
Target Compile => _ => _ Target Compile => _ => _
.DependsOn(Restore, PrintEnv) .DependsOn(Restore)
.Executes(() => .Executes(() =>
{ {
DotNetBuild(s => s DotNetBuild(s => s
@ -81,14 +71,12 @@ class Build : NukeBuild
.EnableNoRestore()); .EnableNoRestore());
}); });
Target IntegrationTestImage => _ => _ Target Format => _ => _
.Executes(() => DockerTasks.DockerBuild(s => s .DependsOn(Restore)
.SetFile(Path.Join("assets", "integration-tests.dockerfile")) .Executes(() => DotNet(CI == null ? "format --no-restore" : "format --no-restore --verify-no-changes"));
.SetTag("inetmock-root")
.SetPath(".")));
Target Test => _ => _ Target Test => _ => _
.DependsOn(Compile, IntegrationTestImage) .DependsOn(Compile, Format)
.Executes(() => DotNetTest(s => s .Executes(() => DotNetTest(s => s
.SetProjectFile(Solution) .SetProjectFile(Solution)
.SetConfiguration(Configuration) .SetConfiguration(Configuration)
@ -97,20 +85,20 @@ class Build : NukeBuild
.EnableProcessLogOutput())); .EnableProcessLogOutput()));
Target AddNugetSource => _ => _ Target AddNugetSource => _ => _
.OnlyWhenStatic(() => GitRepository.IsOnMasterBranch()) .OnlyWhenStatic(() => GitRepository.IsOnMainBranch())
.OnlyWhenStatic(() => CI != null) .OnlyWhenStatic(() => CI != null)
.OnlyWhenStatic(() => !string.IsNullOrEmpty(NuGetUsername) && !string.IsNullOrEmpty(NuGetPassword))
.ProceedAfterFailure() .ProceedAfterFailure()
.Executes(() => DotNetNuGetAddSource(s => s .Executes(() => DotNetNuGetAddSource(s => s
.SetName(NuGetSourceName) .SetName(NuGetSourceName)
//.SetSource($"https://gitlab.com/api/v4/projects/{CI.ProjectId}/packages/nuget/index.json") .SetSource($"https://gitlab.com/api/v4/projects/{CI.ProjectId}/packages/nuget/index.json")
.SetSource($"https://gitlab.com/api/v4/projects/24385200/packages/nuget/index.json") .SetUsername(NuGetUsername)
.SetUsername("baez90") .SetPassword(NuGetPassword)
.SetPassword("RcMwfaXgvBxSWt4ZMB6z")
.EnableStorePasswordInClearText())); .EnableStorePasswordInClearText()));
Target Pack => _ => _ Target Pack => _ => _
.DependsOn(Test) .DependsOn(Compile)
.OnlyWhenStatic(() => GitRepository.IsOnMasterBranch()) .OnlyWhenStatic(() => GitRepository.IsOnMainBranch())
.Executes(() => SourceDirectory .Executes(() => SourceDirectory
.GlobFiles("**/*.csproj") .GlobFiles("**/*.csproj")
.ForEach(csproj => DotNetPack(s => s .ForEach(csproj => DotNetPack(s => s
@ -127,7 +115,7 @@ class Build : NukeBuild
Target NuGetPush => _ => _ Target NuGetPush => _ => _
.DependsOn(Pack, AddNugetSource) .DependsOn(Pack, AddNugetSource)
.OnlyWhenStatic(() => GitRepository.IsOnMasterBranch()) .OnlyWhenStatic(() => GitRepository.IsOnMainBranch())
.Executes(() => ArtifactsDirectory .Executes(() => ArtifactsDirectory
.GlobFiles("**/*.nupkg") .GlobFiles("**/*.nupkg")
.ForEach(nupkg => DotNetNuGetPush(s => s .ForEach(nupkg => DotNetNuGetPush(s => s

View file

@ -1,13 +1,11 @@
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using Nuke.Common.Tooling; using Nuke.Common.Tooling;
[TypeConverter(typeof(TypeConverter<Configuration>))] [TypeConverter(typeof(TypeConverter<Configuration>))]
public class Configuration : Enumeration public class Configuration : Enumeration
{ {
public static Configuration Debug = new Configuration { Value = nameof(Debug) }; public static Configuration Debug = new() { Value = nameof(Debug) };
public static Configuration Release = new Configuration { Value = nameof(Release) }; public static Configuration Release = new() { Value = nameof(Release) };
public static implicit operator string(Configuration configuration) public static implicit operator string(Configuration configuration)
{ {

View file

@ -6,12 +6,14 @@
<NoWarn>CS0649;CS0169</NoWarn> <NoWarn>CS0649;CS0169</NoWarn>
<NukeRootDirectory>..</NukeRootDirectory> <NukeRootDirectory>..</NukeRootDirectory>
<NukeScriptDirectory>..</NukeScriptDirectory> <NukeScriptDirectory>..</NukeScriptDirectory>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
<NukeTelemetryVersion>1</NukeTelemetryVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Nuke.Common" Version="5.3.0" /> <PackageReference Include="Nuke.Common" Version="6.0.1" />
<PackageDownload Include="GitVersion.Tool" Version="[5.6.6]" /> <PackageDownload Include="GitVersion.Tool" Version="[5.8.0]" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
global.json Normal file
View file

@ -0,0 +1,6 @@
{
"sdk": {
"version": "6.0.100",
"rollForward": "latestMinor"
}
}

View file

@ -6,12 +6,11 @@ using Grpc.Core;
using Grpc.Net.Client; using Grpc.Net.Client;
using INetMock.Client.Audit.Serialization; using INetMock.Client.Audit.Serialization;
using INetMock.Client.Grpc; using INetMock.Client.Grpc;
using INetMock.Client.Rpc;
namespace INetMock.Client.Audit.Client namespace INetMock.Client.Audit.Client;
public class AuditApiClient : IAuditApiClient
{ {
public class AuditApiClient : IAuditApiClient
{
private readonly AuditService.AuditServiceClient _auditClient; private readonly AuditService.AuditServiceClient _auditClient;
public AuditApiClient(string address, GrpcChannelOptions? options = null) : this(new Uri(address), options) public AuditApiClient(string address, GrpcChannelOptions? options = null) : this(new Uri(address), options)
@ -37,7 +36,7 @@ namespace INetMock.Client.Audit.Client
public async Task<string> RegisterFileSinkAsync(string targetPath, CancellationToken token = default) public async Task<string> RegisterFileSinkAsync(string targetPath, CancellationToken token = default)
{ {
var resp = await _auditClient.RegisterFileSinkAsync( var resp = await _auditClient.RegisterFileSinkAsync(
new RegisterFileSinkRequest {TargetPath = targetPath}, new RegisterFileSinkRequest { TargetPath = targetPath },
Metadata.Empty, Metadata.Empty,
null, null,
token token
@ -63,12 +62,11 @@ namespace INetMock.Client.Audit.Client
public IProtoEventReader EventStreamAsync(string watcherName, CancellationToken token = default) public IProtoEventReader EventStreamAsync(string watcherName, CancellationToken token = default)
{ {
var stream = _auditClient.WatchEvents( var stream = _auditClient.WatchEvents(
new WatchEventsRequest {WatcherName = watcherName}, new WatchEventsRequest { WatcherName = watcherName },
Metadata.Empty, Metadata.Empty,
null, null,
token token
); );
return new EventServerStreamReader(stream); return new EventServerStreamReader(stream);
} }
}
} }

View file

@ -3,57 +3,15 @@ using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using Google.Protobuf.Collections; using Google.Protobuf.Collections;
using Google.Protobuf.WellKnownTypes;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit;
public abstract record EventDetails;
public record HttpDetails() : EventDetails
{ {
public abstract record EventDetails; public HttpDetails(HTTPDetailsEntity detailsEntity) : this()
public record EmptyDetails : EventDetails;
public record GenericDetails : EventDetails
{ {
private readonly Any? _detailsAny;
public GenericDetails()
{
_detailsAny = null;
}
public GenericDetails(Any? any)
{
_detailsAny = any;
}
public static implicit operator HttpDetails(GenericDetails gd)
{
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
if (!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName))
throw new InvalidOperationException();
return new HttpDetails(gd._detailsAny);
}
public static implicit operator DnsDetails(GenericDetails gd)
{
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
if (!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName))
throw new InvalidOperationException();
return new DnsDetails(gd._detailsAny);
}
}
public record HttpDetails : EventDetails
{
public HttpDetails()
{
}
public HttpDetails(Any? any)
{
if (any == null || any.Value == null) return;
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;
@ -66,25 +24,30 @@ namespace INetMock.Client.Audit
public string Uri { get; init; } = string.Empty; public string Uri { get; init; } = string.Empty;
public string Proto { get; init; } = string.Empty; public string Proto { get; init; } = string.Empty;
public HttpHeaders Headers { get; init; } = new INetMockHttpHeaders(new MapField<string, HTTPHeaderValue>()); public HttpHeaders Headers { get; init; } = new INetMockHttpHeaders(new MapField<string, HTTPHeaderValue>());
} }
public record DnsDetails : EventDetails public record DnsDetails() : EventDetails
{
public DnsDetails(DNSDetailsEntity entity) : this()
{ {
public DnsDetails()
{
}
public DNSOpCode OpCode { get; init; } = DNSOpCode.Query;
public IReadOnlyList<DNSQuestionEntity> Questions { get; init; } = Array.Empty<DNSQuestionEntity>();
public DnsDetails(Any? any)
{
if (any == null || any.Value == null) return;
var entity = DNSDetailsEntity.Parser.ParseFrom(any.Value);
OpCode = entity.Opcode; OpCode = entity.Opcode;
Questions = entity.Questions; Questions = entity.Questions;
} }
}
public DNSOpCode OpCode { get; init; } = DNSOpCode.Query;
public IReadOnlyList<DNSQuestionEntity> Questions { get; init; } = Array.Empty<DNSQuestionEntity>();
}
public record DhcpDetails() : EventDetails
{
public DhcpDetails(DHCPDetailsEntity entity) : this()
{
HopCount = entity.HopCount;
OpCode = entity.Opcode;
HardwareType = entity.HwType;
}
public int HopCount { get; init; }
public DHCPOpCode OpCode { get; init; } = DHCPOpCode.Unspecified;
public DHCPHwType HardwareType { get; init; } = DHCPHwType.Unspecified;
} }

View file

@ -1,26 +1,17 @@
using System; using System;
using System.Net; using System.Net;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit;
public abstract record EventBase
{ {
public record Event<T> where T : EventDetails, new() protected EventBase(EventEntity entity)
{ {
public Event() if (entity == null)
{ {
throw new ArgumentNullException(nameof(entity));
} }
public Event(EventEntity entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
var details = (new T(), entity.Application) switch
{
(GenericDetails, _) => new GenericDetails(entity.ProtocolDetails) as T,
(_, AppProtocol.Dns) => new DnsDetails(entity.ProtocolDetails) as T,
(_, AppProtocol.Http or AppProtocol.HttpProxy) => new HttpDetails(entity.ProtocolDetails) as T,
(_, _) => new EmptyDetails() as T
};
Id = entity.Id; Id = entity.Id;
Timestamp = entity.Timestamp.ToDateTimeOffset(); Timestamp = entity.Timestamp.ToDateTimeOffset();
Transport = entity.Transport; Transport = entity.Transport;
@ -30,7 +21,6 @@ namespace INetMock.Client.Audit
DestinationPort = Convert.ToUInt16(entity.DestinationPort); DestinationPort = Convert.ToUInt16(entity.DestinationPort);
TlsDetails = entity.Tls; TlsDetails = entity.Tls;
Application = entity.Application; Application = entity.Application;
Details = details;
} }
public long Id { get; init; } public long Id { get; init; }
@ -42,49 +32,45 @@ namespace INetMock.Client.Audit
public ushort SourcePort { get; init; } public ushort SourcePort { get; init; }
public ushort DestinationPort { get; init; } public ushort DestinationPort { get; init; }
public TLSDetailsEntity TlsDetails { get; init; } = new(); public TLSDetailsEntity TlsDetails { get; init; } = new();
}
public record Event : EventBase
{
public Event(EventEntity entity) : base(entity)
{
Details = entity.ProtocolDetailsCase switch
{
EventEntity.ProtocolDetailsOneofCase.Http => new HttpDetails(entity.Http),
EventEntity.ProtocolDetailsOneofCase.Dns => new DnsDetails(entity.Dns),
EventEntity.ProtocolDetailsOneofCase.Dhcp => new DhcpDetails(entity.Dhcp),
_ => null
};
}
public object? Details { get; init; }
public T? DetailsAs<T>() where T : EventDetails => Details as T;
}
public record Event<T> : EventBase where T : EventDetails, new()
{
public Event(EventEntity entity) : base(entity)
{
Details = entity.ProtocolDetailsCase switch
{
EventEntity.ProtocolDetailsOneofCase.Http => new HttpDetails(entity.Http) as T,
EventEntity.ProtocolDetailsOneofCase.Dns => new DnsDetails(entity.Dns) as T,
EventEntity.ProtocolDetailsOneofCase.Dhcp => new DhcpDetails(entity.Dhcp) as T,
_ => null
};
}
private Event(Event raw) : base(raw)
{
Details = raw.Details as T;
}
public T? Details { get; init; } public T? Details { get; init; }
public bool CanConvert<TTarget>() where TTarget : EventDetails, new() public static explicit operator Event<T>(Event raw) => new(raw);
{
if (Details == null) return false;
return (new TTarget(), Application) switch
{
(DnsDetails, AppProtocol.Dns) => true,
(HttpDetails, AppProtocol.Http or AppProtocol.HttpProxy) => true,
(_, _) => false
};
}
public static explicit operator Event<T>?(Event<GenericDetails> ge)
{
T? details = null;
if (ge.Details != null)
{
details = ge.Application switch
{
AppProtocol.Dns => (DnsDetails) ge.Details as T,
AppProtocol.Http or AppProtocol.HttpProxy => (HttpDetails) ge.Details as T,
_ => new EmptyDetails() as T
};
}
if (details == null) return null;
return new()
{
Id = ge.Id,
Timestamp = ge.Timestamp,
Transport = ge.Transport,
SourceIp = ge.SourceIp,
SourcePort = ge.SourcePort,
DestinationIp = ge.DestinationIp,
DestinationPort = ge.DestinationPort,
TlsDetails = ge.TlsDetails,
Application = ge.Application,
Details = details
};
}
}
} }

View file

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

View file

@ -3,10 +3,25 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit;
public interface IEventReader : IDisposable, IAsyncDisposable
{
IAsyncEnumerable<Event> ReadAllAsync(CancellationToken token = default);
/// <summary>
///
/// </summary>
/// <param name="token"></param>
/// <returns>
/// An event as long as underlying stream has data.
/// When the end of the stream has been reached it will return null.
/// </returns>
Task<Event?> ReadAsync(CancellationToken token = default);
}
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>
@ -18,5 +33,4 @@ namespace INetMock.Client.Audit
/// When the end of the stream has been reached it will return null. /// When the end of the stream has been reached it will return null.
/// </returns> /// </returns>
Task<Event<T>?> ReadAsync(CancellationToken token = default); Task<Event<T>?> ReadAsync(CancellationToken token = default);
}
} }

View file

@ -1,17 +1,20 @@
using System.Net.Http.Headers; using System.Net.Http.Headers;
using Google.Protobuf.Collections; using Google.Protobuf.Collections;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit;
internal class INetMockHttpHeaders : HttpHeaders
{ {
internal class INetMockHttpHeaders : HttpHeaders
{
internal INetMockHttpHeaders(MapField<string, HTTPHeaderValue> headers) internal INetMockHttpHeaders(MapField<string, HTTPHeaderValue> headers)
{ {
foreach (var (key, values) in headers) foreach (var (key, values) in headers)
{ {
if (string.IsNullOrEmpty(key) || values == null) continue; if (string.IsNullOrEmpty(key) || values == null)
{
continue;
}
Add(key, values.Values); Add(key, values.Values);
} }
} }
}
} }

View file

@ -1,11 +1,11 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Inetmock.Audit.V1;
namespace INetMock.Client.Audit namespace INetMock.Client.Audit;
public interface IProtoEventReader : IDisposable, IAsyncDisposable
{ {
public interface IProtoEventReader : IDisposable, IAsyncDisposable
{
Task<EventEntity?> ReadAsync(CancellationToken token = default); Task<EventEntity?> ReadAsync(CancellationToken token = default);
}
} }

View file

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

View file

@ -3,10 +3,10 @@ using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace INetMock.Client.Audit.Serialization namespace INetMock.Client.Audit.Serialization;
public sealed class GenericReader : IEventReader
{ {
public sealed class GenericReader : IEventReader<GenericDetails>
{
private readonly IProtoEventReader _reader; private readonly IProtoEventReader _reader;
public GenericReader(IProtoEventReader reader) public GenericReader(IProtoEventReader reader)
@ -14,7 +14,7 @@ namespace INetMock.Client.Audit.Serialization
_reader = reader; _reader = reader;
} }
public async IAsyncEnumerable<Event<GenericDetails>> ReadAllAsync( public async IAsyncEnumerable<Event> ReadAllAsync(
[EnumeratorCancellation] CancellationToken token = default) [EnumeratorCancellation] CancellationToken token = default)
{ {
while (true) while (true)
@ -29,11 +29,10 @@ namespace INetMock.Client.Audit.Serialization
} }
} }
public async Task<Event<GenericDetails>?> ReadAsync(CancellationToken token = default) public async Task<Event?> ReadAsync(CancellationToken token = default)
{ {
var entity = await _reader.ReadAsync(token); var entity = await _reader.ReadAsync(token);
if (entity == null) return null; return entity == null ? null : new Event(entity);
return new Event<GenericDetails>(entity);
} }
public ValueTask DisposeAsync() => _reader.DisposeAsync(); public ValueTask DisposeAsync() => _reader.DisposeAsync();
@ -42,5 +41,4 @@ namespace INetMock.Client.Audit.Serialization
{ {
_reader.Dispose(); _reader.Dispose();
} }
}
} }

View file

@ -5,10 +5,10 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Google.Protobuf; using Google.Protobuf;
namespace INetMock.Client.Audit.Serialization 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;
@ -64,5 +64,4 @@ namespace INetMock.Client.Audit.Serialization
_memoryPool?.Dispose(); _memoryPool?.Dispose();
} }
}
} }

View file

@ -3,15 +3,15 @@ using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace INetMock.Client.Audit.Serialization namespace INetMock.Client.Audit.Serialization;
/// <summary>
/// Configures how the TypedReader proceeds with mismatching entities.
/// Given a TypedReader&lt;HttpDetails&gt; it can't guarantee that every Event&lt;T&gt; contains a HttpDetails entity.
/// Therefore different dropping strategies can be configured.
/// </summary>
public enum DropMode
{ {
/// <summary>
/// Configures how the TypedReader proceeds with mismatching entities.
/// Given a TypedReader&lt;HttpDetails&gt; it can't guarantee that every Event&lt;T&gt; contains a HttpDetails entity.
/// Therefore different dropping strategies can be configured.
/// </summary>
public enum DropMode
{
/// <summary> /// <summary>
/// Drops the whole entity. /// Drops the whole entity.
/// This filters the actual stream for entities supporting the details in question. /// This filters the actual stream for entities supporting the details in question.
@ -23,10 +23,10 @@ namespace INetMock.Client.Audit.Serialization
/// e.g. a TypedReader&lt;HttpDetails&gt; would still contain DNS events but no details about them /// e.g. a TypedReader&lt;HttpDetails&gt; would still contain DNS events but no details about them
/// </summary> /// </summary>
DropDetails DropDetails
} }
public sealed class TypedReader<T> : IEventReader<T> where T : EventDetails, new() public sealed class TypedReader<T> : IEventReader<T> where T : EventDetails, new()
{ {
private readonly IProtoEventReader _reader; private readonly IProtoEventReader _reader;
private readonly DropMode _dropMode; private readonly DropMode _dropMode;
@ -55,7 +55,11 @@ namespace INetMock.Client.Audit.Serialization
do do
{ {
var entity = await _reader.ReadAsync(token); var entity = await _reader.ReadAsync(token);
if (entity == null) return null; if (entity == null)
{
return null;
}
var parsed = new Event<T>(entity); var parsed = new Event<T>(entity);
var canReturn = (_dropMode, parsed.Details) switch var canReturn = (_dropMode, parsed.Details) switch
@ -75,5 +79,4 @@ namespace INetMock.Client.Audit.Serialization
public void Dispose() => _reader.Dispose(); public void Dispose() => _reader.Dispose();
public ValueTask DisposeAsync() => _reader.DisposeAsync(); public ValueTask DisposeAsync() => _reader.DisposeAsync();
}
} }

View file

@ -3,10 +3,10 @@ using System.Net.Http;
using System.Net.Sockets; using System.Net.Sockets;
using Grpc.Net.Client; using Grpc.Net.Client;
namespace INetMock.Client.Grpc namespace INetMock.Client.Grpc;
internal static class ChannelFactory
{ {
internal static class ChannelFactory
{
internal static GrpcChannel ForAddress(Uri uri, GrpcChannelOptions options) => internal static GrpcChannel ForAddress(Uri uri, GrpcChannelOptions options) =>
uri.Scheme.ToLowerInvariant() switch uri.Scheme.ToLowerInvariant() switch
{ {
@ -36,5 +36,4 @@ namespace INetMock.Client.Grpc
}; };
return GrpcChannel.ForAddress("http://localhost", options); return GrpcChannel.ForAddress("http://localhost", options);
} }
}
} }

View file

@ -1,24 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.19.0" /> <Using Include="Inetmock.Audit.V1" />
<PackageReference Include="Grpc.Net.Client" Version="2.40.0" /> <Using Include="Inetmock.Rpc.V1" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.40.0" /> </ItemGroup>
<PackageReference Include="Grpc.Tools" Version="2.41.1"> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.19.3" />
<PackageReference Include="Grpc.Net.Client" Version="2.42.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.42.0" />
<PackageReference Include="Grpc.Tools" Version="2.43.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Protobuf Include="proto/audit/v1/event_entity.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/audit/v1/dhcp_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/audit/v1/dns_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/audit/v1/dns_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/audit/v1/event_entity.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/audit/v1/http_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/audit/v1/http_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/rpc/v1/audit.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/rpc/v1/audit.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/rpc/v1/health.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/rpc/v1/endpoint.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/rpc/v1/pcap.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" /> <Protobuf Include="proto/rpc/v1/pcap.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
<Protobuf Include="proto/rpc/v1/pprof.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -7,13 +7,12 @@ using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Grpc.Core; using Grpc.Core;
using Grpc.Net.Client; using Grpc.Net.Client;
using INetMock.Client.Grpc; using ChannelFactory = INetMock.Client.Grpc.ChannelFactory;
using INetMock.Client.Rpc;
namespace INetMock.Client.PCAP.Client namespace INetMock.Client.PCAP.Client;
public class PcapApiClient : IPcapApiClient
{ {
public class PcapApiClient : IPcapApiClient
{
private readonly PCAPService.PCAPServiceClient _pcapServiceClient; private readonly PCAPService.PCAPServiceClient _pcapServiceClient;
public PcapApiClient(string address, GrpcChannelOptions? options = null) public PcapApiClient(string address, GrpcChannelOptions? options = null)
@ -73,9 +72,9 @@ namespace INetMock.Client.PCAP.Client
return result.ResolvedPath; return result.ResolvedPath;
} }
public async Task<bool> StopPcapFileRecording(string consumerKey, CancellationToken token = default) public async Task<bool> StopPcapFileRecordingAsync(string consumerKey, CancellationToken token = default)
{ {
var clientRequest = new StopPCAPFileRecordingRequest {ConsumerKey = consumerKey}; var clientRequest = new StopPCAPFileRecordingRequest { ConsumerKey = consumerKey };
var result = await _pcapServiceClient.StopPCAPFileRecordingAsync( var result = await _pcapServiceClient.StopPCAPFileRecordingAsync(
clientRequest, clientRequest,
Metadata.Empty, Metadata.Empty,
@ -84,5 +83,4 @@ namespace INetMock.Client.PCAP.Client
); );
return result.Removed; return result.Removed;
} }
}
} }

View file

@ -2,12 +2,12 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace INetMock.Client.PCAP namespace INetMock.Client.PCAP;
public interface IPcapApiClient
{ {
public interface IPcapApiClient
{
Task<IReadOnlyList<RecordingDevice>> ListAvailableDevicesAsync(CancellationToken token = default); Task<IReadOnlyList<RecordingDevice>> ListAvailableDevicesAsync(CancellationToken token = default);
Task<IReadOnlyList<Subscription>> ListActiveRecordingsAsync(CancellationToken token = default); Task<IReadOnlyList<Subscription>> ListActiveRecordingsAsync(CancellationToken token = default);
Task<string> StartPcapFileRecordingAsync(RecordingRequest request, CancellationToken token = default); Task<string> StartPcapFileRecordingAsync(RecordingRequest request, CancellationToken token = default);
} Task<bool> StopPcapFileRecordingAsync(string consumerKey, CancellationToken token = default);
} }

View file

@ -2,12 +2,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
namespace INetMock.Client.PCAP namespace INetMock.Client.PCAP;
{
public record RecordingDevice(string Name, IReadOnlyList<IPAddress> Addresses);
public record Subscription public record RecordingDevice(string Name, IReadOnlyList<IPAddress> Addresses);
{
public record Subscription
{
public Subscription(string consumerKey) public Subscription(string consumerKey)
{ {
ConsumerKey = consumerKey; ConsumerKey = consumerKey;
@ -38,10 +38,10 @@ namespace INetMock.Client.PCAP
return (consumerKey[(splitIndex + 1)..], consumerKey[..splitIndex]); return (consumerKey[(splitIndex + 1)..], consumerKey[..splitIndex]);
} }
} }
public record RecordingRequest(string Device, string TargetPath, bool Promiscuous = false) public record RecordingRequest(string Device, string TargetPath, bool Promiscuous = false)
{ {
public RecordingRequest(string device, string targetPath, bool promiscuous, TimeSpan readTimeout) : this(device, public RecordingRequest(string device, string targetPath, bool promiscuous, TimeSpan readTimeout) : this(device,
targetPath, promiscuous) targetPath, promiscuous)
{ {
@ -49,5 +49,4 @@ namespace INetMock.Client.PCAP
} }
public TimeSpan ReadTimeout { get; } = TimeSpan.FromSeconds(30); public TimeSpan ReadTimeout { get; } = TimeSpan.FromSeconds(30);
}
} }

View file

@ -0,0 +1,26 @@
using System.IO;
using System.Threading.Tasks;
using INetMock.Client.Audit;
using INetMock.Client.Audit.Serialization;
using Xunit;
namespace INetMock.Client.IntegrationTest.Audit.Serialization;
public class GenericReaderTest
{
[Fact]
public async Task Test_ReadAllAsync_AuditFile()
{
await using var auditFileStream = File.OpenRead(Path.Join("testdata", "test.ima"));
await using IEventReader reader = new GenericReader(new ProtoReader(auditFileStream));
var count = 0;
await foreach (var ev in reader.ReadAllAsync())
{
Assert.NotNull(ev);
count++;
}
Assert.True(count > 0);
}
}

View file

@ -0,0 +1,71 @@
using System.IO;
using System.Threading.Tasks;
using INetMock.Client.Audit;
using INetMock.Client.Audit.Serialization;
using Xunit;
namespace INetMock.Client.IntegrationTest.Audit.Serialization;
public class TypedReaderTest
{
[Fact]
public async Task Test_ReadAllAsync_DropDetails_AuditFile_HTTPEvents()
{
await using var fileStream = File.OpenRead(Path.Join("testdata", "test.ima"));
await using IEventReader<HttpDetails> httpReader = new TypedReader<HttpDetails>(new ProtoReader(fileStream));
var count = 0;
await foreach (var ev in httpReader.ReadAllAsync())
{
Assert.NotNull(ev);
count++;
}
Assert.True(count > 0);
}
[Fact]
public async Task Test_ReadAllAsync_DropEntity_AuditFile_HTTPEvents()
{
await using var fileStream = File.OpenRead(Path.Join("testdata", "test.ima"));
await using IEventReader<HttpDetails> httpReader = new TypedReader<HttpDetails>(new ProtoReader(fileStream), DropMode.DropEntity);
var count = 0;
await foreach (var ev in httpReader.ReadAllAsync())
{
Assert.NotNull(ev);
Assert.NotNull(ev.Details);
count++;
}
Assert.True(count > 0);
}
[Fact]
public async Task Test_ReadAllAsync_DropDetails_AuditFile_DNSEvents()
{
await using var fileStream = File.OpenRead(Path.Join("testdata", "test.ima"));
await using IEventReader<DnsDetails> httpReader = new TypedReader<DnsDetails>(new ProtoReader(fileStream));
var count = 0;
await foreach (var ev in httpReader.ReadAllAsync())
{
Assert.NotNull(ev);
count++;
}
Assert.True(count > 0);
}
[Fact]
public async Task Test_ReadAllAsync_DropEntity_AuditFile_DNSEvents()
{
await using var fileStream = File.OpenRead(Path.Join("testdata", "test.ima"));
await using IEventReader<DnsDetails> httpReader = new TypedReader<DnsDetails>(new ProtoReader(fileStream), DropMode.DropEntity);
var count = 0;
await foreach (var ev in httpReader.ReadAllAsync())
{
Assert.NotNull(ev);
count++;
}
Assert.True(count > 0);
}
}

View file

@ -1,36 +0,0 @@
using System;
using System.Globalization;
using System.Runtime.InteropServices;
namespace INetMock.Client.IntegrationTest
{
internal static class DockerEndpoint
{
private const string DockerHostEnvName = "DOCKER_HOST";
private const string DockerTlsVerifyEnvName = "DOCKER_TLS_VERIFY";
internal static string DetermineFromEnv() => DetermineEndpoint(
Environment.GetEnvironmentVariable(DockerHostEnvName),
Environment.GetEnvironmentVariable(DockerTlsVerifyEnvName)
);
internal static string DetermineEndpoint(string? dockerHost, string? tlsVerify)
{
dockerHost ??= "";
var dockerTlsVerify = int.TryParse(tlsVerify, out var verify) && verify == 1;
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
return (dockerHost, dockerTlsVerify, isWindows) switch
{
("", _, true) => "npipe://./pipe/docker_engine",
("", _, false) => "unix:/var/run/docker.sock",
(_, false, _) => dockerHost,
(var h, true, _) when h.StartsWith("tcp") => h.Replace(
"tcp",
"https",
true,
CultureInfo.InvariantCulture),
_ => "",
};
}
}
}

View file

@ -1,22 +0,0 @@
using Xunit;
namespace INetMock.Client.IntegrationTest
{
public class DockerEndpointTests
{
[Theory]
[InlineData(null, null, "unix:/var/run/docker.sock")]
[InlineData("tcp://docker:2375", null, "tcp://docker:2375")]
[InlineData("tcp://docker:2375", "0", "tcp://docker:2375")]
[InlineData("http://docker:2375", null, "http://docker:2375")]
[InlineData("http://docker:2375", "0", "http://docker:2375")]
[InlineData("tcp://docker:2376", "1", "https://docker:2376")]
[InlineData("https://docker:2376", "0", "https://docker:2376")]
public void DetermineDockerEndpoint_Input_ExpectedOutput(string? dockerHost, string? tlsVerify, string expected)
{
var actual = DockerEndpoint.DetermineEndpoint(dockerHost, tlsVerify);
Assert.Equal(expected, actual);
}
}
}

View file

@ -1,22 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DotNet.Testcontainers" Version="1.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0"> <PackageReference Include="coverlet.collector" Version="3.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
@ -26,4 +27,10 @@
<ProjectReference Include="..\..\src\INetMock.Client\INetMock.Client.csproj" /> <ProjectReference Include="..\..\src\INetMock.Client\INetMock.Client.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="testdata\test.ima">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

View file

@ -0,0 +1,19 @@
using System;
namespace INetMock.Client.IntegrationTest;
public class INetMockFixture
{
private const string DefaultINetMockSocketPath = "unix:///var/run/inetmock/inetmock.sock";
public INetMockFixture()
{
INetMockSocketPath = Environment.GetEnvironmentVariable("INETMOCK_SOCKET") ?? DefaultINetMockSocketPath;
if (INetMockSocketPath.StartsWith("http:"))
{
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
}
}
public string INetMockSocketPath { get; }
}

View file

@ -1,42 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using DotNet.Testcontainers.Containers.Builders;
using DotNet.Testcontainers.Containers.Modules;
using DotNet.Testcontainers.Containers.WaitStrategies;
using Xunit;
namespace INetMock.Client.IntegrationTest
{
public class INetMockServerFixture : IAsyncLifetime
{
private readonly TestcontainersContainer _inetmockContainer;
public INetMockServerFixture()
{
_inetmockContainer = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("inetmock-root:latest")
.WithCommand("serve")
.WithPortBinding(80, true)
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(80))
.WithDockerEndpoint(DockerEndpoint.DetermineFromEnv())
.WithMount(Path.GetTempPath(), "/var/run/inetmock")
.WithCleanUp(true)
.Build();
}
public Uri SocketPath => new($"unix://{Path.Join(Path.GetTempPath(), "inetmock.sock")}", UriKind.Absolute);
public async Task InitializeAsync()
{
await _inetmockContainer.StartAsync();
}
public async Task DisposeAsync()
{
await _inetmockContainer.StopAsync();
await _inetmockContainer.DisposeAsync();
}
}
}

View file

@ -1,19 +1,20 @@
using System.Linq;
using INetMock.Client.PCAP; using INetMock.Client.PCAP;
using INetMock.Client.PCAP.Client; using INetMock.Client.PCAP.Client;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace INetMock.Client.IntegrationTest.PCAP.Client namespace INetMock.Client.IntegrationTest.PCAP.Client;
public class PcapApiClientTests : IClassFixture<INetMockFixture>
{ {
public class PcapApiClientTests : IClassFixture<INetMockServerFixture>
{
private readonly ITestOutputHelper _outputHelper; private readonly ITestOutputHelper _outputHelper;
private readonly IPcapApiClient _apiClient; private readonly IPcapApiClient _apiClient;
public PcapApiClientTests(ITestOutputHelper testOutputHelper, INetMockServerFixture inetmockFixture) public PcapApiClientTests(ITestOutputHelper testOutputHelper, INetMockFixture inetMockFixture)
{ {
_outputHelper = testOutputHelper; _outputHelper = testOutputHelper;
_apiClient = new PcapApiClient(inetmockFixture.SocketPath); _apiClient = new PcapApiClient(inetMockFixture.INetMockSocketPath);
} }
[Fact] [Fact]
@ -21,6 +22,11 @@ namespace INetMock.Client.IntegrationTest.PCAP.Client
{ {
var devs = await _apiClient.ListAvailableDevicesAsync(); var devs = await _apiClient.ListAvailableDevicesAsync();
foreach (var (name, _) in devs)
{
_outputHelper.WriteLine(name);
}
Assert.Contains(devs, device => device.Name.Equals("lo")); Assert.Contains(devs, device => device.Name.Equals("lo"));
} }
@ -35,11 +41,23 @@ namespace INetMock.Client.IntegrationTest.PCAP.Client
[Fact] [Fact]
public async void StartPcapFileRecordingAsync_RecordLoopbackInterface_RunningRecording() public async void StartPcapFileRecordingAsync_RecordLoopbackInterface_RunningRecording()
{ {
const string targetPath = "/tmp/lo_record.pcap"; var recordingDevice = (await _apiClient.ListAvailableDevicesAsync()).FirstOrDefault();
await _apiClient.StartPcapFileRecordingAsync(new("lo", targetPath)); if (recordingDevice == null)
{
return;
}
var targetPath = $"/tmp/{recordingDevice.Name}_record.pcap";
await _apiClient.StartPcapFileRecordingAsync(new(recordingDevice.Name, targetPath));
var subscriptions = await _apiClient.ListActiveRecordingsAsync(); var subscriptions = await _apiClient.ListActiveRecordingsAsync();
Assert.Contains(subscriptions, subscription => subscription.Device.Equals("lo") && subscription.ConsumerName.Equals(targetPath)); Assert.Contains(subscriptions, subscription =>
subscription.Device.Equals(recordingDevice.Name) &&
subscription.ConsumerName.Equals(targetPath)
);
foreach (var subscription in subscriptions)
{
await _apiClient.StopPcapFileRecordingAsync(subscription.ConsumerKey);
} }
} }
} }

Binary file not shown.

View file

@ -4,12 +4,12 @@ using INetMock.Client.Audit.Serialization;
using INetMock.Client.Test.Hex; using INetMock.Client.Test.Hex;
using Xunit; using Xunit;
namespace INetMock.Client.Test.Audit.Serialization namespace INetMock.Client.Test.Audit.Serialization;
public class GenericReaderTest
{ {
public class GenericReaderTest private const string HttpEventPayload = "000000a7120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f7374a2014c080112096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e";
{ private const string DnsEventPayload = "0000004e120b088092b8c398feffffff01180220012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050aa0110120e0801120a6769746c61622e636f6d";
private const string HttpEventPayload = "000000e2120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f73745287010a37747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e76312e4854545044657461696c73456e74697479124c080112096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e";
private const string DnsEventPayload = "0000003b120b088092b8c398feffffff01180220012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050";
private readonly byte[] _httpEventPayloadBytes; private readonly byte[] _httpEventPayloadBytes;
private readonly byte[] _dnsEventPayloadBytes; private readonly byte[] _dnsEventPayloadBytes;
@ -24,19 +24,18 @@ namespace INetMock.Client.Test.Audit.Serialization
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 IEventReader<GenericDetails> reader = new GenericReader(protoReader); await using IEventReader reader = new GenericReader(protoReader);
await foreach (var ev in reader.ReadAllAsync()) await foreach (var ev in reader.ReadAllAsync())
{ {
Assert.NotNull(ev); Assert.NotNull(ev);
Assert.NotNull(ev.Details);
Assert.True(ev.CanConvert<HttpDetails>()); Assert.NotNull(ev.DetailsAs<HttpDetails>());
Assert.False(ev.CanConvert<DnsDetails>()); Assert.Null(ev.DetailsAs<DnsDetails>());
var httpEvent = (Event<HttpDetails>?) ev;
var dnsEvent = (Event<DnsDetails>?) ev;
var httpEvent = (Event<HttpDetails>)ev;
Assert.NotNull(httpEvent); Assert.NotNull(httpEvent);
Assert.Null(dnsEvent); Assert.NotNull(httpEvent.Details);
} }
} }
@ -45,14 +44,16 @@ namespace INetMock.Client.Test.Audit.Serialization
{ {
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 IEventReader<GenericDetails> reader = new GenericReader(protoReader); await using IEventReader reader = new GenericReader(protoReader);
await foreach (var ev in reader.ReadAllAsync()) await foreach (var ev in reader.ReadAllAsync())
{ {
Assert.NotNull(ev); Assert.NotNull(ev);
Assert.True(ev.CanConvert<DnsDetails>()); Assert.NotNull(ev.Details);
var dnsEvent = (Event<DnsDetails>?) ev; Assert.NotNull(ev.DetailsAs<DnsDetails>());
var dnsEvent = (Event<DnsDetails>)ev;
Assert.NotNull(dnsEvent); Assert.NotNull(dnsEvent);
} Assert.NotNull(dnsEvent.Details);
} }
} }
} }

View file

@ -4,10 +4,10 @@ using INetMock.Client.Audit.Serialization;
using INetMock.Client.Test.Hex; using INetMock.Client.Test.Hex;
using Xunit; using Xunit;
namespace INetMock.Client.Test.Audit.Serialization namespace INetMock.Client.Test.Audit.Serialization;
public class TypedReaderTest
{ {
public class TypedReaderTest
{
private const string HttpEventPayload = "000000e5120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f7374528a010a3c747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e64657461696c732e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e"; private const string HttpEventPayload = "000000e5120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f7374528a010a3c747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e64657461696c732e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e";
private const string DnsEventPayload = "0000003b120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050"; private const string DnsEventPayload = "0000003b120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050";
@ -41,5 +41,4 @@ namespace INetMock.Client.Test.Audit.Serialization
Assert.NotNull(ev); Assert.NotNull(ev);
} }
} }
}
} }

View file

@ -1,15 +1,15 @@
using System; using System;
using System.Linq; using System.Linq;
namespace INetMock.Client.Test.Hex namespace INetMock.Client.Test.Hex;
public static class Converter
{ {
public static class Converter public static byte[] HexToByteArray(this string hex)
{ {
public static byte[] HexToByteArray(this string hex) {
return Enumerable.Range(0, hex.Length) return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0) .Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray(); .ToArray();
} }
}
} }

View file

@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.2"> <PackageReference Include="coverlet.collector" Version="3.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View file

@ -1,10 +1,10 @@
using INetMock.Client.PCAP; using INetMock.Client.PCAP;
using Xunit; using Xunit;
namespace INetMock.Client.Test.PCAP namespace INetMock.Client.Test.PCAP;
public class SubscriptionTests
{ {
public class SubscriptionTests
{
[Theory] [Theory]
[InlineData("lo:test.pcap", "test.pcap", "lo")] [InlineData("lo:test.pcap", "test.pcap", "lo")]
public void Constructor_ConsumerKey_SplitIntoComponents(string key, string expectedName, string expectedDevice) public void Constructor_ConsumerKey_SplitIntoComponents(string key, string expectedName, string expectedDevice)
@ -13,5 +13,4 @@ namespace INetMock.Client.Test.PCAP
Assert.Equal(sub, new Subscription(key, expectedName, expectedDevice)); Assert.Equal(sub, new Subscription(key, expectedName, expectedDevice));
} }
}
} }