Skip to content

Commit

Permalink
fix: add test coverage for log rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
garethgeorge committed Feb 4, 2024
1 parent 2cba3d5 commit f1084ca
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 13 deletions.
2 changes: 1 addition & 1 deletion backrest.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func main() {
defer oplog.Close()

// Create rotating log storage
logStore := rotatinglog.NewRotatingLog(path.Join(config.DataDir(), "rotatinglogs"), 30)
logStore := rotatinglog.NewRotatingLog(path.Join(config.DataDir(), "rotatinglogs"), 30) // 30 days of logs
if err != nil {
zap.S().Fatalf("Error creating rotating log storage: %v", err)
}
Expand Down
3 changes: 3 additions & 0 deletions internal/hook/discordhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ func (h *Hook) doDiscord(cmd *v1.Hook_ActionDiscord, vars HookVars, output io.Wr

requestBytes, _ := json.Marshal(request)

fmt.Fprintf(output, "Sending Discord message to %s\n---- payload ----", cmd.ActionDiscord.GetWebhookUrl())
output.Write(requestBytes)

_, err = post(cmd.ActionDiscord.GetWebhookUrl(), "application/json", bytes.NewReader(requestBytes))
return err
}
4 changes: 3 additions & 1 deletion internal/hook/gotifyhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ func (h *Hook) doGotify(cmd *v1.Hook_ActionGotify, vars HookVars, output io.Writ
baseUrl,
url.QueryEscape(cmd.ActionGotify.GetToken()))

output.Write([]byte(fmt.Sprintf("Sending gotify message to %s\n", postUrl)))
fmt.Fprintf(output, "Sending gotify message to %s\n", postUrl)
fmt.Fprintf(output, "---- payload ----\n")
output.Write(b)

body, err := post(postUrl, "application/json", bytes.NewReader(b))

Expand Down
3 changes: 2 additions & 1 deletion internal/orchestrator/taskstats.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
)

var statBytesThreshold int64 = 10 * 1024 * 1024 * 1024 // 10 GB added.
var statOperationsThreshold int = 100 // run a stat command every 100 operations.

// StatsTask tracks a restic stats operation.
type StatsTask struct {
Expand Down Expand Up @@ -42,7 +43,7 @@ func (t *StatsTask) Name() string {

func (t *StatsTask) shouldRun() (bool, error) {
var bytesSinceLastStat int64 = -1
if err := t.orch.OpLog.ForEachByRepo(t.plan.Repo, indexutil.Reversed(indexutil.CollectAll()), func(op *v1.Operation) error {
if err := t.orch.OpLog.ForEachByRepo(t.plan.Repo, indexutil.Reversed(indexutil.CollectLastN(statOperationsThreshold)), func(op *v1.Operation) error {
if _, ok := op.Op.(*v1.Operation_OperationStats); ok {
return oplog.ErrStopIteration
} else if backup, ok := op.Op.(*v1.Operation_OperationBackup); ok && backup.OperationBackup.LastStatus != nil {
Expand Down
37 changes: 27 additions & 10 deletions internal/rotatinglog/rotatinglog.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,32 @@ type RotatingLog struct {
dir string
lastFile string
maxLogFiles int
now func() time.Time
}

func NewRotatingLog(dir string, maxLogFiles int) *RotatingLog {
return &RotatingLog{dir: dir, maxLogFiles: maxLogFiles}
}

func (r *RotatingLog) curfile() string {
return path.Join(r.dir, time.Now().Format("2006-01-02.tar.gz"))
t := time.Now()
if r.now != nil {
t = r.now() // for testing
}
return path.Join(r.dir, t.Format("2006-01-02-logs.tar"))
}

func (r *RotatingLog) removeExpiredFiles() error {
if r.maxLogFiles < 0 {
return nil
}
files, err := os.ReadDir(r.dir)
files, err := r.files()
if err != nil {
return err
return fmt.Errorf("list files: %w", err)
}
files = slices.DeleteFunc(files, func(f fs.DirEntry) bool {
return f.IsDir()
})
sort.Slice(files, func(i, j int) bool {
return files[i].Name() < files[j].Name()
})
if len(files) >= r.maxLogFiles {
for i := 0; i < len(files)-r.maxLogFiles+1; i++ {
if err := os.Remove(path.Join(r.dir, files[i].Name())); err != nil {
if err := os.Remove(path.Join(r.dir, files[i])); err != nil {
return err
}
}
Expand Down Expand Up @@ -167,6 +166,24 @@ func (r *RotatingLog) Read(name string) ([]byte, error) {
return nil, ErrNotFound
}

func (r *RotatingLog) files() ([]string, error) {
files, err := os.ReadDir(r.dir)
if err != nil {
return nil, err
}
files = slices.DeleteFunc(files, func(f fs.DirEntry) bool {
return f.IsDir() || !strings.HasSuffix(f.Name(), "-logs.tar")
})
sort.Slice(files, func(i, j int) bool {
return files[i].Name() < files[j].Name()
})
var result []string
for _, f := range files {
result = append(result, f.Name())
}
return result, nil
}

func compress(data []byte) ([]byte, error) {
var buf bytes.Buffer
zw := gzip.NewWriter(&buf)
Expand Down
41 changes: 41 additions & 0 deletions internal/rotatinglog/rotatinglog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package rotatinglog

import (
"fmt"
"slices"
"strings"
"testing"
"time"
)

func TestRotatingLog(t *testing.T) {
Expand All @@ -23,6 +25,7 @@ func TestRotatingLog(t *testing.T) {

func TestRotatingLogMultipleEntries(t *testing.T) {
log := NewRotatingLog(t.TempDir()+"/rotatinglog", 10)
refs := make([]string, 10)
for i := 0; i < 10; i++ {
name, err := log.Write([]byte(fmt.Sprintf("%d", i)))
if err != nil {
Expand All @@ -35,6 +38,17 @@ func TestRotatingLogMultipleEntries(t *testing.T) {
if fmt.Sprintf("%d", i) != string(data) {
t.Fatalf("Read failed: expected %d, got %s", i, string(data))
}
refs[i] = name
}

for i := 0; i < 10; i++ {
data, err := log.Read(refs[i])
if err != nil {
t.Fatalf("Read failed: %v", err)
}
if fmt.Sprintf("%d", i) != string(data) {
t.Fatalf("Read failed: expected %d, got %s", i, string(data))
}
}
}

Expand All @@ -56,6 +70,33 @@ func TestBigEntries(t *testing.T) {
}
}

func TestLogRotate(t *testing.T) {
curTime := time.Unix(0, 0)
curTime.Add(time.Hour * 24)

log := NewRotatingLog(t.TempDir()+"/rotatinglog", 3)
log.now = func() time.Time { return curTime }

for i := 0; i < 10; i++ {
_, err := log.Write([]byte(fmt.Sprintf("%d", i)))
if err != nil {
t.Fatalf("Write failed: %v", err)
}
curTime = curTime.Add(time.Hour * 24)
}

files, err := log.files()
if err != nil {
t.Fatalf("files failed: %v", err)
}
if len(files) != 3 {
t.Fatalf("files failed: expected 3, got %d", len(files))
}
if slices.Compare(files, []string{"1970-01-07-logs.tar", "1970-01-08-logs.tar", "1970-01-09-logs.tar"}) != 0 {
t.Fatalf("unexpected files in list: %v", files)
}
}

func genstr(size int) string {
return strings.Repeat("a", size)
}

0 comments on commit f1084ca

Please sign in to comment.