parent
605463ec24
commit
ae67b12313
28 changed files with 943 additions and 554 deletions
|
@ -7,11 +7,13 @@ using FrostFS.SDK.ModelsV2;
|
|||
using FrostFS.SDK.ModelsV2.Netmap;
|
||||
using FrostFS.Session;
|
||||
using Grpc.Core;
|
||||
using Grpc.Core.Interceptors;
|
||||
using Grpc.Net.Client;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using Version = FrostFS.SDK.ModelsV2.Version;
|
||||
|
||||
|
@ -20,10 +22,15 @@ namespace FrostFS.SDK.ClientV2;
|
|||
public class Client : IFrostFSClient
|
||||
{
|
||||
private bool isDisposed;
|
||||
|
||||
internal ContainerService.ContainerServiceClient? ContainerServiceClient { get; set; }
|
||||
internal NetmapService.NetmapServiceClient? NetmapServiceClient { get; set; }
|
||||
internal SessionService.SessionServiceClient? SessionServiceClient { get; set; }
|
||||
internal ObjectService.ObjectServiceClient? ObjectServiceClient { get; set; }
|
||||
|
||||
internal ClientEnvironment ClientCtx { get; set; }
|
||||
|
||||
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, GrpcChannelOptions channelOptions)
|
||||
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, GrpcChannelOptions? channelOptions = null)
|
||||
{
|
||||
return new Client(clientOptions, channelOptions);
|
||||
}
|
||||
|
@ -32,7 +39,7 @@ public class Client : IFrostFSClient
|
|||
/// 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 cjannel</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>
|
||||
|
@ -61,18 +68,19 @@ public class Client : IFrostFSClient
|
|||
OwnerId.FromKey(ecdsaKey);
|
||||
|
||||
ClientCtx = new ClientEnvironment(
|
||||
this,
|
||||
key: ecdsaKey,
|
||||
owner: OwnerId.FromKey(ecdsaKey),
|
||||
channel: InitGrpcChannel(settings.Value.Host, channelOptions),
|
||||
version: new Version(2, 13));
|
||||
|
||||
ClientCtx.ContainerService = new ContainerServiceProvider(containerService, ClientCtx);
|
||||
ClientCtx.NetmapService = new NetmapServiceProvider(netmapService, ClientCtx);
|
||||
ClientCtx.SessionService = new SessionServiceProvider(sessionService, ClientCtx);
|
||||
ClientCtx.ObjectService = new ObjectServiceProvider(objectService, ClientCtx);
|
||||
ContainerServiceClient = containerService;
|
||||
NetmapServiceClient = netmapService;
|
||||
SessionServiceClient = sessionService;
|
||||
ObjectServiceClient = objectService;
|
||||
}
|
||||
|
||||
private Client(IOptions<ClientSettings> options, GrpcChannelOptions channelOptions)
|
||||
private Client(IOptions<ClientSettings> options, GrpcChannelOptions? channelOptions)
|
||||
{
|
||||
var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized");
|
||||
|
||||
|
@ -83,16 +91,12 @@ public class Client : IFrostFSClient
|
|||
var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
|
||||
|
||||
ClientCtx = new ClientEnvironment(
|
||||
this,
|
||||
key: ecdsaKey,
|
||||
owner: OwnerId.FromKey(ecdsaKey),
|
||||
channel: channel,
|
||||
version: new Version(2, 13));
|
||||
|
||||
ClientCtx.ContainerService = new ContainerServiceProvider(new ContainerService.ContainerServiceClient(channel), ClientCtx);
|
||||
ClientCtx.NetmapService = new NetmapServiceProvider(new NetmapService.NetmapServiceClient(channel), ClientCtx);
|
||||
ClientCtx.SessionService = new SessionServiceProvider(new SessionService.SessionServiceClient(channel), ClientCtx);
|
||||
ClientCtx.ObjectService = new ObjectServiceProvider(new ObjectService.ObjectServiceClient(channel), ClientCtx);
|
||||
|
||||
CheckFrostFsVersionSupport();
|
||||
}
|
||||
|
||||
|
@ -111,72 +115,81 @@ public class Client : IFrostFSClient
|
|||
}
|
||||
}
|
||||
|
||||
public GrpcChannel Channel => ClientCtx.Channel;
|
||||
|
||||
#region ContainerImplementation
|
||||
public Task<ModelsV2.Container> GetContainerAsync(ContainerId containerId, Context? ctx = null)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.GetContainerAsync(containerId, ctx!);
|
||||
var service = GetContainerService(ref ctx);
|
||||
return service.GetContainerAsync(containerId, ctx!);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<ContainerId> ListContainersAsync(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.ListContainersAsync(ctx!);
|
||||
var service = GetContainerService(ref ctx);
|
||||
return service.ListContainersAsync(ctx!);
|
||||
}
|
||||
|
||||
public Task<ContainerId> CreateContainerAsync(ModelsV2.Container container, Context? ctx = null)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.CreateContainerAsync(container, ctx!);
|
||||
var service = GetContainerService(ref ctx);
|
||||
return service.CreateContainerAsync(container, ctx!);
|
||||
}
|
||||
|
||||
public Task DeleteContainerAsync(ContainerId containerId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.DeleteContainerAsync(containerId, ctx!);
|
||||
var service = GetContainerService(ref ctx);
|
||||
return service.DeleteContainerAsync(containerId, ctx!);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region NetworkImplementation
|
||||
public Task<NetmapSnapshot> GetNetmapSnapshotAsync(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.NetmapService!.GetNetmapSnapshotAsync(ctx!);
|
||||
var service = GetNetmapService(ref ctx);
|
||||
return service.GetNetmapSnapshotAsync(ctx!);
|
||||
}
|
||||
|
||||
public Task<ModelsV2.Netmap.NodeInfo> GetNodeInfoAsync(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
|
||||
var service = GetNetmapService(ref ctx);
|
||||
return service.GetLocalNodeInfoAsync(ctx!);
|
||||
}
|
||||
|
||||
public Task<NetworkSettings> GetNetworkSettingsAsync(Context? ctx = default)
|
||||
{
|
||||
var service = GetNetmapService(ref ctx);
|
||||
return service.GetNetworkSettingsAsync(ctx!);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ObjectImplementation
|
||||
public Task<ObjectHeader> GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.GetObjectHeadAsync(containerId, objectId, ctx!);
|
||||
var service = GetObjectService(ref ctx);
|
||||
return service.GetObjectHeadAsync(containerId, objectId, ctx!);
|
||||
}
|
||||
|
||||
public Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.GetObjectAsync(containerId, objectId, ctx!);
|
||||
var service = GetObjectService(ref ctx);
|
||||
return service.GetObjectAsync(containerId, objectId, ctx!);
|
||||
}
|
||||
|
||||
public Task<ObjectId> PutObjectAsync(PutObjectParameters putObjectParameters, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.PutObjectAsync(putObjectParameters, ctx!);
|
||||
var service = GetObjectService(ref ctx);
|
||||
return service.PutObjectAsync(putObjectParameters, ctx!);
|
||||
}
|
||||
|
||||
public Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object obj, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.PutSingleObjectAsync(obj, ctx!);
|
||||
var service = GetObjectService(ref ctx);
|
||||
return service.PutSingleObjectAsync(obj, ctx!);
|
||||
}
|
||||
|
||||
public Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.DeleteObjectAsync(containerId, objectId, ctx!);
|
||||
var service = GetObjectService(ref ctx);
|
||||
return service.DeleteObjectAsync(containerId, objectId, ctx!);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<ObjectId> SearchObjectsAsync(
|
||||
|
@ -184,19 +197,38 @@ public class Client : IFrostFSClient
|
|||
IEnumerable<ObjectFilter> filters,
|
||||
Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.SearchObjectsAsync(containerId, filters, ctx!);
|
||||
var service = GetObjectService(ref ctx);
|
||||
return service.SearchObjectsAsync(containerId, filters, ctx!);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SessionImplementation
|
||||
public async Task<ModelsV2.SessionToken> CreateSessionAsync(ulong expiration, Context? ctx = null)
|
||||
{
|
||||
var session = await CreateSessionInternalAsync(expiration, ctx);
|
||||
var token = session.Serialize();
|
||||
|
||||
return new ModelsV2.SessionToken([], token);
|
||||
}
|
||||
|
||||
public Task<Session.SessionToken> CreateSessionInternalAsync(ulong expiration, Context? ctx = null)
|
||||
{
|
||||
var service = GetSessionService(ref ctx);
|
||||
return service.CreateSessionAsync(expiration, ctx!);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ToolsImplementation
|
||||
public ObjectId CalculateObjectId(ObjectHeader header)
|
||||
{
|
||||
return ClientCtx.ObjectService!.CalculateObjectId(header);
|
||||
{
|
||||
return new ObjectTools(ClientCtx).CalculateObjectId(header);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private async void CheckFrostFsVersionSupport(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
var localNodeInfo = await ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
|
||||
var service = GetNetmapService(ref ctx);
|
||||
var localNodeInfo = await service.GetLocalNodeInfoAsync(ctx!);
|
||||
|
||||
if (!localNodeInfo.Version.IsSupported(ClientCtx.Version))
|
||||
{
|
||||
|
@ -206,15 +238,76 @@ public class Client : IFrostFSClient
|
|||
}
|
||||
}
|
||||
|
||||
private void ValidateEnvironment(ref Context? ctx)
|
||||
private CallInvoker? SetupEnvironment(ref Context? ctx)
|
||||
{
|
||||
if (isDisposed)
|
||||
throw new Exception("Client is disposed.");
|
||||
|
||||
if (ClientCtx == null || !ClientCtx.Initialized)
|
||||
throw new Exception("Client is not initialized.");
|
||||
|
||||
ctx ??= new Context();
|
||||
|
||||
CallInvoker? callInvoker = null;
|
||||
if (ctx.Interceptors != null && ctx.Interceptors.Count > 0)
|
||||
{
|
||||
foreach (var interceptor in ctx.Interceptors)
|
||||
{
|
||||
callInvoker = AddInvoker(callInvoker, interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.Callback != null)
|
||||
callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Callback));
|
||||
|
||||
return callInvoker;
|
||||
|
||||
CallInvoker AddInvoker(CallInvoker? callInvoker, Interceptor interceptor)
|
||||
{
|
||||
if (callInvoker == null)
|
||||
callInvoker = ClientCtx.Channel.Intercept(interceptor);
|
||||
else
|
||||
callInvoker.Intercept(interceptor);
|
||||
|
||||
return callInvoker;
|
||||
}
|
||||
}
|
||||
|
||||
private NetmapServiceProvider GetNetmapService(ref Context? ctx)
|
||||
{
|
||||
var callInvoker = SetupEnvironment(ref ctx);
|
||||
var client = NetmapServiceClient ?? (callInvoker != null
|
||||
? new NetmapService.NetmapServiceClient(callInvoker)
|
||||
: new NetmapService.NetmapServiceClient(ClientCtx.Channel));
|
||||
|
||||
return new NetmapServiceProvider(client, ClientCtx);
|
||||
}
|
||||
|
||||
private SessionServiceProvider GetSessionService(ref Context? ctx)
|
||||
{
|
||||
var callInvoker = SetupEnvironment(ref ctx);
|
||||
var client = SessionServiceClient ?? (callInvoker != null
|
||||
? new SessionService.SessionServiceClient(callInvoker)
|
||||
: new SessionService.SessionServiceClient(ClientCtx.Channel));
|
||||
|
||||
return new SessionServiceProvider(client, ClientCtx);
|
||||
}
|
||||
|
||||
private ContainerServiceProvider GetContainerService(ref Context? ctx)
|
||||
{
|
||||
var callInvoker = SetupEnvironment(ref ctx);
|
||||
var client = ContainerServiceClient ?? (callInvoker != null
|
||||
? new ContainerService.ContainerServiceClient(callInvoker)
|
||||
: new ContainerService.ContainerServiceClient(ClientCtx.Channel));
|
||||
|
||||
return new ContainerServiceProvider(client, ClientCtx);
|
||||
}
|
||||
|
||||
private ObjectServiceProvider GetObjectService(ref Context? ctx)
|
||||
{
|
||||
var callInvoker = SetupEnvironment(ref ctx);
|
||||
var client = ObjectServiceClient ?? (callInvoker != null
|
||||
? new ObjectService.ObjectServiceClient(callInvoker)
|
||||
: new ObjectService.ObjectServiceClient(ClientCtx.Channel));
|
||||
|
||||
return new ObjectServiceProvider(client, ClientCtx);
|
||||
}
|
||||
|
||||
private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
|
||||
|
@ -230,21 +323,14 @@ public class Client : IFrostFSClient
|
|||
throw new ArgumentException(msg);
|
||||
}
|
||||
|
||||
ChannelCredentials grpcCredentials = uri.Scheme switch
|
||||
{
|
||||
"https" => ChannelCredentials.SecureSsl,
|
||||
"http" => ChannelCredentials.Insecure,
|
||||
_ => throw new ArgumentException($"Host '{host}' has invalid URI scheme: '{uri.Scheme}'.")
|
||||
};
|
||||
|
||||
if (channelOptions != null)
|
||||
{
|
||||
return GrpcChannel.ForAddress(uri, channelOptions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
|
||||
{
|
||||
Credentials = grpcCredentials,
|
||||
HttpHandler = new HttpClientHandler()
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue