core/config: refactor change dispatcher (#4657)

* core/config: refactor change dispatcher

* update test

* close listener go routine when context is canceled

* use cancel cause

* use context

* add more time

* more time
This commit is contained in:
Caleb Doxsey 2023-11-01 13:52:23 -06:00 committed by GitHub
parent 53573dc046
commit e0693e54f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 241 additions and 14 deletions

View file

@ -10,6 +10,7 @@ import (
"github.com/google/uuid"
"github.com/rs/zerolog"
"github.com/pomerium/pomerium/internal/events"
"github.com/pomerium/pomerium/internal/fileutil"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/metrics"
@ -19,27 +20,27 @@ import (
// A ChangeListener is called when configuration changes.
type ChangeListener = func(context.Context, *Config)
type changeDispatcherEvent struct {
cfg *Config
}
// A ChangeDispatcher manages listeners on config changes.
type ChangeDispatcher struct {
sync.Mutex
onConfigChangeListeners []ChangeListener
target events.Target[changeDispatcherEvent]
}
// Trigger triggers a change.
func (dispatcher *ChangeDispatcher) Trigger(ctx context.Context, cfg *Config) {
dispatcher.Lock()
defer dispatcher.Unlock()
for _, li := range dispatcher.onConfigChangeListeners {
li(ctx, cfg)
}
dispatcher.target.Dispatch(ctx, changeDispatcherEvent{
cfg: cfg,
})
}
// OnConfigChange adds a listener.
func (dispatcher *ChangeDispatcher) OnConfigChange(_ context.Context, li ChangeListener) {
dispatcher.Lock()
defer dispatcher.Unlock()
dispatcher.onConfigChangeListeners = append(dispatcher.onConfigChangeListeners, li)
dispatcher.target.AddListener(func(ctx context.Context, evt changeDispatcherEvent) {
li(ctx, evt.cfg)
})
}
// A Source gets configuration.

View file

@ -3,7 +3,9 @@ package config_test
import (
"context"
"errors"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -13,6 +15,8 @@ import (
)
func TestLayeredConfig(t *testing.T) {
t.Parallel()
ctx := context.Background()
t.Run("error on initial build", func(t *testing.T) {
@ -33,12 +37,15 @@ func TestLayeredConfig(t *testing.T) {
})
require.NoError(t, err)
var dst *config.Config
var dst atomic.Pointer[config.Config]
dst.Store(layered.GetConfig())
layered.OnConfigChange(ctx, func(ctx context.Context, c *config.Config) {
dst = c
dst.Store(c)
})
underlying.SetConfig(ctx, &config.Config{Options: &config.Options{DeriveInternalDomainCert: proto.String("b.com")}})
assert.Equal(t, "b.com", dst.Options.GetDeriveInternalDomain())
assert.Eventually(t, func() bool {
return dst.Load().Options.GetDeriveInternalDomain() == "b.com"
}, 10*time.Second, time.Millisecond)
})
}