Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions foreign/csharp/Iggy_SDK.Tests.Integration/SystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ public async Task Ping_Should_Pong(Protocol protocol)
await Should.NotThrowAsync(Fixture.Clients[protocol].PingAsync());
}

[Test]
[DependsOn(nameof(Ping_Should_Pong))]
[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
public async Task GetSnapshot_Should_Return_ValidZipData(Protocol protocol)
{
var snapshot = await Fixture.Clients[protocol].GetSnapshotAsync(
SnapshotCompression.Deflated,
[SystemSnapshotType.Test]);

snapshot.ShouldNotBeNull();
snapshot.Length.ShouldBeGreaterThan(0);

// Verify it's a valid ZIP archive by checking the ZIP magic bytes (PK\x03\x04)
snapshot[0].ShouldBe((byte)'P');
snapshot[1].ShouldBe((byte)'K');
}

// [Test]
// [DependsOn(nameof(Ping_Should_Pong))]
// [MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
Expand Down
15 changes: 15 additions & 0 deletions foreign/csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,21 @@ private static byte GetPollingStrategyByte(MessagePolling pollingStrategy)
};
}

internal static byte[] GetSnapshot(SnapshotCompression compression, IList<SystemSnapshotType> snapshotTypes)
{
// Binary format: [compression_code: u8] [types_count: u8] [type_code_1: u8] [type_code_2: u8] ...
var length = 1 + 1 + snapshotTypes.Count;
Span<byte> bytes = stackalloc byte[length];
bytes[0] = (byte)compression;
bytes[1] = (byte)snapshotTypes.Count;
for (var i = 0; i < snapshotTypes.Count; i++)
{
bytes[2 + i] = (byte)snapshotTypes[i];
}

return bytes.ToArray();
}

internal static byte[] DeleteOffset(Identifier streamId, Identifier topicId, Consumer consumer, uint? partitionId)
{
Span<byte> bytes =
Expand Down
12 changes: 6 additions & 6 deletions foreign/csharp/Iggy_SDK/Enums/SnapshotCompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ namespace Apache.Iggy.Enums;
public enum SnapshotCompression
{
/// Store the file as is
Stored,
Stored = 1,

/// Compress the file using Deflate
Deflated,
Deflated = 2,

/// Compress the file using BZIP2
Bzip2,
Bzip2 = 3,

/// Compress the file using ZStandard
Zstd,
Zstd = 4,

/// Compress the file using LZMA
Lzma,
Lzma = 5,

/// Compress the file using XZ
Xz
Xz = 6
}
14 changes: 7 additions & 7 deletions foreign/csharp/Iggy_SDK/Enums/SystemSnapshotType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,23 @@ namespace Apache.Iggy.Enums;
public enum SystemSnapshotType
{
/// Overview of the filesystem.
FilesystemOverview,
FilesystemOverview = 1,

/// List of currently running processes.
ProcessList,
ProcessList = 2,

/// Resource usage statistics of the system.
ResourceUsage,
ResourceUsage = 3,

/// Test snapshot type for development purposes.
Test,
Test = 4,

/// Server logs
ServerLogs,
ServerLogs = 5,

/// Server configuration
ServerConfig,
ServerConfig = 6,

/// Everything
All
All = 100
}
16 changes: 16 additions & 0 deletions foreign/csharp/Iggy_SDK/IggyClient/IIggySystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// under the License.

using Apache.Iggy.Contracts;
using Apache.Iggy.Enums;

namespace Apache.Iggy.IggyClient;

Expand Down Expand Up @@ -90,4 +91,19 @@ public interface IIggySystem
/// <param name="token">The cancellation token to cancel the operation.</param>
/// <returns>A task representing the asynchronous operation.</returns>
Task PingAsync(CancellationToken token = default);

/// <summary>
/// Captures and packages the current system state as a snapshot.
/// </summary>
/// <remarks>
/// The snapshot is returned as raw bytes of a ZIP archive. The contents depend on
/// the requested snapshot types (filesystem overview, process list, resource usage, etc.).
/// Authentication is required.
/// </remarks>
/// <param name="compression">The compression method to use for the snapshot archive.</param>
/// <param name="snapshotTypes">The types of system information to include in the snapshot.</param>
/// <param name="token">The cancellation token to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation and returns the snapshot as raw bytes of a ZIP archive.</returns>
Task<byte[]> GetSnapshotAsync(SnapshotCompression compression, IList<SystemSnapshotType> snapshotTypes,
CancellationToken token = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,31 @@ public async Task PingAsync(CancellationToken token = default)
}
}

/// <inheritdoc />
public async Task<byte[]> GetSnapshotAsync(SnapshotCompression compression,
IList<SystemSnapshotType> snapshotTypes, CancellationToken token = default)
{
// Rust serde uses default derive (PascalCase) for these enums, not snake_case.
// We use .ToString() to produce PascalCase names matching Rust's serde expectations.
var request = new
{
compression = compression.ToString(),
snapshot_types = snapshotTypes.Select(t => t.ToString()).ToList()
};
var json = JsonSerializer.Serialize(request, _jsonSerializerOptions);
var data = new StringContent(json, Encoding.UTF8, "application/json");

var response = await _httpClient.PostAsync("/snapshot", data, token);

if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsByteArrayAsync(token);
}

await HandleResponseAsync(response);
return [];
}

/// <inheritdoc />
public Task ConnectAsync(CancellationToken token = default)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,17 @@ public async Task PingAsync(CancellationToken token = default)
await SendWithResponseAsync(payload, token);
}

/// <inheritdoc />
public async Task<byte[]> GetSnapshotAsync(SnapshotCompression compression,
IList<SystemSnapshotType> snapshotTypes, CancellationToken token = default)
{
var message = TcpContracts.GetSnapshot(compression, snapshotTypes);
var payload = new byte[4 + BufferSizes.INITIAL_BYTES_LENGTH + message.Length];
TcpMessageStreamHelpers.CreatePayload(payload, message, CommandCodes.GET_SNAPSHOT_CODE);

return await SendWithResponseAsync(payload, token);
}

/// <inheritdoc />
public async Task ConnectAsync(CancellationToken token = default)
{
Expand Down