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

Receive: Reload tenant limit configuration on file change #5673

Merged
merged 37 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fb52a9c
Create a PathOrContent reloader
douglascamata Sep 5, 2022
72fbfdb
Add docs to staticPathContent.Rewrite
douglascamata Sep 5, 2022
f1828c8
Run goimports
douglascamata Sep 5, 2022
caaf733
Properly cancel the context in the test
douglascamata Sep 7, 2022
103aab0
Watch parent directory of file
douglascamata Sep 7, 2022
a4981f6
Remove useless ctx.Done()
douglascamata Sep 7, 2022
b95bd4f
Add a debounce timer to config reload
douglascamata Sep 7, 2022
168818e
Fix event.Op bitmask check
douglascamata Sep 7, 2022
b2d9022
Update lastReload
douglascamata Sep 7, 2022
54d6314
Fix debouncer for path content reloader
douglascamata Sep 7, 2022
c21e084
Improve documentation of the PathContentRealoder
douglascamata Sep 7, 2022
ce4663f
Dain reload timer before resetting
douglascamata Sep 7, 2022
e072f02
Run tests in parallel
douglascamata Sep 7, 2022
066ae0a
Simplify debouncing logic
douglascamata Sep 7, 2022
bc78cc9
Add more tests to file reloader
douglascamata Sep 7, 2022
2711539
Simplify condition for triggering reload
douglascamata Sep 7, 2022
bf4c80b
Use absolute path to config file
douglascamata Sep 7, 2022
14b87ef
Get rid of parallel test
douglascamata Sep 7, 2022
a2600a3
Put back 2s wait between fs operations
douglascamata Sep 7, 2022
9813d35
Remove useless sleep
douglascamata Sep 7, 2022
8d86e7d
Stop reloadTimer when context cancelled
douglascamata Sep 7, 2022
030848c
Remove unused fucntion
douglascamata Sep 8, 2022
871b396
Add missing copyright to test file
douglascamata Sep 8, 2022
e0bf6a2
Merge branch 'main' of github.com:thanos-io/thanos into add-path-cont…
douglascamata Sep 8, 2022
80a53b1
Auto-reload tenant limit config on file changes
douglascamata Sep 8, 2022
5522234
Merge branch 'main' of github.com:thanos-io/thanos into add-path-cont…
douglascamata Sep 27, 2022
caf3bf3
Wrap error when reloading config
douglascamata Oct 7, 2022
62af70f
Move limiter config reloader and update logs
douglascamata Oct 7, 2022
fba7571
Get rid of useless types and allocations
douglascamata Oct 7, 2022
b3aaa1a
Merge branch 'main' of github.com:thanos-io/thanos into add-path-cont…
douglascamata Oct 7, 2022
e983a70
Remove errorChan from config reload starter
douglascamata Oct 10, 2022
b7632d7
Retrigger CI
douglascamata Oct 11, 2022
67ea81b
Merge branch 'main' of github.com:thanos-io/thanos into add-path-cont…
douglascamata Oct 11, 2022
fd98829
Use UnRegisterer in the Limiter
douglascamata Oct 11, 2022
ca56664
Better guard against nil registerer in the limiter
douglascamata Oct 11, 2022
315c0ea
Remove wrong nil guard
douglascamata Oct 11, 2022
cb21419
Retrigger CI
douglascamata Oct 11, 2022
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
94 changes: 94 additions & 0 deletions pkg/extkingpin/path_content_reloader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) The Thanos Authors.
// Licensed under the Apache License 2.0.

package extkingpin

import (
"context"
"fmt"
"github.com/go-kit/log/level"
douglascamata marked this conversation as resolved.
Show resolved Hide resolved
"os"

"github.com/fsnotify/fsnotify"
"github.com/go-kit/log"
"github.com/pkg/errors"
)

type fileContent interface {
Content() ([]byte, error)
Path() string
}

