Add more tests and update config examples
This commit is contained in:
parent
97765853b4
commit
92209822db
4
Makefile
4
Makefile
@ -27,10 +27,8 @@ build: $(APP_NAME)
|
|||||||
# Run all tests
|
# Run all tests
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
go test -coverprofile=coverage.out
|
go test -coverprofile=coverage.out # -short
|
||||||
go tool cover -func=coverage.out
|
go tool cover -func=coverage.out
|
||||||
@go tool cover -func=coverage.out | awk -v target=80.0% \
|
|
||||||
'/^total:/ { print "Total coverage: " $3 " Minimum coverage: " target; if ($3+0.0 >= target+0.0) print "ok"; else { print "fail"; exit 1; } }'
|
|
||||||
|
|
||||||
# Installs pre-commit hooks
|
# Installs pre-commit hooks
|
||||||
.PHONY: install-hooks
|
.PHONY: install-hooks
|
||||||
|
41
config.hcl
41
config.hcl
@ -16,32 +16,38 @@ job "MyApp" {
|
|||||||
mysql "DumpMainDB" {
|
mysql "DumpMainDB" {
|
||||||
hostname = "foo"
|
hostname = "foo"
|
||||||
username = "bar"
|
username = "bar"
|
||||||
|
dump_to = "/data/main.sql"
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite "DumpSqlite" {
|
sqlite "DumpSqlite" {
|
||||||
path = "/db/path"
|
path = "/db/sqlite.db"
|
||||||
|
dump_to = "/data/sqlite.db.bak"
|
||||||
}
|
}
|
||||||
|
|
||||||
task "RunSomePreScripts" {
|
task "Create biz file" {
|
||||||
script {
|
|
||||||
on_backup = <<EOF
|
|
||||||
echo foo > /biz.txt
|
|
||||||
EOF
|
|
||||||
|
|
||||||
on_restore = "/foo/bar.sh"
|
pre_script {
|
||||||
}
|
|
||||||
|
|
||||||
script {
|
|
||||||
on_backup = <<EOF
|
on_backup = <<EOF
|
||||||
echo bar >> /biz.txt
|
echo bar >> /biz.txt
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post_script {
|
||||||
|
on_backup = <<EOF
|
||||||
|
rm /biz.txt
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "Run restore shell script" {
|
||||||
|
pre_script {
|
||||||
|
on_restore = "/foo/bar.sh"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task "ActuallyBackupSomeStuff" {
|
|
||||||
backup {
|
backup {
|
||||||
files =[
|
files =[
|
||||||
"/foo/bar",
|
"/data",
|
||||||
"/biz.txt",
|
"/biz.txt",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -51,15 +57,8 @@ job "MyApp" {
|
|||||||
|
|
||||||
restore_opts {
|
restore_opts {
|
||||||
Verify = true
|
Verify = true
|
||||||
}
|
# Since paths are absolute, restore to root
|
||||||
}
|
Target = "/"
|
||||||
}
|
|
||||||
|
|
||||||
task "RunSomePostScripts" {
|
|
||||||
script {
|
|
||||||
on_backup = <<EOF
|
|
||||||
rm /biz.txt
|
|
||||||
EOF
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
job.go
22
job.go
@ -10,12 +10,9 @@ import (
|
|||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const WorkDirPerms = 0770
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoJobsFound = errors.New("no jobs found and at least one job is required")
|
ErrNoJobsFound = errors.New("no jobs found and at least one job is required")
|
||||||
ErrMissingField = errors.New("missing config field")
|
ErrMissingField = errors.New("missing config field")
|
||||||
ErrMissingBlock = errors.New("missing config block")
|
|
||||||
ErrMutuallyExclusive = errors.New("mutually exclusive values not valid")
|
ErrMutuallyExclusive = errors.New("mutually exclusive values not valid")
|
||||||
ErrInvalidConfigValue = errors.New("invalid config value")
|
ErrInvalidConfigValue = errors.New("invalid config value")
|
||||||
|
|
||||||
@ -69,10 +66,6 @@ type Job struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (j Job) validateTasks() error {
|
func (j Job) validateTasks() error {
|
||||||
if len(j.Tasks) == 0 {
|
|
||||||
return fmt.Errorf("job %s is missing tasks: %w", j.Name, ErrMissingBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, task := range j.Tasks {
|
for _, task := range j.Tasks {
|
||||||
if err := task.Validate(); err != nil {
|
if err := task.Validate(); err != nil {
|
||||||
return fmt.Errorf("job %s has an invalid task: %w", j.Name, err)
|
return fmt.Errorf("job %s has an invalid task: %w", j.Name, err)
|
||||||
@ -88,7 +81,7 @@ func (j Job) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := cron.ParseStandard(j.Schedule); err != nil {
|
if _, err := cron.ParseStandard(j.Schedule); err != nil {
|
||||||
return fmt.Errorf("job %s has an invalid schedule: %w", j.Name, err)
|
return fmt.Errorf("job %s has an invalid schedule: %v: %w", j.Name, err, ErrInvalidConfigValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := j.Config.Validate(); err != nil {
|
if err := j.Config.Validate(); err != nil {
|
||||||
@ -111,6 +104,10 @@ func (j Job) Validate() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := j.Backup.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("job %s has an invalid backup config: %w", j.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,15 +146,8 @@ func (j Job) AllTasks() []ExecutableTask {
|
|||||||
return allTasks
|
return allTasks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j Job) JobDir() string {
|
|
||||||
cwd := filepath.Join(JobBaseDir, j.Name)
|
|
||||||
_ = os.MkdirAll(cwd, WorkDirPerms)
|
|
||||||
|
|
||||||
return cwd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j Job) BackupPaths() []string {
|
func (j Job) BackupPaths() []string {
|
||||||
files := j.Backup.Files
|
files := j.Backup.Paths
|
||||||
|
|
||||||
for _, t := range j.MySQL {
|
for _, t := range j.MySQL {
|
||||||
files = append(files, t.DumpToPath)
|
files = append(files, t.DumpToPath)
|
||||||
|
189
job_test.go
189
job_test.go
@ -7,6 +7,15 @@ import (
|
|||||||
main "git.iamthefij.com/iamthefij/restic-scheduler"
|
main "git.iamthefij.com/iamthefij/restic-scheduler"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ValidResticConfig() main.ResticConfig {
|
||||||
|
return main.ResticConfig{
|
||||||
|
Passphrase: "shh",
|
||||||
|
Repo: "./data",
|
||||||
|
Env: nil,
|
||||||
|
GlobalOpts: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResticConfigValidate(t *testing.T) {
|
func TestResticConfigValidate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -64,3 +73,183 @@ func TestResticConfigValidate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJobValidation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
job main.Job
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid job",
|
||||||
|
job: main.Job{
|
||||||
|
Name: "Valid job",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
},
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid name",
|
||||||
|
job: main.Job{
|
||||||
|
Name: "",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
},
|
||||||
|
expectedErr: main.ErrMissingField,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid schedule",
|
||||||
|
job: main.Job{
|
||||||
|
Name: "Test job",
|
||||||
|
Schedule: "shrug",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
},
|
||||||
|
expectedErr: main.ErrInvalidConfigValue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid config",
|
||||||
|
job: main.Job{
|
||||||
|
Name: "Test job",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: main.ResticConfig{}, // nolint:exhaustivestruct
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
},
|
||||||
|
expectedErr: main.ErrMutuallyExclusive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid task",
|
||||||
|
job: main.Job{
|
||||||
|
Name: "Test job",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{{}},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
},
|
||||||
|
expectedErr: main.ErrMissingField,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid mysql",
|
||||||
|
job: main.Job{
|
||||||
|
Name: "Test job",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{{}},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
},
|
||||||
|
expectedErr: main.ErrMissingField,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid sqlite",
|
||||||
|
job: main.Job{
|
||||||
|
Name: "Test job",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{{}},
|
||||||
|
},
|
||||||
|
expectedErr: main.ErrMissingField,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
testCase := c
|
||||||
|
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := testCase.job.Validate()
|
||||||
|
|
||||||
|
if !errors.Is(actual, testCase.expectedErr) {
|
||||||
|
t.Errorf("expected %v but found %v", testCase.expectedErr, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigValidation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
config main.Config
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid job",
|
||||||
|
config: main.Config{Jobs: []main.Job{{
|
||||||
|
Name: "Valid job",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
}}},
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "No jobs",
|
||||||
|
config: main.Config{Jobs: []main.Job{}},
|
||||||
|
expectedErr: main.ErrNoJobsFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid name",
|
||||||
|
config: main.Config{Jobs: []main.Job{{
|
||||||
|
Name: "",
|
||||||
|
Schedule: "@daily",
|
||||||
|
Config: ValidResticConfig(),
|
||||||
|
Tasks: []main.JobTask{},
|
||||||
|
Backup: main.BackupFilesTask{Paths: []string{"/test"}}, // nolint:exhaustivestruct
|
||||||
|
Forget: nil,
|
||||||
|
MySQL: []main.JobTaskMySQL{},
|
||||||
|
Sqlite: []main.JobTaskSqlite{},
|
||||||
|
}}},
|
||||||
|
expectedErr: main.ErrMissingField,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
testCase := c
|
||||||
|
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := testCase.config.Validate()
|
||||||
|
|
||||||
|
if !errors.Is(actual, testCase.expectedErr) {
|
||||||
|
t.Errorf("expected %v but found %v", testCase.expectedErr, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
24
main_test.go
Normal file
24
main_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MinCoverage = 0.5
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
testResult := m.Run()
|
||||||
|
|
||||||
|
if testResult == 0 && testing.CoverMode() != "" {
|
||||||
|
c := testing.Coverage()
|
||||||
|
if c < MinCoverage {
|
||||||
|
fmt.Printf("Tests passed but coverage failed at %0.2f and minimum to pass is %0.2f\n", c, MinCoverage)
|
||||||
|
|
||||||
|
testResult = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(testResult)
|
||||||
|
}
|
@ -189,6 +189,10 @@ func TestBuildEnv(t *testing.T) {
|
|||||||
func TestResticInterface(t *testing.T) {
|
func TestResticInterface(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skip integration test when running short tests")
|
||||||
|
}
|
||||||
|
|
||||||
dataDir := t.TempDir()
|
dataDir := t.TempDir()
|
||||||
repoDir := t.TempDir()
|
repoDir := t.TempDir()
|
||||||
cacheDir := t.TempDir()
|
cacheDir := t.TempDir()
|
||||||
|
149
shell_test.go
Normal file
149
shell_test.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package main_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
main "git.iamthefij.com/iamthefij/restic-scheduler"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* type TestCase interface {
|
||||||
|
* Run(*testing.T)
|
||||||
|
* Name() string
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* type TestCases []TestCase
|
||||||
|
*
|
||||||
|
* func (c TestCases) Run(t *testing.T) {
|
||||||
|
* t.Helper()
|
||||||
|
*
|
||||||
|
* for _, tc := range c {
|
||||||
|
* testCase := tc
|
||||||
|
*
|
||||||
|
* t.Parallel()
|
||||||
|
*
|
||||||
|
* t.Run(tc.Name(), tc.Run(t))
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestGetLogger(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
initialLogger := main.GetLogger("test")
|
||||||
|
|
||||||
|
t.Run("initial logger", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
AssertEqual(t, "incorrect logger prefix", "test:", initialLogger.Prefix())
|
||||||
|
})
|
||||||
|
|
||||||
|
dupeLogger := main.GetLogger("test")
|
||||||
|
|
||||||
|
t.Run("dupe logger", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
AssertEqual(t, "incorrect logger prefix", "test:", dupeLogger.Prefix())
|
||||||
|
|
||||||
|
if initialLogger != dupeLogger {
|
||||||
|
t.Error("expected reused instance")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
secondLogger := main.GetLogger("test2")
|
||||||
|
|
||||||
|
t.Run("dupe logger", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
AssertEqual(t, "incorrect logger prefix", "test2:", secondLogger.Prefix())
|
||||||
|
|
||||||
|
if initialLogger == secondLogger {
|
||||||
|
t.Error("expected new instance")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetChildLogger(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
parentLogger := main.GetLogger("parent")
|
||||||
|
childLogger := main.GetChildLogger(parentLogger, "child")
|
||||||
|
|
||||||
|
AssertEqual(t, "unexpected child logger prefix", "parent:child:", childLogger.Prefix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCapturedLogWriter(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
logger := log.New(&buffer, "test:", log.Lmsgprefix)
|
||||||
|
capturedLogWriter := main.NewCapturedLogWriter(logger)
|
||||||
|
|
||||||
|
if _, err := capturedLogWriter.Write([]byte("testing")); err != nil {
|
||||||
|
t.Fatalf("failed to write to captured log writter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
AssertEqual(t, "buffer contains incorrect values", "test: testing\n", buffer.String())
|
||||||
|
AssertEqual(t, "lines contains incorrect values", []string{"testing"}, capturedLogWriter.Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunShell(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
script string
|
||||||
|
cwd string
|
||||||
|
env map[string]string
|
||||||
|
expectedOutput string
|
||||||
|
expectedErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "successful script",
|
||||||
|
script: "echo $FOO",
|
||||||
|
cwd: ".",
|
||||||
|
env: map[string]string{
|
||||||
|
"FOO": "bar",
|
||||||
|
},
|
||||||
|
expectedOutput: "prefix: bar\nprefix: \n",
|
||||||
|
expectedErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failed script",
|
||||||
|
script: "echo $FOO\nexit 1",
|
||||||
|
cwd: ".",
|
||||||
|
env: map[string]string{
|
||||||
|
"FOO": "bar",
|
||||||
|
},
|
||||||
|
expectedOutput: "prefix: bar\nprefix: \n",
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
testCase := c
|
||||||
|
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
logger := log.New(&buffer, "prefix:", log.Lmsgprefix)
|
||||||
|
|
||||||
|
err := main.RunShell(
|
||||||
|
testCase.script,
|
||||||
|
testCase.cwd,
|
||||||
|
testCase.env,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
|
||||||
|
if testCase.expectedErr && err == nil {
|
||||||
|
t.Error("expected an error but didn't get one")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testCase.expectedErr && err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
AssertEqual(t, "unexpected output", testCase.expectedOutput, buffer.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
32
tasks.go
32
tasks.go
@ -83,17 +83,21 @@ func (t JobTaskMySQL) Paths() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t JobTaskMySQL) Validate() error {
|
func (t JobTaskMySQL) Validate() error {
|
||||||
|
if t.DumpToPath == "" {
|
||||||
|
return fmt.Errorf("task %s is missing dump_to path: %w", t.Name, ErrMissingField)
|
||||||
|
}
|
||||||
|
|
||||||
if s, err := os.Stat(t.DumpToPath); err != nil {
|
if s, err := os.Stat(t.DumpToPath); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return fmt.Errorf("Could not stat dump file path: %w", err)
|
return fmt.Errorf("task %s: invalid dump_to: could not stat path: %v: %w", t.Name, err, ErrInvalidConfigValue)
|
||||||
}
|
}
|
||||||
} else if s.Mode().IsDir() {
|
} else if s.Mode().IsDir() {
|
||||||
return fmt.Errorf("dump_to cannot be a directory: %w", ErrInvalidConfigValue)
|
return fmt.Errorf("task %s: dump_to cannot be a directory: %w", t.Name, ErrInvalidConfigValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t.Tables) > 0 && t.Database == "" {
|
if len(t.Tables) > 0 && t.Database == "" {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"mysql task %s is invalid. Must specify a database to use tables: %w",
|
"task %s is invalid. Must specify a database to use tables: %w",
|
||||||
t.Name,
|
t.Name,
|
||||||
ErrMissingField,
|
ErrMissingField,
|
||||||
)
|
)
|
||||||
@ -170,12 +174,16 @@ func (t JobTaskSqlite) Paths() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t JobTaskSqlite) Validate() error {
|
func (t JobTaskSqlite) Validate() error {
|
||||||
|
if t.DumpToPath == "" {
|
||||||
|
return fmt.Errorf("task %s is missing dump_to path: %w", t.Name, ErrMissingField)
|
||||||
|
}
|
||||||
|
|
||||||
if s, err := os.Stat(t.DumpToPath); err != nil {
|
if s, err := os.Stat(t.DumpToPath); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return fmt.Errorf("Could not stat dump file path: %w", err)
|
return fmt.Errorf("task %s: invalid dump_to: could not stat path: %v: %w", t.Name, err, ErrInvalidConfigValue)
|
||||||
}
|
}
|
||||||
} else if s.Mode().IsDir() {
|
} else if s.Mode().IsDir() {
|
||||||
return fmt.Errorf("dump_to cannot be a directory: %w", ErrInvalidConfigValue)
|
return fmt.Errorf("task %s: dump_to cannot be a directory: %w", t.Name, ErrInvalidConfigValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -202,7 +210,7 @@ func (t JobTaskSqlite) GetPostTask() ExecutableTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BackupFilesTask struct {
|
type BackupFilesTask struct {
|
||||||
Files []string `hcl:"files"`
|
Paths []string `hcl:"files"`
|
||||||
BackupOpts *BackupOpts `hcl:"backup_opts,block"`
|
BackupOpts *BackupOpts `hcl:"backup_opts,block"`
|
||||||
RestoreOpts *RestoreOpts `hcl:"restore_opts,block"`
|
RestoreOpts *RestoreOpts `hcl:"restore_opts,block"`
|
||||||
name string
|
name string
|
||||||
@ -247,6 +255,14 @@ func (t *BackupFilesTask) SetName(name string) {
|
|||||||
t.name = name
|
t.name = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *BackupFilesTask) Validate() error {
|
||||||
|
if len(t.Paths) == 0 {
|
||||||
|
return fmt.Errorf("backup config doesn't include any paths: %w", ErrInvalidConfigValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// JobTask represents a single task within a backup job.
|
// JobTask represents a single task within a backup job.
|
||||||
type JobTask struct {
|
type JobTask struct {
|
||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
@ -255,6 +271,10 @@ type JobTask struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t JobTask) Validate() error {
|
func (t JobTask) Validate() error {
|
||||||
|
if t.Name == "" {
|
||||||
|
return fmt.Errorf("task is missing a name: %w", ErrMissingField)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user