diff --git a/src/PodmanClient/Models/Image/ImagePullStatusResponse.cs b/src/PodmanClient/Models/Image/ImagePullStatusResponse.cs
deleted file mode 100644
index 7ebccb8..0000000
--- a/src/PodmanClient/Models/Image/ImagePullStatusResponse.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MaksIT.PodmanClientDotNet.Models.Image {
- public class ImagePullStatusResponse {
- public string Status { get; set; }
- public string Id { get; set; }
- public string Progress { get; set; }
- public ProgressDetail ProgressDetail { get; set; }
- }
-}
diff --git a/src/PodmanClient/Models/Image/ImagePullStreamResponse.cs b/src/PodmanClient/Models/Image/ImagePullStreamResponse.cs
deleted file mode 100644
index 9f4ff21..0000000
--- a/src/PodmanClient/Models/Image/ImagePullStreamResponse.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MaksIT.PodmanClientDotNet.Models.Image {
- public class ImagePullStreamResponse {
- public string Stream { get; set; }
- }
-}
diff --git a/src/PodmanClient/Models/Image/PullImageResponse.cs b/src/PodmanClient/Models/Image/PullImageResponse.cs
new file mode 100644
index 0000000..576ac8e
--- /dev/null
+++ b/src/PodmanClient/Models/Image/PullImageResponse.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MaksIT.PodmanClientDotNet.Models.Image
+{
+ public class PullImageResponse
+ {
+
+ ///
+ /// Error contains text of errors from c/image.
+ ///
+ public string Error { get; set; }
+
+ ///
+ /// ID contains image ID (retained for backwards compatibility).
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// Images contains the IDs of the images pulled.
+ ///
+ public List Images { get; set; }
+
+ ///
+ /// Stream used to provide output from c/image.
+ ///
+ public string Stream { get; set; }
+ }
+}
diff --git a/src/PodmanClient/PodmanClient.cs b/src/PodmanClient/PodmanClient.cs
new file mode 100644
index 0000000..74c2a9e
--- /dev/null
+++ b/src/PodmanClient/PodmanClient.cs
@@ -0,0 +1,61 @@
+using Microsoft.Extensions.Logging;
+
+namespace MaksIT.PodmanClientDotNet {
+ public partial class PodmanClient {
+ private readonly ILogger _logger;
+ private readonly HttpClient _httpClient;
+
+ private const string _apiVersion = "v1.41";
+
+ ///
+ /// Initializes a new instance of the class using the specified base address and timeout.
+ ///
+ /// The logger instance used for logging within the client.
+ /// The base address of the Podman service.
+ /// The timeout period in minutes for HTTP requests.
+ public PodmanClient(
+ ILogger logger,
+ string baseAddress,
+ int timeOut = 60
+ ) : this(
+ logger,
+ baseAddress,
+ new HttpClient {
+ Timeout = TimeSpan.FromMinutes(timeOut)
+ }
+ ) { }
+
+ ///
+ /// Initializes a new instance of the class using the provided instance.
+ ///
+ /// The logger instance used for logging within the client.
+ /// An existing instance configured for use with the Podman service.
+ /// Thrown when the logger or httpClient parameter is null.
+ public PodmanClient(
+ ILogger logger,
+ string serverUrl,
+ HttpClient httpClient
+ ) {
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ if (serverUrl == null)
+ throw new ArgumentNullException(nameof(serverUrl));
+
+ _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
+
+ ConfigureHttpClient(serverUrl);
+ }
+
+ ///
+ /// Configures the default settings for the used by this instance.
+ /// Ensures that the "Accept" header is set to "application/json".
+ ///
+ private void ConfigureHttpClient(string baseAddress) {
+ _httpClient.BaseAddress = new Uri(baseAddress);
+
+ if (_httpClient.DefaultRequestHeaders.Contains("Accept"))
+ _httpClient.DefaultRequestHeaders.Remove("Accept");
+
+ _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
+ }
+ }
+}
diff --git a/src/PodmanClient/PodmanConnectorContainer.cs b/src/PodmanClient/PodmanClientContainer.cs
similarity index 78%
rename from src/PodmanClient/PodmanConnectorContainer.cs
rename to src/PodmanClient/PodmanClientContainer.cs
index 80e7890..a02b8cd 100644
--- a/src/PodmanClient/PodmanConnectorContainer.cs
+++ b/src/PodmanClient/PodmanClientContainer.cs
@@ -1,10 +1,12 @@
using System.Text;
using System.Text.Json;
-using MaksIT.PodmanClientDotNet.Extensions;
+using Microsoft.Extensions.Logging;
using MaksIT.PodmanClientDotNet.Models;
using MaksIT.PodmanClientDotNet.Models.Container;
+using MaksIT.PodmanClientDotNet.Extensions;
+
namespace MaksIT.PodmanClientDotNet {
public partial class PodmanClient {
@@ -246,31 +248,31 @@ namespace MaksIT.PodmanClientDotNet {
};
var jsonContent = new StringContent(JsonSerializer.Serialize(createContainerParameters), Encoding.UTF8, "application/json");
- var response = await _httpClient.PostAsync("/v1.41/libpod/containers/create", jsonContent);
+ var response = await _httpClient.PostAsync($"/{_apiVersion}/libpod/containers/create", jsonContent);
if (response.IsSuccessStatusCode) {
var jsonResponse = await response.Content.ReadAsStringAsync();
- return JsonSerializer.Deserialize(jsonResponse);
+ return jsonResponse.ToObject();
}
else {
var errorContent = await response.Content.ReadAsStringAsync();
- var errorDetails = JsonSerializer.Deserialize(errorContent);
+ var errorDetails = errorContent.ToObject();
switch (response.StatusCode) {
case System.Net.HttpStatusCode.BadRequest:
- Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
+ _logger.LogError($"Bad parameter in request: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such container: {errorDetails?.Message}");
+ _logger.LogError($"No such container: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.Conflict:
- Console.WriteLine($"Conflict error in operation: {errorDetails?.Message}");
+ _logger.LogError($"Conflict error in operation: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorDetails?.Message}");
+ _logger.LogError($"Internal server error: {errorDetails?.Message}");
break;
default:
- Console.WriteLine($"Error creating container: {errorDetails?.Message}");
+ _logger.LogError($"Error creating container: {errorDetails?.Message}");
break;
}
@@ -284,34 +286,34 @@ namespace MaksIT.PodmanClientDotNet {
public async Task StartContainerAsync(string containerId, string detachKeys = "ctrl-p,ctrl-q") {
var response = await _httpClient.PostAsync(
- $"/v1.41/libpod/containers/{containerId}/start?detachKeys={Uri.EscapeDataString(detachKeys)}", null);
+ $"/{_apiVersion}/libpod/containers/{containerId}/start?detachKeys={Uri.EscapeDataString(detachKeys)}", null);
switch (response.StatusCode) {
case System.Net.HttpStatusCode.NoContent:
- Console.WriteLine("Container started successfully.");
+ _logger.LogInformation("Container started successfully.");
break;
case System.Net.HttpStatusCode.NotModified:
- Console.WriteLine("Container was already started.");
+ _logger.LogWarning("Container was already started.");
break;
case System.Net.HttpStatusCode.NotFound:
var errorContent404 = await response.Content.ReadAsStringAsync();
var errorDetails404 = errorContent404.ToObject();
- Console.WriteLine($"Container not found: {errorDetails404?.Message}");
+ _logger.LogError($"Container not found: {errorDetails404?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
var errorContent500 = await response.Content.ReadAsStringAsync();
var errorDetails500 = errorContent500.ToObject();
- Console.WriteLine($"Internal server error: {errorDetails500?.Message}");
+ _logger.LogError($"Internal server error: {errorDetails500?.Message}");
break;
default:
if ((int)response.StatusCode >= 400) {
var errorContent = await response.Content.ReadAsStringAsync();
var errorDetails = errorContent.ToObject();
- Console.WriteLine($"Error starting container: {errorDetails?.Message}");
+ _logger.LogError($"Error starting container: {errorDetails?.Message}");
}
break;
}
@@ -321,14 +323,14 @@ namespace MaksIT.PodmanClientDotNet {
public async Task StopContainerAsync(string containerId, int timeout = 10, bool ignoreAlreadyStopped = false) {
var queryParams = $"?timeout={timeout}&Ignore={ignoreAlreadyStopped.ToString().ToLower()}";
- var response = await _httpClient.PostAsync($"/v1.41/libpod/containers/{containerId}/stop{queryParams}", null);
+ var response = await _httpClient.PostAsync($"/{_apiVersion}/libpod/containers/{containerId}/stop{queryParams}", null);
if (response.IsSuccessStatusCode) {
if (response.StatusCode == System.Net.HttpStatusCode.NoContent) {
- Console.WriteLine("Container stopped successfully.");
+ _logger.LogInformation("Container stopped successfully.");
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotModified) {
- Console.WriteLine("Container was already stopped.");
+ _logger.LogWarning("Container was already stopped.");
}
}
else {
@@ -337,15 +339,15 @@ namespace MaksIT.PodmanClientDotNet {
switch (response.StatusCode) {
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such container: {errorDetails?.Message}");
+ _logger.LogError($"No such container: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorDetails?.Message}");
+ _logger.LogError($"Internal server error: {errorDetails?.Message}");
break;
default:
- Console.WriteLine($"Error stopping container: {errorDetails?.Message}");
+ _logger.LogError($"Error stopping container: {errorDetails?.Message}");
break;
}
@@ -355,49 +357,49 @@ namespace MaksIT.PodmanClientDotNet {
public async Task ForceDeleteContainerAsync(string containerId, bool deleteVolumes = false, int timeout = 10) {
var queryParams = $"?force=true&v={deleteVolumes.ToString().ToLower()}&timeout={timeout}";
- var response = await _httpClient.DeleteAsync($"/v1.41/libpod/containers/{containerId}{queryParams}");
+ var response = await _httpClient.DeleteAsync($"/{_apiVersion}/libpod/containers/{containerId}{queryParams}");
if (response.IsSuccessStatusCode) {
if (response.StatusCode == System.Net.HttpStatusCode.NoContent) {
- Console.WriteLine("Container force deleted successfully.");
+ _logger.LogInformation("Container force deleted successfully.");
}
else if (response.StatusCode == System.Net.HttpStatusCode.OK) {
var responseContent = await response.Content.ReadAsStringAsync();
- var deleteResponses = JsonSerializer.Deserialize(responseContent);
+ var deleteResponses = responseContent.ToObject();
foreach (var deleteResponse in deleteResponses) {
if (string.IsNullOrEmpty(deleteResponse.Err)) {
- Console.WriteLine($"Container {deleteResponse.Id} deleted successfully.");
+ _logger.LogInformation($"Container {deleteResponse.Id} deleted successfully.");
}
else {
- Console.WriteLine($"Error deleting container {deleteResponse.Id}: {deleteResponse.Err}");
+ _logger.LogError($"Error deleting container {deleteResponse.Id}: {deleteResponse.Err}");
}
}
}
}
else {
var errorContent = await response.Content.ReadAsStringAsync();
- var errorDetails = JsonSerializer.Deserialize(errorContent);
+ var errorDetails = errorContent.ToObject();
switch (response.StatusCode) {
case System.Net.HttpStatusCode.BadRequest:
- Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
+ _logger.LogError($"Bad parameter in request: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such container: {errorDetails?.Message}");
+ _logger.LogError($"No such container: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.Conflict:
- Console.WriteLine($"Conflict error: {errorDetails?.Message}");
+ _logger.LogError($"Conflict error: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorDetails?.Message}");
+ _logger.LogError($"Internal server error: {errorDetails?.Message}");
break;
default:
- Console.WriteLine($"Error deleting container: {errorDetails?.Message}");
+ _logger.LogError($"Error deleting container: {errorDetails?.Message}");
break;
}
@@ -407,11 +409,11 @@ namespace MaksIT.PodmanClientDotNet {
public async Task DeleteContainerAsync(string containerId, bool depend = false, bool ignore = false, int timeout = 10) {
var queryParams = $"?depend={depend.ToString().ToLower()}&ignore={ignore.ToString().ToLower()}&timeout={timeout}";
- var response = await _httpClient.DeleteAsync($"/v1.41/libpod/containers/{containerId}{queryParams}");
+ var response = await _httpClient.DeleteAsync($"/libpod/containers/{containerId}{queryParams}");
if (response.IsSuccessStatusCode) {
if (response.StatusCode == System.Net.HttpStatusCode.NoContent) {
- Console.WriteLine("Container deleted successfully.");
+ _logger.LogInformation("Container deleted successfully.");
}
else if (response.StatusCode == System.Net.HttpStatusCode.OK) {
var responseContent = await response.Content.ReadAsStringAsync();
@@ -419,10 +421,10 @@ namespace MaksIT.PodmanClientDotNet {
foreach (var deleteResponse in deleteResponses) {
if (string.IsNullOrEmpty(deleteResponse.Err)) {
- Console.WriteLine($"Container {deleteResponse.Id} deleted successfully.");
+ _logger.LogInformation($"Container {deleteResponse.Id} deleted successfully.");
}
else {
- Console.WriteLine($"Error deleting container {deleteResponse.Id}: {deleteResponse.Err}");
+ _logger.LogInformation($"Error deleting container {deleteResponse.Id}: {deleteResponse.Err}");
}
}
}
@@ -433,23 +435,23 @@ namespace MaksIT.PodmanClientDotNet {
switch (response.StatusCode) {
case System.Net.HttpStatusCode.BadRequest:
- Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
+ _logger.LogInformation($"Bad parameter in request: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such container: {errorDetails?.Message}");
+ _logger.LogInformation($"No such container: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.Conflict:
- Console.WriteLine($"Conflict error: {errorDetails?.Message}");
+ _logger.LogInformation($"Conflict error: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorDetails?.Message}");
+ _logger.LogInformation($"Internal server error: {errorDetails?.Message}");
break;
default:
- Console.WriteLine($"Error deleting container: {errorDetails?.Message}");
+ _logger.LogInformation($"Error deleting container: {errorDetails?.Message}");
break;
}
@@ -464,34 +466,34 @@ namespace MaksIT.PodmanClientDotNet {
content.Headers.Add("Content-Type", "application/x-tar");
var queryParams = $"?path={Uri.EscapeDataString(path)}&pause={pause.ToString().ToLower()}";
- var response = await _httpClient.PutAsync($"/v1.41/libpod/containers/{containerId}/archive{queryParams}", content);
+ var response = await _httpClient.PutAsync($"/{_apiVersion}/libpod/containers/{containerId}/archive{queryParams}", content);
if (response.IsSuccessStatusCode) {
- Console.WriteLine("Files copied successfully to the container.");
+ _logger.LogInformation("Files copied successfully to the container.");
}
else {
var errorContent = await response.Content.ReadAsStringAsync();
- var errorDetails = JsonSerializer.Deserialize(errorContent);
+ var errorDetails = errorContent.ToObject();
switch (response.StatusCode) {
case System.Net.HttpStatusCode.BadRequest:
- Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
+ _logger.LogError($"Bad parameter in request: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.Forbidden:
- Console.WriteLine($"The container root filesystem is read-only: {errorDetails?.Message}");
+ _logger.LogError($"The container root filesystem is read-only: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such container: {errorDetails?.Message}");
+ _logger.LogError($"No such container: {errorDetails?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorDetails?.Message}");
+ _logger.LogError($"Internal server error: {errorDetails?.Message}");
break;
default:
- Console.WriteLine($"Error copying files: {errorDetails?.Message}");
+ _logger.LogError($"Error copying files: {errorDetails?.Message}");
break;
}
diff --git a/src/PodmanClient/PodmanClientDotNet.csproj b/src/PodmanClient/PodmanClientDotNet.csproj
index c4cb102..da9dc2b 100644
--- a/src/PodmanClient/PodmanClientDotNet.csproj
+++ b/src/PodmanClient/PodmanClientDotNet.csproj
@@ -19,4 +19,8 @@
false
+
+
+
+
diff --git a/src/PodmanClient/PodmanConnectorExec.cs b/src/PodmanClient/PodmanClientExec.cs
similarity index 58%
rename from src/PodmanClient/PodmanConnectorExec.cs
rename to src/PodmanClient/PodmanClientExec.cs
index caa82ee..103d563 100644
--- a/src/PodmanClient/PodmanConnectorExec.cs
+++ b/src/PodmanClient/PodmanClientExec.cs
@@ -1,26 +1,28 @@
using System.Text;
-using System.Text.Json;
-using MaksIT.PodmanClientDotNet.Extensions;
-using MaksIT.PodmanClientDotNet.Models.Exec;
+using Microsoft.Extensions.Logging;
+
using MaksIT.PodmanClientDotNet.Models;
+using MaksIT.PodmanClientDotNet.Models.Exec;
+using MaksIT.PodmanClientDotNet.Extensions;
+
namespace MaksIT.PodmanClientDotNet {
public partial class PodmanClient {
public async Task CreateExecAsync(
- string containerName,
- string[] cmd,
- bool attachStderr = true,
- bool attachStdin = false,
- bool attachStdout = true,
- string detachKeys = null,
- string[] env = null,
- bool privileged = false,
- bool tty = false,
- string user = null,
- string workingDir = null
- ) {
+ string containerName,
+ string[] cmd,
+ bool attachStderr = true,
+ bool attachStdin = false,
+ bool attachStdout = true,
+ string detachKeys = null,
+ string[] env = null,
+ bool privileged = false,
+ bool tty = false,
+ string user = null,
+ string workingDir = null
+ ) {
// Construct the request object
var execRequest = new CreateExecRequest {
AttachStderr = attachStderr,
@@ -40,7 +42,7 @@ namespace MaksIT.PodmanClientDotNet {
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
// Create the request URL
- var requestUrl = $"/containers/{Uri.EscapeDataString(containerName)}/exec";
+ var requestUrl = $"/{_apiVersion}/containers/{Uri.EscapeDataString(containerName)}/exec";
// Send the POST request
var response = await _httpClient.PostAsync(requestUrl, content);
@@ -51,21 +53,21 @@ namespace MaksIT.PodmanClientDotNet {
}
else {
var jsonResponse = await response.Content.ReadAsStringAsync();
- var errorResponse = JsonSerializer.Deserialize(jsonResponse);
+ var errorResponse = jsonResponse.ToObject();
// Handle different response codes
switch (response.StatusCode) {
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such container: {errorResponse?.Message}");
+ _logger.LogInformation($"No such container: {errorResponse?.Message}");
break;
case System.Net.HttpStatusCode.Conflict:
- Console.WriteLine($"Conflict error: {errorResponse?.Message}");
+ _logger.LogInformation($"Conflict error: {errorResponse?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorResponse?.Message}");
+ _logger.LogInformation($"Internal server error: {errorResponse?.Message}");
break;
default:
- Console.WriteLine($"Error creating exec instance: {errorResponse?.Message}");
+ _logger.LogInformation($"Error creating exec instance: {errorResponse?.Message}");
break;
}
@@ -75,16 +77,12 @@ namespace MaksIT.PodmanClientDotNet {
}
public async Task StartExecAsync(
- string execId,
- bool detach = false,
- bool tty = false,
- int? height = null,
- int? width = null,
- string outputFilePath = "exec_output.log"
- ) {
-
- outputFilePath = Path.Combine(Path.GetTempPath(), outputFilePath);
-
+ string execId,
+ bool detach = false,
+ bool tty = false,
+ int? height = null,
+ int? width = null
+) {
// Construct the request object
var startExecRequest = new StartExecRequest {
Detach = detach,
@@ -94,41 +92,37 @@ namespace MaksIT.PodmanClientDotNet {
};
// Serialize the request object to JSON
- var jsonRequest = JsonSerializer.Serialize(startExecRequest);
+ var jsonRequest = startExecRequest.ToJson();
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
// Create the request URL
- var requestUrl = $"/exec/{Uri.EscapeDataString(execId)}/start";
+ var requestUrl = $"/{_apiVersion}/exec/{Uri.EscapeDataString(execId)}/start";
// Send the POST request
var response = await _httpClient.PostAsync(requestUrl, content);
if (response.IsSuccessStatusCode) {
- // Write the response stream directly to a file
- using (var responseStream = await response.Content.ReadAsStreamAsync())
- using (var fileStream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write)) {
- await responseStream.CopyToAsync(fileStream);
- }
- var test = File.ReadAllText(outputFilePath);
- Console.WriteLine($"Exec instance started and output written to {outputFilePath}");
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ _logger.LogInformation(stringResponse);
+
}
else {
var jsonResponse = await response.Content.ReadAsStringAsync();
- var errorResponse = JsonSerializer.Deserialize(jsonResponse);
+ var errorResponse = jsonResponse.ToObject();
// Handle different response codes
switch (response.StatusCode) {
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such exec instance: {errorResponse?.Message}");
+ _logger.LogWarning($"No such exec instance: {errorResponse?.Message}");
break;
case System.Net.HttpStatusCode.Conflict:
- Console.WriteLine($"Conflict error: {errorResponse?.Message}");
+ _logger.LogError($"Conflict error: {errorResponse?.Message}");
break;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorResponse?.Message}");
+ _logger.LogError($"Internal server error: {errorResponse?.Message}");
break;
default:
- Console.WriteLine($"Error starting exec instance: {errorResponse?.Message}");
+ _logger.LogError($"Error starting exec instance: {errorResponse?.Message}");
break;
}
@@ -136,8 +130,9 @@ namespace MaksIT.PodmanClientDotNet {
}
}
+
public async Task InspectExecAsync(string execId) {
- var requestUrl = $"/exec/{Uri.EscapeDataString(execId)}/json";
+ var requestUrl = $"/{_apiVersion}/exec/{Uri.EscapeDataString(execId)}/json";
var response = await _httpClient.GetAsync(requestUrl);
if (response.IsSuccessStatusCode) {
@@ -146,19 +141,19 @@ namespace MaksIT.PodmanClientDotNet {
}
else {
var jsonResponse = await response.Content.ReadAsStringAsync();
- var errorResponse = JsonSerializer.Deserialize(jsonResponse);
+ var errorResponse = jsonResponse.ToObject();
switch (response.StatusCode) {
case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such exec instance: {errorResponse?.Message}");
+ _logger.LogWarning($"No such exec instance: {errorResponse?.Message}");
return null;
case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorResponse?.Message}");
+ _logger.LogError($"Internal server error: {errorResponse?.Message}");
break;
default:
- Console.WriteLine($"Error inspecting exec instance: {errorResponse?.Message}");
+ _logger.LogError($"Error inspecting exec instance: {errorResponse?.Message}");
break;
}
diff --git a/src/PodmanClient/PodmanClientImage.cs b/src/PodmanClient/PodmanClientImage.cs
new file mode 100644
index 0000000..1447a75
--- /dev/null
+++ b/src/PodmanClient/PodmanClientImage.cs
@@ -0,0 +1,98 @@
+using Microsoft.Extensions.Logging;
+
+using MaksIT.PodmanClientDotNet.Models;
+using MaksIT.PodmanClientDotNet.Models.Image;
+using MaksIT.PodmanClientDotNet.Extensions;
+
+namespace MaksIT.PodmanClientDotNet {
+ public partial class PodmanClient {
+
+ public async Task PullImageAsync(string reference, bool tlsVerify = true, bool quiet = false, string policy = "always", string arch = null, string os = null, string variant = null, bool allTags = false, string authHeader = null) {
+ var query = $"reference={Uri.EscapeDataString(reference)}&tlsVerify={tlsVerify}&quiet={quiet}&policy={Uri.EscapeDataString(policy)}";
+ if (!string.IsNullOrEmpty(arch)) query += $"&Arch={Uri.EscapeDataString(arch)}";
+ if (!string.IsNullOrEmpty(os)) query += $"&OS={Uri.EscapeDataString(os)}";
+ if (!string.IsNullOrEmpty(variant)) query += $"&Variant={Uri.EscapeDataString(variant)}";
+ if (allTags) query += "&allTags=true";
+
+ if (!string.IsNullOrEmpty(authHeader)) {
+ _httpClient.DefaultRequestHeaders.Add("X-Registry-Auth", authHeader);
+ }
+
+ var response = await _httpClient.PostAsync($"/{_apiVersion}/libpod/images/pull?{query}", null);
+
+ if (response.IsSuccessStatusCode) {
+
+ var responseStream = await response.Content.ReadAsStreamAsync();
+ using (var reader = new StreamReader(responseStream)) {
+ string line;
+ while ((line = await reader.ReadLineAsync()) != null) {
+ if (line.StartsWith("{\"error\"")) {
+ var errorDetails = line.ToObject();
+ _logger.LogError($"Error pulling image: {errorDetails?.Error}");
+ throw new HttpRequestException($"Forced exception: {response.StatusCode} - {errorDetails?.Error}");
+ }
+ }
+ }
+ }
+ else {
+ var errorContent = await response.Content.ReadAsStringAsync();
+ var errorDetails = errorContent.ToObject();
+
+ switch (response.StatusCode) {
+ case System.Net.HttpStatusCode.BadRequest:
+ _logger.LogError($"Bad request: {errorDetails?.Message}");
+ break;
+
+ case System.Net.HttpStatusCode.InternalServerError:
+ _logger.LogError($"Internal server error: {errorDetails?.Message}");
+ break;
+
+ default:
+ _logger.LogError($"Error pulling image: {errorDetails?.Message}");
+ break;
+ }
+
+ response.EnsureSuccessStatusCode();
+ }
+ }
+
+
+ public async Task TagImageAsync(string image, string repo, string tag) {
+ var response = await _httpClient.PostAsync($"/{_apiVersion}/libpod/images/{image}/tag?repo={Uri.EscapeDataString(repo)}&tag={Uri.EscapeDataString(tag)}", null);
+
+ if (response.IsSuccessStatusCode) {
+ if (response.StatusCode == System.Net.HttpStatusCode.Created) {
+ _logger.LogInformation("Image tagged successfully.");
+ }
+ }
+ else {
+ var errorContent = await response.Content.ReadAsStringAsync();
+ var errorDetails = errorContent.ToObject();
+
+ switch (response.StatusCode) {
+ case System.Net.HttpStatusCode.BadRequest:
+ _logger.LogError($"Bad parameter in request: {errorDetails?.Message}");
+ break;
+
+ case System.Net.HttpStatusCode.NotFound:
+ _logger.LogWarning($"No such image: {errorDetails?.Message}");
+ break;
+
+ case System.Net.HttpStatusCode.Conflict:
+ _logger.LogError($"Conflict error in operation: {errorDetails?.Message}");
+ break;
+
+ case System.Net.HttpStatusCode.InternalServerError:
+ _logger.LogError($"Internal server error: {errorDetails?.Message}");
+ break;
+
+ default:
+ _logger.LogError($"Error tagging image: {errorDetails?.Message}");
+ break;
+ }
+
+ response.EnsureSuccessStatusCode();
+ }
+ }
+ }
+}
diff --git a/src/PodmanClient/PodmanConnector.cs b/src/PodmanClient/PodmanConnector.cs
deleted file mode 100644
index ed4e554..0000000
--- a/src/PodmanClient/PodmanConnector.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace MaksIT.PodmanClientDotNet {
- public partial class PodmanClient {
- private readonly HttpClient _httpClient;
-
- public PodmanClient(string baseAddress, int timeOut) {
- _httpClient = new HttpClient();
- _httpClient.BaseAddress = new Uri(baseAddress);
- _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
- _httpClient.Timeout = TimeSpan.FromMinutes(timeOut);
- }
-
- public PodmanClient(HttpClient httpClient) {
- _httpClient = httpClient;
- _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
- _httpClient.Timeout = TimeSpan.FromMinutes(60);
- }
-
- }
-}
diff --git a/src/PodmanClient/PodmanConnectorImage.cs b/src/PodmanClient/PodmanConnectorImage.cs
deleted file mode 100644
index 294b574..0000000
--- a/src/PodmanClient/PodmanConnectorImage.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System.Text.Json;
-
-using MaksIT.PodmanClientDotNet.Models.Image;
-using MaksIT.PodmanClientDotNet.Models;
-
-namespace MaksIT.PodmanClientDotNet {
- public partial class PodmanClient {
-
- public async Task> PullImageAsync(string reference, bool tlsVerify = true, bool quiet = false, string policy = "always", string arch = null, string os = null, string variant = null, bool allTags = false, string authHeader = null) {
- var query = $"reference={Uri.EscapeDataString(reference)}&tlsVerify={tlsVerify}&quiet={quiet}&policy={Uri.EscapeDataString(policy)}";
- if (!string.IsNullOrEmpty(arch)) query += $"&Arch={Uri.EscapeDataString(arch)}";
- if (!string.IsNullOrEmpty(os)) query += $"&OS={Uri.EscapeDataString(os)}";
- if (!string.IsNullOrEmpty(variant)) query += $"&Variant={Uri.EscapeDataString(variant)}";
- if (allTags) query += "&allTags=true";
-
- if (!string.IsNullOrEmpty(authHeader)) {
- _httpClient.DefaultRequestHeaders.Add("X-Registry-Auth", authHeader);
- }
-
- var response = await _httpClient.PostAsync($"/v1.41/libpod/images/pull?{query}", null);
- var imagePullResponses = new List();
-
- if (response.IsSuccessStatusCode) {
- using var responseStream = await response.Content.ReadAsStreamAsync();
- using var reader = new StreamReader(responseStream);
- string line;
-
- while ((line = await reader.ReadLineAsync()) != null) {
- if (line.Contains("\"status\"")) {
- // The line contains status information
- var statusResponse = JsonSerializer.Deserialize(line);
- Console.WriteLine($"Status: {statusResponse.Status}");
- }
- else if (line.Contains("\"id\"") || line.Contains("\"images\"")) {
- // The line contains image ID information
- var imageResponse = JsonSerializer.Deserialize(line);
- if (imageResponse != null) {
- imagePullResponses.Add(imageResponse);
- }
- }
- }
-
- return imagePullResponses;
- }
- else {
- var errorContent = await response.Content.ReadAsStringAsync();
- var errorDetails = JsonSerializer.Deserialize(errorContent);
-
- switch (response.StatusCode) {
- case System.Net.HttpStatusCode.BadRequest:
- Console.WriteLine($"Bad request: {errorDetails?.Message}");
- break;
-
- case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorDetails?.Message}");
- break;
-
- default:
- Console.WriteLine($"Error pulling image: {errorDetails?.Message}");
- break;
- }
-
- response.EnsureSuccessStatusCode();
- return null;
- }
- }
-
-
- public async Task TagImageAsync(string image, string repo, string tag) {
- var response = await _httpClient.PostAsync($"/v1.41/libpod/images/{image}/tag?repo={Uri.EscapeDataString(repo)}&tag={Uri.EscapeDataString(tag)}", null);
-
- if (response.IsSuccessStatusCode) {
- if (response.StatusCode == System.Net.HttpStatusCode.Created) {
- Console.WriteLine("Image tagged successfully.");
- }
- }
- else {
- var errorContent = await response.Content.ReadAsStringAsync();
- var errorDetails = JsonSerializer.Deserialize(errorContent);
-
- switch (response.StatusCode) {
- case System.Net.HttpStatusCode.BadRequest:
- Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
- break;
-
- case System.Net.HttpStatusCode.NotFound:
- Console.WriteLine($"No such image: {errorDetails?.Message}");
- break;
-
- case System.Net.HttpStatusCode.Conflict:
- Console.WriteLine($"Conflict error in operation: {errorDetails?.Message}");
- break;
-
- case System.Net.HttpStatusCode.InternalServerError:
- Console.WriteLine($"Internal server error: {errorDetails?.Message}");
- break;
-
- default:
- Console.WriteLine($"Error tagging image: {errorDetails?.Message}");
- break;
- }
-
- response.EnsureSuccessStatusCode();
- }
- }
- }
-}
diff --git a/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj b/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj
new file mode 100644
index 0000000..17f4ec6
--- /dev/null
+++ b/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj
@@ -0,0 +1,34 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/PodmanClientDotNet.Tests/PodmanClientTests.cs b/src/PodmanClientDotNet.Tests/PodmanClientTests.cs
new file mode 100644
index 0000000..b5827be
--- /dev/null
+++ b/src/PodmanClientDotNet.Tests/PodmanClientTests.cs
@@ -0,0 +1,90 @@
+
+using Microsoft.Extensions.Logging;
+using static System.Net.Mime.MediaTypeNames;
+
+
+namespace MaksIT.PodmanClientDotNet.Tests {
+ public class PodmanClientIntegrationTests {
+ private readonly PodmanClient _client;
+
+ public PodmanClientIntegrationTests() {
+ // Initialize the logger
+ var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
+ var logger = loggerFactory.CreateLogger();
+
+ // Initialize PodmanClient with real HttpClient
+ _client = new PodmanClient(logger, "http://wks0002.corp.maks-it.com:8080", 60);
+ }
+
+ [Fact]
+ public async Task PodmanClient_IntegrationTests() {
+ // Test 1: Pull Image - Success
+ await PullImageAsync_Should_Succeed();
+
+ // Test 2: Tag Image - Success
+ await TagImageAsync_Should_Succeed();
+ }
+
+ private async Task PullImageAsync_Should_Succeed() {
+ // Arrange
+ string imageReference = "alpine:latest"; // Example image
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(imageReference));
+
+ // Assert
+ Assert.Null(exception); // Expect no exceptions if the pull was successful
+ }
+
+ private async Task TagImageAsync_Should_Succeed() {
+ // Arrange
+ string image = "alpine:latest"; // Example image
+ string repo = "myrepo";
+ string tag = "v1";
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.TagImageAsync(image, repo, tag));
+
+ // Assert
+ Assert.Null(exception); // Expect no exceptions if the tagging was successful
+ }
+
+ [Fact]
+ public async Task PodmanClient_PullImage_Errors() {
+
+ // Arrange
+ string imageReference = "dghdfdghmhgn:latest"; // Intentionally wrong image
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(imageReference));
+
+ // Assert
+ Assert.NotNull(exception); // Expect an exception due to nonexistent image
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+ [Fact]
+ public async Task PodmanClient_TagImage_Errors() {
+ // Arrange
+ string image = "dghdfdghmhgn:latest"; // Intentionally wrong image
+ string repo = "myrepo";
+ string tag = "v1";
+
+ // Act
+ var exception = await Record.ExceptionAsync(() => _client.TagImageAsync(image, repo, tag));
+
+ // Assert
+ Assert.NotNull(exception); // Expect an exception due to nonexistent image
+ Assert.IsType(exception); // Ensure it's the expected type
+ }
+
+
+
+
+
+
+
+
+
+ }
+}
diff --git a/src/PodmanClientDotNet.sln b/src/PodmanClientDotNet.sln
index fc4a0ef..b14cb33 100644
--- a/src/PodmanClientDotNet.sln
+++ b/src/PodmanClientDotNet.sln
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.9.34902.65
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PodmanClientDotNet", "PodmanClient\PodmanClientDotNet.csproj", "{0833C90F-6BF3-40E4-A035-B6D6C81DB9D7}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PodmanClientDotNet.Tests", "PodmanClientDotNet.Tests\PodmanClientDotNet.Tests.csproj", "{657C39AC-BF63-4678-9A35-A782FE9D4D7E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +17,10 @@ Global
{0833C90F-6BF3-40E4-A035-B6D6C81DB9D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0833C90F-6BF3-40E4-A035-B6D6C81DB9D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0833C90F-6BF3-40E4-A035-B6D6C81DB9D7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {657C39AC-BF63-4678-9A35-A782FE9D4D7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {657C39AC-BF63-4678-9A35-A782FE9D4D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {657C39AC-BF63-4678-9A35-A782FE9D4D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {657C39AC-BF63-4678-9A35-A782FE9D4D7E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE