Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Dhutton/sre 3199 expose ib kubernetes ufm client for consumption#19

Draft
Dombo wants to merge 33 commits into
masterfrom
dhutton/sre-3199-expose-ib-kubernetes-ufm-client-for-consumption
Draft

Dhutton/sre 3199 expose ib kubernetes ufm client for consumption#19
Dombo wants to merge 33 commits into
masterfrom
dhutton/sre-3199-expose-ib-kubernetes-ufm-client-for-consumption

Conversation

@Dombo
Copy link
Copy Markdown

@Dombo Dombo commented May 27, 2026

WIP

Dombo added 30 commits April 25, 2026 00:03
Move UFM API client logic from the plugin (package main) into
pkg/ufm/ so external consumers can import it directly. The plugin
shim now parses env vars and delegates to ufm.NewClient().
IsPKeyValid and GUIDToString were only used by the UFM client.
Move them in as unexported functions and delete pkg/ib-utils/.
The HTTP client in pkg/drivers/http/ had no consumers outside the UFM
client. Move it into pkg/ufm/http.go with a private interface and
delete pkg/drivers/http/. The mock is now internal to the test files.
The httpClient interface and its implementations are private to
pkg/ufm, so the methods don't need to be exported.
Move Date header parsing into Client.GetServerTime and replace
getServerTime with a generic head method on httpClient. Collapse
createRequest/executeRequest into a single doRequest that returns
body, headers, and status code.
Replace the bespoke HTTP wrapper with idiomatic Go patterns:
- authTransport RoundTripper handles basic auth and content-type
- Functional options (WithTLSInsecure, WithTLSCACert) configure TLS
- Single-method httpClient interface (do) for testability
- HTTP verb helpers (get/post/head) live on Client, not the transport
- NewClient accepts variadic options, derives defaults from Config
- Tests inject mocks via withHTTPClient option through real constructor
…ient

Add StatusError/CapabilityError types replacing string-based error matching.
Add context.Context to all SubnetManagerClient interface methods.
Add version detection with sync.Once caching, VersionInfo, and Capabilities.
Validate() warms the version cache from its existing /ufm_version call.
Pull Config, Option, and TLS option functions into config.go.
Move parseVersionResponse alongside parseVersion in version.go.
client.go drops from 567 to 487 lines with cleaner concern separation.
Delete httpClient interface, httpClientImpl, and mock. Client stores
*http.Client directly. Single do() method handles request execution
and status checking (non-200 returns StatusError). Tests rewritten
with httptest.Server — no mocks, full HTTP stack exercised.
Replace fmt.Sprintf JSON construction with json.Marshal of typed
PKeyGUIDsRequest structs. Move inline response types (GUID, PKey,
PKeyLastUpdatedResponse) to types.go with clearer names (PKeyGUID,
PKeyResponse).
Move EnableIPOverIB, DefaultLimitedPartition, EnableIndex0ForPrimaryPkey,
and SMTimezone out of ufm.Config. The first two were dead code in the
client. The latter two become method parameters: AddGuidsToPKey now
accepts index0 bool, GetLastPKeyUpdateTimestamp accepts *time.Location.

Delete SetConfig from the SubnetManagerClient interface — it existed
solely to pass these fields and is no longer needed. The daemon now
resolves smLocation at startup and passes both values at call sites.
Accept *http.Client directly instead of hiding it behind Config.
Group PKey endpoints into PKeyService (client.PKeys) and version
detection into VersionService (client.Version). Export
BasicAuthTransport for callers to compose their own http.Client.

Delete Config, Option, and TLS helpers — the caller owns transport.
Move plugin metadata (Name/Spec) and interface bridging to an adapter
in the UFM plugin package. Remove Capabilities concept entirely.
All version-endpoint operations now live on VersionService: Get,
Validate, and ServerTime. The top-level Client no longer has any
API methods directly — callers use client.Version.* or client.PKeys.*.
Add WithBasicAuth, WithTLSInsecure, WithTLSCACert, and WithHTTPClient
options to NewClient. The plugin initializer now composes these options
instead of building *http.Client and TLS config inline.

NewClient signature changes from (baseURL, *http.Client) to
(baseURL, ...Option) returning (*Client, error).
Replace the defensive cloneTransport unwrapping logic with a simple
transport() that initializes from http.DefaultTransport when nil.
TLS options now precede WithBasicAuth so they operate on the raw
*http.Transport before auth wrapping occurs.
Options now write to a clientConfig struct instead of mutating the
transport directly. NewClient materializes the transport chain after
all options have been applied: TLS config first, then auth wrapping.
This eliminates the ordering constraint between options.
The separate http.go file held only BasicAuthTransport. Move it
into ufm.go alongside the other transport-related code and delete
the file.
Expose a top-level Connect method on Client for connectivity checks.
The adapter's Validate implementation now delegates to Connect instead
of routing through the version service.
Head issues an uncached HEAD request to the version endpoint and
returns the server time from the Date header. The name reflects the
HTTP method and distinguishes it from the cached Get.
The detect method was a trivial wrapper around do + parseVersionResponse
called from exactly one place. Inline it into Get.
Introduce a per-Client UFM version range with two functional options:

