Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
cf1fae8d2c | |||
423b5a35b0 | |||
d4c88467d8 | |||
2a71ea3ff9 | |||
c107a219b5 | |||
7125ecae64 | |||
c36a707cb5 |
243 changed files with 4973 additions and 2418 deletions
|
@ -3,11 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.5.002.0
|
VisualStudioVersion = 17.5.002.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ClientV2", "src\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Client", "src\FrostFS.SDK.Client\FrostFS.SDK.Client.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Protos", "src\FrostFS.SDK.Protos\FrostFS.SDK.Protos.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
22
src/FrostFS.SDK.Client/Caches.cs
Normal file
22
src/FrostFS.SDK.Client/Caches.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
internal static class Caches
|
||||||
|
{
|
||||||
|
private static readonly IMemoryCache _ownersCache = new MemoryCache(new MemoryCacheOptions
|
||||||
|
{
|
||||||
|
// TODO: get from options?
|
||||||
|
SizeLimit = 256
|
||||||
|
});
|
||||||
|
|
||||||
|
private static readonly IMemoryCache _containersCache = new MemoryCache(new MemoryCacheOptions
|
||||||
|
{
|
||||||
|
// TODO: get from options?
|
||||||
|
SizeLimit = 1024
|
||||||
|
});
|
||||||
|
|
||||||
|
internal static IMemoryCache Owners => _ownersCache;
|
||||||
|
|
||||||
|
internal static IMemoryCache Containers => _containersCache;
|
||||||
|
}
|
14
src/FrostFS.SDK.Client/CllientKey.cs
Normal file
14
src/FrostFS.SDK.Client/CllientKey.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public class ClientKey(ECDsa key)
|
||||||
|
{
|
||||||
|
internal ECDsa ECDsaKey { get; } = key;
|
||||||
|
|
||||||
|
internal ByteString PublicKeyProto { get; } = ByteString.CopyFrom(key.PublicKey());
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class FrostFsException : Exception
|
public class FrostFsException : Exception
|
||||||
{
|
{
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public class FrostFsInvalidObjectException : FrostFsException
|
||||||
|
{
|
||||||
|
public FrostFsInvalidObjectException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsInvalidObjectException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsInvalidObjectException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public class FrostFsResponseException : FrostFsException
|
||||||
|
{
|
||||||
|
public FrostFsResponseStatus? Status { get; private set; }
|
||||||
|
|
||||||
|
public FrostFsResponseException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsResponseException(FrostFsResponseStatus status)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsResponseException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsResponseException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
18
src/FrostFS.SDK.Client/Exceptions/FrostFsStreamException.cs
Normal file
18
src/FrostFS.SDK.Client/Exceptions/FrostFsStreamException.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public class FrostFsStreamException : FrostFsException
|
||||||
|
{
|
||||||
|
public FrostFsStreamException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsStreamException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsStreamException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
18
src/FrostFS.SDK.Client/Exceptions/SessionExpiredException.cs
Normal file
18
src/FrostFS.SDK.Client/Exceptions/SessionExpiredException.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public class SessionExpiredException : FrostFsException
|
||||||
|
{
|
||||||
|
public SessionExpiredException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionExpiredException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionExpiredException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public class SessionNotFoundException : FrostFsException
|
||||||
|
{
|
||||||
|
public SessionNotFoundException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionNotFoundException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionNotFoundException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<LangVersion>12.0</LangVersion>
|
<LangVersion>12.0</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||||
|
@ -22,15 +22,15 @@
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
||||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
|
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
|
||||||
<PackageReference Include="System.Runtime.Caching" Version="8.0.0" />
|
<PackageReference Include="System.Runtime.Caching" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj" />
|
<ProjectReference Include="..\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj" />
|
||||||
<ProjectReference Include="..\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj" />
|
<ProjectReference Include="..\FrostFS.SDK.Protos\FrostFS.SDK.Protos.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -6,10 +6,12 @@ using System.Threading.Tasks;
|
||||||
using Frostfs.V2.Ape;
|
using Frostfs.V2.Ape;
|
||||||
using Frostfs.V2.Apemanager;
|
using Frostfs.V2.Apemanager;
|
||||||
|
|
||||||
|
using FrostFS.Accounting;
|
||||||
using FrostFS.Container;
|
using FrostFS.Container;
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
using FrostFS.SDK.ClientV2.Interfaces;
|
using FrostFS.SDK.Client.Interfaces;
|
||||||
|
using FrostFS.SDK.Client.Services;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
using FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ using Grpc.Net.Client;
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class FrostFSClient : IFrostFSClient
|
public class FrostFSClient : IFrostFSClient
|
||||||
{
|
{
|
||||||
|
@ -35,7 +37,9 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
internal ObjectService.ObjectServiceClient? ObjectServiceClient { get; set; }
|
internal ObjectService.ObjectServiceClient? ObjectServiceClient { get; set; }
|
||||||
|
|
||||||
internal ClientEnvironment ClientCtx { get; set; }
|
internal AccountingService.AccountingServiceClient? AccountingServiceClient { get; set; }
|
||||||
|
|
||||||
|
internal ClientContext ClientCtx { get; set; }
|
||||||
|
|
||||||
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, GrpcChannelOptions? channelOptions = null)
|
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, GrpcChannelOptions? channelOptions = null)
|
||||||
{
|
{
|
||||||
|
@ -89,7 +93,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
var ecdsaKey = settings.Value.Key.LoadWif();
|
var ecdsaKey = settings.Value.Key.LoadWif();
|
||||||
FrostFsOwner.FromKey(ecdsaKey);
|
FrostFsOwner.FromKey(ecdsaKey);
|
||||||
|
|
||||||
ClientCtx = new ClientEnvironment(
|
ClientCtx = new ClientContext(
|
||||||
client: this,
|
client: this,
|
||||||
key: ecdsaKey,
|
key: ecdsaKey,
|
||||||
owner: FrostFsOwner.FromKey(ecdsaKey),
|
owner: FrostFsOwner.FromKey(ecdsaKey),
|
||||||
|
@ -104,13 +108,13 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
private FrostFSClient(IOptions<ClientSettings> options, GrpcChannelOptions? channelOptions)
|
private FrostFSClient(IOptions<ClientSettings> options, GrpcChannelOptions? channelOptions)
|
||||||
{
|
{
|
||||||
var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized");
|
var clientSettings = (options?.Value) ?? throw new ArgumentNullException(nameof(options), "Options value must be initialized");
|
||||||
|
|
||||||
clientSettings.Validate();
|
clientSettings.Validate();
|
||||||
|
|
||||||
var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
|
var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
|
||||||
|
|
||||||
ClientCtx = new ClientEnvironment(
|
ClientCtx = new ClientContext(
|
||||||
this,
|
this,
|
||||||
key: null,
|
key: null,
|
||||||
owner: null,
|
owner: null,
|
||||||
|
@ -123,7 +127,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
private FrostFSClient(IOptions<SingleOwnerClientSettings> options, GrpcChannelOptions? channelOptions)
|
private FrostFSClient(IOptions<SingleOwnerClientSettings> options, GrpcChannelOptions? channelOptions)
|
||||||
{
|
{
|
||||||
var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized");
|
var clientSettings = (options?.Value) ?? throw new ArgumentNullException(nameof(options), "Options value must be initialized");
|
||||||
|
|
||||||
clientSettings.Validate();
|
clientSettings.Validate();
|
||||||
|
|
||||||
|
@ -131,7 +135,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
|
var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
|
||||||
|
|
||||||
ClientCtx = new ClientEnvironment(
|
ClientCtx = new ClientContext(
|
||||||
this,
|
this,
|
||||||
key: ecdsaKey,
|
key: ecdsaKey,
|
||||||
owner: FrostFsOwner.FromKey(ecdsaKey),
|
owner: FrostFsOwner.FromKey(ecdsaKey),
|
||||||
|
@ -142,6 +146,19 @@ public class FrostFSClient : IFrostFSClient
|
||||||
// CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) });
|
// CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal FrostFSClient(WrapperPrm prm, SessionCache cache)
|
||||||
|
{
|
||||||
|
ClientCtx = new ClientContext(
|
||||||
|
client: this,
|
||||||
|
key: prm.Key,
|
||||||
|
owner: FrostFsOwner.FromKey(prm.Key!),
|
||||||
|
channel: InitGrpcChannel(prm.Address, null), //prm.GrpcChannelOptions),
|
||||||
|
version: new FrostFsVersion(2, 13))
|
||||||
|
{
|
||||||
|
SessionCache = cache
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
@ -158,7 +175,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
|
|
||||||
#region ApeManagerImplementation
|
#region ApeManagerImplementation
|
||||||
public Task<byte[]> AddChainAsync(PrmApeChainAdd args)
|
public Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
{
|
{
|
||||||
|
@ -268,6 +285,25 @@ public class FrostFSClient : IFrostFSClient
|
||||||
return service.GetObjectAsync(args);
|
return service.GetObjectAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<RangeReader> GetRangeAsync(PrmRangeGet args)
|
||||||
|
{
|
||||||
|
if (args is null)
|
||||||
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
|
var service = GetObjectService(args);
|
||||||
|
return service.GetRangeAsync(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IEnumerable<ReadOnlyMemory<byte>>> GetRangeHashAsync(PrmRangeHashGet args)
|
||||||
|
{
|
||||||
|
if (args is null)
|
||||||
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
|
var service = GetObjectService(args);
|
||||||
|
return service.GetRangeHashAsync(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args)
|
public Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
|
@ -286,6 +322,17 @@ public class FrostFSClient : IFrostFSClient
|
||||||
return service.PutSingleObjectAsync(args);
|
return service.PutSingleObjectAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args)
|
||||||
|
{
|
||||||
|
if (args is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
var service = GetObjectService(args);
|
||||||
|
return service.PatchObjectAsync(args);
|
||||||
|
}
|
||||||
|
|
||||||
public Task DeleteObjectAsync(PrmObjectDelete args)
|
public Task DeleteObjectAsync(PrmObjectDelete args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
|
@ -305,16 +352,16 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region SessionImplementation
|
#region Session Implementation
|
||||||
public async Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args)
|
public async Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var session = await CreateSessionInternalAsync(args).ConfigureAwait(false);
|
var session = await CreateSessionInternalAsync(args).ConfigureAwait(false);
|
||||||
var token = session.Serialize();
|
|
||||||
|
|
||||||
return new FrostFsSessionToken(token);
|
var token = session.Serialize();
|
||||||
|
return new FrostFsSessionToken(token, session.Body.Id.ToUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task<SessionToken> CreateSessionInternalAsync(PrmSessionCreate args)
|
internal Task<SessionToken> CreateSessionInternalAsync(PrmSessionCreate args)
|
||||||
|
@ -327,8 +374,18 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Accounting Implementation
|
||||||
|
public async Task<Accounting.Decimal> GetBalanceAsync(PrmBalance? args)
|
||||||
|
{
|
||||||
|
args ??= new PrmBalance();
|
||||||
|
|
||||||
|
var service = GetAccouningService(args);
|
||||||
|
return await service.GetBallance(args).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region ToolsImplementation
|
#region ToolsImplementation
|
||||||
public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, Context ctx)
|
public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx)
|
||||||
{
|
{
|
||||||
if (header == null)
|
if (header == null)
|
||||||
throw new ArgumentNullException(nameof(header));
|
throw new ArgumentNullException(nameof(header));
|
||||||
|
@ -337,12 +394,12 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private async void CheckFrostFsVersionSupport(Context? ctx = default)
|
private async void CheckFrostFsVersionSupport(CallContext? ctx = default)
|
||||||
{
|
{
|
||||||
var args = new PrmNodeInfo { Context = ctx };
|
var args = new PrmNodeInfo(ctx);
|
||||||
|
|
||||||
if (ctx?.Version == null)
|
if (ctx?.Version == null)
|
||||||
throw new InvalidObjectException(nameof(ctx.Version));
|
throw new ArgumentNullException(nameof(ctx), "Version must be initialized");
|
||||||
|
|
||||||
var service = GetNetmapService(args);
|
var service = GetNetmapService(args);
|
||||||
var localNodeInfo = await service.GetLocalNodeInfoAsync(args).ConfigureAwait(false);
|
var localNodeInfo = await service.GetLocalNodeInfoAsync(args).ConfigureAwait(false);
|
||||||
|
@ -354,18 +411,16 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallInvoker? SetupEnvironment(IContext ctx)
|
private CallInvoker? SetupClientContext(IContext ctx)
|
||||||
{
|
{
|
||||||
if (isDisposed)
|
if (isDisposed)
|
||||||
throw new InvalidObjectException("Client is disposed.");
|
throw new FrostFsInvalidObjectException("Client is disposed.");
|
||||||
|
|
||||||
ctx.Context ??= new Context();
|
if (ctx.Context!.Key == null)
|
||||||
|
|
||||||
if (ctx.Context.Key == null)
|
|
||||||
{
|
{
|
||||||
if (ClientCtx.Key == null)
|
if (ClientCtx.Key == null)
|
||||||
{
|
{
|
||||||
throw new InvalidObjectException("Key is not initialized.");
|
throw new ArgumentNullException(nameof(ctx), "Key is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Context.Key = ClientCtx.Key.ECDsaKey;
|
ctx.Context.Key = ClientCtx.Key.ECDsaKey;
|
||||||
|
@ -380,24 +435,23 @@ public class FrostFSClient : IFrostFSClient
|
||||||
{
|
{
|
||||||
if (ClientCtx.Version == null)
|
if (ClientCtx.Version == null)
|
||||||
{
|
{
|
||||||
throw new InvalidObjectException("Version is not initialized.");
|
throw new ArgumentNullException(nameof(ctx), "Version is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Context.Version = ClientCtx.Version;
|
ctx.Context.Version = ClientCtx.Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
CallInvoker? callInvoker = null;
|
CallInvoker? callInvoker = null;
|
||||||
if (ctx.Context.Interceptors != null && ctx.Context.Interceptors.Count > 0)
|
|
||||||
{
|
foreach (var interceptor in ctx.Context.Interceptors)
|
||||||
foreach (var interceptor in ctx.Context.Interceptors)
|
callInvoker = AddInvoker(callInvoker, interceptor);
|
||||||
{
|
|
||||||
callInvoker = AddInvoker(callInvoker, interceptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.Context.Callback != null)
|
if (ctx.Context.Callback != null)
|
||||||
callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Context.Callback));
|
callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Context.Callback));
|
||||||
|
|
||||||
|
if (ctx.Context.PoolErrorHandler != null)
|
||||||
|
callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ctx.Context.PoolErrorHandler));
|
||||||
|
|
||||||
return callInvoker;
|
return callInvoker;
|
||||||
|
|
||||||
CallInvoker AddInvoker(CallInvoker? callInvoker, Interceptor interceptor)
|
CallInvoker AddInvoker(CallInvoker? callInvoker, Interceptor interceptor)
|
||||||
|
@ -405,7 +459,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (callInvoker == null)
|
if (callInvoker == null)
|
||||||
callInvoker = ClientCtx.Channel.Intercept(interceptor);
|
callInvoker = ClientCtx.Channel.Intercept(interceptor);
|
||||||
else
|
else
|
||||||
callInvoker.Intercept(interceptor);
|
callInvoker = callInvoker.Intercept(interceptor);
|
||||||
|
|
||||||
return callInvoker;
|
return callInvoker;
|
||||||
}
|
}
|
||||||
|
@ -413,7 +467,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
private NetmapServiceProvider GetNetmapService(IContext ctx)
|
private NetmapServiceProvider GetNetmapService(IContext ctx)
|
||||||
{
|
{
|
||||||
var callInvoker = SetupEnvironment(ctx);
|
var callInvoker = SetupClientContext(ctx);
|
||||||
var client = NetmapServiceClient ?? (callInvoker != null
|
var client = NetmapServiceClient ?? (callInvoker != null
|
||||||
? new NetmapService.NetmapServiceClient(callInvoker)
|
? new NetmapService.NetmapServiceClient(callInvoker)
|
||||||
: new NetmapService.NetmapServiceClient(ClientCtx.Channel));
|
: new NetmapService.NetmapServiceClient(ClientCtx.Channel));
|
||||||
|
@ -423,7 +477,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
private SessionServiceProvider GetSessionService(IContext ctx)
|
private SessionServiceProvider GetSessionService(IContext ctx)
|
||||||
{
|
{
|
||||||
var callInvoker = SetupEnvironment(ctx);
|
var callInvoker = SetupClientContext(ctx);
|
||||||
var client = SessionServiceClient ?? (callInvoker != null
|
var client = SessionServiceClient ?? (callInvoker != null
|
||||||
? new SessionService.SessionServiceClient(callInvoker)
|
? new SessionService.SessionServiceClient(callInvoker)
|
||||||
: new SessionService.SessionServiceClient(ClientCtx.Channel));
|
: new SessionService.SessionServiceClient(ClientCtx.Channel));
|
||||||
|
@ -433,7 +487,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
private ApeManagerServiceProvider GetApeManagerService(IContext ctx)
|
private ApeManagerServiceProvider GetApeManagerService(IContext ctx)
|
||||||
{
|
{
|
||||||
var callInvoker = SetupEnvironment(ctx);
|
var callInvoker = SetupClientContext(ctx);
|
||||||
var client = ApeManagerServiceClient ?? (callInvoker != null
|
var client = ApeManagerServiceClient ?? (callInvoker != null
|
||||||
? new APEManagerService.APEManagerServiceClient(callInvoker)
|
? new APEManagerService.APEManagerServiceClient(callInvoker)
|
||||||
: new APEManagerService.APEManagerServiceClient(ClientCtx.Channel));
|
: new APEManagerService.APEManagerServiceClient(ClientCtx.Channel));
|
||||||
|
@ -441,9 +495,19 @@ public class FrostFSClient : IFrostFSClient
|
||||||
return new ApeManagerServiceProvider(client, ClientCtx);
|
return new ApeManagerServiceProvider(client, ClientCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AccountingServiceProvider GetAccouningService(IContext ctx)
|
||||||
|
{
|
||||||
|
var callInvoker = SetupClientContext(ctx);
|
||||||
|
var client = AccountingServiceClient ?? (callInvoker != null
|
||||||
|
? new AccountingService.AccountingServiceClient(callInvoker)
|
||||||
|
: new AccountingService.AccountingServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
return new AccountingServiceProvider(client, ClientCtx);
|
||||||
|
}
|
||||||
|
|
||||||
private ContainerServiceProvider GetContainerService(IContext ctx)
|
private ContainerServiceProvider GetContainerService(IContext ctx)
|
||||||
{
|
{
|
||||||
var callInvoker = SetupEnvironment(ctx);
|
var callInvoker = SetupClientContext(ctx);
|
||||||
var client = ContainerServiceClient ?? (callInvoker != null
|
var client = ContainerServiceClient ?? (callInvoker != null
|
||||||
? new ContainerService.ContainerServiceClient(callInvoker)
|
? new ContainerService.ContainerServiceClient(callInvoker)
|
||||||
: new ContainerService.ContainerServiceClient(ClientCtx.Channel));
|
: new ContainerService.ContainerServiceClient(ClientCtx.Channel));
|
||||||
|
@ -453,7 +517,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
private ObjectServiceProvider GetObjectService(IContext ctx)
|
private ObjectServiceProvider GetObjectService(IContext ctx)
|
||||||
{
|
{
|
||||||
var callInvoker = SetupEnvironment(ctx);
|
var callInvoker = SetupClientContext(ctx);
|
||||||
var client = ObjectServiceClient ?? (callInvoker != null
|
var client = ObjectServiceClient ?? (callInvoker != null
|
||||||
? new ObjectService.ObjectServiceClient(callInvoker)
|
? new ObjectService.ObjectServiceClient(callInvoker)
|
||||||
: new ObjectService.ObjectServiceClient(ClientCtx.Channel));
|
: new ObjectService.ObjectServiceClient(ClientCtx.Channel));
|
||||||
|
@ -461,6 +525,16 @@ public class FrostFSClient : IFrostFSClient
|
||||||
return new ObjectServiceProvider(client, ClientCtx);
|
return new ObjectServiceProvider(client, ClientCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AccountingServiceProvider GetAccountService(IContext ctx)
|
||||||
|
{
|
||||||
|
var callInvoker = SetupClientContext(ctx);
|
||||||
|
var client = AccountingServiceClient ?? (callInvoker != null
|
||||||
|
? new AccountingService.AccountingServiceClient(callInvoker)
|
||||||
|
: new AccountingService.AccountingServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
return new AccountingServiceProvider(client, ClientCtx);
|
||||||
|
}
|
||||||
|
|
||||||
private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
|
private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -480,4 +554,24 @@ public class FrostFSClient : IFrostFSClient
|
||||||
throw new ArgumentException($"Host '{host}' has invalid format. Error: {e.Message}");
|
throw new ArgumentException($"Host '{host}' has invalid format. Error: {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string?> Dial(CallContext ctx)
|
||||||
|
{
|
||||||
|
var prm = new PrmBalance(ctx);
|
||||||
|
|
||||||
|
var service = GetAccouningService(prm);
|
||||||
|
_ = await service.GetBallance(prm).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RestartIfUnhealthy(CallContext ctx)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
8
src/FrostFS.SDK.Client/GlobalSuppressions.cs
Normal file
8
src/FrostFS.SDK.Client/GlobalSuppressions.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// This file is used by Code Analysis to maintain SuppressMessage
|
||||||
|
// attributes that are applied to this project.
|
||||||
|
// Project-level suppressions either have no target or are given
|
||||||
|
// a specific target and scoped to a namespace, type, member, etc.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
[assembly: SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "<Pending>", Scope = "member", Target = "~M:FrostFS.SDK.Client.Sampler.Next~System.Int32")]
|
68
src/FrostFS.SDK.Client/Interceptors/ErrorInterceptor.cs
Normal file
68
src/FrostFS.SDK.Client/Interceptors/ErrorInterceptor.cs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Grpc.Core;
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods",
|
||||||
|
Justification = "parameters are provided by GRPC infrastructure")]
|
||||||
|
public class ErrorInterceptor(Action<Exception> handler) : Interceptor
|
||||||
|
{
|
||||||
|
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
||||||
|
TRequest request,
|
||||||
|
ClientInterceptorContext<TRequest, TResponse> context,
|
||||||
|
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
||||||
|
{
|
||||||
|
var call = continuation(request, context);
|
||||||
|
|
||||||
|
return new AsyncUnaryCall<TResponse>(
|
||||||
|
HandleUnaryResponse(call),
|
||||||
|
call.ResponseHeadersAsync,
|
||||||
|
call.GetStatus,
|
||||||
|
call.GetTrailers,
|
||||||
|
call.Dispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(
|
||||||
|
ClientInterceptorContext<TRequest, TResponse> context,
|
||||||
|
AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
|
||||||
|
{
|
||||||
|
var call = continuation(context);
|
||||||
|
|
||||||
|
return new AsyncClientStreamingCall<TRequest, TResponse>(
|
||||||
|
call.RequestStream,
|
||||||
|
HandleStreamResponse(call),
|
||||||
|
call.ResponseHeadersAsync,
|
||||||
|
call.GetStatus,
|
||||||
|
call.GetTrailers,
|
||||||
|
call.Dispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await call;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
handler(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<TResponse> HandleStreamResponse<TRequest, TResponse>(AsyncClientStreamingCall<TRequest, TResponse> call)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await call;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
handler(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,8 +5,10 @@ using System.Threading.Tasks;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Grpc.Core.Interceptors;
|
using Grpc.Core.Interceptors;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods",
|
||||||
|
Justification = "parameters are provided by GRPC infrastructure")]
|
||||||
public class MetricsInterceptor(Action<CallStatistics> callback) : Interceptor
|
public class MetricsInterceptor(Action<CallStatistics> callback) : Interceptor
|
||||||
{
|
{
|
||||||
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
||||||
|
@ -14,11 +16,6 @@ public class MetricsInterceptor(Action<CallStatistics> callback) : Interceptor
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
ClientInterceptorContext<TRequest, TResponse> context,
|
||||||
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
||||||
{
|
{
|
||||||
if (continuation is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(continuation));
|
|
||||||
}
|
|
||||||
|
|
||||||
var call = continuation(request, context);
|
var call = continuation(request, context);
|
||||||
|
|
||||||
return new AsyncUnaryCall<TResponse>(
|
return new AsyncUnaryCall<TResponse>(
|
||||||
|
@ -33,9 +30,6 @@ public class MetricsInterceptor(Action<CallStatistics> callback) : Interceptor
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
ClientInterceptorContext<TRequest, TResponse> context,
|
||||||
AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
|
AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
|
||||||
{
|
{
|
||||||
if (continuation is null)
|
|
||||||
throw new ArgumentNullException(nameof(continuation));
|
|
||||||
|
|
||||||
var call = continuation(context);
|
var call = continuation(context);
|
||||||
|
|
||||||
return new AsyncClientStreamingCall<TRequest, TResponse>(
|
return new AsyncClientStreamingCall<TRequest, TResponse>(
|
||||||
|
@ -52,7 +46,7 @@ public class MetricsInterceptor(Action<CallStatistics> callback) : Interceptor
|
||||||
var watch = new Stopwatch();
|
var watch = new Stopwatch();
|
||||||
watch.Start();
|
watch.Start();
|
||||||
|
|
||||||
var response = await call.ResponseAsync.ConfigureAwait(false);
|
var response = await call;
|
||||||
|
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
|
|
||||||
|
@ -68,7 +62,7 @@ public class MetricsInterceptor(Action<CallStatistics> callback) : Interceptor
|
||||||
var watch = new Stopwatch();
|
var watch = new Stopwatch();
|
||||||
watch.Start();
|
watch.Start();
|
||||||
|
|
||||||
var response = await call.ResponseAsync.ConfigureAwait(false);
|
var response = await call;
|
||||||
|
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
|
|
|
@ -3,7 +3,8 @@ using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Frostfs.V2.Ape;
|
using Frostfs.V2.Ape;
|
||||||
namespace FrostFS.SDK.ClientV2.Interfaces;
|
|
||||||
|
namespace FrostFS.SDK.Client.Interfaces;
|
||||||
|
|
||||||
public interface IFrostFSClient : IDisposable
|
public interface IFrostFSClient : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -19,8 +20,8 @@ public interface IFrostFSClient : IDisposable
|
||||||
Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args);
|
Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ApeMAnager
|
#region ApeManager
|
||||||
Task<byte[]> AddChainAsync(PrmApeChainAdd args);
|
Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args);
|
||||||
|
|
||||||
Task RemoveChainAsync(PrmApeChainRemove args);
|
Task RemoveChainAsync(PrmApeChainRemove args);
|
||||||
|
|
||||||
|
@ -42,16 +43,30 @@ public interface IFrostFSClient : IDisposable
|
||||||
|
|
||||||
Task<FrostFsObject> GetObjectAsync(PrmObjectGet args);
|
Task<FrostFsObject> GetObjectAsync(PrmObjectGet args);
|
||||||
|
|
||||||
|
Task<RangeReader> GetRangeAsync(PrmRangeGet args);
|
||||||
|
|
||||||
|
Task<IEnumerable<ReadOnlyMemory<byte>>> GetRangeHashAsync(PrmRangeHashGet args);
|
||||||
|
|
||||||
Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args);
|
Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args);
|
||||||
|
|
||||||
Task<FrostFsObjectId> PutSingleObjectAsync(PrmSingleObjectPut args);
|
Task<FrostFsObjectId> PutSingleObjectAsync(PrmSingleObjectPut args);
|
||||||
|
|
||||||
|
Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args);
|
||||||
|
|
||||||
Task DeleteObjectAsync(PrmObjectDelete args);
|
Task DeleteObjectAsync(PrmObjectDelete args);
|
||||||
|
|
||||||
IAsyncEnumerable<FrostFsObjectId> SearchObjectsAsync(PrmObjectSearch args);
|
IAsyncEnumerable<FrostFsObjectId> SearchObjectsAsync(PrmObjectSearch args);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Tools
|
#region Account
|
||||||
FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, Context ctx);
|
Task<Accounting.Decimal> GetBalanceAsync(PrmBalance? args = null);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Tools
|
||||||
|
FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public Task<string?> Dial(CallContext ctx);
|
||||||
|
|
||||||
|
public void Close();
|
||||||
}
|
}
|
24
src/FrostFS.SDK.Client/Logging/FrostFsMessages.cs
Normal file
24
src/FrostFS.SDK.Client/Logging/FrostFsMessages.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
internal static partial class FrostFsMessages
|
||||||
|
{
|
||||||
|
[LoggerMessage(100,
|
||||||
|
LogLevel.Warning,
|
||||||
|
"Failed to create frostfs session token for client. Address {address}, {error}",
|
||||||
|
EventName = nameof(SessionCreationError))]
|
||||||
|
internal static partial void SessionCreationError(ILogger logger, string address, string error);
|
||||||
|
|
||||||
|
[LoggerMessage(101,
|
||||||
|
LogLevel.Warning,
|
||||||
|
"Error threshold reached. Address {address}, threshold {threshold}",
|
||||||
|
EventName = nameof(ErrorЕhresholdReached))]
|
||||||
|
internal static partial void ErrorЕhresholdReached(ILogger logger, string address, uint threshold);
|
||||||
|
|
||||||
|
[LoggerMessage(102,
|
||||||
|
LogLevel.Warning,
|
||||||
|
"Health has changed: {address} healthy {healthy}, reason {error}",
|
||||||
|
EventName = nameof(HealthChanged))]
|
||||||
|
internal static partial void HealthChanged(ILogger logger, string address, bool healthy, string error);
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ using System.Linq;
|
||||||
|
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class ContainerMapper
|
public static class ContainerMapper
|
||||||
{
|
{
|
|
@ -7,7 +7,7 @@ using Google.Protobuf;
|
||||||
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class ContainerIdMapper
|
public static class ContainerIdMapper
|
||||||
{
|
{
|
||||||
|
@ -24,16 +24,25 @@ public static class ContainerIdMapper
|
||||||
|
|
||||||
var containerId = model.GetValue() ?? throw new ArgumentNullException(nameof(model));
|
var containerId = model.GetValue() ?? throw new ArgumentNullException(nameof(model));
|
||||||
|
|
||||||
if (!Cache.Containers.TryGetValue(containerId, out ContainerID? message))
|
if (!Caches.Containers.TryGetValue(containerId, out ContainerID? message))
|
||||||
{
|
{
|
||||||
message = new ContainerID
|
message = new ContainerID
|
||||||
{
|
{
|
||||||
Value = ByteString.CopyFrom(Base58.Decode(containerId))
|
Value = ByteString.CopyFrom(Base58.Decode(containerId))
|
||||||
};
|
};
|
||||||
|
|
||||||
Cache.Containers.Set(containerId, message, _oneHourExpiration);
|
Caches.Containers.Set(containerId, message, _oneHourExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
return message!;
|
return message!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FrostFsContainerId ToModel(this ContainerID message)
|
||||||
|
{
|
||||||
|
if (message is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FrostFsContainerId(Base58.Encode(message.Value.Span));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
using FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class MetaHeaderMapper
|
public static class MetaHeaderMapper
|
||||||
{
|
{
|
|
@ -3,7 +3,7 @@ using System.Linq;
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public static class NetmapMapper
|
public static class NetmapMapper
|
||||||
{
|
{
|
|
@ -2,9 +2,9 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public static class NodeInfoMapper
|
public static class NodeInfoMapper
|
||||||
{
|
{
|
||||||
|
@ -39,7 +39,7 @@ public static class NodeInfoMapper
|
||||||
state: state,
|
state: state,
|
||||||
addresses: [.. nodeInfo.Addresses],
|
addresses: [.. nodeInfo.Addresses],
|
||||||
attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
|
attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
|
||||||
publicKey: nodeInfo.PublicKey.ToByteArray()
|
publicKey: nodeInfo.PublicKey.Memory
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ using System.Linq;
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public static class PlacementPolicyMapper
|
public static class PlacementPolicyMapper
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public static class ReplicaMapper
|
public static class ReplicaMapper
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
internal static class ObjectMapper
|
internal static class ObjectMapper
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,7 @@ internal static class ObjectMapper
|
||||||
{
|
{
|
||||||
return new FrostFsObject(obj.Header.ToModel())
|
return new FrostFsObject(obj.Header.ToModel())
|
||||||
{
|
{
|
||||||
ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.ToByteArray())
|
ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.Span)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class ObjectAttributeMapper
|
public static class ObjectAttributeMapper
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class ObjectFilterMapper
|
public static class ObjectFilterMapper
|
||||||
{
|
{
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class ObjectHeaderMapper
|
public static class ObjectHeaderMapper
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,7 @@ public static class ObjectHeaderMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
var model = new FrostFsObjectHeader(
|
var model = new FrostFsObjectHeader(
|
||||||
new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.ToByteArray())),
|
new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.Span)),
|
||||||
objTypeName,
|
objTypeName,
|
||||||
header.Attributes.Select(attribute => attribute.ToModel()).ToArray(),
|
header.Attributes.Select(attribute => attribute.ToModel()).ToArray(),
|
||||||
split,
|
split,
|
|
@ -4,7 +4,7 @@ using FrostFS.Refs;
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class ObjectIdMapper
|
public static class ObjectIdMapper
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,6 @@ public static class ObjectIdMapper
|
||||||
throw new ArgumentNullException(nameof(objectId));
|
throw new ArgumentNullException(nameof(objectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return FrostFsObjectId.FromHash(objectId.Value.ToByteArray());
|
return FrostFsObjectId.FromHash(objectId.Value.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ using Google.Protobuf;
|
||||||
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class OwnerIdMapper
|
public static class OwnerIdMapper
|
||||||
{
|
{
|
||||||
|
@ -22,14 +22,14 @@ public static class OwnerIdMapper
|
||||||
throw new ArgumentNullException(nameof(model));
|
throw new ArgumentNullException(nameof(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Cache.Owners.TryGetValue(model, out OwnerID? message))
|
if (!Caches.Owners.TryGetValue(model, out OwnerID? message))
|
||||||
{
|
{
|
||||||
message = new OwnerID
|
message = new OwnerID
|
||||||
{
|
{
|
||||||
Value = ByteString.CopyFrom(model.ToHash())
|
Value = ByteString.CopyFrom(model.ToHash())
|
||||||
};
|
};
|
||||||
|
|
||||||
Cache.Owners.Set(model, message, _oneHourExpiration);
|
Caches.Owners.Set(model, message, _oneHourExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
return message!;
|
return message!;
|
||||||
|
@ -42,11 +42,11 @@ public static class OwnerIdMapper
|
||||||
throw new ArgumentNullException(nameof(message));
|
throw new ArgumentNullException(nameof(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Cache.Owners.TryGetValue(message, out FrostFsOwner? model))
|
if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model))
|
||||||
{
|
{
|
||||||
model = new FrostFsOwner(Base58.Encode(message.Value.ToByteArray()));
|
model = new FrostFsOwner(Base58.Encode(message.Value.Span));
|
||||||
|
|
||||||
Cache.Owners.Set(message, model, _oneHourExpiration);
|
Caches.Owners.Set(message, model, _oneHourExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
return model!;
|
return model!;
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public static class SessionMapper
|
public static class SessionMapper
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class SignatureMapper
|
public static class SignatureMapper
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class StatusMapper
|
public static class StatusMapper
|
||||||
{
|
{
|
|
@ -3,7 +3,7 @@ using System.Threading;
|
||||||
|
|
||||||
using FrostFS.Refs;
|
using FrostFS.Refs;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
|
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
public static class VersionMapper
|
public static class VersionMapper
|
||||||
{
|
{
|
62
src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs
Normal file
62
src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using Frostfs.V2.Ape;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public struct FrostFsChainTarget(FrostFsTargetType type, string name) : IEquatable<FrostFsChainTarget>
|
||||||
|
{
|
||||||
|
private ChainTarget? chainTarget;
|
||||||
|
|
||||||
|
public FrostFsTargetType Type { get; } = type;
|
||||||
|
|
||||||
|
public string Name { get; } = name;
|
||||||
|
|
||||||
|
internal ChainTarget GetChainTarget()
|
||||||
|
{
|
||||||
|
return chainTarget ??= new ChainTarget
|
||||||
|
{
|
||||||
|
Type = GetTargetType(Type),
|
||||||
|
Name = Name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TargetType GetTargetType(FrostFsTargetType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
FrostFsTargetType.Undefined => TargetType.Undefined,
|
||||||
|
FrostFsTargetType.Namespace => TargetType.Namespace,
|
||||||
|
FrostFsTargetType.Container => TargetType.Container,
|
||||||
|
FrostFsTargetType.User => TargetType.User,
|
||||||
|
FrostFsTargetType.Group => TargetType.Group,
|
||||||
|
_ => throw new ArgumentException("Unexpected value for TargetType", nameof(type)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly bool Equals(object obj)
|
||||||
|
{
|
||||||
|
var target = (FrostFsChainTarget)obj;
|
||||||
|
return Equals(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly int GetHashCode()
|
||||||
|
{
|
||||||
|
return $"{Name}{Type}".GetHashCode(StringComparison.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(FrostFsChainTarget left, FrostFsChainTarget right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(FrostFsChainTarget left, FrostFsChainTarget right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(FrostFsChainTarget other)
|
||||||
|
{
|
||||||
|
return Type == other.Type && Name.Equals(other.Name, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
}
|
41
src/FrostFS.SDK.Client/Models/Chain/FrostFsChain.cs
Normal file
41
src/FrostFS.SDK.Client/Models/Chain/FrostFsChain.cs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public struct FrostFsChain(byte[] raw) : System.IEquatable<FrostFsChain>
|
||||||
|
{
|
||||||
|
private ByteString? grpcRaw;
|
||||||
|
|
||||||
|
public byte[] Raw { get; } = raw;
|
||||||
|
|
||||||
|
internal ByteString GetRaw()
|
||||||
|
{
|
||||||
|
return grpcRaw ??= ByteString.CopyFrom(Raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly bool Equals(object obj)
|
||||||
|
{
|
||||||
|
var chain = (FrostFsChain)obj;
|
||||||
|
return Equals(chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly int GetHashCode()
|
||||||
|
{
|
||||||
|
return Raw.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(FrostFsChain left, FrostFsChain right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(FrostFsChain left, FrostFsChain right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(FrostFsChain other)
|
||||||
|
{
|
||||||
|
return Raw == other.Raw;
|
||||||
|
}
|
||||||
|
}
|
10
src/FrostFS.SDK.Client/Models/Chain/FrostFsTargetType.cs
Normal file
10
src/FrostFS.SDK.Client/Models/Chain/FrostFsTargetType.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public enum FrostFsTargetType
|
||||||
|
{
|
||||||
|
Undefined = 0,
|
||||||
|
Namespace,
|
||||||
|
Container,
|
||||||
|
User,
|
||||||
|
Group
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ public class ClientSettings
|
||||||
{
|
{
|
||||||
var errors = CheckFields();
|
var errors = CheckFields();
|
||||||
if (errors != null)
|
if (errors != null)
|
||||||
ThrowException(errors);
|
ThrowSettingsException(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Collection<string>? CheckFields()
|
protected Collection<string>? CheckFields()
|
||||||
|
@ -29,7 +29,7 @@ public class ClientSettings
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void ThrowException(Collection<string> errors)
|
protected static void ThrowSettingsException(Collection<string> errors)
|
||||||
{
|
{
|
||||||
if (errors is null)
|
if (errors is null)
|
||||||
{
|
{
|
||||||
|
@ -55,7 +55,7 @@ public class SingleOwnerClientSettings : ClientSettings
|
||||||
{
|
{
|
||||||
var errors = CheckFields();
|
var errors = CheckFields();
|
||||||
if (errors != null)
|
if (errors != null)
|
||||||
ThrowException(errors);
|
ThrowSettingsException(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected new Collection<string>? CheckFields()
|
protected new Collection<string>? CheckFields()
|
|
@ -1,6 +1,6 @@
|
||||||
using FrostFS.Refs;
|
using FrostFS.Refs;
|
||||||
using FrostFS.SDK.ClientV2;
|
using FrostFS.SDK.Client;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
@ -27,11 +27,11 @@ public class FrostFsContainerId
|
||||||
|
|
||||||
if (containerID != null)
|
if (containerID != null)
|
||||||
{
|
{
|
||||||
this.modelId = Base58.Encode(containerID.Value.ToByteArray());
|
this.modelId = Base58.Encode(containerID.Value.Span);
|
||||||
return this.modelId;
|
return this.modelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidObjectException();
|
throw new FrostFsInvalidObjectException();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ContainerID ContainerID
|
internal ContainerID ContainerID
|
||||||
|
@ -47,7 +47,7 @@ public class FrostFsContainerId
|
||||||
return this.containerID;
|
return this.containerID;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidObjectException();
|
throw new FrostFsInvalidObjectException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using FrostFS.SDK.ClientV2;
|
using FrostFS.SDK.Client;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
@ -88,7 +88,7 @@ public class FrostFsContainerInfo
|
||||||
{
|
{
|
||||||
if (PlacementPolicy == null)
|
if (PlacementPolicy == null)
|
||||||
{
|
{
|
||||||
throw new InvalidObjectException("PlacementPolicy is null");
|
throw new ArgumentNullException("PlacementPolicy is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container = new Container.Container()
|
this.container = new Container.Container()
|
|
@ -16,6 +16,6 @@ public class CheckSum
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return text ??= BitConverter.ToString(hash).Replace("-", "");
|
return text ??= BitConverter.ToString(hash).Replace("-", "", StringComparison.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
using FrostFS.SDK.ClientV2;
|
using FrostFS.SDK.Client;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
|
|
@ -27,7 +27,7 @@ public struct FrostFsReplica : IEquatable<FrostFsReplica>
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
public override readonly int GetHashCode()
|
||||||
{
|
{
|
||||||
return Count + Selector.GetHashCode();
|
return Count + Selector.GetHashCode(StringComparison.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(FrostFsReplica left, FrostFsReplica right)
|
public static bool operator ==(FrostFsReplica left, FrostFsReplica right)
|
|
@ -1,5 +1,5 @@
|
||||||
using FrostFS.Refs;
|
using FrostFS.Refs;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
|
48
src/FrostFS.SDK.Client/Models/Object/FrostFsAddress.cs
Normal file
48
src/FrostFS.SDK.Client/Models/Object/FrostFsAddress.cs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
using FrostFS.Refs;
|
||||||
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK;
|
||||||
|
|
||||||
|
public class FrostFsAddress
|
||||||
|
{
|
||||||
|
private FrostFsObjectId? frostFsObjectId;
|
||||||
|
private FrostFsContainerId? frostFsContainerId;
|
||||||
|
private ObjectID? objectId;
|
||||||
|
private ContainerID? containerId;
|
||||||
|
|
||||||
|
public FrostFsAddress(FrostFsContainerId frostFsContainerId, FrostFsObjectId frostFsObjectId)
|
||||||
|
{
|
||||||
|
FrostFsObjectId = frostFsObjectId ?? throw new System.ArgumentNullException(nameof(frostFsObjectId));
|
||||||
|
FrostFsContainerId = frostFsContainerId ?? throw new System.ArgumentNullException(nameof(frostFsContainerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FrostFsAddress(ObjectID objectId, ContainerID containerId)
|
||||||
|
{
|
||||||
|
ObjectId = objectId ?? throw new System.ArgumentNullException(nameof(objectId));
|
||||||
|
ContainerId = containerId ?? throw new System.ArgumentNullException(nameof(containerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsObjectId FrostFsObjectId
|
||||||
|
{
|
||||||
|
get => frostFsObjectId ??= objectId!.ToModel();
|
||||||
|
set => frostFsObjectId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrostFsContainerId FrostFsContainerId
|
||||||
|
{
|
||||||
|
get => frostFsContainerId ??= containerId!.ToModel();
|
||||||
|
set => frostFsContainerId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectID ObjectId
|
||||||
|
{
|
||||||
|
get => objectId ??= frostFsObjectId!.ToMessage();
|
||||||
|
set => objectId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerID ContainerId
|
||||||
|
{
|
||||||
|
get => containerId ??= frostFsContainerId!.ToMessage();
|
||||||
|
set => containerId = value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using FrostFS.SDK.ClientV2;
|
using System;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ public class FrostFsObject
|
||||||
public void SetParent(FrostFsObjectHeader largeObjectHeader)
|
public void SetParent(FrostFsObjectHeader largeObjectHeader)
|
||||||
{
|
{
|
||||||
if (Header?.Split == null)
|
if (Header?.Split == null)
|
||||||
throw new InvalidObjectException("The object is not initialized properly");
|
throw new ArgumentNullException(nameof(largeObjectHeader), "Split value must not be null");
|
||||||
|
|
||||||
Header.Split.ParentHeader = largeObjectHeader;
|
Header.Split.ParentHeader = largeObjectHeader;
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
||||||
|
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
|
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
|
|
@ -8,13 +8,8 @@ public class FrostFsObjectId(string id)
|
||||||
{
|
{
|
||||||
public string Value { get; } = id;
|
public string Value { get; } = id;
|
||||||
|
|
||||||
public static FrostFsObjectId FromHash(byte[] hash)
|
public static FrostFsObjectId FromHash(ReadOnlySpan<byte> hash)
|
||||||
{
|
{
|
||||||
if (hash is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hash.Length != Constants.Sha256HashLength)
|
if (hash.Length != Constants.Sha256HashLength)
|
||||||
throw new FormatException("ObjectID must be a sha256 hash.");
|
throw new FormatException("ObjectID must be a sha256 hash.");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
using FrostFS.Refs;
|
using FrostFS.Refs;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
29
src/FrostFS.SDK.Client/Models/Object/FrostFsRange.cs
Normal file
29
src/FrostFS.SDK.Client/Models/Object/FrostFsRange.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK;
|
||||||
|
|
||||||
|
public readonly struct FrostFsRange(ulong offset, ulong length) : System.IEquatable<FrostFsRange>
|
||||||
|
{
|
||||||
|
public ulong Offset { get; } = offset;
|
||||||
|
|
||||||
|
public ulong Length { get; } = length;
|
||||||
|
|
||||||
|
public override readonly bool Equals(object obj) => this == (FrostFsRange)obj;
|
||||||
|
|
||||||
|
public override readonly int GetHashCode() => $"{Offset}{Length}".GetHashCode(StringComparison.InvariantCulture);
|
||||||
|
|
||||||
|
public static bool operator ==(FrostFsRange left, FrostFsRange right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(FrostFsRange left, FrostFsRange right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(FrostFsRange other)
|
||||||
|
{
|
||||||
|
return this == other;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,5 +6,5 @@ namespace FrostFS.SDK;
|
||||||
|
|
||||||
public interface IObjectReader : IDisposable
|
public interface IObjectReader : IDisposable
|
||||||
{
|
{
|
||||||
Task<ReadOnlyMemory<byte>?> ReadChunk(CancellationToken cancellationToken = default);
|
ValueTask<ReadOnlyMemory<byte>?> ReadChunk(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK;
|
||||||
|
|
||||||
|
public class FrostFsSessionToken(byte[] token, Guid id)
|
||||||
|
{
|
||||||
|
public Guid Id { get; private set; } = id;
|
||||||
|
public byte[] Token { get; private set; } = token;
|
||||||
|
}
|
|
@ -9,14 +9,14 @@ using Google.Protobuf;
|
||||||
|
|
||||||
using Grpc.Core.Interceptors;
|
using Grpc.Core.Interceptors;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class Context()
|
public class CallContext()
|
||||||
{
|
{
|
||||||
private ReadOnlyCollection<Interceptor>? interceptors;
|
|
||||||
|
|
||||||
private ByteString? publicKeyCache;
|
private ByteString? publicKeyCache;
|
||||||
|
|
||||||
|
internal Action<Exception>? PoolErrorHandler { get; set; }
|
||||||
|
|
||||||
public ECDsa? Key { get; set; }
|
public ECDsa? Key { get; set; }
|
||||||
|
|
||||||
public FrostFsOwner? OwnerId { get; set; }
|
public FrostFsOwner? OwnerId { get; set; }
|
||||||
|
@ -31,11 +31,7 @@ public class Context()
|
||||||
|
|
||||||
public Action<CallStatistics>? Callback { get; set; }
|
public Action<CallStatistics>? Callback { get; set; }
|
||||||
|
|
||||||
public ReadOnlyCollection<Interceptor>? Interceptors
|
public Collection<Interceptor> Interceptors { get; } = [];
|
||||||
{
|
|
||||||
get { return this.interceptors; }
|
|
||||||
set { this.interceptors = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteString? GetPublicKeyCache()
|
public ByteString? GetPublicKeyCache()
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class Credentials(ECDsa key, FrostFsOwner ownerId)
|
public class Credentials(ECDsa key, FrostFsOwner ownerId)
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public interface IContext
|
public interface IContext
|
||||||
{
|
{
|
||||||
|
@ -7,5 +7,5 @@ public interface IContext
|
||||||
/// callbacks, interceptors.
|
/// callbacks, interceptors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>Additional parameters for calling the method</value>
|
/// <value>Additional parameters for calling the method</value>
|
||||||
Context? Context { get; set; }
|
CallContext? Context { get; }
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public interface ISessionToken
|
public interface ISessionToken
|
||||||
{
|
{
|
6
src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs
Normal file
6
src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmApeChainList(FrostFsChainTarget target, CallContext? ctx = null) : PrmBase(ctx)
|
||||||
|
{
|
||||||
|
public FrostFsChainTarget Target { get; } = target;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmApeChainRemove(FrostFsChainTarget target, FrostFsChain chain) : PrmBase
|
public sealed class PrmApeChainRemove(FrostFsChainTarget target, FrostFsChain chain, CallContext? ctx = null) : PrmBase(ctx)
|
||||||
{
|
{
|
||||||
public FrostFsChainTarget Target { get; } = target;
|
public FrostFsChainTarget Target { get; } = target;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmApeChainAdd(FrostFsChainTarget target, FrostFsChain chain) : PrmBase
|
public sealed class PrmApeChainAdd(FrostFsChainTarget target, FrostFsChain chain, CallContext? ctx = null) : PrmBase(ctx)
|
||||||
{
|
{
|
||||||
public FrostFsChainTarget Target { get; } = target;
|
public FrostFsChainTarget Target { get; } = target;
|
||||||
|
|
5
src/FrostFS.SDK.Client/Parameters/PrmBalance.cs
Normal file
5
src/FrostFS.SDK.Client/Parameters/PrmBalance.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmBalance(CallContext? ctx = null) : PrmBase(ctx)
|
||||||
|
{
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class PrmBase(NameValueCollection? xheaders = null) : IContext
|
public class PrmBase(CallContext? ctx, NameValueCollection? xheaders = null) : IContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// FrostFS request X-Headers
|
/// FrostFS request X-Headers
|
||||||
|
@ -10,5 +10,5 @@ public class PrmBase(NameValueCollection? xheaders = null) : IContext
|
||||||
public NameValueCollection XHeaders { get; } = xheaders ?? [];
|
public NameValueCollection XHeaders { get; } = xheaders ?? [];
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Context? Context { get; set; }
|
public CallContext Context { get; } = ctx ?? new CallContext();
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmContainerCreate(FrostFsContainerInfo container) : PrmBase, ISessionToken
|
public sealed class PrmContainerCreate(FrostFsContainerInfo container, CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
public FrostFsContainerInfo Container { get; set; } = container;
|
public FrostFsContainerInfo Container { get; set; } = container;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmContainerDelete(FrostFsContainerId containerId) : PrmBase, ISessionToken
|
public sealed class PrmContainerDelete(FrostFsContainerId containerId, CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmContainerGet(FrostFsContainerId container) : PrmBase
|
public sealed class PrmContainerGet(FrostFsContainerId container, CallContext? ctx = null) : PrmBase(ctx)
|
||||||
{
|
{
|
||||||
public FrostFsContainerId Container { get; set; } = container;
|
public FrostFsContainerId Container { get; set; } = container;
|
||||||
}
|
}
|
5
src/FrostFS.SDK.Client/Parameters/PrmContainerGetAll.cs
Normal file
5
src/FrostFS.SDK.Client/Parameters/PrmContainerGetAll.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmContainerGetAll(CallContext? ctx = null) : PrmBase(ctx)
|
||||||
|
{
|
||||||
|
}
|
5
src/FrostFS.SDK.Client/Parameters/PrmNetmapSnapshot.cs
Normal file
5
src/FrostFS.SDK.Client/Parameters/PrmNetmapSnapshot.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmNetmapSnapshot(CallContext? ctx = null) : PrmBase(ctx)
|
||||||
|
{
|
||||||
|
}
|
5
src/FrostFS.SDK.Client/Parameters/PrmNetworkSettings.cs
Normal file
5
src/FrostFS.SDK.Client/Parameters/PrmNetworkSettings.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmNetworkSettings(CallContext? ctx = null) : PrmBase(ctx)
|
||||||
|
{
|
||||||
|
}
|
5
src/FrostFS.SDK.Client/Parameters/PrmNodeInfo.cs
Normal file
5
src/FrostFS.SDK.Client/Parameters/PrmNodeInfo.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmNodeInfo(CallContext? ctx = null) : PrmBase(ctx)
|
||||||
|
{
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmObjectDelete(FrostFsContainerId containerId, FrostFsObjectId objectId) : PrmBase, ISessionToken
|
public sealed class PrmObjectDelete(FrostFsContainerId containerId, FrostFsObjectId objectId, CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmObjectGet(FrostFsContainerId containerId, FrostFsObjectId objectId) : PrmBase, ISessionToken
|
public sealed class PrmObjectGet(FrostFsContainerId containerId, FrostFsObjectId objectId, CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmObjectHeadGet(FrostFsContainerId containerId, FrostFsObjectId objectId) : PrmBase, ISessionToken
|
public sealed class PrmObjectHeadGet(FrostFsContainerId containerId, FrostFsObjectId objectId, CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
public FrostFsContainerId ContainerId { get; set; } = containerId;
|
||||||
|
|
24
src/FrostFS.SDK.Client/Parameters/PrmObjectPatch.cs
Normal file
24
src/FrostFS.SDK.Client/Parameters/PrmObjectPatch.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmObjectPatch(FrostFsAddress address, CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
|
{
|
||||||
|
public FrostFsAddress Address { get; } = address;
|
||||||
|
|
||||||
|
public FrostFsRange Range { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A stream with source data
|
||||||
|
/// </summary>
|
||||||
|
public Stream? Payload { get; set; }
|
||||||
|
|
||||||
|
public FrostFsAttributePair[]? NewAttributes { get; set; }
|
||||||
|
|
||||||
|
public bool ReplaceAttributes { get; set; }
|
||||||
|
|
||||||
|
public int MaxPayloadPatchChunkLength { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public FrostFsSessionToken? SessionToken { get; set; }
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmObjectPut : PrmBase, ISessionToken
|
public sealed class PrmObjectPut(CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Need to provide values like <c>ContainerId</c> and <c>ObjectType</c> to create and object.
|
/// Need to provide values like <c>ContainerId</c> and <c>ObjectType</c> to create and object.
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmObjectSearch(FrostFsContainerId containerId, params IObjectFilter[] filters) : PrmBase, ISessionToken
|
public sealed class PrmObjectSearch(FrostFsContainerId containerId, CallContext? ctx = null, params IObjectFilter[] filters) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines container for the search
|
/// Defines container for the search
|
20
src/FrostFS.SDK.Client/Parameters/PrmRangeGet.cs
Normal file
20
src/FrostFS.SDK.Client/Parameters/PrmRangeGet.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmRangeGet(
|
||||||
|
FrostFsContainerId containerId,
|
||||||
|
FrostFsObjectId objectId,
|
||||||
|
FrostFsRange range,
|
||||||
|
bool raw = false,
|
||||||
|
CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
|
{
|
||||||
|
public FrostFsContainerId ContainerId { get; } = containerId;
|
||||||
|
|
||||||
|
public FrostFsObjectId ObjectId { get; } = objectId;
|
||||||
|
|
||||||
|
public FrostFsRange Range { get; } = range;
|
||||||
|
|
||||||
|
public bool Raw { get; } = raw;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public FrostFsSessionToken? SessionToken { get; set; }
|
||||||
|
}
|
20
src/FrostFS.SDK.Client/Parameters/PrmRangeHashGet.cs
Normal file
20
src/FrostFS.SDK.Client/Parameters/PrmRangeHashGet.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmRangeHashGet(
|
||||||
|
FrostFsContainerId containerId,
|
||||||
|
FrostFsObjectId objectId,
|
||||||
|
FrostFsRange[] ranges,
|
||||||
|
byte[] salt,
|
||||||
|
CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
|
{
|
||||||
|
public FrostFsContainerId ContainerId { get; } = containerId;
|
||||||
|
|
||||||
|
public FrostFsObjectId ObjectId { get; } = objectId;
|
||||||
|
|
||||||
|
public FrostFsRange[] Ranges { get; } = ranges;
|
||||||
|
|
||||||
|
public byte[] Salt { get; } = salt;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public FrostFsSessionToken? SessionToken { get; set; }
|
||||||
|
}
|
6
src/FrostFS.SDK.Client/Parameters/PrmSessionCreate.cs
Normal file
6
src/FrostFS.SDK.Client/Parameters/PrmSessionCreate.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public sealed class PrmSessionCreate(ulong expiration, CallContext? ctx = null) : PrmBase(ctx)
|
||||||
|
{
|
||||||
|
public ulong Expiration { get; set; } = expiration;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public sealed class PrmSingleObjectPut(FrostFsObject frostFsObject) : PrmBase, ISessionToken
|
public sealed class PrmSingleObjectPut(FrostFsObject frostFsObject, CallContext? ctx = null) : PrmBase(ctx), ISessionToken
|
||||||
{
|
{
|
||||||
public FrostFsObject FrostFsObject { get; set; } = frostFsObject;
|
public FrostFsObject FrostFsObject { get; set; } = frostFsObject;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class PrmWait(TimeSpan timeout, TimeSpan pollInterval)
|
public class PrmWait(TimeSpan timeout, TimeSpan pollInterval)
|
||||||
{
|
{
|
163
src/FrostFS.SDK.Client/Pool/ClientStatusMonitor.cs
Normal file
163
src/FrostFS.SDK.Client/Pool/ClientStatusMonitor.cs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
// clientStatusMonitor count error rate and other statistics for connection.
|
||||||
|
public class ClientStatusMonitor : IClientStatus
|
||||||
|
{
|
||||||
|
private static readonly MethodIndex[] MethodIndexes =
|
||||||
|
[
|
||||||
|
MethodIndex.methodBalanceGet,
|
||||||
|
MethodIndex.methodContainerPut,
|
||||||
|
MethodIndex.methodContainerGet,
|
||||||
|
MethodIndex.methodContainerList,
|
||||||
|
MethodIndex.methodContainerDelete,
|
||||||
|
MethodIndex.methodEndpointInfo,
|
||||||
|
MethodIndex.methodNetworkInfo,
|
||||||
|
MethodIndex.methodNetMapSnapshot,
|
||||||
|
MethodIndex.methodObjectPut,
|
||||||
|
MethodIndex.methodObjectDelete,
|
||||||
|
MethodIndex.methodObjectGet,
|
||||||
|
MethodIndex.methodObjectHead,
|
||||||
|
MethodIndex.methodObjectRange,
|
||||||
|
MethodIndex.methodObjectPatch,
|
||||||
|
MethodIndex.methodSessionCreate,
|
||||||
|
MethodIndex.methodAPEManagerAddChain,
|
||||||
|
MethodIndex.methodAPEManagerRemoveChain,
|
||||||
|
MethodIndex.methodAPEManagerListChains
|
||||||
|
];
|
||||||
|
|
||||||
|
public static string GetMethodName(MethodIndex index)
|
||||||
|
{
|
||||||
|
return index switch
|
||||||
|
{
|
||||||
|
MethodIndex.methodBalanceGet => "BalanceGet",
|
||||||
|
MethodIndex.methodContainerPut => "ContainerPut",
|
||||||
|
MethodIndex.methodContainerGet => "ContainerGet",
|
||||||
|
MethodIndex.methodContainerList => "ContainerList",
|
||||||
|
MethodIndex.methodContainerDelete => "ContainerDelete",
|
||||||
|
MethodIndex.methodEndpointInfo => "EndpointInfo",
|
||||||
|
MethodIndex.methodNetworkInfo => "NetworkInfo",
|
||||||
|
MethodIndex.methodNetMapSnapshot => "NetMapSnapshot",
|
||||||
|
MethodIndex.methodObjectPut => "ObjectPut",
|
||||||
|
MethodIndex.methodObjectDelete => "ObjectDelete",
|
||||||
|
MethodIndex.methodObjectGet => "ObjectGet",
|
||||||
|
MethodIndex.methodObjectHead => "ObjectHead",
|
||||||
|
MethodIndex.methodObjectRange => "ObjectRange",
|
||||||
|
MethodIndex.methodObjectPatch => "ObjectPatch",
|
||||||
|
MethodIndex.methodSessionCreate => "SessionCreate",
|
||||||
|
MethodIndex.methodAPEManagerAddChain => "APEManagerAddChain",
|
||||||
|
MethodIndex.methodAPEManagerRemoveChain => "APEManagerRemoveChain",
|
||||||
|
MethodIndex.methodAPEManagerListChains => "APEManagerListChains",
|
||||||
|
_ => throw new ArgumentException("Unknown method", nameof(index)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
private readonly ILogger? logger;
|
||||||
|
private int healthy;
|
||||||
|
|
||||||
|
public ClientStatusMonitor(ILogger? logger, string address)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
healthy = (int)HealthyStatus.Healthy;
|
||||||
|
|
||||||
|
Address = address;
|
||||||
|
Methods = new MethodStatus[MethodIndexes.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < MethodIndexes.Length; i++)
|
||||||
|
{
|
||||||
|
Methods[i] = new MethodStatus(GetMethodName(MethodIndexes[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Address { get; }
|
||||||
|
|
||||||
|
internal uint ErrorThreshold { get; set; }
|
||||||
|
|
||||||
|
public uint CurrentErrorCount { get; set; }
|
||||||
|
|
||||||
|
public ulong OverallErrorCount { get; set; }
|
||||||
|
|
||||||
|
public MethodStatus[] Methods { get; private set; }
|
||||||
|
|
||||||
|
public bool IsHealthy()
|
||||||
|
{
|
||||||
|
var res = Interlocked.CompareExchange(ref healthy, -1, -1) == (int)HealthyStatus.Healthy;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDialed()
|
||||||
|
{
|
||||||
|
return Interlocked.CompareExchange(ref healthy, -1, -1) != (int)HealthyStatus.UnhealthyOnDial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetHealthy()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref healthy, (int)HealthyStatus.Healthy);
|
||||||
|
}
|
||||||
|
public void SetUnhealthy()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref healthy, (int)HealthyStatus.UnhealthyOnRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetUnhealthyOnDial()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref healthy, (int)HealthyStatus.UnhealthyOnDial);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncErrorRate()
|
||||||
|
{
|
||||||
|
bool thresholdReached;
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
CurrentErrorCount++;
|
||||||
|
OverallErrorCount++;
|
||||||
|
|
||||||
|
thresholdReached = CurrentErrorCount >= ErrorThreshold;
|
||||||
|
|
||||||
|
if (thresholdReached)
|
||||||
|
{
|
||||||
|
SetUnhealthy();
|
||||||
|
CurrentErrorCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thresholdReached && logger != null)
|
||||||
|
{
|
||||||
|
FrostFsMessages.ErrorЕhresholdReached(logger, Address, ErrorThreshold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetCurrentErrorRate()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return CurrentErrorCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong GetOverallErrorRate()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return OverallErrorCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusSnapshot[] MethodsStatus()
|
||||||
|
{
|
||||||
|
var result = new StatusSnapshot[Methods.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < result.Length; i++)
|
||||||
|
{
|
||||||
|
result[i] = Methods[i].Snapshot!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
151
src/FrostFS.SDK.Client/Pool/ClientWrapper.cs
Normal file
151
src/FrostFS.SDK.Client/Pool/ClientWrapper.cs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Grpc.Core;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
// clientWrapper is used by default, alternative implementations are intended for testing purposes only.
|
||||||
|
public class ClientWrapper : ClientStatusMonitor
|
||||||
|
{
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
private SessionCache sessionCache;
|
||||||
|
|
||||||
|
|
||||||
|
internal ClientWrapper(WrapperPrm wrapperPrm, Pool pool) : base(wrapperPrm.Logger, wrapperPrm.Address)
|
||||||
|
{
|
||||||
|
WrapperPrm = wrapperPrm;
|
||||||
|
ErrorThreshold = wrapperPrm.ErrorThreshold;
|
||||||
|
|
||||||
|
sessionCache = pool.SessionCache;
|
||||||
|
Client = new FrostFSClient(WrapperPrm, sessionCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FrostFSClient? Client { get; private set; }
|
||||||
|
|
||||||
|
internal WrapperPrm WrapperPrm { get; }
|
||||||
|
|
||||||
|
internal FrostFSClient? GetClient()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (IsHealthy())
|
||||||
|
{
|
||||||
|
return Client;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dial establishes a connection to the server from the FrostFS network.
|
||||||
|
// Returns an error describing failure reason. If failed, the client
|
||||||
|
// SHOULD NOT be used.
|
||||||
|
internal async Task Dial(CallContext ctx)
|
||||||
|
{
|
||||||
|
var client = GetClient() ?? throw new FrostFsInvalidObjectException("pool client unhealthy");
|
||||||
|
|
||||||
|
await client.Dial(ctx).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void HandleError(Exception ex)
|
||||||
|
{
|
||||||
|
if (ex is FrostFsResponseException responseException && responseException.Status != null)
|
||||||
|
{
|
||||||
|
switch (responseException.Status.Code)
|
||||||
|
{
|
||||||
|
case FrostFsStatusCode.Internal:
|
||||||
|
case FrostFsStatusCode.WrongMagicNumber:
|
||||||
|
case FrostFsStatusCode.SignatureVerificationFailure:
|
||||||
|
case FrostFsStatusCode.NodeUnderMaintenance:
|
||||||
|
IncErrorRate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IncErrorRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ScheduleGracefulClose()
|
||||||
|
{
|
||||||
|
if (Client == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Task.Delay((int)WrapperPrm.GracefulCloseOnSwitchTimeout).ConfigureAwait(false);
|
||||||
|
|
||||||
|
Client.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// restartIfUnhealthy checks healthy status of client and recreate it if status is unhealthy.
|
||||||
|
// Indicating if status was changed by this function call and returns error that caused unhealthy status.
|
||||||
|
internal async Task<bool> RestartIfUnhealthy(CallContext ctx)
|
||||||
|
{
|
||||||
|
bool wasHealthy;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var prmNodeInfo = new PrmNodeInfo(ctx);
|
||||||
|
var response = await Client!.GetNodeInfoAsync(prmNodeInfo).ConfigureAwait(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (RpcException)
|
||||||
|
{
|
||||||
|
wasHealthy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if connection is dialed before, to avoid routine/connection leak,
|
||||||
|
// pool has to close it and then initialize once again.
|
||||||
|
if (IsDialed())
|
||||||
|
{
|
||||||
|
await ScheduleGracefulClose().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FrostFSClient? client = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client = new(WrapperPrm, sessionCache);
|
||||||
|
|
||||||
|
//TODO: set additioanl params
|
||||||
|
var error = await client.Dial(ctx).ConfigureAwait(false);
|
||||||
|
if (!string.IsNullOrEmpty(error))
|
||||||
|
{
|
||||||
|
SetUnhealthyOnDial();
|
||||||
|
return wasHealthy;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
Client = client;
|
||||||
|
client = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var prmNodeInfo = new PrmNodeInfo(ctx);
|
||||||
|
var res = await Client.GetNodeInfoAsync(prmNodeInfo).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (FrostFsException)
|
||||||
|
{
|
||||||
|
SetUnhealthy();
|
||||||
|
return wasHealthy;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetHealthy();
|
||||||
|
return !wasHealthy;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
client?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void IncRequests(ulong elapsed, MethodIndex method)
|
||||||
|
{
|
||||||
|
var methodStat = Methods[(int)method];
|
||||||
|
|
||||||
|
methodStat.IncRequests(elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
18
src/FrostFS.SDK.Client/Pool/HealthyStatus.cs
Normal file
18
src/FrostFS.SDK.Client/Pool/HealthyStatus.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
// values for healthy status of clientStatusMonitor.
|
||||||
|
public enum HealthyStatus
|
||||||
|
{
|
||||||
|
// statusUnhealthyOnDial is set when dialing to the endpoint is failed,
|
||||||
|
// so there is no connection to the endpoint, and pool should not close it
|
||||||
|
// before re-establishing connection once again.
|
||||||
|
UnhealthyOnDial,
|
||||||
|
|
||||||
|
// statusUnhealthyOnRequest is set when communication after dialing to the
|
||||||
|
// endpoint is failed due to immediate or accumulated errors, connection is
|
||||||
|
// available and pool should close it before re-establishing connection once again.
|
||||||
|
UnhealthyOnRequest,
|
||||||
|
|
||||||
|
// statusHealthy is set when connection is ready to be used by the pool.
|
||||||
|
Healthy
|
||||||
|
}
|
28
src/FrostFS.SDK.Client/Pool/IClientStatus.cs
Normal file
28
src/FrostFS.SDK.Client/Pool/IClientStatus.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
|
public interface IClientStatus
|
||||||
|
{
|
||||||
|
// isHealthy checks if the connection can handle requests.
|
||||||
|
bool IsHealthy();
|
||||||
|
|
||||||
|
// isDialed checks if the connection was created.
|
||||||
|
bool IsDialed();
|
||||||
|
|
||||||
|
// setUnhealthy marks client as unhealthy.
|
||||||
|
void SetUnhealthy();
|
||||||
|
|
||||||
|
// address return address of endpoint.
|
||||||
|
string Address { get; }
|
||||||
|
|
||||||
|
// currentErrorRate returns current errors rate.
|
||||||
|
// After specific threshold connection is considered as unhealthy.
|
||||||
|
// Pool.startRebalance routine can make this connection healthy again.
|
||||||
|
uint GetCurrentErrorRate();
|
||||||
|
|
||||||
|
// overallErrorRate returns the number of all happened errors.
|
||||||
|
ulong GetOverallErrorRate();
|
||||||
|
|
||||||
|
// methodsStatus returns statistic for all used methods.
|
||||||
|
StatusSnapshot[] MethodsStatus();
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue