refactor: route the container cmdlets through ContainerService (#126) (#199)

New-PveContainerSnapshot, Remove-PveContainerSnapshot and
Restore-PveContainerSnapshot each built their own PveHttpClient and parsed
the response inline while ContainerService.CreateContainerSnapshot /
RemoveContainerSnapshot / RollbackContainerSnapshot carried the same three
requests with no callers. The cmdlets now call the service, so the path
and form each sends is asserted offline (ADR 0021).

New-PveContainer already called ContainerService.CreateContainer but
allocated its container ID with a private PveHttpClient hitting
cluster/nextid; it now goes through ClusterConfigService.GetNextId, the
seam Copy-PveVm, Copy-PveContainer, New-PveVm and Import-PveOva already
use.

ContainerService.ParseTask's UPID-string branch now stamps
Status = "running", matching SnapshotService.ParseTask from the #126
pattern PR (#196). ParseTask is shared by every ContainerService lifecycle
method, so this also changes the non-Wait Status output of ten cmdlets
beyond the four converted here, from null to "running" — pinned with a
dedicated test.

Co-authored-by: goodolclint-claude[bot] <323206664+goodolclint-claude[bot]@users.noreply.github.com>
This commit is contained in:
goodolclint-claude[bot]
2026-09-03 00:09:54 +00:00
committed by GitHub
parent 48bab3e4cb
commit 3fa7eeb023
6 changed files with 240 additions and 51 deletions
@@ -513,7 +513,7 @@ namespace PSProxmoxVE.Core.Services
{
var data = JObject.Parse(response)["data"];
if (data?.Type == JTokenType.String)
return new PveTask { Upid = data.ToString(), Node = node };
return new PveTask { Upid = data.ToString(), Node = node, Status = "running" };
var task = data?.ToObject<PveTask>() ?? new PveTask();
task.Node = node;
@@ -1,9 +1,6 @@
using System.Collections.Generic;
using System.Management.Automation;
using System.Net;
using System.Runtime.InteropServices;
using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Client;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
using PSProxmoxVE.Core.Utilities;
@@ -152,11 +149,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
}
else
{
// Auto-allocate the next available ID from the cluster.
using var allocClient = new PveHttpClient(session);
var nextIdJson = allocClient.GetAsync("cluster/nextid").GetAwaiter().GetResult();
var nextIdData = JObject.Parse(nextIdJson)["data"];
config["vmid"] = int.Parse(nextIdData!.ToString());
config["vmid"] = new ClusterConfigService().GetNextId(session).ToString();
}
if (!string.IsNullOrEmpty(Hostname))
config["hostname"] = Hostname!;
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Management.Automation;
using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Client;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
@@ -46,25 +42,15 @@ namespace PSProxmoxVE.Cmdlets.Containers
return;
var session = GetSession();
using var client = new PveHttpClient(session);
WriteVerbose($"Creating snapshot '{Name}' for container {VmId}...");
var data = new Dictionary<string, string>
{
["snapname"] = Name
};
if (!string.IsNullOrEmpty(Description)) data["description"] = Description!;
var service = new ContainerService();
var task = service.CreateContainerSnapshot(session, Node, VmId, Name, Description);
var json = client.PostAsync($"nodes/{Uri.EscapeDataString(Node)}/lxc/{VmId}/snapshot", data).GetAwaiter().GetResult();
var root = JObject.Parse(json);
var upid = root["data"]?.ToString() ?? string.Empty;
var task = new PveTask { Upid = upid, Node = Node, Status = "running" };
if (Wait.IsPresent && !string.IsNullOrEmpty(upid))
if (Wait.IsPresent && !string.IsNullOrEmpty(task.Upid))
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, Node, upid);
task = taskService.WaitForTask(session, Node, task.Upid);
}
WriteObject(task);
@@ -1,7 +1,4 @@
using System;
using System.Management.Automation;
using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Client;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
@@ -45,18 +42,13 @@ namespace PSProxmoxVE.Cmdlets.Containers
return;
WriteVerbose($"Removing snapshot '{Name}' from container {VmId}...");
using var client = new PveHttpClient(session);
var service = new ContainerService();
var task = service.RemoveContainerSnapshot(session, Node, VmId, Name);
var json = client.DeleteAsync($"nodes/{Uri.EscapeDataString(Node)}/lxc/{VmId}/snapshot/{Uri.EscapeDataString(Name)}").GetAwaiter().GetResult();
var root = JObject.Parse(json);
var upid = root["data"]?.ToString() ?? string.Empty;
var task = new PveTask { Upid = upid, Node = Node, Status = "running" };
if (Wait.IsPresent && !string.IsNullOrEmpty(upid))
if (Wait.IsPresent && !string.IsNullOrEmpty(task.Upid))
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, Node, upid);
task = taskService.WaitForTask(session, Node, task.Upid);
}
WriteObject(task);
@@ -1,7 +1,4 @@
using System;
using System.Management.Automation;
using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Client;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
@@ -48,18 +45,13 @@ namespace PSProxmoxVE.Cmdlets.Containers
return;
WriteVerbose($"Restoring snapshot '{Name}' on container {VmId}...");
using var client = new PveHttpClient(session);
var service = new ContainerService();
var task = service.RollbackContainerSnapshot(session, Node, VmId, Name);
var json = client.PostAsync($"nodes/{Uri.EscapeDataString(Node)}/lxc/{VmId}/snapshot/{Uri.EscapeDataString(Name)}/rollback").GetAwaiter().GetResult();
var root = JObject.Parse(json);
var upid = root["data"]?.ToString() ?? string.Empty;
var task = new PveTask { Upid = upid, Node = Node, Status = "running" };
if (Wait.IsPresent && !string.IsNullOrEmpty(upid))
if (Wait.IsPresent && !string.IsNullOrEmpty(task.Upid))
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, Node, upid);
task = taskService.WaitForTask(session, Node, task.Upid);
}
WriteObject(task);