From 195854a45b8bc92670bfc6117b190da38c4d8567 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Wed, 12 Feb 2025 04:20:01 +0300
Subject: [PATCH] [#30] Client: Add object model for Rules
Signed-off-by: Pavel Gross
---
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
+{
+ 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
+{
+ 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
+{
+ 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;
+
+ ///
+ /// maxSliceLen taken from https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77
+ ///
+ 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[] slice, Func 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(byte[] buf, int offset, T[] slice, Func 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
-{
- 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
{
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
{
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
{
public FrostFsChainTarget Target { get; } = target;
- public FrostFsChain Chain { get; } = chain;
+ public byte[] ChainId { get; } = chainId;
///
/// 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> 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 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);
+ }
+ }
}
///
@@ -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);