Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(repeater): adopt self-deploying repeaters #175

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/SecTester.Core/Dispatchers/HttpCommandDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using System.Threading;
using System.Threading.Tasks;
using SecTester.Core.Bus;
using SecTester.Core.Utils;
using SecTester.Core.Commands;
using SecTester.Core.Extensions;
using SecTester.Core.Utils;

namespace SecTester.Core.Dispatchers;

Expand Down
22 changes: 0 additions & 22 deletions src/SecTester.Repeater/Api/CreateRepeaterRequest.cs

This file was deleted.

34 changes: 0 additions & 34 deletions src/SecTester.Repeater/Api/DefaultRepeaters.cs

This file was deleted.

13 changes: 0 additions & 13 deletions src/SecTester.Repeater/Api/DeleteRepeaterRequest.cs

This file was deleted.

10 changes: 0 additions & 10 deletions src/SecTester.Repeater/Api/IRepeaters.cs

This file was deleted.

8 changes: 0 additions & 8 deletions src/SecTester.Repeater/Api/RepeaterIdentity.cs

This file was deleted.

4 changes: 2 additions & 2 deletions src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public DefaultRepeaterBusFactory(Configuration config, ILoggerFactory loggerFact
_scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
}

public IRepeaterBus Create(string repeaterId)
public IRepeaterBus Create(string? namePrefix = default)
{
if (_config.Credentials is null)
{
Expand All @@ -39,7 +39,7 @@ public IRepeaterBus Create(string repeaterId)
ReconnectionDelayMax = options.ReconnectionDelayMax,
ConnectionTimeout = options.ConnectionTimeout,
Transport = TransportProtocol.WebSocket,
Auth = new { token = _config.Credentials.Token, domain = repeaterId }
Auth = new { token = _config.Credentials.Token, domain = namePrefix ?? System.Net.Dns.GetHostName() }
})
{
Serializer = new SocketIOMessagePackSerializer()
Expand Down
2 changes: 1 addition & 1 deletion src/SecTester.Repeater/Bus/IRepeaterBus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ public interface IRepeaterBus : IAsyncDisposable
event Action<Version> UpgradeAvailable;

Task Connect();
Task Deploy(string repeaterId, CancellationToken? cancellationToken = null);
Task<string> Deploy(string? repeaterId, CancellationToken? cancellationToken = null);
}
2 changes: 1 addition & 1 deletion src/SecTester.Repeater/Bus/IRepeaterBusFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ namespace SecTester.Repeater.Bus;

public interface IRepeaterBusFactory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See NeuraLegion/sectester-js#197 (comment). Let's rename to  IBridgeService

{
IRepeaterBus Create(string repeaterId);
IRepeaterBus Create(string? namePrefix = default);
}
5 changes: 3 additions & 2 deletions src/SecTester.Repeater/Bus/RepeaterInfo.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System;
using MessagePack;

namespace SecTester.Repeater.Bus;

[MessagePackObject]
public sealed record RepeaterInfo
public sealed record RepeaterInfo(string RepeaterId)
{
[Key("repeaterId")]
public string RepeaterId { get; set; } = null!;
public string RepeaterId { get; init; } = RepeaterId ?? throw new ArgumentNullException(nameof(RepeaterId));
}
15 changes: 13 additions & 2 deletions src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
_connection.On("error", response =>
{
var err = response.GetValue<RepeaterError>();
ErrorOccurred?.Invoke(new(err.Message));

Check warning on line 51 in src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs

View workflow job for this annotation

GitHub Actions / windows-2019

Exception type System.Exception is not sufficiently specific
});

_connection.On("update-available", response =>
Expand Down Expand Up @@ -90,21 +90,32 @@
GC.SuppressFinalize(this);
}

