213 lines
6.6 KiB
C#
213 lines
6.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Security.Cryptography;
|
|
using System.Threading.Tasks;
|
|
|
|
using FrostFS.Container;
|
|
using FrostFS.Refs;
|
|
using FrostFS.SDK.Client;
|
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
using FrostFS.SDK.Cryptography;
|
|
using FrostFS.Session;
|
|
|
|
namespace FrostFS.SDK.Client;
|
|
|
|
internal sealed class ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientContext clientCtx) : ContextAccessor(clientCtx)
|
|
{
|
|
private SessionProvider? sessions;
|
|
|
|
public async ValueTask<FrostFsSessionToken> GetDefaultSession(ISessionToken args, CallContext ctx)
|
|
{
|
|
sessions ??= new(ClientContext);
|
|
|
|
if (!ClientContext.SessionCache!.TryGetValue(ClientContext.SessionCacheKey, out var token))
|
|
{
|
|
var protoToken = await sessions.GetDefaultSession(args, ctx).ConfigureAwait(false);
|
|
|
|
token = new FrostFsSessionToken(protoToken);
|
|
|
|
ClientContext.SessionCache.SetValue(ClientContext.SessionCacheKey, token);
|
|
}
|
|
|
|
if (token == null)
|
|
{
|
|
throw new FrostFsException("Cannot create session");
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
internal async Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args, CallContext ctx)
|
|
{
|
|
GetRequest request = GetContainerRequest(args.Container.GetContainerID(), args.XHeaders, ClientContext.Key.ECDsaKey);
|
|
|
|
var response = await service.GetAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken);
|
|
|
|
Verifier.CheckResponse(response);
|
|
|
|
return response.Body.Container.ToModel();
|
|
}
|
|
|
|
internal async IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll args, CallContext ctx)
|
|
{
|
|
var request = new ListRequest
|
|
{
|
|
Body = new()
|
|
{
|
|
OwnerId = ClientContext.Owner.OwnerID
|
|
}
|
|
};
|
|
|
|
request.AddMetaHeader(args.XHeaders);
|
|
request.Sign(ClientContext.Key.ECDsaKey);
|
|
|
|
var response = await service.ListAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken);
|
|
|
|
Verifier.CheckResponse(response);
|
|
|
|
foreach (var cid in response.Body.ContainerIds)
|
|
{
|
|
yield return new FrostFsContainerId(Base58.Encode(cid.Value.Span));
|
|
}
|
|
}
|
|
|
|
internal async Task<FrostFsContainerId> CreateContainerAsync(PrmContainerCreate args, CallContext ctx)
|
|
{
|
|
var grpcContainer = args.Container.GetContainer();
|
|
|
|
grpcContainer.OwnerId ??= ClientContext.Owner.OwnerID;
|
|
grpcContainer.Version ??= ClientContext.Version.VersionID;
|
|
|
|
var request = new PutRequest
|
|
{
|
|
Body = new PutRequest.Types.Body
|
|
{
|
|
Container = grpcContainer,
|
|
Signature = ClientContext.Key.ECDsaKey.SignRFC6979(grpcContainer)
|
|
}
|
|
};
|
|
|
|
var sessionToken = (args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false)) ?? throw new FrostFsException("Cannot create session token");
|
|
|
|
var protoToken = sessionToken.CreateContainerToken(
|
|
null,
|
|
ContainerSessionContext.Types.Verb.Put,
|
|
ClientContext.Key);
|
|
|
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
|
|
|
request.Sign(ClientContext.Key.ECDsaKey);
|
|
|
|
var response = await service.PutAsync(request, null, ctx.GetDeadline(), 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, CallContext ctx)
|
|
{
|
|
var request = new DeleteRequest
|
|
{
|
|
Body = new DeleteRequest.Types.Body
|
|
{
|
|
ContainerId = args.ContainerId.ToMessage(),
|
|
Signature = ClientContext.Key.ECDsaKey.SignRFC6979(args.ContainerId.ToMessage().Value)
|
|
}
|
|
};
|
|
|
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
|
|
|
var protoToken = sessionToken.CreateContainerToken(
|
|
request.Body.ContainerId,
|
|
ContainerSessionContext.Types.Verb.Delete,
|
|
ClientContext.Key);
|
|
|
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
|
|
|
request.Sign(ClientContext.Key.ECDsaKey);
|
|
|
|
var response = await service.DeleteAsync(request, null, ctx.GetDeadline(), 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, string[] xHeaders, ECDsa key)
|
|
{
|
|
var request = new GetRequest
|
|
{
|
|
Body = new GetRequest.Types.Body
|
|
{
|
|
ContainerId = id
|
|
}
|
|
};
|
|
|
|
request.AddMetaHeader(xHeaders);
|
|
request.Sign(key);
|
|
|
|
return request;
|
|
}
|
|
|
|
private enum WaitExpects
|
|
{
|
|
Exists,
|
|
Removed
|
|
}
|
|
|
|
private async Task WaitForContainer(WaitExpects expect, ContainerID id, PrmWait waitParams, CallContext ctx)
|
|
{
|
|
var request = GetContainerRequest(id, [], ClientContext.Key.ECDsaKey);
|
|
|
|
async Task action()
|
|
{
|
|
var response = await service.GetAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken);
|
|
Verifier.CheckResponse(response);
|
|
}
|
|
|
|
await WaitFor(action, expect, waitParams).ConfigureAwait(false);
|
|
}
|
|
|
|
private static async Task WaitFor(
|
|
Func<Task> action,
|
|
WaitExpects expect,
|
|
PrmWait waitParams)
|
|
{
|
|
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 (FrostFsResponseException 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);
|
|
}
|
|
}
|
|
}
|
|
}
|