fix: hook bug fixes

This commit is contained in:
garethgeorge
2024-06-13 00:25:26 -07:00
parent 72cf45be83
commit 2c7d85b33b
7 changed files with 140 additions and 50 deletions
+7
View File
@@ -64,6 +64,13 @@ jobs:
with:
fetch-depth: 0
# download dist artifacts from previous job
- name: Download Artifacts
uses: actions/download-artifact@v3
with:
name: release-artifacts
path: dist
- name: Get Release Artifact URL # used to append installer executables to the release after GoReleaser runs
id: geturl
run: |
+1
View File
@@ -43,6 +43,7 @@ Backrest is a web-accessible backup solution built on top of [restic](https://re
```bash [MacOS]
brew tap garethgeorge/homebrew-backrest-tap
brew install backrest
brew services start backrest
```
```bash [Arch Linux]
paru -Sy backrest
@@ -12,6 +12,48 @@ When run on `CONDITION_SNAPSHOT_START` command hooks have the ability to send co
- `ON_ERROR_FATAL` - If the script exits with a non-zero status, it is treated as a backup failure and error notifications are triggered.
- `ON_ERROR_IGNORE` - If the script exits with a non-zero status, the backup operation will continue and the error will be ignored.
## Examples
#### Notify a healthcheck service
Ping a healthcheck service (e.g. https://healthchecks.io/ in the example) to notify it of backup status (or failure) using a command hook.
Note that this hook example takes advantage of the fact that the hook is a golang template to render different commands based on whether an error occurred.
**Condition** `CONDITION_SNAPSHOT_END`
**Script**
```bash
#!/bin/bash
{{ if .Error -}}
curl -fsS --retry 3 https://hc-ping.com/your-uuid/fail
{{ else -}}
curl -fsS --retry 3 https://hc-ping.com/your-uuid
{{ end -}}
```
**Error Behavior:** `ON_ERROR_IGNORE`
#### (MacOS) Show system notification
Show a system notification using the `osascript` command on MacOS.
**Condition** `CONDITION_SNAPSHOT_END`, `CONDITION_PRUNE_ERROR`, `CONDITION_CHECK_ERROR`
**Script**
```bash
#!/bin/bash
{{ if .Error -}}
osascript -e 'display notification "{{ .ShellEscape .Task }} failed" with title "Backrest"'
{{ else -}}
osascript -e 'display notification "{{ .ShellEscape .Task }} succeeded" with title "Backrest"'
{{ end -}}
```
#### Check for internet connectivity
Add a hook to check for internet connectivity before running a backup.
@@ -74,24 +116,3 @@ fi
```
**Error Behavior:** `ON_ERROR_CANCEL`
#### Notify a healthcheck service
Ping a healthcheck service (e.g. https://healthchecks.io/ in the example) to notify it of backup status (or failure) using a command hook.
Note that this hook example takes advantage of the fact that the hook is a golang template to render different commands based on whether an error occurred.
**Condition** `CONDITION_SNAPSHOT_END`
**Script**
```bash
#!/bin/bash
{{ if .Error -}}
curl -fsS --retry 3 https://hc-ping.com/your-uuid/fail
{{ else -}}
curl -fsS --retry 3 https://hc-ping.com/your-uuid
{{ end -}}
```
**Error Behavior:** `ON_ERROR_IGNORE`
+27 -13
View File
@@ -37,6 +37,20 @@ func (v HookVars) EventName(cond v1.Hook_Condition) string {
return "error"
case v1.Hook_CONDITION_SNAPSHOT_ERROR:
return "snapshot error"
case v1.Hook_CONDITION_SNAPSHOT_WARNING:
return "snapshot warning"
case v1.Hook_CONDITION_CHECK_START:
return "check start"
case v1.Hook_CONDITION_CHECK_ERROR:
return "check error"
case v1.Hook_CONDITION_CHECK_SUCCESS:
return "check success"
case v1.Hook_CONDITION_PRUNE_START:
return "prune start"
case v1.Hook_CONDITION_PRUNE_ERROR:
return "prune error"
case v1.Hook_CONDITION_PRUNE_SUCCESS:
return "prune success"
default:
return "unknown"
}
@@ -96,13 +110,9 @@ func (v HookVars) Summary() (string, error) {
switch v.Event {
case v1.Hook_CONDITION_SNAPSHOT_START:
return v.renderTemplate(templateForSnapshotStart)
case v1.Hook_CONDITION_SNAPSHOT_END:
case v1.Hook_CONDITION_SNAPSHOT_END, v1.Hook_CONDITION_SNAPSHOT_WARNING, v1.Hook_CONDITION_SNAPSHOT_SUCCESS:
return v.renderTemplate(templateForSnapshotEnd)
case v1.Hook_CONDITION_ANY_ERROR:
return v.renderTemplate(templateForError)
case v1.Hook_CONDITION_SNAPSHOT_ERROR:
return v.renderTemplate(templateForError)
case v1.Hook_CONDITION_SNAPSHOT_WARNING:
case v1.Hook_CONDITION_ANY_ERROR, v1.Hook_CONDITION_SNAPSHOT_ERROR, v1.Hook_CONDITION_CHECK_ERROR, v1.Hook_CONDITION_PRUNE_ERROR:
return v.renderTemplate(templateForError)
default:
return "unknown event", nil
@@ -123,15 +133,21 @@ func (v HookVars) renderTemplate(templ string) (string, error) {
return buf.String(), nil
}
var templateForSnapshotEnd = `
Backrest Notification for Snapshot End
var defaultTemplate = `
{{ if .Error -}}
Backrest Notification for Error
Task: "{{ .Task }}" at {{ .FormatTime .CurTime }}
Event: {{ .EventName .Event }}
Repo: {{ .Repo.Id }}
Plan: {{ .Plan.Id }}
Repo: {{ .Repo.Id }}
`
var templateForSnapshotEnd = `
Backrest Snapshot Notification
Task: {{ .Task }} at {{ .FormatTime .CurTime }}
Event: {{ .EventName .Event }}
Snapshot: {{ .SnapshotId }}
{{ if .Error -}}
Failed to create snapshot: {{ .Error }}
Error: {{ .Error }}
{{ else -}}
{{ if .SnapshotStats -}}
@@ -154,8 +170,6 @@ Backup Statistics:
{{ end }}`
var templateForError = `
Backrest Notification for Error
Task: "{{ .Task }}" at {{ .FormatTime .CurTime }}
{{ if .Error -}}
Error: {{ .Error }}
{{ end }}
+9 -4
View File
@@ -190,11 +190,16 @@ func (t *BackupTask) Run(ctx context.Context, st ScheduledTask, runner TaskRunne
}
op.Status = v1.OperationStatus_STATUS_WARNING
op.DisplayMessage = "Partial backup, some files may not have been read completely."
}
runner.ExecuteHooks([]v1.Hook_Condition{
v1.Hook_CONDITION_SNAPSHOT_END,
}, vars)
runner.ExecuteHooks([]v1.Hook_Condition{
v1.Hook_CONDITION_SNAPSHOT_END,
}, vars)
} else {
runner.ExecuteHooks([]v1.Hook_Condition{
v1.Hook_CONDITION_SNAPSHOT_SUCCESS,
v1.Hook_CONDITION_SNAPSHOT_END,
}, vars)
}
op.SnapshotId = summary.SnapshotId
backupOp.OperationBackup.LastStatus = protoutil.BackupProgressEntryToProto(summary)
+1 -1
View File
@@ -25,7 +25,7 @@ type CheckTask struct {
func NewCheckTask(repoID, planID string, force bool) Task {
return &CheckTask{
BaseTask: BaseTask{
TaskName: fmt.Sprintf("prune repo %q", repoID),
TaskName: fmt.Sprintf("check for repo %q", repoID),
TaskRepoID: repoID,
TaskPlanID: planID,
},
+53 -11
View File
@@ -120,15 +120,17 @@ export const HooksFormList = () => {
style={{ marginBottom: "5px" }}
>
<Form.Item name={[field.name, "conditions"]}>
<Select
mode="multiple"
allowClear
style={{ width: "100%" }}
placeholder="Runs when..."
options={proto3
.getEnumType(Hook_Condition)
.values.map((v) => ({ label: v.name, value: v.name }))}
/>
<HookConditionsTooltip>
<Select
mode="multiple"
allowClear
style={{ width: "100%" }}
placeholder="Runs when..."
options={proto3
.getEnumType(Hook_Condition)
.values.map((v) => ({ label: v.name, value: v.name }))}
/>
</HookConditionsTooltip>
</Form.Item>
<Form.Item
shouldUpdate={(prevValues, curValues) => {
@@ -410,8 +412,8 @@ const ItemOnErrorSelector = ({ field }: { field: FormListFieldData }) => {
<Tooltip
title={
<>
What happens when the hook fails (currently only has effect on
backup start hooks)
What happens when the hook fails (only effective on start hooks e.g.
backup start, prune start, check start)
<ul>
<li>
IGNORE - the failure is ignored, subsequent hooks and the backup
@@ -449,3 +451,43 @@ const requiredField = (message: string, extra?: Rule) => ({
required: true,
message: message,
});
const HookConditionsTooltip = ({ children }: { children: React.ReactNode }) => {
return (
<Tooltip
title={
<div>
Available conditions
<ul>
<li>CONDITION_ANY_ERROR - error executing any task</li>
<li>CONDITION_SNAPSHOT_START - start of a backup operation</li>
<li>
CONDITION_SNAPSHOT_END - end of backup operation (success or
failure)
</li>
<li>
CONDITION_SNAPSHOT_SUCCESS - end of successful backup operation
</li>
<li>CONDITION_SNAPSHOT_ERROR - end of failed backup</li>
<li>CONDITION_SNAPSHOT_WARNING - end of partial backup</li>
<li>CONDITION_PRUNE_START - start of prune operation</li>
<li>CONDITION_PRUNE_SUCCESS - end of successful prune</li>
<li>CONDITION_PRUNE_ERROR - end of failed prune</li>
<li>CONDITION_CHECK_START - start of check operation</li>
<li>CONDITION_CHECK_SUCCESS - end of successful check</li>
<li>CONDITION_CHECK_ERROR - end of failed check</li>
</ul>
for more info see the{" "}
<a
href="https://garethgeorge.github.io/backrest/docs/hooks"
target="_blank"
>
documentation
</a>
</div>
}
>
{children}
</Tooltip>
);
};