Skip to content

Commit

Permalink
run shell commands before and after a profile
Browse files Browse the repository at this point in the history
In previous versions, you could only run commands before and after a backup
  • Loading branch information
creativeprojects committed Jun 24, 2020
1 parent d71f45f commit 88cb74f
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 18 deletions.
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -110,4 +110,5 @@ rest-server:

nightly:
go install github.com/goreleaser/goreleaser
go mod tidy
goreleaser --snapshot --skip-publish --rm-dist
42 changes: 29 additions & 13 deletions README.md
Expand Up @@ -15,6 +15,7 @@ With resticprofile:
* You can check a repository before or after a backup
* You can create groups of profiles that will run sequentially
* You can run shell commands before or after a backup
* You can also run shell commands before or after running a profile (any command): useful if you need to mount your backup disk
* You can send a backup stream via _stdin_
* You can start restic at a lower or higher priority (Priority Class in Windows, *nice* in all unixes) and/or _ionice_ (only available on Linux)

Expand All @@ -33,7 +34,7 @@ It's been actively tested on macOS X and Linux, and regularly tested on Windows.

**This is at _beta_ stage. Please don't use it in production yet. Even though I'm using it on my servers, I cannot guarantee all combinations of configuration are going to work properly for you.**

## Install
## Installation

Here's a simple script to download the binary automatically for you. It works on mac OS X, FreeBSD, OpenBSD and Linux:

