Files
kopia/internal/server/api_snapshots.go
Jarek Kowalski 7673753050 Merge retention tags in snapshot lists (#1567)
* cli: refactored snapshot list

* cli: show range tags in snapshot list

For example if N snapshots are coalesced together because they
have identical roots we may emit now:

```
  2021-03-31 23:09:27 PDT ked3400debc7dd61baffab070bafd59cd (monthly-10)
  2021-04-30 06:12:53 PDT kd0576d212e55a831b7ff1636f90a7233 (monthly-4..9)
  + 5 identical snapshots until 2021-09-30 23:00:19 PDT
  2021-10-31 23:22:25 PDT k846bf22aa2863d27f05e820f840b14f8 (monthly-3)
  2021-11-08 21:29:31 PST k5793ddcd61ef27b93c75ab74a5828176 (latest-1..3,hourly-1..13,daily-1..7,weekly-1..4,monthly-1..2,annual-1)
  + 18 identical snapshots until 2021-12-04 10:09:54 PST
```

* server: server-side coalescing of snapshot

* ui: added coalescing of retention tags
2021-12-05 20:49:41 -08:00

114 lines
2.6 KiB
Go

package server
import (
"context"
"net/http"
"net/url"
"github.com/kopia/kopia/internal/serverapi"
"github.com/kopia/kopia/snapshot"
"github.com/kopia/kopia/snapshot/policy"
)
func (s *Server) handleSnapshotList(ctx context.Context, r *http.Request, body []byte) (interface{}, *apiError) {
si := getSnapshotSourceFromURL(r.URL)
manifestIDs, err := snapshot.ListSnapshotManifests(ctx, s.rep, &si, nil)
if err != nil {
return nil, internalServerError(err)
}
manifests, err := snapshot.LoadSnapshots(ctx, s.rep, manifestIDs)
if err != nil {
return nil, internalServerError(err)
}
manifests = snapshot.SortByTime(manifests, false)
resp := &serverapi.SnapshotsResponse{
Snapshots: []*serverapi.Snapshot{},
}
pol, _, _, err := policy.GetEffectivePolicy(ctx, s.rep, si)
if err == nil {
pol.RetentionPolicy.ComputeRetentionReasons(manifests)
}
for _, m := range manifests {
resp.Snapshots = append(resp.Snapshots, convertSnapshotManifest(m))
}
resp.UnfilteredCount = len(resp.Snapshots)
if r.URL.Query().Get("all") == "" {
resp.Snapshots = uniqueSnapshots(resp.Snapshots)
resp.UniqueCount = len(resp.Snapshots)
} else {
resp.UniqueCount = len(uniqueSnapshots(resp.Snapshots))
}
return resp, nil
}
func uniqueSnapshots(rows []*serverapi.Snapshot) []*serverapi.Snapshot {
var result []*serverapi.Snapshot
for _, r := range rows {
if len(result) == 0 {
result = append(result, r)
continue
}
last := result[len(result)-1]
if r.RootEntry == last.RootEntry {
last.RetentionReasons = append(last.RetentionReasons, r.RetentionReasons...)
last.Pins = append(last.Pins, r.Pins...)
} else {
result = append(result, r)
}
}
for _, r := range result {
r.RetentionReasons = policy.CompactRetentionReasons(r.RetentionReasons)
r.Pins = policy.CompactPins(r.Pins)
}
return result
}
func sourceMatchesURLFilter(src snapshot.SourceInfo, query url.Values) bool {
if v := query.Get("host"); v != "" && src.Host != v {
return false
}
if v := query.Get("userName"); v != "" && src.UserName != v {
return false
}
if v := query.Get("path"); v != "" && src.Path != v {
return false
}
return true
}
func convertSnapshotManifest(m *snapshot.Manifest) *serverapi.Snapshot {
e := &serverapi.Snapshot{
ID: m.ID,
Description: m.Description,
StartTime: m.StartTime,
EndTime: m.EndTime,
IncompleteReason: m.IncompleteReason,
RootEntry: m.RootObjectID().String(),
RetentionReasons: append([]string{}, m.RetentionReasons...),
Pins: append([]string{}, m.Pins...),
}
if re := m.RootEntry; re != nil {
e.Summary = re.DirSummary
}
return e
}