v0.317.0: a second copy counts only on a proven different disk; the no-drive install speaks in words (felhom.eu architecture/16, build A)
gates / gates (push) Successful in 46s
gates / gates (push) Successful in 46s
Tier-2 and the one-copy notice ask the host agent (POST /disks/same-disk, agent 0.157.0): different + proven -> made and counted; same disk -> not made (D1); unknown (VM, hidden USB serial) -> made, not counted, the row says why (D2); a NAS never counts and no longer hides the notice (R-1075). An older agent keeps the st_dev rule, logged once. D4: a data-folder or media app on a box with no drive is refused in words with a link to the drives page (R-1068's raw-error half). MinAgent 0.157.0. Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
@@ -355,6 +355,12 @@ type DiskInfo struct {
|
||||
// agent predates the field — the disk-health card + 6h degradation check feature-detect on this and
|
||||
// render "Nincs adat" (never alarm) when nil. See DiskVerdictFor.
|
||||
Smart *SmartSummary `json:"smart,omitempty"`
|
||||
// Physical-disk identity (agent v0.157.0, `16` §3): kind, the keys of the physical disks under the place,
|
||||
// proven|unknown + reason. Empty from an older agent or for an absent drive.
|
||||
Kind string `json:"kind,omitempty"`
|
||||
PhysicalDisks []string `json:"physical_disks,omitempty"`
|
||||
Identity string `json:"identity,omitempty"`
|
||||
IdentityReason string `json:"identity_reason,omitempty"`
|
||||
}
|
||||
|
||||
// FSUUID returns the raw filesystem UUID from a "uuid:<…>" DurableID, or "" if this disk's identity
|
||||
@@ -419,6 +425,29 @@ var ErrFormatRefused = fmt.Errorf("agentapi: format refused — system/backup de
|
||||
// The UI surfaces the type-to-confirm flow, then re-submits with confirmed=true + that durable id.
|
||||
var ErrNeedsConfirmation = fmt.Errorf("agentapi: format needs customer confirmation — user-data device")
|
||||
|
||||
// SameDiskResult is the agent's answer to POST /disks/same-disk (v0.157.0). Act on Proven first:
|
||||
// Proven+Different = a counted second copy; Proven+!Different = the same disk; !Proven = unknown
|
||||
// (Reason vm|network|usb_bridge|weak_wwn|no_serial|unmapped).
|
||||
type SameDiskResult struct {
|
||||
Different bool `json:"different"`
|
||||
Proven bool `json:"proven"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// SameDisk asks the agent whether two guest paths sit on the same physical disk. Gate it with
|
||||
// Supports(FeatureSameDisk) — an older agent answers 404.
|
||||
func (c *Client) SameDisk(ctx context.Context, a, b string) (SameDiskResult, error) {
|
||||
var out SameDiskResult
|
||||
raw, err := c.post(ctx, "/disks/same-disk", map[string]string{"a": a, "b": b})
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
if err := json.Unmarshal(raw, &out); err != nil {
|
||||
return out, fmt.Errorf("agentapi: decode /disks/same-disk: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Disks lists the host drives the agent manages, with a data-bearing flag per drive.
|
||||
func (c *Client) Disks(ctx context.Context) (DisksResponse, error) {
|
||||
var out DisksResponse
|
||||
|
||||
@@ -88,6 +88,11 @@ const FeatureRetainedRecoveryClass Feature = "retained_recovery_class"
|
||||
// Unknown it stays, and an old agent's 404 is answered with the update sentence.
|
||||
const FeatureHostPowerOff Feature = "host_poweroff"
|
||||
|
||||
// FeatureSameDisk — POST /disks/same-disk (agent v0.157.0, felhom.eu architecture/16-storage-physical-identity.md
|
||||
// §4): the agent's physical-disk verdict for two guest paths. Below it the Tier-2 runner keeps the old st_dev
|
||||
// rule (backup.Manager.diskRelation), logged once.
|
||||
const FeatureSameDisk Feature = "same_disk"
|
||||
|
||||
// SupportState is a probe verdict. The zero value is SupportUnknown (fail-open: unknown never
|
||||
// refuses — the existing agent-error paths speak honestly when the agent is down).
|
||||
type SupportState int
|
||||
@@ -168,6 +173,9 @@ var featureProbes = map[Feature]func(ctx context.Context, p SupportProber) error
|
||||
FeatureHostPowerOff: func(ctx context.Context, p SupportProber) error {
|
||||
return errNoRecoveryProbe
|
||||
},
|
||||
FeatureSameDisk: func(ctx context.Context, p SupportProber) error {
|
||||
return errNoRecoveryProbe // not probeable: decided by the version header alone
|
||||
},
|
||||
}
|
||||
|
||||
// errNoMemoryProbe classifies to SupportUnknown (not a *StatusError 404), so a prober that cannot be
|
||||
@@ -204,6 +212,8 @@ var featureMinAgent = map[Feature]string{
|
||||
|
||||
// R-998 — POST /host/poweroff, the household's „A gép kikapcsolása".
|
||||
FeatureHostPowerOff: "0.155.0",
|
||||
|
||||
FeatureSameDisk: "0.157.0",
|
||||
}
|
||||
|
||||
// MinAgentFor returns the declared minimum agent version for a feature ("" when the feature has no
|
||||
|
||||
Reference in New Issue
Block a user