forked from TrueCloudLab/restic
fuse/mount: Add option for snapshot template
This commit is contained in:
parent
f49f5c5903
commit
97565d68a2
4 changed files with 61 additions and 26 deletions
|
@ -5,6 +5,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
@ -25,6 +27,21 @@ var cmdMount = &cobra.Command{
|
||||||
Long: `
|
Long: `
|
||||||
The "mount" command mounts the repository via fuse to a directory. This is a
|
The "mount" command mounts the repository via fuse to a directory. This is a
|
||||||
read-only mount.
|
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,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
@ -34,12 +51,13 @@ read-only mount.
|
||||||
|
|
||||||
// MountOptions collects all options for the mount command.
|
// MountOptions collects all options for the mount command.
|
||||||
type MountOptions struct {
|
type MountOptions struct {
|
||||||
OwnerRoot bool
|
OwnerRoot bool
|
||||||
AllowRoot bool
|
AllowRoot bool
|
||||||
AllowOther bool
|
AllowOther bool
|
||||||
Host string
|
Host string
|
||||||
Tags restic.TagLists
|
Tags restic.TagLists
|
||||||
Paths []string
|
Paths []string
|
||||||
|
SnapshotTemplate string
|
||||||
}
|
}
|
||||||
|
|
||||||
var mountOptions MountOptions
|
var mountOptions MountOptions
|
||||||
|
@ -55,6 +73,8 @@ func init() {
|
||||||
mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`)
|
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.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.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 {
|
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{
|
cfg := fuse.Config{
|
||||||
OwnerIsRoot: opts.OwnerRoot,
|
OwnerIsRoot: opts.OwnerRoot,
|
||||||
Host: opts.Host,
|
Host: opts.Host,
|
||||||
Tags: opts.Tags,
|
Tags: opts.Tags,
|
||||||
Paths: opts.Paths,
|
Paths: opts.Paths,
|
||||||
|
SnapshotTemplate: opts.SnapshotTemplate,
|
||||||
}
|
}
|
||||||
root, err := fuse.NewRoot(gopts.ctx, repo, cfg)
|
root, err := fuse.NewRoot(gopts.ctx, repo, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -136,6 +157,14 @@ func umount(mountpoint string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMount(opts MountOptions, gopts GlobalOptions, args []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 {
|
if len(args) == 0 {
|
||||||
return errors.Fatal("wrong number of parameters")
|
return errors.Fatal("wrong number of parameters")
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,9 @@ func waitForMount(t testing.TB, dir string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRunMount(t testing.TB, gopts GlobalOptions, 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}))
|
rtest.OK(t, runMount(opts, gopts, []string{dir}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,11 @@ import (
|
||||||
|
|
||||||
// Config holds settings for the fuse mount.
|
// Config holds settings for the fuse mount.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
OwnerIsRoot bool
|
OwnerIsRoot bool
|
||||||
Host string
|
Host string
|
||||||
Tags []restic.TagList
|
Tags []restic.TagList
|
||||||
Paths []string
|
Paths []string
|
||||||
|
SnapshotTemplate string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root is the root node of the fuse mount of a repository.
|
// Root is the root node of the fuse mount of a repository.
|
||||||
|
|
|
@ -26,6 +26,8 @@ type SnapshotsDir struct {
|
||||||
tag string
|
tag string
|
||||||
host string
|
host string
|
||||||
snCount int
|
snCount int
|
||||||
|
|
||||||
|
template string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotsIDSDir is a fuse directory which contains snapshots named by ids.
|
// 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 {
|
func NewSnapshotsDir(root *Root, inode uint64, tag string, host string) *SnapshotsDir {
|
||||||
debug.Log("create snapshots dir, inode %d", inode)
|
debug.Log("create snapshots dir, inode %d", inode)
|
||||||
d := &SnapshotsDir{
|
d := &SnapshotsDir{
|
||||||
root: root,
|
root: root,
|
||||||
inode: inode,
|
inode: inode,
|
||||||
names: make(map[string]*restic.Snapshot),
|
names: make(map[string]*restic.Snapshot),
|
||||||
latest: "",
|
latest: "",
|
||||||
tag: tag,
|
tag: tag,
|
||||||
host: host,
|
host: host,
|
||||||
|
template: root.cfg.SnapshotTemplate,
|
||||||
}
|
}
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
@ -239,7 +242,7 @@ func updateSnapshots(ctx context.Context, root *Root) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// read snapshot timestamps from the current repository-state.
|
// read snapshot timestamps from the current repository-state.
|
||||||
func updateSnapshotNames(d *SnapshotsDir) {
|
func updateSnapshotNames(d *SnapshotsDir, template string) {
|
||||||
if d.snCount != d.root.snCount {
|
if d.snCount != d.root.snCount {
|
||||||
d.snCount = d.root.snCount
|
d.snCount = d.root.snCount
|
||||||
var latestTime time.Time
|
var latestTime time.Time
|
||||||
|
@ -248,7 +251,7 @@ func updateSnapshotNames(d *SnapshotsDir) {
|
||||||
for _, sn := range d.root.snapshots {
|
for _, sn := range d.root.snapshots {
|
||||||
if d.tag == "" || isElem(d.tag, sn.Tags) {
|
if d.tag == "" || isElem(d.tag, sn.Tags) {
|
||||||
if d.host == "" || d.host == sn.Hostname {
|
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) {
|
if d.latest == "" || !sn.Time.Before(latestTime) {
|
||||||
latestTime = sn.Time
|
latestTime = sn.Time
|
||||||
d.latest = name
|
d.latest = name
|
||||||
|
@ -258,7 +261,7 @@ func updateSnapshotNames(d *SnapshotsDir) {
|
||||||
break
|
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
|
d.names[name] = sn
|
||||||
|
@ -276,7 +279,7 @@ func (d *SnapshotsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
||||||
updateSnapshots(ctx, d.root)
|
updateSnapshots(ctx, d.root)
|
||||||
|
|
||||||
// update snapshot names
|
// update snapshot names
|
||||||
updateSnapshotNames(d)
|
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
|
||||||
|
|
||||||
items := []fuse.Dirent{
|
items := []fuse.Dirent{
|
||||||
{
|
{
|
||||||
|
@ -450,7 +453,7 @@ func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
|
||||||
updateSnapshots(ctx, d.root)
|
updateSnapshots(ctx, d.root)
|
||||||
|
|
||||||
// update snapshot names
|
// update snapshot names
|
||||||
updateSnapshotNames(d)
|
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
|
||||||
|
|
||||||
sn, ok := d.names[name]
|
sn, ok := d.names[name]
|
||||||
if ok {
|
if ok {
|
||||||
|
|
Loading…
Reference in a new issue