329 Commits

Author SHA1 Message Date
GoodOlClint ab768e114a Merge pull request #18 from GoodOlClint/feat/multi-node-storage-vm
Multi-node PVE provisioning and Docker-based shared storage
2026-03-24 14:09:31 -05:00
GoodOlClint b3481e259b Merge pull request #19 from GoodOlClint/copilot/sub-pr-18
fix(ci): address PR #18 review feedback on Docker storage reliability and security
2026-03-24 14:02:54 -05:00
copilot-swe-agent[bot] 6a63dea923 fix: apply review feedback on Docker storage and security documentation
- Fix storage_ip derivation to use default route first, not Swarm RemoteManagers
- Make iSCSI container entrypoint idempotent (check before create for target/LUN/bind)
- Pin erichough/nfs-server from 'latest' to '2.2.1' for reproducibility
- Replace curl|sh Docker install with apt repo in Dockerfile.test
- Add docker.sock security warnings in docker-compose.test.yml and CI workflow
- Update stale 'storage VM' references in comments and .env.test.example"

Co-authored-by: GoodOlClint <151449+GoodOlClint@users.noreply.github.com>
Agent-Logs-Url: https://github.com/GoodOlClint/PSProxmoxVE/sessions/534c0a63-2988-496c-b0ae-e291fcfa050b
2026-03-24 18:53:30 +00:00
copilot-swe-agent[bot] 8acd04c272 Initial plan 2026-03-24 18:47:58 +00:00
Clint Branham 9817d30a11 feat(ci): multi-node PVE provisioning and Docker-based shared storage
Provision two PVE nodes per version (a/b) for future cluster testing,
plus Docker-based iSCSI target and NFS server for shared storage tests.

Multi-node changes:
- Each PVE version gets two nodes: 9a/9b and 8a/8b (4 VMs total)
- Parameterized answer.toml FQDN for unique hostnames per node
- Per-node auto-install ISOs with unique answer files
- Node name discovered from FQDN and included in test config
- API token creation handles pre-existing tokens (delete + recreate)
- New test env vars: PVETEST_HOST_B, PVETEST_APITOKEN_B
- Removed preflight cleanup from provision (use explicit cleanup instead)

Docker storage services:
- New docker-compose.storage.yml with iSCSI (tgt) and NFS containers
- Host networking so PVE nodes can reach storage services
- Docker socket mounted into dev-infra container for host Docker access
- Docker CLI added to dev-infra Dockerfile stage
- New test env vars: PVETEST_STORAGE_VM_IP, PVETEST_ISCSI_IQN, PVETEST_NFS_EXPORT

Other fixes:
- first-boot.sh installs open-iscsi on PVE nodes
- preflight-cleanup.sh handles empty ISO filename gracefully
- TMPDIR set to work dir to avoid /tmp overflow during ISO uploads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 13:34:15 -05:00
GoodOlClint 105bf4b2ba Merge pull request #17 from GoodOlClint/feat/local-integration-runner
fix(ci): add .dockerignore to prevent sending 250MB build context
2026-03-24 10:55:35 -05:00
GoodOlClint da0591c84c Merge branch 'main' into feat/local-integration-runner 2026-03-24 10:53:17 -05:00
Clint Branham 8d5df62b41 fix(ci): add .dockerignore to prevent sending 250MB build context
The Dockerfile.test stages only install packages (no COPY), but the
build context changed from tests/infrastructure/ to repo root. Without
a .dockerignore, Docker sends the entire repo (250MB) to the daemon
before building, causing CI to hang.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:51:27 -05:00
GoodOlClint cc07e110f9 Merge pull request #16 from GoodOlClint/feat/local-integration-runner
Unify CI and local integration test infrastructure
2026-03-24 10:44:32 -05:00
Clint Branham f21c7f84b7 feat(ci): unify CI and local integration test infrastructure
- Replace tests/infrastructure/Dockerfile with tests/Dockerfile.test
  (single multi-stage Dockerfile for both CI and local dev)
- CI container-image job now builds from Dockerfile.test target dev-infra
- Add ARM support: PowerShell installed via dotnet tool on arm64,
  APT package on amd64
- Replace tests/dev.sh (bash) with tests/dev.ps1 (PowerShell) for
  cross-platform support (Windows, macOS, Linux)