Expand All @@ -59,14 +60,19 @@ It will install resticprofile in `/usr/local/bin/`
- Download the package corresponding to your system and CPU from the [release page](https://github.com/creativeprojects/resticprofile/releases)
- Once downloaded you need to open the archive and copy the binary file `resticprofile` (or `resticprofile.exe`) in your PATH.

## Update
## Upgrade

Once installed, you can easily update resticprofile to the latest release using this command:
Once installed, you can easily upgrade resticprofile to the latest release using this flag:

```
$ resticprofile --self-update
```

Starting with version 0.7.0 you can also upgrade resticprofile using this command:

```
$ resticprofile self-update
```

## Using docker image ##

Expand Down Expand Up @@ -193,6 +199,9 @@ full-backup = [ "root", "src" ]
repository = "/backup"
password-file = "key"
initialize = false
# will run these scripts before and after each command (including 'backup')
run-before = "mount /backup"
run-after = "umount /backup"

[default.env]
TMPDIR= "/tmp"
Expand Down Expand Up @@ -247,14 +256,15 @@ initialize = true

# 'backup' command of profile 'src'
[src.backup]
run-before = [ "echo Starting!", "ls -al ./src" ]
run-after = "echo All Done!"
exclude = [ '/**/.git' ]
exclude-caches = true
one-file-system = false
tag = [ "test", "dev" ]
source = [ "./src" ]
check-before = true
# will only run these scripts before and after a backup
run-before = [ "echo Starting!", "ls -al ./src" ]
run-after = "echo All Done!"

# retention policy for profile src
[src.retention]
Expand Down Expand Up @@ -325,15 +335,15 @@ See all available profiles in your configuration file (and the restic commands w
$ resticprofile profiles
Profiles available:
stdin (snapshots, backup)
src (retention, backup, snapshots)
root (backup, retention)
documents (snapshots, backup)
default (env)
self (backup, snapshots)
stdin: (backup)
default: (env)
root: (retention, backup)
src: (retention, backup)
linux: (retention, backup, snapshots, env)
no-cache: (n/a)
Groups available:
full-backup: root, src
full-backup: root, src
```

Expand Down Expand Up @@ -369,10 +379,14 @@ resticprofile flags:
-n, --name string profile name (default "default")
--no-ansi disable ansi control characters (disable console colouring)
-q, --quiet display only warnings and errors
--self-update auto update of resticprofile (does not update restic)
--theme string console colouring theme (dark, light, none) (default "light")
-v, --verbose display all debugging information
resticprofile own commands:
profiles display profile names from the configuration file
self-update update resticprofile to latest version (does not update restic)
systemd-unit create a user systemd timer
```

A command is a restic command **except** for one command recognized by resticprofile only: `profiles`
Expand Down Expand Up @@ -417,6 +431,8 @@ Flags used by resticprofile only
* ****inherit****: string
* **initialize**: true / false
* **lock**: string: specify a local lockfile
* **run-before**: string OR list of strings
* **run-after**: string OR list of strings

Flags passed to the restic command line

Expand Down
18 changes: 16 additions & 2 deletions config/profile.go
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"

"github.com/creativeprojects/resticprofile/clog"
"github.com/creativeprojects/resticprofile/constants"
"github.com/spf13/viper"
)
Expand All @@ -23,6 +24,8 @@ type Profile struct {
Initialize bool `mapstructure:"initialize"`
Inherit string `mapstructure:"inherit"`
Lock string `mapstructure:"lock"`
RunBefore []string `mapstructure:"run-before"`
RunAfter []string `mapstructure:"run-after"`
Environment map[string]string `mapstructure:"env"`
Backup *BackupSection `mapstructure:"backup"`
Retention *RetentionSection `mapstructure:"retention"`
Expand Down Expand Up @@ -185,17 +188,25 @@ func (p *Profile) GetCommandFlags(command string) map[string][]string {

switch command {
case constants.CommandBackup:
if p.Backup == nil {
clog.Warning("No definition for backup command in this profile")
break
}
commandFlags := convertStructToFlags(*p.Backup)
if commandFlags != nil && len(commandFlags) > 0 {
flags = mergeFlags(flags, commandFlags)
}
flags = addOtherFlags(flags, p.Backup.OtherFlags)

case constants.CommandSnapshots:
flags = addOtherFlags(flags, p.Snapshots)
if p.Snapshots != nil {
flags = addOtherFlags(flags, p.Snapshots)
}

case constants.CommandCheck:
flags = addOtherFlags(flags, p.Check)
if p.Check != nil {
flags = addOtherFlags(flags, p.Check)
}
}

return flags
Expand All @@ -214,6 +225,9 @@ func (p *Profile) GetRetentionFlags() map[string][]string {

// GetBackupSource returns the directories to backup
func (p *Profile) GetBackupSource() []string {
if p.Backup == nil {
return nil
}
return p.Backup.Source
}

Expand Down
2 changes: 2 additions & 0 deletions examples/dev.conf
Expand Up @@ -29,6 +29,8 @@ TMP= "/tmp"
[root]
inherit = "default"
initialize = true
run-before = "echo mount backup disk"
run-after = ["echo sync", "echo umount backup disk"]

# 'backup' command of profile 'root'
[root.backup]
Expand Down
2 changes: 2 additions & 0 deletions examples/profiles.conf
Expand Up @@ -69,6 +69,8 @@ host = true
[src]
inherit = "default"
initialize = true
run-before = "echo mount backup disk"
run-after = ["echo sync", "echo umount backup disk"]

# 'backup' command of profile 'src'
[src.backup]
Expand Down
13 changes: 13 additions & 0 deletions main.go
Expand Up @@ -244,6 +244,12 @@ func runProfile(global *config.Global, flags commandLineFlags, profileName strin
err = lockRun(profile.Lock, func() error {
var err error

// pre-profile commands
err = wrapper.runProfilePreCommand()
if err != nil {
return err
}

// pre-commands (for backup)
if resticCommand == constants.CommandBackup {
// Shell commands
Expand Down Expand Up @@ -295,6 +301,13 @@ func runProfile(global *config.Global, flags commandLineFlags, profileName strin
return err
}
}

// post-profile commands
err = wrapper.runProfilePostCommand()
if err != nil {
return err
}

return nil
})
if err != nil {
Expand Down
40 changes: 37 additions & 3 deletions wrapper.go
Expand Up @@ -76,7 +76,7 @@ func (r *resticWrapper) prepareCommand(command string, args []string) shellComma
rCommand := newShellCommand(r.resticBinary, arguments, env)
rCommand.sigChan = r.sigChan

if command == constants.CommandBackup && r.profile.Backup.UseStdin {
if command == constants.CommandBackup && r.profile.Backup != nil && r.profile.Backup.UseStdin {
clog.Debug("Redirecting stdin to the backup")
rCommand.useStdin = true
}
Expand All @@ -88,7 +88,7 @@ func (r *resticWrapper) runPreCommand(command string) error {
if command != constants.CommandBackup {
return nil
}
if r.profile.Backup.RunBefore == nil || len(r.profile.Backup.RunBefore) == 0 {
if r.profile.Backup == nil || r.profile.Backup.RunBefore == nil || len(r.profile.Backup.RunBefore) == 0 {
return nil
}
for i, preCommand := range r.profile.Backup.RunBefore {
Expand All @@ -109,7 +109,7 @@ func (r *resticWrapper) runPostCommand(command string) error {
if command != constants.CommandBackup {
return nil
}
if r.profile.Backup.RunAfter == nil || len(r.profile.Backup.RunAfter) == 0 {
if r.profile.Backup == nil || r.profile.Backup.RunAfter == nil || len(r.profile.Backup.RunAfter) == 0 {
return nil
}
for i, postCommand := range r.profile.Backup.RunAfter {
Expand All @@ -125,6 +125,40 @@ func (r *resticWrapper) runPostCommand(command string) error {
return nil
}

func (r *resticWrapper) runProfilePreCommand() error {
if r.profile.RunBefore == nil || len(r.profile.RunBefore) == 0 {
return nil
}
for i, preCommand := range r.profile.RunBefore {
clog.Debugf("Starting 'run-before' profile command %d/%d", i+1, len(r.profile.RunBefore))
env := append(os.Environ(), r.getEnvironment()...)
rCommand := newShellCommand(preCommand, nil, env)
rCommand.sigChan = r.sigChan
err := runShellCommand(rCommand)
if err != nil {
return err
}
}
return nil
}

func (r *resticWrapper) runProfilePostCommand() error {
if r.profile.RunAfter == nil || len(r.profile.RunAfter) == 0 {
return nil
}
for i, postCommand := range r.profile.RunAfter {
clog.Debugf("Starting 'run-after' profile command %d/%d", i+1, len(r.profile.RunAfter))
env := append(os.Environ(), r.getEnvironment()...)
rCommand := newShellCommand(postCommand, nil, env)
rCommand.sigChan = r.sigChan
err := runShellCommand(rCommand)
if err != nil {
return err
}
}
return nil
}

func (r *resticWrapper) getEnvironment() []string {
if r.profile.Environment == nil || len(r.profile.Environment) == 0 {
return nil
Expand Down

0 comments on commit 88cb74f

Please sign in to comment.