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

Move cron scheduling inside application #347

Merged
merged 5 commits into from
Feb 12, 2024
Merged
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
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,5 @@ WORKDIR /root
RUN apk add --no-cache ca-certificates

COPY --from=builder /app/cmd/backup/backup /usr/bin/backup
COPY --chmod=755 ./entrypoint.sh /root/

ENTRYPOINT ["/root/entrypoint.sh"]
ENTRYPOINT ["/usr/bin/backup", "-foreground"]
1 change: 1 addition & 0 deletions cmd/backup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Config struct {
BackupFilenameExpand bool `split_words:"true"`
BackupLatestSymlink string `split_words:"true"`
BackupArchive string `split_words:"true" default:"/archive"`
BackupCronExpression string `split_words:"true" default:"@daily"`
BackupRetentionDays int32 `split_words:"true" default:"-1"`
BackupPruningLeeway time.Duration `split_words:"true" default:"1m"`
BackupPruningPrefix string `split_words:"true"`
Expand Down
87 changes: 87 additions & 0 deletions cmd/backup/config_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2021-2022 - Offen Authors <[email protected]>
// SPDX-License-Identifier: MPL-2.0

package main

import (
"fmt"
"os"
"path/filepath"

"github.com/joho/godotenv"
"github.com/offen/envconfig"
)

// envProxy is a function that mimics os.LookupEnv but can read values from any other source
type envProxy func(string) (string, bool)

func loadConfig(lookup envProxy) (*Config, error) {
envconfig.Lookup = func(key string) (string, bool) {
value, okValue := lookup(key)
location, okFile := lookup(key + "_FILE")

switch {
case okValue && !okFile: // only value
return value, true
case !okValue && okFile: // only file
contents, err := os.ReadFile(location)
if err != nil {
return "", false
}
return string(contents), true
case okValue && okFile: // both
return "", false
default: // neither, ignore
return "", false
}
}

var c = &Config{}
if err := envconfig.Process("", c); err != nil {
return nil, fmt.Errorf("loadConfig: failed to process configuration values: %w", err)
}

return c, nil
}

func loadEnvVars() (*Config, error) {
return loadConfig(os.LookupEnv)
}

type configFile struct {
name string
config *Config
}

func loadEnvFiles(directory string) ([]configFile, error) {
items, err := os.ReadDir(directory)
if err != nil {
if os.IsNotExist(err) {
return nil, err
}
return nil, fmt.Errorf("loadEnvFiles: failed to read files from env directory: %w", err)
}

cs := []configFile{}
for _, item := range items {
if item.IsDir() {
continue
}
p := filepath.Join(directory, item.Name())
envFile, err := godotenv.Read(p)
if err != nil {
return nil, fmt.Errorf("loadEnvFiles: error reading config file %s: %w", p, err)
}
lookup := func(key string) (string, bool) {
val, ok := envFile[key]
return val, ok
}
c, err := loadConfig(lookup)
if err != nil {
return nil, fmt.Errorf("loadEnvFiles: error loading config from file %s: %w", p, err)
}
cs = append(cs, configFile{config: c, name: item.Name()})
}

return cs, nil
}
29 changes: 29 additions & 0 deletions cmd/backup/cron.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2024 - Offen Authors <[email protected]>
// SPDX-License-Identifier: MPL-2.0

package main

import (
"time"

"github.com/robfig/cron/v3"
)

// checkCronSchedule detects whether the given cron expression will actually
// ever be executed or not.
func checkCronSchedule(expression string) (ok bool) {
defer func() {
if err := recover(); err != nil {
ok = false
}
}()
sched, err := cron.ParseStandard(expression)
if err != nil {
ok = false
return
}
now := time.Now()
sched.Next(now) // panics when the cron would never run
ok = true
return
}
15 changes: 10 additions & 5 deletions cmd/backup/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,18 @@ func (s *script) withLabeledCommands(step lifecyclePhase, cb func() error) func(
if s.cli == nil {
return cb
}
return func() error {
if err := s.runLabeledCommands(fmt.Sprintf("docker-volume-backup.%s-pre", step)); err != nil {
return fmt.Errorf("withLabeledCommands: %s: error running pre commands: %w", step, err)
return func() (err error) {
if err = s.runLabeledCommands(fmt.Sprintf("docker-volume-backup.%s-pre", step)); err != nil {
err = fmt.Errorf("withLabeledCommands: %s: error running pre commands: %w", step, err)
return
}
defer func() {
s.must(s.runLabeledCommands(fmt.Sprintf("docker-volume-backup.%s-post", step)))
derr := s.runLabeledCommands(fmt.Sprintf("docker-volume-backup.%s-post", step))
if err == nil && derr != nil {
err = derr
}
}()
return cb()
err = cb()
return
}
}
Loading
Loading