Skip to content

Refactor to Sampler/DSC Community layout and expand REST v0 coverage#14

Open
rcfmartin wants to merge 403 commits into
vatesfr:mainfrom
rcfmartin:dev
Open

Refactor to Sampler/DSC Community layout and expand REST v0 coverage#14
rcfmartin wants to merge 403 commits into
vatesfr:mainfrom
rcfmartin:dev

Conversation

@rcfmartin
Copy link
Copy Markdown

Why the refactor

I use this module in my home lab and at work, so rather than build a new module from scratch, I decided to fork this one and contribute the work back to the community.

Heads-up: this is a big PR, and I'm sorry about that. I need this to be production-ready where I'm running it, which meant bringing the whole repo up to the DSC Community style guidelines in one pass rather than chipping away at it piecemeal. The diff is large, but the individual commits are small and narrowly scoped (one file each, one-line messages), so I tried to make it easier to digest. Also, I tried to follow patterns that were already there as much as possible.

The original module stuffed every function into a handful of big src/<topic>.ps1 files. That works, but it makes it harder to review changes, add tests, and plug the repo into a standard build pipeline. Moving to the layout Sampler scaffolds, which matches the DSC Community guidelines above, gives us:

  • one function per file (easier diffs, less merge pain),
  • a clear Public/Private split so only the supported cmdlets get exported,
  • format data lifted into src/formats/*.ps1xml and registered in the manifest,
  • per-helper Pester tests under tests/Unit/,
  • an Invoke-Build-based build.ps1 plus build.yaml so ResolveDependency / Build / Test / Publish are all one-liners.

Building the module

First time on a fresh checkout, let Sampler bootstrap its own dependencies:

./build.ps1 -ResolveDependency

That drops the tooling (PSDepend, InvokeBuild, Pester, ModuleBuilder, etc.) into ./output/RequiredModules/. After that, ./build.ps1 on its own runs the full build and test pipeline. The built module ends up under ./output/module/xo-powershell/<version>/, so you can import it directly for local testing:

$env:PSModulePath = "$pwd/output/module" + [IO.Path]::PathSeparator + $env:PSModulePath
Import-Module xo-powershell -Force

Documentation generation

The build also uses PlatyPS to keep the cmdlet documentation in sync. During a normal ./build.ps1 run PlatyPS reads each cmdlet's comment-based help and regenerates the matching docs/*.md and about_*.help.txt files, so everything under docs/ is a build artefact so nobody needs to hand-edit it.

Publishing to the PowerShell Gallery

build.yaml already has a publish task list set up:

publish:
  - Publish_Release_To_GitHub
  - Publish_GitHub_Wiki_Content
  - publish_module_to_gallery

Once you're happy with the build output, you can wire ./build.ps1 -Tasks publish into a pipeline (Azure Pipelines, GitHub Actions, whatever you prefer) and it'll:

  1. Cut a GitHub release with generated release notes.
  2. Publish the wiki content.
  3. Push the module to the PowerShell Gallery via publish_module_to_gallery.

All the pipeline needs is a GalleryApiKey (or NuGetApiKey) secret plus a GitHubToken, then two steps: ./build.ps1 -ResolveDependency followed by ./build.ps1 -Tasks publish on your release branch or tag. No more source changes required.

Module layout

src/ went from a handful of multi-function files to the standard Sampler layout:

Location Count Notes
src/Public/*.ps1 200 One cmdlet per file, file name matches the function name
src/Private/*.ps1 43 ConvertTo-Xo*Object, ConvertFrom-Xo*, internal helpers
src/formats/*.ps1xml 34 Table views, all registered in FormatsToProcess
tests/Unit/Private/*.tests.ps1 43 Pester tests

Cmdlet coverage

After the audit, the module covers more than 170 swagger endpoints. Verb breakdown:

Verb Count Verb Count
Get 106 Export 5
Set 21 Connect / Disconnect 4 each
Remove 16 Invoke 7
New 13 Stop / Import 3 each
other (Move, Copy, Add, Resume, Suspend, Enable, Disable, Test, Restart, Start, Update, Clear, Wait) 1–2 each

Fixes to pre-existing code

  • Eight Set-Xo* cmdlets (Host, Network, Pif, Pool, Sr, Vdi, Vif, Vm) were hitting PATCH /resource/{id} endpoints that the current XO REST API doesn't expose. Each one now emits a Write-Warning explaining the limitation and keeps its existing signature so nothing breaks for anyone still calling them.
  • Get-XoPoolVm and Get-XoSrVdi — the /pools/{id}/vms and /srs/{id}/vdis GETs don't exist either; both now delegate to Get-XoVm -PoolUuid / Get-XoVdi -SrUuid.
  • Start-XoSchedule — uses the canonical /schedules/{id}/actions/run path (was /schedules/{id}/run).
  • Invoke-XoPoolAction — two bugs fixed. The URI interpolation was treating $Action?sync= as a null-conditional expression and collapsing the action name, and the body was being sent without Content-Type: application/json, so XO couldn't parse it. JSON bodies now go out as UTF-8 bytes with the right header, and the response handler normalises task-href strings, bare task ids, and {id: …} objects back into an XoPowershell.Task so New-XoVm | Wait-XoTask works the way you'd expect.
  • Get-XoEventSubscription was removed — the REST API has no GET variant for subscriptions.
  • Format XML views — the "ID" column label was renamed to the real property name (VmUuid, TaskId, HostUuid, …) in nine format files. Format-Table no longer shows a column labeled ID that you can't actually address by property.

Help quality

  • Every public cmdlet has .SYNOPSIS, .DESCRIPTION (> 40 chars), a .PARAMETER entry per parameter (> 25 chars each), and at least one realistic .EXAMPLE.
  • Sub-collection cmdlets (Get-Xo<Parent><Child>) got new help text with friendlier resource names and a pipeline example.
  • Action cmdlets now describe their side effects (e.g. Disable-XoHost notes existing VMs keep running).
  • Tag cmdlets call out that they only touch a single tag, unlike Set-Xo* which replaces the whole tag list.
  • Body-carrying action cmdlets point at the XO REST docs path for the expected schema.

Testing

  • Unit tests — 124 Pester test cases across 43 files. Every ConvertTo-Xo*Object, ConvertFrom-Xo*, Set-XoObject, Format-XoSize, Invoke-XoRestMethod, and Invoke-XoPoolAction helper has its own spec, with realistic input cribbed from the swagger examples.

Known limitations (documented, not blockers)

  • There's no Set-XoVm / Set-XoHost / etc. until XO brings the PATCH endpoints back; the deprecation warnings are already in place.
  • Get-XoUserAuthenticationToken and New-XoUserAuthenticationToken only work against the authenticated user's own account. Admin-targeted token ops return 403 from XO.
  • The VBD REST endpoint has no DELETE, so we use Disconnect-XoVbd for cleanup, which is consistent with how VBDs get unplugged in XO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant