| index cb5fd3b7e..5bd273fcb 100644 |
| --- a/internal/console/offlinereturn.go |
| +++ b/internal/console/offlinereturn.go |
| @@ -200,7 +200,9 @@ func (s *Server) readReturnReport(ctx context.Context, host, returnOf string, o |
| |
| // returnReadyDecision decides a return-ready report for the return of the |
| // forward o in return mode: it accepts exactly the forward's leftovers and |
| -// otherwise decides as targetReadyDecision does. |
| +// otherwise decides as targetReadyDecision does, in sentences made only of |
| +// what the console knows (an operator door speaks them, and a box's own |
| +// details may carry its errors). |
| // |
| // - the lock: present, owned by the forward's tombstone owner, lifted; |
| // - the instances prefix: every current object older than the forward's |
| @@ -245,25 +247,67 @@ func returnReadyDecision(host, label string, o *moves.OpenRecord, r *moves.Targe |
| for _, p := range lo.EmptyNested { |
| empty[p] = true |
| } |
| - // The leftovers taken out, the rest is the ordinary decision. |
| - c := *r |
| - c.Lock = moves.Fact{Known: true, OK: true} |
| - c.InstancesPrefix = moves.PrefixFacts{Known: true} |
| - c.ParkDir = moves.Fact{Known: true, OK: true} |
| - if c.ProgramsFile.Known { |
| - c.ProgramsFile.OK = true |
| - } |
| - c.DataPaths = map[string]moves.Fact{} |
| - for p, f := range r.DataPaths { |
| - if nested[p] && f.Known && !f.OK { |
| - if o.Layout != moves.LayoutNested || !empty[p] { |
| - return ssDataHeld(host, p) |
| - } |
| - f.OK = true |
| + // The ordinary facts, spoken from what the console knows (the box's |
| + // own details, which may carry its errors, are never repeated to the |
| + // operator here). |
| + switch { |
| + case !r.RouteAbsent.Known: |
| + return couldNotDetermine(factRoute, label) |
| + case !r.RouteAbsent.OK: |
| + return ssRouteHeld(host) |
| + case !r.TimersAbsent.Known: |
| + return couldNotDetermine(factTimers, label) |
| + case !r.TimersAbsent.OK: |
| + return ssTimersHeld(host) |
| + case !r.LiveLink.Known: |
| + return couldNotDetermine(factLiveLink, label) |
| + case !r.LiveLink.OK: |
| + return ssStaleLiveLinkHere(host) |
| + case !r.ProgramsFile.Known: |
| + return couldNotDetermine(factProgramsFile, label) |
| + } |
| + for _, p := range targetDataPaths(host, r.DataPaths) { |
| + f, ok := r.DataPaths[p] |
| + switch { |
| + case !ok || !f.Known: |
| + return couldNotDetermine(p, label) |
| + case f.OK: |
| + case nested[p] && o.Layout == moves.LayoutNested && empty[p]: |
| + // The nested forward's directory, or its mount view, left |
| + // empty. |
| + default: |
| + return ssDataHeld(host, p) |
| + } |
| + } |
| + switch { |
| + case !r.ParkDir.Known: |
| + return couldNotDetermine(factParkDir, label) |
| + case !r.DiskFree.Known: |
| + return ssDiskLow(host) |
| + case !r.MovesPrefix.Known: |
| + return couldNotDetermine(factMovesPrefix, label) |
| + case len(r.MovesPrefix.Objects) > 0: |
| + return ssBucketHeld(host) |
| + case !r.StagePathArmed.Known: |
| + return couldNotDetermine(factStagePath, label) |
| + case !r.StagePathArmed.OK: |
| + return msgSelfServeStageUnarmed |
| + } |
| + for _, c := range want.caps { |
| + if !containsString(r.Capabilities, c) { |
| + return ssLacksCapability(c) |
| } |
| - c.DataPaths[p] = f |
| } |
| - return targetReadyDecision(host, label, &c, 0, true, want) |
| + if want.secretsFP == "" || r.SecretsKeyFP != want.secretsFP { |
| + return msgSelfServeKeyMismatch |
| + } |
| + return "" |
| +} |
| + |
| +// ssStaleLiveLinkHere is return mode's live link that does not name the |
| +// channel's build (pending copy review). |
| +func ssStaleLiveLinkHere(host string) string { |
| + return fmt.Sprintf("the target box has a stale live link for %s", host) |
| } |
| |
| // firstDataPath is the report's <host>.db path (the data directory's |