From 2a28806ace76a79f42bd5d1805a2fa1da5ca20d4 Mon Sep 17 00:00:00 2001 From: Pavel Gross Date: Mon, 12 Aug 2024 10:46:59 +0300 Subject: [PATCH] [#21] Client: Allows multinenant client Using one client for several owners Signed-off-by: Pavel Gross --- src/FrostFS.SDK.ClientV2/Client.cs | 73 ++++++++++-- .../Interfaces/IFrostFSClient.cs | 2 +- .../Parameters}/Context.cs | 28 ++++- .../Parameters/Credentials.cs | 11 ++ .../Parameters/PrmBase.cs | 14 +++ .../Parameters/PrmContainerCreate.cs | 12 +- .../Parameters/PrmContainerDelete.cs | 13 +-- .../Parameters/PrmContainerGet.cs | 13 +-- .../Parameters/PrmContainerGetAll.cs | 9 +- .../Parameters/PrmNetmapSnapshot.cs | 11 +- .../Parameters/PrmNetworkSettings.cs | 15 +-- .../Parameters/PrmNodeInfo.cs | 15 +-- .../Parameters/PrmObjectDelete.cs | 11 +- .../Parameters/PrmObjectGet.cs | 14 +-- .../Parameters/PrmObjectHeadGet.cs | 16 +-- .../Parameters/PrmObjectPut.cs | 14 +-- .../Parameters/PrmObjectSearch.cs | 17 +-- .../Parameters/PrmSessionCreate.cs | 14 +-- .../Parameters/PrmSingleObjectPut.cs | 14 +-- .../Services/ContainerServiceProvider.cs | 24 ++-- .../Services/NetmapServiceProvider.cs | 6 +- .../Services/ObjectServiceProvider.cs | 40 +++---- .../Services/SessionServiceProvider.cs | 5 +- .../Services/Shared/SessionProvider.cs | 2 +- .../Tools/ClientEnvironment.cs | 11 +- src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs | 33 +++--- .../Client/ClientSettings.cs | 64 ++++++++--- src/FrostFS.SDK.Tests/ContainerTest.cs | 4 +- src/FrostFS.SDK.Tests/ObjectTest.cs | 19 ++-- src/FrostFS.SDK.Tests/SmokeTests.cs | 106 ++++++++++++------ 30 files changed, 349 insertions(+), 281 deletions(-) rename src/{FrostFS.SDK.ModelsV2/Client => FrostFS.SDK.ClientV2/Parameters}/Context.cs (54%) create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/Credentials.cs create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs index faf1802..0142c03 100644 --- a/src/FrostFS.SDK.ClientV2/Client.cs +++ b/src/FrostFS.SDK.ClientV2/Client.cs @@ -35,6 +35,11 @@ public class Client : IFrostFSClient return new Client(clientOptions, channelOptions); } + public static IFrostFSClient GetSingleOwnerInstance(IOptions clientOptions, GrpcChannelOptions? channelOptions = null) + { + return new Client(clientOptions, channelOptions); + } + /// /// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client. /// @@ -46,7 +51,7 @@ public class Client : IFrostFSClient /// Object.ObjectService.ObjectServiceClient implementation /// public static IFrostFSClient GetTestInstance( - IOptions clientOptions, + IOptions clientOptions, GrpcChannelOptions? channelOptions, NetmapService.NetmapServiceClient netmapService, SessionService.SessionServiceClient sessionService, @@ -57,18 +62,18 @@ public class Client : IFrostFSClient } private Client( - IOptions settings, - GrpcChannelOptions? channelOptions, - ContainerService.ContainerServiceClient containerService, - NetmapService.NetmapServiceClient netmapService, - SessionService.SessionServiceClient sessionService, - ObjectService.ObjectServiceClient objectService) + IOptions settings, + GrpcChannelOptions? channelOptions, + ContainerService.ContainerServiceClient containerService, + NetmapService.NetmapServiceClient netmapService, + SessionService.SessionServiceClient sessionService, + ObjectService.ObjectServiceClient objectService) { var ecdsaKey = settings.Value.Key.LoadWif(); OwnerId.FromKey(ecdsaKey); ClientCtx = new ClientEnvironment( - this, + client: this, key: ecdsaKey, owner: OwnerId.FromKey(ecdsaKey), channel: InitGrpcChannel(settings.Value.Host, channelOptions), @@ -86,6 +91,25 @@ public class Client : IFrostFSClient clientSettings.Validate(); + var channel = InitGrpcChannel(clientSettings.Host, channelOptions); + + ClientCtx = new ClientEnvironment( + this, + key: null, + owner: null, + channel: channel, + version: new Version(2, 13)); + + // TODO: define timeout logic + // CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) }); + } + + private Client(IOptions options, GrpcChannelOptions? channelOptions) + { + var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized"); + + clientSettings.Validate(); + var ecdsaKey = clientSettings.Key.LoadWif(); var channel = InitGrpcChannel(clientSettings.Host, channelOptions); @@ -221,22 +245,22 @@ public class Client : IFrostFSClient #endregion #region ToolsImplementation - public ObjectId CalculateObjectId(ObjectHeader header) + public ObjectId CalculateObjectId(ObjectHeader header, Context ctx) { if (header == null) throw new ArgumentNullException(nameof(header)); - return ObjectTools.CalculateObjectId(header, ClientCtx); + return ObjectTools.CalculateObjectId(header, ctx); } #endregion private async void CheckFrostFsVersionSupport(Context? ctx = default) { - var args = new PrmNodeInfo(ctx); + var args = new PrmNodeInfo { Context = ctx }; var service = GetNetmapService(args); var localNodeInfo = await service.GetLocalNodeInfoAsync(args); - if (!localNodeInfo.Version.IsSupported(ClientCtx.Version)) + if (!localNodeInfo.Version.IsSupported(args.Context!.Version)) { var msg = $"FrostFS {localNodeInfo.Version} is not supported."; throw new ApplicationException(msg); @@ -250,6 +274,31 @@ public class Client : IFrostFSClient ctx.Context ??= new Context(); + if (ctx.Context.Key == null) + { + if (ClientCtx.Key == null) + { + throw new Exception("Key is not initialized."); + } + + ctx.Context.Key = ClientCtx.Key.ECDsaKey; + } + + if (ctx.Context.OwnerId == null) + { + ctx.Context.OwnerId = ClientCtx.Owner ?? OwnerId.FromKey(ctx.Context.Key); + } + + if (ctx.Context.Version == null) + { + if (ClientCtx.Version == null) + { + throw new Exception("Version is not initialized."); + } + + ctx.Context.Version = ClientCtx.Version; + } + CallInvoker? callInvoker = null; if (ctx.Context.Interceptors != null && ctx.Context.Interceptors.Count > 0) { diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs index a3a7f81..500a9c2 100644 --- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs +++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs @@ -45,6 +45,6 @@ public interface IFrostFSClient : IDisposable #endregion #region Tools - ObjectId CalculateObjectId(ObjectHeader header); + ObjectId CalculateObjectId(ObjectHeader header, Context ctx); #endregion } diff --git a/src/FrostFS.SDK.ModelsV2/Client/Context.cs b/src/FrostFS.SDK.ClientV2/Parameters/Context.cs similarity index 54% rename from src/FrostFS.SDK.ModelsV2/Client/Context.cs rename to src/FrostFS.SDK.ClientV2/Parameters/Context.cs index 4ac90a8..ca3728a 100644 --- a/src/FrostFS.SDK.ModelsV2/Client/Context.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/Context.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Security.Cryptography; using System.Threading; using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.Cryptography; +using Google.Protobuf; using Grpc.Core.Interceptors; namespace FrostFS.SDK.ClientV2; @@ -10,8 +13,18 @@ public class Context() { private List? interceptors; + private ByteString publicKeyCache; + + public ECDsa Key { get; set; } + + public OwnerId OwnerId { get; set; } + + public ModelsV2.Version Version { get; set; } + public CancellationToken CancellationToken { get; set; } = default; + public TimeSpan Timeout { get; set; } = default; + public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null; public Action? Callback { get; set; } @@ -20,5 +33,18 @@ public class Context() { get { return this.interceptors ??= []; } set { this.interceptors = value; } - } + } + + public ByteString PublicKeyCache + { + get + { + if (publicKeyCache == null) + { + publicKeyCache = ByteString.CopyFrom(Key.PublicKey()); + } + + return publicKeyCache; + } + } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/Credentials.cs b/src/FrostFS.SDK.ClientV2/Parameters/Credentials.cs new file mode 100644 index 0000000..a4915c3 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Parameters/Credentials.cs @@ -0,0 +1,11 @@ +using FrostFS.SDK.ModelsV2; +using System.Security.Cryptography; + +namespace FrostFS.SDK.ClientV2.Parameters; + +public class Credentials(ECDsa key, OwnerId ownerId) +{ + public ECDsa Key { get; } = key; + + public OwnerId OwnerId { get; } = ownerId; +} diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs new file mode 100644 index 0000000..09ca363 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs @@ -0,0 +1,14 @@ +using System.Collections.Specialized; + +namespace FrostFS.SDK.ClientV2.Parameters; + +public class PrmBase() : IContext +{ + /// + /// FrostFS request X-Headers + /// + public NameValueCollection XHeaders { get; set; } = []; + + /// + public Context? Context { get; set; } +} diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs index 4185a41..21e4931 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs @@ -1,8 +1,8 @@ -using System.Collections.Specialized; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmContainerCreate(ModelsV2.Container container) : IContext +public sealed class PrmContainerCreate(ModelsV2.Container container) : PrmBase, ISessionToken { public ModelsV2.Container Container { get; set; } = container; @@ -12,11 +12,7 @@ public sealed class PrmContainerCreate(ModelsV2.Container container) : IContext /// Rules for polling the result public PrmWait? WaitParams { get; set; } - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; + public string SessionKey { get; set; } - /// - public Context? Context { get; set; } + public SessionToken? SessionToken { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerDelete.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerDelete.cs index 8455f51..63308aa 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerDelete.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerDelete.cs @@ -1,9 +1,8 @@ -using System.Collections.Specialized; -using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmContainerDelete(ContainerId containerId, Context? ctx = null) : IContext +public sealed class PrmContainerDelete(ContainerId containerId) : PrmBase, ISessionToken { public ContainerId ContainerId { get; set; } = containerId; @@ -12,12 +11,6 @@ public sealed class PrmContainerDelete(ContainerId containerId, Context? ctx = n /// /// Rules for polling the result public PrmWait? WaitParams { get; set; } - - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - /// - public Context? Context { get; set; } = ctx; + public SessionToken? SessionToken { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGet.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGet.cs index 2761ba2..cebdaca 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGet.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGet.cs @@ -1,17 +1,8 @@ -using System.Collections.Specialized; -using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmContainerGet(ContainerId containerId, Context? ctx = null) : IContext +public sealed class PrmContainerGet(ContainerId containerId) : PrmBase { public ContainerId ContainerId { get; set; } = containerId; - - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } = ctx; } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs index 77d954e..e155606 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs @@ -2,13 +2,6 @@ namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmContainerGetAll() : IContext +public sealed class PrmContainerGetAll() : PrmBase() { - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs index 117ab85..e5316ee 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs @@ -2,13 +2,6 @@ namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmNetmapSnapshot(Context? context = default) : IContext -{ - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } = context; +public sealed class PrmNetmapSnapshot() : PrmBase +{ } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs index 250eb04..c82db6e 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs @@ -1,14 +1,5 @@ -using System.Collections.Specialized; +namespace FrostFS.SDK.ClientV2.Parameters; -namespace FrostFS.SDK.ClientV2.Parameters; - -public sealed class PrmNetworkSettings(Context? context = default) : IContext -{ - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } = context; +public sealed class PrmNetworkSettings() : PrmBase +{ } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs index 8a293a5..2b3b661 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs @@ -1,14 +1,5 @@ -using System.Collections.Specialized; +namespace FrostFS.SDK.ClientV2.Parameters; -namespace FrostFS.SDK.ClientV2.Parameters; - -public sealed class PrmNodeInfo(Context? context = default) : IContext -{ - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } = context; +public sealed class PrmNodeInfo() : PrmBase +{ } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectDelete.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectDelete.cs index 100f558..96df2c1 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectDelete.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectDelete.cs @@ -3,19 +3,12 @@ using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmObjectDelete(ContainerId containerId, ObjectId objectId) : IContext, ISessionToken +public sealed class PrmObjectDelete(ContainerId containerId, ObjectId objectId) : PrmBase, ISessionToken { public ContainerId ContainerId { get; set; } = containerId; + public ObjectId ObjectId { get; set; } = objectId; - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } - /// public SessionToken? SessionToken { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectGet.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectGet.cs index 9c8cb1c..07e717f 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectGet.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectGet.cs @@ -1,21 +1,13 @@ -using System.Collections.Specialized; -using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmObjectGet(ContainerId containerId, ObjectId objectId) : IContext, ISessionToken +public sealed class PrmObjectGet(ContainerId containerId, ObjectId objectId) : PrmBase, ISessionToken { public ContainerId ContainerId { get; set; } = containerId; + public ObjectId ObjectId { get; set; } = objectId; - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } - /// public SessionToken? SessionToken { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs index 5184fe8..cdc75cb 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs @@ -1,21 +1,13 @@ -using System.Collections.Specialized; -using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmObjectHeadGet(ContainerId containerId, ObjectId objectId) : IContext, ISessionToken +public sealed class PrmObjectHeadGet(ContainerId containerId, ObjectId objectId) : PrmBase, ISessionToken { public ContainerId ContainerId { get; set; } = containerId; + public ObjectId ObjectId { get; set; } = objectId; - - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } - + /// public SessionToken? SessionToken { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs index fea3bd5..0fcf91d 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs @@ -1,10 +1,10 @@ -using FrostFS.SDK.ModelsV2; using System.Collections.Specialized; using System.IO; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmObjectPut : IContext, ISessionToken +public sealed class PrmObjectPut : PrmBase, ISessionToken { /// /// Need to provide values like ContainerId and ObjectType to create and object. @@ -30,21 +30,13 @@ public sealed class PrmObjectPut : IContext, ISessionToken /// Overrides default size of the buffer for stream transferring. /// /// Size of the buffer - public int BufferMaxSize { get; set; } + public int BufferMaxSize { get; set; } /// /// Allows to define a buffer for chunks to manage by the memory allocation and releasing. /// public byte[]? CustomBuffer { get; set; } - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } - /// public SessionToken? SessionToken { get; set; } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs index c4e8fc9..e7c56be 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs @@ -1,9 +1,8 @@ -using System.Collections.Generic; -using System.Collections.Specialized; -using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2; +using System.Collections.Generic; namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmObjectSearch(ContainerId containerId, params IObjectFilter[] filters) : IContext, ISessionToken +public sealed class PrmObjectSearch(ContainerId containerId, params IObjectFilter[] filters) : PrmBase, ISessionToken { /// /// Defines container for the search @@ -16,15 +15,7 @@ public sealed class PrmObjectSearch(ContainerId containerId, params IObjectFilte /// /// Collection of filters public IEnumerable Filters { get; set; } = filters; - - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } - + /// public SessionToken? SessionToken { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmSessionCreate.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmSessionCreate.cs index 22f0c4e..2afc76f 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmSessionCreate.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmSessionCreate.cs @@ -1,16 +1,6 @@ -using System.Collections.Specialized; +namespace FrostFS.SDK.ClientV2.Parameters; -namespace FrostFS.SDK.ClientV2.Parameters; - -public sealed class PrmSessionCreate(ulong expiration, Context? context = default) : IContext +public sealed class PrmSessionCreate(ulong expiration) : PrmBase { public ulong Expiration { get; set; } = expiration; - - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } = context; } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmSingleObjectPut.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmSingleObjectPut.cs index 0d1df0f..fc22bf7 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmSingleObjectPut.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmSingleObjectPut.cs @@ -1,19 +1,11 @@ -using System.Collections.Specialized; -using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2; + namespace FrostFS.SDK.ClientV2.Parameters; -public sealed class PrmSingleObjectPut(FrostFsObject frostFsObject, Context? context = null) : IContext, ISessionToken +public sealed class PrmSingleObjectPut(FrostFsObject frostFsObject) : PrmBase, ISessionToken { public FrostFsObject FrostFsObject { get; set; } = frostFsObject; - /// - /// FrostFS request X-Headers - /// - public NameValueCollection XHeaders { get; set; } = []; - - /// - public Context? Context { get; set; } = context; - /// public SessionToken? SessionToken { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs index 7f412c1..b91fcd1 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs @@ -17,7 +17,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient { internal async Task GetContainerAsync(PrmContainerGet args) { - GetRequest request = GetContainerRequest(args.ContainerId.ToMessage(), args.XHeaders); + GetRequest request = GetContainerRequest(args.ContainerId.ToMessage(), args.XHeaders, args.Context!); var response = await service.GetAsync(request, null, args.Context!.Deadline, args.Context.CancellationToken); @@ -34,12 +34,12 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient { Body = new ListRequest.Types.Body { - OwnerId = Context.Owner.ToMessage() + OwnerId = ctx.OwnerId.ToMessage() } }; request.AddMetaHeader(args.XHeaders); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await service.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -55,20 +55,20 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient { var ctx = args.Context!; var grpcContainer = args.Container.ToMessage(); - grpcContainer.OwnerId = Context.Owner.ToMessage(); - grpcContainer.Version = Context.Version.ToMessage(); + grpcContainer.OwnerId = ctx.OwnerId.ToMessage(); + grpcContainer.Version = ctx.Version.ToMessage(); var request = new PutRequest { Body = new PutRequest.Types.Body { Container = grpcContainer, - Signature = Context.Key.ECDsaKey.SignRFC6979(grpcContainer) + Signature = ctx.Key.SignRFC6979(grpcContainer) } }; request.AddMetaHeader(args.XHeaders); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await service.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -87,13 +87,13 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient Body = new DeleteRequest.Types.Body { ContainerId = args.ContainerId.ToMessage(), - Signature = Context.Key.ECDsaKey.SignRFC6979(args.ContainerId.ToMessage().Value) + Signature = ctx.Key.SignRFC6979(args.ContainerId.ToMessage().Value) } }; request.AddMetaHeader(args.XHeaders); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await service.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -102,7 +102,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient Verifier.CheckResponse(response); } - private GetRequest GetContainerRequest(ContainerID id, NameValueCollection? xHeaders) + private static GetRequest GetContainerRequest(ContainerID id, NameValueCollection? xHeaders, Context ctx) { var request = new GetRequest { @@ -113,7 +113,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient }; request.AddMetaHeader(xHeaders); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); return request; } @@ -126,7 +126,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient private async Task WaitForContainer(WaitExpects expect, ContainerID id, PrmWait? waitParams, Context ctx) { - var request = GetContainerRequest(id, null); + var request = GetContainerRequest(id, null, ctx); async Task action() { diff --git a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs index e088ba4..60c7e1f 100644 --- a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs @@ -49,7 +49,7 @@ internal class NetmapServiceProvider : ContextAccessor }; request.AddMetaHeader(args.XHeaders); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -63,7 +63,7 @@ internal class NetmapServiceProvider : ContextAccessor var request = new NetworkInfoRequest(); request.AddMetaHeader(null); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -79,7 +79,7 @@ internal class NetmapServiceProvider : ContextAccessor var request = new NetmapSnapshotRequest(); request.AddMetaHeader(args.XHeaders); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken); diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs index 26ba3e8..95106b7 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs @@ -46,11 +46,11 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C sessionToken.CreateObjectTokenContext( request.Body.Address, ObjectSessionContext.Types.Verb.Head, - Context.Key.ECDsaKey); + ctx.Key); request.AddMetaHeader(args.XHeaders, sessionToken); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await client!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -80,11 +80,11 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C sessionToken.CreateObjectTokenContext( request.Body.Address, ObjectSessionContext.Types.Verb.Get, - Context.Key.ECDsaKey); + ctx.Key); request.AddMetaHeader(args.XHeaders, sessionToken); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); return await GetObject(request, ctx); } @@ -109,10 +109,10 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C sessionToken.CreateObjectTokenContext( request.Body.Address, ObjectSessionContext.Types.Verb.Delete, - Context.Key.ECDsaKey); + ctx.Key); request.AddMetaHeader(args.XHeaders, sessionToken); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await client.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -139,11 +139,11 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C sessionToken.CreateObjectTokenContext( new Address { ContainerId = request.Body.ContainerId }, ObjectSessionContext.Types.Verb.Search, - Context.Key.ECDsaKey); + ctx.Key); request.AddMetaHeader(args.XHeaders, sessionToken); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var objectsIds = SearchObjects(request, ctx); @@ -177,7 +177,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C internal async Task PutSingleObjectAsync(PrmSingleObjectPut args) { var ctx = args.Context!; - var grpcObject = ObjectTools.CreateObject(args.FrostFsObject, env); + var grpcObject = ObjectTools.CreateObject(args.FrostFsObject, ctx); var request = new PutSingleRequest { @@ -192,11 +192,11 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C sessionToken.CreateObjectTokenContext( new Address { ContainerId = grpcObject.Header.ContainerId, ObjectId = grpcObject.ObjectId}, ObjectSessionContext.Types.Verb.Put, - Context.Key.ECDsaKey); + ctx.Key); request.AddMetaHeader(args.XHeaders, sessionToken); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); var response = await client.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -226,7 +226,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C if (args.MaxObjectSizeCache == 0) { - var networkSettings = await Context.Client.GetNetworkSettingsAsync(new PrmNetworkSettings(ctx)); + var networkSettings = await Context.Client.GetNetworkSettingsAsync(new PrmNetworkSettings() { Context = ctx }); args.MaxObjectSizeCache = (int)networkSettings.MaxObjectSize; } @@ -331,7 +331,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C while (objectLimitSize == 0 || sentBytes < objectLimitSize) { - // send chanks limited to default or user's settings + // send chunks limited to default or user's settings var bufferSize = objectLimitSize > 0 ? (int)Math.Min(objectLimitSize - sentBytes, chunkSize) : chunkSize; @@ -350,8 +350,8 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C Chunk = ByteString.CopyFrom(chunkBuffer, 0, bytesCount) } }; - - chunkRequest.Sign(Context.Key.ECDsaKey); + + chunkRequest.Sign(ctx.Key); await stream.Write(chunkRequest); } @@ -374,14 +374,14 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C { var header = args.Header!; - header.OwnerId = Context.Owner; - header.Version = Context.Version; + header.OwnerId = ctx.OwnerId; + header.Version = ctx.Version; var grpcHeader = header.ToMessage(); if (header.Split != null) { - ObjectTools.SetSplitValues(grpcHeader, header.Split, env); + ObjectTools.SetSplitValues(grpcHeader, header.Split, ctx); } var oid = new ObjectID { Value = grpcHeader.Sha256() }; @@ -402,12 +402,12 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C sessionToken.CreateObjectTokenContext( new Address { ContainerId = grpcHeader.ContainerId, ObjectId = oid }, ObjectSessionContext.Types.Verb.Put, - Context.Key.ECDsaKey + ctx.Key ); initRequest.AddMetaHeader(args.XHeaders, sessionToken); - initRequest.Sign(Context.Key.ECDsaKey); + initRequest.Sign(ctx.Key); return await PutObjectInit(initRequest, ctx); } diff --git a/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs index 0bb5499..104fa4c 100644 --- a/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs @@ -17,17 +17,18 @@ internal class SessionServiceProvider : ContextAccessor internal async Task CreateSessionAsync(PrmSessionCreate args) { + var ctx = args.Context!; var request = new CreateRequest { Body = new CreateRequest.Types.Body { - OwnerId = Context.Owner.ToMessage(), + OwnerId = ctx.OwnerId.ToMessage(), Expiration = args.Expiration } }; request.AddMetaHeader(args.XHeaders); - request.Sign(Context.Key.ECDsaKey); + request.Sign(ctx.Key); return await CreateSession(request, args.Context!); } diff --git a/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs b/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs index 05e5723..ccfec71 100644 --- a/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs @@ -15,7 +15,7 @@ internal class SessionProvider(ClientEnvironment env) { if (args.SessionToken is null) { - return await env.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue, ctx)); + return await env.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue) { Context = ctx }); } return new Session.SessionToken().Deserialize(args.SessionToken.Token); diff --git a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs index 4f4fa1b..ec06eab 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs @@ -1,25 +1,26 @@ using FrostFS.SDK.ModelsV2; -using Google.Protobuf; using Grpc.Net.Client; using System; using System.Security.Cryptography; using FrostFS.SDK.Cryptography; -using System.Buffers; namespace FrostFS.SDK.ClientV2; -public class ClientEnvironment(Client client, ECDsa key, OwnerId owner, GrpcChannel channel, ModelsV2.Version version) : IDisposable +public class ClientEnvironment(Client client, ECDsa? key, OwnerId? owner, GrpcChannel channel, ModelsV2.Version version) : IDisposable { private ArrayPool _arrayPool; - internal OwnerId Owner { get; } = owner; + internal OwnerId? Owner { get; } = owner; + internal GrpcChannel Channel { get; private set; } = channel; + internal ModelsV2.Version Version { get; } = version; + internal NetworkSettings? NetworkSettings { get; set; } internal Client Client { get; } = client; - internal ClientKey Key { get; } = new ClientKey(key); + internal ClientKey? Key { get; } = key != null ? new ClientKey(key) : null; /// /// Custom pool is used for predefined sizes of buffers like grpc chunk diff --git a/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs index aad073b..321724a 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs @@ -7,26 +7,25 @@ using FrostFS.Refs; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; using FrostFS.SDK.ModelsV2; -using static FrostFS.Object.Header.Types; namespace FrostFS.SDK.ClientV2; internal class ObjectTools { - internal static ObjectId CalculateObjectId(ObjectHeader header, ClientEnvironment env) + internal static ObjectId CalculateObjectId(ObjectHeader header, Context ctx) { - var grpcHeader = CreateHeader(header, [], env); + var grpcHeader = CreateHeader(header, [], ctx); if (header.Split != null) - SetSplitValues(grpcHeader, header.Split, env); + SetSplitValues(grpcHeader, header.Split, ctx); return new ObjectID { Value = grpcHeader.Sha256() }.ToModel(); } - internal static Object.Object CreateObject(FrostFsObject @object, ClientEnvironment env) + internal static Object.Object CreateObject(FrostFsObject @object, Context ctx) { - @object.Header.OwnerId = env.Owner; - @object.Header.Version = env.Version; + @object.Header.OwnerId = ctx.OwnerId; + @object.Header.Version = ctx.Version; var grpcHeader = @object.Header.ToMessage(); @@ -36,7 +35,7 @@ internal class ObjectTools var split = @object.Header.Split; if (split != null) { - SetSplitValues(grpcHeader, split, env); + SetSplitValues(grpcHeader, split, ctx); } var obj = new Object.Object @@ -48,14 +47,14 @@ internal class ObjectTools obj.Signature = new Refs.Signature { - Key = env.Key.PublicKeyProto, - Sign = ByteString.CopyFrom(env.Key.ECDsaKey.SignData(obj.ObjectId.ToByteArray())), + Key = ctx.PublicKeyCache, + Sign = ByteString.CopyFrom(ctx.Key.SignData(obj.ObjectId.ToByteArray())), }; return obj; } - internal static void SetSplitValues(Header grpcHeader, ModelsV2.Split split, ClientEnvironment env) + internal static void SetSplitValues(Header grpcHeader, ModelsV2.Split split, Context ctx) { grpcHeader.Split = new Header.Types.Split { @@ -67,14 +66,14 @@ internal class ObjectTools if (split.ParentHeader is not null) { - var grpcParentHeader = CreateHeader(split.ParentHeader, [], env); + var grpcParentHeader = CreateHeader(split.ParentHeader, [], ctx); grpcHeader.Split.Parent = new ObjectID { Value = grpcParentHeader.Sha256() }; grpcHeader.Split.ParentHeader = grpcParentHeader; grpcHeader.Split.ParentSignature = new Refs.Signature { - Key = env.Key.PublicKeyProto, - Sign = ByteString.CopyFrom(env.Key.ECDsaKey.SignData(grpcHeader.Split.Parent.ToByteArray())), + Key = ctx.PublicKeyCache, + Sign = ByteString.CopyFrom(ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray())), }; split.Parent = grpcHeader.Split.Parent.ToModel(); @@ -83,12 +82,12 @@ internal class ObjectTools grpcHeader.Split.Previous = split.Previous?.ToMessage(); } - internal static Header CreateHeader(ObjectHeader header, byte[]? payload, ClientEnvironment env) + internal static Header CreateHeader(ObjectHeader header, byte[]? payload, Context ctx) { var grpcHeader = header.ToMessage(); - grpcHeader.OwnerId = env.Owner.ToMessage(); - grpcHeader.Version = env.Version.ToMessage(); + grpcHeader.OwnerId = ctx.OwnerId.ToMessage(); + grpcHeader.Version = ctx.Version.ToMessage(); if (payload != null) // && payload.Length > 0 grpcHeader.PayloadHash = Sha256Checksum(payload); diff --git a/src/FrostFS.SDK.ModelsV2/Client/ClientSettings.cs b/src/FrostFS.SDK.ModelsV2/Client/ClientSettings.cs index 4268d53..049d3f7 100644 --- a/src/FrostFS.SDK.ModelsV2/Client/ClientSettings.cs +++ b/src/FrostFS.SDK.ModelsV2/Client/ClientSettings.cs @@ -1,28 +1,64 @@ +using Google.Protobuf; using System; +using System.Collections.Generic; using System.Text; namespace FrostFS.SDK.ModelsV2; public class ClientSettings { - private static readonly string errorTemplate = "{0} is required parameter"; - - public string Key { get; set; } = string.Empty; + protected static readonly string errorTemplate = "{0} is required parameter"; public string Host { get; set; } = string.Empty; - public void Validate() + public virtual void Validate() { - StringBuilder? error = null; - - if (string.IsNullOrWhiteSpace(Key)) - (error ??= new StringBuilder()).AppendLine(string.Format(errorTemplate, nameof(Key))); - - if (string.IsNullOrWhiteSpace(Host)) - (error ??= new StringBuilder()).AppendLine(string.Format(errorTemplate, nameof(Host))); - - if (error != null) - throw new ArgumentException(error.ToString()); + var errors = CheckFields(); + if (errors != null) + ThrowException(errors); } + protected List? CheckFields() + { + List? errors = null; + + if (string.IsNullOrWhiteSpace(Host)) + (errors ??= []).Add(string.Format(errorTemplate, nameof(Host))); + + return errors; + } + + protected static void ThrowException(List errors) + { + StringBuilder messages = new(); + + foreach (var error in errors) + { + messages.AppendLine(error); + } + + throw new ArgumentException(messages.ToString()); + } +} + +public class SingleOwnerClientSettings : ClientSettings +{ + public string Key { get; set; } = string.Empty; + + public override void Validate() + { + var errors = CheckFields(); + if (errors != null) + ThrowException(errors); + } + + protected List? CheckFields() + { + List? errors = base.CheckFields(); + + if (string.IsNullOrWhiteSpace(Key)) + (errors ??= []).Add(string.Format(errorTemplate, nameof(Key))); + + return errors; + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/ContainerTest.cs b/src/FrostFS.SDK.Tests/ContainerTest.cs index 74f80e3..0af0e70 100644 --- a/src/FrostFS.SDK.Tests/ContainerTest.cs +++ b/src/FrostFS.SDK.Tests/ContainerTest.cs @@ -16,12 +16,12 @@ public abstract class ContainerTestsBase { protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - protected IOptions Settings { get; set; } + protected IOptions Settings { get; set; } protected ContainerMocker Mocker { get; set; } protected ContainerTestsBase() { - Settings = Options.Create(new ClientSettings + Settings = Options.Create(new SingleOwnerClientSettings { Key = key, Host = "http://localhost:8080" diff --git a/src/FrostFS.SDK.Tests/ObjectTest.cs b/src/FrostFS.SDK.Tests/ObjectTest.cs index 6fb3f0a..30ec8f0 100644 --- a/src/FrostFS.SDK.Tests/ObjectTest.cs +++ b/src/FrostFS.SDK.Tests/ObjectTest.cs @@ -19,7 +19,7 @@ public abstract class ObjectTestsBase { protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - protected IOptions Settings { get; set; } + protected IOptions Settings { get; set; } protected ContainerId ContainerId { get; set; } protected NetworkMocker NetworkMocker { get; set; } = new NetworkMocker(key); @@ -31,7 +31,7 @@ public abstract class ObjectTestsBase { var ecdsaKey = key.LoadWif(); - Settings = Options.Create(new ClientSettings + Settings = Options.Create(new SingleOwnerClientSettings { Key = key, Host = "http://localhost:8080" @@ -71,14 +71,17 @@ public class ObjectTest : ObjectTestsBase public async void GetObjectTest() { var client = GetClient(); - var objectId = client.CalculateObjectId(Mocker.ObjectHeader!); - var context = new Context - { - Timeout = TimeSpan.FromSeconds(2) - }; + var ecdsaKey = key.LoadWif(); - var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId) { Context = context }); + var ctx = new Context { + Key = ecdsaKey, + OwnerId = OwnerId.FromKey(ecdsaKey), + Version = new ModelsV2.Version(2, 13) }; + + var objectId = client.CalculateObjectId(Mocker.ObjectHeader!, ctx); + + var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId) { Context = ctx }); Assert.NotNull(result); diff --git a/src/FrostFS.SDK.Tests/SmokeTests.cs b/src/FrostFS.SDK.Tests/SmokeTests.cs index 8b89531..aff3298 100644 --- a/src/FrostFS.SDK.Tests/SmokeTests.cs +++ b/src/FrostFS.SDK.Tests/SmokeTests.cs @@ -13,21 +13,46 @@ using System.Diagnostics; using static FrostFS.Session.SessionToken.Types.Body; using FrostFS.SDK.ClientV2.Parameters; +using FrostFS.SDK.Tests; namespace FrostFS.SDK.SmokeTests; -public class SmokeTests +public abstract class SmokeTestsBase +{ + protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; + protected readonly string url = "http://172.23.32.4:8080"; + + protected ECDsa Key { get; } + + protected OwnerId OwnerId { get; } + + protected ModelsV2.Version Version { get; } + + protected Context Ctx { get; } + + protected SmokeTestsBase() + { + Key = key.LoadWif(); + OwnerId = OwnerId.FromKey(Key); + Version = new ModelsV2.Version(2, 13); + + Ctx = new Context { Key = Key, OwnerId = OwnerId, Version = Version }; + } +} + +public class SmokeTests : SmokeTestsBase { private static readonly PrmWait lightWait = new (100, 1); - private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - private readonly string url = "http://172.23.32.4:8080"; - - [Fact] - public async void NetworkMapTest() - { - using var client = Client.GetInstance(GetOptions(this.key, this.url)); - var result = await client.GetNetmapSnapshotAsync(); + [Theory] + [InlineData(false)] + [InlineData(true)] + public async void NetworkMapTest(bool isSingleOnwerClient) + { + using var client = isSingleOnwerClient ? Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : Client.GetInstance(GetOptions(this.url)); + + PrmNetmapSnapshot? prm = isSingleOnwerClient ? default : new () { Context = Ctx }; + var result = await client.GetNetmapSnapshotAsync(prm); Assert.True(result.Epoch > 0); Assert.Single(result.NodeInfoCollection); @@ -41,12 +66,17 @@ public class SmokeTests Assert.Equal(9, item.Attributes.Count); } - [Fact] - public async void NodeInfoTest() - { - using var client = Client.GetInstance(GetOptions(this.key, this.url)); - var result = await client.GetNodeInfoAsync(); + [Theory] + [InlineData(false)] + [InlineData(true)] + public async void NodeInfoTest(bool isSingleOnwerClient) + { + using var client = isSingleOnwerClient ? Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : Client.GetInstance(GetOptions(this.url)); + + PrmNodeInfo? prm = isSingleOnwerClient ? default : new() { Context = Ctx }; + + var result = await client.GetNodeInfoAsync(prm); Assert.Equal(2, result.Version.Major); Assert.Equal(13, result.Version.Minor); @@ -64,25 +94,25 @@ public class SmokeTests Callback = (cs) => Console.WriteLine($"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds") }; - using var client = Client.GetInstance(GetOptions(this.key, this.url)); + using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); var result = await client.GetNodeInfoAsync(); } - [Fact] - public async void GetSessionTest() + [Theory] + [InlineData(false)] + [InlineData(true)] + public async void GetSessionTest(bool isSingleOnwerClient) { - var ecdsaKey = this.key.LoadWif(); + using var client = isSingleOnwerClient ? Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : Client.GetInstance(GetOptions(this.url)); - using var client = Client.GetInstance(GetOptions(this.key, this.url)); + PrmSessionCreate? prm = isSingleOnwerClient ? new PrmSessionCreate(100) : new PrmSessionCreate(100) { Context = Ctx }; - var token = await client.CreateSessionAsync(new PrmSessionCreate(100)); + var token = await client.CreateSessionAsync(prm); var session = new Session.SessionToken().Deserialize(token.Token); - var owner = OwnerId.FromKey(ecdsaKey); - - var ownerHash = Base58.Decode(owner.Value); + var ownerHash = Base58.Decode(OwnerId.Value); Assert.NotNull(session); Assert.Null(session.Body.Container); @@ -97,7 +127,7 @@ public class SmokeTests [Fact] public async void CreateObjectWithSessionToken() { - using var client = Client.GetInstance(GetOptions(this.key, this.url)); + using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); await Cleanup(client); @@ -144,7 +174,7 @@ public class SmokeTests [Fact] public async void FilterTest() { - using var client = Client.GetInstance(GetOptions(this.key, this.url)); + using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); await Cleanup(client); @@ -230,7 +260,7 @@ public class SmokeTests [InlineData(6 * 1024 * 1024 + 100)] public async void SimpleScenarioTest(int objectSize) { - using var client = Client.GetInstance(GetOptions(this.key, this.url)); + using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); await Cleanup(client); @@ -253,7 +283,7 @@ public class SmokeTests var containerId = await client.CreateContainerAsync(createContainerParam); - var container = await client.GetContainerAsync(new PrmContainerGet(containerId,ctx)); + var container = await client.GetContainerAsync(new PrmContainerGet(containerId) { Context = ctx }); Assert.NotNull(container); Assert.True(callbackInvoked); @@ -318,7 +348,7 @@ public class SmokeTests [InlineData(6 * 1024 * 1024 + 100)] public async void SimpleScenarioWithSessionTest(int objectSize) { - using var client = Client.GetInstance(GetOptions(this.key, this.url)); + using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue)); @@ -338,7 +368,7 @@ public class SmokeTests var containerId = await client.CreateContainerAsync(createContainerParam); - var container = await client.GetContainerAsync(new PrmContainerGet(containerId,ctx)); + var container = await client.GetContainerAsync(new PrmContainerGet(containerId) { Context = ctx }); Assert.NotNull(container); var bytes = GetRandomBytes(objectSize); @@ -406,7 +436,7 @@ public class SmokeTests [InlineData(200)] public async void ClientCutScenarioTest(int objectSize) { - using var client = Client.GetInstance(GetOptions(this.key, this.url)); + using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); await Cleanup(client); @@ -417,13 +447,13 @@ public class SmokeTests var containerId = await client.CreateContainerAsync(createContainerParam); - var context = new Context + var ctx = new Context { Timeout = TimeSpan.FromSeconds(10), Interceptors = new([new MetricsInterceptor()]) }; - var container = await client.GetContainerAsync(new PrmContainerGet(containerId, context)); + var container = await client.GetContainerAsync(new PrmContainerGet(containerId) { Context = ctx }); Assert.NotNull(container); @@ -499,11 +529,19 @@ public class SmokeTests return bytes; } - private static IOptions GetOptions(string key, string url) + private static IOptions GetSingleOwnerOptions(string key, string url) + { + return Options.Create(new SingleOwnerClientSettings + { + Key = key, + Host = url + }); + } + + private static IOptions GetOptions(string url) { return Options.Create(new ClientSettings { - Key = key, Host = url }); }