Compare commits
2 commits
6083834582
...
2aa1b4382f
Author | SHA1 | Date | |
---|---|---|---|
2aa1b4382f | |||
c2501cc112 |
36 changed files with 910 additions and 301 deletions
|
@ -35,6 +35,11 @@ public class Client : IFrostFSClient
|
|||
return new Client(clientOptions, channelOptions);
|
||||
}
|
||||
|
||||
public static IFrostFSClient GetSingleOwnerInstance(IOptions<SingleOwnerClientSettings> clientOptions, GrpcChannelOptions? channelOptions = null)
|
||||
{
|
||||
return new Client(clientOptions, channelOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client.
|
||||
/// </summary>
|
||||
|
@ -46,7 +51,7 @@ public class Client : IFrostFSClient
|
|||
/// <param name="objectService">Object.ObjectService.ObjectServiceClient implementation</param>
|
||||
/// <returns></returns>
|
||||
public static IFrostFSClient GetTestInstance(
|
||||
IOptions<ClientSettings> clientOptions,
|
||||
IOptions<SingleOwnerClientSettings> clientOptions,
|
||||
GrpcChannelOptions? channelOptions,
|
||||
NetmapService.NetmapServiceClient netmapService,
|
||||
SessionService.SessionServiceClient sessionService,
|
||||
|
@ -57,7 +62,7 @@ public class Client : IFrostFSClient
|
|||
}
|
||||
|
||||
private Client(
|
||||
IOptions<ClientSettings> settings,
|
||||
IOptions<SingleOwnerClientSettings> settings,
|
||||
GrpcChannelOptions? channelOptions,
|
||||
ContainerService.ContainerServiceClient containerService,
|
||||
NetmapService.NetmapServiceClient netmapService,
|
||||
|
@ -68,7 +73,7 @@ public class Client : IFrostFSClient
|
|||
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<SingleOwnerClientSettings> 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)
|
||||
{
|
||||
|
|
|
@ -45,6 +45,6 @@ public interface IFrostFSClient : IDisposable
|
|||
#endregion
|
||||
|
||||
#region Tools
|
||||
ObjectId CalculateObjectId(ObjectHeader header);
|
||||
ObjectId CalculateObjectId(ObjectHeader header, Context ctx);
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -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<Interceptor>? 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<CallStatistics>? Callback { get; set; }
|
||||
|
@ -21,4 +34,17 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
11
src/FrostFS.SDK.ClientV2/Parameters/Credentials.cs
Normal file
11
src/FrostFS.SDK.ClientV2/Parameters/Credentials.cs
Normal file
|
@ -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;
|
||||
}
|
14
src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs
Normal file
14
src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Specialized;
|
||||
|
||||
namespace FrostFS.SDK.ClientV2.Parameters;
|
||||
|
||||
public class PrmBase() : IContext
|
||||
{
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
}
|
|
@ -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
|
|||
/// <value>Rules for polling the result</value>
|
||||
public PrmWait? WaitParams { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
public string SessionKey { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -13,11 +12,5 @@ public sealed class PrmContainerDelete(ContainerId containerId, Context? ctx = n
|
|||
/// <value>Rules for polling the result</value>
|
||||
public PrmWait? WaitParams { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; } = ctx;
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; } = ctx;
|
||||
}
|
||||
|
|
|
@ -2,13 +2,6 @@
|
|||
|
||||
namespace FrostFS.SDK.ClientV2.Parameters;
|
||||
|
||||
public sealed class PrmContainerGetAll() : IContext
|
||||
public sealed class PrmContainerGetAll() : PrmBase()
|
||||
{
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,13 +2,6 @@
|
|||
|
||||
namespace FrostFS.SDK.ClientV2.Parameters;
|
||||
|
||||
public sealed class PrmNetmapSnapshot(Context? context = default) : IContext
|
||||
public sealed class PrmNetmapSnapshot() : PrmBase
|
||||
{
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; } = context;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
public sealed class PrmNetworkSettings() : PrmBase
|
||||
{
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; } = context;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
public sealed class PrmNodeInfo() : PrmBase
|
||||
{
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; } = context;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using System.Collections.Specialized;
|
||||
using FrostFS.SDK.ModelsV2;
|
||||
using System.IO;
|
||||
using FrostFS.SDK.ModelsV2;
|
||||
|
||||
namespace FrostFS.SDK.ClientV2.Parameters;
|
||||
|
||||
public sealed class PrmObjectPut : IContext, ISessionToken
|
||||
public sealed class PrmObjectPut : PrmBase, ISessionToken
|
||||
{
|
||||
/// <summary>
|
||||
/// Need to provide values like <c>ContainerId</c> and <c>ObjectType</c> to create and object.
|
||||
|
@ -32,12 +32,9 @@ public sealed class PrmObjectPut : IContext, ISessionToken
|
|||
public int BufferMaxSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// Allows to define a buffer for chunks to manage by the memory allocation and releasing.
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
public byte[]? CustomBuffer { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines container for the search
|
||||
|
@ -17,14 +16,6 @@ public sealed class PrmObjectSearch(ContainerId containerId, params IObjectFilte
|
|||
/// <value>Collection of filters</value>
|
||||
public IEnumerable<IObjectFilter> Filters { get; set; } = filters;
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; } = context;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// FrostFS request X-Headers
|
||||
/// </summary>
|
||||
public NameValueCollection XHeaders { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Context? Context { get; set; } = context;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SessionToken? SessionToken { get; set; }
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient
|
|||
{
|
||||
internal async Task<ModelsV2.Container> 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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<ObjectId> 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;
|
||||
}
|
||||
|
||||
|
@ -337,7 +337,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
|
|||
}
|
||||
};
|
||||
|
||||
chunkRequest.Sign(Context.Key.ECDsaKey);
|
||||
chunkRequest.Sign(ctx.Key);
|
||||
|
||||
await stream.Write(chunkRequest);
|
||||
}
|
||||
|
@ -352,14 +352,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() };
|
||||
|
@ -380,12 +380,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);
|
||||
}
|
||||
|
|
|
@ -17,17 +17,18 @@ internal class SessionServiceProvider : ContextAccessor
|
|||
|
||||
internal async Task<SessionToken> 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!);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
using FrostFS.SDK.ModelsV2;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Net.Client;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using FrostFS.SDK.Cryptography;
|
||||
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -49,4 +49,22 @@ public static class RequestConstructor
|
|||
|
||||
sessionToken.Signature = key.SignMessagePart(sessionToken.Body);
|
||||
}
|
||||
|
||||
public static void CreateContainerTokenContext(this Session.SessionToken sessionToken,
|
||||
ContainerID? containerId,
|
||||
ContainerSessionContext.Types.Verb verb,
|
||||
ECDsa key)
|
||||
{
|
||||
if (sessionToken.Body.Container?.ContainerId != null)
|
||||
return;
|
||||
|
||||
sessionToken.Body.Container = new() { Verb = verb };
|
||||
|
||||
if (containerId != null)
|
||||
sessionToken.Body.Container.ContainerId = containerId;
|
||||
else
|
||||
sessionToken.Body.Container.Wildcard = true;
|
||||
|
||||
sessionToken.Signature = key.SignMessagePart(sessionToken.Body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string>? CheckFields()
|
||||
{
|
||||
List<string>? errors = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Host))
|
||||
(errors ??= []).Add(string.Format(errorTemplate, nameof(Host)));
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
protected static void ThrowException(List<string> 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<string>? CheckFields()
|
||||
{
|
||||
List<string>? errors = base.CheckFields();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Key))
|
||||
(errors ??= []).Add(string.Format(errorTemplate, nameof(Key)));
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
|
@ -16,12 +16,12 @@ public abstract class ContainerTestsBase
|
|||
{
|
||||
protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||
|
||||
protected IOptions<ClientSettings> Settings { get; set; }
|
||||
protected IOptions<SingleOwnerClientSettings> 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"
|
||||
|
|
|
@ -27,6 +27,12 @@ public abstract class ServiceBase(string key)
|
|||
public static BasicAcl DefaultAcl { get; } = BasicAcl.PublicRW;
|
||||
public static PlacementPolicy DefaultPlacementPolicy { get; } = new PlacementPolicy(true, new Replica(1));
|
||||
|
||||
public Metadata Metadata { get; protected set; }
|
||||
public DateTime? DateTime { get; protected set; }
|
||||
public CancellationToken CancellationToken { get; protected set; }
|
||||
|
||||
public CancellationTokenSource CancellationTokenSource { get; protected set; } = new CancellationTokenSource();
|
||||
|
||||
public static Metadata ResponseMetaData => [];
|
||||
|
||||
protected ResponseVerificationHeader GetResponseVerificationHeader(IResponse response)
|
||||
|
|
|
@ -3,24 +3,40 @@ using FrostFS.Netmap;
|
|||
using Grpc.Core;
|
||||
using FrostFS.SDK.ClientV2;
|
||||
using Google.Protobuf;
|
||||
using FrostFS.SDK.ModelsV2;
|
||||
|
||||
namespace FrostFS.SDK.Tests;
|
||||
|
||||
public class NetworkMocker(string key) : ServiceBase(key)
|
||||
{
|
||||
private static readonly string[] parameterKeys = [
|
||||
internal static readonly string[] ParameterKeys = [
|
||||
"AuditFee",
|
||||
"BasicIncomeRate",
|
||||
"ContainerFee",
|
||||
"ContainerAliasFee",
|
||||
"EpochDuration",
|
||||
"IRCandidateFee",
|
||||
"InnerRingCandidateFee",
|
||||
"MaxECDataCount",
|
||||
"MaxECParityCount",
|
||||
"MaxObjectSize",
|
||||
"WithdrawalFee",
|
||||
"WithdrawFee",
|
||||
"HomomorphicHashingDisabled",
|
||||
"MaintenanceModeAllowed" ];
|
||||
"MaintenanceModeAllowed"
|
||||
];
|
||||
|
||||
public Dictionary<string, byte[]>? Parameters { get; set; }
|
||||
|
||||
public LocalNodeInfoResponse NodeInfoResponse { get; set; }
|
||||
|
||||
public LocalNodeInfoRequest LocalNodeInfoRequest { get; set; }
|
||||
|
||||
public NetworkInfoRequest NetworkInfoRequest { get; set; }
|
||||
|
||||
public NetmapSnapshotResponse NetmapSnapshotResponse { get; set; }
|
||||
|
||||
public NetmapSnapshotRequest NetmapSnapshotRequest { get; set; }
|
||||
|
||||
|
||||
public Mock<NetmapService.NetmapServiceClient> GetMock()
|
||||
{
|
||||
var mock = new Mock<NetmapService.NetmapServiceClient>();
|
||||
|
@ -29,7 +45,7 @@ public class NetworkMocker(string key) : ServiceBase(key)
|
|||
|
||||
var networkConfig = new NetworkConfig();
|
||||
|
||||
foreach (var key in parameterKeys)
|
||||
foreach (var key in ParameterKeys)
|
||||
{
|
||||
networkConfig.Parameters.Add(new NetworkConfig.Types.Parameter
|
||||
{
|
||||
|
@ -62,7 +78,10 @@ public class NetworkMocker(string key) : ServiceBase(key)
|
|||
It.IsAny<CancellationToken>()))
|
||||
.Returns((NetworkInfoRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
|
||||
{
|
||||
Verifier.CheckRequest(r);
|
||||
NetworkInfoRequest = r;
|
||||
Metadata = m;
|
||||
DateTime = dt;
|
||||
CancellationToken = ct;
|
||||
|
||||
return new AsyncUnaryCall<NetworkInfoResponse>(
|
||||
Task.FromResult(response),
|
||||
|
@ -72,6 +91,58 @@ public class NetworkMocker(string key) : ServiceBase(key)
|
|||
() => { });
|
||||
});
|
||||
|
||||
if (NodeInfoResponse != null)
|
||||
{
|
||||
NodeInfoResponse.MetaHeader = ResponseMetaHeader;
|
||||
NodeInfoResponse.VerifyHeader = GetResponseVerificationHeader(NodeInfoResponse);
|
||||
|
||||
mock.Setup(x => x.LocalNodeInfoAsync(
|
||||
It.IsAny<LocalNodeInfoRequest>(),
|
||||
It.IsAny<Metadata>(),
|
||||
It.IsAny<DateTime?>(),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.Returns((LocalNodeInfoRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
|
||||
{
|
||||
LocalNodeInfoRequest = r;
|
||||
Metadata = m;
|
||||
DateTime = dt;
|
||||
CancellationToken = ct;
|
||||
|
||||
return new AsyncUnaryCall<LocalNodeInfoResponse>(
|
||||
Task.FromResult(NodeInfoResponse),
|
||||
Task.FromResult(ResponseMetaData),
|
||||
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
|
||||
() => ResponseMetaData,
|
||||
() => { });
|
||||
});
|
||||
}
|
||||
|
||||
if (NetmapSnapshotResponse != null)
|
||||
{
|
||||
NetmapSnapshotResponse.MetaHeader = ResponseMetaHeader;
|
||||
NetmapSnapshotResponse.VerifyHeader = GetResponseVerificationHeader(NetmapSnapshotResponse);
|
||||
|
||||
mock.Setup(x => x.NetmapSnapshotAsync(
|
||||
It.IsAny<NetmapSnapshotRequest>(),
|
||||
It.IsAny<Metadata>(),
|
||||
It.IsAny<DateTime?>(),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.Returns((NetmapSnapshotRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
|
||||
{
|
||||
NetmapSnapshotRequest = r;
|
||||
Metadata = m;
|
||||
DateTime = dt;
|
||||
CancellationToken = ct;
|
||||
|
||||
return new AsyncUnaryCall<NetmapSnapshotResponse>(
|
||||
Task.FromResult(NetmapSnapshotResponse),
|
||||
Task.FromResult(ResponseMetaData),
|
||||
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
|
||||
() => ResponseMetaData,
|
||||
() => { });
|
||||
});
|
||||
}
|
||||
|
||||
return mock;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
using Moq;
|
||||
using FrostFS.Session;
|
||||
using Grpc.Core;
|
||||
using FrostFS.SDK.ClientV2;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Moq;
|
||||
|
||||
namespace FrostFS.SDK.Tests;
|
||||
|
||||
|
@ -12,23 +11,32 @@ public class SessionMocker(string key) : ServiceBase(key)
|
|||
|
||||
public byte[]? SessionKey { get; set; }
|
||||
|
||||
public CreateRequest CreateSessionRequest { get; private set; }
|
||||
|
||||
public Mock<SessionService.SessionServiceClient> GetMock()
|
||||
{
|
||||
var mock = new Mock<SessionService.SessionServiceClient>();
|
||||
|
||||
Random rand = new();
|
||||
SessionId = new byte[32];
|
||||
SessionKey = new byte[32];
|
||||
|
||||
if (SessionId == null)
|
||||
{
|
||||
SessionId = new byte[32];
|
||||
rand.NextBytes(SessionId);
|
||||
}
|
||||
|
||||
if (SessionKey == null)
|
||||
{
|
||||
SessionKey = new byte[32];
|
||||
rand.NextBytes(SessionKey);
|
||||
}
|
||||
|
||||
CreateResponse response = new()
|
||||
{
|
||||
Body = new CreateResponse.Types.Body
|
||||
{
|
||||
Id = ByteString.CopyFrom(SessionId),
|
||||
SessionKey = ByteString.CopyFrom(SessionId)
|
||||
SessionKey = ByteString.CopyFrom(SessionKey)
|
||||
},
|
||||
MetaHeader = ResponseMetaHeader
|
||||
};
|
||||
|
@ -42,7 +50,10 @@ public class SessionMocker(string key) : ServiceBase(key)
|
|||
It.IsAny<CancellationToken>()))
|
||||
.Returns((CreateRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
|
||||
{
|
||||
Verifier.CheckRequest(r);
|
||||
CreateSessionRequest = r;
|
||||
Metadata = m;
|
||||
DateTime = dt;
|
||||
CancellationToken = ct;
|
||||
|
||||
return new AsyncUnaryCall<CreateResponse>(
|
||||
Task.FromResult(response),
|
||||
|
|
303
src/FrostFS.SDK.Tests/NetworkTest.cs
Normal file
303
src/FrostFS.SDK.Tests/NetworkTest.cs
Normal file
|
@ -0,0 +1,303 @@
|
|||
using FrostFS.Netmap;
|
||||
using FrostFS.SDK.ClientV2;
|
||||
using FrostFS.SDK.ClientV2.Interfaces;
|
||||
using FrostFS.SDK.ClientV2.Parameters;
|
||||
using FrostFS.SDK.Cryptography;
|
||||
using FrostFS.SDK.ModelsV2;
|
||||
using FrostFS.SDK.ModelsV2.Enums;
|
||||
using Google.Protobuf;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
|
||||
namespace FrostFS.SDK.Tests;
|
||||
|
||||
public abstract class NetworkTestsBase
|
||||
{
|
||||
protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||
|
||||
protected IOptions<SingleOwnerClientSettings> Settings { get; set; }
|
||||
|
||||
protected ModelsV2.Version Version { get; set; } = new ModelsV2.Version(2, 13);
|
||||
|
||||
protected ECDsa ECDsaKey { get; set; }
|
||||
protected OwnerId OwnerId { get; set; }
|
||||
protected NetworkMocker Mocker { get; set; }
|
||||
|
||||
protected NetworkTestsBase()
|
||||
{
|
||||
Settings = Options.Create(new SingleOwnerClientSettings
|
||||
{
|
||||
Key = key,
|
||||
Host = "http://localhost:8080"
|
||||
});
|
||||
|
||||
ECDsaKey = key.LoadWif();
|
||||
OwnerId = OwnerId.FromKey(ECDsaKey);
|
||||
|
||||
Mocker = new NetworkMocker(this.key);
|
||||
}
|
||||
|
||||
protected IFrostFSClient GetClient()
|
||||
{
|
||||
return ClientV2.Client.GetTestInstance(
|
||||
Settings,
|
||||
null,
|
||||
Mocker.GetMock().Object,
|
||||
new SessionMocker(this.key).GetMock().Object,
|
||||
new ContainerMocker(this.key).GetMock().Object,
|
||||
new ObjectMocker(this.key).GetMock().Object);
|
||||
}
|
||||
}
|
||||
|
||||
public class NetworkTest : NetworkTestsBase
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async void NetworkSettingsTest(bool useContext)
|
||||
{
|
||||
Mocker.Parameters = new Dictionary<string, byte[]>
|
||||
{
|
||||
{ "AuditFee", [1] },
|
||||
{ "BasicIncomeRate", [2] },
|
||||
{ "ContainerFee", [3] },
|
||||
{ "ContainerAliasFee", [4] },
|
||||
{ "EpochDuration", [5] },
|
||||
{ "InnerRingCandidateFee", [6] },
|
||||
{ "MaxECDataCount", [7] },
|
||||
{ "MaxECParityCount", [8] },
|
||||
{ "MaxObjectSize", [9] },
|
||||
{ "WithdrawFee", [10] },
|
||||
{ "HomomorphicHashingDisabled", [1] },
|
||||
{ "MaintenanceModeAllowed", [1] },
|
||||
};
|
||||
|
||||
var param = new PrmNetworkSettings();
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
param.Context = new Context
|
||||
{
|
||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||
Timeout = TimeSpan.FromSeconds(20),
|
||||
OwnerId = OwnerId,
|
||||
Key = ECDsaKey,
|
||||
Version = Version
|
||||
};
|
||||
}
|
||||
|
||||
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
var result = await GetClient().GetNetworkSettingsAsync(param);
|
||||
|
||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Equal(Mocker.Parameters["AuditFee"], [(byte)result.AuditFee]);
|
||||
Assert.Equal(Mocker.Parameters["BasicIncomeRate"], [(byte)result.BasicIncomeRate]);
|
||||
Assert.Equal(Mocker.Parameters["ContainerFee"], [(byte)result.ContainerFee]);
|
||||
Assert.Equal(Mocker.Parameters["ContainerAliasFee"], [(byte)result.ContainerAliasFee]);
|
||||
Assert.Equal(Mocker.Parameters["EpochDuration"], [(byte)result.EpochDuration]);
|
||||
Assert.Equal(Mocker.Parameters["InnerRingCandidateFee"], [(byte)result.InnerRingCandidateFee]);
|
||||
Assert.Equal(Mocker.Parameters["MaxECDataCount"], [(byte)result.MaxECDataCount]);
|
||||
Assert.Equal(Mocker.Parameters["MaxECParityCount"], [(byte)result.MaxECParityCount]);
|
||||
Assert.Equal(Mocker.Parameters["MaxObjectSize"], [(byte)result.MaxObjectSize]);
|
||||
Assert.Equal(Mocker.Parameters["WithdrawFee"], [(byte)result.WithdrawFee]);
|
||||
|
||||
Assert.True(result.HomomorphicHashingDisabled);
|
||||
Assert.True(result.MaintenanceModeAllowed);
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken);
|
||||
Assert.NotNull(Mocker.DateTime);
|
||||
|
||||
Assert.True(Mocker.DateTime.Value >= validTimeoutFrom);
|
||||
Assert.True(Mocker.DateTime.Value <= validTimeoutTo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Empty(Mocker.NetworkInfoRequest.MetaHeader.XHeaders);
|
||||
Assert.Null(Mocker.DateTime);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async void NetmapSnapshotTest(bool useContext)
|
||||
{
|
||||
var body = new NetmapSnapshotResponse.Types.Body
|
||||
{
|
||||
Netmap = new Netmap.Netmap { Epoch = 99 }
|
||||
};
|
||||
|
||||
var nodeInfo1 = new NodeInfo
|
||||
{
|
||||
State = NodeInfo.Types.State.Online,
|
||||
PublicKey = ByteString.CopyFrom([1, 2, 3])
|
||||
};
|
||||
|
||||
nodeInfo1.Addresses.Add("address1");
|
||||
nodeInfo1.Addresses.Add("address2");
|
||||
nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1"});
|
||||
nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" });
|
||||
|
||||
var nodeInfo2 = new NodeInfo
|
||||
{
|
||||
State = NodeInfo.Types.State.Offline,
|
||||
PublicKey = ByteString.CopyFrom([3,4,5])
|
||||
};
|
||||
|
||||
nodeInfo2.Addresses.Add("address3");
|
||||
nodeInfo2.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key3", Value = "value3" });
|
||||
|
||||
body.Netmap.Nodes.Add(nodeInfo1);
|
||||
body.Netmap.Nodes.Add(nodeInfo2);
|
||||
|
||||
Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body };
|
||||
|
||||
var param = new PrmNetmapSnapshot();
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
param.XHeaders.Add("headerKey1", "headerValue1");
|
||||
param.Context = new Context
|
||||
{
|
||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||
Timeout = TimeSpan.FromSeconds(20),
|
||||
OwnerId = OwnerId,
|
||||
Key = ECDsaKey,
|
||||
Version = Version
|
||||
};
|
||||
}
|
||||
|
||||
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
var result = await GetClient().GetNetmapSnapshotAsync(param);
|
||||
|
||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Equal(99u, result.Epoch);
|
||||
Assert.Equal(2, result.NodeInfoCollection.Count);
|
||||
|
||||
var node1 = result.NodeInfoCollection[0];
|
||||
Assert.Equal(NodeState.Online, node1.State);
|
||||
Assert.Equal(2, node1.Addresses.Count);
|
||||
Assert.Equal("address1", node1.Addresses.ElementAt(0));
|
||||
Assert.Equal("address2", node1.Addresses.ElementAt(1));
|
||||
|
||||
Assert.Equal(2, node1.Attributes.Count);
|
||||
|
||||
Assert.Equal("key1", node1.Attributes.ElementAt(0).Key);
|
||||
Assert.Equal("value1", node1.Attributes.ElementAt(0).Value);
|
||||
Assert.Equal("key2", node1.Attributes.ElementAt(1).Key);
|
||||
Assert.Equal("value2", node1.Attributes.ElementAt(1).Value);
|
||||
|
||||
var node2 = result.NodeInfoCollection[1];
|
||||
Assert.Equal(NodeState.Offline, node2.State);
|
||||
Assert.Single(node2.Addresses);
|
||||
Assert.Equal("address3", node2.Addresses.ElementAt(0));
|
||||
|
||||
Assert.Single(node2.Attributes);
|
||||
|
||||
Assert.Equal("key3", node2.Attributes.ElementAt(0).Key);
|
||||
Assert.Equal("value3", node2.Attributes.ElementAt(0).Value);
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
Assert.Single(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders);
|
||||
Assert.Equal(param.XHeaders.Keys[0], Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders.First().Key);
|
||||
Assert.Equal(param.XHeaders[param.XHeaders.Keys[0]], Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders.First().Value);
|
||||
|
||||
Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken);
|
||||
Assert.NotNull(Mocker.DateTime);
|
||||
|
||||
Assert.True(Mocker.DateTime.Value >= validTimeoutFrom);
|
||||
Assert.True(Mocker.DateTime.Value <= validTimeoutTo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Empty(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders);
|
||||
Assert.Null(Mocker.DateTime);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async void NodeInfoTest(bool useContext)
|
||||
{
|
||||
var body = new LocalNodeInfoResponse.Types.Body
|
||||
{
|
||||
NodeInfo = new NodeInfo()
|
||||
{
|
||||
State = NodeInfo.Types.State.Online,
|
||||
PublicKey = ByteString.CopyFrom([1, 2, 3])
|
||||
},
|
||||
Version = new Refs.Version { Major = 2, Minor = 12 }
|
||||
};
|
||||
|
||||
body.NodeInfo.Addresses.Add("address1");
|
||||
body.NodeInfo.Addresses.Add("address2");
|
||||
body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1"});
|
||||
body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" });
|
||||
|
||||
Mocker.NodeInfoResponse = new LocalNodeInfoResponse { Body = body };
|
||||
|
||||
var param = new PrmNodeInfo();
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
param.XHeaders.Add("headerKey1", "headerValue1");
|
||||
param.Context = new Context
|
||||
{
|
||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||
Timeout = TimeSpan.FromSeconds(20),
|
||||
OwnerId = OwnerId,
|
||||
Key = ECDsaKey,
|
||||
Version = Version
|
||||
};
|
||||
}
|
||||
|
||||
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
var result = await GetClient().GetNodeInfoAsync(param);
|
||||
|
||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Equal(NodeState.Online, result.State);
|
||||
|
||||
Assert.Equal(2, result.Addresses.Count);
|
||||
Assert.Equal("address1", result.Addresses.ElementAt(0));
|
||||
Assert.Equal("address2", result.Addresses.ElementAt(1));
|
||||
|
||||
Assert.Equal(2, result.Attributes.Count);
|
||||
Assert.Equal("value1", result.Attributes["key1"]);
|
||||
Assert.Equal("value2", result.Attributes["key2"]);
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
Assert.Single(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders);
|
||||
Assert.Equal(param.XHeaders.Keys[0], Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders.First().Key);
|
||||
Assert.Equal(param.XHeaders[param.XHeaders.Keys[0]], Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders.First().Value);
|
||||
|
||||
Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken);
|
||||
Assert.NotNull(Mocker.DateTime);
|
||||
|
||||
Assert.True(Mocker.DateTime.Value >= validTimeoutFrom);
|
||||
Assert.True(Mocker.DateTime.Value <= validTimeoutTo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Empty(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders);
|
||||
Assert.Null(Mocker.DateTime);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ public abstract class ObjectTestsBase
|
|||
{
|
||||
protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||
|
||||
protected IOptions<ClientSettings> Settings { get; set; }
|
||||
protected IOptions<SingleOwnerClientSettings> 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);
|
||||
|
||||
|
|
127
src/FrostFS.SDK.Tests/SessionTests.cs
Normal file
127
src/FrostFS.SDK.Tests/SessionTests.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using FrostFS.SDK.ClientV2;
|
||||
using FrostFS.SDK.ClientV2.Interfaces;
|
||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
||||
using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
|
||||
using FrostFS.SDK.ClientV2.Parameters;
|
||||
using FrostFS.SDK.Cryptography;
|
||||
using FrostFS.SDK.ModelsV2;
|
||||
using FrostFS.SDK.ModelsV2.Netmap;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace FrostFS.SDK.Tests;
|
||||
|
||||
public abstract class SessionTestsBase
|
||||
{
|
||||
protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||
|
||||
protected IOptions<SingleOwnerClientSettings> Settings { get; set; }
|
||||
|
||||
|
||||
protected ECDsa ECDsaKey { get; set; }
|
||||
protected OwnerId OwnerId { get; set; }
|
||||
protected SessionMocker Mocker { get; set; }
|
||||
|
||||
protected SessionTestsBase()
|
||||
{
|
||||
Settings = Options.Create(new SingleOwnerClientSettings
|
||||
{
|
||||
Key = key,
|
||||
Host = "http://localhost:8080"
|
||||
});
|
||||
|
||||
ECDsaKey = key.LoadWif();
|
||||
OwnerId = OwnerId.FromKey(ECDsaKey);
|
||||
|
||||
Mocker = new SessionMocker(this.key)
|
||||
{
|
||||
PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
|
||||
Version = new ModelsV2.Version(2, 13)
|
||||
};
|
||||
}
|
||||
|
||||
protected IFrostFSClient GetClient()
|
||||
{
|
||||
return ClientV2.Client.GetTestInstance(
|
||||
Settings,
|
||||
null,
|
||||
new NetworkMocker(this.key).GetMock().Object,
|
||||
Mocker.GetMock().Object,
|
||||
new ContainerMocker(this.key).GetMock().Object,
|
||||
new ObjectMocker(this.key).GetMock().Object);
|
||||
}
|
||||
}
|
||||
|
||||
public class SessionTest : SessionTestsBase
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async void CreateSessionTest(bool useContext)
|
||||
{
|
||||
var exp = 100u;
|
||||
var param = new PrmSessionCreate(exp);
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
param.XHeaders.Add("headerKey1", "headerValue1");
|
||||
param.Context = new Context
|
||||
{
|
||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||
Timeout = TimeSpan.FromSeconds(20),
|
||||
OwnerId = OwnerId,
|
||||
Key = ECDsaKey,
|
||||
Version = Mocker.Version
|
||||
};
|
||||
}
|
||||
|
||||
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
var result = await GetClient().CreateSessionAsync(param);
|
||||
|
||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.Token);
|
||||
|
||||
var session = new Session.SessionToken().Deserialize(result.Token);
|
||||
|
||||
Assert.Equal(Mocker.SessionId, session.Body.Id);
|
||||
Assert.Equal(Mocker.SessionKey, session.Body.SessionKey);
|
||||
|
||||
Assert.Equal(OwnerId.ToMessage(), session.Body.OwnerId);
|
||||
Assert.Equal(exp, session.Body.Lifetime.Exp);
|
||||
Assert.Equal(exp, session.Body.Lifetime.Iat);
|
||||
Assert.Equal(exp, session.Body.Lifetime.Nbf);
|
||||
Assert.Null(session.Body.Container);
|
||||
|
||||
Assert.NotNull(Mocker.CreateSessionRequest);
|
||||
|
||||
Assert.Equal(OwnerId.ToMessage(), Mocker.CreateSessionRequest.Body.OwnerId);
|
||||
Assert.Equal(exp, Mocker.CreateSessionRequest.Body.Expiration);
|
||||
Assert.NotNull(Mocker.CreateSessionRequest.MetaHeader);
|
||||
Assert.Equal(Mocker.Version.ToMessage(), Mocker.CreateSessionRequest.MetaHeader.Version);
|
||||
|
||||
|
||||
Assert.Null(Mocker.Metadata);
|
||||
|
||||
if (useContext)
|
||||
{
|
||||
Assert.Single(Mocker.CreateSessionRequest.MetaHeader.XHeaders);
|
||||
Assert.Equal(param.XHeaders.Keys[0], Mocker.CreateSessionRequest.MetaHeader.XHeaders.First().Key);
|
||||
Assert.Equal(param.XHeaders[param.XHeaders.Keys[0]], Mocker.CreateSessionRequest.MetaHeader.XHeaders.First().Value);
|
||||
|
||||
Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken);
|
||||
Assert.NotNull(Mocker.DateTime);
|
||||
|
||||
Assert.True(Mocker.DateTime.Value >= validTimeoutFrom);
|
||||
Assert.True(Mocker.DateTime.Value <= validTimeoutTo);
|
||||
Assert.True(validTimeoutTo.Ticks >= Mocker.DateTime.Value.Ticks);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Empty(Mocker.CreateSessionRequest.MetaHeader.XHeaders);
|
||||
Assert.Null(Mocker.DateTime);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async void NetworkMapTest(bool isSingleOnwerClient)
|
||||
{
|
||||
using var client = Client.GetInstance(GetOptions(this.key, this.url));
|
||||
using var client = isSingleOnwerClient ? Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : Client.GetInstance(GetOptions(this.url));
|
||||
|
||||
var result = await client.GetNetmapSnapshotAsync();
|
||||
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<ClientSettings> GetOptions(string key, string url)
|
||||
private static IOptions<SingleOwnerClientSettings> GetSingleOwnerOptions(string key, string url)
|
||||
{
|
||||
return Options.Create(new SingleOwnerClientSettings
|
||||
{
|
||||
Key = key,
|
||||
Host = url
|
||||
});
|
||||
}
|
||||
|
||||
private static IOptions<ClientSettings> GetOptions(string url)
|
||||
{
|
||||
return Options.Create(new ClientSettings
|
||||
{
|
||||
Key = key,
|
||||
Host = url
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue