Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow lockfile path to be configured #384

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions cmd/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)

Expand All @@ -15,9 +14,9 @@ var backupCmd = &cobra.Command{
Short: "Create backups for given locations",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
err := internal.Lock()
CheckErr(err)
defer lock.Unlock()
defer internal.Unlock()

selected, err := internal.GetAllOrSelected(cmd, false)
CheckErr(err)
Expand Down
5 changes: 2 additions & 3 deletions cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)

Expand All @@ -12,9 +11,9 @@ var checkCmd = &cobra.Command{
Short: "Check if everything is setup",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
err := internal.Lock()
CheckErr(err)
defer lock.Unlock()
defer internal.Unlock()

CheckErr(internal.CheckConfig())

Expand Down
5 changes: 2 additions & 3 deletions cmd/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/flags"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)

Expand All @@ -13,9 +12,9 @@ var cronCmd = &cobra.Command{
Long: `Intended to be mainly triggered by an automated system like systemd or crontab. For each location checks if a cron backup is due and runs it.`,
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
err := internal.Lock()
CheckErr(err)
defer lock.Unlock()
defer internal.Unlock()

err = internal.RunCron()
CheckErr(err)
Expand Down
5 changes: 2 additions & 3 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)

Expand All @@ -14,9 +13,9 @@ var execCmd = &cobra.Command{
Short: "Execute arbitrary native restic commands for given backends",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
err := internal.Lock()
CheckErr(err)
defer lock.Unlock()
defer internal.Unlock()

selected, err := internal.GetAllOrSelected(cmd, true)
CheckErr(err)
Expand Down
5 changes: 2 additions & 3 deletions cmd/forget.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)

Expand All @@ -11,9 +10,9 @@ var forgetCmd = &cobra.Command{
Short: "Forget and optionally prune snapshots according the specified policies",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
err := internal.Lock()
CheckErr(err)
defer lock.Unlock()
defer internal.Unlock()

selected, err := internal.GetAllOrSelected(cmd, false)
CheckErr(err)
Expand Down
5 changes: 2 additions & 3 deletions cmd/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"

"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)

Expand All @@ -14,9 +13,9 @@ var restoreCmd = &cobra.Command{
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
err := internal.Lock()
CheckErr(err)
defer lock.Unlock()
defer internal.Unlock()

location, _ := cmd.Flags().GetString("location")
l, ok := internal.GetLocation(location)
Expand Down
4 changes: 2 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/flags"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"

homedir "github.com/mitchellh/go-homedir"
Expand All @@ -18,7 +17,7 @@ import (
func CheckErr(err error) {
if err != nil {
colors.Error.Fprintln(os.Stderr, "Error:", err)
lock.Unlock()
internal.Unlock()
os.Exit(1)
}
}
Expand All @@ -42,6 +41,7 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&flags.VERBOSE, "verbose", "v", false, "verbose mode")
rootCmd.PersistentFlags().StringVar(&flags.RESTIC_BIN, "restic-bin", "restic", "specify custom restic binary")
rootCmd.PersistentFlags().StringVar(&flags.DOCKER_IMAGE, "docker-image", "cupcakearmy/autorestic:"+internal.VERSION, "specify a custom docker image")
rootCmd.PersistentFlags().StringVar(&flags.LOCKFILE, "lockfile", "", "specify a custom path for the lockfile (defaults to .autorestic.lock.yml next to the loaded autorestic config file)")
cobra.OnInitialize(initConfig)
}

Expand Down
3 changes: 1 addition & 2 deletions cmd/unlock.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)

Expand All @@ -33,7 +32,7 @@ To check you can run "ps aux | grep autorestic".`,
}
}

err := lock.Unlock()
err := internal.Unlock()
if err != nil {
colors.Error.Println("Could not unlock:", err)
return
Expand Down
1 change: 1 addition & 0 deletions docs/pages/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"quick": "Quick Start",
"installation": "Installation",
"config": "Configuration",
"lockfile": "Lockfile",
"location": "Locations",
"backend": "Backend",
"cli": "CLI",
Expand Down
8 changes: 8 additions & 0 deletions docs/pages/cli/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ With `--restic-bin` you can specify to run a specific restic binary. This can be
```bash
autorestic --restic-bin /some/path/to/my/custom/restic/binary
```

## `--lockfile`

Specify the path for the [lockfile](../lockfile.md) used by `autorestic`. If omitted, this will default to `.autorestic.lock.yml` next to the loaded config file.

```bash
autorestic --lockfile /path/to/my/.autorestic.lock.yml
```
14 changes: 14 additions & 0 deletions docs/pages/lockfile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Lockfile

Under the hood, `autorestic` uses a lockfile to ensure that only one instance is running and to keep track of when [cronjobs](./location/cron.md) were last run.

By default, the lockfile is stored next to your [configuration file](./config.md) as `.autorestic.lock.yml`. In other words, if your config file is located at `/some/path/.autorestic.yml`, then the lockfile will be located at `/some/path/.autorestic.lock.yml`.

## Customization

The path to the lockfile can be customized if need be. This can be done is a few ways:

1. Using the `--lockfile ...` command line flag
1. Setting `lockfile: ...` in the configuration file

Note that `autorestic` will check for a customized lockfile path in the order listed above. This means that if you specify a lockfile path in multiple places, the method that's higher in the list will win.
4 changes: 2 additions & 2 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/flags"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/joho/godotenv"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
Expand All @@ -24,6 +23,7 @@ type Options map[string]OptionMap

type Config struct {
Version string `mapstructure:"version" yaml:"version"`
Lockfile string `mapstructure:"lockfile,omitempty" yaml:"lockfile,omitempty"`
Extras interface{} `mapstructure:"extras" yaml:"extras"`
Locations map[string]Location `mapstructure:"locations" yaml:"locations"`
Backends map[string]Backend `mapstructure:"backends" yaml:"backends"`
Expand All @@ -40,7 +40,7 @@ func exitConfig(err error, msg string) {
if msg != "" {
colors.Error.Println(msg)
}
lock.Unlock()
Unlock()
os.Exit(1)
}

Expand Down
9 changes: 5 additions & 4 deletions internal/flags/flags.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package flags

var (
CI bool = false
VERBOSE bool = false
CRON_LEAN bool = false
RESTIC_BIN string
CI bool = false
VERBOSE bool = false
CRON_LEAN bool = false
RESTIC_BIN string
DOCKER_IMAGE string
LOCKFILE string
)
5 changes: 2 additions & 3 deletions internal/location.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/flags"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/cupcakearmy/autorestic/internal/metadata"
"github.com/robfig/cron"
)
Expand Down Expand Up @@ -442,11 +441,11 @@ func (l Location) RunCron() error {
if err != nil {
return err
}
last := time.Unix(lock.GetCron(l.name), 0)
last := time.Unix(GetCron(l.name), 0)
next := schedule.Next(last)
now := time.Now()
if now.After(next) {
lock.SetCron(l.name, now.Unix())
SetCron(l.name, now.Unix())
errs := l.Backup(true, "")
if len(errs) > 0 {
return fmt.Errorf("Failed to backup location \"%s\":\n%w", l.name, errors.Join(errs...))
Expand Down
44 changes: 34 additions & 10 deletions internal/lock/lock.go → internal/lock.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package lock
package internal

import (
"os"
"path"
"path/filepath"
"sync"

"github.com/cupcakearmy/autorestic/internal/colors"
Expand All @@ -12,24 +13,47 @@ import (

var lock *viper.Viper
var file string
var once sync.Once
var lockOnce sync.Once

const (
RUNNING = "running"
)

// getLockfilePath returns the path to the lockfile. The path for the lockfile
// can be sources from multiple places If flags.LOCKFILE is set, its value is
// used; if the config has the `lockfile` option set, its value is used;
// otherwise the path is generated relative to the config file.
func getLockfilePath() string {
if flags.LOCKFILE != "" {
abs, err := filepath.Abs(flags.LOCKFILE)
if err != nil {
return flags.LOCKFILE
}
return abs
}

if lockfile := GetConfig().Lockfile; lockfile != "" {
abs, err := filepath.Abs(lockfile)
if err != nil {
return lockfile
}
return abs
}

p := viper.ConfigFileUsed()
if p == "" {
colors.Error.Println("cannot lock before reading config location")
os.Exit(1)
}
return path.Join(path.Dir(p), ".autorestic.lock.yml")
}

func getLock() *viper.Viper {
if lock == nil {

once.Do(func() {
lockOnce.Do(func() {
lock = viper.New()
lock.SetDefault("running", false)
p := viper.ConfigFileUsed()
if p == "" {
colors.Error.Println("cannot lock before reading config location")
os.Exit(1)
}
file = path.Join(path.Dir(p), ".autorestic.lock.yml")
file = getLockfilePath()
if !flags.CRON_LEAN {
colors.Faint.Println("Using lock:\t", file)
}
Expand Down
Loading