diff --git a/src/PodmanClient/PodmanClientDotNet.csproj b/src/PodmanClient/PodmanClientDotNet.csproj
index da9dc2b..46b0db0 100644
--- a/src/PodmanClient/PodmanClientDotNet.csproj
+++ b/src/PodmanClient/PodmanClientDotNet.csproj
@@ -8,7 +8,7 @@
PodmanClient.DotNet
- 1.0.2
+ 1.0.3
Maksym Sadovnychyy
MAKS-IT
PodmanClient
diff --git a/src/PodmanClientDotNet.Tests/Archives/Tar.cs b/src/PodmanClientDotNet.Tests/Archives/Tar.cs
new file mode 100644
index 0000000..8dd6dbe
--- /dev/null
+++ b/src/PodmanClientDotNet.Tests/Archives/Tar.cs
@@ -0,0 +1,54 @@
+using ICSharpCode.SharpZipLib.Tar;
+
+namespace MaksIT.PodmanClientDotNet.Tests.Archives
+{
+ public static class Tar
+ {
+ public static void CreateTarFromDirectory(string sourceDirectory, Stream outputStream)
+ {
+ using (var tarOutputStream = new TarOutputStream(outputStream))
+ {
+ tarOutputStream.IsStreamOwner = false;
+ AddDirectoryFilesToTar(tarOutputStream, sourceDirectory, true);
+ }
+ }
+
+ static void AddDirectoryFilesToTar(TarOutputStream tarOutputStream, string sourceDirectory, bool recursive, string baseDirectory = null)
+ {
+ // If baseDirectory is null, set it to the sourceDirectory to start with
+ if (baseDirectory == null)
+ {
+ baseDirectory = sourceDirectory;
+ }
+
+ var directoryInfo = new DirectoryInfo(sourceDirectory);
+
+ foreach (var fileInfo in directoryInfo.GetFiles())
+ {
+ // Calculate the relative path for the file within the base directory
+ string relativePath = Path.GetRelativePath(baseDirectory, fileInfo.FullName);
+
+ // Create tar entry with the relative path
+ var entry = TarEntry.CreateEntryFromFile(fileInfo.FullName);
+ entry.Name = relativePath.Replace(Path.DirectorySeparatorChar, '/'); // Use Unix-style path separators
+ tarOutputStream.PutNextEntry(entry);
+
+ using (var fileStream = fileInfo.OpenRead())
+ {
+ fileStream.CopyTo(tarOutputStream);
+ }
+
+ tarOutputStream.CloseEntry();
+ }
+
+ if (recursive)
+ {
+ foreach (var subDirectory in directoryInfo.GetDirectories())
+ {
+ // Recurse into subdirectories, passing the base directory
+ AddDirectoryFilesToTar(tarOutputStream, subDirectory.FullName, true, baseDirectory);
+ }
+ }
+ }
+ }
+}
diff --git a/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs b/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs
index aceceb8..8b1e257 100644
--- a/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs
+++ b/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
-using Xunit;
-using System.Threading.Tasks;
+
+using MaksIT.PodmanClientDotNet.Tests.Archives;
namespace MaksIT.PodmanClientDotNet.Tests {
public class PodmanClientContainersTests {
@@ -15,11 +15,11 @@ namespace MaksIT.PodmanClientDotNet.Tests {
_client = new PodmanClient(logger, "http://wks0002.corp.maks-it.com:8080", 60);
}
- #region Success
+ #region Success Cases
[Fact]
public async Task PodmanClient_ContainerLifecycle_Success() {
// Arrange
- string containerName = "test-container";
+ string containerName = $"podman-client-test-{Guid.NewGuid()}";
string image = "alpine:latest";
// Act & Assert
@@ -30,20 +30,54 @@ namespace MaksIT.PodmanClientDotNet.Tests {
await ForceDeleteContainerAsync(containerId);
}
+ [Fact]
+ public async Task CopyFilesToContainer_Success() {
+ // Arrange
+ string containerName = $"podman-client-test-{Guid.NewGuid()}";
+ string image = "alpine:latest";
+ string pathInContainer = "/podman-test-copy";
+
+ // Create temporary folder with random files
+ string tempFolderPath = CreateTemporaryFolderWithFiles();
+
+ try {
+ // Act
+ await PullImageAsync(image);
+ var containerId = await CreateContainerAsync(containerName, image);
+ await StartContainerAsync(containerId);
+
+ // Archive the folder and copy to container
+ using (var tarStream = CreateTarStream(tempFolderPath)) {
+ await CopyToContainerAsync(containerId, tarStream, pathInContainer);
+ }
+
+ // Stop and delete the container
+ await StopContainerAsync(containerId);
+ await ForceDeleteContainerAsync(containerId);
+ }
+ finally {
+ // Cleanup: Delete temporary folder
+ if (Directory.Exists(tempFolderPath)) {
+ Directory.Delete(tempFolderPath, true);
+ }
+ }
+ }
+ #endregion
+
+ #region Helper Methods
private async Task PullImageAsync(string image) {
- // Implement the logic to pull the image
var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(image));
Assert.Null(exception); // Expect no exceptions if the pull was successful
}
private async Task CreateContainerAsync(string containerName, string image) {
var createResponse = await _client.CreateContainerAsync(
- name: containerName,
- image: image,
- command: new List {
- "sh", "-c",
- "sleep infinity"
- });
+ name: containerName,
+ image: image,
+ command: new List {
+ "sh", "-c",
+ "sleep infinity"
+ });
Assert.NotNull(createResponse);
Assert.False(string.IsNullOrEmpty(createResponse.Id)); // Ensure a valid container ID is returned
return createResponse.Id;
@@ -63,42 +97,51 @@ namespace MaksIT.PodmanClientDotNet.Tests {
var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(containerId));
Assert.Null(exception); // Expect no exceptions if the container was deleted successfully
}
+
+ private async Task CopyToContainerAsync(string containerId, Stream tarStream, string path) {
+ var exception = await Record.ExceptionAsync(() => _client.ExtractArchiveToContainerAsync(containerId, tarStream, path));
+ Assert.Null(exception); // Expect no exceptions if the copy was successful
+ }
+
+ private string CreateTemporaryFolderWithFiles() {
+ string tempFolder = Path.Combine(Path.GetTempPath(), $"podman-test-{Guid.NewGuid()}");
+ Directory.CreateDirectory(tempFolder);
+
+ // Create some random files
+ for (int i = 0; i < 5; i++) {
+ File.WriteAllText(Path.Combine(tempFolder, $"test-file-{i}.txt"), $"This is test file {i}");
+ }
+
+ return tempFolder;
+ }
+
+ private Stream CreateTarStream(string folderPath) {
+ var memoryStream = new MemoryStream();
+ Tar.CreateTarFromDirectory(folderPath, memoryStream);
+ memoryStream.Seek(0, SeekOrigin.Begin); // Reset the stream position for reading
+ return memoryStream;
+ }
#endregion
- #region Fail
+ #region Fail Cases
[Fact]
public async Task StartContainerAsync_Should_HandleErrors() {
- // Arrange
string invalidContainerId = "invalid-container-id";
-
- // Act
var exception = await Record.ExceptionAsync(() => _client.StartContainerAsync(invalidContainerId));
-
- // Assert
Assert.NotNull(exception); // Expect an exception due to invalid container ID
}
[Fact]
public async Task StopContainerAsync_Should_HandleErrors() {
- // Arrange
string invalidContainerId = "invalid-container-id";
-
- // Act
var exception = await Record.ExceptionAsync(() => _client.StopContainerAsync(invalidContainerId));
-
- // Assert
Assert.NotNull(exception); // Expect an exception due to invalid container ID
}
[Fact]
public async Task ForceDeleteContainerAsync_Should_HandleErrors() {
- // Arrange
string invalidContainerId = "invalid-container-id";
-
- // Act
var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(invalidContainerId));
-
- // Assert
Assert.NotNull(exception); // Expect an exception due to invalid container ID
}
#endregion
diff --git a/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj b/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj
index 17f4ec6..06fff20 100644
--- a/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj
+++ b/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj
@@ -16,6 +16,7 @@
+
all
diff --git a/src/PodmanClientDotNet.Tests/PodmanClientExecTests.cs b/src/PodmanClientDotNet.Tests/PodmanClientExecTests.cs
new file mode 100644
index 0000000..dc0c396
--- /dev/null
+++ b/src/PodmanClientDotNet.Tests/PodmanClientExecTests.cs
@@ -0,0 +1,193 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Xunit;
+
+namespace MaksIT.PodmanClientDotNet.Tests {
+ public class PodmanClientExecTests {
+ private readonly PodmanClient _client;
+
+ public PodmanClientExecTests() {
+ var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
+ var logger = loggerFactory.CreateLogger();
+
+ _client = new PodmanClient(logger, "http://wks0002.corp.maks-it.com:8080", 60);
+ }
+
+ #region Success Cases
+
+ [Fact]
+ public async Task Full_ContainerLifecycle_With_Exec_Should_Succeed() {
+ // Arrange
+ string containerName = $"podman-client-test-{Guid.NewGuid()}";
+ string image = "alpine:latest";
+
+ // Act & Assert
+ // 1. Pull the image
+ await PullImageAsync(image);
+
+ // 2. Create the container with sleep infinity command
+ var containerId = await CreateContainerAsync(containerName, image);
+
+ // 3. Start the container
+ await StartContainerAsync(containerId);
+
+ // 4. Execute a command in the container to install a package (e.g., "apk add curl")
+ var execId = await CreateExecAsync(containerName, new[] { "apk", "add", "--no-cache", "curl" });
+ await StartExecAsync(execId);
+
+ // 5. Stop the container
+ await StopContainerAsync(containerId);
+
+ // 6. Delete the container
+ await ForceDeleteContainerAsync(containerId);
+ }
+
+ #endregion
+
+ #region Helper Methods
+
+ private async Task PullImageAsync(string image) {
+ var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(image));
+ Assert.Null(exception); // Expect no exceptions if the pull was successful
+ }
+
+ private async Task CreateContainerAsync(string containerName, string image) {
+ var createResponse = await _client.CreateContainerAsync(
+ name: containerName,
+ image: image,
+ command: new List {
+ "sh", "-c",
+ "sleep infinity"
+ });
+ Assert.NotNull(createResponse);
+ Assert.False(string.IsNullOrEmpty(createResponse.Id)); // Ensure a valid container ID is returned
+ return createResponse.Id;
+ }
+
+ private async Task StartContainerAsync(string containerId) {
+ var exception = await Record.ExceptionAsync(() => _client.StartContainerAsync(containerId));
+ Assert.Null(exception); // Expect no exceptions if the container was started successfully
+ }
+
+ private async Task CreateExecAsync(string containerName, string[] cmd) {
+ var execResponse = await _client.CreateExecAsync(containerName, cmd);
+ Assert.NotNull(execResponse);
+ Assert.False(string.IsNullOrEmpty(execResponse.Id)); // Ensure a valid exec ID is returned
+ return execResponse.Id;
+ }
+
+ private async Task StartExecAsync(string execId) {
+ var exception = await Record.ExceptionAsync(() => _client.StartExecAsync(execId));
+ Assert.Null(exception); // Expect no exceptions if the exec command was started successfully
+ }
+
+ private async Task StopContainerAsync(string containerId) {
+ var exception = await Record.ExceptionAsync(() => _client.StopContainerAsync(containerId));
+ Assert.Null(exception); // Expect no exceptions if the container was stopped successfully
+ }
+
+ private async Task ForceDeleteContainerAsync(string containerId) {
+ var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(containerId));
+ Assert.Null(exception); // Expect no exceptions if the container was deleted successfully
+ }
+
+ #endregion
+
+ #region Fail Cases
+
+ [Fact]
+ public async Task PullImageAsync_Should_HandleErrors() {
+ // Arrange
+ string invalidImageReference = "invalidimage:latest"; // Intentionally wrong image
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(invalidImageReference));
+
+ // Assert
+ Assert.NotNull(exception);
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ [Fact]
+ public async Task CreateContainerAsync_Should_HandleErrors() {
+ // Arrange
+ string invalidImage = "invalidimage:latest"; // Intentionally wrong image
+ string containerName = "test-container";
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.CreateContainerAsync(containerName, invalidImage, new List { "sh", "-c", "sleep infinity" }));
+
+ // Assert
+ Assert.NotNull(exception);
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ [Fact]
+ public async Task StartContainerAsync_Should_HandleErrors() {
+ // Arrange
+ string invalidContainerId = "invalid-container-id"; // Intentionally wrong container ID
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.StartContainerAsync(invalidContainerId));
+
+ // Assert
+ Assert.NotNull(exception);
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ [Fact]
+ public async Task CreateExecAsync_Should_HandleErrors() {
+ // Arrange
+ string containerName = "invalid-container"; // Intentionally wrong container name
+ var cmd = new[] { "apk", "add", "--no-cache", "curl" };
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.CreateExecAsync(containerName, cmd));
+
+ // Assert
+ Assert.NotNull(exception);
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ [Fact]
+ public async Task StartExecAsync_Should_HandleErrors() {
+ // Arrange
+ string invalidExecId = "invalid-exec-id"; // Intentionally wrong exec ID
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.StartExecAsync(invalidExecId));
+
+ // Assert
+ Assert.NotNull(exception);
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ [Fact]
+ public async Task StopContainerAsync_Should_HandleErrors() {
+ // Arrange
+ string invalidContainerId = "invalid-container-id"; // Intentionally wrong container ID
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.StopContainerAsync(invalidContainerId));
+
+ // Assert
+ Assert.NotNull(exception);
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ [Fact]
+ public async Task ForceDeleteContainerAsync_Should_HandleErrors() {
+ // Arrange
+ string invalidContainerId = "invalid-container-id"; // Intentionally wrong container ID
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(invalidContainerId));
+
+ // Assert
+ Assert.NotNull(exception);
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ #endregion
+ }
+}
diff --git a/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs b/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs
index 72fe949..1a87e41 100644
--- a/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs
+++ b/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs
@@ -15,6 +15,8 @@ public class PodmanClientImagesTests {
_client = new PodmanClient(logger, "http://wks0002.corp.maks-it.com:8080", 60);
}
+ #region Success Cases
+
[Fact]
public async Task PodmanClient_IntegrationTests() {
// Test 1: Pull Image - Success
@@ -23,6 +25,9 @@ public class PodmanClientImagesTests {
// Test 2: Tag Image - Success
await TagImageAsync_Should_Succeed();
}
+ #endregion
+
+ #region Helper Methods
private async Task PullImageAsync_Should_Succeed() {
// Arrange
@@ -47,7 +52,9 @@ public class PodmanClientImagesTests {
// Assert
Assert.Null(exception); // Expect no exceptions if the tagging was successful
}
+ #endregion
+ #region Fail Cases
[Fact]
public async Task PodmanClient_PullImage_Errors() {
@@ -76,4 +83,5 @@ public class PodmanClientImagesTests {
Assert.NotNull(exception); // Expect an exception due to nonexistent image
Assert.IsType(exception); // Ensure it's the expected type
}
+ #endregion
}