rework server responsibilities (#18)

* rework server responsibilities
add remote configuration

* start metrics only when compiled as not debug

* add some more logging to discord bot

* fixes of some casts

* make metrics port configurable, minor fixes

* add docker bullshit

* md formatting

* adjustments to docker stuff

* fix docker json files, fix some stuff in discord bot, add /useradd for Discord bot

* adjust docker configs and fix sharded.bat

* fixes for logs, cache file provider repeat trying to open filestream

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2022-12-27 13:48:05 +01:00
committed by GitHub
parent 7ee7fdaf48
commit 9eb5967935
101 changed files with 2470 additions and 585 deletions

View File

@@ -0,0 +1,30 @@
using Grpc.Core;
using MareSynchronosShared.Protos;
using MareSynchronosShared.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace MareSynchronosShared.Services;
[Authorize]
[AllowAnonymous]
public class GrpcConfigurationService<T> : ConfigurationService.ConfigurationServiceBase where T : class, IMareConfiguration
{
private readonly T _config;
private readonly ILogger<GrpcConfigurationService<T>> logger;
public GrpcConfigurationService(IOptions<T> config, ILogger<GrpcConfigurationService<T>> logger)
{
_config = config.Value;
this.logger = logger;
}
[AllowAnonymous]
public override Task<ValueMessage> GetConfigurationEntry(KeyMessage request, ServerCallContext context)
{
logger.LogInformation("Remote requested {key}", request.Key);
var returnVal = _config.SerializeValue(request.Key, request.Default);
return Task.FromResult(new ValueMessage() { Value = returnVal });
}
}

View File

@@ -0,0 +1,11 @@
using MareSynchronosShared.Utils;
namespace MareSynchronosShared.Services;
public interface IConfigurationService<T> where T : class, IMareConfiguration
{
bool IsMain { get; }
T1 GetValue<T1>(string key);
T1 GetValueOrDefault<T1>(string key, T1 defaultValue);
string ToString();
}

View File

@@ -0,0 +1,126 @@
using Grpc.Net.ClientFactory;
using MareSynchronosShared.Protos;
using MareSynchronosShared.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Globalization;
using System.Text;
using System.Text.Json;
using static MareSynchronosShared.Protos.ConfigurationService;
namespace MareSynchronosShared.Services;
public class MareConfigurationServiceClient<T> : IConfigurationService<T> where T : class, IMareConfiguration
{
internal record RemoteCachedEntry(object Value, DateTime Inserted);
private readonly T _config;
private readonly ConcurrentDictionary<string, RemoteCachedEntry> _cachedRemoteProperties = new(StringComparer.Ordinal);
private readonly ILogger<MareConfigurationServiceClient<T>> _logger;
private readonly ConfigurationServiceClient _configurationServiceClient;
public MareConfigurationServiceClient(ILogger<MareConfigurationServiceClient<T>> logger, IOptions<T> config, ConfigurationServiceClient configurationServiceClient)
{
_config = config.Value;
_logger = logger;
_configurationServiceClient = configurationServiceClient;
}
public MareConfigurationServiceClient(ILogger<MareConfigurationServiceClient<T>> logger, IOptions<T> config, GrpcClientFactory grpcClientFactory, string grpcClientName)
{
_config = config.Value;
_logger = logger;
_configurationServiceClient = grpcClientFactory.CreateClient<ConfigurationServiceClient>(grpcClientName);
}
public bool IsMain => false;
public T1 GetValueOrDefault<T1>(string key, T1 defaultValue)
{
var prop = _config.GetType().GetProperty(key);
if (prop == null) return defaultValue;
if (prop.PropertyType != typeof(T1)) throw new InvalidCastException($"Invalid Cast: Property {key} is {prop.PropertyType}, wanted: {typeof(T1)}");
bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any();
if (isRemote)
{
bool isCurrent = false;
if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(30))
{
isCurrent = true;
}
if (!isCurrent)
{
var result = GetValueFromGrpc(key, defaultValue, prop.PropertyType);
if (result == null) return defaultValue;
_cachedRemoteProperties[key] = result;
return (T1)_cachedRemoteProperties[key].Value;
}
}
var value = prop.GetValue(_config);
return (T1)value;
}
private RemoteCachedEntry? GetValueFromGrpc(string key, object defaultValue, Type t)
{
// grab stuff from grpc
try
{
_logger.LogInformation("Getting {key} from Grpc", key);
var response = _configurationServiceClient.GetConfigurationEntry(new KeyMessage { Key = key, Default = Convert.ToString(defaultValue, CultureInfo.InvariantCulture) });
_logger.LogInformation("Grpc Response for {key} = {value}", key, response.Value);
return new RemoteCachedEntry(JsonSerializer.Deserialize(response.Value, t), DateTime.Now);
}
catch
{
return null;
}
}
public T1 GetValue<T1>(string key)
{
var prop = _config.GetType().GetProperty(key);
if (prop == null) throw new KeyNotFoundException(key);
if (prop.PropertyType != typeof(T1)) throw new InvalidCastException($"Invalid Cast: Property {key} is {prop.PropertyType}, wanted: {typeof(T1)}");
bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any();
if (isRemote)
{
bool isCurrent = false;
if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(30))
{
isCurrent = true;
}
if (!isCurrent)
{
var result = GetValueFromGrpc(key, null, prop.PropertyType);
if (result == null) throw new KeyNotFoundException(key);
_cachedRemoteProperties[key] = result;
}
if (!_cachedRemoteProperties.ContainsKey(key)) throw new KeyNotFoundException(key);
return (T1)_cachedRemoteProperties[key].Value;
}
var value = prop.GetValue(_config);
return (T1)value;
}
public override string ToString()
{
var props = _config.GetType().GetProperties();
StringBuilder sb = new();
foreach (var prop in props)
{
var isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any();
var mi = GetType().GetMethod(nameof(GetValue)).MakeGenericMethod(prop.PropertyType);
var val = mi.Invoke(this, new[] { prop.Name });
var value = isRemote ? val : prop.GetValue(_config);
sb.AppendLine($"{prop.Name} (IsRemote: {isRemote}) => {value}");
}
return sb.ToString();
}
}

View File

@@ -0,0 +1,37 @@
using MareSynchronosShared.Utils;
using Microsoft.Extensions.Options;
using System.Text;
namespace MareSynchronosShared.Services;
public class MareConfigurationServiceServer<T> : IConfigurationService<T> where T : class, IMareConfiguration
{
private readonly T _config;
public bool IsMain => true;
public MareConfigurationServiceServer(IOptions<T> config)
{
_config = config.Value;
}
public T1 GetValueOrDefault<T1>(string key, T1 defaultValue)
{
return _config.GetValueOrDefault<T1>(key, defaultValue);
}
public T1 GetValue<T1>(string key)
{
return _config.GetValue<T1>(key);
}
public override string ToString()
{
var props = _config.GetType().GetProperties();
StringBuilder sb = new();
foreach (var prop in props)
{
sb.AppendLine($"{prop.Name} (IsRemote: {prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any()}) => {prop.GetValue(_config)}");
}
return sb.ToString();
}
}