Merge pull request #1953 from kitone/ls-add-json

ls: Add JSON output support for restic ls cmd
This commit is contained in:
Alexander Neumann 2018-08-18 15:31:15 +02:00
commit d708d607fa
2 changed files with 90 additions and 3 deletions

View file

@ -0,0 +1,7 @@
Enhancement: ls: Add JSON output support for restic ls cmd
We've implemented listing files in the repository with JSON as output, just
pass `--json` as an option to `restic ls`. This makes the output of the command
machine readable.
https://github.com/restic/restic/pull/1953

View file

@ -2,7 +2,10 @@ package main
import (
"context"
"encoding/json"
"os"
"strings"
"time"
"github.com/spf13/cobra"
@ -59,6 +62,29 @@ func init() {
flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
}
type lsSnapshot struct {
*restic.Snapshot
ID *restic.ID `json:"id"`
ShortID string `json:"short_id"`
Nodes []lsNode `json:"nodes"`
StructType string `json:"struct_type"` // "snapshot"
}
type lsNode struct {
Name string `json:"name"`
Type string `json:"type"`
Path string `json:"path"`
UID uint32 `json:"uid"`
GID uint32 `json:"gid"`
Size uint64 `json:"size,omitempty"`
Mode os.FileMode `json:"mode,omitempty"`
ModTime time.Time `json:"mtime,omitempty"`
AccessTime time.Time `json:"atime,omitempty"`
ChangeTime time.Time `json:"ctime,omitempty"`
StructType string `json:"struct_type"` // "node"
}
func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
if len(args) == 0 && opts.Host == "" && len(opts.Tags) == 0 && len(opts.Paths) == 0 {
return errors.Fatal("Invalid arguments, either give one or more snapshot IDs or set filters.")
@ -120,8 +146,62 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
ctx, cancel := context.WithCancel(gopts.ctx)
defer cancel()
var (
printSnapshot func(sn *restic.Snapshot)
printNode func(path string, node *restic.Node)
printFinish func() error
)
if gopts.JSON {
var lssnapshots []lsSnapshot
printSnapshot = func(sn *restic.Snapshot) {
lss := lsSnapshot{
Snapshot: sn,
ID: sn.ID(),
ShortID: sn.ID().Str(),
StructType: "snapshot",
}
lssnapshots = append(lssnapshots, lss)
}
printNode = func(path string, node *restic.Node) {
lsn := lsNode{
Name: node.Name,
Type: node.Type,
Path: path,
UID: node.UID,
GID: node.GID,
Size: node.Size,
Mode: node.Mode,
ModTime: node.ModTime,
AccessTime: node.AccessTime,
ChangeTime: node.ChangeTime,
StructType: "node",
}
s := &lssnapshots[len(lssnapshots)-1]
s.Nodes = append(s.Nodes, lsn)
}
printFinish = func() error {
return json.NewEncoder(gopts.stdout).Encode(lssnapshots)
}
} else {
// default output methods
printSnapshot = func(sn *restic.Snapshot) {
Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)
}
printNode = func(path string, node *restic.Node) {
Printf("%s\n", formatNode(path, node, lsOptions.ListLong))
}
printFinish = func() error {
return nil
}
}
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args[:1]) {
Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)
printSnapshot(sn)
err := walker.Walk(ctx, repo, *sn.Tree, nil, func(nodepath string, node *restic.Node, err error) (bool, error) {
if err != nil {
@ -133,7 +213,7 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
if withinDir(nodepath) {
// if we're within a dir, print the node
Printf("%s\n", formatNode(nodepath, node, lsOptions.ListLong))
printNode(nodepath, node)
// if recursive listing is requested, signal the walker that it
// should continue walking recursively
@ -160,5 +240,5 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
return err
}
}
return nil
return printFinish()
}