public async Task Deploy(string repeaterId, CancellationToken? cancellationToken = null)
public async Task<string> Deploy(string? repeaterId, CancellationToken? cancellationToken = null)
{
try
{
var tcs = new TaskCompletionSource<RepeaterInfo>();

_connection.On("deployed", response => tcs.TrySetResult(response.GetValue<RepeaterInfo>()));

await _connection.EmitAsync("deploy", new RepeaterInfo { RepeaterId = repeaterId }).ConfigureAwait(false);
var args = string.IsNullOrWhiteSpace(repeaterId) ? Array.Empty<object>() : new object[] { new RepeaterInfo(repeaterId!) };

await _connection
.EmitAsync("deploy", args)
.ConfigureAwait(false);

using var _ = cancellationToken?.Register(() => tcs.TrySetCanceled());

var result = await tcs.Task.ConfigureAwait(false);

_logger.LogDebug("Repeater ({RepeaterId}) deployed", result?.RepeaterId);

if (null == result || string.IsNullOrWhiteSpace(result.RepeaterId))
{
throw new InvalidOperationException("An error occured while repeater is being deployed");
}

return result.RepeaterId;
}
finally
{
Expand Down
13 changes: 4 additions & 9 deletions src/SecTester.Repeater/DefaultRepeaterFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Microsoft.Extensions.Logging;
using SecTester.Core;
using SecTester.Core.Logger;
using SecTester.Repeater.Api;
using SecTester.Repeater.Bus;

namespace SecTester.Repeater;
Expand All @@ -12,30 +11,26 @@ public class DefaultRepeaterFactory : IRepeaterFactory
{
private readonly Configuration _configuration;
private readonly IRepeaterBusFactory _busFactory;
private readonly IRepeaters _repeaters;
private readonly ILoggerFactory _loggerFactory;
private readonly IAnsiCodeColorizer _ansiCodeColorizer;
private readonly RequestRunnerResolver _requestRunnerResolver;

public DefaultRepeaterFactory(IRepeaters repeaters, IRepeaterBusFactory busFactory, Configuration configuration, ILoggerFactory loggerFactory, IAnsiCodeColorizer ansiCodeColorizer, RequestRunnerResolver requestRunnerResolver)
public DefaultRepeaterFactory(IRepeaterBusFactory busFactory, Configuration configuration, ILoggerFactory loggerFactory, IAnsiCodeColorizer ansiCodeColorizer, RequestRunnerResolver requestRunnerResolver)
{
_repeaters = repeaters ?? throw new ArgumentNullException(nameof(repeaters));
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
_busFactory = busFactory ?? throw new ArgumentNullException(nameof(busFactory));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_ansiCodeColorizer = ansiCodeColorizer ?? throw new ArgumentNullException(nameof(ansiCodeColorizer));
_requestRunnerResolver = requestRunnerResolver ?? throw new ArgumentNullException(nameof(requestRunnerResolver));
}

public async Task<IRepeater> CreateRepeater(RepeaterOptions? options = default)
public Task<IRepeater> CreateRepeater(RepeaterOptions? options = default)
{
options ??= new RepeaterOptions();

var repeaterId = await _repeaters.CreateRepeater($"{options.NamePrefix}-{Guid.NewGuid()}", options.Description).ConfigureAwait(false);

var bus = _busFactory.Create(repeaterId);
var bus = _busFactory.Create(options.NamePrefix);
var version = new Version(_configuration.RepeaterVersion);

return new Repeater(repeaterId, bus, version, _loggerFactory.CreateLogger<Repeater>(), _ansiCodeColorizer, _requestRunnerResolver);
return Task.FromResult<IRepeater>(new Repeater(bus, version, _loggerFactory.CreateLogger<Repeater>(), _ansiCodeColorizer, _requestRunnerResolver));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Microsoft.Extensions.DependencyInjection;
using SecTester.Core.Extensions;
using SecTester.Core.Utils;
using SecTester.Repeater.Api;
using SecTester.Repeater.Bus;
using SecTester.Repeater.Runners;

Expand All @@ -22,7 +21,6 @@ public static IServiceCollection AddSecTesterRepeater(this IServiceCollection co
.AddHttpCommandDispatcher()
.AddSingleton<IRepeaterBusFactory, DefaultRepeaterBusFactory>()
.AddScoped<IRepeaterFactory, DefaultRepeaterFactory>()
.AddScoped<IRepeaters, DefaultRepeaters>()
.AddScoped<ITimerProvider, SystemTimerProvider>()
.AddScoped<IRequestRunner, HttpRequestRunner>()
.AddScoped<RequestRunnerResolver>(sp =>
Expand Down
36 changes: 3 additions & 33 deletions src/SecTester.Repeater/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,39 +41,9 @@ await using var repeater = await repeaterFactory.CreateRepeater(new RepeaterOpti

The `CreateRepeater` method accepts the options described below:

| Option | Description |
| :--------------------- | ----------------------------------------------------------------------------------------------------- |
| `namePrefix` | Enter a name prefix that will be used as a constant part of the unique name. By default, `sectester`. |
| `description` | Set a short description of the Repeater. |
| `requestRunnerOptions` | Custom the request runner settings that will be used to execute requests to your application. |

The default `requestRunnerOptions` is as follows:

```json
{
"timeout": 30000,
"maxContentLength": 100,
"reuseConnection": false,
"allowedMimes": [
"text/html",
"text/plain",
"text/css",
"text/javascript",
"text/markdown",
"text/xml",
"application/javascript",
"application/x-javascript",
"application/json",
"application/xml",
"application/x-www-form-urlencoded",
"application/msgpack",
"application/ld+json",
"application/graphql"
]
}
```

The `RequestRunnerOptions` exposes the following options that can used to customize the request runner's behavior: [RequestRunnerOptions.cs](https://github.com/NeuraLegion/sectester-net/blob/master/src/SecTester.Repeater/Runners/RequestRunnerOptions.cs)
| Option | Description |
| :--------------------- |--------------------------------------------------------------------------------------------------------------------|
| `namePrefix` | Enter a name prefix that will be used as a constant part of the unique name. By default, the host name value used. |

The `Repeater` instance provides the `Start` method. This method is required to establish a connection with the Bright cloud engine and interact with other services.

Expand Down
10 changes: 4 additions & 6 deletions src/SecTester.Repeater/Repeater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

public delegate IRequestRunner? RequestRunnerResolver(Protocol key);


public class Repeater : IRepeater
{
private readonly IRepeaterBus _bus;
Expand All @@ -21,10 +20,9 @@
private readonly IAnsiCodeColorizer _ansiCodeColorizer;
private readonly RequestRunnerResolver _requestRunnersAccessor;

public Repeater(string repeaterId, IRepeaterBus bus, Version version, ILogger<Repeater> logger,
public Repeater(IRepeaterBus bus, Version version, ILogger<Repeater> logger,
IAnsiCodeColorizer ansiCodeColorizer, RequestRunnerResolver requestRunnersAccessor)
{
RepeaterId = repeaterId ?? throw new ArgumentNullException(nameof(repeaterId));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_version = version ?? throw new ArgumentNullException(nameof(version));
_bus = bus ?? throw new ArgumentNullException(nameof(bus));
Expand All @@ -33,7 +31,7 @@
}

public RunningStatus Status { get; private set; } = RunningStatus.Off;
public string RepeaterId { get; }
public string RepeaterId { get; private set; } = string.Empty;

public async ValueTask DisposeAsync()
{
Expand All @@ -60,7 +58,7 @@
SubscribeToEvents();

await _bus.Connect().ConfigureAwait(false);
await _bus.Deploy(RepeaterId, cancellationToken).ConfigureAwait(false);
RepeaterId = await _bus.Deploy(RepeaterId, cancellationToken).ConfigureAwait(false);

Status = RunningStatus.Running;
}
Expand Down Expand Up @@ -94,14 +92,14 @@
}
}

public async Task<OutgoingResponse> HandleIncomingRequest(IncomingRequest message)
private async Task<OutgoingResponse> HandleIncomingRequest(IncomingRequest message)
{
var runner = _requestRunnersAccessor(message.Protocol);

if (runner == null)
{
var msg = $"Unsupported protocol {message.Protocol}";
_logger.LogError(msg);

Check warning on line 102 in src/SecTester.Repeater/Repeater.cs

View workflow job for this annotation

GitHub Actions / windows-2019

The logging message template should not vary between calls to 'LoggerExtensions.LogError(ILogger, string?, params object?[])'
return new OutgoingResponse { Message = msg };
}

Expand Down
4 changes: 1 addition & 3 deletions src/SecTester.Repeater/RepeaterOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace SecTester.Repeater;
public record RepeaterOptions
{
private const int MaxPrefixLength = 44;
private readonly string _namePrefix = "sectester";
private readonly string _namePrefix = System.Net.Dns.GetHostName();

public string NamePrefix
{
Expand All @@ -19,6 +19,4 @@ public string NamePrefix
_namePrefix = value;
}
}

public string? Description { get; init; }
}
1 change: 0 additions & 1 deletion src/SecTester.Repeater/SecTester.Repeater.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
</ItemGroup>

<ItemGroup>
<Folder Include="Api"/>
<Folder Include="Bus" />
<Folder Include="Extensions" />
<Folder Include="Internal" />
Expand Down
6 changes: 1 addition & 5 deletions src/SecTester.Runner/SecRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using SecTester.Core;
using SecTester.Core.Extensions;
using SecTester.Repeater;
using SecTester.Repeater.Api;
using SecTester.Repeater.Extensions;
using SecTester.Reporter;
using SecTester.Scan;
Expand All @@ -18,17 +17,15 @@ public class SecRunner : IAsyncDisposable
private readonly Configuration _configuration;
private readonly IFormatter _formatter;
private readonly IRepeaterFactory _repeaterFactory;
private readonly IRepeaters _repeatersManager;
private readonly IScanFactory _scanFactory;
private IRepeater? _repeater;

public SecRunner(Configuration configuration, IRepeaterFactory repeaterFactory, IScanFactory scanFactory, IRepeaters repeatersManager,
public SecRunner(Configuration configuration, IRepeaterFactory repeaterFactory, IScanFactory scanFactory,
IFormatter formatter)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_repeaterFactory = repeaterFactory ?? throw new ArgumentNullException(nameof(repeaterFactory));
_scanFactory = scanFactory ?? throw new ArgumentNullException(nameof(scanFactory));
_repeatersManager = repeatersManager ?? throw new ArgumentNullException(nameof(repeatersManager));
_formatter = formatter ?? throw new ArgumentNullException(nameof(formatter));
}

Expand Down Expand Up @@ -80,7 +77,6 @@ public async Task Clear(CancellationToken cancellationToken = default)
if (_repeater is not null)
{
await _repeater.Stop(cancellationToken).ConfigureAwait(false);
await _repeatersManager.DeleteRepeater(_repeater.RepeaterId).ConfigureAwait(false);
await _repeater.DisposeAsync().ConfigureAwait(false);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Net;
using System.Text;

namespace SecTester.Core.Tests.Extensions;

Expand Down
Loading
Loading