Skip to content

Commit 183e653

Browse files
committed
authz: Add onPolicyUpdate callback to file watcher.
Signed-off-by: Keith Collister <kcollister@google.com>
1 parent 18a8d3d commit 183e653

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

authz/grpc_authz_server_interceptors.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,19 +97,28 @@ type FileWatcherInterceptor struct {
9797
policyContents []byte
9898
refreshDuration time.Duration
9999
cancel context.CancelFunc
100+
onPolicyUpdate func()
100101
}
101102

102103
// NewFileWatcher returns a new FileWatcherInterceptor from a policy file
103104
// that contains JSON string of authorization policy and a refresh duration to
104105
// specify the amount of time between policy refreshes.
105106
func NewFileWatcher(file string, duration time.Duration) (*FileWatcherInterceptor, error) {
107+
return NewFileWatcherWithCallback(file, duration, func() {})
108+
}
109+
110+
// NewFileWatcherWithCallback returns a new FileWatcherInterceptor from a policy file
111+
// that contains JSON string of authorization policy and a refresh duration to
112+
// specify the amount of time between policy refreshes. The callback is invoked when
113+
// a policy is loaded.
114+
func NewFileWatcherWithCallback(file string, duration time.Duration, onPolicyUpdate func()) (*FileWatcherInterceptor, error) {
106115
if file == "" {
107116
return nil, fmt.Errorf("authorization policy file path is empty")
108117
}
109118
if duration <= time.Duration(0) {
110119
return nil, fmt.Errorf("requires refresh interval(%v) greater than 0s", duration)
111120
}
112-
i := &FileWatcherInterceptor{policyFile: file, refreshDuration: duration}
121+
i := &FileWatcherInterceptor{policyFile: file, refreshDuration: duration, onPolicyUpdate: onPolicyUpdate}
113122
if err := i.updateInternalInterceptor(); err != nil {
114123
return nil, err
115124
}
@@ -155,6 +164,7 @@ func (i *FileWatcherInterceptor) updateInternalInterceptor() error {
155164
}
156165
atomic.StorePointer(&i.internalInterceptor, unsafe.Pointer(interceptor))
157166
logger.Infof("authorization policy reload status: successfully loaded new policy %v", policyContentsString)
167+
i.onPolicyUpdate()
158168
return nil
159169
}
160170

authz/grpc_authz_server_interceptors_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,34 @@ func (s) TestNewFileWatcher(t *testing.T) {
118118
})
119119
}
120120
}
121+
122+
func (s) TestOnPolicyUpdate(t *testing.T) {
123+
updateCount := 0
124+
onPolicyUpdate := func() {
125+
updateCount++
126+
}
127+
128+
file := createTmpPolicyFile(t, "onpolicyupdate", []byte(`{"name": "foo1", "allow_rules":[{"name":"bar"}]}`))
129+
i, err := authz.NewFileWatcherWithCallback(file, time.Millisecond, onPolicyUpdate)
130+
if err != nil {
131+
t.Fatalf("NewFileWatcherWithCallback() returned err: %v", err)
132+
}
133+
defer i.Close()
134+
if updateCount != 1 {
135+
t.Fatalf("expected updateCount=1 after first call of NewFileWatcherWithCallback()")
136+
}
137+
138+
// Tweak the file, expect an update.
139+
if err := os.WriteFile(file, []byte(`{"name": "foo2", "allow_rules":[{"name":"bar"}]}`), os.ModePerm); err != nil {
140+
t.Fatalf("os.WriteFile(%q) failed: %v", file, err)
141+
}
142+
143+
// Wait a little until we see the update.
144+
for range 100 {
145+
time.Sleep(time.Millisecond)
146+
if updateCount == 2 {
147+
return
148+
}
149+
}
150+
t.Fatalf("expected updateCount=2 after second call of NewFileWatcherWithCallback()")
151+
}

0 commit comments

Comments
 (0)