Skip to content

Releases: schankst/go-xen-api-client

v0.2.3 - CI, gofmt-clean generated code, docs

Choose a tag to compare

@schankst schankst released this 30 Jul 19:50

Tooling and documentation release - no API or behavior change, and no
schema change (still XAPI release 26.16.1-next,
xenapi.SchemaXAPIRelease unchanged).

CI

  • New Tests workflow: go build, go vet and go test -short -race on
    every push and pull request, across a matrix of Go 1.16 (the minimum
    go.mod declares) and current stable Go.
  • A repository-wide gofmt check runs alongside it.
  • regenerate.yml now pins the generators to stable Go instead of
    following go.mod's 1.16. The generators format their own output, so
    the toolchain running them decides what the committed files look like,
    and gofmt's rules change between Go releases (Go 1.19 reformatted doc
    comments). The 1.16 minimum for consumers of the library is covered by
    the test matrix instead.

Generated code

  • xenapi.go and gen_errors.go render into memory and run the result
    through go/format before writing, so *_gen.go and error.go are
    gofmt-clean by construction rather than depending on whitespace tuning
    in the templates.
  • Everything was regenerated accordingly. The diff is whitespace-only:
    apart from intra-line spacing (_method + " -> " became
    _method+" -> "), the only change is the blank // line gofmt inserts
    before an indented block in a doc comment. No declaration, signature or
    value changed.

Documentation

  • New UPDATES_AND_PATCHES.md
    • finding pending patches: unapplied pool_update objects,
      repository-based updates, and outstanding post-update guidances.
  • GOOD_TO_KNOW.md: the other_config boot_time/agent_start_time
    convention.
  • MAINTAINING.md: a section on formatting of generated code and why
    regeneration needs a current toolchain.
  • README: Go Reference, release, Go version, license and CI badges.
  • Code of Conduct, issue templates, and a pull request template.
  • LICENSE copyright holder corrected.

v0.2.2 - Event timestamp parsing fix

Choose a tag to compare

@schankst schankst released this 24 Jul 22:43

Bugfix release - no schema change, still XAPI release 26.16.1-next
(xenapi.SchemaXAPIRelease unchanged).

Fixed

  • EventRecord.Timestamp (from Event.Next, i.e. event.next) could
    arrive from a live XCP-ng host as an OCaml-style float-as-string Unix
    timestamp (e.g. "1784931839." - note the trailing dot, from OCaml's
    string_of_float on a whole number) instead of a properly wire-tagged
    dateTime.iso8601 value. convertTimeToGo failed the entire event on
    this mismatch instead of just this one field.
  • convertTimeToGo now falls back to strconv.ParseFloat + time.Unix
    for any datetime-typed field that isn't already a decoded time.Time,
    mirroring this generator's existing enum-tolerance approach to
    schema/wire divergence (baked into xenapi.go, not a one-off patch).

Why this showed up now

Found building a Task/Event-driven VM start/stop monitor in the
xen CLI: watching a XenAPI Task via
Event.Register/Event.Next (scoped to the task class) hit this on
every single event, silently degrading the monitor to polling-only every
time instead of ever using the event stream it was built to use.

See GOOD_TO_KNOW.md
for the write-up, and convert_test.go for regression tests covering the
normal time.Time case, the OCaml float-string case, a plain-integer
fallback, and the invalid-input error case.

v0.2.1 - Enum tolerance baked into the generator

Choose a tag to compare

@schankst schankst released this 24 Jul 20:07

Through v0.2.0, unknown-enum-value tolerance was a separate post-generation step: xenapi.go generated strict, upstream-style hard-erroring enum parsers, then patch_enums.go rewrote convert_gen.go's default cases afterward. That made sense when the fix was first discovered reactively — now that xenapi.go is fully documented and understood (see v0.2.0's documentation pass), there's no reason not to fix it at the source.

convertEnumTypeToGoFuncTemplate now emits the tolerant default case directly. patch_enums.go is removed, along with its step in regenerate.yml — one less moving part in the regeneration pipeline.

No functional change: a full regeneration produces byte-identical output except for the 72 default-case bodies, which are functionally identical to before (same value = EnumType(strValue) fallback, just emitted directly instead of patched in afterward). Verified against a real XCP-ng host.

v0.2.0 - xmlrpc rewritten from scratch (license fix)

Choose a tag to compare

@schankst schankst released this 24 Jul 18:57

Why

amfranz/go-xmlrpc-client, vendored into xmlrpc/ in v0.1.2, has no license — upstream never had one. Fine as a compiled external module dependency; a real problem once fully copied into this repo. Rather than accept that risk (or just wait on an upstream response), rewrote the package from scratch.

What's different

Same public contract (Client, NewClient, Struct, Base64, Call, minus the Params wrapper type — folded directly into Call's signature), materially different internals:

  • Dropped net/rpc's ClientCodec. XML-RPC over HTTP is one POST, one response — no multiplexed-connection semantics to justify net/rpc's channel/sequence-number machinery. Client.Call is now a direct, synchronous round trip.

  • Request building now uses strings.Builder instead of repeated string += concatenation. Benchmarked both against identical payloads:

    old new
    struct, 100 fields 322,683 ns/op, 1413 allocs 14,848 ns/op, 214 allocs ~22x faster
    array, 5000 elements 133 ms/op, 51,424 allocs, 567 MB 0.53 ms/op, 5,026 allocs, 988 KB ~250x faster, ~573x less memory

    The old code was quietly O(n²) on payload size — this isn't a cosmetic rewrite, it's a real algorithmic fix.

  • Response parsing replaces regex-based substring extraction over the raw response bytes with a proper recursive-descent walk of the full <methodResponse> document via encoding/xml.Decoder. Every token read checks its error immediately and, by construction, cannot loop forever on malformed input (the class of bug fixed reactively in v0.1.3 for the old parser).

Tests

Rewritten test suite: httptest.Server-based integration tests for Client.Call (success, XML-RPC fault, pool-affinity cookie capture/replay, concurrent calls), plus value round-trip and malformed-input tests.

Verified against a real XCP-ng host — identical output to v0.1.5.

What this unblocks

This was the last blocker to eventually making this repo public — no part of it is unlicensed third-party code anymore.

v0.1.5 - Tests, error messages, README status

Choose a tag to compare

@schankst schankst released this 24 Jul 18:29

Addresses the remaining items from upstream's original 'Project status' TODO list:

  • Tests: xmlrpc request/response round-trip (string, int, bool, struct, array, nested), specifically including that unknown enum values pass through correctly and that malformed/truncated XML returns an error instead of hanging (the exact class of bug fixed in v0.1.3). Plus a few representative convert_gen.go converter tests. Not exhaustive across all 74 generated files, but covers what has actually broken in this fork's history.
  • CI: go test -short ./... added to the regeneration workflow, so these actually run continuously.
  • Fixed client_test.go's pre-existing TestAuthentication, which required a live server on localhost:40080 and would hang/fail under any CI — now skips under -short.
  • Better error messages: Error.Error() no longer prints blank fields for error codes that don't carry an object type/UUID.
  • README: rewrote the 'Project status' section against this fork's actual state (2 of upstream's 5 TODO items were already resolved, one was already true from the start) and documented that this fork is actively maintained.

v0.1.4 - Documentation fixes

Choose a tag to compare

@schankst schankst released this 24 Jul 18:06

Fills in documentation gaps in the hand-maintained scaffolding around the generated API (the generated code itself was already well documented from the XenAPI schema's own descriptions):

  • Added a package-level doc comment (doc.go) — go doc . previously dumped straight into the symbol list with no introduction.
  • Documented NewClient, Client (via its generator template, so it survives regeneration), Client.APICall, and APIResult — all previously undocumented.
  • Replaced upstream's placeholder // Code ...-style comments on Error's accessor methods with real descriptions (via gen_errors.go's template, so they survive regeneration too).
  • Replaced xmlrpc's leftover TODO: Actual usage documentation here with an actual note.

No functional changes. Verified against a real XCP-ng host.

v0.1.3 - Parser hang-risk fix

Choose a tag to compare

@schankst schankst released this 24 Jul 17:51

Fixes both go vet findings surfaced by vendoring xmlrpc/ in v0.1.2:

  • xmlrpc/request.go: buildValueElement's default case built an fmt.Errorf and discarded it instead of surfacing it, silently emitting an incomplete <value></value> for any unsupported Go type. Now panics instead — every caller in this codebase only ever passes known-safe types, so this is a programmer error, not a runtime condition to tolerate.
  • xmlrpc/result.go: what looked like simple unreachable code (a trailing return after an unconditional for {} loop) turned out to be masking a real bug — getValue, getStructValue, getStructMember, and getArrayValue didn't check the error from parser.Token() inside their loops. On malformed or truncated XML, this could spin forever re-fetching the same error instead of returning it. Added the missing checks throughout (getStructMember now returns an error too) and removed the now-genuinely-unreachable trailing returns.

Verified against a real XCP-ng host afterwards — identical output to v0.1.2, no regressions.

v0.1.2 - Vendor the last external dependency

Choose a tag to compare

@schankst schankst released this 24 Jul 17:45

Vendors amfranz/go-xmlrpc-client (the XML-RPC wire-protocol implementation XenAPI's client sits on) into xmlrpc/, dropping the last remaining external module dependency. go.mod now has zero external requires.

Not a reaction to a vulnerability — the library had none, and per its own go.mod no transitive dependencies either. Purely to remove the last external module reference from this fork's supply chain.

No LICENSE file existed upstream at the time of vendoring; attribution to the original author is preserved in xmlrpc/doc.go.

Side effect: go vet now checks the vendored code too (it never did as an external dependency) and flagged two pre-existing minor issues in xmlrpc/request.go and xmlrpc/result.go (unreachable code, a discarded fmt.Errorf() result). Left as-is for this release; candidates for a follow-up cleanup.

v0.1.1 - Security fix

Choose a tag to compare

@schankst schankst released this 24 Jul 17:31

Removes the github.com/sirupsen/logrus dependency, fixing CVE-2025-65637 (GHSA-4f99-4q7p-p3gh) — a DoS in Entry.Writer() on long single-line payloads, present in the pinned v1.6.0.

logrus was only used in client_test.go's TestMain for log.SetOutput(os.Stdout), which the standard library's log package already does identically. Switched to stdlib log and dropped the dependency entirely rather than just bumping the version — no functional change, one less dependency.

v0.1.0

Choose a tag to compare

@schankst schankst released this 24 Jul 17:20

First tagged release of this private fork.

Why this fork exists

Upstream terra-farm/go-xen-api-client
was last released as v0.0.2, generated from an XenAPI schema snapshot
current only through XenServer 7.3 ("inverness", ~2017). Against a modern
XCP-ng host this broke immediately: the schema predates VM operations like
"sysprep", and the generated enum parsers hard-error on any value they
don't recognize instead of tolerating it.

Changes vs. upstream v0.0.2

Bindings regenerated from the current XenAPI schema (release
26.16.1-next at the time of this release — see xenapi.SchemaXAPIRelease).
Regenerating from a schema this much newer than the generator (xenapi.go)
itself required teaching the generator four schema constructs it didn't
understand yet:

  • lifecycle changed from a bare array to an object ({state, transitions}).
  • New opaque result type "an event batch" (event batching, used by
    Event.from) — mapped to xmlrpc.Struct since it isn't a proper record
    and this fork doesn't need it typed.
  • New polymorphic field type <class> record (Event.snapshot — the
    concrete type depends on the event's class at runtime) — likewise mapped
    to xmlrpc.Struct.
  • New X option type pattern (optional values) — added as a generic
    nil-tolerant wrapper around the inner type's own converter.

Along the way, some enums (e.g. CertificatePurpose, UpdateGuidances)
turned out to be declared under more than one class in the newer schema; the
generator now tracks already-emitted enum names so it doesn't emit the same
Go type twice.

Tolerate unknown enum values from newer XAPI versions, as a hedge against
anything even newer than the schema above. All generated *ToGo enum
converters in convert_gen.go (72 of them, one per enum type) pass unknown
values through as-is instead of failing the whole record parse.

error.go regenerated from the current upstream error definitions
(ocaml/xapi-consts/api_errors.ml) instead of the 7-year-old hand-copied
list: 414 -> 635 constants. Six stale ones were dropped because the
underlying error codes were removed/consolidated upstream (three
VM_LACKS_FEATURE_* variants collapsed into one generic VM_LACKS_FEATURE;
POOL_JOINING_HOST_CANNOT_CONTAIN_NETWORK_BOND / SR_ATTACHED_ON_SLAVE /
SR_DETACHED_ON_MASTER no longer exist upstream). Constant names are now
mechanically derived (ERR_ + uppercased OCaml identifier) instead of
hand-picked, so they can't drift out of sync again.

Automated weekly regeneration via .github/workflows/regenerate.yml
checks upstream xenapi.json for drift and, on success, auto-commits, bumps
the version, tags, and pushes. Fails loudly and opens an issue instead if it
hits a schema construct the generator has never seen. See README for details
and its limits.

Module path changed to github.com/schankst/go-xen-api-client (from
github.com/terra-farm/go-xen-api-client) so it can be pulled in directly
via go get/require without a replace directive.

Versioning

This module's Git tags are its own SemVer, independent of XenAPI's own
versioning — see README's "Versioning" section for why, and for how
xenapi.SchemaXAPIRelease tracks which live XAPI version a given tag was
verified against.