Commit 33f98d4d authored by Matthew Wong's avatar Matthew Wong

Switch pv controller to shared informers

parent 7d4383c6
...@@ -483,6 +483,9 @@ func StartControllers(controllers map[string]InitFunc, s *options.CMServer, root ...@@ -483,6 +483,9 @@ func StartControllers(controllers map[string]InitFunc, s *options.CMServer, root
VolumePlugins: ProbeControllerVolumePlugins(cloud, s.VolumeConfiguration), VolumePlugins: ProbeControllerVolumePlugins(cloud, s.VolumeConfiguration),
Cloud: cloud, Cloud: cloud,
ClusterName: s.ClusterName, ClusterName: s.ClusterName,
VolumeInformer: newSharedInformers.Core().V1().PersistentVolumes(),
ClaimInformer: newSharedInformers.Core().V1().PersistentVolumeClaims(),
ClassInformer: newSharedInformers.Storage().V1beta1().StorageClasses(),
EnableDynamicProvisioning: s.VolumeConfiguration.EnableDynamicProvisioning, EnableDynamicProvisioning: s.VolumeConfiguration.EnableDynamicProvisioning,
} }
volumeController := persistentvolumecontroller.NewController(params) volumeController := persistentvolumecontroller.NewController(params)
......
...@@ -23,6 +23,10 @@ go_library( ...@@ -23,6 +23,10 @@ go_library(
"//pkg/apis/storage/v1beta1:go_default_library", "//pkg/apis/storage/v1beta1:go_default_library",
"//pkg/apis/storage/v1beta1/util:go_default_library", "//pkg/apis/storage/v1beta1/util:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/informers/informers_generated/externalversions/core/v1:go_default_library",
"//pkg/client/informers/informers_generated/externalversions/storage/v1beta1:go_default_library",
"//pkg/client/listers/core/v1:go_default_library",
"//pkg/client/listers/storage/v1beta1:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/util/goroutinemap:go_default_library", "//pkg/util/goroutinemap:go_default_library",
...@@ -34,10 +38,9 @@ go_library( ...@@ -34,10 +38,9 @@ go_library(
"//vendor:k8s.io/apimachinery/pkg/api/meta", "//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels", "//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/types", "//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/wait", "//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/client-go/kubernetes/typed/core/v1", "//vendor:k8s.io/client-go/kubernetes/typed/core/v1",
"//vendor:k8s.io/client-go/pkg/api/v1", "//vendor:k8s.io/client-go/pkg/api/v1",
"//vendor:k8s.io/client-go/tools/cache", "//vendor:k8s.io/client-go/tools/cache",
...@@ -67,7 +70,9 @@ go_test( ...@@ -67,7 +70,9 @@ go_test(
"//pkg/apis/storage/v1beta1/util:go_default_library", "//pkg/apis/storage/v1beta1/util:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/clientset_generated/clientset/fake:go_default_library", "//pkg/client/clientset_generated/clientset/fake:go_default_library",
"//pkg/controller/volume/persistentvolume/testing:go_default_library", "//pkg/client/informers/informers_generated/externalversions:go_default_library",
"//pkg/client/listers/storage/v1beta1:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//vendor:github.com/golang/glog", "//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/api/resource",
...@@ -76,6 +81,7 @@ go_test( ...@@ -76,6 +81,7 @@ go_test(
"//vendor:k8s.io/apimachinery/pkg/types", "//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/diff", "//vendor:k8s.io/apimachinery/pkg/util/diff",
"//vendor:k8s.io/apimachinery/pkg/util/wait", "//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/client-go/testing", "//vendor:k8s.io/client-go/testing",
"//vendor:k8s.io/client-go/tools/cache", "//vendor:k8s.io/client-go/tools/cache",
"//vendor:k8s.io/client-go/tools/record", "//vendor:k8s.io/client-go/tools/record",
...@@ -94,7 +100,6 @@ filegroup( ...@@ -94,7 +100,6 @@ filegroup(
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//pkg/controller/volume/persistentvolume/options:all-srcs", "//pkg/controller/volume/persistentvolume/options:all-srcs",
"//pkg/controller/volume/persistentvolume/testing:all-srcs",
], ],
tags = ["automanaged"], tags = ["automanaged"],
) )
...@@ -422,7 +422,7 @@ func TestProvisionMultiSync(t *testing.T) { ...@@ -422,7 +422,7 @@ func TestProvisionMultiSync(t *testing.T) {
// When provisioning is disabled, provisioning a claim should instantly return nil // When provisioning is disabled, provisioning a claim should instantly return nil
func TestDisablingDynamicProvisioner(t *testing.T) { func TestDisablingDynamicProvisioner(t *testing.T) {
ctrl := newTestController(nil, nil, nil, nil, false) ctrl := newTestController(nil, nil, false)
retVal := ctrl.provisionClaim(nil) retVal := ctrl.provisionClaim(nil)
if retVal != nil { if retVal != nil {
t.Errorf("Expected nil return but got %v", retVal) t.Errorf("Expected nil return but got %v", retVal)
......
...@@ -31,6 +31,8 @@ import ( ...@@ -31,6 +31,8 @@ import (
storage "k8s.io/kubernetes/pkg/apis/storage/v1beta1" storage "k8s.io/kubernetes/pkg/apis/storage/v1beta1"
storageutil "k8s.io/kubernetes/pkg/apis/storage/v1beta1/util" storageutil "k8s.io/kubernetes/pkg/apis/storage/v1beta1/util"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1"
storagelisters "k8s.io/kubernetes/pkg/client/listers/storage/v1beta1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/util/goroutinemap" "k8s.io/kubernetes/pkg/util/goroutinemap"
vol "k8s.io/kubernetes/pkg/volume" vol "k8s.io/kubernetes/pkg/volume"
...@@ -146,14 +148,13 @@ const createProvisionedPVInterval = 10 * time.Second ...@@ -146,14 +148,13 @@ const createProvisionedPVInterval = 10 * time.Second
// cache.Controllers that watch PersistentVolume and PersistentVolumeClaim // cache.Controllers that watch PersistentVolume and PersistentVolumeClaim
// changes. // changes.
type PersistentVolumeController struct { type PersistentVolumeController struct {
volumeController cache.Controller volumeLister corelisters.PersistentVolumeLister
volumeInformer cache.Indexer volumeListerSynced cache.InformerSynced
volumeSource cache.ListerWatcher claimLister corelisters.PersistentVolumeClaimLister
claimController cache.Controller claimListerSynced cache.InformerSynced
claimInformer cache.Store classLister storagelisters.StorageClassLister
claimSource cache.ListerWatcher classListerSynced cache.InformerSynced
classReflector *cache.Reflector
classSource cache.ListerWatcher
kubeClient clientset.Interface kubeClient clientset.Interface
eventRecorder record.EventRecorder eventRecorder record.EventRecorder
cloud cloudprovider.Interface cloud cloudprovider.Interface
...@@ -182,7 +183,6 @@ type PersistentVolumeController struct { ...@@ -182,7 +183,6 @@ type PersistentVolumeController struct {
// have been already written. // have been already written.
volumes persistentVolumeOrderedIndex volumes persistentVolumeOrderedIndex
claims cache.Store claims cache.Store
classes cache.Store
// Work queues of claims and volumes to process. Every queue should have // Work queues of claims and volumes to process. Every queue should have
// exactly one worker thread, especially syncClaim() is not reentrant. // exactly one worker thread, especially syncClaim() is not reentrant.
...@@ -1464,17 +1464,10 @@ func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *v1.Persis ...@@ -1464,17 +1464,10 @@ func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *v1.Persis
// provisionClaim() which leads here is never called with claimClass=="", we // provisionClaim() which leads here is never called with claimClass=="", we
// can save some checks. // can save some checks.
claimClass := storageutil.GetClaimStorageClass(claim) claimClass := storageutil.GetClaimStorageClass(claim)
classObj, found, err := ctrl.classes.GetByKey(claimClass) class, err := ctrl.classLister.Get(claimClass)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if !found {
return nil, nil, fmt.Errorf("StorageClass %q not found", claimClass)
}
class, ok := classObj.(*storage.StorageClass)
if !ok {
return nil, nil, fmt.Errorf("Cannot convert object to StorageClass: %+v", classObj)
}
// Find a plugin for the class // Find a plugin for the class
plugin, err := ctrl.volumePluginMgr.FindProvisionablePluginByName(class.Provisioner) plugin, err := ctrl.volumePluginMgr.FindProvisionablePluginByName(class.Provisioner)
......
...@@ -21,10 +21,13 @@ import ( ...@@ -21,10 +21,13 @@ import (
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/apimachinery/pkg/watch"
core "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
fcache "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/testing" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions"
"k8s.io/kubernetes/pkg/controller"
) )
// Test the real controller methods (add/update/delete claim/volume) with // Test the real controller methods (add/update/delete claim/volume) with
...@@ -161,26 +164,38 @@ func TestControllerSync(t *testing.T) { ...@@ -161,26 +164,38 @@ func TestControllerSync(t *testing.T) {
// Initialize the controller // Initialize the controller
client := &fake.Clientset{} client := &fake.Clientset{}
volumeSource := fcache.NewFakePVControllerSource()
claimSource := fcache.NewFakePVCControllerSource() fakeVolumeWatch := watch.NewFake()
ctrl := newTestController(client, volumeSource, claimSource, nil, true) client.PrependWatchReactor("persistentvolumes", core.DefaultWatchReactor(fakeVolumeWatch, nil))
reactor := newVolumeReactor(client, ctrl, volumeSource, claimSource, test.errors) fakeClaimWatch := watch.NewFake()
client.PrependWatchReactor("persistentvolumeclaims", core.DefaultWatchReactor(fakeClaimWatch, nil))
client.PrependWatchReactor("storageclasses", core.DefaultWatchReactor(watch.NewFake(), nil))
informers := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
ctrl := newTestController(client, informers, true)
reactor := newVolumeReactor(client, ctrl, fakeVolumeWatch, fakeClaimWatch, test.errors)
for _, claim := range test.initialClaims { for _, claim := range test.initialClaims {
claimSource.Add(claim)
reactor.claims[claim.Name] = claim reactor.claims[claim.Name] = claim
go func(claim *v1.PersistentVolumeClaim) {
fakeClaimWatch.Add(claim)
}(claim)
} }
for _, volume := range test.initialVolumes { for _, volume := range test.initialVolumes {
volumeSource.Add(volume)
reactor.volumes[volume.Name] = volume reactor.volumes[volume.Name] = volume
go func(volume *v1.PersistentVolume) {
fakeVolumeWatch.Add(volume)
}(volume)
} }
// Start the controller // Start the controller
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go ctrl.Run(stopCh) go ctrl.Run(stopCh)
// Wait for the controller to pass initial sync and fill its caches. // Wait for the controller to pass initial sync and fill its caches.
for !ctrl.volumeController.HasSynced() || for !ctrl.volumeListerSynced() ||
!ctrl.claimController.HasSynced() || !ctrl.claimListerSynced() ||
len(ctrl.claims.ListKeys()) < len(test.initialClaims) || len(ctrl.claims.ListKeys()) < len(test.initialClaims) ||
len(ctrl.volumes.store.ListKeys()) < len(test.initialVolumes) { len(ctrl.volumes.store.ListKeys()) < len(test.initialVolumes) {
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["fake_controller_source_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/v1:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/watch",
],
)
go_library(
name = "go_default_library",
srcs = ["fake_controller_source.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/v1:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/watch",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package framework
import (
"errors"
"math/rand"
"strconv"
"sync"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
)
func NewFakeControllerSource() *FakeControllerSource {
return &FakeControllerSource{
Items: map[nnu]runtime.Object{},
Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull),
}
}
func NewFakePVControllerSource() *FakePVControllerSource {
return &FakePVControllerSource{
FakeControllerSource{
Items: map[nnu]runtime.Object{},
Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull),
}}
}
func NewFakePVCControllerSource() *FakePVCControllerSource {
return &FakePVCControllerSource{
FakeControllerSource{
Items: map[nnu]runtime.Object{},
Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull),
}}
}
// FakeControllerSource implements listing/watching for testing.
type FakeControllerSource struct {
lock sync.RWMutex
Items map[nnu]runtime.Object
changes []watch.Event // one change per resourceVersion
Broadcaster *watch.Broadcaster
}
type FakePVControllerSource struct {
FakeControllerSource
}
type FakePVCControllerSource struct {
FakeControllerSource
}
// namespace, name, uid to be used as a key.
type nnu struct {
namespace, name string
uid types.UID
}
// Add adds an object to the set and sends an add event to watchers.
// obj's ResourceVersion is set.
func (f *FakeControllerSource) Add(obj runtime.Object) {
f.Change(watch.Event{Type: watch.Added, Object: obj}, 1)
}
// Modify updates an object in the set and sends a modified event to watchers.
// obj's ResourceVersion is set.
func (f *FakeControllerSource) Modify(obj runtime.Object) {
f.Change(watch.Event{Type: watch.Modified, Object: obj}, 1)
}
// Delete deletes an object from the set and sends a delete event to watchers.
// obj's ResourceVersion is set.
func (f *FakeControllerSource) Delete(lastValue runtime.Object) {
f.Change(watch.Event{Type: watch.Deleted, Object: lastValue}, 1)
}
// AddDropWatch adds an object to the set but forgets to send an add event to
// watchers.
// obj's ResourceVersion is set.
func (f *FakeControllerSource) AddDropWatch(obj runtime.Object) {
f.Change(watch.Event{Type: watch.Added, Object: obj}, 0)
}
// ModifyDropWatch updates an object in the set but forgets to send a modify
// event to watchers.
// obj's ResourceVersion is set.
func (f *FakeControllerSource) ModifyDropWatch(obj runtime.Object) {
f.Change(watch.Event{Type: watch.Modified, Object: obj}, 0)
}
// DeleteDropWatch deletes an object from the set but forgets to send a delete
// event to watchers.
// obj's ResourceVersion is set.
func (f *FakeControllerSource) DeleteDropWatch(lastValue runtime.Object) {
f.Change(watch.Event{Type: watch.Deleted, Object: lastValue}, 0)
}
func (f *FakeControllerSource) key(accessor metav1.Object) nnu {
return nnu{accessor.GetNamespace(), accessor.GetName(), accessor.GetUID()}
}
// Change records the given event (setting the object's resource version) and
// sends a watch event with the specified probability.
func (f *FakeControllerSource) Change(e watch.Event, watchProbability float64) {
f.lock.Lock()
defer f.lock.Unlock()
accessor, err := meta.Accessor(e.Object)
if err != nil {
panic(err) // this is test code only
}
resourceVersion := len(f.changes) + 1
accessor.SetResourceVersion(strconv.Itoa(resourceVersion))
f.changes = append(f.changes, e)
key := f.key(accessor)
switch e.Type {
case watch.Added, watch.Modified:
f.Items[key] = e.Object
case watch.Deleted:
delete(f.Items, key)
}
if rand.Float64() < watchProbability {
f.Broadcaster.Action(e.Type, e.Object)
}
}
func (f *FakeControllerSource) getListItemsLocked() ([]runtime.Object, error) {
list := make([]runtime.Object, 0, len(f.Items))
for _, obj := range f.Items {
// Must make a copy to allow clients to modify the object.
// Otherwise, if they make a change and write it back, they
// will inadvertently change our canonical copy (in
// addition to racing with other clients).
objCopy, err := api.Scheme.DeepCopy(obj)
if err != nil {
return nil, err
}
list = append(list, objCopy.(runtime.Object))
}
return list, nil
}
// List returns a list object, with its resource version set.
func (f *FakeControllerSource) List(options metav1.ListOptions) (runtime.Object, error) {
f.lock.RLock()
defer f.lock.RUnlock()
list, err := f.getListItemsLocked()
if err != nil {
return nil, err
}
listObj := &api.List{}
if err := meta.SetList(listObj, list); err != nil {
return nil, err
}
objMeta, err := metav1.ListMetaFor(listObj)
if err != nil {
return nil, err
}
resourceVersion := len(f.changes)
objMeta.ResourceVersion = strconv.Itoa(resourceVersion)
return listObj, nil
}
// List returns a list object, with its resource version set.
func (f *FakePVControllerSource) List(options metav1.ListOptions) (runtime.Object, error) {
f.lock.RLock()
defer f.lock.RUnlock()
list, err := f.FakeControllerSource.getListItemsLocked()
if err != nil {
return nil, err
}
listObj := &v1.PersistentVolumeList{}
if err := meta.SetList(listObj, list); err != nil {
return nil, err
}
objMeta, err := metav1.ListMetaFor(listObj)
if err != nil {
return nil, err
}
resourceVersion := len(f.changes)
objMeta.ResourceVersion = strconv.Itoa(resourceVersion)
return listObj, nil
}
// List returns a list object, with its resource version set.
func (f *FakePVCControllerSource) List(options metav1.ListOptions) (runtime.Object, error) {
f.lock.RLock()
defer f.lock.RUnlock()
list, err := f.FakeControllerSource.getListItemsLocked()
if err != nil {
return nil, err
}
listObj := &v1.PersistentVolumeClaimList{}
if err := meta.SetList(listObj, list); err != nil {
return nil, err
}
objMeta, err := metav1.ListMetaFor(listObj)
if err != nil {
return nil, err
}
resourceVersion := len(f.changes)
objMeta.ResourceVersion = strconv.Itoa(resourceVersion)
return listObj, nil
}
// Watch returns a watch, which will be pre-populated with all changes
// after resourceVersion.
func (f *FakeControllerSource) Watch(options metav1.ListOptions) (watch.Interface, error) {
f.lock.RLock()
defer f.lock.RUnlock()
rc, err := strconv.Atoi(options.ResourceVersion)
if err != nil {
return nil, err
}
if rc < len(f.changes) {
changes := []watch.Event{}
for _, c := range f.changes[rc:] {
// Must make a copy to allow clients to modify the
// object. Otherwise, if they make a change and write
// it back, they will inadvertently change the our
// canonical copy (in addition to racing with other
// clients).
objCopy, err := api.Scheme.DeepCopy(c.Object)
if err != nil {
return nil, err
}
changes = append(changes, watch.Event{Type: c.Type, Object: objCopy.(runtime.Object)})
}
return f.Broadcaster.WatchWithPrefix(changes), nil
} else if rc > len(f.changes) {
return nil, errors.New("resource version in the future not supported by this fake")
}
return f.Broadcaster.Watch(), nil
}
// Shutdown closes the underlying broadcaster, waiting for events to be
// delivered. It's an error to call any method after calling shutdown. This is
// enforced by Shutdown() leaving f locked.
func (f *FakeControllerSource) Shutdown() {
f.lock.Lock() // Purposely no unlock.
f.Broadcaster.Shutdown()
}
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package framework
import (
"sync"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
)
// ensure the watch delivers the requested and only the requested items.
func consume(t *testing.T, w watch.Interface, rvs []string, done *sync.WaitGroup) {
defer done.Done()
for _, rv := range rvs {
got, ok := <-w.ResultChan()
if !ok {
t.Errorf("%#v: unexpected channel close, wanted %v", rvs, rv)
return
}
gotRV := got.Object.(*v1.Pod).ObjectMeta.ResourceVersion
if e, a := rv, gotRV; e != a {
t.Errorf("wanted %v, got %v", e, a)
} else {
t.Logf("Got %v as expected", gotRV)
}
}
// We should not get anything else.
got, open := <-w.ResultChan()
if open {
t.Errorf("%#v: unwanted object %#v", rvs, got)
}
}
func TestRCNumber(t *testing.T) {
pod := func(name string) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}
}
wg := &sync.WaitGroup{}
wg.Add(3)
source := NewFakeControllerSource()
source.Add(pod("foo"))
source.Modify(pod("foo"))
source.Modify(pod("foo"))
w, err := source.Watch(metav1.ListOptions{ResourceVersion: "1"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
go consume(t, w, []string{"2", "3"}, wg)
list, err := source.List(metav1.ListOptions{})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if e, a := "3", list.(*api.List).ResourceVersion; e != a {
t.Errorf("wanted %v, got %v", e, a)
}
w2, err := source.Watch(metav1.ListOptions{ResourceVersion: "2"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
go consume(t, w2, []string{"3"}, wg)
w3, err := source.Watch(metav1.ListOptions{ResourceVersion: "3"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
go consume(t, w3, []string{}, wg)
source.Shutdown()
wg.Wait()
}
...@@ -314,6 +314,7 @@ func ClusterRoles() []rbac.ClusterRole { ...@@ -314,6 +314,7 @@ func ClusterRoles() []rbac.ClusterRole {
rbac.NewRule("list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(), rbac.NewRule("list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
rbac.NewRule("list", "watch").Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), rbac.NewRule("list", "watch").Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
rbac.NewRule("list", "watch").Groups(certificatesGroup).Resources("certificatesigningrequests").RuleOrDie(), rbac.NewRule("list", "watch").Groups(certificatesGroup).Resources("certificatesigningrequests").RuleOrDie(),
rbac.NewRule("list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
}, },
}, },
{ {
......
...@@ -523,6 +523,13 @@ items: ...@@ -523,6 +523,13 @@ items:
verbs: verbs:
- list - list
- watch - watch
- apiGroups:
- storage.k8s.io
resources:
- storageclasses
verbs:
- list
- watch
- apiVersion: rbac.authorization.k8s.io/v1beta1 - apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole kind: ClusterRole
metadata: metadata:
......
...@@ -36,6 +36,7 @@ import ( ...@@ -36,6 +36,7 @@ import (
storage "k8s.io/kubernetes/pkg/apis/storage/v1beta1" storage "k8s.io/kubernetes/pkg/apis/storage/v1beta1"
storageutil "k8s.io/kubernetes/pkg/apis/storage/v1beta1/util" storageutil "k8s.io/kubernetes/pkg/apis/storage/v1beta1/util"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions"
fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake" fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
persistentvolumecontroller "k8s.io/kubernetes/pkg/controller/volume/persistentvolume" persistentvolumecontroller "k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
...@@ -110,7 +111,7 @@ func TestPersistentVolumeRecycler(t *testing.T) { ...@@ -110,7 +111,7 @@ func TestPersistentVolumeRecycler(t *testing.T) {
ns := framework.CreateTestingNamespace("pv-recycler", s, t) ns := framework.CreateTestingNamespace("pv-recycler", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, ctrl, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, ctrl, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -119,6 +120,7 @@ func TestPersistentVolumeRecycler(t *testing.T) { ...@@ -119,6 +120,7 @@ func TestPersistentVolumeRecycler(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go ctrl.Run(stopCh) go ctrl.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -164,7 +166,7 @@ func TestPersistentVolumeDeleter(t *testing.T) { ...@@ -164,7 +166,7 @@ func TestPersistentVolumeDeleter(t *testing.T) {
ns := framework.CreateTestingNamespace("pv-deleter", s, t) ns := framework.CreateTestingNamespace("pv-deleter", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, ctrl, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, ctrl, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -173,6 +175,7 @@ func TestPersistentVolumeDeleter(t *testing.T) { ...@@ -173,6 +175,7 @@ func TestPersistentVolumeDeleter(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go ctrl.Run(stopCh) go ctrl.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -223,7 +226,7 @@ func TestPersistentVolumeBindRace(t *testing.T) { ...@@ -223,7 +226,7 @@ func TestPersistentVolumeBindRace(t *testing.T) {
ns := framework.CreateTestingNamespace("pv-bind-race", s, t) ns := framework.CreateTestingNamespace("pv-bind-race", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, ctrl, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, ctrl, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -232,6 +235,7 @@ func TestPersistentVolumeBindRace(t *testing.T) { ...@@ -232,6 +235,7 @@ func TestPersistentVolumeBindRace(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go ctrl.Run(stopCh) go ctrl.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -294,7 +298,7 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) { ...@@ -294,7 +298,7 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) {
ns := framework.CreateTestingNamespace("pvc-label-selector", s, t) ns := framework.CreateTestingNamespace("pvc-label-selector", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, controller, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, controller, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -303,6 +307,7 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) { ...@@ -303,6 +307,7 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go controller.Run(stopCh) go controller.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -374,7 +379,7 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) { ...@@ -374,7 +379,7 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) {
ns := framework.CreateTestingNamespace("pvc-match-expressions", s, t) ns := framework.CreateTestingNamespace("pvc-match-expressions", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, controller, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, controller, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -383,6 +388,7 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) { ...@@ -383,6 +388,7 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go controller.Run(stopCh) go controller.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -473,7 +479,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) { ...@@ -473,7 +479,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) {
ns := framework.CreateTestingNamespace("multi-pvs", s, t) ns := framework.CreateTestingNamespace("multi-pvs", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, controller, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, controller, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -482,6 +488,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) { ...@@ -482,6 +488,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go controller.Run(stopCh) go controller.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -562,7 +569,7 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) { ...@@ -562,7 +569,7 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) {
ns := framework.CreateTestingNamespace("multi-pvs-pvcs", s, t) ns := framework.CreateTestingNamespace("multi-pvs-pvcs", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, binder, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, binder, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -571,6 +578,7 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) { ...@@ -571,6 +578,7 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
controllerStopCh := make(chan struct{}) controllerStopCh := make(chan struct{})
informers.Start(controllerStopCh)
go binder.Run(controllerStopCh) go binder.Run(controllerStopCh)
defer close(controllerStopCh) defer close(controllerStopCh)
...@@ -727,7 +735,7 @@ func TestPersistentVolumeControllerStartup(t *testing.T) { ...@@ -727,7 +735,7 @@ func TestPersistentVolumeControllerStartup(t *testing.T) {
const shortSyncPeriod = 2 * time.Second const shortSyncPeriod = 2 * time.Second
syncPeriod := getSyncPeriod(shortSyncPeriod) syncPeriod := getSyncPeriod(shortSyncPeriod)
testClient, binder, watchPV, watchPVC := createClients(ns, t, s, shortSyncPeriod) testClient, binder, informers, watchPV, watchPVC := createClients(ns, t, s, shortSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -784,6 +792,7 @@ func TestPersistentVolumeControllerStartup(t *testing.T) { ...@@ -784,6 +792,7 @@ func TestPersistentVolumeControllerStartup(t *testing.T) {
// Start the controller when all PVs and PVCs are already saved in etcd // Start the controller when all PVs and PVCs are already saved in etcd
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go binder.Run(stopCh) go binder.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -850,7 +859,7 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { ...@@ -850,7 +859,7 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) {
ns := framework.CreateTestingNamespace("provision-multi-pvs", s, t) ns := framework.CreateTestingNamespace("provision-multi-pvs", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, binder, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, binder, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -871,6 +880,7 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { ...@@ -871,6 +880,7 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) {
testClient.Storage().StorageClasses().Create(&storageClass) testClient.Storage().StorageClasses().Create(&storageClass)
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go binder.Run(stopCh) go binder.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -947,7 +957,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) { ...@@ -947,7 +957,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) {
ns := framework.CreateTestingNamespace("multi-pvs-diff-access", s, t) ns := framework.CreateTestingNamespace("multi-pvs-diff-access", s, t)
defer framework.DeleteTestingNamespace(ns, s, t) defer framework.DeleteTestingNamespace(ns, s, t)
testClient, controller, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod) testClient, controller, informers, watchPV, watchPVC := createClients(ns, t, s, defaultSyncPeriod)
defer watchPV.Stop() defer watchPV.Stop()
defer watchPVC.Stop() defer watchPVC.Stop()
...@@ -956,6 +966,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) { ...@@ -956,6 +966,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) {
defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{}) defer testClient.Core().PersistentVolumes().DeleteCollection(nil, metav1.ListOptions{})
stopCh := make(chan struct{}) stopCh := make(chan struct{})
informers.Start(stopCh)
go controller.Run(stopCh) go controller.Run(stopCh)
defer close(stopCh) defer close(stopCh)
...@@ -1088,7 +1099,7 @@ func waitForAnyPersistentVolumeClaimPhase(w watch.Interface, phase v1.Persistent ...@@ -1088,7 +1099,7 @@ func waitForAnyPersistentVolumeClaimPhase(w watch.Interface, phase v1.Persistent
} }
} }
func createClients(ns *v1.Namespace, t *testing.T, s *httptest.Server, syncPeriod time.Duration) (*clientset.Clientset, *persistentvolumecontroller.PersistentVolumeController, watch.Interface, watch.Interface) { func createClients(ns *v1.Namespace, t *testing.T, s *httptest.Server, syncPeriod time.Duration) (*clientset.Clientset, *persistentvolumecontroller.PersistentVolumeController, informers.SharedInformerFactory, watch.Interface, watch.Interface) {
// Use higher QPS and Burst, there is a test for race conditions which // Use higher QPS and Burst, there is a test for race conditions which
// creates many objects and default values were too low. // creates many objects and default values were too low.
binderClient := clientset.NewForConfigOrDie(&restclient.Config{ binderClient := clientset.NewForConfigOrDie(&restclient.Config{
...@@ -1119,12 +1130,16 @@ func createClients(ns *v1.Namespace, t *testing.T, s *httptest.Server, syncPerio ...@@ -1119,12 +1130,16 @@ func createClients(ns *v1.Namespace, t *testing.T, s *httptest.Server, syncPerio
} }
plugins := []volume.VolumePlugin{plugin} plugins := []volume.VolumePlugin{plugin}
cloud := &fakecloud.FakeCloud{} cloud := &fakecloud.FakeCloud{}
informers := informers.NewSharedInformerFactory(testClient, getSyncPeriod(syncPeriod))
ctrl := persistentvolumecontroller.NewController( ctrl := persistentvolumecontroller.NewController(
persistentvolumecontroller.ControllerParameters{ persistentvolumecontroller.ControllerParameters{
KubeClient: binderClient, KubeClient: binderClient,
SyncPeriod: getSyncPeriod(syncPeriod), SyncPeriod: getSyncPeriod(syncPeriod),
VolumePlugins: plugins, VolumePlugins: plugins,
Cloud: cloud, Cloud: cloud,
VolumeInformer: informers.Core().V1().PersistentVolumes(),
ClaimInformer: informers.Core().V1().PersistentVolumeClaims(),
ClassInformer: informers.Storage().V1beta1().StorageClasses(),
EnableDynamicProvisioning: true, EnableDynamicProvisioning: true,
}) })
...@@ -1137,7 +1152,7 @@ func createClients(ns *v1.Namespace, t *testing.T, s *httptest.Server, syncPerio ...@@ -1137,7 +1152,7 @@ func createClients(ns *v1.Namespace, t *testing.T, s *httptest.Server, syncPerio
t.Fatalf("Failed to watch PersistentVolumeClaims: %v", err) t.Fatalf("Failed to watch PersistentVolumeClaims: %v", err)
} }
return testClient, ctrl, watchPV, watchPVC return testClient, ctrl, informers, watchPV, watchPVC
} }
func createPV(name, path, cap string, mode []v1.PersistentVolumeAccessMode, reclaim v1.PersistentVolumeReclaimPolicy) *v1.PersistentVolume { func createPV(name, path, cap string, mode []v1.PersistentVolumeAccessMode, reclaim v1.PersistentVolumeReclaimPolicy) *v1.PersistentVolume {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment