Skip to content

Commit c48d9e9

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

2 files changed

Lines changed: 43 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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,35 @@ func (s) TestNewFileWatcher(t *testing.T) {
118118
})
119119
}
120120
}
121+
122+
func (s) TestOnPolicyUpdate(t *testing.T) {
123+
updateCount := 0
124+
updates := make(chan int, 2)
125+
onPolicyUpdate := func() {
126+
updateCount++
127+
updates <- updateCount
128+
}
129+
130+
file := createTmpPolicyFile(t, "onpolicyupdate", []byte(`{"name": "foo1", "allow_rules":[{"name":"bar"}]}`))
131+
i, err := authz.NewFileWatcherWithCallback(file, time.Millisecond, onPolicyUpdate)
132+
if err != nil {
133+
t.Fatalf("NewFileWatcherWithCallback() returned err: %v", err)
134+
}
135+
defer i.Close()
136+
if <-updates != 1 {
137+
t.Fatalf("expected updateCount=1 after first call of NewFileWatcherWithCallback()")
138+
}
139+
140+
// Tweak the file, expect an update.
141+
if err := os.WriteFile(file, []byte(`{"name": "foo2", "allow_rules":[{"name":"bar"}]}`), os.ModePerm); err != nil {
142+
t.Fatalf("os.WriteFile(%q) failed: %v", file, err)
143+
}
144+
145+
if <-updates != 2 {
146+
t.Fatalf("expected updateCount=2 after second call of NewFileWatcherWithCallback()")
147+
}
148+
close(updates)
149+
if len(updates) != 0 {
150+
t.Fatalf("expected exactly 2 updates in channel")
151+
}
152+
}

0 commit comments

Comments
 (0)