frostfs-sdk-csharp/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
Pavel Gross c9a75ea025 [#24] Client: Implement pool part1
first iteration - base classes and methods

Signed-off-by: Pavel Gross <p.gross@yadro.com>
2024-10-21 10:48:00 +03:00

225 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Threading.Tasks;
using FrostFS.Container;
using FrostFS.Refs;
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography;
using FrostFS.Session;
namespace FrostFS.SDK.ClientV2;
internal sealed class ContainerServiceProvider(ContainerService.ContainerServiceClient service, EnvironmentContext envCtx) : ContextAccessor(envCtx), ISessionProvider
{
readonly SessionProvider sessions = new(envCtx);
public async ValueTask<SessionToken> GetOrCreateSession(ISessionToken args, CallContext ctx)
{
return await sessions.GetOrCreateSession(args, ctx).ConfigureAwait(false);
}
internal async Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args)
{
GetRequest request = GetContainerRequest(args.Container.ContainerID, args.XHeaders, args.Context!);
var response = await service.GetAsync(request, null, args.Context!.Deadline, args.Context.CancellationToken);
Verifier.CheckResponse(response);
return response.Body.Container.ToModel();
}
internal async IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll args)
{
var ctx = args.Context!;
ctx.OwnerId ??= EnvironmentContext.Owner;
ctx.Key ??= EnvironmentContext.Key?.ECDsaKey;
if (ctx.Key == null)
throw new InvalidObjectException(nameof(ctx.Key));
if (ctx.OwnerId == null)
throw new InvalidObjectException(nameof(ctx.OwnerId));
var request = new ListRequest
{
Body = new()
{
OwnerId = ctx.OwnerId.ToMessage()
}
};
request.AddMetaHeader(args.XHeaders);
request.Sign(ctx.Key);
var response = await service.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
foreach (var cid in response.Body.ContainerIds)
{
yield return new FrostFsContainerId(Base58.Encode(cid.Value.ToByteArray()));
}
}
internal async Task<FrostFsContainerId> CreateContainerAsync(PrmContainerCreate args)
{
var ctx = args.Context!;
var grpcContainer = args.Container.GetContainer();
grpcContainer.OwnerId ??= ctx.OwnerId?.ToMessage();
grpcContainer.Version ??= ctx.Version?.ToMessage();
if (ctx.Key == null)
throw new InvalidObjectException(nameof(ctx.Key));
if (grpcContainer.OwnerId == null)
throw new InvalidObjectException(nameof(grpcContainer.OwnerId));
if (grpcContainer.Version == null)
throw new InvalidObjectException(nameof(grpcContainer.Version));
var request = new PutRequest
{
Body = new PutRequest.Types.Body
{
Container = grpcContainer,
Signature = ctx.Key.SignRFC6979(grpcContainer)
}
};
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
sessionToken.CreateContainerTokenContext(
null,
ContainerSessionContext.Types.Verb.Put,
ctx.Key,
ctx.GetPublicKeyCache()!);
request.AddMetaHeader(args.XHeaders, sessionToken);
request.Sign(ctx.Key);
var response = await service.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
await WaitForContainer(WaitExpects.Exists, response.Body.ContainerId, args.WaitParams, ctx).ConfigureAwait(false);
return new FrostFsContainerId(response.Body.ContainerId);
}
internal async Task DeleteContainerAsync(PrmContainerDelete args)
{
var ctx = args.Context!;
if (ctx.Key == null)
throw new InvalidObjectException(nameof(ctx.Key));
var request = new DeleteRequest
{
Body = new DeleteRequest.Types.Body
{
ContainerId = args.ContainerId.ToMessage(),
Signature = ctx.Key.SignRFC6979(args.ContainerId.ToMessage().Value)
}
};
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
sessionToken.CreateContainerTokenContext(
request.Body.ContainerId,
ContainerSessionContext.Types.Verb.Delete,
ctx.Key,
ctx.GetPublicKeyCache()!);
request.AddMetaHeader(args.XHeaders, sessionToken);
request.Sign(ctx.Key);
var response = await service.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
await WaitForContainer(WaitExpects.Removed, request.Body.ContainerId, args.WaitParams, ctx)
.ConfigureAwait(false);
Verifier.CheckResponse(response);
}
private static GetRequest GetContainerRequest(ContainerID id, NameValueCollection? xHeaders, CallContext ctx)
{
if (ctx.Key == null)
throw new InvalidObjectException(nameof(ctx.Key));
var request = new GetRequest
{
Body = new GetRequest.Types.Body
{
ContainerId = id
}
};
request.AddMetaHeader(xHeaders);
request.Sign(ctx.Key);
return request;
}
private enum WaitExpects
{
Exists,
Removed
}
private async Task WaitForContainer(WaitExpects expect, ContainerID id, PrmWait? waitParams, CallContext ctx)
{
var request = GetContainerRequest(id, null, ctx);
async Task action()
{
var response = await service.GetAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
}
await WaitFor(action, expect, waitParams).ConfigureAwait(false);
}
private static async Task WaitFor(
Func<Task> action,
WaitExpects expect,
PrmWait? waitParams)
{
waitParams ??= PrmWait.DefaultParams;
var deadLine = waitParams.GetDeadline();
while (true)
{
try
{
await action().ConfigureAwait(false);
if (expect == WaitExpects.Exists)
return;
if (DateTime.UtcNow >= deadLine)
throw new TimeoutException();
await Task.Delay(waitParams.PollInterval).ConfigureAwait(false);
}
catch (ResponseException ex)
{
if (DateTime.UtcNow >= deadLine)
throw new TimeoutException();
if (ex.Status?.Code != FrostFsStatusCode.ContainerNotFound)
throw;
if (expect == WaitExpects.Removed)
return;
await Task.Delay(waitParams.PollInterval).ConfigureAwait(false);
}
}
}
}