- Add -DockerHost parameter for running x86 containers on a remote
  Docker host from ARM Macs (rsyncs repo, uses SSH Docker transport)
- Add -NoCleanup switch to keep nested PVE VMs after integration tests
- integration command now provisions nested PVE VMs instead of testing
  against a pre-existing PVE directly
- Share /opt/pve-isos host path between CI and local dev (was separate
  Docker named volume)
- Delete tools/Invoke-Tests.ps1 (unused, overlapped with run-integration.sh)
- Add .gitignore entries for Terraform state/artifacts
- Update all documentation references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:41:28 -05:00
GoodOlClint 629551021c Merge pull request #15 from GoodOlClint/fix/unmask-target-node
fix(ci): move PVE_TARGET_NODE from secret to variable to stop log masking
2026-03-24 09:55:15 -05:00
Clint Branham c1e1442155 fix(ci): move PVE_TARGET_NODE from secret to variable
The node name "pve" was being masked in all CI logs because GitHub
Actions auto-masks secret values. Since the node name is not sensitive,
use vars.PVE_TARGET_NODE instead of secrets.PVE_TARGET_NODE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:49:21 -05:00
GoodOlClint da9e553683 Merge pull request #13 from GoodOlClint/refactor/integration-test-orchestration
refactor: extract integration test orchestration, add dev container
2026-03-24 09:44:19 -05:00
GoodOlClint cddb3ef71a Merge pull request #14 from GoodOlClint/copilot/sub-pr-13
fix: harden run-integration.sh against special chars, token leakage, and ARM
2026-03-24 09:40:06 -05:00
copilot-swe-agent[bot] 2291379040 fix: apply security and correctness fixes from PR #13 review
Co-authored-by: GoodOlClint <151449+GoodOlClint@users.noreply.github.com>
Agent-Logs-Url: https://github.com/GoodOlClint/PSProxmoxVE/sessions/677fba6b-906d-4cf6-82fa-3b7ab6afc648
2026-03-24 14:36:51 +00:00
copilot-swe-agent[bot] bdb62a3fbf Initial plan 2026-03-24 14:34:00 +00:00
Clint Branham ee717cccd1 docs: update CLAUDE.md with PR workflow and dev container instructions
All changes now go through pull requests. Branch protection is enabled
on main (required build checks, required review, admin enforced).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:21:59 -05:00
Clint Branham fc092ea5b3 feat: add dev container for local build, test, and provisioning
- Dockerfile.test: multi-stage build with dev (ARM+x86) and dev-infra
  (x86 only, adds Terraform + PVE provisioning tools) targets
- docker-compose.test.yml: services for both targets, .env.test support
- dev.sh: helper script wrapping container lifecycle and run-integration.sh
- .env.test.example: template for integration test configuration
- .gitignore: exclude .env.test, whitelist .env.*.example

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:21:52 -05:00
Clint Branham 9752b854e4 refactor(ci): simplify workflow to delegate to run-integration.sh
Provision, test, and cleanup jobs now call run-integration.sh instead
of inlining ~170 lines of shell. Logic changes only need to be made
in one place, shared between CI and local dev.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:21:14 -05:00
Clint Branham 7f3430cd50 refactor(ci): extract integration test orchestration into run-integration.sh
Single source of truth for the provision → test → cleanup lifecycle,
callable from both CI workflows and local dev containers.

Subcommands: provision, test [8|9|all], cleanup, all
Detects CI vs local via $GITHUB_ACTIONS and adjusts output accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:21:08 -05:00
Clint Branham 6a1253c37b fix: remove pull_request trigger from integration tests workflow 2026-03-24 08:39:16 -05:00
Clint Branham b4754c72a0 docs: update README, CHANGELOG, SECURITY.md, and review artifacts
- README: move Set-PveStorage to Storage section, Set-PveApiToken to
  Access section (were miscategorized under "Access — Groups & Domains")
- CHANGELOG: add Changed section with 13 architectural improvements,
  expand Fixed section with all ConfirmImpact and credential fixes
- SECURITY.md: add SecureString detail, Input Validation section,
  Dependabot mention, SkipCertificateCheck warning note