// PathContentReloader starts a file watcher that monitors the file indicated by fileContent.Path() and runs
// reloadFunc whenever a change is detected.
func PathContentReloader(ctx context.Context, fileContent fileContent, logger log.Logger, reloadFunc func()) error {
path := fileContent.Path()
watcher, err := fsnotify.NewWatcher()
if path == "" {
level.Debug(logger).Log("msg", "no path detected for config reload")
}
if err != nil {
return errors.Wrap(err, "creating file watcher")
}
go func() {
for {
select {
case <-ctx.Done():
return
case event := <-watcher.Events:
// fsnotify sometimes sends a bunch of events without name or operation.
// It's unclear what they are and why they are sent - filter them out.
if event.Name == "" {
break
}
// Everything but a CHMOD requires rereading.
// If the file was removed, we can't read it, so skip.
if event.Op^fsnotify.Chmod == 0 || event.Op^fsnotify.Remove == 0 {
break
}
level.Debug(logger).Log("msg", fmt.Sprintf("change detected for %s", path), "eventName", event.Name, "eventOp", event.Op)
reloadFunc()
case err := <-watcher.Errors:
level.Error(logger).Log("msg", "watcher error", "error", err)
}
}
}()
if err := watcher.Add(path); err != nil {
ctx.Done()
return errors.Wrapf(err, "adding path %s to file watcher", path)
}
return nil
}

type staticPathContent struct {
content []byte
path string
}

var _ fileContent = (*staticPathContent)(nil)

// Content returns the cached content.
func (t *staticPathContent) Content() ([]byte, error) {
return t.content, nil
}

// Path returns the path to the file that contains the content.
func (t *staticPathContent) Path() string {
return t.path
}

// NewStaticPathContent creates a new content that can be used to serve a static configuration. It copies the
// configuration from `fromPath` into `destPath` to avoid confusion with file watchers.
func NewStaticPathContent(fromPath string) (*staticPathContent, error) {
content, err := os.ReadFile(fromPath)
if err != nil {
return nil, errors.Wrapf(err, "could not load test content: %s", fromPath)
}
return &staticPathContent{content, fromPath}, nil
}

func (t *staticPathContent) Rewrite(newContent []byte) error {
t.content = newContent
// Write the file to ensure possible file watcher reloaders get triggered.
return os.WriteFile(t.path, newContent, 0666)
}
78 changes: 78 additions & 0 deletions pkg/extkingpin/path_content_reloader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package extkingpin

import (
"context"
"github.com/go-kit/log"
douglascamata marked this conversation as resolved.
Show resolved Hide resolved
"github.com/thanos-io/thanos/pkg/testutil"
"os"
"path"
"sync"
"testing"
)

func TestPathContentReloader(t *testing.T) {
type args struct {
runSteps func(t *testing.T, testFile string, pathContent *staticPathContent)
}
tests := []struct {
name string
args args
wantReloads int
}{
{
name: "Many operations, only rewrite triggers reload",
args: args{
runSteps: func(t *testing.T, testFile string, pathContent *staticPathContent) {
testutil.Ok(t, os.Chmod(testFile, 0777))
testutil.Ok(t, os.Remove(testFile))
testutil.Ok(t, pathContent.Rewrite([]byte("test modified")))
},
},
wantReloads: 1,
},
{
name: "Chmod doesn't trigger reload",
args: args{
runSteps: func(t *testing.T, testFile string, pathContent *staticPathContent) {
testutil.Ok(t, os.Chmod(testFile, 0777))
},
},
wantReloads: 0,
},
{
name: "Remove doesn't trigger reload",
args: args{
runSteps: func(t *testing.T, testFile string, pathContent *staticPathContent) {
testutil.Ok(t, os.Remove(testFile))
},
},
wantReloads: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testFile := path.Join(t.TempDir(), "test")
testutil.Ok(t, os.WriteFile(testFile, []byte("test"), 0666))
pathContent, err := NewStaticPathContent(testFile)
if err != nil {
t.Fatalf("error trying to save static limit config: %s", err)
}

wg := &sync.WaitGroup{}
wg.Add(tt.wantReloads)

ctx := context.Background()
defer ctx.Done()
reloadCount := 0
err = PathContentReloader(ctx, pathContent, log.NewLogfmtLogger(os.Stdout), func() {
reloadCount++
wg.Done()
})
testutil.Ok(t, err)

tt.args.runSteps(t, testFile, pathContent)
wg.Wait()
testutil.Equals(t, tt.wantReloads, reloadCount)
})
}
}