From 195854a45b8bc92670bfc6117b190da38c4d8567 Mon Sep 17 00:00:00 2001 From: Pavel Gross <p.gross@yadro.com> Date: Wed, 12 Feb 2025 04:20:01 +0300 Subject: [PATCH] [#30] Client: Add object model for Rules Signed-off-by: Pavel Gross <p.gross@yadro.com> --- src/FrostFS.SDK.Client/ApeRules/Actions.cs | 36 +++ .../{Models/Chain => ApeRules}/ChainTarget.cs | 0 src/FrostFS.SDK.Client/ApeRules/Condition.cs | 43 +++ .../ApeRules/Enums/ConditionKindType.cs | 7 + .../ApeRules/Enums/ConditionType.cs | 36 +++ .../Enums}/FrostFsTargetType.cs | 2 +- .../ApeRules/Enums/Status.cs | 9 + .../ApeRules/FrostFsChain.cs | 10 + .../ApeRules/FrostFsRule.cs | 18 ++ src/FrostFS.SDK.Client/ApeRules/MatchType.cs | 10 + src/FrostFS.SDK.Client/ApeRules/Resources.cs | 36 +++ .../ApeRules/RuleSerializer.cs | 286 ++++++++++++++++++ .../Exceptions/FrostFsResponseException.cs | 3 +- src/FrostFS.SDK.Client/Mappers/Status.cs | 6 +- .../Models/Chain/FrostFsChain.cs | 41 --- .../Models/Containers/FrostFsContainerId.cs | 21 +- .../Models/Response/FrostFsResponseStatus.cs | 8 +- .../Parameters/PrmApeChainAdd.cs | 4 +- .../Parameters/PrmApeChainList.cs | 4 +- .../Parameters/PrmApeChainRemove.cs | 13 +- .../Services/ApeManagerServiceProvider.cs | 9 +- .../Services/ContainerServiceProvider.cs | 2 +- .../Services/ObjectServiceProvider.cs | 9 +- src/FrostFS.SDK.Client/Tools/Verifier.cs | 16 +- .../Smoke/SmokeClientTests.cs | 191 +++++++----- src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs | 10 +- .../Unit/PlacementVectorTests.cs | 2 +- 27 files changed, 677 insertions(+), 155 deletions(-) create mode 100644 src/FrostFS.SDK.Client/ApeRules/Actions.cs rename src/FrostFS.SDK.Client/{Models/Chain => ApeRules}/ChainTarget.cs (100%) create mode 100644 src/FrostFS.SDK.Client/ApeRules/Condition.cs create mode 100644 src/FrostFS.SDK.Client/ApeRules/Enums/ConditionKindType.cs create mode 100644 src/FrostFS.SDK.Client/ApeRules/Enums/ConditionType.cs rename src/FrostFS.SDK.Client/{Models/Chain => ApeRules/Enums}/FrostFsTargetType.cs (86%) create mode 100644 src/FrostFS.SDK.Client/ApeRules/Enums/Status.cs create mode 100644 src/FrostFS.SDK.Client/ApeRules/FrostFsChain.cs create mode 100644 src/FrostFS.SDK.Client/ApeRules/FrostFsRule.cs create mode 100644 src/FrostFS.SDK.Client/ApeRules/MatchType.cs create mode 100644 src/FrostFS.SDK.Client/ApeRules/Resources.cs create mode 100644 src/FrostFS.SDK.Client/ApeRules/RuleSerializer.cs delete mode 100644 src/FrostFS.SDK.Client/Models/Chain/FrostFsChain.cs diff --git a/src/FrostFS.SDK.Client/ApeRules/Actions.cs b/src/FrostFS.SDK.Client/ApeRules/Actions.cs new file mode 100644 index 0000000..28600b8 --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/Actions.cs @@ -0,0 +1,36 @@ +namespace FrostFS.SDK.Client; + +public struct Actions(bool inverted, string[] names) : System.IEquatable<Actions> +{ + public bool Inverted { get; set; } = inverted; + + public string[] Names { get; set; } = names; + + public override bool Equals(object obj) + { + if (obj == null || obj is not Actions) + return false; + + return Equals((Actions)obj); + } + + public override readonly int GetHashCode() + { + return Inverted.GetHashCode() ^ string.Join(string.Empty, Names).GetHashCode(); + } + + public static bool operator ==(Actions left, Actions right) + { + return left.Equals(right); + } + + public static bool operator !=(Actions left, Actions right) + { + return !(left == right); + } + + public readonly bool Equals(Actions other) + { + return this.GetHashCode().Equals(other.GetHashCode()); + } +} diff --git a/src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs b/src/FrostFS.SDK.Client/ApeRules/ChainTarget.cs similarity index 100% rename from src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs rename to src/FrostFS.SDK.Client/ApeRules/ChainTarget.cs diff --git a/src/FrostFS.SDK.Client/ApeRules/Condition.cs b/src/FrostFS.SDK.Client/ApeRules/Condition.cs new file mode 100644 index 0000000..11c1f7a --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/Condition.cs @@ -0,0 +1,43 @@ +namespace FrostFS.SDK.Client; + +public struct Condition : System.IEquatable<Condition> +{ + public ConditionType Op { get; set; } + + public ConditionKindType Kind { get; set; } + + public string? Key { get; set; } + + public string? Value { get; set; } + + public override bool Equals(object obj) + { + if (obj == null || obj is not Condition) + return false; + + return Equals((Condition)obj); + } + + public override readonly int GetHashCode() + { + return Op.GetHashCode() + ^ Kind.GetHashCode() + ^ (Key != null ? Key.GetHashCode() : 0) + ^ (Value != null ? Value.GetHashCode() : 0); + } + + public static bool operator ==(Condition left, Condition right) + { + return left.Equals(right); + } + + public static bool operator !=(Condition left, Condition right) + { + return !(left == right); + } + + public readonly bool Equals(Condition other) + { + return this.GetHashCode().Equals(other.GetHashCode()); + } +} diff --git a/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionKindType.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionKindType.cs new file mode 100644 index 0000000..fd13893 --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionKindType.cs @@ -0,0 +1,7 @@ +namespace FrostFS.SDK.Client; + +public enum ConditionKindType +{ + Resource, + Request +} diff --git a/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionType.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionType.cs new file mode 100644 index 0000000..a5f6ab4 --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionType.cs @@ -0,0 +1,36 @@ +namespace FrostFS.SDK.Client; + +public enum ConditionType +{ + CondStringEquals, + + CondStringNotEquals, + CondStringEqualsIgnoreCase, + + CondStringNotEqualsIgnoreCase, + CondStringLike, + + CondStringNotLike, + CondStringLessThan, + + CondStringLessThanEquals, + CondStringGreaterThan, + + CondStringGreaterThanEquals, + + // Numeric condition operators. + CondNumericEquals, + + CondNumericNotEquals, + CondNumericLessThan, + + CondNumericLessThanEquals, + CondNumericGreaterThan, + + CondNumericGreaterThanEquals, + + CondSliceContains, + + CondIPAddress, + CondNotIPAddress, +} diff --git a/src/FrostFS.SDK.Client/Models/Chain/FrostFsTargetType.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/FrostFsTargetType.cs similarity index 86% rename from src/FrostFS.SDK.Client/Models/Chain/FrostFsTargetType.cs rename to src/FrostFS.SDK.Client/ApeRules/Enums/FrostFsTargetType.cs index d5379f8..178ca63 100644 --- a/src/FrostFS.SDK.Client/Models/Chain/FrostFsTargetType.cs +++ b/src/FrostFS.SDK.Client/ApeRules/Enums/FrostFsTargetType.cs @@ -2,7 +2,7 @@ public enum FrostFsTargetType { - Undefined = 0, + Undefined, Namespace, Container, User, diff --git a/src/FrostFS.SDK.Client/ApeRules/Enums/Status.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/Status.cs new file mode 100644 index 0000000..547449b --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/Enums/Status.cs @@ -0,0 +1,9 @@ +namespace FrostFS.SDK.Client; + +public enum RuleStatus +{ + Allow, + NoRuleFound, + AccessDenied, + QuotaLimitReached +} diff --git a/src/FrostFS.SDK.Client/ApeRules/FrostFsChain.cs b/src/FrostFS.SDK.Client/ApeRules/FrostFsChain.cs new file mode 100644 index 0000000..c79bd7d --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/FrostFsChain.cs @@ -0,0 +1,10 @@ +namespace FrostFS.SDK.Client; + +public class FrostFsChain +{ + public byte[] ID { get; set; } = []; + + public FrostFsRule[] Rules { get; set; } = []; + + public RuleMatchType MatchType { get; set; } +} diff --git a/src/FrostFS.SDK.Client/ApeRules/FrostFsRule.cs b/src/FrostFS.SDK.Client/ApeRules/FrostFsRule.cs new file mode 100644 index 0000000..125e3f1 --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/FrostFsRule.cs @@ -0,0 +1,18 @@ +namespace FrostFS.SDK.Client; + +public class FrostFsRule +{ + public RuleStatus Status { get; set; } + + // Actions the operation is applied to. + public Actions Actions { get; set; } + + // List of the resources the operation is applied to. + public Resource Resources { get; set; } + + // True iff individual conditions must be combined with the logical OR. + // By default AND is used, so _each_ condition must pass. + public bool Any { get; set; } + + public Condition[]? Conditions { get; set; } +} diff --git a/src/FrostFS.SDK.Client/ApeRules/MatchType.cs b/src/FrostFS.SDK.Client/ApeRules/MatchType.cs new file mode 100644 index 0000000..fe3305e --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/MatchType.cs @@ -0,0 +1,10 @@ +namespace FrostFS.SDK.Client; + +public enum RuleMatchType +{ + // DenyPriority rejects the request if any `Deny` is specified. + DenyPriority, + + // FirstMatch returns the first rule action matched to the request. + FirstMatch +} diff --git a/src/FrostFS.SDK.Client/ApeRules/Resources.cs b/src/FrostFS.SDK.Client/ApeRules/Resources.cs new file mode 100644 index 0000000..55849af --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/Resources.cs @@ -0,0 +1,36 @@ +namespace FrostFS.SDK.Client; + +public struct Resource(bool inverted, string[] names) : System.IEquatable<Resource> +{ + public bool Inverted { get; set; } = inverted; + + public string[] Names { get; set; } = names; + + public override bool Equals(object obj) + { + if (obj == null || obj is not Resource) + return false; + + return Equals((Resource)obj); + } + + public override readonly int GetHashCode() + { + return Inverted.GetHashCode() ^ string.Join(string.Empty, Names).GetHashCode(); + } + + public static bool operator ==(Resource left, Resource right) + { + return left.Equals(right); + } + + public static bool operator !=(Resource left, Resource right) + { + return !(left == right); + } + + public readonly bool Equals(Resource other) + { + return this.GetHashCode().Equals(other.GetHashCode()); + } +} diff --git a/src/FrostFS.SDK.Client/ApeRules/RuleSerializer.cs b/src/FrostFS.SDK.Client/ApeRules/RuleSerializer.cs new file mode 100644 index 0000000..d5602f3 --- /dev/null +++ b/src/FrostFS.SDK.Client/ApeRules/RuleSerializer.cs @@ -0,0 +1,286 @@ +using System; + +namespace FrostFS.SDK.Client; + +internal static class RuleSerializer +{ + const byte Version = 0; // increase if breaking change + + const int ByteSize = 1; + const int UInt8Size = ByteSize; + const int BoolSize = ByteSize; + + const long NullSlice = -1; + const int NullSliceSize = 1; + + const byte ByteTrue = 1; + const byte ByteFalse = 0; + + /// <summary> + /// maxSliceLen taken from https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77 + /// </summary> + const int MaxSliceLen = 0x1000000; + + const int ChainMarshalVersion = 0; + + internal static byte[] Serialize(FrostFsChain chain) + { + int s = UInt8Size // Marshaller version + + UInt8Size // Chain version + + SliceSize(chain.ID, b => ByteSize) + + SliceSize(chain.Rules, RuleSize) + + UInt8Size; // MatchType + + byte[] buf = new byte[s]; + + int offset = UInt8Marshal(buf, 0, Version); + offset = UInt8Marshal(buf, offset, ChainMarshalVersion); + offset = SliceMarshal(buf, offset, chain.ID, ByteMarshal); + offset = SliceMarshal(buf, offset, chain.Rules, MarshalRule); + offset = UInt8Marshal(buf, offset, (byte)chain.MatchType); + + VerifyMarshal(buf, offset); + + return buf; + } + + private static int Int64Size(long value) + { + // https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=92;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c + // and + // https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=41;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c + ulong ux = (ulong)value << 1; + if (value < 0) + { + ux = ~ux; + } + + int size = 0; + while (ux >= 0x80) + { + size++; + ux >>= 7; + } + + return size + 1; + } + + private static int SliceSize<T>(T[] slice, Func<T, int> sizeOf) + { + if (slice == null) + { + return NullSliceSize; + } + + // Assuming Int64Size is the size of the slice + var size = Int64Size(slice.Length); + foreach (var v in slice) + { + size += sizeOf(v); + } + + return size; + } + + private static int StringSize(string? s) + { + var len = s !=null ? s.Length : 0; + return Int64Size(len) + len; + } + + private static int ActionsSize(Actions action) + { + return BoolSize // Inverted + + SliceSize(action.Names, StringSize); + } + + private static int ResourcesSize(Resource resource) + { + return BoolSize // Inverted + + SliceSize(resource.Names, StringSize); + } + + private static int ConditionSize(Condition condition) + { + return ByteSize // Op + + ByteSize // Object + + StringSize(condition.Key) + + StringSize(condition.Value); + } + + public static int RuleSize(FrostFsRule rule) + { + if (rule is null) + { + throw new ArgumentNullException(nameof(rule)); + } + + return ByteSize // Status + + ActionsSize(rule.Actions) + + ResourcesSize(rule.Resources) + + BoolSize // Any + + SliceSize(rule.Conditions!, ConditionSize); + } + + public static int UInt8Marshal(byte[] buf, int offset, byte value) + { + if (buf.Length - offset < 1) + { + throw new FrostFsException("Not enough bytes left to serialize value of type byte"); + } + + buf[offset] = value; + + return offset + 1; + } + + public static int ByteMarshal(byte[] buf, int offset, byte value) + { + return UInt8Marshal(buf, offset, value); + } + + // PutVarint encodes an int64 into buf and returns the number of bytes written. + // If the buffer is too small, PutVarint will panic. + private static int PutVarint(byte[] buf, int offset, long x) + { + var ux = (ulong)x << 1; + + if (x < 0) + { + ux = ~ux; + } + + return PutUvarint(buf, offset, ux); + } + + private static int PutUvarint(byte[] buf, int offset, ulong x) + { + while (x >= 0x80) + { + buf[offset] = (byte)(x | 0x80); + x >>= 7; + offset++; + } + + buf[offset] = (byte)x; + + return offset + 1; + } + + public static int Int64Marshal(byte[] buf, int offset, long v) + { + if (buf.Length - offset < Int64Size(v)) + { + throw new FrostFsException("Not enough bytes left to serialize value of type long"); + } + + return PutVarint(buf, offset, v); + } + + public static int SliceMarshal<T>(byte[] buf, int offset, T[] slice, Func<byte[], int, T, int> marshalT) + { + if (slice == null) + { + return Int64Marshal(buf, offset, NullSlice); + } + + if (slice.Length > MaxSliceLen) + { + throw new FrostFsException($"slice size if too big: {slice.Length}"); + } + + offset = Int64Marshal(buf, offset, slice.Length); + + foreach (var v in slice) + { + offset = marshalT(buf, offset, v); + } + + return offset; + } + + private static int BoolMarshal(byte[] buf, int offset, bool value) + { + return UInt8Marshal(buf, offset, value ? ByteTrue : ByteFalse); + } + + private static int StringMarshal(byte[] buf, int offset, string value) + { + if (value == null) + { + throw new FrostFsException($"string value is null"); + } + + if (value.Length > MaxSliceLen) + { + throw new FrostFsException($"string is too long: {value.Length}"); + } + + if (buf.Length - offset < Int64Size(value.Length) + value.Length) + { + throw new FrostFsException($"Not enough bytes left to serialize value of type string with length {value.Length}"); + } + + offset = Int64Marshal(buf, offset, value.Length); + + if (string.IsNullOrEmpty(value)) + { + return offset; + } + + Buffer.BlockCopy(System.Text.Encoding.UTF8.GetBytes(value), 0, buf, offset, value.Length); + + return offset + value.Length; + } + + private static int MarshalActions(byte[] buf, int offset, Actions action) + { + offset = BoolMarshal(buf, offset, action.Inverted); + + return SliceMarshal(buf, offset, action.Names, StringMarshal); + } + + private static int MarshalCondition(byte[] buf, int offset, Condition condition) + { + offset = ByteMarshal(buf, offset, (byte)condition.Op); + + offset = ByteMarshal(buf, offset, (byte)condition.Kind); + + offset = StringMarshal(buf, offset, condition.Key!); + + return StringMarshal(buf, offset, condition.Value!); + } + + private static int MarshalRule(byte[] buf, int offset, FrostFsRule rule) + { + if (rule is null) + { + throw new ArgumentNullException(nameof(rule)); + } + + offset = ByteMarshal(buf, offset, (byte)rule.Status); + + offset = MarshalActions(buf, offset, rule.Actions); + + offset = MarshalResources(buf, offset, rule.Resources); + + offset = BoolMarshal(buf, offset, rule.Any); + + return SliceMarshal(buf, offset, rule.Conditions!, MarshalCondition); + } + + private static int MarshalResources(byte[] buf, int offset, Resource resources) + { + offset = BoolMarshal(buf, offset, resources.Inverted); + + return SliceMarshal(buf, offset, resources.Names, StringMarshal); + } + + private static void VerifyMarshal(byte[] buf, int lastOffset) + { + if (buf.Length != lastOffset) + { + throw new FrostFsException("actual data size differs from expected"); + } + } +} diff --git a/src/FrostFS.SDK.Client/Exceptions/FrostFsResponseException.cs b/src/FrostFS.SDK.Client/Exceptions/FrostFsResponseException.cs index 61f4e98..0e64db5 100644 --- a/src/FrostFS.SDK.Client/Exceptions/FrostFsResponseException.cs +++ b/src/FrostFS.SDK.Client/Exceptions/FrostFsResponseException.cs @@ -10,7 +10,8 @@ public class FrostFsResponseException : FrostFsException { } - public FrostFsResponseException(FrostFsResponseStatus status) + public FrostFsResponseException(FrostFsResponseStatus status) + : base(status != null ? status.Message != null ? "" : "" : "") { Status = status; } diff --git a/src/FrostFS.SDK.Client/Mappers/Status.cs b/src/FrostFS.SDK.Client/Mappers/Status.cs index f39b1fe..f3da090 100644 --- a/src/FrostFS.SDK.Client/Mappers/Status.cs +++ b/src/FrostFS.SDK.Client/Mappers/Status.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace FrostFS.SDK.Client.Mappers.GRPC; @@ -13,6 +14,9 @@ public static class StatusMapper return codeName is null ? throw new ArgumentException($"Unknown StatusCode. Value: '{status.Code}'.") - : new FrostFsResponseStatus((FrostFsStatusCode)status.Code, status.Message); + : new FrostFsResponseStatus( + (FrostFsStatusCode)status.Code, + status.Message, + string.Join(", ", status.Details.Select(d => System.Text.Encoding.UTF8.GetString([.. d.Value])))); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Chain/FrostFsChain.cs b/src/FrostFS.SDK.Client/Models/Chain/FrostFsChain.cs deleted file mode 100644 index 70bf093..0000000 --- a/src/FrostFS.SDK.Client/Models/Chain/FrostFsChain.cs +++ /dev/null @@ -1,41 +0,0 @@ -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; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs index 9f081cd..e91e1b8 100644 --- a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs +++ b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs @@ -34,21 +34,18 @@ public class FrostFsContainerId throw new FrostFsInvalidObjectException(); } - internal ContainerID ContainerID + public ContainerID GetContainerID() { - get + if (this.containerID != null) + return this.containerID; + + if (modelId != null) { - if (this.containerID != null) - return this.containerID; - - if (modelId != null) - { - this.containerID = this.ToMessage(); - return this.containerID; - } - - throw new FrostFsInvalidObjectException(); + this.containerID = this.ToMessage(); + return this.containerID; } + + throw new FrostFsInvalidObjectException(); } public override string ToString() diff --git a/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs b/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs index b5e5831..2f48ba3 100644 --- a/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs +++ b/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs @@ -1,14 +1,16 @@ namespace FrostFS.SDK; -public class FrostFsResponseStatus(FrostFsStatusCode code, string? message = null) +public class FrostFsResponseStatus(FrostFsStatusCode code, string? message = null, string? details = null) { public FrostFsStatusCode Code { get; set; } = code; public string Message { get; set; } = message ?? string.Empty; - + + public string Details { get; set; } = details ?? string.Empty; + public bool IsSuccess => Code == FrostFsStatusCode.Success; public override string ToString() { - return $"Response status: {Code}. Message: {Message}."; + return $"Response status: {Code}. Message: {Message}. Details: {Details}"; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Parameters/PrmApeChainAdd.cs b/src/FrostFS.SDK.Client/Parameters/PrmApeChainAdd.cs index adcc1b9..54b5171 100644 --- a/src/FrostFS.SDK.Client/Parameters/PrmApeChainAdd.cs +++ b/src/FrostFS.SDK.Client/Parameters/PrmApeChainAdd.cs @@ -1,4 +1,6 @@ -namespace FrostFS.SDK.Client; +using FrostFS.SDK.Client; + +namespace FrostFS.SDK.Client; public readonly struct PrmApeChainAdd(FrostFsChainTarget target, FrostFsChain chain, string[]? xheaders = null) : System.IEquatable<PrmApeChainAdd> { diff --git a/src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs b/src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs index a946840..57c90bd 100644 --- a/src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs +++ b/src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs @@ -1,4 +1,6 @@ -namespace FrostFS.SDK.Client; +using FrostFS.SDK.Client; + +namespace FrostFS.SDK.Client; public readonly struct PrmApeChainList(FrostFsChainTarget target, string[]? xheaders = null) : System.IEquatable<PrmApeChainList> { diff --git a/src/FrostFS.SDK.Client/Parameters/PrmApeChainRemove.cs b/src/FrostFS.SDK.Client/Parameters/PrmApeChainRemove.cs index 7e31ff4..1fc110b 100644 --- a/src/FrostFS.SDK.Client/Parameters/PrmApeChainRemove.cs +++ b/src/FrostFS.SDK.Client/Parameters/PrmApeChainRemove.cs @@ -1,13 +1,16 @@ -namespace FrostFS.SDK.Client; +using System; +using FrostFS.SDK.Client; + +namespace FrostFS.SDK.Client; public readonly struct PrmApeChainRemove( FrostFsChainTarget target, - FrostFsChain chain, + byte[] chainId, string[]? xheaders = null) : System.IEquatable<PrmApeChainRemove> { public FrostFsChainTarget Target { get; } = target; - public FrostFsChain Chain { get; } = chain; + public byte[] ChainId { get; } = chainId; /// <summary> /// FrostFS request X-Headers @@ -25,13 +28,13 @@ public readonly struct PrmApeChainRemove( public readonly bool Equals(PrmApeChainRemove other) { return Target == other.Target - && Chain == other.Chain + && ChainId.Equals(other.ChainId) && XHeaders == other.XHeaders; } public override readonly int GetHashCode() { - return Chain.GetHashCode() ^ Target.GetHashCode() ^ XHeaders.GetHashCode(); + return ChainId.GetHashCode() ^ Target.GetHashCode() ^ XHeaders.GetHashCode(); } public static bool operator ==(PrmApeChainRemove left, PrmApeChainRemove right) diff --git a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs index 0484b16..174d460 100644 --- a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Frostfs.V2.Ape; using Frostfs.V2.Apemanager; +using Google.Protobuf; namespace FrostFS.SDK.Client.Services; @@ -18,11 +19,15 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor internal async Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args, CallContext ctx) { + var binary = RuleSerializer.Serialize(args.Chain); + + var base64 = Convert.ToBase64String(binary); + AddChainRequest request = new() { Body = new() { - Chain = new() { Raw = args.Chain.GetRaw() }, + Chain = new() { Raw = UnsafeByteOperations.UnsafeWrap(binary) }, Target = args.Target.GetChainTarget() } }; @@ -43,7 +48,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor { Body = new() { - ChainId = args.Chain.GetRaw(), + ChainId = UnsafeByteOperations.UnsafeWrap(args.ChainId), Target = args.Target.GetChainTarget() } }; diff --git a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs index 6e02f10..ca76e08 100644 --- a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs @@ -39,7 +39,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService internal async Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args, CallContext ctx) { - GetRequest request = GetContainerRequest(args.Container.ContainerID, args.XHeaders, ClientContext.Key.ECDsaKey); + GetRequest request = GetContainerRequest(args.Container.GetContainerID(), args.XHeaders, ClientContext.Key.ECDsaKey); var response = await service.GetAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); diff --git a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs index 246c3eb..3125547 100644 --- a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs @@ -52,7 +52,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { Address = new Address { - ContainerId = args.ContainerId.ContainerID, + ContainerId = args.ContainerId.GetContainerID(), ObjectId = args.ObjectId.ToMessage() }, Raw = args.Raw @@ -435,7 +435,10 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl // send the last part and create linkObject if (sentObjectIds.Count > 0) { - var largeObjectHeader = new FrostFsObjectHeader(header.ContainerId, FrostFsObjectType.Regular, [.. attributes]) + var largeObjectHeader = new FrostFsObjectHeader( + header.ContainerId, + FrostFsObjectType.Regular, + attributes != null ? [.. attributes] : []) { PayloadLength = args.PutObjectContext.FullLength, }; @@ -581,7 +584,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl } }; - var sessionToken = (args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false)); + var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); var protoToken = sessionToken.CreateObjectTokenContext( new Address { ContainerId = grpcHeader.ContainerId, ObjectId = oid }, diff --git a/src/FrostFS.SDK.Client/Tools/Verifier.cs b/src/FrostFS.SDK.Client/Tools/Verifier.cs index ce215ba..063fa21 100644 --- a/src/FrostFS.SDK.Client/Tools/Verifier.cs +++ b/src/FrostFS.SDK.Client/Tools/Verifier.cs @@ -88,7 +88,6 @@ public static class Verifier return key.VerifyData(data2Verify, sig.Sign.ToByteArray()); } - internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) { if (!verification.MetaSignature.VerifyMessagePart(meta)) @@ -118,12 +117,19 @@ public static class Verifier internal static void CheckResponse(IResponse resp) { if (!resp.Verify()) + { throw new FormatException($"invalid response, type={resp.GetType()}"); + } - var status = resp.MetaHeader.Status.ToModel(); + if (resp.MetaHeader != null) + { + var status = resp.MetaHeader.Status.ToModel(); - if (status != null && !status.IsSuccess) - throw new FrostFsResponseException(status); + if (status != null && !status.IsSuccess) + { + throw new FrostFsResponseException(status); + } + } } /// <summary> @@ -138,6 +144,8 @@ public static class Verifier } if (!request.Verify()) + { throw new FrostFsResponseException($"invalid response, type={request.GetType()}"); + } } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Smoke/SmokeClientTests.cs b/src/FrostFS.SDK.Tests/Smoke/SmokeClientTests.cs index d774b47..e1b0c8d 100644 --- a/src/FrostFS.SDK.Tests/Smoke/SmokeClientTests.cs +++ b/src/FrostFS.SDK.Tests/Smoke/SmokeClientTests.cs @@ -1,11 +1,13 @@ using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; - +using System.Security.Cryptography.X509Certificates; +using System.Text; +using FrostFS.Refs; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Interfaces; using FrostFS.SDK.Cryptography; using FrostFS.SDK.SmokeTests; - +using Google.Protobuf; using Microsoft.Extensions.Options; namespace FrostFS.SDK.Tests.Smoke; @@ -38,8 +40,8 @@ public class SmokeClientTests : SmokeTestsBase Assert.Equal(13, result.Version.Minor); Assert.Equal(NodeState.Online, result.State); Assert.Equal(33, result.PublicKey.Length); - Assert.Single(result.Addresses); - Assert.Equal(9, result.Attributes.Count); + // Assert.Single(result.Addresses); + // Assert.Equal(9, result.Attributes.Count); } [Fact] @@ -81,12 +83,16 @@ public class SmokeClientTests : SmokeTestsBase var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), PrmWait.DefaultParams, xheaders: ["key1", "value1"]); var containerId = await client.CreateContainerAsync(createContainerParam, default); + await AddObjectRules(client, containerId); + var bytes = GetRandomBytes(1024); var param = new PrmObjectPut( @@ -126,34 +132,29 @@ public class SmokeClientTests : SmokeTestsBase await Cleanup(client); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), lightWait); var containerId = await client.CreateContainerAsync(createContainerParam, default); + await AddObjectRules(client, containerId); + var bytes = new byte[] { 1, 2, 3 }; - var ParentHeader = new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular) - { - PayloadLength = 3 - }; - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")], - new FrostFsSplit())); + new FrostFsObjectHeader( + containerId: containerId, + type: FrostFsObjectType.Regular, + [new FrostFsAttributePair("fileName", "test")], + new FrostFsSplit())); var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); await stream.WriteAsync(bytes.AsMemory()); var objectId = await stream.CompleteAsync(); - var head = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId), default); - var ecdsaKey = keyString.LoadWif(); var networkInfo = await client.GetNetmapSnapshotAsync(default); @@ -176,7 +177,7 @@ public class SmokeClientTests : SmokeTestsBase var checkSum = CheckSum.CreateCheckSum(bytes); - await CheckFilter(client, containerId, new FilterByPayloadHash(FrostFsMatchType.Equals, checkSum)); + // await CheckFilter(client, containerId, new FilterByPayloadHash(FrostFsMatchType.Equals, checkSum)); await CheckFilter(client, containerId, new FilterByPhysicallyStored()); } @@ -219,21 +220,25 @@ public class SmokeClientTests : SmokeTestsBase var ctx = new CallContext(TimeSpan.FromSeconds(20)); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, ctx); + var containerId = await client.CreateContainerAsync(createContainerParam, ctx); - var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); + var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); Assert.NotNull(container); Assert.True(callbackInvoked); + await AddObjectRules(client, containerId); + var bytes = GetRandomBytes(objectSize); var param = new PrmObjectPut( new FrostFsObjectHeader( - containerId: createdContainer, + containerId: containerId, type: FrostFsObjectType.Regular, [new FrostFsAttributePair("fileName", "test")])); @@ -243,22 +248,19 @@ public class SmokeClientTests : SmokeTestsBase var objectId = await stream.CompleteAsync(); var filter1 = new FilterByOwnerId(FrostFsMatchType.Equals, OwnerId!); - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer, null, [], filter1), default)) + await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, [], filter1), default)) { - var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId, false), default); - - var objHeader1 = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId, true), default); - + var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId, false), default); } var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); bool hasObject = false; - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer, null, [], filter), default)) + await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, [], filter), default)) { hasObject = true; - var res = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId), default); + var res = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId), default); var objHeader = res.HeaderInfo; Assert.NotNull(objHeader); @@ -271,7 +273,7 @@ public class SmokeClientTests : SmokeTestsBase Assert.True(hasObject); - var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, objectId), default); + var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId), default); var downloadedBytes = new byte[@object.Header.PayloadLength]; MemoryStream ms = new(downloadedBytes); @@ -300,15 +302,19 @@ public class SmokeClientTests : SmokeTestsBase await Cleanup(client); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, default); + var containerId = await client.CreateContainerAsync(createContainerParam, default); - var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); + var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); Assert.NotNull(container); + await AddObjectRules(client, containerId); + var bytes = new byte[1024]; for (int i = 0; i < 1024; i++) { @@ -317,7 +323,7 @@ public class SmokeClientTests : SmokeTestsBase var param = new PrmObjectPut( new FrostFsObjectHeader( - containerId: createdContainer, + containerId: containerId, type: FrostFsObjectType.Regular, [new FrostFsAttributePair("fileName", "test")])); @@ -335,14 +341,14 @@ public class SmokeClientTests : SmokeTestsBase var range = new FrostFsRange(64, (ulong)patch.Length); var patchParams = new PrmObjectPatch( - new FrostFsAddress(createdContainer, objectId), + new FrostFsAddress(containerId, objectId), payload: new MemoryStream(patch), maxChunkLength: 256, range: range); var newIbjId = await client.PatchObjectAsync(patchParams, default); - var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, newIbjId), default); + var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, newIbjId), default); var downloadedBytes = new byte[@object.Header.PayloadLength]; MemoryStream ms = new(downloadedBytes); @@ -380,15 +386,19 @@ public class SmokeClientTests : SmokeTestsBase await Cleanup(client); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, default); + var containerId = await client.CreateContainerAsync(createContainerParam, default); - var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); + var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); Assert.NotNull(container); + await AddObjectRules(client, containerId); + var bytes = new byte[256]; for (int i = 0; i < 256; i++) { @@ -397,7 +407,7 @@ public class SmokeClientTests : SmokeTestsBase var param = new PrmObjectPut( new FrostFsObjectHeader( - containerId: createdContainer, + containerId: containerId, type: FrostFsObjectType.Regular)); var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); @@ -405,7 +415,7 @@ public class SmokeClientTests : SmokeTestsBase await stream.WriteAsync(bytes.AsMemory()); var objectId = await stream.CompleteAsync(); - var rangeParam = new PrmRangeGet(createdContainer, objectId, new FrostFsRange(50, 100)); + var rangeParam = new PrmRangeGet(containerId, objectId, new FrostFsRange(50, 100)); var rangeReader = await client.GetRangeAsync(rangeParam, default); @@ -436,15 +446,19 @@ public class SmokeClientTests : SmokeTestsBase await Cleanup(client); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, default); + var containerId = await client.CreateContainerAsync(createContainerParam, default); - var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); + var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); Assert.NotNull(container); + await AddObjectRules(client, containerId); + var bytes = new byte[256]; for (int i = 0; i < 256; i++) { @@ -453,7 +467,7 @@ public class SmokeClientTests : SmokeTestsBase var param = new PrmObjectPut( new FrostFsObjectHeader( - containerId: createdContainer, + containerId: containerId, type: FrostFsObjectType.Regular)); var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); @@ -461,7 +475,7 @@ public class SmokeClientTests : SmokeTestsBase await stream.WriteAsync(bytes.AsMemory()); var objectId = await stream.CompleteAsync(); - var rangeParam = new PrmRangeHashGet(createdContainer, objectId, [new FrostFsRange(100, 64)], bytes); + var rangeParam = new PrmRangeHashGet(containerId, objectId, [new FrostFsRange(100, 64)], bytes); var hashes = await client.GetRangeHashAsync(rangeParam, default); @@ -493,7 +507,9 @@ public class SmokeClientTests : SmokeTestsBase var ctx = new CallContext(TimeSpan.FromSeconds(20)); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), PrmWait.DefaultParams); var container = await client.CreateContainerAsync(createContainerParam, ctx); @@ -501,6 +517,8 @@ public class SmokeClientTests : SmokeTestsBase var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container), ctx); Assert.NotNull(containerInfo); + await AddObjectRules(client, container); + var bytes = GetRandomBytes(objectSize); var param = new PrmObjectPut( @@ -573,7 +591,9 @@ public class SmokeClientTests : SmokeTestsBase await Cleanup(client); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), + new FrostFsContainerInfo( + new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), + [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), lightWait); var containerId = await client.CreateContainerAsync(createContainerParam, default); @@ -584,6 +604,8 @@ public class SmokeClientTests : SmokeTestsBase Assert.NotNull(container); + await AddObjectRules(client, containerId); + byte[] bytes = GetRandomBytes(objectSize); var param = new PrmObjectClientCutPut( @@ -595,29 +617,10 @@ public class SmokeClientTests : SmokeTestsBase var objectId = await client.PutClientCutObjectAsync(param, default).ConfigureAwait(true); - // var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); - - //bool hasObject = false; - //await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, [], filter), default)) - //{ - // hasObject = true; - - // var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId), default); - // Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); - // Assert.NotNull(objHeader.Attributes); - // Assert.Single(objHeader.Attributes); - // Assert.Equal("fileName", objHeader.Attributes[0].Key); - // Assert.Equal("test", objHeader.Attributes[0].Value); - //} - - //Assert.True(hasObject); - var filter1 = new FilterByOwnerId(FrostFsMatchType.Equals, OwnerId!); await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, [], filter1), default)) { var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId, false), default); - - var objHeader1 = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId, true), default); } var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId), default); @@ -658,7 +661,7 @@ public class SmokeClientTests : SmokeTestsBase public async void NodeInfoCallbackAndInterceptorTest() { bool callbackInvoked = false; - bool intercepterInvoked = false; + bool interceptorInvoked = false; var options = ClientOptions; options.Value.Callback = (cs) => @@ -667,21 +670,21 @@ public class SmokeClientTests : SmokeTestsBase Assert.True(cs.ElapsedMicroSeconds > 0); }; - options.Value.Interceptors.Add(new CallbackInterceptor(s => intercepterInvoked = true)); + options.Value.Interceptors.Add(new CallbackInterceptor(s => interceptorInvoked = true)); var client = FrostFSClient.GetInstance(options, GrpcChannel); var result = await client.GetNodeInfoAsync(default); Assert.True(callbackInvoked); - Assert.True(intercepterInvoked); + Assert.True(interceptorInvoked); Assert.Equal(2, result.Version.Major); Assert.Equal(13, result.Version.Minor); Assert.Equal(NodeState.Online, result.State); Assert.Equal(33, result.PublicKey.Length); - Assert.Single(result.Addresses); - Assert.Equal(9, result.Attributes.Count); + Assert.NotNull(result.Addresses); + Assert.True(result.Attributes.Count > 0); } private static byte[] GetRandomBytes(int size) @@ -701,7 +704,6 @@ public class SmokeClientTests : SmokeTestsBase }); } - static async Task Cleanup(IFrostFSClient client) { await foreach (var cid in client.ListContainersAsync(default, default)) @@ -709,4 +711,41 @@ public class SmokeClientTests : SmokeTestsBase await client.DeleteContainerAsync(new PrmContainerDelete(cid, lightWait), default); } } + + private static async Task AddObjectRules(IFrostFSClient client, FrostFsContainerId containerId) + { + var addChainPrm = new PrmApeChainAdd( + new FrostFsChainTarget(FrostFsTargetType.Container, containerId.GetValue()), + new FrostFsChain + { + ID = Encoding.ASCII.GetBytes("chain-id-test"), + Rules = [ + new FrostFsRule + { + Status = RuleStatus.Allow, + Actions = new Actions(inverted: false, names: ["*"]), + Resources = new Resource (inverted: false, names: [$"native:object/*"]), + Any = false, + Conditions = [] + } + ], + MatchType = RuleMatchType.DenyPriority + } + ); + + await client.AddChainAsync(addChainPrm, default); + + var listChainPrm = new PrmApeChainList(new FrostFsChainTarget(FrostFsTargetType.Container, containerId.GetValue())); + + while (true) + { + await Task.Delay(1000); + var chains = await client.ListChainAsync(listChainPrm, default); + + if (chains.Length > 0) + break; + } + + await Task.Delay(8000); + } } diff --git a/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs b/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs index 5a9942d..95e353a 100644 --- a/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs +++ b/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs @@ -11,9 +11,15 @@ namespace FrostFS.SDK.Tests.Smoke; public abstract class SmokeTestsBase { - internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK"; + // cluster + internal readonly string url = "http://10.78.128.190:8080"; + internal readonly string keyString = "L47c3bunc6bJd7uEAfPUae2VkyupFR9nizoH6jfPonzQxijqH2Ba"; - internal readonly string url = "http://172.23.32.4:8080"; + // WSL2 + //internal readonly string url = "http://172.29.238.97:8080"; + //internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK"; + + //"KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; protected ECDsa? Key { get; } diff --git a/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs b/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs index 98d3ee9..fdf7ffe 100644 --- a/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs +++ b/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs @@ -35,7 +35,7 @@ public class PlacementVectorTests(ITestOutputHelper testOutputHelper) //if (!file.EndsWith("selector_invalid.json")) // continue; - var fileName = file[(file.LastIndexOf("..\\") + 3)..]; + var fileName = file[(file.LastIndexOf("..\\", StringComparison.OrdinalIgnoreCase) + 3)..]; _testOutputHelper.WriteLine($"Open file {fileName}"); var str = File.ReadAllText(file); -- 2.45.3