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 clientOptions, Func grpcChannelFactory) { if (clientOptions is null) { throw new ArgumentNullException(nameof(clientOptions)); } if (grpcChannelFactory is null) { throw new ArgumentNullException(nameof(grpcChannelFactory)); } return new FrostFSClient(clientOptions, grpcChannelFactory); } /// /// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client. /// /// Global setting for client /// Setting for gRPC channel /// ContainerService.ContainerServiceClient implementation /// Netmap.NetmapService.NetmapServiceClient implementation /// Session.SessionService.SessionServiceClient implementation /// Object.ObjectService.ObjectServiceClient implementation /// public static IFrostFSClient GetTestInstance( IOptions settings, Func 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 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 settings, Func 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> AddChainAsync(PrmApeChainAdd args, CallContext ctx) { return GetApeManagerService().AddChainAsync(args, ctx); } public Task RemoveChainAsync(PrmApeChainRemove args, CallContext ctx) { return GetApeManagerService().RemoveChainAsync(args, ctx); } public Task ListChainAsync(PrmApeChainList args, CallContext ctx) { return GetApeManagerService().ListChainAsync(args, ctx); } #endregion #region ContainerImplementation public Task GetContainerAsync(PrmContainerGet args, CallContext ctx) { return GetContainerService().GetContainerAsync(args, ctx); } public IAsyncEnumerable ListContainersAsync(PrmContainerGetAll args, CallContext ctx) { return GetContainerService().ListContainersAsync(args, ctx); } [Obsolete("Use PutContainerAsync method")] public Task CreateContainerAsync(PrmContainerCreate args, CallContext ctx) { return GetContainerService().PutContainerAsync(args, ctx); } public Task 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 GetNetmapSnapshotAsync(CallContext ctx) { return GetNetmapService().GetNetmapSnapshotAsync(ctx); } public Task GetNodeInfoAsync(CallContext ctx) { return GetNetmapService().GetLocalNodeInfoAsync(ctx); } public Task GetNetworkSettingsAsync(CallContext ctx) { return GetNetmapService().GetNetworkSettingsAsync(ctx); } #endregion #region ObjectImplementation public Task GetObjectHeadAsync(PrmObjectHeadGet args, CallContext ctx) { return GetObjectService().GetObjectHeadAsync(args, ctx); } public Task GetObjectAsync(PrmObjectGet args, CallContext ctx) { return GetObjectService().GetObjectAsync(args, ctx); } public Task GetRangeAsync(PrmRangeGet args, CallContext ctx) { return GetObjectService().GetRangeAsync(args, ctx); } public Task[]> GetRangeHashAsync(PrmRangeHashGet args, CallContext ctx) { return GetObjectService().GetRangeHashAsync(args, ctx); } public Task PutObjectAsync(PrmObjectPut args, CallContext ctx) { return GetObjectService().PutStreamObjectAsync(args, ctx); } public Task PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx) { return GetObjectService().PutClientCutObjectAsync(args, ctx); } public Task PutSingleObjectAsync(PrmSingleObjectPut args, CallContext ctx) { return GetObjectService().PutSingleObjectAsync(args, ctx); } public Task PatchObjectAsync(PrmObjectPatch args, CallContext ctx) { return GetObjectService().PatchObjectAsync(args, ctx); } public Task DeleteObjectAsync(PrmObjectDelete args, CallContext ctx) { return GetObjectService().DeleteObjectAsync(args, ctx); } public IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args, CallContext ctx) { return GetObjectService().SearchObjectsAsync(args, ctx); } #endregion #region Session Implementation public async Task CreateSessionAsync(PrmSessionCreate args, CallContext ctx) { var token = await CreateSessionInternalAsync(args, ctx).ConfigureAwait(false); return new FrostFsSessionToken(token); } internal Task CreateSessionInternalAsync(PrmSessionCreate args, CallContext ctx) { var service = GetSessionService(); return service.CreateSessionAsync(args, ctx); } #endregion #region Accounting Implementation public async Task 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 Dial(CallContext ctx) { var service = GetAccouningService(); _ = await service.GetBallance(ctx).ConfigureAwait(false); return null; } public bool RestartIfUnhealthy(CallContext ctx) { throw new NotImplementedException(); } }