- CLAUDE.md: update review system reference to scan-7
- findings.json: F050 resolved (URL encoding), F031 resolved (credentials)
- REVIEW_REPORT.md: scan-7 report

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:41:19 -05:00
Clint Branham f6a2f843db docs: update cmdlet help via platyPS merge, fix generate-help.ps1
- generate-help.ps1: rewritten to use Update-MarkdownHelp (merge) by
  default, preserving hand-written descriptions and examples. Only
  New-MarkdownHelp on genuinely new cmdlets. -Force flag for full regen.
- docs/cmdlets: parameter metadata updated via platyPS merge, CRLF
  normalized to LF
- Removed stale Send-PveIso.md (cmdlet renamed to Send-PveFile)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:41:04 -05:00
Clint Branham d5f4e13637 fix(security): move hardcoded test password to GitHub Actions secret
- CI workflow: PVE_PASSWORD now reads from secrets.PVE_TEST_PASSWORD
- variables.tf: removed default password, requires TF_VAR env var
- Integration README: examples use <your-test-password> placeholder
- create-api-token.sh: example IP changed to pve.example.com
- .gitignore: added .env/.env.* exclusion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:40:47 -05:00
Clint Branham 053fd4c06a fix(security): add Uri.EscapeDataString to all URL path parameters
20 instances across 5 service files were missing URL encoding on
dynamic path segments (node, iface, zone, vnet). Fixes D003 regression.

- NodeService: 7 methods
- NetworkService: 7 methods (5 node-network + 2 SDN remove)
- CloudInitService: 3 methods
- TaskService: 2 methods (GetTask, GetTaskLog)
- TemplateService: 1 method (CreateTemplate)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:40:36 -05:00
Clint Branham 625ea0fa28 docs: update README for netstandard2.0, add missing cmdlets, fix version ref
- .NET compatibility table: module ships as netstandard2.0 (not net48/net8.0)
- Add Integration Tests badge
- Add missing cmdlets: Get-PveTaskList, Stop-PveTask, Get/New/Remove-PveApiToken
- Remove "(v1)" from Known Limitations header (currently v0.1.0-preview)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:28 -05:00
Clint Branham 1faaba0a20 perf(ci): run PVE 8 and PVE 9 tests in parallel (max-parallel: 2)
Runner VM now has 2 runner instances registered, so both test matrix
entries can execute simultaneously against their respective pre-
provisioned nested PVE VMs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:41:59 -05:00
Clint Branham 68c528e278 fix(ci): pass test config via shared volume instead of job outputs
GitHub Actions redacts any value that matches a secret or masked string
in job outputs, making them empty in downstream jobs. This affected
host IPs, tokens, and even port 8006 (substring of PVE_ENDPOINT).

The provision job now writes connection details to
/opt/pve-isos/test-config.json on the shared volume. Test jobs read
this file directly with jq — no job outputs needed for sensitive values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:29:18 -05:00
Clint Branham 1dd677a50c fix(ci): stop masking nested PVE IPs so job outputs pass through
::add-mask:: on the VM IP addresses caused GitHub Actions to redact
them in job outputs, making needs.provision.outputs.pve*_host empty
in the test jobs. The IPs are internal lab addresses, not sensitive.
Token masking is retained.

Also removes terraform debug output from prior commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:18:32 -05:00
Clint Branham c8c285e000 fix(ci): use RUNNER_TEMP env var in tfvars heredoc, add debug output
${{ runner.temp }} may resolve differently inside containers. Use
${RUNNER_TEMP} shell variable with unquoted heredoc instead. Added
debug output to diagnose ISO path issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:10:26 -05:00
Clint Branham a424dd18ac refactor(ci): parallel provisioning, file caching, provision/test/cleanup split
Restructures the integration test workflow from a monolithic sequential
job into separate provision → test → cleanup stages:

- provision: creates ALL nested PVE VMs in a single terraform apply
  (parallel), waits for APIs, creates tokens, passes outputs to tests
- test: matrix [pve9, pve8] consumes provision outputs, no provisioning
- cleanup: always runs, API-only teardown for all VMs

Terraform refactored to for_each with pve_instances map variable,
enabling parallel ISO upload and VM creation.

New caching scripts reduce redundant downloads:
- ensure-base-iso.sh: downloads PVE ISOs to /opt/pve-isos if missing
- ensure-cloud-images.sh: caches cloud image + OVA with 7-day TTL
- prepare-auto-iso.sh: --cache-dir flag with hash-based skip

