Skip to content

Commit 508da9e

Browse files
Merge pull request #85 from martinkunc/config-observer-test
Add test to observe config changes
2 parents 0b0a7a2 + 73c2bbf commit 508da9e

File tree

2 files changed

+109
-35
lines changed

2 files changed

+109
-35
lines changed

pkg/config/configobserver/configobserver.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ type Controller struct {
3030
tokenConfig *config.Controller
3131
secretConfig *config.Controller
3232
config *config.Controller
33+
checkPeriod time.Duration
3334
listeners []chan struct{}
3435
}
3536

3637
func New(defaultConfig config.Controller, kubeClient kubernetes.Interface) *Controller {
3738
c := &Controller{
3839
kubeClient: kubeClient,
3940
defaultConfig: defaultConfig,
41+
checkPeriod: 5 * time.Minute,
4042
}
4143
c.mergeConfigLocked()
4244
if err := c.retrieveToken(); err != nil {
@@ -48,6 +50,7 @@ func New(defaultConfig config.Controller, kubeClient kubernetes.Interface) *Cont
4850
return c
4951
}
5052

53+
// Start is periodically invoking check and set of config and token
5154
func (c *Controller) Start(ctx context.Context) {
5255
wait.Until(func() {
5356
if err := c.retrieveToken(); err != nil {
@@ -56,7 +59,7 @@ func (c *Controller) Start(ctx context.Context) {
5659
if err := c.retrieveConfig(); err != nil {
5760
klog.Warningf("Unable to retrieve config: %v", err)
5861
}
59-
}, 5*time.Minute, ctx.Done())
62+
}, c.checkPeriod, ctx.Done())
6063
}
6164

6265
func (c *Controller) retrieveToken() error {

pkg/config/configobserver/configobserver_test.go

+105-34
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package configobserver
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
6-
"io/ioutil"
7-
"os"
87
"reflect"
8+
"sync"
99
"testing"
1010
"time"
1111

@@ -66,22 +66,9 @@ func TestChangeSupportConfig(t *testing.T) {
6666

6767
ctrl := config.Controller{}
6868
kube := kubeClientResponder{}
69-
secs := tt.config
7069
// setup mock responses for secretes by secret name
71-
kube.CoreV1().(*corefake.FakeCoreV1).Fake.AddReactor("get", "secrets",
72-
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
73-
actionName := ""
74-
if getAction, ok := action.(clienttesting.GetAction); ok {
75-
actionName = getAction.GetName()
76-
}
77-
78-
key := fmt.Sprintf("(%s) %s.%s", action.GetResource(), action.GetNamespace(), actionName)
79-
sv, ok := secs[key]
80-
if !ok {
81-
return false, nil, nil
82-
}
83-
return true, sv, nil
84-
})
70+
provideSecretMock(&kube, tt.config)
71+
8572
// imitates New function
8673
c := &Controller{
8774
kubeClient: &kube,
@@ -111,31 +98,115 @@ func TestChangeSupportConfig(t *testing.T) {
11198

11299
}
113100

101+
func TestChangeObserved(t *testing.T) {
102+
setIntervals := map[int]time.Duration{
103+
0: time.Duration(10 * time.Minute),
104+
1: time.Duration(1 * time.Minute),
105+
2: time.Duration(3 * time.Minute),
106+
3: time.Duration(4 * time.Minute),
107+
}
108+
109+
klog.SetOutput(utils.NewTestLog(t).Writer())
110+
111+
ctrl := config.Controller{}
112+
kube := kubeClientResponder{}
113+
// The initial values set in configobserver.New
114+
secs := map[string]*corev1.Secret{
115+
pullSecretKey: &corev1.Secret{Data: map[string][]byte{
116+
".dockerconfigjson": fakeDockerConfig(),
117+
}},
118+
supportKey: &corev1.Secret{Data: map[string][]byte{
119+
"username": []byte("someone"),
120+
"password": []byte("secret"),
121+
"endpoint": []byte("http://po.rt"),
122+
intervalKey: []byte("10m"),
123+
}},
124+
}
125+
126+
provideSecretMock(&kube, secs)
127+
// New reads first k8 configuration
128+
co := New(ctrl, &kube)
129+
// set some initial config because we are tracking changes only
130+
co.setConfigLocked(&config.Controller{})
131+
132+
// observe changes every 50 ms
133+
co.checkPeriod = 50 * time.Millisecond
134+
ctx, cancel := context.WithCancel(context.Background())
135+
defer cancel()
136+
137+
// Watch for changes in configurations
138+
done := make(chan bool)
139+
go co.Start(ctx)
140+
changedC, _ := co.ConfigChanged()
141+
142+
// Sets gather intervals in config to 3 and 4 minutes after 100ms elapses
143+
go func() {
144+
145+
for i := range setIntervals {
146+
time.Sleep(100 * time.Millisecond)
147+
secs[supportKey].Data[intervalKey] = []byte(setIntervals[i].String())
148+
}
149+
// Give observer chance to catch the change
150+
time.Sleep(50 * time.Millisecond)
151+
done <- true
152+
}()
153+
154+
actualIntervals := map[int]time.Duration{}
155+
actIntMu := sync.Mutex{}
156+
go func() {
157+
for {
158+
select {
159+
case <-ctx.Done():
160+
done <- true
161+
162+
case <-changedC:
163+
actInt := co.Config().Interval
164+
165+
actIntMu.Lock()
166+
actIntMu.Unlock()
167+
actualIntervals[len(actualIntervals)] = actInt
168+
}
169+
}
170+
}()
171+
<-done
172+
173+
if !reflect.DeepEqual(setIntervals, actualIntervals) {
174+
t.Fatalf("the expected intervals didn't match actual intervals. \nExpected %v \nActual %v", setIntervals, actualIntervals)
175+
}
176+
}
177+
114178
const (
115179
pullSecretKey = "(/v1, Resource=secrets) openshift-config.pull-secret"
116180
supportKey = "(/v1, Resource=secrets) openshift-config.support"
117181
intervalKey = "interval"
118182
)
119183

120-
func mustMarshal(v interface{}) []byte {
121-
bt, err := json.Marshal(v)
122-
if err != nil {
123-
panic(err)
124-
}
125-
return bt
184+
func fakeDockerConfig() []byte {
185+
d, _ := json.Marshal(
186+
serializedAuthMap{
187+
Auths: map[string]serializedAuth{
188+
"cloud.openshift.com": serializedAuth{Auth: ".."},
189+
},
190+
})
191+
return d
126192
}
127193

128-
func mustLoad(filename string) []byte {
129-
f, err := os.Open(filename)
130-
if err != nil {
131-
panic(fmt.Errorf("test failed to load data: %v", err))
132-
}
133-
defer f.Close()
134-
bts, err := ioutil.ReadAll(f)
135-
if err != nil {
136-
panic(fmt.Errorf("test failed to read data: %v", err))
137-
}
138-
return bts
194+
func provideSecretMock(kube kubernetes.Interface, secs map[string]*corev1.Secret) {
195+
kube.CoreV1().(*corefake.FakeCoreV1).Fake.AddReactor("get", "secrets",
196+
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
197+
actionName := ""
198+
if getAction, ok := action.(clienttesting.GetAction); ok {
199+
actionName = getAction.GetName()
200+
}
201+
202+
key := fmt.Sprintf("(%s) %s.%s", action.GetResource(), action.GetNamespace(), actionName)
203+
sv, ok := secs[key]
204+
205+
if !ok {
206+
return false, nil, nil
207+
}
208+
return true, sv, nil
209+
})
139210
}
140211

141212
type kubeClientResponder struct {

0 commit comments

Comments
 (0)