2
2
mirror of https://github.com/octoleo/restic.git synced 2024-12-22 19:08:55 +00:00

fuse/mount: Add option for snapshot template

This commit is contained in:
Alexander Neumann 2018-01-14 14:22:08 +01:00
parent f49f5c5903
commit 97565d68a2
4 changed files with 61 additions and 26 deletions

View File

@ -5,6 +5,8 @@ package main
import (
"os"
"strings"
"time"
"github.com/spf13/cobra"
@ -25,6 +27,21 @@ var cmdMount = &cobra.Command{
Long: `
The "mount" command mounts the repository via fuse to a directory. This is a
read-only mount.
Snapshot Directories
====================
If you need a different template for all directories that contain snapshots,
you can pass a template via --snapshot-template. Example without colons:
--snapshot-template "2006-01-02_15-04-05"
You need to specify a sample format for exactly the following timestamp:
Mon Jan 2 15:04:05 -0700 MST 2006
For details please see the documentation for time.Format() at:
https://godoc.org/time#Time.Format
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
@ -34,12 +51,13 @@ read-only mount.
// MountOptions collects all options for the mount command.
type MountOptions struct {
OwnerRoot bool
AllowRoot bool
AllowOther bool
Host string
Tags restic.TagLists
Paths []string
OwnerRoot bool
AllowRoot bool
AllowOther bool
Host string
Tags restic.TagLists
Paths []string
SnapshotTemplate string
}
var mountOptions MountOptions
@ -55,6 +73,8 @@ func init() {
mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`)
mountFlags.Var(&mountOptions.Tags, "tag", "only consider snapshots which include this `taglist`")
mountFlags.StringArrayVar(&mountOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`")
mountFlags.StringVar(&mountOptions.SnapshotTemplate, "snapshot-template", time.RFC3339, "set `template` to use for snapshot dirs")
}
func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
@ -108,10 +128,11 @@ func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
}
cfg := fuse.Config{
OwnerIsRoot: opts.OwnerRoot,
Host: opts.Host,
Tags: opts.Tags,
Paths: opts.Paths,
OwnerIsRoot: opts.OwnerRoot,
Host: opts.Host,
Tags: opts.Tags,
Paths: opts.Paths,
SnapshotTemplate: opts.SnapshotTemplate,
}
root, err := fuse.NewRoot(gopts.ctx, repo, cfg)
if err != nil {
@ -136,6 +157,14 @@ func umount(mountpoint string) error {
}
func runMount(opts MountOptions, gopts GlobalOptions, args []string) error {
if opts.SnapshotTemplate == "" {
return errors.Fatal("snapshot template string cannot be empty")
}
if strings.ContainsAny(opts.SnapshotTemplate, `\/`) {
return errors.Fatal("snapshot template string contains a slash (/) or backslash (\\) character")
}
if len(args) == 0 {
return errors.Fatal("wrong number of parameters")
}

View File

@ -55,7 +55,9 @@ func waitForMount(t testing.TB, dir string) {
}
func testRunMount(t testing.TB, gopts GlobalOptions, dir string) {
opts := MountOptions{}
opts := MountOptions{
SnapshotTemplate: time.RFC3339,
}
rtest.OK(t, runMount(opts, gopts, []string{dir}))
}

View File

@ -16,10 +16,11 @@ import (
// Config holds settings for the fuse mount.
type Config struct {
OwnerIsRoot bool
Host string
Tags []restic.TagList
Paths []string
OwnerIsRoot bool
Host string
Tags []restic.TagList
Paths []string
SnapshotTemplate string
}
// Root is the root node of the fuse mount of a repository.

View File

@ -26,6 +26,8 @@ type SnapshotsDir struct {
tag string
host string
snCount int
template string
}
// SnapshotsIDSDir is a fuse directory which contains snapshots named by ids.
@ -112,12 +114,13 @@ func updateSnapshotIDSNames(d *SnapshotsIDSDir) {
func NewSnapshotsDir(root *Root, inode uint64, tag string, host string) *SnapshotsDir {
debug.Log("create snapshots dir, inode %d", inode)
d := &SnapshotsDir{
root: root,
inode: inode,
names: make(map[string]*restic.Snapshot),
latest: "",
tag: tag,
host: host,
root: root,
inode: inode,
names: make(map[string]*restic.Snapshot),
latest: "",
tag: tag,
host: host,
template: root.cfg.SnapshotTemplate,
}
return d
@ -239,7 +242,7 @@ func updateSnapshots(ctx context.Context, root *Root) {
}
// read snapshot timestamps from the current repository-state.
func updateSnapshotNames(d *SnapshotsDir) {
func updateSnapshotNames(d *SnapshotsDir, template string) {
if d.snCount != d.root.snCount {
d.snCount = d.root.snCount
var latestTime time.Time
@ -248,7 +251,7 @@ func updateSnapshotNames(d *SnapshotsDir) {
for _, sn := range d.root.snapshots {
if d.tag == "" || isElem(d.tag, sn.Tags) {
if d.host == "" || d.host == sn.Hostname {
name := sn.Time.Format(time.RFC3339)
name := sn.Time.Format(template)
if d.latest == "" || !sn.Time.Before(latestTime) {
latestTime = sn.Time
d.latest = name
@ -258,7 +261,7 @@ func updateSnapshotNames(d *SnapshotsDir) {
break
}
name = fmt.Sprintf("%s-%d", sn.Time.Format(time.RFC3339), i)
name = fmt.Sprintf("%s-%d", sn.Time.Format(template), i)
}
d.names[name] = sn
@ -276,7 +279,7 @@ func (d *SnapshotsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
updateSnapshots(ctx, d.root)
// update snapshot names
updateSnapshotNames(d)
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
items := []fuse.Dirent{
{
@ -450,7 +453,7 @@ func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
updateSnapshots(ctx, d.root)
// update snapshot names
updateSnapshotNames(d)
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
sn, ok := d.names[name]
if ok {