v0.254.0 — the saved notes follow the language, and the switch becomes a globe (R-557 slice 2 release C; SLICE 2 CLOSED)
gates / gates (push) Successful in 23s
gates / gates (push) Successful in 23s
The notes a background run SAVES — last night's backup line, the last error, the proof result, the restore outcome — are written in the BOX's language at the moment they are written. A household that switches sees the previous run's note in the old language until the next run rewrites it: the operator's §16 option 1, stated rather than hidden. EndRestoreOp no longer receives a Hungarian literal from anywhere. The language switch is a globe. Two text links wrapped in the sidebar footer and asked the reader to recognise "Magyar"/"English" as links; a globe is the one symbol every web user already reads as "language", so nobody has to read Hungarian to escape Hungarian. It is <details>/<summary> — a menu with no script, drawn inline because the icon sprite lives only in layout.html and the visitor pages have their own shell. Those visitor pages get the same globe, and a visitor's choice stays theirs: a display-only felhom_lang cookie that langFor reads ONLY when there is no session. A signed-in household can never inherit a language a previous visitor picked in the same browser. POST /lang is CSRF-exempt for a narrow reason written at the exemption — its only achievable effect is the language of the page the victim's own browser shows them — and safeBackPath refuses //evil.example as well as https://, because "starts with /" alone is not the test. §16 taken: a successful claim carries the cookie into the household's setting. TWO PARITY EXCEPTIONS, MEASURED: 106 fixtures compared with a real diff — exactly two change shapes (the dashboard footer, the globe in the shells) and 5 byte-identical, which are the three pages that must not change. I INTRODUCED A DEADLOCK AND THE SUITE CAUGHT IT BY HANGING. UpdateOffboxStatus holds the settings write lock while running its callback; boxLang() wants the read lock; sync.RWMutex is not reentrant. On a real box an off-site run would have hung forever HOLDING the settings lock. Fixed by resolving the language before the callback, and guarded by a test that names the file and line in a second instead of hanging for 25 minutes. MinAgent: 0.131.0 (unchanged). No hub release needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -1526,9 +1526,9 @@ func (s *Server) buildAppBackupRows(status *backup.FullBackupStatus, lang string
|
||||
// preserved leg those are different dates and the run's is the flattering one.
|
||||
pkgDate, stale := rp.CopyDate, rp.PackagePreserved
|
||||
row.Tier2CopyDate, row.Tier2CopyDateProven = pkgDate, rp.CopyDateProven
|
||||
row.Tier2UnitConfirm = tier2UnitConfirmWithStaleness(pkgDate, row.Tier2CopyDateProven, stale)
|
||||
row.Tier2UnitConfirm = s.tier2UnitConfirmWithStaleness(pkgDate, row.Tier2CopyDateProven, stale)
|
||||
if stale && pkgDate != "" {
|
||||
row.Tier2UnitStaleNotice = fmt.Sprintf(tier2UnitStaleNoticeFmt, fmtRFC3339Local(pkgDate))
|
||||
row.Tier2UnitStaleNotice = s.msgLang(lang, tier2UnitStaleNoticeFmtKey, fmtRFC3339Local(pkgDate))
|
||||
}
|
||||
}
|
||||
switch cd.LastStatus {
|
||||
@@ -1662,7 +1662,7 @@ func (s *Server) backupRestoreHandler(w http.ResponseWriter, r *http.Request) {
|
||||
res, err := s.backupMgr.RestoreFromRecoveryUnit(stackName)
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Restore failed (async): stack=%s: %v", stackName, err)
|
||||
s.backupMgr.EndRestoreOp(false, "Visszaállítás sikertelen: "+err.Error())
|
||||
s.backupMgr.EndRestoreOp(false, s.note("note.restore.unit_failed", s.noteErr(err)))
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[INFO] [web] Restore completed (async): stack=%s in %s (volumes %d/%d, dbs %d/%d)",
|
||||
@@ -1672,7 +1672,7 @@ func (s *Server) backupRestoreHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// The customer reads it as "my data is back". The snapshot id is dropped from the sentence
|
||||
// deliberately: it identified WHICH backup ran and told the customer nothing about what came out
|
||||
// of it, which is the question the sentence exists to answer.
|
||||
s.backupMgr.EndRestoreOp(true, unitRestoreOutcomeMsg(stackName, res))
|
||||
s.backupMgr.EndRestoreOp(true, s.unitRestoreOutcomeMsg(stackName, res))
|
||||
}()
|
||||
http.Redirect(w, r, "/backups/restore?"+flashQuery("flash", "flash.restore.started"), http.StatusFound)
|
||||
}
|
||||
@@ -1702,31 +1702,31 @@ func (s *Server) backupRestoreHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// that have nothing to do with whether the app has a database.
|
||||
//
|
||||
// No filesystem path appears in the message, only counts — same rule as reconstituteOutcomeMsg.
|
||||
func unitRestoreOutcomeMsg(app string, res backup.UnitRestoreResult) string {
|
||||
func (s *Server) unitRestoreOutcomeMsg(app string, res backup.UnitRestoreResult) string {
|
||||
if res.VolumesReplayed > 0 || res.DBsReplayed > 0 {
|
||||
var what string
|
||||
if res.VolumesReplayed > 0 {
|
||||
what = fmt.Sprintf("%d adatkötet", res.VolumesReplayed)
|
||||
what = s.note("note.restore.volumes_count", res.VolumesReplayed)
|
||||
}
|
||||
if res.DBsReplayed > 0 {
|
||||
if what != "" {
|
||||
what += " és az adatbázis"
|
||||
what += s.note("note.restore.and_database")
|
||||
} else {
|
||||
what = "az adatbázis"
|
||||
what = s.note("note.restore.the_database")
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(unitRestoreDataMsgFmt, app, what)
|
||||
return s.note(unitRestoreDataKey, app, what)
|
||||
}
|
||||
// An unknown must never be drawn as a zero. The no-unit fallback cannot report counts, and the
|
||||
// zero-value shape would otherwise read as „the backup held only settings" over a restore that may
|
||||
// have replayed the app's whole dataset. Same failure direction as R-88: degrade to UNKNOWN.
|
||||
if res.CountsUnknown {
|
||||
return fmt.Sprintf(unitRestoreCountsUnknownMsgFmt, app)
|
||||
return s.note(unitRestoreCountsUnknownKey, app)
|
||||
}
|
||||
if res.ManifestVolumes+res.ManifestDBs > 0 {
|
||||
return fmt.Sprintf(unitRestoreNoneReturnedMsgFmt, app, res.ManifestVolumes, res.ManifestDBs)
|
||||
return s.note(unitRestoreNoneReturnedKey, app, res.ManifestVolumes, res.ManifestDBs)
|
||||
}
|
||||
return fmt.Sprintf(unitRestoreSettingsOnlyMsgFmt, app)
|
||||
return s.note(unitRestoreSettingsOnlyKey, app)
|
||||
}
|
||||
|
||||
// R-353 customer-facing strings. Named constants, not inlined, because each is asserted verbatim by
|
||||
@@ -1735,12 +1735,12 @@ func unitRestoreOutcomeMsg(app string, res backup.UnitRestoreResult) string {
|
||||
const (
|
||||
// unitRestoreDataMsgFmt — data really came back. %s app, %s the "N adatkötet[ és az adatbázis]"
|
||||
// clause built above.
|
||||
unitRestoreDataMsgFmt = "A(z) %s: %s visszaállítva — az alkalmazás újraindult."
|
||||
unitRestoreDataKey = "note.unit_restore_data"
|
||||
|
||||
// unitRestoreSettingsOnlyMsgFmt — nothing came back and the unit listed nothing. The FIGYELEM
|
||||
// sentence is a statement about THE BACKUP; it deliberately says nothing about whether the app has
|
||||
// data of its own, because the manifest cannot answer that (R-355).
|
||||
unitRestoreSettingsOnlyMsgFmt = "A(z) %s: a beállítások visszaálltak — az alkalmazás újraindult. FIGYELEM: ez a mentés csak a beállításokat tartalmazta, adatot nem. Az alkalmazás adatai NEM álltak vissza ebből a mentésből."
|
||||
unitRestoreSettingsOnlyKey = "note.unit_restore_settings_only"
|
||||
|
||||
// unitRestoreNoneReturnedMsgFmt — the unit listed data and none of it returned. %d volumes, %d
|
||||
// database dumps LISTED. It states the data is unchanged because that is true and load-bearing: the
|
||||
@@ -1748,9 +1748,9 @@ const (
|
||||
// who believes otherwise will do something worse than waiting.
|
||||
// unitRestoreCountsUnknownMsgFmt — the no-unit fallback. It claims only what is known: the restore
|
||||
// ran and the app is back. It deliberately does NOT say data returned and does NOT say it did not.
|
||||
unitRestoreCountsUnknownMsgFmt = "A(z) %s visszaállítása lefutott — az alkalmazás újraindult. Ehhez a mentéshez nem tartozik mentési egység, ezért nem tudjuk megmondani, mi állt vissza belőle. Ellenőrizd az alkalmazásban, hogy megvannak-e az adataid."
|
||||
unitRestoreCountsUnknownKey = "note.unit_restore_counts_unknown"
|
||||
|
||||
unitRestoreNoneReturnedMsgFmt = "A(z) %s: FIGYELEM — a mentés %d adatkötetet és %d adatbázis-mentést sorol fel, de egyik sem állt vissza. Az adataid változatlanok maradtak. Kérj segítséget, mielőtt újra próbálod."
|
||||
unitRestoreNoneReturnedKey = "note.unit_restore_none_returned"
|
||||
)
|
||||
|
||||
// monitoringIntegritySchedule (R-359) describes what the off-site integrity job actually does.
|
||||
@@ -1779,13 +1779,13 @@ const (
|
||||
// no file legs but a full unit mirror sitting in the copy — and it sent those customers to a
|
||||
// button on another page for data that is now restorable on the page they are already looking at.
|
||||
// tier2UnitAvailableMsg is that case now.
|
||||
tier2NoCoverageMsg = "Ennek az alkalmazásnak az adatai nem ebből a másolatból állíthatók vissza — az alkalmazás nem állt le. Használd a Visszaállítás indítása gombot a Biztonsági mentés → Visszaállítás oldalon."
|
||||
tier2NoCoverageKey = "note.tier2_no_coverage"
|
||||
|
||||
// tier2UnitAvailableMsg (R-103) — the copy holds no restorable FILES, but it does hold an openable
|
||||
// recovery unit, so the answer is the action beside this one, not a different page. It names the
|
||||
// button by its own label and says why the two differ, because the difference is the whole reason
|
||||
// they are not one button: this one overwrites.
|
||||
tier2UnitAvailableMsg = "Ennek az alkalmazásnak az adatai nem fájlokban, hanem az alkalmazás saját adatbázisában és köteteiben vannak — az alkalmazás nem állt le. Ezeket a mellette lévő „Teljes visszaállítás a másolatból” gombbal tudod visszahozni ugyanerről a másolatról. Figyelem: az a művelet FELÜLÍRJA a jelenlegi adatokat, míg ez a gomb csak a hiányzó fájlokat pótolja."
|
||||
tier2UnitAvailableKey = "note.tier2_unit_available"
|
||||
|
||||
// tier2UnitNotCoveredMsg is appended wherever the FILE restore DID run, so a clean result never
|
||||
// reads as a clean bill of health for data the operation never opened.
|
||||
@@ -1795,17 +1795,17 @@ const (
|
||||
// database or the named volumes. Deleting it would let a clean file-restore result read as a clean
|
||||
// bill of health for data the operation did not look at, which is the sentence it exists to
|
||||
// prevent.
|
||||
tier2UnitNotCoveredMsg = "Az alkalmazás adatbázisa és belső kötetei nem tartoznak ebbe a visszaállításba."
|
||||
tier2UnitNotCoveredKey = "note.tier2_unit_not_covered"
|
||||
|
||||
// The Tier-2 UNIT restore's outcome suffix (R-102, Scenario E). The outcome names WHICH copy was
|
||||
// just written over the app's live data — an action that overwrites must say what it overwrote
|
||||
// with, in the sentence the customer is left holding.
|
||||
tier2UnitRestoreSourceMsgFmt = "A visszaállítás forrása a második meghajtón lévő másolat volt (%s)."
|
||||
tier2UnitRestoreSourceKey = "note.tier2_unit_restore_source"
|
||||
|
||||
// …and the R-101 variant. CopyLastRun is the ATTEMPT clock: it advances on a FAILED Tier-2 run
|
||||
// too. Where no success has ever been recorded for this app, the date shown is evidence that a
|
||||
// copy was attempted and nothing more, and the sentence must not present it as evidence of a copy.
|
||||
tier2UnitRestoreSourceUnprovenMsgFmt = "A visszaállítás forrása a második meghajtón lévő másolat volt (%s — ez az utolsó mentési kísérlet ideje; azt nem tudjuk igazolni, hogy az sikeres volt)."
|
||||
tier2UnitRestoreSourceUnprovenKey = "note.tier2_unit_restore_source_unproven"
|
||||
|
||||
// R-102/R-103 — the DESTRUCTIVE CONFIRM, in pieces, and in Go rather than in the template.
|
||||
//
|
||||
@@ -1822,25 +1822,25 @@ const (
|
||||
// Contrast — how it differs from the additive button beside it. The register's own requirement:
|
||||
// a destructive operation reached from a non-destructive surface must carry the
|
||||
// difference in the confirm, not rely on the customer inferring it from a label.
|
||||
tier2UnitActionLabel = "Teljes visszaállítás a másolatból"
|
||||
tier2UnitActionLabelKey = "note.tier2_unit_action_label"
|
||||
|
||||
tier2UnitConfirmBase = "Ez a művelet FELÜLÍRJA az alkalmazás jelenlegi adatait – az adatbázisát és a belső köteteit is – a második meghajtón lévő másolattal. Ami a másolat óta keletkezett, elveszik."
|
||||
tier2UnitConfirmBaseKey = "note.tier2_unit_confirm_base"
|
||||
|
||||
tier2UnitConfirmDateFmt = " A másolat kelte: %s."
|
||||
tier2UnitConfirmDateFmtKey = "note.tier2_unit_confirm_date_fmt"
|
||||
|
||||
tier2UnitConfirmDateUnprovenFmt = " A másolat kelte: %s – ez az utolsó mentési kísérlet ideje, azt nem tudjuk igazolni, hogy sikeres volt."
|
||||
tier2UnitConfirmDateUnprovenFmtKey = "note.tier2_unit_confirm_date_unproven_fmt"
|
||||
|
||||
tier2UnitConfirmContrast = " A mellette lévő „Fájlok visszaállítása” ezzel szemben csak a hiányzó fájlokat pótolja, és semmit nem ír felül. Az alkalmazás a művelet idejére leáll."
|
||||
tier2UnitConfirmContrastKey = "note.tier2_unit_confirm_contrast"
|
||||
|
||||
// tier2UnitStaleClause (R-403) — the package in this copy is OLDER than the copy's newest run,
|
||||
// because that run PRESERVED it rather than replacing it with an empty one. Without this the
|
||||
// confirm would name a date the customer reads as "last night" over a package from before it.
|
||||
// The whole point of preserving the copy is lost if the surface then misdescribes what it kept.
|
||||
tier2UnitStaleClause = " FIGYELEM: ennek a másolatnak az adatcsomagja régebbi, mint a legutóbbi mentés — a fő meghajtón lévő csomag hiányos volt, ezért a meglévő, teljes másolatot megőriztük. A visszaállítás a fent megadott csomagot használja."
|
||||
tier2UnitStaleClauseKey = "note.tier2_unit_stale_clause"
|
||||
|
||||
// tier2UnitStaleNoticeFmt (R-403) — the same fact on the per-app backup card, where the customer
|
||||
// looks BEFORE deciding anything. %s is the package's own date.
|
||||
tier2UnitStaleNoticeFmt = "A másolat adatcsomagja régebbi, mint a legutóbbi mentés (%s): a fő meghajtón lévő csomag hiányos volt, ezért a meglévő, teljes másolatot megőriztük."
|
||||
tier2UnitStaleNoticeFmtKey = "note.tier2_unit_stale_notice_fmt"
|
||||
)
|
||||
|
||||
// tier2UnitConfirmMsg assembles the destructive confirm for one app's Tier-2 unit restore. Pure, so
|
||||
@@ -1848,26 +1848,26 @@ const (
|
||||
//
|
||||
// A copy with no recorded date at all still gets a confirm — it just cannot name one. Dropping the
|
||||
// whole confirm because a date is missing would remove the warning and keep the destruction.
|
||||
func tier2UnitConfirmMsg(copyDate string, proven bool) string {
|
||||
return tier2UnitConfirmWithStaleness(copyDate, proven, false)
|
||||
func (s *Server) tier2UnitConfirmMsg(copyDate string, proven bool) string {
|
||||
return s.tier2UnitConfirmWithStaleness(copyDate, proven, false)
|
||||
}
|
||||
|
||||
// tier2UnitConfirmWithStaleness is tier2UnitConfirmMsg for a copy whose PACKAGE may be older than its
|
||||
// newest run (R-403). ONE implementation, two callers — the two-argument form above is the ordinary
|
||||
// case where the run really did refresh the package.
|
||||
func tier2UnitConfirmWithStaleness(copyDate string, proven bool, stale bool) string {
|
||||
msg := tier2UnitConfirmBase
|
||||
func (s *Server) tier2UnitConfirmWithStaleness(copyDate string, proven bool, stale bool) string {
|
||||
msg := s.note(tier2UnitConfirmBaseKey)
|
||||
if copyDate != "" {
|
||||
if proven {
|
||||
msg += fmt.Sprintf(tier2UnitConfirmDateFmt, fmtRFC3339Local(copyDate))
|
||||
msg += s.note(tier2UnitConfirmDateFmtKey, fmtRFC3339Local(copyDate))
|
||||
} else {
|
||||
msg += fmt.Sprintf(tier2UnitConfirmDateUnprovenFmt, fmtRFC3339Local(copyDate))
|
||||
msg += s.note(tier2UnitConfirmDateUnprovenFmtKey, fmtRFC3339Local(copyDate))
|
||||
}
|
||||
}
|
||||
msg += tier2UnitConfirmContrast
|
||||
msg += s.note(tier2UnitConfirmContrastKey)
|
||||
// R-403 last, so it is the sentence the customer is left holding before they press.
|
||||
if stale {
|
||||
msg += tier2UnitStaleClause
|
||||
msg += s.note(tier2UnitStaleClauseKey)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
@@ -1876,7 +1876,7 @@ func tier2UnitConfirmWithStaleness(copyDate string, proven bool, stale bool) str
|
||||
// no date is recorded at all. It asks Tier2CopyDate — the SAME resolver the surface uses to pick the
|
||||
// date it puts in the confirm — so the sentence the customer approves and the sentence they are left
|
||||
// with cannot name different copies.
|
||||
func tier2UnitSourceMsg(cov backup.Tier2Coverage) string {
|
||||
func (s *Server) tier2UnitSourceMsg(cov backup.Tier2Coverage) string {
|
||||
// R-403: the OUTCOME names the same date the CONFIRM did — the PACKAGE's, not the copy's newest
|
||||
// run. Live on demo-hp 2026-08-31 these disagreed by two and a half hours (confirm 11:43, outcome
|
||||
// 14:23) after a preserved leg, and a customer reading both would not know which restore they had
|
||||
@@ -1887,9 +1887,9 @@ func tier2UnitSourceMsg(cov backup.Tier2Coverage) string {
|
||||
return ""
|
||||
}
|
||||
if proven {
|
||||
return fmt.Sprintf(tier2UnitRestoreSourceMsgFmt, fmtRFC3339Local(date))
|
||||
return s.note(tier2UnitRestoreSourceKey, fmtRFC3339Local(date))
|
||||
}
|
||||
return fmt.Sprintf(tier2UnitRestoreSourceUnprovenMsgFmt, fmtRFC3339Local(date))
|
||||
return s.note(tier2UnitRestoreSourceUnprovenKey, fmtRFC3339Local(date))
|
||||
}
|
||||
|
||||
// backupTier2RestoreHandler (C2, closes F2) restores an app's MISSING user files in place from its
|
||||
@@ -1932,9 +1932,9 @@ func (s *Server) backupTier2RestoreHandler(w http.ResponseWriter, r *http.Reques
|
||||
// restorable on this one.
|
||||
cov, covErr := s.backupMgr.Tier2RestoreCoverage(stackName)
|
||||
if covErr == nil && !cov.CanRestore() {
|
||||
msg := tier2NoCoverageMsg
|
||||
msg := s.note(tier2NoCoverageKey)
|
||||
if cov.CanRestoreUnit() {
|
||||
msg = tier2UnitAvailableMsg
|
||||
msg = s.note(tier2UnitAvailableKey)
|
||||
}
|
||||
s.logger.Printf("[WARN] [web] Tier-2 file restore refused up front: stack=%s has no restorable subtree in its copy (unit_present=%v unit_restorable=%v) — app NOT stopped",
|
||||
stackName, cov.HasUnit, cov.CanRestoreUnit())
|
||||
@@ -1951,11 +1951,11 @@ func (s *Server) backupTier2RestoreHandler(w http.ResponseWriter, r *http.Reques
|
||||
// apply to this app. Say that, and name the one that does, instead of "sikertelen".
|
||||
if errors.Is(err, backup.ErrTier2NoRestorableData) {
|
||||
s.logger.Printf("[WARN] [web] Tier-2 file restore not applicable: stack=%s", stackName)
|
||||
s.backupMgr.EndRestoreOp(false, tier2NoCoverageMsg)
|
||||
s.backupMgr.EndRestoreOp(false, s.note(tier2NoCoverageKey))
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[ERROR] [web] Tier-2 file restore failed (async): stack=%s: %v", stackName, err)
|
||||
s.backupMgr.EndRestoreOp(false, "Fájl-visszaállítás sikertelen: "+err.Error())
|
||||
s.backupMgr.EndRestoreOp(false, s.note("note.restore.file_failed", s.noteErr(err)))
|
||||
return
|
||||
}
|
||||
// C9-F1 (the quiet half): even where the restore DOES cover something it covers only the
|
||||
@@ -1963,12 +1963,12 @@ func (s *Server) backupTier2RestoreHandler(w http.ResponseWriter, r *http.Reques
|
||||
// copy's recovery-unit/. „minden fájl megvan a helyén" was a blanket claim over data that was
|
||||
// never opened; immich's 1.3 GB Postgres unit is the case that makes it dangerous. Claim only
|
||||
// what was EXAMINED, and disclose the rest.
|
||||
msg := "Minden vizsgált fájl megvan a helyén."
|
||||
msg := s.note("note.restore.all_files_present")
|
||||
if n > 0 {
|
||||
msg = fmt.Sprintf("%s: %d fájl visszaállítva a másodlagos másolatból.", stackName, n)
|
||||
msg = s.note("note.restore.files_from_second", stackName, n)
|
||||
}
|
||||
if cov.HasUnit {
|
||||
msg += " " + tier2UnitNotCoveredMsg
|
||||
msg += " " + s.note(tier2UnitNotCoveredKey)
|
||||
}
|
||||
s.logger.Printf("[INFO] [web] Tier-2 file restore completed (async): stack=%s (%d files, legs=%v)", stackName, n, cov.Legs)
|
||||
s.backupMgr.EndRestoreOp(true, msg)
|
||||
@@ -2024,7 +2024,7 @@ func (s *Server) backupTier2UnitRestoreHandler(w http.ResponseWriter, r *http.Re
|
||||
}
|
||||
if !cov.CanRestoreUnit() {
|
||||
s.logger.Printf("[WARN] [web] Tier-2 unit restore refused up front: stack=%s has no openable unit in its copy (unit_present=%v) — app NOT stopped", stackName, cov.HasUnit)
|
||||
http.Redirect(w, r, "/backups/apps?flash_error="+url.QueryEscape(tier2NoCoverageMsg), http.StatusFound)
|
||||
http.Redirect(w, r, "/backups/apps?"+flashQuery("flash_error", tier2NoCoverageKey), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2035,7 +2035,7 @@ func (s *Server) backupTier2UnitRestoreHandler(w http.ResponseWriter, r *http.Re
|
||||
res, err := s.backupMgr.RestoreTier2Unit(stackName)
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Tier-2 unit restore failed (async): stack=%s: %v", stackName, err)
|
||||
s.backupMgr.EndRestoreOp(false, "Teljes visszaállítás sikertelen: "+err.Error())
|
||||
s.backupMgr.EndRestoreOp(false, s.note("note.restore.full_unit_failed", s.noteErr(err)))
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[INFO] [web] Tier-2 unit restore completed (async): stack=%s in %s (volumes %d/%d, dbs %d/%d)",
|
||||
@@ -2045,8 +2045,8 @@ func (s *Server) backupTier2UnitRestoreHandler(w http.ResponseWriter, r *http.Re
|
||||
// second thing to keep honest. What IS added is which copy it came from and how old that copy
|
||||
// is: this action overwrote the customer's live data, and the sentence they are left with has
|
||||
// to say what it overwrote it with (Scenario E).
|
||||
msg := unitRestoreOutcomeMsg(stackName, res)
|
||||
if src := tier2UnitSourceMsg(cov); src != "" {
|
||||
msg := s.unitRestoreOutcomeMsg(stackName, res)
|
||||
if src := s.tier2UnitSourceMsg(cov); src != "" {
|
||||
msg += " " + src
|
||||
}
|
||||
s.backupMgr.EndRestoreOp(true, msg)
|
||||
|
||||
Reference in New Issue
Block a user