Kubernetes client-goのIndexerInformerを使った実装で、以下のようにclientsetからRESTClientを取得してcacheを作成しようとしたところ、go testでfake clientを渡した際にclient内部でパニックが発生していた。
cachedLw := cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), resource, "", fields.Everything())
indexer, informer := cache.NewIndexerInformer(cachedLw, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
// on create...
},
UpdateFunc: func(old interface{}, new interface{}) {
// on update...
},
DeleteFunc: func(obj interface{}) {
// on delete...
},
}, cache.Indexers{})
↓
E0119 10:17:45.953340 57221 runtime.go:79] Observed a panic: "invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference)
fake clientではなく実際のkubernetesに接続すると問題なく動作していた。原因を調べてみると古いissueが見つかった。
どうもfake-clientのRESTClient()メソッドは動作する形では実装されていない模様。以下のような実装で、RESTClient()
は常にnilを返す。
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeCoreV1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}
go testでcacheは必要ないので、cache.ListerWatcher
をmockにする形で解決した。
import (
"context"
"testing"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
k "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
)
type podListWatcher struct {
clientset k.Interface
}
// ListFunc knows how to list resources
func (w *podListWatcher) List(options metav1.ListOptions) (runtime.Object, error) {
return w.clientset.CoreV1().Pods("").List(context.TODO(), options)
}
// WatchFunc knows how to watch resources
func (w *podListWatcher) Watch(options metav1.ListOptions) (watch.Interface, error) {
return w.clientset.CoreV1().Pods("").Watch(context.TODO(), options)
}
func Test(t *testing.T) {
clientset := fake.NewSimpleClientset()
// prepare fake client...
lw := &podListWatcher{clientset: clientset}
indexer, informer := cache.NewIndexerInformer(lw, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
// on create...
},
UpdateFunc: func(old interface{}, new interface{}) {
// on update...
},
DeleteFunc: func(obj interface{}) {
// on delete...
},
}, cache.Indexers{})
}