frostfs-sdk-csharp/src/FrostFS.SDK.Client/FrostFSClient.cs
Pavel Gross 8835b23ed3
All checks were successful
DCO / DCO (pull_request) Successful in 23s
lint-build / dotnet8.0 (pull_request) Successful in 44s
lint-build / dotnet8.0 (push) Successful in 43s
[#34] Client: Add rules deserialization
Signed-off-by: Pavel Gross <p.gross@yadro.com>
2025-02-28 10:36:12 +03:00

464 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FrostFS.SDK.Client.Interfaces;
using FrostFS.SDK.Client.Services;
using FrostFS.SDK.Cryptography;
using FrostFS.Session;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Options;
using static Frostfs.V2.Apemanager.APEManagerService;
using static FrostFS.Accounting.AccountingService;
using static FrostFS.Container.ContainerService;
using static FrostFS.Netmap.NetmapService;
using static FrostFS.Object.ObjectService;
using static FrostFS.Session.SessionService;
namespace FrostFS.SDK.Client;
public class FrostFSClient : IFrostFSClient
{
internal ContainerServiceClient? ContainerServiceClient { get; set; }
internal ContainerServiceProvider? ContainerServiceProvider { get; set; }
internal NetmapServiceClient? NetmapServiceClient { get; set; }
internal NetmapServiceProvider? NetmapServiceProvider { get; set; }
internal APEManagerServiceClient? ApeManagerServiceClient { get; set; }
internal ApeManagerServiceProvider? ApeManagerServiceProvider { get; set; }
internal SessionServiceClient? SessionServiceClient { get; set; }
internal SessionServiceProvider? SessionServiceProvider { get; set; }
internal ObjectServiceClient? ObjectServiceClient { get; set; }
internal ObjectServiceProvider? ObjectServiceProvider { get; set; }
internal AccountingServiceClient? AccountingServiceClient { get; set; }
internal AccountingServiceProvider? AccountingServiceProvider { get; set; }
internal ClientContext ClientCtx { get; set; }
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, Func<string, ChannelBase> grpcChannelFactory)
{
if (clientOptions is null)
{
throw new ArgumentNullException(nameof(clientOptions));
}
if (grpcChannelFactory is null)
{
throw new ArgumentNullException(nameof(grpcChannelFactory));
}
return new FrostFSClient(clientOptions, grpcChannelFactory);
}
/// <summary>
/// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client.
/// </summary>
/// <param name="clientOptions">Global setting for client</param>
/// <param name="channelOptions">Setting for gRPC channel</param>
/// <param name="containerService">ContainerService.ContainerServiceClient implementation</param>
/// <param name="netmapService">Netmap.NetmapService.NetmapServiceClient implementation</param>
/// <param name="sessionService">Session.SessionService.SessionServiceClient implementation</param>
/// <param name="objectService">Object.ObjectService.ObjectServiceClient implementation</param>
/// <returns></returns>
public static IFrostFSClient GetTestInstance(
IOptions<ClientSettings> settings,
Func<string, ChannelBase> grpcChannelFactory,
NetmapServiceClient netmapService,
SessionServiceClient sessionService,
ContainerServiceClient containerService,
ObjectServiceClient objectService)
{
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
if (grpcChannelFactory is null)
{
throw new ArgumentNullException(nameof(grpcChannelFactory));
}
if (netmapService is null)
{
throw new ArgumentNullException(nameof(netmapService));
}
if (sessionService is null)
{
throw new ArgumentNullException(nameof(sessionService));
}
if (containerService is null)
{
throw new ArgumentNullException(nameof(containerService));
}
if (objectService is null)
{
throw new ArgumentNullException(nameof(objectService));
}
return new FrostFSClient(
settings, channel: grpcChannelFactory(settings.Value.Host), containerService, netmapService, sessionService, objectService);
}
private FrostFSClient(
IOptions<ClientSettings> settings,
ChannelBase channel,
ContainerServiceClient containerService,
NetmapServiceClient netmapService,
SessionServiceClient sessionService,
ObjectServiceClient objectService)
{
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
var ecdsaKey = settings.Value.Key.LoadWif();
ClientCtx = new ClientContext(
client: this,
key: new ClientKey(ecdsaKey),
owner: FrostFsOwner.FromKey(ecdsaKey),
channel: channel,
version: new FrostFsVersion(2, 13))
{
SessionCache = new SessionCache(0),
Callback = settings.Value.Callback,
Interceptors = settings.Value.Interceptors
};
ContainerServiceClient = containerService ?? throw new ArgumentNullException(nameof(containerService));
NetmapServiceClient = netmapService ?? throw new ArgumentNullException(nameof(netmapService));
SessionServiceClient = sessionService ?? throw new ArgumentNullException(nameof(sessionService));
ObjectServiceClient = objectService ?? throw new ArgumentNullException(nameof(objectService));
}
private FrostFSClient(IOptions<ClientSettings> settings, Func<string, ChannelBase> grpcChannelFactory)
{
var clientSettings = (settings?.Value) ?? throw new ArgumentNullException(nameof(settings), "Options value must be initialized");
clientSettings.Validate();
var ecdsaKey = clientSettings.Key.LoadWif();
ClientCtx = new ClientContext(
this,
key: new ClientKey(ecdsaKey),
owner: FrostFsOwner.FromKey(ecdsaKey),
channel: grpcChannelFactory(settings.Value.Host),
version: new FrostFsVersion(2, 13))
{
SessionCache = new SessionCache(0),
Callback = settings.Value.Callback,
Interceptors = settings.Value.Interceptors
};
// TODO: define timeout logic
// CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) });
}
internal FrostFSClient(WrapperPrm prm, SessionCache cache)
{
ClientCtx = new ClientContext(
client: this,
key: new ClientKey(prm.Key),
owner: FrostFsOwner.FromKey(prm.Key!),
channel: prm.GrpcChannelFactory(prm.Address),
version: new FrostFsVersion(2, 13))
{
SessionCache = cache,
Interceptors = prm.Interceptors,
Callback = prm.Callback
};
}
#region ApeManagerImplementation
public Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args, CallContext ctx)
{
return GetApeManagerService().AddChainAsync(args, ctx);
}
public Task RemoveChainAsync(PrmApeChainRemove args, CallContext ctx)
{
return GetApeManagerService().RemoveChainAsync(args, ctx);
}
public Task<FrostFsChain[]> ListChainAsync(PrmApeChainList args, CallContext ctx)
{
return GetApeManagerService().ListChainAsync(args, ctx);
}
#endregion
#region ContainerImplementation
public Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args, CallContext ctx)
{
return GetContainerService().GetContainerAsync(args, ctx);
}
public IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll args, CallContext ctx)
{
return GetContainerService().ListContainersAsync(args, ctx);
}
[Obsolete("Use PutContainerAsync method")]
public Task<FrostFsContainerId> CreateContainerAsync(PrmContainerCreate args, CallContext ctx)
{
return GetContainerService().PutContainerAsync(args, ctx);
}
public Task<FrostFsContainerId> PutContainerAsync(PrmContainerCreate args, CallContext ctx)
{
return GetContainerService().PutContainerAsync(args, ctx);
}
public Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx)
{
return GetContainerService().DeleteContainerAsync(args, ctx);
}
#endregion
#region NetworkImplementation
public Task<FrostFsNetmapSnapshot> GetNetmapSnapshotAsync(CallContext ctx)
{
return GetNetmapService().GetNetmapSnapshotAsync(ctx);
}
public Task<FrostFsNodeInfo> GetNodeInfoAsync(CallContext ctx)
{
return GetNetmapService().GetLocalNodeInfoAsync(ctx);
}
public Task<NetworkSettings> GetNetworkSettingsAsync(CallContext ctx)
{
return GetNetmapService().GetNetworkSettingsAsync(ctx);
}
#endregion
#region ObjectImplementation
public Task<FrostFsHeaderResult> GetObjectHeadAsync(PrmObjectHeadGet args, CallContext ctx)
{
return GetObjectService().GetObjectHeadAsync(args, ctx);
}
public Task<FrostFsObject> GetObjectAsync(PrmObjectGet args, CallContext ctx)
{
return GetObjectService().GetObjectAsync(args, ctx);
}
public Task<RangeReader> GetRangeAsync(PrmRangeGet args, CallContext ctx)
{
return GetObjectService().GetRangeAsync(args, ctx);
}
public Task<ReadOnlyMemory<byte>[]> GetRangeHashAsync(PrmRangeHashGet args, CallContext ctx)
{
return GetObjectService().GetRangeHashAsync(args, ctx);
}
public Task<IObjectWriter> PutObjectAsync(PrmObjectPut args, CallContext ctx)
{
return GetObjectService().PutStreamObjectAsync(args, ctx);
}
public Task<FrostFsObjectId> PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx)
{
return GetObjectService().PutClientCutObjectAsync(args, ctx);
}
public Task<FrostFsObjectId> PutSingleObjectAsync(PrmSingleObjectPut args, CallContext ctx)
{
return GetObjectService().PutSingleObjectAsync(args, ctx);
}
public Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args, CallContext ctx)
{
return GetObjectService().PatchObjectAsync(args, ctx);
}
public Task DeleteObjectAsync(PrmObjectDelete args, CallContext ctx)
{
return GetObjectService().DeleteObjectAsync(args, ctx);
}
public IAsyncEnumerable<FrostFsObjectId> SearchObjectsAsync(PrmObjectSearch args, CallContext ctx)
{
return GetObjectService().SearchObjectsAsync(args, ctx);
}
#endregion
#region Session Implementation
public async Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args, CallContext ctx)
{
var token = await CreateSessionInternalAsync(args, ctx).ConfigureAwait(false);
return new FrostFsSessionToken(token);
}
internal Task<SessionToken> CreateSessionInternalAsync(PrmSessionCreate args, CallContext ctx)
{
var service = GetSessionService();
return service.CreateSessionAsync(args, ctx);
}
#endregion
#region Accounting Implementation
public async Task<Accounting.Decimal> GetBalanceAsync(CallContext ctx)
{
return await GetAccouningService().GetBallance(ctx).ConfigureAwait(false);
}
#endregion
private CallInvoker? CreateInvoker()
{
CallInvoker? callInvoker = null;
if (ClientCtx.Interceptors != null)
{
foreach (var interceptor in ClientCtx.Interceptors)
callInvoker = AddInvoker(callInvoker, interceptor);
}
if (ClientCtx.Callback != null)
callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ClientCtx.Callback));
if (ClientCtx.PoolErrorHandler != null)
callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ClientCtx.PoolErrorHandler));
return callInvoker;
CallInvoker AddInvoker(CallInvoker? callInvoker, Interceptor interceptor)
{
if (callInvoker == null)
callInvoker = ClientCtx.Channel.Intercept(interceptor);
else
callInvoker = callInvoker.Intercept(interceptor);
return callInvoker;
}
}
private NetmapServiceProvider GetNetmapService()
{
if (NetmapServiceProvider == null)
{
var invoker = CreateInvoker();
NetmapServiceClient ??= (
invoker != null
? new NetmapServiceClient(invoker)
: new NetmapServiceClient(ClientCtx.Channel));
NetmapServiceProvider = new NetmapServiceProvider(NetmapServiceClient, ClientCtx);
}
return NetmapServiceProvider;
}
private SessionServiceProvider GetSessionService()
{
if (SessionServiceProvider == null)
{
var invoker = CreateInvoker();
SessionServiceClient ??= (
invoker != null
? new SessionServiceClient(invoker)
: new SessionServiceClient(ClientCtx.Channel));
SessionServiceProvider = new SessionServiceProvider(SessionServiceClient, ClientCtx);
}
return SessionServiceProvider;
}
private ApeManagerServiceProvider GetApeManagerService()
{
if (ApeManagerServiceProvider == null)
{
var invoker = CreateInvoker();
ApeManagerServiceClient ??= (
invoker != null
? new APEManagerServiceClient(invoker)
: new APEManagerServiceClient(ClientCtx.Channel));
ApeManagerServiceProvider = new ApeManagerServiceProvider(ApeManagerServiceClient, ClientCtx);
}
return ApeManagerServiceProvider;
}
private AccountingServiceProvider GetAccouningService()
{
if (this.AccountingServiceProvider == null)
{
var invoker = CreateInvoker();
AccountingServiceClient ??= (
invoker != null
? new AccountingServiceClient(invoker)
: new AccountingServiceClient(ClientCtx.Channel));
AccountingServiceProvider = new AccountingServiceProvider(AccountingServiceClient, ClientCtx);
}
return AccountingServiceProvider;
}
private ContainerServiceProvider GetContainerService()
{
if (this.ContainerServiceProvider == null)
{
var invoker = CreateInvoker();
ContainerServiceClient ??= (
invoker != null
? new ContainerServiceClient(invoker)
: new ContainerServiceClient(ClientCtx.Channel));
ContainerServiceProvider = new ContainerServiceProvider(ContainerServiceClient, ClientCtx);
}
return ContainerServiceProvider;
}
private ObjectServiceProvider GetObjectService()
{
if (this.ObjectServiceProvider == null)
{
var invoker = CreateInvoker();
ObjectServiceClient ??= (
invoker != null
? new ObjectServiceClient(invoker)
: new ObjectServiceClient(ClientCtx.Channel));
ObjectServiceProvider = new ObjectServiceProvider(ObjectServiceClient, ClientCtx);
}
return ObjectServiceProvider;
}
public async Task<string?> Dial(CallContext ctx)
{
var service = GetAccouningService();
_ = await service.GetBallance(ctx).ConfigureAwait(false);
return null;
}
public bool RestartIfUnhealthy(CallContext ctx)
{
throw new NotImplementedException();
}
}