Runner no longer needs manual ISO provisioning (zero-touch setup).
cleanup-images bumped to min-versions-to-keep: 3 to survive overlapping
runs. All jobs gated against dependabot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:05:20 -05:00
Clint Branham d19c6f0c4b fix: upgrade SharpCompress from 0.38.0 to 0.47.3 (#12)
SharpCompress 0.47.3 renamed ReaderFactory.Open() to OpenReader() and
now requires an explicit ReaderOptions parameter. The synchronous API
is still available — no async conversion needed.

Closes #12

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 15:53:56 -05:00
GoodOlClint 40a1390094 Merge pull request #10 from GoodOlClint/dependabot/nuget/tests/PSProxmoxVE.Core.Tests/main/xunit-2.9.3
Bump xunit from 2.7.0 to 2.9.3
2026-03-23 15:49:57 -05:00
dependabot[bot] 6f9ffd9ad1 Bump xunit from 2.7.0 to 2.9.3
---
updated-dependencies:
- dependency-name: xunit
  dependency-version: 2.9.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 20:47:03 +00:00
GoodOlClint 81291d98fd Merge pull request #7 from GoodOlClint/dependabot/nuget/tests/PSProxmoxVE.Core.Tests/main/Moq-4.20.72
Bump Moq from 4.20.70 to 4.20.72
2026-03-23 15:44:20 -05:00
dependabot[bot] 17b42b6a89 Bump Moq from 4.20.70 to 4.20.72
---
updated-dependencies:
- dependency-name: Moq
  dependency-version: 4.20.72
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 20:41:03 +00:00
GoodOlClint c5ad5bcca7 Merge pull request #11 from GoodOlClint/dependabot/nuget/tests/PSProxmoxVE.Core.Tests/main/xunit.runner.visualstudio-3.1.5
Bump xunit.runner.visualstudio from 2.5.7 to 3.1.5
2026-03-23 15:38:00 -05:00
GoodOlClint ac1fb4872d Merge pull request #6 from GoodOlClint/dependabot/nuget/tests/PSProxmoxVE.Core.Tests/main/Microsoft.NET.Test.Sdk-18.3.0
Bump Microsoft.NET.Test.Sdk from 17.9.0 to 18.3.0
2026-03-23 15:37:29 -05:00
GoodOlClint cccd64e817 Merge pull request #5 from GoodOlClint/dependabot/nuget/tests/PSProxmoxVE.Core.Tests/main/coverlet.collector-8.0.1
Bump coverlet.collector from 6.0.1 to 8.0.1
2026-03-23 15:37:16 -05:00
dependabot[bot] fd8030415c Bump xunit.runner.visualstudio from 2.5.7 to 3.1.5
---
updated-dependencies:
- dependency-name: xunit.runner.visualstudio
  dependency-version: 3.1.5
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 20:32:20 +00:00
dependabot[bot] eef68ee96d Bump coverlet.collector from 6.0.1 to 8.0.1
---
updated-dependencies:
- dependency-name: coverlet.collector
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 20:32:19 +00:00
dependabot[bot] 2d3099aaef Bump Microsoft.NET.Test.Sdk from 17.9.0 to 18.3.0
---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.3.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 20:32:17 +00:00
Clint Branham 728e8df335 fix(ci): gate all integration jobs against dependabot
The dependabot actor check was only on the build job. The container-image
and cleanup-images jobs still ran — cleanup-images with if: always()
deleted GHCR container images while the main branch integration run was
still using them, causing PVE 8 tests to fail with "image not found".

Now all four integration jobs (build, container-image, integration,
cleanup-images) are gated with github.actor != 'dependabot[bot]'.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 15:28:19 -05:00
GoodOlClint 6a7379c6fb Merge pull request #4 from GoodOlClint/dependabot/github_actions/main/actions/checkout-6
chore(deps): Bump actions/checkout from 5 to 6
2026-03-23 15:26:39 -05:00
dependabot[bot] 26a43fc52b chore(deps): Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 20:21:00 +00:00
Clint Branham d74f8c952a fix(test): add -Confirm:$false to Suspend/Restart container Pester tests
Suspend-PveContainer and Restart-PveContainer gained ConfirmImpact.High
in F062/F063 fixes. The "Should throw when no session is active" Pester
tests invoke these cmdlets without -Confirm:$false, causing the
confirmation prompt to fire before the session check — failing in
non-interactive CI on all platforms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 15:09:58 -05:00
Clint Branham 339ffaf0bd fix(ci): remove net48 build target from unit-tests, skip integration for dependabot
unit-tests.yml: The publishable project targets netstandard2.0 only
(D009/F047), so the net48 build matrix entry fails. PS 5.1 loads
netstandard2.0 assemblies on .NET Framework 4.8, so the PS 5.1 Pester
tests now use the netstandard2.0 artifact.

integration-tests.yml: Skip entire workflow for dependabot PRs since
integration tests on the self-hosted runner are expensive and dependency
bumps don't warrant full end-to-end testing. Also updates dotnet SDK
from 9.0.x to 10.0.x for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 15:02:04 -05:00
Clint Branham 89bead7a0e fix: add -Confirm:$false to Restart-PveContainer integration test (F080, F081)
Restart-PveContainer gained ConfirmImpact.High in the F062 fix, which
correctly prompts for confirmation on destructive operations. The
integration test was missing -Confirm:$false, causing a
NullReferenceException in non-interactive CI. The cascading failure also
broke the Copy-PveContainer test (F081).

Also updates CONTRIBUTING.md to reference .NET SDK 10.0+ (was 9.0+) to
match all CI workflows and test project TFM (F083).

Includes scan-6 review report and findings database update (F001-F083).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:48:11 -05:00
Clint Branham 68dadbfdc0 fix: remediate findings F045, F047, F048, F064, F070, F071, F076-F079
Phase 1 — Trivial fixes:
- F071: Add Uri.EscapeDataString to GetPveTemplateCmdlet node path
- F077: Add ValidateRange(100, 999999999) to GetPveTaskListCmdlet.VmId
- F076: Create .github/dependabot.yml (nuget + github-actions, weekly)
- F079: Fix unit-tests.yml dotnet SDK from 9.0.x to 10.0.x
- F048: Mark wont_fix — sync-over-async accepted for PS 5.1 compat

Phase 2 — Framework targeting (D009 compliance):
- F047: Reduce publishable csproj to netstandard2.0 only, remove all
  #if NET48/NETSTANDARD2_0 conditionals from PveHttpClient.cs,
  restructure build.yml for netstandard2.0 publish + net10.0/net48 tests
- F064: Resolved by F047 — SMA 7.5.0 ItemGroup removed with net10.0 TFM
- F070: Add PS 5.1 smoke-test job to publish.yml (windows-latest)

Phase 3 — IPveHttpClient interface extraction (F045):
- Extract IPveHttpClient interface from PveHttpClient
- Add constructor injection to all 14 service classes
- Services use injected client when available, create+dispose when not

Phase 4 — Service unit tests (F078):
- 196 new xUnit tests across 10 service test files
- All services tested via Moq-mocked IPveHttpClient
- Total test count: 382 (was 186)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 13:51:37 -05:00
Clint Branham 94424367bf fix: remediate scan-4 findings F058, F062, F063, F071, F072, F073, F074, F075
F058 (critical): Replace while(true) infinite-loop task polling with
TaskService.WaitForTask in 5 container snapshot and storage cmdlets.

F073+F047 (high): Migrate net9.0 → net10.0 across both source .csproj
files, build.yml, publish.yml, and test helper.

F071 (medium): Add Uri.EscapeDataString() to all inline URL path
segments in ~16 cmdlets that bypass service classes (D003).

F062+F063 (medium): Add ConfirmImpact.High to Restart-PveContainer
and Suspend-PveContainer (D006).

F075 (medium): Generate markdown help docs for 89 cmdlets that were
missing documentation (170 total, up from 81).

F072 (low): Remove unused System.Text.Json dependency from Core.csproj.
F074 (low): Raise publish smoke-test threshold from 60 to 150.
F065 (low): Add .github/ISSUE_TEMPLATE/config.yml.
F066 (low): Add CODEOWNERS.

Also fix _TestHelper.ps1 net9.0 → net10.0 framework reference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:54:28 -05:00