(refactor): code clean up, image pull and tag test
This commit is contained in:
parent
de06e407c5
commit
acdcb1300d
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
32
src/PodmanClient/Models/Image/PullImageResponse.cs
Normal file
32
src/PodmanClient/Models/Image/PullImageResponse.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Error contains text of errors from c/image.
|
||||||
|
/// </summary>
|
||||||
|
public string Error { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID contains image ID (retained for backwards compatibility).
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Images contains the IDs of the images pulled.
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Images { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stream used to provide output from c/image.
|
||||||
|
/// </summary>
|
||||||
|
public string Stream { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/PodmanClient/PodmanClient.cs
Normal file
61
src/PodmanClient/PodmanClient.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MaksIT.PodmanClientDotNet {
|
||||||
|
public partial class PodmanClient {
|
||||||
|
private readonly ILogger<PodmanClient> _logger;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
private const string _apiVersion = "v1.41";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PodmanClient"/> class using the specified base address and timeout.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">The logger instance used for logging within the client.</param>
|
||||||
|
/// <param name="baseAddress">The base address of the Podman service.</param>
|
||||||
|
/// <param name="timeOut">The timeout period in minutes for HTTP requests.</param>
|
||||||
|
public PodmanClient(
|
||||||
|
ILogger<PodmanClient> logger,
|
||||||
|
string baseAddress,
|
||||||
|
int timeOut = 60
|
||||||
|
) : this(
|
||||||
|
logger,
|
||||||
|
baseAddress,
|
||||||
|
new HttpClient {
|
||||||
|
Timeout = TimeSpan.FromMinutes(timeOut)
|
||||||
|
}
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PodmanClient"/> class using the provided <see cref="HttpClient"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">The logger instance used for logging within the client.</param>
|
||||||
|
/// <param name="httpClient">An existing <see cref="HttpClient"/> instance configured for use with the Podman service.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when the logger or httpClient parameter is null.</exception>
|
||||||
|
public PodmanClient(
|
||||||
|
ILogger<PodmanClient> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures the default settings for the <see cref="HttpClient"/> used by this instance.
|
||||||
|
/// Ensures that the "Accept" header is set to "application/json".
|
||||||
|
/// </summary>
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,12 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
using MaksIT.PodmanClientDotNet.Extensions;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
using MaksIT.PodmanClientDotNet.Models;
|
using MaksIT.PodmanClientDotNet.Models;
|
||||||
using MaksIT.PodmanClientDotNet.Models.Container;
|
using MaksIT.PodmanClientDotNet.Models.Container;
|
||||||
|
using MaksIT.PodmanClientDotNet.Extensions;
|
||||||
|
|
||||||
|
|
||||||
namespace MaksIT.PodmanClientDotNet {
|
namespace MaksIT.PodmanClientDotNet {
|
||||||
public partial class PodmanClient {
|
public partial class PodmanClient {
|
||||||
@ -246,31 +248,31 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var jsonContent = new StringContent(JsonSerializer.Serialize(createContainerParameters), Encoding.UTF8, "application/json");
|
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) {
|
if (response.IsSuccessStatusCode) {
|
||||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
return JsonSerializer.Deserialize<CreateContainerResponse>(jsonResponse);
|
return jsonResponse.ToObject<CreateContainerResponse>();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
var errorDetails = JsonSerializer.Deserialize<ErrorResponse>(errorContent);
|
var errorDetails = errorContent.ToObject<ErrorResponse>();
|
||||||
|
|
||||||
switch (response.StatusCode) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.BadRequest:
|
case System.Net.HttpStatusCode.BadRequest:
|
||||||
Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
|
_logger.LogError($"Bad parameter in request: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such container: {errorDetails?.Message}");
|
_logger.LogError($"No such container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
case System.Net.HttpStatusCode.Conflict:
|
case System.Net.HttpStatusCode.Conflict:
|
||||||
Console.WriteLine($"Conflict error in operation: {errorDetails?.Message}");
|
_logger.LogError($"Conflict error in operation: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorDetails?.Message}");
|
_logger.LogError($"Internal server error: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error creating container: {errorDetails?.Message}");
|
_logger.LogError($"Error creating container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,34 +286,34 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
|
|
||||||
public async Task StartContainerAsync(string containerId, string detachKeys = "ctrl-p,ctrl-q") {
|
public async Task StartContainerAsync(string containerId, string detachKeys = "ctrl-p,ctrl-q") {
|
||||||
var response = await _httpClient.PostAsync(
|
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) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.NoContent:
|
case System.Net.HttpStatusCode.NoContent:
|
||||||
Console.WriteLine("Container started successfully.");
|
_logger.LogInformation("Container started successfully.");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.NotModified:
|
case System.Net.HttpStatusCode.NotModified:
|
||||||
Console.WriteLine("Container was already started.");
|
_logger.LogWarning("Container was already started.");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
var errorContent404 = await response.Content.ReadAsStringAsync();
|
var errorContent404 = await response.Content.ReadAsStringAsync();
|
||||||
var errorDetails404 = errorContent404.ToObject<ErrorResponse>();
|
var errorDetails404 = errorContent404.ToObject<ErrorResponse>();
|
||||||
Console.WriteLine($"Container not found: {errorDetails404?.Message}");
|
_logger.LogError($"Container not found: {errorDetails404?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
var errorContent500 = await response.Content.ReadAsStringAsync();
|
var errorContent500 = await response.Content.ReadAsStringAsync();
|
||||||
var errorDetails500 = errorContent500.ToObject<ErrorResponse>();
|
var errorDetails500 = errorContent500.ToObject<ErrorResponse>();
|
||||||
Console.WriteLine($"Internal server error: {errorDetails500?.Message}");
|
_logger.LogError($"Internal server error: {errorDetails500?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if ((int)response.StatusCode >= 400) {
|
if ((int)response.StatusCode >= 400) {
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
var errorDetails = errorContent.ToObject<ErrorResponse>();
|
var errorDetails = errorContent.ToObject<ErrorResponse>();
|
||||||
Console.WriteLine($"Error starting container: {errorDetails?.Message}");
|
_logger.LogError($"Error starting container: {errorDetails?.Message}");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -321,14 +323,14 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
|
|
||||||
public async Task StopContainerAsync(string containerId, int timeout = 10, bool ignoreAlreadyStopped = false) {
|
public async Task StopContainerAsync(string containerId, int timeout = 10, bool ignoreAlreadyStopped = false) {
|
||||||
var queryParams = $"?timeout={timeout}&Ignore={ignoreAlreadyStopped.ToString().ToLower()}";
|
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.IsSuccessStatusCode) {
|
||||||
if (response.StatusCode == System.Net.HttpStatusCode.NoContent) {
|
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) {
|
else if (response.StatusCode == System.Net.HttpStatusCode.NotModified) {
|
||||||
Console.WriteLine("Container was already stopped.");
|
_logger.LogWarning("Container was already stopped.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -337,15 +339,15 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
|
|
||||||
switch (response.StatusCode) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such container: {errorDetails?.Message}");
|
_logger.LogError($"No such container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorDetails?.Message}");
|
_logger.LogError($"Internal server error: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error stopping container: {errorDetails?.Message}");
|
_logger.LogError($"Error stopping container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,49 +357,49 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
|
|
||||||
public async Task ForceDeleteContainerAsync(string containerId, bool deleteVolumes = false, int timeout = 10) {
|
public async Task ForceDeleteContainerAsync(string containerId, bool deleteVolumes = false, int timeout = 10) {
|
||||||
var queryParams = $"?force=true&v={deleteVolumes.ToString().ToLower()}&timeout={timeout}";
|
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.IsSuccessStatusCode) {
|
||||||
if (response.StatusCode == System.Net.HttpStatusCode.NoContent) {
|
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) {
|
else if (response.StatusCode == System.Net.HttpStatusCode.OK) {
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
var deleteResponses = JsonSerializer.Deserialize<DeleteContainerResponse[]>(responseContent);
|
var deleteResponses = responseContent.ToObject<DeleteContainerResponse[]>();
|
||||||
|
|
||||||
foreach (var deleteResponse in deleteResponses) {
|
foreach (var deleteResponse in deleteResponses) {
|
||||||
if (string.IsNullOrEmpty(deleteResponse.Err)) {
|
if (string.IsNullOrEmpty(deleteResponse.Err)) {
|
||||||
Console.WriteLine($"Container {deleteResponse.Id} deleted successfully.");
|
_logger.LogInformation($"Container {deleteResponse.Id} deleted successfully.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Console.WriteLine($"Error deleting container {deleteResponse.Id}: {deleteResponse.Err}");
|
_logger.LogError($"Error deleting container {deleteResponse.Id}: {deleteResponse.Err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
var errorDetails = JsonSerializer.Deserialize<ErrorResponse>(errorContent);
|
var errorDetails = errorContent.ToObject<ErrorResponse>();
|
||||||
|
|
||||||
switch (response.StatusCode) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.BadRequest:
|
case System.Net.HttpStatusCode.BadRequest:
|
||||||
Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
|
_logger.LogError($"Bad parameter in request: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such container: {errorDetails?.Message}");
|
_logger.LogError($"No such container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.Conflict:
|
case System.Net.HttpStatusCode.Conflict:
|
||||||
Console.WriteLine($"Conflict error: {errorDetails?.Message}");
|
_logger.LogError($"Conflict error: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorDetails?.Message}");
|
_logger.LogError($"Internal server error: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error deleting container: {errorDetails?.Message}");
|
_logger.LogError($"Error deleting container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,11 +409,11 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
|
|
||||||
public async Task DeleteContainerAsync(string containerId, bool depend = false, bool ignore = false, int timeout = 10) {
|
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 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.IsSuccessStatusCode) {
|
||||||
if (response.StatusCode == System.Net.HttpStatusCode.NoContent) {
|
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) {
|
else if (response.StatusCode == System.Net.HttpStatusCode.OK) {
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
@ -419,10 +421,10 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
|
|
||||||
foreach (var deleteResponse in deleteResponses) {
|
foreach (var deleteResponse in deleteResponses) {
|
||||||
if (string.IsNullOrEmpty(deleteResponse.Err)) {
|
if (string.IsNullOrEmpty(deleteResponse.Err)) {
|
||||||
Console.WriteLine($"Container {deleteResponse.Id} deleted successfully.");
|
_logger.LogInformation($"Container {deleteResponse.Id} deleted successfully.");
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.BadRequest:
|
case System.Net.HttpStatusCode.BadRequest:
|
||||||
Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
|
_logger.LogInformation($"Bad parameter in request: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such container: {errorDetails?.Message}");
|
_logger.LogInformation($"No such container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.Conflict:
|
case System.Net.HttpStatusCode.Conflict:
|
||||||
Console.WriteLine($"Conflict error: {errorDetails?.Message}");
|
_logger.LogInformation($"Conflict error: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorDetails?.Message}");
|
_logger.LogInformation($"Internal server error: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error deleting container: {errorDetails?.Message}");
|
_logger.LogInformation($"Error deleting container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,34 +466,34 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
content.Headers.Add("Content-Type", "application/x-tar");
|
content.Headers.Add("Content-Type", "application/x-tar");
|
||||||
|
|
||||||
var queryParams = $"?path={Uri.EscapeDataString(path)}&pause={pause.ToString().ToLower()}";
|
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) {
|
if (response.IsSuccessStatusCode) {
|
||||||
Console.WriteLine("Files copied successfully to the container.");
|
_logger.LogInformation("Files copied successfully to the container.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
var errorDetails = JsonSerializer.Deserialize<ErrorResponse>(errorContent);
|
var errorDetails = errorContent.ToObject<ErrorResponse>();
|
||||||
|
|
||||||
switch (response.StatusCode) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.BadRequest:
|
case System.Net.HttpStatusCode.BadRequest:
|
||||||
Console.WriteLine($"Bad parameter in request: {errorDetails?.Message}");
|
_logger.LogError($"Bad parameter in request: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.Forbidden:
|
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;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such container: {errorDetails?.Message}");
|
_logger.LogError($"No such container: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorDetails?.Message}");
|
_logger.LogError($"Internal server error: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error copying files: {errorDetails?.Message}");
|
_logger.LogError($"Error copying files: {errorDetails?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,4 +19,8 @@
|
|||||||
<RequireLicenseAcceptance>false</RequireLicenseAcceptance>
|
<RequireLicenseAcceptance>false</RequireLicenseAcceptance>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,26 +1,28 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using MaksIT.PodmanClientDotNet.Extensions;
|
using Microsoft.Extensions.Logging;
|
||||||
using MaksIT.PodmanClientDotNet.Models.Exec;
|
|
||||||
using MaksIT.PodmanClientDotNet.Models;
|
using MaksIT.PodmanClientDotNet.Models;
|
||||||
|
using MaksIT.PodmanClientDotNet.Models.Exec;
|
||||||
|
using MaksIT.PodmanClientDotNet.Extensions;
|
||||||
|
|
||||||
|
|
||||||
namespace MaksIT.PodmanClientDotNet {
|
namespace MaksIT.PodmanClientDotNet {
|
||||||
public partial class PodmanClient {
|
public partial class PodmanClient {
|
||||||
|
|
||||||
public async Task<CreateExecResponse> CreateExecAsync(
|
public async Task<CreateExecResponse> CreateExecAsync(
|
||||||
string containerName,
|
string containerName,
|
||||||
string[] cmd,
|
string[] cmd,
|
||||||
bool attachStderr = true,
|
bool attachStderr = true,
|
||||||
bool attachStdin = false,
|
bool attachStdin = false,
|
||||||
bool attachStdout = true,
|
bool attachStdout = true,
|
||||||
string detachKeys = null,
|
string detachKeys = null,
|
||||||
string[] env = null,
|
string[] env = null,
|
||||||
bool privileged = false,
|
bool privileged = false,
|
||||||
bool tty = false,
|
bool tty = false,
|
||||||
string user = null,
|
string user = null,
|
||||||
string workingDir = null
|
string workingDir = null
|
||||||
) {
|
) {
|
||||||
// Construct the request object
|
// Construct the request object
|
||||||
var execRequest = new CreateExecRequest {
|
var execRequest = new CreateExecRequest {
|
||||||
AttachStderr = attachStderr,
|
AttachStderr = attachStderr,
|
||||||
@ -40,7 +42,7 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
|
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
// Create the request URL
|
// Create the request URL
|
||||||
var requestUrl = $"/containers/{Uri.EscapeDataString(containerName)}/exec";
|
var requestUrl = $"/{_apiVersion}/containers/{Uri.EscapeDataString(containerName)}/exec";
|
||||||
|
|
||||||
// Send the POST request
|
// Send the POST request
|
||||||
var response = await _httpClient.PostAsync(requestUrl, content);
|
var response = await _httpClient.PostAsync(requestUrl, content);
|
||||||
@ -51,21 +53,21 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
var errorResponse = JsonSerializer.Deserialize<ErrorResponse>(jsonResponse);
|
var errorResponse = jsonResponse.ToObject<ErrorResponse>();
|
||||||
|
|
||||||
// Handle different response codes
|
// Handle different response codes
|
||||||
switch (response.StatusCode) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such container: {errorResponse?.Message}");
|
_logger.LogInformation($"No such container: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
case System.Net.HttpStatusCode.Conflict:
|
case System.Net.HttpStatusCode.Conflict:
|
||||||
Console.WriteLine($"Conflict error: {errorResponse?.Message}");
|
_logger.LogInformation($"Conflict error: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorResponse?.Message}");
|
_logger.LogInformation($"Internal server error: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error creating exec instance: {errorResponse?.Message}");
|
_logger.LogInformation($"Error creating exec instance: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,16 +77,12 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartExecAsync(
|
public async Task StartExecAsync(
|
||||||
string execId,
|
string execId,
|
||||||
bool detach = false,
|
bool detach = false,
|
||||||
bool tty = false,
|
bool tty = false,
|
||||||
int? height = null,
|
int? height = null,
|
||||||
int? width = null,
|
int? width = null
|
||||||
string outputFilePath = "exec_output.log"
|
) {
|
||||||
) {
|
|
||||||
|
|
||||||
outputFilePath = Path.Combine(Path.GetTempPath(), outputFilePath);
|
|
||||||
|
|
||||||
// Construct the request object
|
// Construct the request object
|
||||||
var startExecRequest = new StartExecRequest {
|
var startExecRequest = new StartExecRequest {
|
||||||
Detach = detach,
|
Detach = detach,
|
||||||
@ -94,41 +92,37 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Serialize the request object to JSON
|
// Serialize the request object to JSON
|
||||||
var jsonRequest = JsonSerializer.Serialize(startExecRequest);
|
var jsonRequest = startExecRequest.ToJson();
|
||||||
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
|
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
// Create the request URL
|
// Create the request URL
|
||||||
var requestUrl = $"/exec/{Uri.EscapeDataString(execId)}/start";
|
var requestUrl = $"/{_apiVersion}/exec/{Uri.EscapeDataString(execId)}/start";
|
||||||
|
|
||||||
// Send the POST request
|
// Send the POST request
|
||||||
var response = await _httpClient.PostAsync(requestUrl, content);
|
var response = await _httpClient.PostAsync(requestUrl, content);
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode) {
|
if (response.IsSuccessStatusCode) {
|
||||||
// Write the response stream directly to a file
|
var stringResponse = await response.Content.ReadAsStringAsync();
|
||||||
using (var responseStream = await response.Content.ReadAsStreamAsync())
|
_logger.LogInformation(stringResponse);
|
||||||
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}");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
var errorResponse = JsonSerializer.Deserialize<ErrorResponse>(jsonResponse);
|
var errorResponse = jsonResponse.ToObject<ErrorResponse>();
|
||||||
|
|
||||||
// Handle different response codes
|
// Handle different response codes
|
||||||
switch (response.StatusCode) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such exec instance: {errorResponse?.Message}");
|
_logger.LogWarning($"No such exec instance: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
case System.Net.HttpStatusCode.Conflict:
|
case System.Net.HttpStatusCode.Conflict:
|
||||||
Console.WriteLine($"Conflict error: {errorResponse?.Message}");
|
_logger.LogError($"Conflict error: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorResponse?.Message}");
|
_logger.LogError($"Internal server error: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error starting exec instance: {errorResponse?.Message}");
|
_logger.LogError($"Error starting exec instance: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +130,9 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<InspectExecResponse?> InspectExecAsync(string execId) {
|
public async Task<InspectExecResponse?> InspectExecAsync(string execId) {
|
||||||
var requestUrl = $"/exec/{Uri.EscapeDataString(execId)}/json";
|
var requestUrl = $"/{_apiVersion}/exec/{Uri.EscapeDataString(execId)}/json";
|
||||||
var response = await _httpClient.GetAsync(requestUrl);
|
var response = await _httpClient.GetAsync(requestUrl);
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode) {
|
if (response.IsSuccessStatusCode) {
|
||||||
@ -146,19 +141,19 @@ namespace MaksIT.PodmanClientDotNet {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
var errorResponse = JsonSerializer.Deserialize<ErrorResponse>(jsonResponse);
|
var errorResponse = jsonResponse.ToObject<ErrorResponse>();
|
||||||
|
|
||||||
switch (response.StatusCode) {
|
switch (response.StatusCode) {
|
||||||
case System.Net.HttpStatusCode.NotFound:
|
case System.Net.HttpStatusCode.NotFound:
|
||||||
Console.WriteLine($"No such exec instance: {errorResponse?.Message}");
|
_logger.LogWarning($"No such exec instance: {errorResponse?.Message}");
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case System.Net.HttpStatusCode.InternalServerError:
|
case System.Net.HttpStatusCode.InternalServerError:
|
||||||
Console.WriteLine($"Internal server error: {errorResponse?.Message}");
|
_logger.LogError($"Internal server error: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Error inspecting exec instance: {errorResponse?.Message}");
|
_logger.LogError($"Error inspecting exec instance: {errorResponse?.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
98
src/PodmanClient/PodmanClientImage.cs
Normal file
98
src/PodmanClient/PodmanClientImage.cs
Normal file
@ -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<PullImageResponse>();
|
||||||
|
_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<ErrorResponse>();
|
||||||
|
|
||||||
|
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<ErrorResponse>();
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<List<ImagePullStatusResponse>> 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<ImagePullStatusResponse>();
|
|
||||||
|
|
||||||
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<ImagePullStatusResponse>(line);
|
|
||||||
Console.WriteLine($"Status: {statusResponse.Status}");
|
|
||||||
}
|
|
||||||
else if (line.Contains("\"id\"") || line.Contains("\"images\"")) {
|
|
||||||
// The line contains image ID information
|
|
||||||
var imageResponse = JsonSerializer.Deserialize<ImagePullStatusResponse>(line);
|
|
||||||
if (imageResponse != null) {
|
|
||||||
imagePullResponses.Add(imageResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return imagePullResponses;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
|
||||||
var errorDetails = JsonSerializer.Deserialize<ErrorResponse>(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<ErrorResponse>(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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
34
src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj
Normal file
34
src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.0" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PodmanClient\PodmanClientDotNet.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
90
src/PodmanClientDotNet.Tests/PodmanClientTests.cs
Normal file
90
src/PodmanClientDotNet.Tests/PodmanClientTests.cs
Normal file
@ -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<PodmanClient>();
|
||||||
|
|
||||||
|
// 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<HttpRequestException>(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<HttpRequestException>(exception); // Ensure it's the expected type
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,8 @@ VisualStudioVersion = 17.9.34902.65
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PodmanClientDotNet", "PodmanClient\PodmanClientDotNet.csproj", "{0833C90F-6BF3-40E4-A035-B6D6C81DB9D7}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PodmanClientDotNet", "PodmanClient\PodmanClientDotNet.csproj", "{0833C90F-6BF3-40E4-A035-B6D6C81DB9D7}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PodmanClientDotNet.Tests", "PodmanClientDotNet.Tests\PodmanClientDotNet.Tests.csproj", "{657C39AC-BF63-4678-9A35-A782FE9D4D7E}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{0833C90F-6BF3-40E4-A035-B6D6C81DB9D7}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user