- WithSupportedVersionRange(VersionRange) overrides the default
  SupportedVersionRange.
- WithSupportedVersionEnforcement() makes NewClient call
  EnforceSupportedVersion against the configured range, surfacing
  out-of-range UFM versions before the Client is handed back.

Both can be combined or used independently. Without
WithSupportedVersionEnforcement, callers can still invoke
VersionService.EnforceSupportedVersion explicitly.

Drop VersionInfo.Raw in favour of a String() method that renders
M.m.p (or M.m.p-B when Build > 0) from the existing integer fields,
removing the duplication between parsed and raw forms. Add
VersionInfo.IsZero so range checks can treat missing bounds as
"unbounded".

Store the range on VersionService where it is consumed; Client no
longer carries an unused field.
…rships rename

Lib refinements for the UFM PKey client:

* New sentinel ErrPKeyDoesNotExist + IsPKeyNotFound predicate so callers
  can branch on the "PKey not registered" condition without substring
  matches. AddGUIDsToPKey wraps the sentinel; the human-readable error
  message text is unchanged.

* RemoveGUIDsFromPKey(ctx, RemoveGUIDsFromPKeyRequest) for verb-noun
  symmetry with AddGUIDsToPKey. The request type is intentionally lean
  (PKey + GUIDs only) because UFM's Remove endpoint is DELETE-with-URL
  with no body. RemoveGuids is retained as a thin wrapper.

* AddGUIDsToPKeyRequest.Membership []string renamed to Memberships, with
  the JSON wire field flipped from "membership" to "memberships" per
  strict UFM spec (memberships is the per-GUID array form; membership is
  a singular string applied to all GUIDs). The lib emits only the
  per-GUID form for unambiguity, so wrappers AddGuids/AddLimitedGuids
  pad to the GUID count.

* PKeyGUIDsRequest dropped — orphaned after the AddGUIDsToPKey rework;
  downstream consumers (tufmctl) declare their own stdin shape.
Expose the full method set of *PKeyService and *VersionService as
named interfaces so consumers can:

* Depend on the interface in their own code (testable seams).
* Pass in either the real *PKeyService or a fake from a future
  ufmtest helper package.

Compile-time guards keep the interfaces in lockstep with the
services: if a method is added or signature-changed on either
service, the build fails until the interface declaration catches
up.

No behavioural change. Pure additive surface.
The lib has only been exercised against UFM up to 6.24.1; 6.24.2 and
above are not yet validated. Tighten the default supported range Max
from 6.25.0 to 6.24.2 (exclusive) so the lib fails closed instead of
silently allowing untested UFM versions through.

Tests updated to match the new boundary:

* TestEnforceSupportedVersion_Default_WithinRange — exercises 6.24.1
  (the last fully-supported patch).
* TestEnforceSupportedVersion_Default_AtMax — exercises 6.24.2
  (the new exclusive boundary).
* TestWithSupportedVersionEnforcement_PassesAtInit — switched to 6.24.1.
* TestSupportedVersionRange_Defaults — assertion bumped to 6.24.2.

Consumers needing 6.24.2 or 6.25.x can opt in via
WithSupportedVersionRange to relax the default. The override test
(TestWithSupportedVersionRange_Overrides) still uses the prior wider
range to exercise that path.
Consolidate GUID/MAC/PKey conversion helpers as public API so consumers
can avoid re-implementing the wire-shape ↔ typed-Go conversions in
their own code. Add ClientConfig + NewClientFromConfig for the same
reason on the auth side.

New in pkg/ufm/format.go:
* ParsePKey(string) (int, error) — hex "0x..." or decimal
* FormatPKey(int) string — canonical "0xNNNN" lowercase
* NormalisePKey(string) string — best-effort canonicalisation
* IsPKeyValid(int) bool — promoted from internal isPKeyValid
* ParseGUID(string) (net.HardwareAddr, error) — accepts colon-MAC,
  dash-MAC, or un-delimited 16-hex (case-insensitive)
* ParseGUIDs([]string) ([]net.HardwareAddr, error) — batch form
* FormatGUID(net.HardwareAddr) string — un-delimited 16-hex
* CanonicaliseGUIDString(string) string — strip separators + lowercase
* GUIDStringToMACString(string) string — insert colons every 2 chars

New in pkg/ufm/client_config.go:
* ClientConfig struct (host/username/password/ca_cert/tls_insecure)
* NewClientFromConfig(cfg, opts...) — wire-shape construction; caller
  opts override cfg-derived ones.

Internal pkey.go helpers (isPKeyValid, guidToString, convertToMacAddr)
collapse to the new exported equivalents — no behaviour change.
@Dombo Dombo requested review from Part-TimeWizard and Yarosh May 27, 2026 16:37
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