Compare commits

..

6 Commits

Author SHA1 Message Date
Frederik Ring
24796928e9 Add test case as per #187 2023-02-10 19:16:23 +01:00
Frederik Ring
81e36289c1 Try deleting file in post hook to ensure correct order 2023-02-10 18:39:40 +01:00
Frederik Ring
2d37e08743 Use go 1.20, join errors using stdlib (#182)
* Use go 1.20, join errors using stdlib

* Use go 1.20 proper
2023-02-02 21:07:25 +01:00
Frederik Ring
1e36bd3eb7 Non-streaming upload to WebDAV fails on big files (#181) 2023-01-16 08:28:29 +01:00
Frederik Ring
e93a74dd48 Instructions in issue templates are not supposed to be shown after submission 2023-01-12 18:02:46 +01:00
Frederik Ring
f799e6c2e9 Azure Blob Storage is missing from headline in README 2023-01-11 21:54:50 +01:00
17 changed files with 107 additions and 53 deletions

View File

@@ -8,7 +8,9 @@ assignees: ''
---
**Describe the bug**
<!--
A clear and concise description of what the bug is.
-->
**To Reproduce**
Steps to reproduce the behavior:
@@ -17,12 +19,16 @@ Steps to reproduce the behavior:
3. ...
**Expected behavior**
<!--
A clear and concise description of what you expected to happen.
-->
**Desktop (please complete the following information):**
- Image Version: [e.g. v2.21.0]
- Docker Version: [e.g. 20.10.17]
- Docker Compose Version (if applicable): [e.g. 1.29.2]
**Version (please complete the following information):**
- Image Version: <!-- e.g. v2.21.0 -->
- Docker Version: <!-- e.g. 20.10.17 -->
- Docker Compose Version (if applicable): <!-- e.g. 1.29.2 -->
**Additional context**
<!--
Add any other context about the problem here.
-->

View File

@@ -8,13 +8,21 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
<!--
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-->
**Describe the solution you'd like**
<!--
A clear and concise description of what you want to happen.
-->
**Describe alternatives you've considered**
<!--
A clear and concise description of any alternative solutions or features you've considered.
-->
**Additional context**
<!--
Add any other context or screenshots about the feature request here.
-->

View File

@@ -8,13 +8,21 @@ assignees: ''
---
**What are you trying to do?**
<!--
A clear and concise description of what you are trying to do, but cannot get working.
-->
**What is your current configuration?**
<!--
Add the full configuration you are using. Please redact out any real-world credentials.
-->
**Log output**
<!--
Provide the full log output of your setup.
-->
**Additional context**
<!--
Add any other context or screenshots about the support request here.
-->

View File

@@ -1,7 +1,7 @@
# Copyright 2021 - Offen Authors <hioffen@posteo.de>
# SPDX-License-Identifier: MPL-2.0
FROM golang:1.19-alpine as builder
FROM golang:1.20-alpine as builder
WORKDIR /app
COPY . .

View File

@@ -4,7 +4,7 @@
# docker-volume-backup
Backup Docker volumes locally or to any S3, WebDAV or SSH compatible storage.
Backup Docker volumes locally or to any S3, WebDAV, Azure Blob Storage or SSH compatible storage.
The [offen/docker-volume-backup](https://hub.docker.com/r/offen/docker-volume-backup) Docker image can be used as a lightweight (below 15MB) sidecar container to an existing Docker setup.
It handles __recurring or one-off backups of Docker volumes__ to a __local directory__, __any S3, WebDAV, Azure Blob Storage or SSH compatible storage (or any combination) and rotates away old backups__ if configured. It also supports __encrypting your backups using GPG__ and __sending notifications for failed backup runs__.

View File

@@ -4,10 +4,9 @@
package main
import (
"errors"
"fmt"
"sort"
"github.com/offen/docker-volume-backup/internal/utilities"
)
// hook contains a queued action that can be trigger them when the script
@@ -52,7 +51,7 @@ func (s *script) runHooks(err error) error {
}
}
if len(actionErrors) != 0 {
return utilities.Join(actionErrors...)
return errors.Join(actionErrors...)
}
return nil
}

View File

@@ -6,13 +6,13 @@ package main
import (
"bytes"
_ "embed"
"errors"
"fmt"
"os"
"text/template"
"time"
sTypes "github.com/containrrr/shoutrrr/pkg/types"
"github.com/offen/docker-volume-backup/internal/utilities"
)
//go:embed notifications.tmpl
@@ -69,7 +69,7 @@ func (s *script) sendNotification(title, body string) error {
}
}
if len(errs) != 0 {
return fmt.Errorf("sendNotification: error sending message: %w", utilities.Join(errs...))
return fmt.Errorf("sendNotification: error sending message: %w", errors.Join(errs...))
}
return nil
}

