02250670c1
Per-snapshot size + file-count come from the embedded summary block restic added to 'snapshots --json' in 0.17 (the source comment in internal/restic/snapshots.go incorrectly said 0.16+). Hosts running 0.16.x leave those columns blank. - Fix the snapshots.go doc comment: '0.16+' -> '0.17+'. - hostDetailPage carries a LegacyRestic bool computed from the host's reported ResticVersion via Env.AtLeastVersion(0, 17). Empty version also counts as legacy (conservative default). - Template attaches title='Needs restic 0.17+ on the agent host. This host runs <ver>.' + cursor:help on the SIZE / FILES headers when the flag is true. Hosts already on 0.17+ get no tooltip and no extra styling. A host upgrading restic to 0.17+ gets the columns populated on the next backup automatically — no further code change needed.
61 lines
2.0 KiB
Go
61 lines
2.0 KiB
Go
package restic
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os/exec"
|
|
"time"
|
|
)
|
|
|
|
// Snapshot mirrors a single entry in `restic snapshots --json`. We
|
|
// decode only the fields we project to the server; restic's full
|
|
// shape has more (parent, tree, program version) that we don't need.
|
|
//
|
|
// Summary is only populated by restic 0.17+ (which embeds the backup
|
|
// summary inside each snapshot record). Older clients leave it nil
|
|
// and the agent reports zero size/file-count — the UI degrades to
|
|
// "—" and the column headers carry a tooltip explaining the version
|
|
// requirement (see web/templates/pages/host_detail.html).
|
|
type Snapshot struct {
|
|
ID string `json:"id"`
|
|
ShortID string `json:"short_id"`
|
|
Time time.Time `json:"time"`
|
|
Hostname string `json:"hostname"`
|
|
Paths []string `json:"paths"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Summary *SnapshotSummary `json:"summary,omitempty"`
|
|
}
|
|
|
|
// SnapshotSummary mirrors the embedded summary block restic 0.16+
|
|
// writes into each snapshot record. The naming follows restic's JSON.
|
|
type SnapshotSummary struct {
|
|
TotalFilesProcessed int64 `json:"total_files_processed"`
|
|
TotalBytesProcessed int64 `json:"total_bytes_processed"`
|
|
}
|
|
|
|
// ListSnapshots calls `restic snapshots --json` and returns the
|
|
// parsed list. Output is a single JSON array — small even on large
|
|
// repos (each entry is ~200 bytes), so we read it all into memory.
|
|
func (e Env) ListSnapshots(ctx context.Context) ([]Snapshot, error) {
|
|
cmd := exec.CommandContext(ctx, e.Bin, "snapshots", "--json")
|
|
cmd.Env = e.envSlice()
|
|
cmd.Dir = e.WorkDir
|
|
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
var ee *exec.ExitError
|
|
if errors.As(err, &ee) {
|
|
return nil, fmt.Errorf("restic snapshots: exit %d: %s",
|
|
ee.ExitCode(), string(ee.Stderr))
|
|
}
|
|
return nil, fmt.Errorf("restic snapshots: %w", err)
|
|
}
|
|
var snaps []Snapshot
|
|
if err := json.Unmarshal(out, &snaps); err != nil {
|
|
return nil, fmt.Errorf("restic snapshots: parse json: %w", err)
|
|
}
|
|
return snaps, nil
|
|
}
|