Skip to content

feat(fleet): add -FleetKey to New-PfbFleetMember with self-identification - #147

Merged
juemerson-at-purestorage merged 3 commits into
dmann000:mainfrom
juemerson-at-purestorage:feat/fleetmember-fleetkey
Aug 26, 2026
Merged

feat(fleet): add -FleetKey to New-PfbFleetMember with self-identification#147
juemerson-at-purestorage merged 3 commits into
dmann000:mainfrom
juemerson-at-purestorage:feat/fleetmember-fleetkey

Conversation

@juemerson-at-purestorage

Copy link
Copy Markdown
Collaborator

What this adds

New-PfbFleetMember gains a -FleetKey parameter, so joining a fleet no longer requires the caller to hand-build the request body and to already know the joining array's own id.

Before:

New-PfbFleetMember -FleetName 'fleet-prod' -Members @{ key = $k; member = @{ id = $selfId } }

After:

$key = New-PfbFleetKey -Array $existingMember
New-PfbFleetMember -FleetName 'fleet-prod' -FleetKey $key.fleet_key -Array $joiningArray

Both halves were recoverable from context. The key comes straight from New-PfbFleetKey, and POST /fleets/members must be called from the array that is joining — so that array is by definition the one the connection already points at. -FleetKey takes the key and resolves the id with Get-PfbArray over the same connection.

-Members stays exactly as it was, as the passthrough for what the convenience path cannot express: several members in one call, or a member other than the array being talked to. The two are separate parameter sets, so the engine rejects supplying both rather than one silently winning.

The wire shape is unchanged. This is an ergonomics layer over the contract fixed in #38, not a change to it. A test asserts the -FleetKey body is byte-identical to the equivalent explicit -Members body, and the live probe below confirms it against real arrays.

-FleetId is not made redundant by this

Worth stating, because the two look interchangeable and are not. Per the spec for POST /fleets/members, fleet_ids/fleet_names are query parameters and the key is a body field inside members[]: the selector says which fleet, the key authorises the join. The key names no fleet, so a selector is still needed. A test pins -FleetId and -FleetKey composing.

One defect deliberately not carried over

An earlier unmerged attempt at this built its ShouldProcess target as "${($self.name)}:${FleetName}", which renders :fleet-prod rather than fb-a:fleet-prod${...} delimits a variable name in PowerShell, not a subexpression. This uses $($self.name) and the live -WhatIf output below confirms it renders.

Behaviour notes

  • Get-PfbArray is called before ShouldProcess, so it runs under -WhatIf too. It is a read, and without it there is no body to describe. Documented on the parameter.
  • If no array id can be resolved, the cmdlet throws rather than POSTing a member reference with an empty id.

Verification

Scoped Pester, both editions, all green. New-PfbFleetMember (20 tests), plus the help- and ShouldProcess-coverage sweeps, the drift-confidence tests, the committed-report guards and the Build-* artifact gates — 93 passed / 0 failed on the artifact leg, 34 passed / 0 failed on the Windows PowerShell 5.1 leg. The full suite is CI's job.

