Make snapshots dirs in mount command customizable

This commit is contained in:
Alexander Weiss 2020-09-03 20:31:57 +02:00 committed by Michael Eischer
parent 57f4003f2f
commit 1751afae26
4 changed files with 63 additions and 24 deletions

View file

@ -0,0 +1,7 @@
Enhancement: Make snapshot directory structure of mount command custimizable
We've added the possibility to customize the snapshot directory structure of the mount command.
This includes using subdirectories which is now also possible within time template and tags.
https://github.com/restic/restic/issues/2907
https://github.com/restic/restic/pull/2913

View file

@ -5,6 +5,7 @@ package main
import (
"os"
"strings"
"time"
"github.com/spf13/cobra"
@ -30,10 +31,13 @@ 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:
If you need a different template for directories that contain snapshots,
you can pass a time template via --time-template and path templates via
--path-template.
--snapshot-template "2006-01-02_15-04-05"
Example time template without colons:
--time-template "2006-01-02_15-04-05"
You need to specify a sample format for exactly the following timestamp:
@ -42,6 +46,20 @@ You need to specify a sample format for exactly the following timestamp:
For details please see the documentation for time.Format() at:
https://godoc.org/time#Time.Format
For path templates, you can use the following patterns which will be replaced:
%i by short snapshot ID
%I by long snapshot ID
%u by username
%h by hostname
%t by tags
%T by timestamp as specified by --time-template
The default path templates are:
"ids/%i"
"snapshots/%T"
"hosts/%h/%T"
"tags/%t/%T"
EXIT STATUS
===========
@ -61,7 +79,8 @@ type MountOptions struct {
Hosts []string
Tags restic.TagLists
Paths []string
SnapshotTemplate string
TimeTemplate string
PathTemplates []string
}
var mountOptions MountOptions
@ -78,13 +97,21 @@ func init() {
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")
mountFlags.StringArrayVar(&mountOptions.PathTemplates, "path-template", nil, "set `template` for path names (can be specified multiple times)")
mountFlags.StringVar(&mountOptions.TimeTemplate, "snapshot-template", time.RFC3339, "set `template` to use for snapshot dirs")
mountFlags.StringVar(&mountOptions.TimeTemplate, "time-template", time.RFC3339, "set `template` to use for times")
_ = mountFlags.MarkDeprecated("snapshot-template", "use --time-template")
}
func runMount(opts MountOptions, gopts GlobalOptions, args []string) error {
if opts.SnapshotTemplate == "" {
return errors.Fatal("snapshot template string cannot be empty")
if opts.TimeTemplate == "" {
return errors.Fatal("time template string cannot be empty")
}
if strings.HasPrefix(opts.TimeTemplate, "/") || strings.HasSuffix(opts.TimeTemplate, "/") {
return errors.Fatal("time template string cannot start or end with '/'")
}
if len(args) == 0 {
return errors.Fatal("wrong number of parameters")
}
@ -150,11 +177,12 @@ func runMount(opts MountOptions, gopts GlobalOptions, args []string) error {
}
cfg := fuse.Config{
OwnerIsRoot: opts.OwnerRoot,
Hosts: opts.Hosts,
Tags: opts.Tags,
Paths: opts.Paths,
SnapshotTemplate: opts.SnapshotTemplate,
OwnerIsRoot: opts.OwnerRoot,
Hosts: opts.Hosts,
Tags: opts.Tags,
Paths: opts.Paths,
TimeTemplate: opts.TimeTemplate,
PathTemplates: opts.PathTemplates,
}
root := fuse.NewRoot(repo, cfg)

View file

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

View file

@ -15,11 +15,12 @@ import (
// Config holds settings for the fuse mount.
type Config struct {
OwnerIsRoot bool
Hosts []string
Tags []restic.TagList
Paths []string
SnapshotTemplate string
OwnerIsRoot bool
Hosts []string
Tags []restic.TagList
Paths []string
TimeTemplate string
PathTemplates []string
}
// Root is the root node of the fuse mount of a repository.
@ -59,14 +60,17 @@ func NewRoot(repo restic.Repository, cfg Config) *Root {
root.gid = uint32(os.Getgid())
}
paths := []string{
"ids/%i",
"snapshots/%T",
"hosts/%h/%T",
"tags/%t/%T",
// set defaults, if PathTemplates is not set
if len(cfg.PathTemplates) == 0 {
cfg.PathTemplates = []string{
"ids/%i",
"snapshots/%T",
"hosts/%h/%T",
"tags/%t/%T",
}
}
root.SnapshotsDir = NewSnapshotsDir(root, rootInode, NewSnapshotsDirStructure(root, paths, cfg.SnapshotTemplate), "")
root.SnapshotsDir = NewSnapshotsDir(root, rootInode, NewSnapshotsDirStructure(root, cfg.PathTemplates, cfg.TimeTemplate), "")
return root
}