View File

@@ -5,6 +5,7 @@ package main
import (
"context"
"errors"
"fmt"
"io"
"io/fs"
@@ -20,7 +21,6 @@ import (
"github.com/offen/docker-volume-backup/internal/storage/s3"
"github.com/offen/docker-volume-backup/internal/storage/ssh"
"github.com/offen/docker-volume-backup/internal/storage/webdav"
"github.com/offen/docker-volume-backup/internal/utilities"
"github.com/containrrr/shoutrrr"
"github.com/containrrr/shoutrrr/pkg/router"
@@ -329,7 +329,7 @@ func (s *script) stopContainers() (func() error, error) {
stopError = fmt.Errorf(
"stopContainers: %d error(s) stopping containers: %w",
len(stopErrors),
utilities.Join(stopErrors...),
errors.Join(stopErrors...),
)
}
@@ -380,7 +380,7 @@ func (s *script) stopContainers() (func() error, error) {
return fmt.Errorf(
"stopContainers: %d error(s) restarting containers and services: %w",
len(restartErrors),
utilities.Join(restartErrors...),
errors.Join(restartErrors...),
)
}
s.logger.Infof(

View File

@@ -6,6 +6,7 @@ package azure
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path/filepath"
@@ -18,7 +19,6 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
"github.com/offen/docker-volume-backup/internal/storage"
"github.com/offen/docker-volume-backup/internal/utilities"
)
type azureBlobStorage struct {
@@ -135,21 +135,21 @@ func (b *azureBlobStorage) Prune(deadline time.Time, pruningPrefix string) (*sto
if err := b.DoPrune(b.Name(), len(matches), int(totalCount), "Azure Blob Storage backup(s)", func() error {
wg := sync.WaitGroup{}
wg.Add(len(matches))
var errors []error
var errs []error
for _, match := range matches {
name := match
go func() {
_, err := b.client.DeleteBlob(context.Background(), b.containerName, name, nil)
if err != nil {
errors = append(errors, err)
errs = append(errs, err)
}
wg.Done()
}()
}
wg.Wait()
if len(errors) != 0 {
return utilities.Join(errors...)
if len(errs) != 0 {
return errors.Join(errs...)
}
return nil
}); err != nil {

View File

@@ -4,6 +4,7 @@
package local
import (
"errors"
"fmt"
"io"
"os"
@@ -12,7 +13,6 @@ import (
"time"
"github.com/offen/docker-volume-backup/internal/storage"
"github.com/offen/docker-volume-backup/internal/utilities"
)
type localStorage struct {
@@ -127,7 +127,7 @@ func (b *localStorage) Prune(deadline time.Time, pruningPrefix string) (*storage
return fmt.Errorf(
"(*localStorage).Prune: %d error(s) deleting local files, starting with: %w",
len(removeErrors),
utilities.Join(removeErrors...),
errors.Join(removeErrors...),
)
}
return nil

View File

@@ -15,7 +15,6 @@ import (
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/offen/docker-volume-backup/internal/storage"
"github.com/offen/docker-volume-backup/internal/utilities"
)
type s3Storage struct {
@@ -159,7 +158,7 @@ func (b *s3Storage) Prune(deadline time.Time, pruningPrefix string) (*storage.Pr
}
}
if len(removeErrors) != 0 {
return utilities.Join(removeErrors...)
return errors.Join(removeErrors...)
}
return nil
}); err != nil {

View File

@@ -67,15 +67,17 @@ func (b *webDavStorage) Name() string {
// Copy copies the given file to the WebDav storage backend.
func (b *webDavStorage) Copy(file string) error {
bytes, err := os.ReadFile(file)
_, name := path.Split(file)
if err != nil {
return fmt.Errorf("(*webDavStorage).Copy: Error reading the file to be uploaded: %w", err)
}
if err := b.client.MkdirAll(b.DestinationPath, 0644); err != nil {
return fmt.Errorf("(*webDavStorage).Copy: Error creating directory '%s' on WebDAV server: %w", b.DestinationPath, err)
}
if err := b.client.Write(filepath.Join(b.DestinationPath, name), bytes, 0644); err != nil {
r, err := os.Open(file)
if err != nil {
return fmt.Errorf("(*webDavStorage).Copy: Error opening the file to be uploaded: %w", err)
}
if err := b.client.WriteStream(filepath.Join(b.DestinationPath, name), r, 0644); err != nil {
return fmt.Errorf("(*webDavStorage).Copy: Error uploading the file to WebDAV server: %w", err)
}
b.Log(storage.LogLevelInfo, b.Name(), "Uploaded a copy of backup '%s' to WebDAV URL '%s' at path '%s'.", file, b.url, b.DestinationPath)

View File

@@ -1,24 +0,0 @@
// Copyright 2022 - Offen Authors <hioffen@posteo.de>
// SPDX-License-Identifier: MPL-2.0
package utilities
import (
"errors"
"strings"
)
// Join takes a list of errors and joins them into a single error
func Join(errs ...error) error {
if len(errs) == 1 {
return errs[0]
}
var msgs []string
for _, err := range errs {
if err == nil {
continue
}
msgs = append(msgs, err.Error())
}
return errors.New("[" + strings.Join(msgs, ", ") + "]")
}

View File

@@ -11,7 +11,8 @@ services:
MARIADB_DATABASE: backup
labels:
# this is testing the deprecated label on purpose
- docker-volume-backup.exec-pre=/bin/sh -c 'mysqldump -ptest --all-databases > /tmp/volume/dump.sql'
- docker-volume-backup.archive-pre=/bin/sh -c 'mysqldump -ptest --all-databases > /tmp/volume/dump.sql'
- docker-volume-backup.archive-post=/bin/sh -c 'rm /tmp/volume/dump.sql'
- docker-volume-backup.copy-post=/bin/sh -c 'echo "post" > /tmp/volume/post.txt'
- docker-volume-backup.exec-label=test
volumes:

View File

@@ -0,0 +1,28 @@
version: "3"
services:
rsync:
image: eeacms/rsync
tty: true
restart: unless-stopped
labels:
- docker-volume-backup.exec-label=order
- docker-volume-backup.archive-pre=sh -c "rsync -aAX --ignore-missing-args --delete-missing-args /data/ /bu/"
- docker-volume-backup.archive-post=sh -c "rm -rf /bu/*"
volumes:
- ./fixture:/data:ro
- bu:/bu
backup:
image: offen/docker-volume-backup:${TEST_VERSION:-canary}
restart: always
environment:
BACKUP_FILENAME: backup.tar.gz
BACKUP_EXEC_LABEL: order
volumes:
- bu:/backup/order:ro
- ./local:/archive
- /var/run/docker.sock:/var/run/docker.sock
volumes:
bu:

View File

@@ -0,0 +1 @@
ok

26
test/order/run.sh Normal file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
set -e
cd $(dirname $0)
. ../util.sh
current_test=$(basename $(pwd))
mkdir -p local
docker compose up -d
sleep 10
docker compose exec backup backup
if [ ! -f "./local/backup.tar.gz" ]; then
fail "Could not find expected backup file."
fi
tmp_dir=$(mktemp -d)
tar -xvf ./local/backup.tar.gz -C $tmp_dir
if [ ! -f "$tmp_dir/backup/order/test.txt" ]; then
fail "Could not find expected file in untared archive."
fi
docker compose down --volumes