Skip to content
Draft
Show file tree
Hide file tree
Changes from 12 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
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
FROM golang:1.26-alpine AS builder

WORKDIR /app
COPY . .
COPY go.mod go.sum ./
Comment thread
mmittelb marked this conversation as resolved.
Outdated
RUN go mod download
COPY . .
WORKDIR /app/cmd/backup
RUN go build -o backup .

Expand Down
1 change: 1 addition & 0 deletions cmd/backup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type Config struct {
GoogleDriveImpersonateSubject string `split_words:"true"`
GoogleDriveEndpoint string `split_words:"true"`
GoogleDriveTokenURL string `split_words:"true"`
ActivateLazyRestart bool `split_words:"true" default:"false"`
Comment thread
mmittelb marked this conversation as resolved.
Outdated
source string
additionalEnvVars map[string]string
}
Expand Down
8 changes: 6 additions & 2 deletions cmd/backup/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
"github.com/offen/docker-volume-backup/internal/errwrap"
)

const LOCK_FILE = "/var/lock/dockervolumebackup.lock"

// lock opens a lockfile at the given location, keeping it locked until the
// caller invokes the returned release func. In case the lock is currently blocked
// by another execution, it will repeatedly retry until the lock is available
// or the given timeout is exceeded.
func (s *script) lock(lockfile string) (func() error, error) {
func (s *script) lock() (func() error, error) {
start := time.Now()
defer func() {
s.stats.LockedTime = time.Since(start)
Expand All @@ -26,7 +28,8 @@
deadline := time.NewTimer(s.c.LockTimeout)
defer deadline.Stop()

fileLock := flock.New(lockfile)
fileLock := flock.New(LOCK_FILE)
defer s.removeWaitingFile()

Check failure on line 32 in cmd/backup/lock.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `s.removeWaitingFile` is not checked (errcheck)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by the usage of lock and waiting file. These two lines in particular make me think they are the same, but somehow they aren't, no? Why is this defer not below, right after the waiting file has been written?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. You are right. Moving the defer improves readability. I pushed a commit to fix this.
  2. The lock file is written by the "active" script process to ensure backups are running consecutively (old behavior).
    The waiting file is written by "waiting" script processes and contains the containers and services the waiting process needs to be stopped. This information is used by the active script to determine which containers and services to start after it finished its backup run.


for {
acquired, err := fileLock.TryLock()
Expand All @@ -41,6 +44,7 @@
}

if !s.encounteredLock {
s.writeWaitingFile()

Check failure on line 47 in cmd/backup/lock.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `s.writeWaitingFile` is not checked (errcheck)
s.logger.Info(
fmt.Sprintf(
"Exclusive lock was not available on first attempt. Will retry until it becomes available or the timeout of %s is exceeded.",
Expand Down
22 changes: 11 additions & 11 deletions cmd/backup/run_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,6 @@ func runScript(c *Config) (err error) {

s := newScript(c)

unlock, lockErr := s.lock("/var/lock/dockervolumebackup.lock")
if lockErr != nil {
err = errwrap.Wrap(lockErr, "error acquiring file lock")
return
}
defer func() {
if derr := unlock(); derr != nil {
err = errors.Join(err, errwrap.Wrap(derr, "error releasing file lock"))
}
}()

unset, warnings, err := s.c.resolve()
if err != nil {
return errwrap.Wrap(err, "error applying env")
Expand Down Expand Up @@ -81,6 +70,17 @@ func runScript(c *Config) (err error) {
return
}

unlock, lockErr := s.lock()
if lockErr != nil {
err = errwrap.Wrap(lockErr, "error acquiring file lock")
return
}
defer func() {
if derr := unlock(); derr != nil {
err = errors.Join(err, errwrap.Wrap(derr, "error releasing file lock"))
}
}()

err = func() (err error) {
scriptErr := func() error {
if err := s.withLabeledCommands(lifecyclePhaseArchive, func() (err error) {
Expand Down
17 changes: 17 additions & 0 deletions cmd/backup/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"text/template"
"time"

"github.com/google/uuid"
"github.com/offen/docker-volume-backup/internal/errwrap"
"github.com/offen/docker-volume-backup/internal/storage"
"github.com/offen/docker-volume-backup/internal/storage/azure"
Expand All @@ -30,6 +31,8 @@ import (
// script holds all the stateful information required to orchestrate a
// single backup run.
type script struct {
id string

cli *client.Client
storages []storage.Backend
logger *slog.Logger
Expand All @@ -43,6 +46,10 @@ type script struct {

encounteredLock bool

isSwarm bool
containersToStop []handledContainer
servicesToScaleDown []handledSwarmService

c *Config
}

Expand Down Expand Up @@ -72,6 +79,8 @@ func newScript(c *Config) *script {
}

func (s *script) init() error {
s.id = uuid.New().String()

s.registerHook(hookLevelPlumbing, func(error) error {
s.stats.EndTime = time.Now()
s.stats.TookTime = s.stats.EndTime.Sub(s.stats.StartTime)
Expand Down Expand Up @@ -135,6 +144,14 @@ func (s *script) init() error {
}
return nil
})
s.isSwarm, err = isSwarm(s.cli)
if err != nil {
return errwrap.Wrap(err, "error determining swarm state")
}
err = s.determineContainersAndServicesToStop()
if err != nil {
return errwrap.Wrap(err, "error determining containers and services to stop")
}
}

logFunc := func(logType storage.LogLevel, context string, msg string, params ...any) {
Expand Down
Loading
Loading