mirror of
https://github.com/kopia/kopia.git
synced 2025-12-23 22:57:50 -05:00
fix(cli): add retention to JSON output (#1992)
refactor(cli): snapshot list JSON functionality. Defines SnapshotManifest struct for the snapshot list JSON output. test(cli): `snapshot list --json`
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/repo/object"
|
||||
@@ -334,7 +335,7 @@ func TestSnapshotFix(t *testing.T) {
|
||||
|
||||
env.RunAndExpectSuccess(t, "snapshot", "verify")
|
||||
|
||||
var manifests []snapshot.Manifest
|
||||
var manifests []cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, env.RunAndExpectSuccess(t, "snapshot", "list", "--json"), &manifests)
|
||||
require.Len(t, manifests, 2)
|
||||
|
||||
@@ -112,11 +112,6 @@ func findManifestIDs(ctx context.Context, rep repo.Repository, source string, ta
|
||||
}
|
||||
|
||||
func (c *commandSnapshotList) run(ctx context.Context, rep repo.Repository) error {
|
||||
var jl jsonList
|
||||
|
||||
jl.begin(&c.jo)
|
||||
defer jl.end()
|
||||
|
||||
tags, err := getTags(c.snapshotListTags)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -133,27 +128,54 @@ func (c *commandSnapshotList) run(ctx context.Context, rep repo.Repository) erro
|
||||
}
|
||||
|
||||
if c.jo.jsonOutput {
|
||||
for _, snapshotGroup := range snapshot.GroupBySource(manifests) {
|
||||
snapshotGroup = snapshot.SortByTime(snapshotGroup, c.reverseSort)
|
||||
|
||||
if c.maxResultsPerPath > 0 && len(snapshotGroup) > c.maxResultsPerPath {
|
||||
snapshotGroup = snapshotGroup[len(snapshotGroup)-c.maxResultsPerPath:]
|
||||
}
|
||||
|
||||
if err := c.iterateSnapshotsMaybeWithStorageStats(ctx, rep, snapshotGroup, func(m *snapshot.Manifest) error {
|
||||
jl.emit(m)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "unable to iterate snapshots")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return c.outputJSON(ctx, rep, manifests)
|
||||
}
|
||||
|
||||
return c.outputManifestGroups(ctx, rep, manifests, strings.Split(relPath, "/"))
|
||||
}
|
||||
|
||||
// SnapshotManifest defines the JSON output for the CLI snapshot commands.
|
||||
type SnapshotManifest struct {
|
||||
*snapshot.Manifest
|
||||
RetentionReasons []string `json:"retentionReason,omitempty"`
|
||||
}
|
||||
|
||||
func (c *commandSnapshotList) outputJSON(ctx context.Context, rep repo.Repository, manifests []*snapshot.Manifest) error {
|
||||
var jl jsonList
|
||||
|
||||
jl.begin(&c.jo)
|
||||
defer jl.end()
|
||||
|
||||
for _, snapshotGroup := range snapshot.GroupBySource(manifests) {
|
||||
snapshotGroup = snapshot.SortByTime(snapshotGroup, c.reverseSort)
|
||||
|
||||
if c.maxResultsPerPath > 0 && len(snapshotGroup) > c.maxResultsPerPath {
|
||||
snapshotGroup = snapshotGroup[len(snapshotGroup)-c.maxResultsPerPath:]
|
||||
}
|
||||
|
||||
if c.snapshotListShowRetentionReasons {
|
||||
src := snapshotGroup[0].Source
|
||||
// compute retention reason
|
||||
pol, _, _, err := policy.GetEffectivePolicy(ctx, rep, src)
|
||||
if err != nil {
|
||||
log(ctx).Errorf("unable to determine effective policy for %v", src)
|
||||
} else {
|
||||
pol.RetentionPolicy.ComputeRetentionReasons(snapshotGroup)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.iterateSnapshotsMaybeWithStorageStats(ctx, rep, snapshotGroup, func(m *snapshot.Manifest) error {
|
||||
wm := SnapshotManifest{Manifest: m, RetentionReasons: m.RetentionReasons}
|
||||
jl.emit(wm)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "unable to iterate snapshots")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *commandSnapshotList) shouldOutputSnapshotSource(rep repo.Repository, src snapshot.SourceInfo) bool {
|
||||
if c.snapshotListShowAll {
|
||||
return true
|
||||
|
||||
50
cli/command_snapshot_list_test.go
Normal file
50
cli/command_snapshot_list_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/tests/testenv"
|
||||
)
|
||||
|
||||
func TestSnapshotList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runner := testenv.NewInProcRunner(t)
|
||||
e := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, runner)
|
||||
|
||||
defer e.RunAndExpectSuccess(t, "repo", "disconnect")
|
||||
e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir)
|
||||
|
||||
srcdir := testutil.TempDirectory(t)
|
||||
require.NoError(t, os.WriteFile(filepath.Join(srcdir, "some-file2"), []byte{1, 2, 3}, 0o755))
|
||||
|
||||
var man cli.SnapshotManifest
|
||||
|
||||
e.RunAndExpectSuccess(t, "policy", "set", srcdir, "--keep-latest=4", "--keep-hourly=0", "--keep-daily=0", "--keep-monthly=0", "--keep-weekly=0", "--keep-annual=0")
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "create", srcdir, "--json"), &man)
|
||||
|
||||
require.NoError(t, os.WriteFile(filepath.Join(srcdir, "some-file3"), []byte{1, 2, 3, 4}, 0o755))
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "create", srcdir, "--json"), &man)
|
||||
|
||||
require.NoError(t, os.WriteFile(filepath.Join(srcdir, "some-file4"), []byte{4}, 0o755))
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
|
||||
|
||||
var snapshots []*cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list",
|
||||
"--json"), &snapshots)
|
||||
|
||||
require.Len(t, snapshots, 4)
|
||||
|
||||
for _, s := range snapshots {
|
||||
require.NotEmpty(t, s.RetentionReasons, "expecting retention reason to be set")
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/snapshot"
|
||||
"github.com/kopia/kopia/tests/testenv"
|
||||
@@ -40,10 +41,7 @@ func TestSnapshotPin(t *testing.T) {
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
|
||||
|
||||
var snapshots []*snapshot.Manifest
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list", "--json"), &snapshots)
|
||||
snapshots = snapshot.SortByTime(snapshots, false)
|
||||
snapshots := mustListSnapshots(t, e)
|
||||
|
||||
// make sure the pinned one is on top.
|
||||
require.Len(t, snapshots, 4)
|
||||
@@ -57,12 +55,9 @@ func TestSnapshotPin(t *testing.T) {
|
||||
e.RunAndExpectSuccess(t, "snapshot", "pin", string(snapshots[0].ID), "--add=c", "--remove=b")
|
||||
e.RunAndExpectSuccess(t, "snapshot", "pin", string(snapshots[3].ID), "--add=d")
|
||||
|
||||
var snapshots2 []*snapshot.Manifest
|
||||
snapshots2 := mustListSnapshots(t, e)
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list", "--json"), &snapshots2)
|
||||
snapshots2 = snapshot.SortByTime(snapshots2, false)
|
||||
require.Len(t, snapshots2, 4)
|
||||
|
||||
require.Equal(t, []string{"a", "c"}, snapshots2[0].Pins)
|
||||
require.Empty(t, snapshots2[1].Pins)
|
||||
require.Empty(t, snapshots2[2].Pins)
|
||||
@@ -75,15 +70,28 @@ func TestSnapshotPin(t *testing.T) {
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", srcdir)
|
||||
|
||||
var snapshots3 []*snapshot.Manifest
|
||||
snapshots3 := mustListSnapshots(t, e)
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list", "--json"), &snapshots3)
|
||||
snapshots3 = snapshot.SortByTime(snapshots3, false)
|
||||
require.Len(t, snapshots3, 5)
|
||||
|
||||
require.Equal(t, []string{"a", "c"}, snapshots3[0].Pins)
|
||||
require.Equal(t, []string{"d"}, snapshots3[1].Pins)
|
||||
require.Empty(t, snapshots3[2].Pins)
|
||||
require.Empty(t, snapshots3[3].Pins)
|
||||
require.Empty(t, snapshots3[4].Pins)
|
||||
}
|
||||
|
||||
func mustListSnapshots(t *testing.T, e *testenv.CLITest) []*snapshot.Manifest {
|
||||
t.Helper()
|
||||
|
||||
var cliSnapshots []cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list", "--json"), &cliSnapshots)
|
||||
|
||||
snapshots := make([]*snapshot.Manifest, 0, len(cliSnapshots))
|
||||
|
||||
for _, s := range cliSnapshots {
|
||||
snapshots = append(snapshots, s.Manifest)
|
||||
}
|
||||
|
||||
return snapshot.SortByTime(snapshots, false)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/snapshot"
|
||||
"github.com/kopia/kopia/tests/testenv"
|
||||
@@ -31,7 +32,7 @@ func TestSnapshotStorageStats(t *testing.T) {
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir1, "subdir", "file4.txt"), []byte{1, 2, 3, 4, 5, 6, 7, 8}, 0o600))
|
||||
env.RunAndExpectSuccess(t, "snapshot", "create", dir1)
|
||||
|
||||
var manifests []*snapshot.Manifest
|
||||
var manifests []cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, env.RunAndExpectSuccess(t, "snapshot", "ls", "--storage-stats", dir1, "--json"), &manifests)
|
||||
require.Len(t, manifests, 2)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/repo"
|
||||
"github.com/kopia/kopia/snapshot"
|
||||
@@ -54,12 +55,12 @@ func TestSnapshotCreate(t *testing.T) {
|
||||
require.NotEqual(t, man1.ID, man2.ID)
|
||||
require.Equal(t, man1.RootEntry.ObjectID, man2.RootEntry.ObjectID)
|
||||
|
||||
var manifests []snapshot.Manifest
|
||||
var manifests []cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list", "-a", "--json"), &manifests)
|
||||
require.Len(t, manifests, 6)
|
||||
|
||||
var manifests2 []snapshot.Manifest
|
||||
var manifests2 []cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list", "-a", "--json", "--max-results=1"), &manifests2)
|
||||
|
||||
@@ -87,7 +88,7 @@ func TestTagging(t *testing.T) {
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", sharedTestDataDir1, "--tags", "testkey1:testkey2")
|
||||
e.RunAndExpectSuccess(t, "snapshot", "create", sharedTestDataDir1)
|
||||
|
||||
var manifests []snapshot.Manifest
|
||||
var manifests []cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "list", "-a", "--json"), &manifests)
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/snapshot"
|
||||
"github.com/kopia/kopia/tests/testenv"
|
||||
)
|
||||
|
||||
@@ -90,7 +90,7 @@ func (s *formatSpecificTestSuite) TestSnapshotMigrateWithIgnores(t *testing.T) {
|
||||
dstenv.RunAndExpectSuccess(t, "policy", "set", sd, "--add-ignore", "file2.txt")
|
||||
dstenv.RunAndExpectSuccess(t, "snapshot", "migrate", "--source-config", filepath.Join(e.ConfigDir, ".kopia.config"), "--all", "--apply-ignore-rules")
|
||||
|
||||
var manifests []snapshot.Manifest
|
||||
var manifests []cli.SnapshotManifest
|
||||
|
||||
testutil.MustParseJSONLines(t, dstenv.RunAndExpectSuccess(t, "snapshot", "list", "-a", sd, "--json"), &manifests)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user