Live-verified against three lab FlashBlade arrays (Purity//FB 4.8.2, REST 2.26) forming a fleet — the coordinator and both non-coordinator members. Code under test feat/fleetmember-fleetkey@2709150, connected as a directory account:

Probe Coordinator Member A Member B
-FleetKey, real key, correct fleet 200 200 200
-Members, same key and same self id 200, identical body 200, identical 200, identical
-FleetKey, nonexistent fleet name 400 Fleet name does not match the provided fleet key. same same

The first two rows returning identical responses is the claim: the convenience path is not a different request. Each array's response echoed its own id with is_local: true, so Get-PfbArray self-identification resolved correctly per array rather than happening to work once. The third row is the control — a specific and different error rules out "the endpoint rejects everything" and shows both the key and the fleet selector were parsed and compared.

-WhatIf rendered each array's own name, e.g. What if: Performing the operation "Add fleet member" on target "<array> into <fleet>".

Honest limit: no array joined a fleet it was absent from. All three were already members, and evicting one would have destroyed a standing lab fixture. A 200 from an already-joined array proves the request body is well-formed and the key validates against the fleet; it is not evidence that enrollment of a new member works. That remains untested on the wire, as it was before this change.

Incidentally measured and worth knowing for anyone testing this family: fleet writes reject a local static array account with HTTP 400 "Operation not permitted." — on the coordinator and on a member alike, with no fleet context involved. POST /fleets/fleet-key and POST /fleets/members both need a directory account.

Derived artifacts

Reports/PfbDeadKeyReport.json, Reports/PfbFieldCmdletMap.json and Reports/PfbFieldCmdletMapping.md are regenerated. -FleetKey is a typed parameter whose wire name the field mapper cannot resolve, because its value lands nested at members[].key rather than as a parameter the spec names directly — so it joins the "typed but unresolved wire name" list (51 → 52), with the matching +1 in the dead-key report's parametersInventoried and its wire name unresolved skip reason. No dead key appears or disappears.

That +1 also raises the ceiling Tests/CommittedDeadKeyReport.Tests.ps1 puts on that skip reason, 126 → 127. The existing 126 was set for the same case and its comment already names the test a raise has to pass: the parameter is new, so nothing that was evaluable stopped being evaluated, and it was never evaluable as a query key in the first place. Neither is the coverage regression the ceiling exists to catch, and the route to the wire is evidenced by the live probe rather than assumed. The comment now spells out both conditions, so the next person to hit this red has to establish them rather than bump the number.

Not included

No version bump and no CHANGELOG.md entry — the maintainer's separate decision.

…tion

Joining a fleet needed the caller to hand-build the request body and to know
the joining array's own id:

    New-PfbFleetMember -FleetName f -Members @{ key = $k; member = @{ id = $selfId } }

Both halves are recoverable from context. The key comes from New-PfbFleetKey,
and POST /fleets/members must be called from the array that is joining, so
that array is by definition the one the connection already points at. -FleetKey
takes the key and resolves the id with Get-PfbArray over the same connection.

-Members stays as the passthrough for what the convenience path cannot express
-- several members in one call, or a member other than the array being talked
to. The two are separate parameter sets so the engine rejects supplying both,
rather than one silently winning.

-FleetId is untouched and is not made redundant by this. The spec has
fleet_ids and fleet_names as the query-parameter selector and the key as a body
field: the selector says which fleet, the key authorises the join. A test pins
that the two compose.

The wire shape is unchanged -- a test asserts the -FleetKey body is byte-equal
to the equivalent explicit -Members body -- so this is an ergonomics layer over
the contract fixed in dmann000#38, not a change to it.

Get-PfbArray is called before ShouldProcess, so it runs under -WhatIf too. It
is a read, and without it there is no body to describe. If it yields no id the
cmdlet throws rather than POSTing a member reference with an empty id.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-FleetKey is a typed parameter whose wire name cannot be resolved by the
field/cmdlet mapper, because it lands in the request body nested under
members[].key rather than as a parameter the spec names directly. So it joins
the "typed but unresolved wire name" list, 51 to 52, and the dead-key report's
parametersInventoried rises 2167 to 2168 with the same +1 under its
"wire name unresolved" skip reason. No dead key appears or disappears.

Regenerated through scripts/Assert-PfbDerivedArtifacts.ps1 -UpdateCommitted so
the output is the one the CI gate compares against, then normalised to LF --
the generators emit CRLF on Windows and the committed reports are LF-only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dead-key gate ceilings each skip reason, because a key the generator
cannot evaluate hides a dead key just as effectively as one it evaluates and
passes. -FleetKey pushes 'wire name unresolved' from 126 to 127 and reds it.

This is the same case the existing 126 was set for, and the comment there
already names the test: the parameter is NEW, so nothing that was evaluable
stopped being evaluated, and it was never evaluable as a query key in the
first place -- its value goes into the request body at members[].key, which
the AST resolver does not follow into. Neither condition is a coverage
regression, which is what the ceiling exists to catch.

The route to the wire is evidenced rather than assumed: POST /fleets/members
returns 200 naming the member the key was sent for, on all three lab arrays.

The comment now states both conditions a future raise has to meet, so the
next person hitting this red has to establish them rather than bumping the
number to clear it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@juemerson-at-purestorage
juemerson-at-purestorage merged commit 76f0c95 into dmann000:main Aug 26, 2026
6 checks passed
@juemerson-at-purestorage
juemerson-at-purestorage deleted the feat/fleetmember-fleetkey branch August 26, 2026 01:15
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