v0.95.0: enrollment wizards use the raw-device scan /disks/candidates (Impl-2b)

Both wizards now source candidates from the agent's Impl-2a raw-device scan
(GET /disks/candidates, proxied) instead of the Observe-based /api/disks — so a
brand-new non-PVE-storage drive is finally discoverable + enrollable end-to-end.
agentapi.ListCandidates + a passthrough proxy (no controller-side filtering; the
agent's unclaimed filter is authoritative). storage_init renders `initialize`,
storage_attach renders `attach`; the enroll flow + Impl-1 guarded mkfs unchanged.
Tests + go build/vet/test clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 18:04:49 +02:00
parent 6ce61e862a
commit feab92ccfc
7 changed files with 174 additions and 33 deletions
+34
View File
@@ -347,6 +347,40 @@ func (c *Client) Disks(ctx context.Context) (DisksResponse, error) {
return out, nil
}
// DiskCandidate mirrors one entry from the agent's GET /disks/candidates (Impl-2a candidates.go) —
// a host disk the agent's unclaimed-disk filter proved is FREE for Felhom to enroll.
type DiskCandidate struct {
Device string `json:"device"`
SizeBytes int64 `json:"size_bytes"`
Model string `json:"model,omitempty"`
FSType string `json:"fstype,omitempty"`
DataBearing bool `json:"data_bearing"`
Mountable bool `json:"mountable"`
MountSource string `json:"mount_source,omitempty"`
DurableID string `json:"durable_id,omitempty"`
}
// CandidatesResult mirrors GET /disks/candidates: disks free to enroll, split into initialize (all
// unclaimed) and attach (the mountable-FS subset).
type CandidatesResult struct {
VMID int `json:"vmid"`
Initialize []DiskCandidate `json:"initialize"`
Attach []DiskCandidate `json:"attach"`
}
// ListCandidates fetches the host disks free for Felhom to enroll (Impl-2b wizard source).
func (c *Client) ListCandidates(ctx context.Context) (CandidatesResult, error) {
var out CandidatesResult
body, err := c.get(ctx, "/disks/candidates")
if err != nil {
return out, err
}
if err := json.Unmarshal(body, &out); err != nil {
return out, fmt.Errorf("agentapi: decode /disks/candidates: %w", err)
}
return out, nil
}
// AssignDisk attaches a drive (by fs-UUID) as a host mount (benign, self-serve).
func (c *Client) AssignDisk(ctx context.Context, uuid, where, fstype, options string) error {
_, err := c.post(ctx, "/disks/assign", map[string]string{