mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	Merge pull request #40362 from deads2k/client-14-move-pkgs
Automatic merge from submit-queue move client/cache and client/discovery to client-go mechanical changes to move those packages. Had to create a `k8s.io/kubernetes/pkg/client/tests` package for tests that were blacklisted from client-go. We can rewrite these tests later and move them, but for now they'll still run at least. @caesarxuchao @sttts
This commit is contained in:
		| @@ -21,7 +21,6 @@ go_test( | |||||||
|         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library", |         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library", | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/listers/core/v1:go_default_library", |         "//pkg/client/listers/core/v1:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
| @@ -29,6 +28,7 @@ go_test( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/sets", |         "//vendor:k8s.io/apimachinery/pkg/util/sets", | ||||||
|         "//vendor:k8s.io/apiserver/pkg/authentication/user", |         "//vendor:k8s.io/apiserver/pkg/authentication/user", | ||||||
|         "//vendor:k8s.io/apiserver/pkg/endpoints/request", |         "//vendor:k8s.io/apiserver/pkg/endpoints/request", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -51,7 +51,6 @@ go_library( | |||||||
|         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library", |         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/registry/apiservice/etcd:go_default_library", |         "//cmd/kube-aggregator/pkg/registry/apiservice/etcd:go_default_library", | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/informers/informers_generated:go_default_library", |         "//pkg/client/informers/informers_generated:go_default_library", | ||||||
|         "//pkg/client/listers/core/v1:go_default_library", |         "//pkg/client/listers/core/v1:go_default_library", | ||||||
| @@ -75,6 +74,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |         "//vendor:k8s.io/apimachinery/pkg/util/wait", | ||||||
|         "//vendor:k8s.io/apiserver/pkg/endpoints/request", |         "//vendor:k8s.io/apiserver/pkg/endpoints/request", | ||||||
|         "//vendor:k8s.io/client-go/rest", |         "//vendor:k8s.io/client-go/rest", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/transport", |         "//vendor:k8s.io/client-go/transport", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ import ( | |||||||
| 	apierrors "k8s.io/apimachinery/pkg/api/errors" | 	apierrors "k8s.io/apimachinery/pkg/api/errors" | ||||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
| 	"k8s.io/kubernetes/pkg/util/workqueue" | 	"k8s.io/kubernetes/pkg/util/workqueue" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,9 +26,9 @@ import ( | |||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/util/diff" | 	"k8s.io/apimachinery/pkg/util/diff" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	corev1 "k8s.io/kubernetes/pkg/api/v1" | 	corev1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	v1listers "k8s.io/kubernetes/pkg/client/listers/core/v1" | 	v1listers "k8s.io/kubernetes/pkg/client/listers/core/v1" | ||||||
|  |  | ||||||
| 	"k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | 	"k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | ||||||
|   | |||||||
| @@ -21,9 +21,9 @@ go_library( | |||||||
|         "//cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset:go_default_library", |         "//cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/client/informers/apiregistration:go_default_library", |         "//cmd/kube-aggregator/pkg/client/informers/apiregistration:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/client/informers/internalinterfaces:go_default_library", |         "//cmd/kube-aggregator/pkg/client/informers/internalinterfaces:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", |         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,10 +19,10 @@ go_library( | |||||||
|         "//cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset:go_default_library", |         "//cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/client/informers/internalinterfaces:go_default_library", |         "//cmd/kube-aggregator/pkg/client/informers/internalinterfaces:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library", |         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,11 +22,11 @@ import ( | |||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | ||||||
| 	internalclientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset" | 	internalclientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/internalinterfaces" | ||||||
| 	internalversion "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion" | 	internalversion "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/listers/apiregistration/internalversion" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	time "time" | 	time "time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,10 +19,10 @@ go_library( | |||||||
|         "//cmd/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library", |         "//cmd/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/client/informers/internalinterfaces:go_default_library", |         "//cmd/kube-aggregator/pkg/client/informers/internalinterfaces:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/v1alpha1:go_default_library", |         "//cmd/kube-aggregator/pkg/client/listers/apiregistration/v1alpha1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,11 +22,11 @@ import ( | |||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	apiregistration_v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1" | 	apiregistration_v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1" | ||||||
| 	clientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/clientset" | 	clientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/clientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/internalinterfaces" | ||||||
| 	v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/listers/apiregistration/v1alpha1" | 	v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/listers/apiregistration/v1alpha1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	time "time" | 	time "time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,11 +21,11 @@ package informers | |||||||
| import ( | import ( | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	schema "k8s.io/apimachinery/pkg/runtime/schema" | 	schema "k8s.io/apimachinery/pkg/runtime/schema" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	clientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/clientset" | 	clientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/clientset" | ||||||
| 	internalclientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset" | 	internalclientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset" | ||||||
| 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/apiregistration" | 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/apiregistration" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/informers/internalinterfaces" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	reflect "reflect" | 	reflect "reflect" | ||||||
| 	sync "sync" | 	sync "sync" | ||||||
| 	time "time" | 	time "time" | ||||||
|   | |||||||
| @@ -21,9 +21,9 @@ package informers | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	schema "k8s.io/apimachinery/pkg/runtime/schema" | 	schema "k8s.io/apimachinery/pkg/runtime/schema" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | ||||||
| 	v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1" | 	v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // GenericInformer is type of SharedIndexInformer which will locate and delegate to other | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other | ||||||
|   | |||||||
| @@ -14,8 +14,8 @@ go_library( | |||||||
|     deps = [ |     deps = [ | ||||||
|         "//cmd/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library", |         "//cmd/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset:go_default_library", |         "//cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,9 +20,9 @@ package internalinterfaces | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	clientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/clientset" | 	clientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/clientset" | ||||||
| 	internalclientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset" | 	internalclientset "k8s.io/kubernetes/cmd/kube-aggregator/pkg/client/clientset_generated/internalclientset" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	time "time" | 	time "time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,10 +16,10 @@ go_library( | |||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//cmd/kube-aggregator/pkg/apis/apiregistration:go_default_library", |         "//cmd/kube-aggregator/pkg/apis/apiregistration:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/api/errors", |         "//vendor:k8s.io/apimachinery/pkg/api/errors", | ||||||
|         "//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/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/labels" | 	"k8s.io/apimachinery/pkg/labels" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // APIServiceLister helps list APIServices. | // APIServiceLister helps list APIServices. | ||||||
|   | |||||||
| @@ -17,10 +17,10 @@ go_library( | |||||||
|     deps = [ |     deps = [ | ||||||
|         "//cmd/kube-aggregator/pkg/apis/apiregistration:go_default_library", |         "//cmd/kube-aggregator/pkg/apis/apiregistration:go_default_library", | ||||||
|         "//cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1:go_default_library", |         "//cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/api/errors", |         "//vendor:k8s.io/apimachinery/pkg/api/errors", | ||||||
|         "//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/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,9 +22,9 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/labels" | 	"k8s.io/apimachinery/pkg/labels" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | 	apiregistration "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration" | ||||||
| 	v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1" | 	v1alpha1 "k8s.io/kubernetes/cmd/kube-aggregator/pkg/apis/apiregistration/v1alpha1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // APIServiceLister helps list APIServices. | // APIServiceLister helps list APIServices. | ||||||
|   | |||||||
| @@ -17,9 +17,9 @@ go_test( | |||||||
|     library = ":go_default_library", |     library = ":go_default_library", | ||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|  |         "//pkg/apis/componentconfig:go_default_library", | ||||||
|         "//pkg/kubelet:go_default_library", |         "//pkg/kubelet:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/diff", |         "//vendor:k8s.io/apimachinery/pkg/util/diff", | ||||||
|         "//vendor:k8s.io/apiserver/pkg/util/flag", |  | ||||||
|         "//vendor:k8s.io/client-go/rest", |         "//vendor:k8s.io/client-go/rest", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ package app | |||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	utilflag "k8s.io/apiserver/pkg/util/flag" | 	"k8s.io/kubernetes/pkg/apis/componentconfig" | ||||||
| 	"k8s.io/kubernetes/pkg/kubelet" | 	"k8s.io/kubernetes/pkg/kubelet" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -51,8 +51,8 @@ func TestValueOfAllocatableResources(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, test := range testCases { | 	for _, test := range testCases { | ||||||
| 		kubeReservedCM := make(utilflag.ConfigurationMap) | 		kubeReservedCM := make(componentconfig.ConfigurationMap) | ||||||
| 		systemReservedCM := make(utilflag.ConfigurationMap) | 		systemReservedCM := make(componentconfig.ConfigurationMap) | ||||||
|  |  | ||||||
| 		kubeReservedCM.Set(test.kubeReserved) | 		kubeReservedCM.Set(test.kubeReserved) | ||||||
| 		systemReservedCM.Set(test.systemReserved) | 		systemReservedCM.Set(test.systemReserved) | ||||||
|   | |||||||
| @@ -20,14 +20,14 @@ import "k8s.io/gengo/types" | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	apiScheme                   = types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "Scheme"} | 	apiScheme                   = types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "Scheme"} | ||||||
| 	cacheGenericLister          = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "GenericLister"} | 	cacheGenericLister          = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "GenericLister"} | ||||||
| 	cacheIndexers               = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "Indexers"} | 	cacheIndexers               = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "Indexers"} | ||||||
| 	cacheListWatch              = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "ListWatch"} | 	cacheListWatch              = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "ListWatch"} | ||||||
| 	cacheMetaNamespaceIndexFunc = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "MetaNamespaceIndexFunc"} | 	cacheMetaNamespaceIndexFunc = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "MetaNamespaceIndexFunc"} | ||||||
| 	cacheNamespaceIndex         = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "NamespaceIndex"} | 	cacheNamespaceIndex         = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "NamespaceIndex"} | ||||||
| 	cacheNewGenericLister       = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "NewGenericLister"} | 	cacheNewGenericLister       = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "NewGenericLister"} | ||||||
| 	cacheNewSharedIndexInformer = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "NewSharedIndexInformer"} | 	cacheNewSharedIndexInformer = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "NewSharedIndexInformer"} | ||||||
| 	cacheSharedIndexInformer    = types.Name{Package: "k8s.io/kubernetes/pkg/client/cache", Name: "SharedIndexInformer"} | 	cacheSharedIndexInformer    = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "SharedIndexInformer"} | ||||||
| 	listOptions                 = types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "ListOptions"} | 	listOptions                 = types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "ListOptions"} | ||||||
| 	reflectType                 = types.Name{Package: "reflect", Name: "Type"} | 	reflectType                 = types.Name{Package: "reflect", Name: "Type"} | ||||||
| 	runtimeObject               = types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "Object"} | 	runtimeObject               = types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "Object"} | ||||||
|   | |||||||
| @@ -219,7 +219,7 @@ func (g *listerGenerator) Imports(c *generator.Context) (imports []string) { | |||||||
| 	imports = append(imports, "k8s.io/apimachinery/pkg/api/errors") | 	imports = append(imports, "k8s.io/apimachinery/pkg/api/errors") | ||||||
| 	imports = append(imports, "k8s.io/apimachinery/pkg/labels") | 	imports = append(imports, "k8s.io/apimachinery/pkg/labels") | ||||||
| 	// for Indexer | 	// for Indexer | ||||||
| 	imports = append(imports, "k8s.io/kubernetes/pkg/client/cache") | 	imports = append(imports, "k8s.io/client-go/tools/cache") | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								federation/client/cache/BUILD
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								federation/client/cache/BUILD
									
									
									
									
										vendored
									
									
								
							| @@ -13,8 +13,8 @@ go_library( | |||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//federation/apis/federation/v1beta1:go_default_library", |         "//federation/apis/federation/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//vendor:github.com/golang/glog", |         "//vendor:github.com/golang/glog", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								federation/client/cache/cluster_cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								federation/client/cache/cluster_cache.go
									
									
									
									
										vendored
									
									
								
							| @@ -18,8 +18,8 @@ package cache | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/golang/glog" | 	"github.com/golang/glog" | ||||||
|  | 	kubecache "k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	"k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	kubecache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // StoreToClusterLister makes a Store have the List method of the metav1.ClusterInterface | // StoreToClusterLister makes a Store have the List method of the metav1.ClusterInterface | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ go_library( | |||||||
|         "//federation/pkg/federation-controller/util:go_default_library", |         "//federation/pkg/federation-controller/util:go_default_library", | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/typed/discovery:go_default_library", |         "//pkg/client/typed/discovery:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
| @@ -35,6 +34,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |         "//vendor:k8s.io/apimachinery/pkg/util/wait", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|         "//vendor:k8s.io/client-go/rest", |         "//vendor:k8s.io/client-go/rest", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,10 +27,10 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/util/sets" | 	"k8s.io/apimachinery/pkg/util/sets" | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	federationv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	clustercache "k8s.io/kubernetes/federation/client/cache" | 	clustercache "k8s.io/kubernetes/federation/client/cache" | ||||||
| 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ go_library( | |||||||
|         "//federation/pkg/federation-controller/util/eventsink:go_default_library", |         "//federation/pkg/federation-controller/util/eventsink:go_default_library", | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
| @@ -28,6 +27,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/types", |         "//vendor:k8s.io/apimachinery/pkg/types", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
| 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/client-go/util/flowcontrol" | 	"k8s.io/client-go/util/flowcontrol" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| @@ -30,7 +31,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" | 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|   | |||||||
| @@ -21,7 +21,6 @@ go_library( | |||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
| @@ -31,6 +30,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/types", |         "//vendor:k8s.io/apimachinery/pkg/types", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ import ( | |||||||
| 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/client-go/util/flowcontrol" | 	"k8s.io/client-go/util/flowcontrol" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| @@ -35,7 +36,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	extensionsv1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | 	extensionsv1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|   | |||||||
| @@ -24,7 +24,6 @@ go_library( | |||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
| @@ -35,6 +34,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |         "//vendor:k8s.io/apimachinery/pkg/util/wait", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/client-go/util/flowcontrol" | 	"k8s.io/client-go/util/flowcontrol" | ||||||
| 	fed "k8s.io/kubernetes/federation/apis/federation" | 	fed "k8s.io/kubernetes/federation/apis/federation" | ||||||
| 	fedv1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	fedv1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| @@ -42,7 +43,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	extensionsv1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | 	extensionsv1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|   | |||||||
| @@ -21,7 +21,6 @@ go_library( | |||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
| @@ -32,6 +31,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", |         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/types", |         "//vendor:k8s.io/apimachinery/pkg/types", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
| @@ -49,7 +49,6 @@ go_test( | |||||||
|         "//federation/pkg/federation-controller/util/test:go_default_library", |         "//federation/pkg/federation-controller/util/test:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache: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", | ||||||
|         "//vendor:github.com/golang/glog", |         "//vendor:github.com/golang/glog", | ||||||
| @@ -59,6 +58,7 @@ go_test( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/types", |         "//vendor:k8s.io/apimachinery/pkg/types", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |         "//vendor:k8s.io/apimachinery/pkg/util/wait", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/client-go/util/flowcontrol" | 	"k8s.io/client-go/util/flowcontrol" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| @@ -36,7 +37,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | 	extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	fakefedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/fake" | 	fakefedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/fake" | ||||||
| 	"k8s.io/kubernetes/federation/pkg/federation-controller/util" | 	"k8s.io/kubernetes/federation/pkg/federation-controller/util" | ||||||
| @@ -34,7 +35,6 @@ import ( | |||||||
| 	. "k8s.io/kubernetes/federation/pkg/federation-controller/util/test" | 	. "k8s.io/kubernetes/federation/pkg/federation-controller/util/test" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | 	extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	fakekubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" | 	fakekubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,7 +20,6 @@ go_library( | |||||||
|         "//federation/pkg/federation-controller/util/eventsink:go_default_library", |         "//federation/pkg/federation-controller/util/eventsink:go_default_library", | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
| @@ -29,6 +28,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ import ( | |||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/client-go/util/flowcontrol" | 	"k8s.io/client-go/util/flowcontrol" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| @@ -32,7 +33,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" | 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|   | |||||||
| @@ -24,7 +24,6 @@ go_library( | |||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/legacylisters:go_default_library", |         "//pkg/client/legacylisters:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
| @@ -36,6 +35,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |         "//vendor:k8s.io/apimachinery/pkg/util/wait", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/client-go/util/flowcontrol" | 	"k8s.io/client-go/util/flowcontrol" | ||||||
| 	fed "k8s.io/kubernetes/federation/apis/federation" | 	fed "k8s.io/kubernetes/federation/apis/federation" | ||||||
| 	fedv1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	fedv1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| @@ -42,7 +43,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	extensionsv1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | 	extensionsv1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/legacylisters" | 	"k8s.io/kubernetes/pkg/client/legacylisters" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
|   | |||||||
| @@ -20,7 +20,6 @@ go_library( | |||||||
|         "//federation/pkg/federation-controller/util/eventsink:go_default_library", |         "//federation/pkg/federation-controller/util/eventsink:go_default_library", | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
| @@ -30,6 +29,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/types", |         "//vendor:k8s.io/apimachinery/pkg/types", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ import ( | |||||||
| 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	"k8s.io/client-go/util/flowcontrol" | 	"k8s.io/client-go/util/flowcontrol" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| @@ -33,7 +34,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" | 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|   | |||||||
| @@ -29,7 +29,6 @@ go_library( | |||||||
|         "//federation/pkg/federation-controller/util/deletionhelper:go_default_library", |         "//federation/pkg/federation-controller/util/deletionhelper:go_default_library", | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/legacylisters:go_default_library", |         "//pkg/client/legacylisters:go_default_library", | ||||||
|         "//pkg/client/record:go_default_library", |         "//pkg/client/record:go_default_library", | ||||||
| @@ -45,6 +44,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |         "//vendor:k8s.io/apimachinery/pkg/util/wait", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|         "//vendor:k8s.io/client-go/rest", |         "//vendor:k8s.io/client-go/rest", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,9 +24,9 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
| 	restclient "k8s.io/client-go/rest" | 	restclient "k8s.io/client-go/rest" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	v1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	v1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	v1 "k8s.io/kubernetes/pkg/api/v1" | 	v1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/legacylisters" | 	"k8s.io/kubernetes/pkg/client/legacylisters" | ||||||
| 	"k8s.io/kubernetes/pkg/util/workqueue" | 	"k8s.io/kubernetes/pkg/util/workqueue" | ||||||
|   | |||||||
| @@ -20,9 +20,9 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| 	v1 "k8s.io/kubernetes/pkg/api/v1" | 	v1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" | 	"github.com/golang/glog" | ||||||
|   | |||||||
| @@ -22,9 +22,9 @@ import ( | |||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| 	v1 "k8s.io/kubernetes/pkg/api/v1" | 	v1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
|  |  | ||||||
| 	"reflect" | 	"reflect" | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/util/sets" | 	"k8s.io/apimachinery/pkg/util/sets" | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	v1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	v1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	federationcache "k8s.io/kubernetes/federation/client/cache" | 	federationcache "k8s.io/kubernetes/federation/client/cache" | ||||||
| 	fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| @@ -41,7 +42,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/deletionhelper" | 	"k8s.io/kubernetes/federation/pkg/federation-controller/util/deletionhelper" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	v1 "k8s.io/kubernetes/pkg/api/v1" | 	v1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	"k8s.io/kubernetes/pkg/client/legacylisters" | 	"k8s.io/kubernetes/pkg/client/legacylisters" | ||||||
| 	"k8s.io/kubernetes/pkg/client/record" | 	"k8s.io/kubernetes/pkg/client/record" | ||||||
|   | |||||||
| @@ -29,7 +29,6 @@ go_library( | |||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/controller/deployment/util:go_default_library", |         "//pkg/controller/deployment/util:go_default_library", | ||||||
| @@ -40,6 +39,7 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |         "//vendor:k8s.io/apimachinery/pkg/util/wait", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|         "//vendor:k8s.io/client-go/rest", |         "//vendor:k8s.io/client-go/rest", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|         "//vendor:k8s.io/client-go/tools/clientcmd", |         "//vendor:k8s.io/client-go/tools/clientcmd", | ||||||
|         "//vendor:k8s.io/client-go/tools/clientcmd/api", |         "//vendor:k8s.io/client-go/tools/clientcmd/api", | ||||||
|         "//vendor:k8s.io/client-go/util/flowcontrol", |         "//vendor:k8s.io/client-go/util/flowcontrol", | ||||||
| @@ -63,7 +63,6 @@ go_test( | |||||||
|         "//federation/client/clientset_generated/federation_clientset/fake:go_default_library", |         "//federation/client/clientset_generated/federation_clientset/fake:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache: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/client/testing/core:go_default_library", |         "//pkg/client/testing/core:go_default_library", | ||||||
| @@ -72,6 +71,7 @@ go_test( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,10 +26,10 @@ import ( | |||||||
| 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
| 	restclient "k8s.io/client-go/rest" | 	restclient "k8s.io/client-go/rest" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | 	federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" | 	"github.com/golang/glog" | ||||||
|   | |||||||
| @@ -23,10 +23,10 @@ import ( | |||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	"k8s.io/client-go/tools/cache" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| 	fakefederationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/fake" | 	fakefederationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/fake" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	fakekubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" | 	fakekubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" | ||||||
| 	"k8s.io/kubernetes/pkg/client/testing/core" | 	"k8s.io/kubernetes/pkg/client/testing/core" | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ import ( | |||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | 	pkgruntime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/kubernetes/pkg/client/cache" | 	"k8s.io/client-go/tools/cache" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Returns cache.ResourceEventHandlerFuncs that trigger the given function | // Returns cache.ResourceEventHandlerFuncs that trigger the given function | ||||||
|   | |||||||
| @@ -53,13 +53,13 @@ go_test( | |||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|         "//pkg/api/testapi:go_default_library", |         "//pkg/api/testapi:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/client/restclient/fake:go_default_library", |  | ||||||
|         "//pkg/client/typed/dynamic:go_default_library", |         "//pkg/client/typed/dynamic:go_default_library", | ||||||
|         "//pkg/kubectl/cmd/testing:go_default_library", |         "//pkg/kubectl/cmd/testing:go_default_library", | ||||||
|         "//pkg/kubectl/cmd/util:go_default_library", |         "//pkg/kubectl/cmd/util:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/api/errors", |         "//vendor:k8s.io/apimachinery/pkg/api/errors", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/diff", |         "//vendor:k8s.io/apimachinery/pkg/util/diff", | ||||||
|  |         "//vendor:k8s.io/client-go/rest/fake", | ||||||
|         "//vendor:k8s.io/client-go/tools/clientcmd", |         "//vendor:k8s.io/client-go/tools/clientcmd", | ||||||
|         "//vendor:k8s.io/client-go/tools/clientcmd/api", |         "//vendor:k8s.io/client-go/tools/clientcmd/api", | ||||||
|     ], |     ], | ||||||
|   | |||||||
| @@ -46,7 +46,6 @@ go_test( | |||||||
|         "//pkg/api/testapi:go_default_library", |         "//pkg/api/testapi:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/apis/extensions/v1beta1:go_default_library", |         "//pkg/apis/extensions/v1beta1:go_default_library", | ||||||
|         "//pkg/client/restclient/fake:go_default_library", |  | ||||||
|         "//pkg/client/typed/dynamic:go_default_library", |         "//pkg/client/typed/dynamic:go_default_library", | ||||||
|         "//pkg/kubectl/cmd/testing:go_default_library", |         "//pkg/kubectl/cmd/testing:go_default_library", | ||||||
|         "//pkg/kubectl/cmd/util:go_default_library", |         "//pkg/kubectl/cmd/util:go_default_library", | ||||||
| @@ -54,6 +53,7 @@ go_test( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/api/errors", |         "//vendor:k8s.io/apimachinery/pkg/api/errors", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/diff", |         "//vendor:k8s.io/apimachinery/pkg/util/diff", | ||||||
|  |         "//vendor:k8s.io/client-go/rest/fake", | ||||||
|         "//vendor:k8s.io/client-go/tools/clientcmd", |         "//vendor:k8s.io/client-go/tools/clientcmd", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/util/diff" | 	"k8s.io/apimachinery/pkg/util/diff" | ||||||
|  | 	"k8s.io/client-go/rest/fake" | ||||||
| 	"k8s.io/client-go/tools/clientcmd" | 	"k8s.io/client-go/tools/clientcmd" | ||||||
| 	kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing" | 	kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing" | ||||||
| 	"k8s.io/kubernetes/federation/pkg/kubefed/util" | 	"k8s.io/kubernetes/federation/pkg/kubefed/util" | ||||||
| @@ -41,7 +42,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/api/testapi" | 	"k8s.io/kubernetes/pkg/api/testapi" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | 	"k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/restclient/fake" |  | ||||||
| 	"k8s.io/kubernetes/pkg/client/typed/dynamic" | 	"k8s.io/kubernetes/pkg/client/typed/dynamic" | ||||||
| 	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" | 	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" | ||||||
| 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | ||||||
| @@ -751,6 +751,7 @@ func fakeInitHostFactory(federationName, namespaceName, ip, dnsZoneName, image, | |||||||
| 	ns := dynamic.ContentConfig().NegotiatedSerializer | 	ns := dynamic.ContentConfig().NegotiatedSerializer | ||||||
| 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | ||||||
| 	tf.Client = &fake.RESTClient{ | 	tf.Client = &fake.RESTClient{ | ||||||
|  | 		APIRegistry:          api.Registry, | ||||||
| 		NegotiatedSerializer: ns, | 		NegotiatedSerializer: ns, | ||||||
| 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | ||||||
| 			switch p, m := req.URL.Path, req.Method; { | 			switch p, m := req.URL.Path, req.Method; { | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ import ( | |||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/util/diff" | 	"k8s.io/apimachinery/pkg/util/diff" | ||||||
|  | 	"k8s.io/client-go/rest/fake" | ||||||
| 	"k8s.io/client-go/tools/clientcmd" | 	"k8s.io/client-go/tools/clientcmd" | ||||||
| 	clientcmdapi "k8s.io/client-go/tools/clientcmd/api" | 	clientcmdapi "k8s.io/client-go/tools/clientcmd/api" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | 	federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" | ||||||
| @@ -33,7 +34,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	"k8s.io/kubernetes/pkg/api/testapi" | 	"k8s.io/kubernetes/pkg/api/testapi" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/client/restclient/fake" |  | ||||||
| 	"k8s.io/kubernetes/pkg/client/typed/dynamic" | 	"k8s.io/kubernetes/pkg/client/typed/dynamic" | ||||||
| 	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" | 	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" | ||||||
| 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | ||||||
| @@ -175,6 +175,7 @@ func testJoinFederationFactory(clusterName, secretName, server string) cmdutil.F | |||||||
| 	codec := testapi.Federation.Codec() | 	codec := testapi.Federation.Codec() | ||||||
| 	ns := dynamic.ContentConfig().NegotiatedSerializer | 	ns := dynamic.ContentConfig().NegotiatedSerializer | ||||||
| 	tf.Client = &fake.RESTClient{ | 	tf.Client = &fake.RESTClient{ | ||||||
|  | 		APIRegistry:          api.Registry, | ||||||
| 		NegotiatedSerializer: ns, | 		NegotiatedSerializer: ns, | ||||||
| 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | ||||||
| 			switch p, m := req.URL.Path, req.Method; { | 			switch p, m := req.URL.Path, req.Method; { | ||||||
| @@ -250,6 +251,7 @@ func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token stri | |||||||
| 	ns := dynamic.ContentConfig().NegotiatedSerializer | 	ns := dynamic.ContentConfig().NegotiatedSerializer | ||||||
| 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | ||||||
| 	tf.Client = &fake.RESTClient{ | 	tf.Client = &fake.RESTClient{ | ||||||
|  | 		APIRegistry:          api.Registry, | ||||||
| 		NegotiatedSerializer: ns, | 		NegotiatedSerializer: ns, | ||||||
| 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | ||||||
| 			switch p, m := req.URL.Path, req.Method; { | 			switch p, m := req.URL.Path, req.Method; { | ||||||
|   | |||||||
| @@ -25,11 +25,11 @@ import ( | |||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/client-go/rest/fake" | ||||||
| 	federationapi "k8s.io/kubernetes/federation/apis/federation" | 	federationapi "k8s.io/kubernetes/federation/apis/federation" | ||||||
| 	kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing" | 	kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	"k8s.io/kubernetes/pkg/api/testapi" | 	"k8s.io/kubernetes/pkg/api/testapi" | ||||||
| 	"k8s.io/kubernetes/pkg/client/restclient/fake" |  | ||||||
| 	"k8s.io/kubernetes/pkg/client/typed/dynamic" | 	"k8s.io/kubernetes/pkg/client/typed/dynamic" | ||||||
| 	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" | 	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" | ||||||
| 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | ||||||
| @@ -169,6 +169,7 @@ func testUnjoinFederationFactory(name, server, secret string) cmdutil.Factory { | |||||||
| 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | ||||||
| 	ns := testapi.Federation.NegotiatedSerializer() | 	ns := testapi.Federation.NegotiatedSerializer() | ||||||
| 	tf.Client = &fake.RESTClient{ | 	tf.Client = &fake.RESTClient{ | ||||||
|  | 		APIRegistry:          api.Registry, | ||||||
| 		NegotiatedSerializer: ns, | 		NegotiatedSerializer: ns, | ||||||
| 		GroupName:            "federation", | 		GroupName:            "federation", | ||||||
| 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | ||||||
| @@ -205,6 +206,7 @@ func fakeUnjoinHostFactory(name string) cmdutil.Factory { | |||||||
| 	ns := dynamic.ContentConfig().NegotiatedSerializer | 	ns := dynamic.ContentConfig().NegotiatedSerializer | ||||||
| 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | 	tf.ClientConfig = kubefedtesting.DefaultClientConfig() | ||||||
| 	tf.Client = &fake.RESTClient{ | 	tf.Client = &fake.RESTClient{ | ||||||
|  | 		APIRegistry:          api.Registry, | ||||||
| 		NegotiatedSerializer: ns, | 		NegotiatedSerializer: ns, | ||||||
| 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | 		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { | ||||||
| 			switch p, m := req.URL.Path, req.Method; { | 			switch p, m := req.URL.Path, req.Method; { | ||||||
|   | |||||||
| @@ -86,7 +86,6 @@ pkg/apis/rbac/install | |||||||
| pkg/apis/rbac/v1alpha1 | pkg/apis/rbac/v1alpha1 | ||||||
| pkg/apis/storage/install | pkg/apis/storage/install | ||||||
| pkg/apis/storage/validation | pkg/apis/storage/validation | ||||||
| pkg/client/cache |  | ||||||
| pkg/client/conditions | pkg/client/conditions | ||||||
| pkg/client/informers/informers_generated/apps | pkg/client/informers/informers_generated/apps | ||||||
| pkg/client/informers/informers_generated/apps/internalversion | pkg/client/informers/informers_generated/apps/internalversion | ||||||
| @@ -297,6 +296,7 @@ staging/src/k8s.io/client-go/plugin/pkg/client/auth | |||||||
| staging/src/k8s.io/client-go/plugin/pkg/client/auth/gcp | staging/src/k8s.io/client-go/plugin/pkg/client/auth/gcp | ||||||
| staging/src/k8s.io/client-go/rest/watch | staging/src/k8s.io/client-go/rest/watch | ||||||
| staging/src/k8s.io/client-go/tools/auth | staging/src/k8s.io/client-go/tools/auth | ||||||
|  | staging/src/k8s.io/client-go/tools/cache | ||||||
| staging/src/k8s.io/client-go/tools/metrics | staging/src/k8s.io/client-go/tools/metrics | ||||||
| staging/src/k8s.io/client-go/util/cert | staging/src/k8s.io/client-go/util/cert | ||||||
| staging/src/k8s.io/client-go/util/homedir | staging/src/k8s.io/client-go/util/homedir | ||||||
|   | |||||||
| @@ -32,7 +32,6 @@ filegroup( | |||||||
|         "//pkg/auth/authorizer/abac:all-srcs", |         "//pkg/auth/authorizer/abac:all-srcs", | ||||||
|         "//pkg/auth/user:all-srcs", |         "//pkg/auth/user:all-srcs", | ||||||
|         "//pkg/capabilities:all-srcs", |         "//pkg/capabilities:all-srcs", | ||||||
|         "//pkg/client/cache:all-srcs", |  | ||||||
|         "//pkg/client/chaosclient:all-srcs", |         "//pkg/client/chaosclient:all-srcs", | ||||||
|         "//pkg/client/clientset_generated/clientset:all-srcs", |         "//pkg/client/clientset_generated/clientset:all-srcs", | ||||||
|         "//pkg/client/clientset_generated/internalclientset:all-srcs", |         "//pkg/client/clientset_generated/internalclientset:all-srcs", | ||||||
| @@ -69,11 +68,11 @@ filegroup( | |||||||
|         "//pkg/client/listers/storage/v1beta1:all-srcs", |         "//pkg/client/listers/storage/v1beta1:all-srcs", | ||||||
|         "//pkg/client/metrics:all-srcs", |         "//pkg/client/metrics:all-srcs", | ||||||
|         "//pkg/client/record:all-srcs", |         "//pkg/client/record:all-srcs", | ||||||
|         "//pkg/client/restclient/fake:all-srcs", |  | ||||||
|         "//pkg/client/retry:all-srcs", |         "//pkg/client/retry:all-srcs", | ||||||
|         "//pkg/client/testdata:all-srcs", |         "//pkg/client/testdata:all-srcs", | ||||||
|         "//pkg/client/testing/cache:all-srcs", |         "//pkg/client/testing/cache:all-srcs", | ||||||
|         "//pkg/client/testing/core:all-srcs", |         "//pkg/client/testing/core:all-srcs", | ||||||
|  |         "//pkg/client/tests:all-srcs", | ||||||
|         "//pkg/client/typed/discovery:all-srcs", |         "//pkg/client/typed/discovery:all-srcs", | ||||||
|         "//pkg/client/typed/dynamic:all-srcs", |         "//pkg/client/typed/dynamic:all-srcs", | ||||||
|         "//pkg/client/unversioned:all-srcs", |         "//pkg/client/unversioned:all-srcs", | ||||||
|   | |||||||
| @@ -25,7 +25,6 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", |         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/net", |         "//vendor:k8s.io/apimachinery/pkg/util/net", | ||||||
|         "//vendor:k8s.io/apiserver/pkg/util/flag", |  | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,9 +17,12 @@ limitations under the License. | |||||||
| package componentconfig | package componentconfig | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"sort" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	utilflag "k8s.io/apiserver/pkg/util/flag" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -445,12 +448,12 @@ type KubeletConfiguration struct { | |||||||
| 	// that describe resources reserved for non-kubernetes components. | 	// that describe resources reserved for non-kubernetes components. | ||||||
| 	// Currently only cpu and memory are supported. [default=none] | 	// Currently only cpu and memory are supported. [default=none] | ||||||
| 	// See http://kubernetes.io/docs/user-guide/compute-resources for more detail. | 	// See http://kubernetes.io/docs/user-guide/compute-resources for more detail. | ||||||
| 	SystemReserved utilflag.ConfigurationMap | 	SystemReserved ConfigurationMap | ||||||
| 	// A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=150G) pairs | 	// A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=150G) pairs | ||||||
| 	// that describe resources reserved for kubernetes system components. | 	// that describe resources reserved for kubernetes system components. | ||||||
| 	// Currently only cpu and memory are supported. [default=none] | 	// Currently only cpu and memory are supported. [default=none] | ||||||
| 	// See http://kubernetes.io/docs/user-guide/compute-resources for more detail. | 	// See http://kubernetes.io/docs/user-guide/compute-resources for more detail. | ||||||
| 	KubeReserved utilflag.ConfigurationMap | 	KubeReserved ConfigurationMap | ||||||
| 	// Default behaviour for kernel tuning | 	// Default behaviour for kernel tuning | ||||||
| 	ProtectKernelDefaults bool | 	ProtectKernelDefaults bool | ||||||
| 	// If true, Kubelet ensures a set of iptables rules are present on host. | 	// If true, Kubelet ensures a set of iptables rules are present on host. | ||||||
| @@ -863,3 +866,33 @@ type AdmissionPluginConfiguration struct { | |||||||
| 	// +optional | 	// +optional | ||||||
| 	Configuration runtime.Object | 	Configuration runtime.Object | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type ConfigurationMap map[string]string | ||||||
|  |  | ||||||
|  | func (m *ConfigurationMap) String() string { | ||||||
|  | 	pairs := []string{} | ||||||
|  | 	for k, v := range *m { | ||||||
|  | 		pairs = append(pairs, fmt.Sprintf("%s=%s", k, v)) | ||||||
|  | 	} | ||||||
|  | 	sort.Strings(pairs) | ||||||
|  | 	return strings.Join(pairs, ",") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ConfigurationMap) Set(value string) error { | ||||||
|  | 	for _, s := range strings.Split(value, ",") { | ||||||
|  | 		if len(s) == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		arr := strings.SplitN(s, "=", 2) | ||||||
|  | 		if len(arr) == 2 { | ||||||
|  | 			(*m)[strings.TrimSpace(arr[0])] = strings.TrimSpace(arr[1]) | ||||||
|  | 		} else { | ||||||
|  | 			(*m)[strings.TrimSpace(arr[0])] = "" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (*ConfigurationMap) Type() string { | ||||||
|  | 	return "mapStringString" | ||||||
|  | } | ||||||
|   | |||||||
| @@ -30,7 +30,6 @@ go_library( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/conversion", |         "//vendor:k8s.io/apimachinery/pkg/conversion", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", |         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", | ||||||
|         "//vendor:k8s.io/apiserver/pkg/util/flag", |  | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ package v1alpha1 | |||||||
| import ( | import ( | ||||||
| 	conversion "k8s.io/apimachinery/pkg/conversion" | 	conversion "k8s.io/apimachinery/pkg/conversion" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	flag "k8s.io/apiserver/pkg/util/flag" |  | ||||||
| 	api "k8s.io/kubernetes/pkg/api" | 	api "k8s.io/kubernetes/pkg/api" | ||||||
| 	v1 "k8s.io/kubernetes/pkg/api/v1" | 	v1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	componentconfig "k8s.io/kubernetes/pkg/apis/componentconfig" | 	componentconfig "k8s.io/kubernetes/pkg/apis/componentconfig" | ||||||
| @@ -464,8 +463,8 @@ func autoConvert_v1alpha1_KubeletConfiguration_To_componentconfig_KubeletConfigu | |||||||
| 	if err := api.Convert_Pointer_bool_To_bool(&in.EnableControllerAttachDetach, &out.EnableControllerAttachDetach, s); err != nil { | 	if err := api.Convert_Pointer_bool_To_bool(&in.EnableControllerAttachDetach, &out.EnableControllerAttachDetach, s); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	out.SystemReserved = *(*flag.ConfigurationMap)(unsafe.Pointer(&in.SystemReserved)) | 	out.SystemReserved = *(*componentconfig.ConfigurationMap)(unsafe.Pointer(&in.SystemReserved)) | ||||||
| 	out.KubeReserved = *(*flag.ConfigurationMap)(unsafe.Pointer(&in.KubeReserved)) | 	out.KubeReserved = *(*componentconfig.ConfigurationMap)(unsafe.Pointer(&in.KubeReserved)) | ||||||
| 	out.ProtectKernelDefaults = in.ProtectKernelDefaults | 	out.ProtectKernelDefaults = in.ProtectKernelDefaults | ||||||
| 	if err := api.Convert_Pointer_bool_To_bool(&in.MakeIPTablesUtilChains, &out.MakeIPTablesUtilChains, s); err != nil { | 	if err := api.Convert_Pointer_bool_To_bool(&in.MakeIPTablesUtilChains, &out.MakeIPTablesUtilChains, s); err != nil { | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ package componentconfig | |||||||
| import ( | import ( | ||||||
| 	conversion "k8s.io/apimachinery/pkg/conversion" | 	conversion "k8s.io/apimachinery/pkg/conversion" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	flag "k8s.io/apiserver/pkg/util/flag" |  | ||||||
| 	api "k8s.io/kubernetes/pkg/api" | 	api "k8s.io/kubernetes/pkg/api" | ||||||
| 	reflect "reflect" | 	reflect "reflect" | ||||||
| ) | ) | ||||||
| @@ -208,14 +207,14 @@ func DeepCopy_componentconfig_KubeletConfiguration(in interface{}, out interface | |||||||
| 		} | 		} | ||||||
| 		if in.SystemReserved != nil { | 		if in.SystemReserved != nil { | ||||||
| 			in, out := &in.SystemReserved, &out.SystemReserved | 			in, out := &in.SystemReserved, &out.SystemReserved | ||||||
| 			*out = make(flag.ConfigurationMap) | 			*out = make(ConfigurationMap) | ||||||
| 			for key, val := range *in { | 			for key, val := range *in { | ||||||
| 				(*out)[key] = val | 				(*out)[key] = val | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if in.KubeReserved != nil { | 		if in.KubeReserved != nil { | ||||||
| 			in, out := &in.KubeReserved, &out.KubeReserved | 			in, out := &in.KubeReserved, &out.KubeReserved | ||||||
| 			*out = make(flag.ConfigurationMap) | 			*out = make(ConfigurationMap) | ||||||
| 			for key, val := range *in { | 			for key, val := range *in { | ||||||
| 				(*out)[key] = val | 				(*out)[key] = val | ||||||
| 			} | 			} | ||||||
|   | |||||||
							
								
								
									
										112
									
								
								pkg/client/cache/BUILD
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										112
									
								
								pkg/client/cache/BUILD
									
									
									
									
										vendored
									
									
								
							| @@ -1,112 +0,0 @@ | |||||||
| package(default_visibility = ["//visibility:public"]) |  | ||||||
|  |  | ||||||
| licenses(["notice"]) |  | ||||||
|  |  | ||||||
| load( |  | ||||||
|     "@io_bazel_rules_go//go:def.bzl", |  | ||||||
|     "go_library", |  | ||||||
|     "go_test", |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| go_library( |  | ||||||
|     name = "go_default_library", |  | ||||||
|     srcs = [ |  | ||||||
|         "controller.go", |  | ||||||
|         "delta_fifo.go", |  | ||||||
|         "doc.go", |  | ||||||
|         "expiration_cache.go", |  | ||||||
|         "expiration_cache_fakes.go", |  | ||||||
|         "fake_custom_store.go", |  | ||||||
|         "fifo.go", |  | ||||||
|         "index.go", |  | ||||||
|         "listers.go", |  | ||||||
|         "listwatch.go", |  | ||||||
|         "mutation_detector.go", |  | ||||||
|         "reflector.go", |  | ||||||
|         "shared_informer.go", |  | ||||||
|         "store.go", |  | ||||||
|         "thread_safe_store.go", |  | ||||||
|         "undelta_store.go", |  | ||||||
|     ], |  | ||||||
|     tags = ["automanaged"], |  | ||||||
|     deps = [ |  | ||||||
|         "//pkg/api:go_default_library", |  | ||||||
|         "//vendor:github.com/golang/glog", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/api/errors", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/api/meta", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/fields", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/labels", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/diff", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/runtime", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/sets", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |  | ||||||
|         "//vendor:k8s.io/client-go/rest", |  | ||||||
|         "//vendor:k8s.io/client-go/util/clock", |  | ||||||
|     ], |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| go_test( |  | ||||||
|     name = "go_default_test", |  | ||||||
|     srcs = [ |  | ||||||
|         "controller_test.go", |  | ||||||
|         "delta_fifo_test.go", |  | ||||||
|         "expiration_cache_test.go", |  | ||||||
|         "fifo_test.go", |  | ||||||
|         "index_test.go", |  | ||||||
|         "mutation_detector_test.go", |  | ||||||
|         "processor_listener_test.go", |  | ||||||
|         "reflector_test.go", |  | ||||||
|         "store_test.go", |  | ||||||
|         "undelta_store_test.go", |  | ||||||
|     ], |  | ||||||
|     library = ":go_default_library", |  | ||||||
|     tags = ["automanaged"], |  | ||||||
|     deps = [ |  | ||||||
|         "//pkg/api:go_default_library", |  | ||||||
|         "//pkg/api/v1:go_default_library", |  | ||||||
|         "//pkg/client/testing/cache:go_default_library", |  | ||||||
|         "//vendor:github.com/google/gofuzz", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/sets", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/wait", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |  | ||||||
|         "//vendor:k8s.io/client-go/util/clock", |  | ||||||
|     ], |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| filegroup( |  | ||||||
|     name = "package-srcs", |  | ||||||
|     srcs = glob(["**"]), |  | ||||||
|     tags = ["automanaged"], |  | ||||||
|     visibility = ["//visibility:private"], |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| filegroup( |  | ||||||
|     name = "all-srcs", |  | ||||||
|     srcs = [":package-srcs"], |  | ||||||
|     tags = ["automanaged"], |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| go_test( |  | ||||||
|     name = "go_default_xtest", |  | ||||||
|     srcs = ["listwatch_test.go"], |  | ||||||
|     tags = ["automanaged"], |  | ||||||
|     deps = [ |  | ||||||
|         "//pkg/api:go_default_library", |  | ||||||
|         "//pkg/api/testapi:go_default_library", |  | ||||||
|         "//pkg/api/v1:go_default_library", |  | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/fields", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |  | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |  | ||||||
|         "//vendor:k8s.io/client-go/rest", |  | ||||||
|         "//vendor:k8s.io/client-go/util/testing", |  | ||||||
|     ], |  | ||||||
| ) |  | ||||||
							
								
								
									
										41
									
								
								pkg/client/cache/OWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								pkg/client/cache/OWNERS
									
									
									
									
										vendored
									
									
								
							| @@ -1,41 +0,0 @@ | |||||||
| reviewers: |  | ||||||
| - thockin |  | ||||||
| - lavalamp |  | ||||||
| - smarterclayton |  | ||||||
| - wojtek-t |  | ||||||
| - deads2k |  | ||||||
| - brendandburns |  | ||||||
| - derekwaynecarr |  | ||||||
| - caesarxuchao |  | ||||||
| - mikedanese |  | ||||||
| - liggitt |  | ||||||
| - nikhiljindal |  | ||||||
| - bprashanth |  | ||||||
| - erictune |  | ||||||
| - davidopp |  | ||||||
| - pmorie |  | ||||||
| - kargakis |  | ||||||
| - janetkuo |  | ||||||
| - justinsb |  | ||||||
| - eparis |  | ||||||
| - soltysh |  | ||||||
| - jsafrane |  | ||||||
| - dims |  | ||||||
| - madhusudancs |  | ||||||
| - hongchaodeng |  | ||||||
| - krousey |  | ||||||
| - markturansky |  | ||||||
| - fgrzadkowski |  | ||||||
| - xiang90 |  | ||||||
| - mml |  | ||||||
| - ingvagabund |  | ||||||
| - resouer |  | ||||||
| - jessfraz |  | ||||||
| - david-mcmahon |  | ||||||
| - mfojtik |  | ||||||
| - '249043822' |  | ||||||
| - lixiaobing10051267 |  | ||||||
| - ddysher |  | ||||||
| - mqliang |  | ||||||
| - feihujiang |  | ||||||
| - sdminonne |  | ||||||
							
								
								
									
										327
									
								
								pkg/client/cache/controller.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										327
									
								
								pkg/client/cache/controller.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,327 +0,0 @@ | |||||||
| /* |  | ||||||
| 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Config contains all the settings for a Controller. |  | ||||||
| type Config struct { |  | ||||||
| 	// The queue for your objects; either a FIFO or |  | ||||||
| 	// a DeltaFIFO. Your Process() function should accept |  | ||||||
| 	// the output of this Queue's Pop() method. |  | ||||||
| 	Queue |  | ||||||
|  |  | ||||||
| 	// Something that can list and watch your objects. |  | ||||||
| 	ListerWatcher |  | ||||||
|  |  | ||||||
| 	// Something that can process your objects. |  | ||||||
| 	Process ProcessFunc |  | ||||||
|  |  | ||||||
| 	// The type of your objects. |  | ||||||
| 	ObjectType runtime.Object |  | ||||||
|  |  | ||||||
| 	// Reprocess everything at least this often. |  | ||||||
| 	// Note that if it takes longer for you to clear the queue than this |  | ||||||
| 	// period, you will end up processing items in the order determined |  | ||||||
| 	// by FIFO.Replace(). Currently, this is random. If this is a |  | ||||||
| 	// problem, we can change that replacement policy to append new |  | ||||||
| 	// things to the end of the queue instead of replacing the entire |  | ||||||
| 	// queue. |  | ||||||
| 	FullResyncPeriod time.Duration |  | ||||||
|  |  | ||||||
| 	// If true, when Process() returns an error, re-enqueue the object. |  | ||||||
| 	// TODO: add interface to let you inject a delay/backoff or drop |  | ||||||
| 	//       the object completely if desired. Pass the object in |  | ||||||
| 	//       question to this interface as a parameter. |  | ||||||
| 	RetryOnError bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ProcessFunc processes a single object. |  | ||||||
| type ProcessFunc func(obj interface{}) error |  | ||||||
|  |  | ||||||
| // Controller is a generic controller framework. |  | ||||||
| type controller struct { |  | ||||||
| 	config         Config |  | ||||||
| 	reflector      *Reflector |  | ||||||
| 	reflectorMutex sync.RWMutex |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Controller interface { |  | ||||||
| 	Run(stopCh <-chan struct{}) |  | ||||||
| 	HasSynced() bool |  | ||||||
| 	LastSyncResourceVersion() string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // New makes a new Controller from the given Config. |  | ||||||
| func New(c *Config) Controller { |  | ||||||
| 	ctlr := &controller{ |  | ||||||
| 		config: *c, |  | ||||||
| 	} |  | ||||||
| 	return ctlr |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Run begins processing items, and will continue until a value is sent down stopCh. |  | ||||||
| // It's an error to call Run more than once. |  | ||||||
| // Run blocks; call via go. |  | ||||||
| func (c *controller) Run(stopCh <-chan struct{}) { |  | ||||||
| 	defer utilruntime.HandleCrash() |  | ||||||
| 	r := NewReflector( |  | ||||||
| 		c.config.ListerWatcher, |  | ||||||
| 		c.config.ObjectType, |  | ||||||
| 		c.config.Queue, |  | ||||||
| 		c.config.FullResyncPeriod, |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	c.reflectorMutex.Lock() |  | ||||||
| 	c.reflector = r |  | ||||||
| 	c.reflectorMutex.Unlock() |  | ||||||
|  |  | ||||||
| 	r.RunUntil(stopCh) |  | ||||||
|  |  | ||||||
| 	wait.Until(c.processLoop, time.Second, stopCh) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Returns true once this controller has completed an initial resource listing |  | ||||||
| func (c *controller) HasSynced() bool { |  | ||||||
| 	return c.config.Queue.HasSynced() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *controller) LastSyncResourceVersion() string { |  | ||||||
| 	if c.reflector == nil { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	return c.reflector.LastSyncResourceVersion() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // processLoop drains the work queue. |  | ||||||
| // TODO: Consider doing the processing in parallel. This will require a little thought |  | ||||||
| // to make sure that we don't end up processing the same object multiple times |  | ||||||
| // concurrently. |  | ||||||
| // |  | ||||||
| // TODO: Plumb through the stopCh here (and down to the queue) so that this can |  | ||||||
| // actually exit when the controller is stopped. Or just give up on this stuff |  | ||||||
| // ever being stoppable. Converting this whole package to use Context would |  | ||||||
| // also be helpful. |  | ||||||
| func (c *controller) processLoop() { |  | ||||||
| 	for { |  | ||||||
| 		obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process)) |  | ||||||
| 		if err != nil { |  | ||||||
| 			if c.config.RetryOnError { |  | ||||||
| 				// This is the safe way to re-enqueue. |  | ||||||
| 				c.config.Queue.AddIfNotPresent(obj) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ResourceEventHandler can handle notifications for events that happen to a |  | ||||||
| // resource. The events are informational only, so you can't return an |  | ||||||
| // error. |  | ||||||
| //  * OnAdd is called when an object is added. |  | ||||||
| //  * OnUpdate is called when an object is modified. Note that oldObj is the |  | ||||||
| //      last known state of the object-- it is possible that several changes |  | ||||||
| //      were combined together, so you can't use this to see every single |  | ||||||
| //      change. OnUpdate is also called when a re-list happens, and it will |  | ||||||
| //      get called even if nothing changed. This is useful for periodically |  | ||||||
| //      evaluating or syncing something. |  | ||||||
| //  * OnDelete will get the final state of the item if it is known, otherwise |  | ||||||
| //      it will get an object of type DeletedFinalStateUnknown. This can |  | ||||||
| //      happen if the watch is closed and misses the delete event and we don't |  | ||||||
| //      notice the deletion until the subsequent re-list. |  | ||||||
| type ResourceEventHandler interface { |  | ||||||
| 	OnAdd(obj interface{}) |  | ||||||
| 	OnUpdate(oldObj, newObj interface{}) |  | ||||||
| 	OnDelete(obj interface{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ResourceEventHandlerFuncs is an adaptor to let you easily specify as many or |  | ||||||
| // as few of the notification functions as you want while still implementing |  | ||||||
| // ResourceEventHandler. |  | ||||||
| type ResourceEventHandlerFuncs struct { |  | ||||||
| 	AddFunc    func(obj interface{}) |  | ||||||
| 	UpdateFunc func(oldObj, newObj interface{}) |  | ||||||
| 	DeleteFunc func(obj interface{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // OnAdd calls AddFunc if it's not nil. |  | ||||||
| func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}) { |  | ||||||
| 	if r.AddFunc != nil { |  | ||||||
| 		r.AddFunc(obj) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // OnUpdate calls UpdateFunc if it's not nil. |  | ||||||
| func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) { |  | ||||||
| 	if r.UpdateFunc != nil { |  | ||||||
| 		r.UpdateFunc(oldObj, newObj) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // OnDelete calls DeleteFunc if it's not nil. |  | ||||||
| func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) { |  | ||||||
| 	if r.DeleteFunc != nil { |  | ||||||
| 		r.DeleteFunc(obj) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeletionHandlingMetaNamespaceKeyFunc checks for |  | ||||||
| // DeletedFinalStateUnknown objects before calling |  | ||||||
| // MetaNamespaceKeyFunc. |  | ||||||
| func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) { |  | ||||||
| 	if d, ok := obj.(DeletedFinalStateUnknown); ok { |  | ||||||
| 		return d.Key, nil |  | ||||||
| 	} |  | ||||||
| 	return MetaNamespaceKeyFunc(obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewInformer returns a Store and a controller for populating the store |  | ||||||
| // while also providing event notifications. You should only used the returned |  | ||||||
| // Store for Get/List operations; Add/Modify/Deletes will cause the event |  | ||||||
| // notifications to be faulty. |  | ||||||
| // |  | ||||||
| // Parameters: |  | ||||||
| //  * lw is list and watch functions for the source of the resource you want to |  | ||||||
| //    be informed of. |  | ||||||
| //  * objType is an object of the type that you expect to receive. |  | ||||||
| //  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate |  | ||||||
| //    calls, even if nothing changed). Otherwise, re-list will be delayed as |  | ||||||
| //    long as possible (until the upstream source closes the watch or times out, |  | ||||||
| //    or you stop the controller). |  | ||||||
| //  * h is the object you want notifications sent to. |  | ||||||
| // |  | ||||||
| func NewInformer( |  | ||||||
| 	lw ListerWatcher, |  | ||||||
| 	objType runtime.Object, |  | ||||||
| 	resyncPeriod time.Duration, |  | ||||||
| 	h ResourceEventHandler, |  | ||||||
| ) (Store, Controller) { |  | ||||||
| 	// This will hold the client state, as we know it. |  | ||||||
| 	clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc) |  | ||||||
|  |  | ||||||
| 	// This will hold incoming changes. Note how we pass clientState in as a |  | ||||||
| 	// KeyLister, that way resync operations will result in the correct set |  | ||||||
| 	// of update/delete deltas. |  | ||||||
| 	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, nil, clientState) |  | ||||||
|  |  | ||||||
| 	cfg := &Config{ |  | ||||||
| 		Queue:            fifo, |  | ||||||
| 		ListerWatcher:    lw, |  | ||||||
| 		ObjectType:       objType, |  | ||||||
| 		FullResyncPeriod: resyncPeriod, |  | ||||||
| 		RetryOnError:     false, |  | ||||||
|  |  | ||||||
| 		Process: func(obj interface{}) error { |  | ||||||
| 			// from oldest to newest |  | ||||||
| 			for _, d := range obj.(Deltas) { |  | ||||||
| 				switch d.Type { |  | ||||||
| 				case Sync, Added, Updated: |  | ||||||
| 					if old, exists, err := clientState.Get(d.Object); err == nil && exists { |  | ||||||
| 						if err := clientState.Update(d.Object); err != nil { |  | ||||||
| 							return err |  | ||||||
| 						} |  | ||||||
| 						h.OnUpdate(old, d.Object) |  | ||||||
| 					} else { |  | ||||||
| 						if err := clientState.Add(d.Object); err != nil { |  | ||||||
| 							return err |  | ||||||
| 						} |  | ||||||
| 						h.OnAdd(d.Object) |  | ||||||
| 					} |  | ||||||
| 				case Deleted: |  | ||||||
| 					if err := clientState.Delete(d.Object); err != nil { |  | ||||||
| 						return err |  | ||||||
| 					} |  | ||||||
| 					h.OnDelete(d.Object) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			return nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	return clientState, New(cfg) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewIndexerInformer returns a Indexer and a controller for populating the index |  | ||||||
| // while also providing event notifications. You should only used the returned |  | ||||||
| // Index for Get/List operations; Add/Modify/Deletes will cause the event |  | ||||||
| // notifications to be faulty. |  | ||||||
| // |  | ||||||
| // Parameters: |  | ||||||
| //  * lw is list and watch functions for the source of the resource you want to |  | ||||||
| //    be informed of. |  | ||||||
| //  * objType is an object of the type that you expect to receive. |  | ||||||
| //  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate |  | ||||||
| //    calls, even if nothing changed). Otherwise, re-list will be delayed as |  | ||||||
| //    long as possible (until the upstream source closes the watch or times out, |  | ||||||
| //    or you stop the controller). |  | ||||||
| //  * h is the object you want notifications sent to. |  | ||||||
| // |  | ||||||
| func NewIndexerInformer( |  | ||||||
| 	lw ListerWatcher, |  | ||||||
| 	objType runtime.Object, |  | ||||||
| 	resyncPeriod time.Duration, |  | ||||||
| 	h ResourceEventHandler, |  | ||||||
| 	indexers Indexers, |  | ||||||
| ) (Indexer, Controller) { |  | ||||||
| 	// This will hold the client state, as we know it. |  | ||||||
| 	clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers) |  | ||||||
|  |  | ||||||
| 	// This will hold incoming changes. Note how we pass clientState in as a |  | ||||||
| 	// KeyLister, that way resync operations will result in the correct set |  | ||||||
| 	// of update/delete deltas. |  | ||||||
| 	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, nil, clientState) |  | ||||||
|  |  | ||||||
| 	cfg := &Config{ |  | ||||||
| 		Queue:            fifo, |  | ||||||
| 		ListerWatcher:    lw, |  | ||||||
| 		ObjectType:       objType, |  | ||||||
| 		FullResyncPeriod: resyncPeriod, |  | ||||||
| 		RetryOnError:     false, |  | ||||||
|  |  | ||||||
| 		Process: func(obj interface{}) error { |  | ||||||
| 			// from oldest to newest |  | ||||||
| 			for _, d := range obj.(Deltas) { |  | ||||||
| 				switch d.Type { |  | ||||||
| 				case Sync, Added, Updated: |  | ||||||
| 					if old, exists, err := clientState.Get(d.Object); err == nil && exists { |  | ||||||
| 						if err := clientState.Update(d.Object); err != nil { |  | ||||||
| 							return err |  | ||||||
| 						} |  | ||||||
| 						h.OnUpdate(old, d.Object) |  | ||||||
| 					} else { |  | ||||||
| 						if err := clientState.Add(d.Object); err != nil { |  | ||||||
| 							return err |  | ||||||
| 						} |  | ||||||
| 						h.OnAdd(d.Object) |  | ||||||
| 					} |  | ||||||
| 				case Deleted: |  | ||||||
| 					if err := clientState.Delete(d.Object); err != nil { |  | ||||||
| 						return err |  | ||||||
| 					} |  | ||||||
| 					h.OnDelete(d.Object) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			return nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	return clientState, New(cfg) |  | ||||||
| } |  | ||||||
							
								
								
									
										405
									
								
								pkg/client/cache/controller_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										405
									
								
								pkg/client/cache/controller_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,405 +0,0 @@ | |||||||
| /* |  | ||||||
| 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"math/rand" |  | ||||||
| 	"sync" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" |  | ||||||
| 	"k8s.io/apimachinery/pkg/watch" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" |  | ||||||
| 	fcache "k8s.io/kubernetes/pkg/client/testing/cache" |  | ||||||
|  |  | ||||||
| 	"github.com/google/gofuzz" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Example() { |  | ||||||
| 	// source simulates an apiserver object endpoint. |  | ||||||
| 	source := fcache.NewFakeControllerSource() |  | ||||||
|  |  | ||||||
| 	// This will hold the downstream state, as we know it. |  | ||||||
| 	downstream := NewStore(DeletionHandlingMetaNamespaceKeyFunc) |  | ||||||
|  |  | ||||||
| 	// This will hold incoming changes. Note how we pass downstream in as a |  | ||||||
| 	// KeyLister, that way resync operations will result in the correct set |  | ||||||
| 	// of update/delete deltas. |  | ||||||
| 	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, nil, downstream) |  | ||||||
|  |  | ||||||
| 	// Let's do threadsafe output to get predictable test results. |  | ||||||
| 	deletionCounter := make(chan string, 1000) |  | ||||||
|  |  | ||||||
| 	cfg := &Config{ |  | ||||||
| 		Queue:            fifo, |  | ||||||
| 		ListerWatcher:    source, |  | ||||||
| 		ObjectType:       &v1.Pod{}, |  | ||||||
| 		FullResyncPeriod: time.Millisecond * 100, |  | ||||||
| 		RetryOnError:     false, |  | ||||||
|  |  | ||||||
| 		// Let's implement a simple controller that just deletes |  | ||||||
| 		// everything that comes in. |  | ||||||
| 		Process: func(obj interface{}) error { |  | ||||||
| 			// Obj is from the Pop method of the Queue we make above. |  | ||||||
| 			newest := obj.(Deltas).Newest() |  | ||||||
|  |  | ||||||
| 			if newest.Type != Deleted { |  | ||||||
| 				// Update our downstream store. |  | ||||||
| 				err := downstream.Add(newest.Object) |  | ||||||
| 				if err != nil { |  | ||||||
| 					return err |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// Delete this object. |  | ||||||
| 				source.Delete(newest.Object.(runtime.Object)) |  | ||||||
| 			} else { |  | ||||||
| 				// Update our downstream store. |  | ||||||
| 				err := downstream.Delete(newest.Object) |  | ||||||
| 				if err != nil { |  | ||||||
| 					return err |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// fifo's KeyOf is easiest, because it handles |  | ||||||
| 				// DeletedFinalStateUnknown markers. |  | ||||||
| 				key, err := fifo.KeyOf(newest.Object) |  | ||||||
| 				if err != nil { |  | ||||||
| 					return err |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// Report this deletion. |  | ||||||
| 				deletionCounter <- key |  | ||||||
| 			} |  | ||||||
| 			return nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Create the controller and run it until we close stop. |  | ||||||
| 	stop := make(chan struct{}) |  | ||||||
| 	defer close(stop) |  | ||||||
| 	go New(cfg).Run(stop) |  | ||||||
|  |  | ||||||
| 	// Let's add a few objects to the source. |  | ||||||
| 	testIDs := []string{"a-hello", "b-controller", "c-framework"} |  | ||||||
| 	for _, name := range testIDs { |  | ||||||
| 		// Note that these pods are not valid-- the fake source doesn't |  | ||||||
| 		// call validation or anything. |  | ||||||
| 		source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: name}}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Let's wait for the controller to process the things we just added. |  | ||||||
| 	outputSet := sets.String{} |  | ||||||
| 	for i := 0; i < len(testIDs); i++ { |  | ||||||
| 		outputSet.Insert(<-deletionCounter) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, key := range outputSet.List() { |  | ||||||
| 		fmt.Println(key) |  | ||||||
| 	} |  | ||||||
| 	// Output: |  | ||||||
| 	// a-hello |  | ||||||
| 	// b-controller |  | ||||||
| 	// c-framework |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func ExampleNewInformer() { |  | ||||||
| 	// source simulates an apiserver object endpoint. |  | ||||||
| 	source := fcache.NewFakeControllerSource() |  | ||||||
|  |  | ||||||
| 	// Let's do threadsafe output to get predictable test results. |  | ||||||
| 	deletionCounter := make(chan string, 1000) |  | ||||||
|  |  | ||||||
| 	// Make a controller that immediately deletes anything added to it, and |  | ||||||
| 	// logs anything deleted. |  | ||||||
| 	_, controller := NewInformer( |  | ||||||
| 		source, |  | ||||||
| 		&v1.Pod{}, |  | ||||||
| 		time.Millisecond*100, |  | ||||||
| 		ResourceEventHandlerFuncs{ |  | ||||||
| 			AddFunc: func(obj interface{}) { |  | ||||||
| 				source.Delete(obj.(runtime.Object)) |  | ||||||
| 			}, |  | ||||||
| 			DeleteFunc: func(obj interface{}) { |  | ||||||
| 				key, err := DeletionHandlingMetaNamespaceKeyFunc(obj) |  | ||||||
| 				if err != nil { |  | ||||||
| 					key = "oops something went wrong with the key" |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// Report this deletion. |  | ||||||
| 				deletionCounter <- key |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	// Run the controller and run it until we close stop. |  | ||||||
| 	stop := make(chan struct{}) |  | ||||||
| 	defer close(stop) |  | ||||||
| 	go controller.Run(stop) |  | ||||||
|  |  | ||||||
| 	// Let's add a few objects to the source. |  | ||||||
| 	testIDs := []string{"a-hello", "b-controller", "c-framework"} |  | ||||||
| 	for _, name := range testIDs { |  | ||||||
| 		// Note that these pods are not valid-- the fake source doesn't |  | ||||||
| 		// call validation or anything. |  | ||||||
| 		source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: name}}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Let's wait for the controller to process the things we just added. |  | ||||||
| 	outputSet := sets.String{} |  | ||||||
| 	for i := 0; i < len(testIDs); i++ { |  | ||||||
| 		outputSet.Insert(<-deletionCounter) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, key := range outputSet.List() { |  | ||||||
| 		fmt.Println(key) |  | ||||||
| 	} |  | ||||||
| 	// Output: |  | ||||||
| 	// a-hello |  | ||||||
| 	// b-controller |  | ||||||
| 	// c-framework |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestHammerController(t *testing.T) { |  | ||||||
| 	// This test executes a bunch of requests through the fake source and |  | ||||||
| 	// controller framework to make sure there's no locking/threading |  | ||||||
| 	// errors. If an error happens, it should hang forever or trigger the |  | ||||||
| 	// race detector. |  | ||||||
|  |  | ||||||
| 	// source simulates an apiserver object endpoint. |  | ||||||
| 	source := fcache.NewFakeControllerSource() |  | ||||||
|  |  | ||||||
| 	// Let's do threadsafe output to get predictable test results. |  | ||||||
| 	outputSetLock := sync.Mutex{} |  | ||||||
| 	// map of key to operations done on the key |  | ||||||
| 	outputSet := map[string][]string{} |  | ||||||
|  |  | ||||||
| 	recordFunc := func(eventType string, obj interface{}) { |  | ||||||
| 		key, err := DeletionHandlingMetaNamespaceKeyFunc(obj) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Errorf("something wrong with key: %v", err) |  | ||||||
| 			key = "oops something went wrong with the key" |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Record some output when items are deleted. |  | ||||||
| 		outputSetLock.Lock() |  | ||||||
| 		defer outputSetLock.Unlock() |  | ||||||
| 		outputSet[key] = append(outputSet[key], eventType) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Make a controller which just logs all the changes it gets. |  | ||||||
| 	_, controller := NewInformer( |  | ||||||
| 		source, |  | ||||||
| 		&v1.Pod{}, |  | ||||||
| 		time.Millisecond*100, |  | ||||||
| 		ResourceEventHandlerFuncs{ |  | ||||||
| 			AddFunc:    func(obj interface{}) { recordFunc("add", obj) }, |  | ||||||
| 			UpdateFunc: func(oldObj, newObj interface{}) { recordFunc("update", newObj) }, |  | ||||||
| 			DeleteFunc: func(obj interface{}) { recordFunc("delete", obj) }, |  | ||||||
| 		}, |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	if controller.HasSynced() { |  | ||||||
| 		t.Errorf("Expected HasSynced() to return false before we started the controller") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Run the controller and run it until we close stop. |  | ||||||
| 	stop := make(chan struct{}) |  | ||||||
| 	go controller.Run(stop) |  | ||||||
|  |  | ||||||
| 	// Let's wait for the controller to do its initial sync |  | ||||||
| 	wait.Poll(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { |  | ||||||
| 		return controller.HasSynced(), nil |  | ||||||
| 	}) |  | ||||||
| 	if !controller.HasSynced() { |  | ||||||
| 		t.Errorf("Expected HasSynced() to return true after the initial sync") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	wg := sync.WaitGroup{} |  | ||||||
| 	const threads = 3 |  | ||||||
| 	wg.Add(threads) |  | ||||||
| 	for i := 0; i < threads; i++ { |  | ||||||
| 		go func() { |  | ||||||
| 			defer wg.Done() |  | ||||||
| 			// Let's add a few objects to the source. |  | ||||||
| 			currentNames := sets.String{} |  | ||||||
| 			rs := rand.NewSource(rand.Int63()) |  | ||||||
| 			f := fuzz.New().NilChance(.5).NumElements(0, 2).RandSource(rs) |  | ||||||
| 			r := rand.New(rs) // Mustn't use r and f concurrently! |  | ||||||
| 			for i := 0; i < 100; i++ { |  | ||||||
| 				var name string |  | ||||||
| 				var isNew bool |  | ||||||
| 				if currentNames.Len() == 0 || r.Intn(3) == 1 { |  | ||||||
| 					f.Fuzz(&name) |  | ||||||
| 					isNew = true |  | ||||||
| 				} else { |  | ||||||
| 					l := currentNames.List() |  | ||||||
| 					name = l[r.Intn(len(l))] |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				pod := &v1.Pod{} |  | ||||||
| 				f.Fuzz(pod) |  | ||||||
| 				pod.ObjectMeta.Name = name |  | ||||||
| 				pod.ObjectMeta.Namespace = "default" |  | ||||||
| 				// Add, update, or delete randomly. |  | ||||||
| 				// Note that these pods are not valid-- the fake source doesn't |  | ||||||
| 				// call validation or perform any other checking. |  | ||||||
| 				if isNew { |  | ||||||
| 					currentNames.Insert(name) |  | ||||||
| 					source.Add(pod) |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 				switch r.Intn(2) { |  | ||||||
| 				case 0: |  | ||||||
| 					currentNames.Insert(name) |  | ||||||
| 					source.Modify(pod) |  | ||||||
| 				case 1: |  | ||||||
| 					currentNames.Delete(name) |  | ||||||
| 					source.Delete(pod) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}() |  | ||||||
| 	} |  | ||||||
| 	wg.Wait() |  | ||||||
|  |  | ||||||
| 	// Let's wait for the controller to finish processing the things we just added. |  | ||||||
| 	// TODO: look in the queue to see how many items need to be processed. |  | ||||||
| 	time.Sleep(100 * time.Millisecond) |  | ||||||
| 	close(stop) |  | ||||||
|  |  | ||||||
| 	// TODO: Verify that no goroutines were leaked here and that everything shut |  | ||||||
| 	// down cleanly. |  | ||||||
|  |  | ||||||
| 	outputSetLock.Lock() |  | ||||||
| 	t.Logf("got: %#v", outputSet) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUpdate(t *testing.T) { |  | ||||||
| 	// This test is going to exercise the various paths that result in a |  | ||||||
| 	// call to update. |  | ||||||
|  |  | ||||||
| 	// source simulates an apiserver object endpoint. |  | ||||||
| 	source := fcache.NewFakeControllerSource() |  | ||||||
|  |  | ||||||
| 	const ( |  | ||||||
| 		FROM = "from" |  | ||||||
| 		TO   = "to" |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	// These are the transitions we expect to see; because this is |  | ||||||
| 	// asynchronous, there are a lot of valid possibilities. |  | ||||||
| 	type pair struct{ from, to string } |  | ||||||
| 	allowedTransitions := map[pair]bool{ |  | ||||||
| 		pair{FROM, TO}: true, |  | ||||||
|  |  | ||||||
| 		// Because a resync can happen when we've already observed one |  | ||||||
| 		// of the above but before the item is deleted. |  | ||||||
| 		pair{TO, TO}: true, |  | ||||||
| 		// Because a resync could happen before we observe an update. |  | ||||||
| 		pair{FROM, FROM}: true, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	pod := func(name, check string, final bool) *v1.Pod { |  | ||||||
| 		p := &v1.Pod{ |  | ||||||
| 			ObjectMeta: metav1.ObjectMeta{ |  | ||||||
| 				Name:   name, |  | ||||||
| 				Labels: map[string]string{"check": check}, |  | ||||||
| 			}, |  | ||||||
| 		} |  | ||||||
| 		if final { |  | ||||||
| 			p.Labels["final"] = "true" |  | ||||||
| 		} |  | ||||||
| 		return p |  | ||||||
| 	} |  | ||||||
| 	deletePod := func(p *v1.Pod) bool { |  | ||||||
| 		return p.Labels["final"] == "true" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	tests := []func(string){ |  | ||||||
| 		func(name string) { |  | ||||||
| 			name = "a-" + name |  | ||||||
| 			source.Add(pod(name, FROM, false)) |  | ||||||
| 			source.Modify(pod(name, TO, true)) |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const threads = 3 |  | ||||||
|  |  | ||||||
| 	var testDoneWG sync.WaitGroup |  | ||||||
| 	testDoneWG.Add(threads * len(tests)) |  | ||||||
|  |  | ||||||
| 	// Make a controller that deletes things once it observes an update. |  | ||||||
| 	// It calls Done() on the wait group on deletions so we can tell when |  | ||||||
| 	// everything we've added has been deleted. |  | ||||||
| 	watchCh := make(chan struct{}) |  | ||||||
| 	_, controller := NewInformer( |  | ||||||
| 		&testLW{ |  | ||||||
| 			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 				watch, err := source.Watch(options) |  | ||||||
| 				close(watchCh) |  | ||||||
| 				return watch, err |  | ||||||
| 			}, |  | ||||||
| 			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 				return source.List(options) |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		&v1.Pod{}, |  | ||||||
| 		0, |  | ||||||
| 		ResourceEventHandlerFuncs{ |  | ||||||
| 			UpdateFunc: func(oldObj, newObj interface{}) { |  | ||||||
| 				o, n := oldObj.(*v1.Pod), newObj.(*v1.Pod) |  | ||||||
| 				from, to := o.Labels["check"], n.Labels["check"] |  | ||||||
| 				if !allowedTransitions[pair{from, to}] { |  | ||||||
| 					t.Errorf("observed transition %q -> %q for %v", from, to, n.Name) |  | ||||||
| 				} |  | ||||||
| 				if deletePod(n) { |  | ||||||
| 					source.Delete(n) |  | ||||||
| 				} |  | ||||||
| 			}, |  | ||||||
| 			DeleteFunc: func(obj interface{}) { |  | ||||||
| 				testDoneWG.Done() |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	// Run the controller and run it until we close stop. |  | ||||||
| 	// Once Run() is called, calls to testDoneWG.Done() might start, so |  | ||||||
| 	// all testDoneWG.Add() calls must happen before this point |  | ||||||
| 	stop := make(chan struct{}) |  | ||||||
| 	go controller.Run(stop) |  | ||||||
| 	<-watchCh |  | ||||||
|  |  | ||||||
| 	// run every test a few times, in parallel |  | ||||||
| 	var wg sync.WaitGroup |  | ||||||
| 	wg.Add(threads * len(tests)) |  | ||||||
| 	for i := 0; i < threads; i++ { |  | ||||||
| 		for j, f := range tests { |  | ||||||
| 			go func(name string, f func(string)) { |  | ||||||
| 				defer wg.Done() |  | ||||||
| 				f(name) |  | ||||||
| 			}(fmt.Sprintf("%v-%v", i, j), f) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	wg.Wait() |  | ||||||
|  |  | ||||||
| 	// Let's wait for the controller to process the things we just added. |  | ||||||
| 	testDoneWG.Wait() |  | ||||||
| 	close(stop) |  | ||||||
| } |  | ||||||
							
								
								
									
										647
									
								
								pkg/client/cache/delta_fifo.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										647
									
								
								pkg/client/cache/delta_fifo.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,647 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // NewDeltaFIFO returns a Store which can be used process changes to items. |  | ||||||
| // |  | ||||||
| // keyFunc is used to figure out what key an object should have. (It's |  | ||||||
| // exposed in the returned DeltaFIFO's KeyOf() method, with bonus features.) |  | ||||||
| // |  | ||||||
| // 'compressor' may compress as many or as few items as it wants |  | ||||||
| // (including returning an empty slice), but it should do what it |  | ||||||
| // does quickly since it is called while the queue is locked. |  | ||||||
| // 'compressor' may be nil if you don't want any delta compression. |  | ||||||
| // |  | ||||||
| // 'keyLister' is expected to return a list of keys that the consumer of |  | ||||||
| // this queue "knows about". It is used to decide which items are missing |  | ||||||
| // when Replace() is called; 'Deleted' deltas are produced for these items. |  | ||||||
| // It may be nil if you don't need to detect all deletions. |  | ||||||
| // TODO: consider merging keyLister with this object, tracking a list of |  | ||||||
| //       "known" keys when Pop() is called. Have to think about how that |  | ||||||
| //       affects error retrying. |  | ||||||
| // TODO(lavalamp): I believe there is a possible race only when using an |  | ||||||
| //                 external known object source that the above TODO would |  | ||||||
| //                 fix. |  | ||||||
| // |  | ||||||
| // Also see the comment on DeltaFIFO. |  | ||||||
| func NewDeltaFIFO(keyFunc KeyFunc, compressor DeltaCompressor, knownObjects KeyListerGetter) *DeltaFIFO { |  | ||||||
| 	f := &DeltaFIFO{ |  | ||||||
| 		items:           map[string]Deltas{}, |  | ||||||
| 		queue:           []string{}, |  | ||||||
| 		keyFunc:         keyFunc, |  | ||||||
| 		deltaCompressor: compressor, |  | ||||||
| 		knownObjects:    knownObjects, |  | ||||||
| 	} |  | ||||||
| 	f.cond.L = &f.lock |  | ||||||
| 	return f |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeltaFIFO is like FIFO, but allows you to process deletes. |  | ||||||
| // |  | ||||||
| // DeltaFIFO is a producer-consumer queue, where a Reflector is |  | ||||||
| // intended to be the producer, and the consumer is whatever calls |  | ||||||
| // the Pop() method. |  | ||||||
| // |  | ||||||
| // DeltaFIFO solves this use case: |  | ||||||
| //  * You want to process every object change (delta) at most once. |  | ||||||
| //  * When you process an object, you want to see everything |  | ||||||
| //    that's happened to it since you last processed it. |  | ||||||
| //  * You want to process the deletion of objects. |  | ||||||
| //  * You might want to periodically reprocess objects. |  | ||||||
| // |  | ||||||
| // DeltaFIFO's Pop(), Get(), and GetByKey() methods return |  | ||||||
| // interface{} to satisfy the Store/Queue interfaces, but it |  | ||||||
| // will always return an object of type Deltas. |  | ||||||
| // |  | ||||||
| // A note on threading: If you call Pop() in parallel from multiple |  | ||||||
| // threads, you could end up with multiple threads processing slightly |  | ||||||
| // different versions of the same object. |  | ||||||
| // |  | ||||||
| // A note on the KeyLister used by the DeltaFIFO: It's main purpose is |  | ||||||
| // to list keys that are "known", for the purpose of figuring out which |  | ||||||
| // items have been deleted when Replace() or Delete() are called. The deleted |  | ||||||
| // object will be included in the DeleteFinalStateUnknown markers. These objects |  | ||||||
| // could be stale. |  | ||||||
| // |  | ||||||
| // You may provide a function to compress deltas (e.g., represent a |  | ||||||
| // series of Updates as a single Update). |  | ||||||
| type DeltaFIFO struct { |  | ||||||
| 	// lock/cond protects access to 'items' and 'queue'. |  | ||||||
| 	lock sync.RWMutex |  | ||||||
| 	cond sync.Cond |  | ||||||
|  |  | ||||||
| 	// We depend on the property that items in the set are in |  | ||||||
| 	// the queue and vice versa, and that all Deltas in this |  | ||||||
| 	// map have at least one Delta. |  | ||||||
| 	items map[string]Deltas |  | ||||||
| 	queue []string |  | ||||||
|  |  | ||||||
| 	// populated is true if the first batch of items inserted by Replace() has been populated |  | ||||||
| 	// or Delete/Add/Update was called first. |  | ||||||
| 	populated bool |  | ||||||
| 	// initialPopulationCount is the number of items inserted by the first call of Replace() |  | ||||||
| 	initialPopulationCount int |  | ||||||
|  |  | ||||||
| 	// keyFunc is used to make the key used for queued item |  | ||||||
| 	// insertion and retrieval, and should be deterministic. |  | ||||||
| 	keyFunc KeyFunc |  | ||||||
|  |  | ||||||
| 	// deltaCompressor tells us how to combine two or more |  | ||||||
| 	// deltas. It may be nil. |  | ||||||
| 	deltaCompressor DeltaCompressor |  | ||||||
|  |  | ||||||
| 	// knownObjects list keys that are "known", for the |  | ||||||
| 	// purpose of figuring out which items have been deleted |  | ||||||
| 	// when Replace() or Delete() is called. |  | ||||||
| 	knownObjects KeyListerGetter |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	_ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	// ErrZeroLengthDeltasObject is returned in a KeyError if a Deltas |  | ||||||
| 	// object with zero length is encountered (should be impossible, |  | ||||||
| 	// even if such an object is accidentally produced by a DeltaCompressor-- |  | ||||||
| 	// but included for completeness). |  | ||||||
| 	ErrZeroLengthDeltasObject = errors.New("0 length Deltas object; can't get key") |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // KeyOf exposes f's keyFunc, but also detects the key of a Deltas object or |  | ||||||
| // DeletedFinalStateUnknown objects. |  | ||||||
| func (f *DeltaFIFO) KeyOf(obj interface{}) (string, error) { |  | ||||||
| 	if d, ok := obj.(Deltas); ok { |  | ||||||
| 		if len(d) == 0 { |  | ||||||
| 			return "", KeyError{obj, ErrZeroLengthDeltasObject} |  | ||||||
| 		} |  | ||||||
| 		obj = d.Newest().Object |  | ||||||
| 	} |  | ||||||
| 	if d, ok := obj.(DeletedFinalStateUnknown); ok { |  | ||||||
| 		return d.Key, nil |  | ||||||
| 	} |  | ||||||
| 	return f.keyFunc(obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Return true if an Add/Update/Delete/AddIfNotPresent are called first, |  | ||||||
| // or an Update called first but the first batch of items inserted by Replace() has been popped |  | ||||||
| func (f *DeltaFIFO) HasSynced() bool { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	return f.populated && f.initialPopulationCount == 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Add inserts an item, and puts it in the queue. The item is only enqueued |  | ||||||
| // if it doesn't already exist in the set. |  | ||||||
| func (f *DeltaFIFO) Add(obj interface{}) error { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	f.populated = true |  | ||||||
| 	return f.queueActionLocked(Added, obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Update is just like Add, but makes an Updated Delta. |  | ||||||
| func (f *DeltaFIFO) Update(obj interface{}) error { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	f.populated = true |  | ||||||
| 	return f.queueActionLocked(Updated, obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Delete is just like Add, but makes an Deleted Delta. If the item does not |  | ||||||
| // already exist, it will be ignored. (It may have already been deleted by a |  | ||||||
| // Replace (re-list), for example. |  | ||||||
| func (f *DeltaFIFO) Delete(obj interface{}) error { |  | ||||||
| 	id, err := f.KeyOf(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	f.populated = true |  | ||||||
| 	if f.knownObjects == nil { |  | ||||||
| 		if _, exists := f.items[id]; !exists { |  | ||||||
| 			// Presumably, this was deleted when a relist happened. |  | ||||||
| 			// Don't provide a second report of the same deletion. |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		// We only want to skip the "deletion" action if the object doesn't |  | ||||||
| 		// exist in knownObjects and it doesn't have corresponding item in items. |  | ||||||
| 		// Note that even if there is a "deletion" action in items, we can ignore it, |  | ||||||
| 		// because it will be deduped automatically in "queueActionLocked" |  | ||||||
| 		_, exists, err := f.knownObjects.GetByKey(id) |  | ||||||
| 		_, itemsExist := f.items[id] |  | ||||||
| 		if err == nil && !exists && !itemsExist { |  | ||||||
| 			// Presumably, this was deleted when a relist happened. |  | ||||||
| 			// Don't provide a second report of the same deletion. |  | ||||||
| 			// TODO(lavalamp): This may be racy-- we aren't properly locked |  | ||||||
| 			// with knownObjects. |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return f.queueActionLocked(Deleted, obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // AddIfNotPresent inserts an item, and puts it in the queue. If the item is already |  | ||||||
| // present in the set, it is neither enqueued nor added to the set. |  | ||||||
| // |  | ||||||
| // This is useful in a single producer/consumer scenario so that the consumer can |  | ||||||
| // safely retry items without contending with the producer and potentially enqueueing |  | ||||||
| // stale items. |  | ||||||
| // |  | ||||||
| // Important: obj must be a Deltas (the output of the Pop() function). Yes, this is |  | ||||||
| // different from the Add/Update/Delete functions. |  | ||||||
| func (f *DeltaFIFO) AddIfNotPresent(obj interface{}) error { |  | ||||||
| 	deltas, ok := obj.(Deltas) |  | ||||||
| 	if !ok { |  | ||||||
| 		return fmt.Errorf("object must be of type deltas, but got: %#v", obj) |  | ||||||
| 	} |  | ||||||
| 	id, err := f.KeyOf(deltas.Newest().Object) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	f.addIfNotPresent(id, deltas) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // addIfNotPresent inserts deltas under id if it does not exist, and assumes the caller |  | ||||||
| // already holds the fifo lock. |  | ||||||
| func (f *DeltaFIFO) addIfNotPresent(id string, deltas Deltas) { |  | ||||||
| 	f.populated = true |  | ||||||
| 	if _, exists := f.items[id]; exists { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f.queue = append(f.queue, id) |  | ||||||
| 	f.items[id] = deltas |  | ||||||
| 	f.cond.Broadcast() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // re-listing and watching can deliver the same update multiple times in any |  | ||||||
| // order. This will combine the most recent two deltas if they are the same. |  | ||||||
| func dedupDeltas(deltas Deltas) Deltas { |  | ||||||
| 	n := len(deltas) |  | ||||||
| 	if n < 2 { |  | ||||||
| 		return deltas |  | ||||||
| 	} |  | ||||||
| 	a := &deltas[n-1] |  | ||||||
| 	b := &deltas[n-2] |  | ||||||
| 	if out := isDup(a, b); out != nil { |  | ||||||
| 		d := append(Deltas{}, deltas[:n-2]...) |  | ||||||
| 		return append(d, *out) |  | ||||||
| 	} |  | ||||||
| 	return deltas |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // If a & b represent the same event, returns the delta that ought to be kept. |  | ||||||
| // Otherwise, returns nil. |  | ||||||
| // TODO: is there anything other than deletions that need deduping? |  | ||||||
| func isDup(a, b *Delta) *Delta { |  | ||||||
| 	if out := isDeletionDup(a, b); out != nil { |  | ||||||
| 		return out |  | ||||||
| 	} |  | ||||||
| 	// TODO: Detect other duplicate situations? Are there any? |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // keep the one with the most information if both are deletions. |  | ||||||
| func isDeletionDup(a, b *Delta) *Delta { |  | ||||||
| 	if b.Type != Deleted || a.Type != Deleted { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	// Do more sophisticated checks, or is this sufficient? |  | ||||||
| 	if _, ok := b.Object.(DeletedFinalStateUnknown); ok { |  | ||||||
| 		return a |  | ||||||
| 	} |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // willObjectBeDeletedLocked returns true only if the last delta for the |  | ||||||
| // given object is Delete. Caller must lock first. |  | ||||||
| func (f *DeltaFIFO) willObjectBeDeletedLocked(id string) bool { |  | ||||||
| 	deltas := f.items[id] |  | ||||||
| 	return len(deltas) > 0 && deltas[len(deltas)-1].Type == Deleted |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // queueActionLocked appends to the delta list for the object, calling |  | ||||||
| // f.deltaCompressor if needed. Caller must lock first. |  | ||||||
| func (f *DeltaFIFO) queueActionLocked(actionType DeltaType, obj interface{}) error { |  | ||||||
| 	id, err := f.KeyOf(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// If object is supposed to be deleted (last event is Deleted), |  | ||||||
| 	// then we should ignore Sync events, because it would result in |  | ||||||
| 	// recreation of this object. |  | ||||||
| 	if actionType == Sync && f.willObjectBeDeletedLocked(id) { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	newDeltas := append(f.items[id], Delta{actionType, obj}) |  | ||||||
| 	newDeltas = dedupDeltas(newDeltas) |  | ||||||
| 	if f.deltaCompressor != nil { |  | ||||||
| 		newDeltas = f.deltaCompressor.Compress(newDeltas) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_, exists := f.items[id] |  | ||||||
| 	if len(newDeltas) > 0 { |  | ||||||
| 		if !exists { |  | ||||||
| 			f.queue = append(f.queue, id) |  | ||||||
| 		} |  | ||||||
| 		f.items[id] = newDeltas |  | ||||||
| 		f.cond.Broadcast() |  | ||||||
| 	} else if exists { |  | ||||||
| 		// The compression step removed all deltas, so |  | ||||||
| 		// we need to remove this from our map (extra items |  | ||||||
| 		// in the queue are ignored if they are not in the |  | ||||||
| 		// map). |  | ||||||
| 		delete(f.items, id) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // List returns a list of all the items; it returns the object |  | ||||||
| // from the most recent Delta. |  | ||||||
| // You should treat the items returned inside the deltas as immutable. |  | ||||||
| func (f *DeltaFIFO) List() []interface{} { |  | ||||||
| 	f.lock.RLock() |  | ||||||
| 	defer f.lock.RUnlock() |  | ||||||
| 	return f.listLocked() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (f *DeltaFIFO) listLocked() []interface{} { |  | ||||||
| 	list := make([]interface{}, 0, len(f.items)) |  | ||||||
| 	for _, item := range f.items { |  | ||||||
| 		// Copy item's slice so operations on this slice (delta |  | ||||||
| 		// compression) won't interfere with the object we return. |  | ||||||
| 		item = copyDeltas(item) |  | ||||||
| 		list = append(list, item.Newest().Object) |  | ||||||
| 	} |  | ||||||
| 	return list |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListKeys returns a list of all the keys of the objects currently |  | ||||||
| // in the FIFO. |  | ||||||
| func (f *DeltaFIFO) ListKeys() []string { |  | ||||||
| 	f.lock.RLock() |  | ||||||
| 	defer f.lock.RUnlock() |  | ||||||
| 	list := make([]string, 0, len(f.items)) |  | ||||||
| 	for key := range f.items { |  | ||||||
| 		list = append(list, key) |  | ||||||
| 	} |  | ||||||
| 	return list |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Get returns the complete list of deltas for the requested item, |  | ||||||
| // or sets exists=false. |  | ||||||
| // You should treat the items returned inside the deltas as immutable. |  | ||||||
| func (f *DeltaFIFO) Get(obj interface{}) (item interface{}, exists bool, err error) { |  | ||||||
| 	key, err := f.KeyOf(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	return f.GetByKey(key) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetByKey returns the complete list of deltas for the requested item, |  | ||||||
| // setting exists=false if that list is empty. |  | ||||||
| // You should treat the items returned inside the deltas as immutable. |  | ||||||
| func (f *DeltaFIFO) GetByKey(key string) (item interface{}, exists bool, err error) { |  | ||||||
| 	f.lock.RLock() |  | ||||||
| 	defer f.lock.RUnlock() |  | ||||||
| 	d, exists := f.items[key] |  | ||||||
| 	if exists { |  | ||||||
| 		// Copy item's slice so operations on this slice (delta |  | ||||||
| 		// compression) won't interfere with the object we return. |  | ||||||
| 		d = copyDeltas(d) |  | ||||||
| 	} |  | ||||||
| 	return d, exists, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Pop blocks until an item is added to the queue, and then returns it.  If |  | ||||||
| // multiple items are ready, they are returned in the order in which they were |  | ||||||
| // added/updated. The item is removed from the queue (and the store) before it |  | ||||||
| // is returned, so if you don't successfully process it, you need to add it back |  | ||||||
| // with AddIfNotPresent(). |  | ||||||
| // process function is called under lock, so it is safe update data structures |  | ||||||
| // in it that need to be in sync with the queue (e.g. knownKeys). The PopProcessFunc |  | ||||||
| // may return an instance of ErrRequeue with a nested error to indicate the current |  | ||||||
| // item should be requeued (equivalent to calling AddIfNotPresent under the lock). |  | ||||||
| // |  | ||||||
| // Pop returns a 'Deltas', which has a complete list of all the things |  | ||||||
| // that happened to the object (deltas) while it was sitting in the queue. |  | ||||||
| func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	for { |  | ||||||
| 		for len(f.queue) == 0 { |  | ||||||
| 			f.cond.Wait() |  | ||||||
| 		} |  | ||||||
| 		id := f.queue[0] |  | ||||||
| 		f.queue = f.queue[1:] |  | ||||||
| 		item, ok := f.items[id] |  | ||||||
| 		if f.initialPopulationCount > 0 { |  | ||||||
| 			f.initialPopulationCount-- |  | ||||||
| 		} |  | ||||||
| 		if !ok { |  | ||||||
| 			// Item may have been deleted subsequently. |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		delete(f.items, id) |  | ||||||
| 		err := process(item) |  | ||||||
| 		if e, ok := err.(ErrRequeue); ok { |  | ||||||
| 			f.addIfNotPresent(id, item) |  | ||||||
| 			err = e.Err |  | ||||||
| 		} |  | ||||||
| 		// Don't need to copyDeltas here, because we're transferring |  | ||||||
| 		// ownership to the caller. |  | ||||||
| 		return item, err |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Replace will delete the contents of 'f', using instead the given map. |  | ||||||
| // 'f' takes ownership of the map, you should not reference the map again |  | ||||||
| // after calling this function. f's queue is reset, too; upon return, it |  | ||||||
| // will contain the items in the map, in no particular order. |  | ||||||
| func (f *DeltaFIFO) Replace(list []interface{}, resourceVersion string) error { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	keys := make(sets.String, len(list)) |  | ||||||
|  |  | ||||||
| 	for _, item := range list { |  | ||||||
| 		key, err := f.KeyOf(item) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return KeyError{item, err} |  | ||||||
| 		} |  | ||||||
| 		keys.Insert(key) |  | ||||||
| 		if err := f.queueActionLocked(Sync, item); err != nil { |  | ||||||
| 			return fmt.Errorf("couldn't enqueue object: %v", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if f.knownObjects == nil { |  | ||||||
| 		// Do deletion detection against our own list. |  | ||||||
| 		for k, oldItem := range f.items { |  | ||||||
| 			if keys.Has(k) { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			var deletedObj interface{} |  | ||||||
| 			if n := oldItem.Newest(); n != nil { |  | ||||||
| 				deletedObj = n.Object |  | ||||||
| 			} |  | ||||||
| 			if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if !f.populated { |  | ||||||
| 			f.populated = true |  | ||||||
| 			f.initialPopulationCount = len(list) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Detect deletions not already in the queue. |  | ||||||
| 	// TODO(lavalamp): This may be racy-- we aren't properly locked |  | ||||||
| 	// with knownObjects. Unproven. |  | ||||||
| 	knownKeys := f.knownObjects.ListKeys() |  | ||||||
| 	queuedDeletions := 0 |  | ||||||
| 	for _, k := range knownKeys { |  | ||||||
| 		if keys.Has(k) { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		deletedObj, exists, err := f.knownObjects.GetByKey(k) |  | ||||||
| 		if err != nil { |  | ||||||
| 			deletedObj = nil |  | ||||||
| 			glog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k) |  | ||||||
| 		} else if !exists { |  | ||||||
| 			deletedObj = nil |  | ||||||
| 			glog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k) |  | ||||||
| 		} |  | ||||||
| 		queuedDeletions++ |  | ||||||
| 		if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !f.populated { |  | ||||||
| 		f.populated = true |  | ||||||
| 		f.initialPopulationCount = len(list) + queuedDeletions |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Resync will send a sync event for each item |  | ||||||
| func (f *DeltaFIFO) Resync() error { |  | ||||||
| 	var keys []string |  | ||||||
| 	func() { |  | ||||||
| 		f.lock.RLock() |  | ||||||
| 		defer f.lock.RUnlock() |  | ||||||
| 		keys = f.knownObjects.ListKeys() |  | ||||||
| 	}() |  | ||||||
| 	for _, k := range keys { |  | ||||||
| 		if err := f.syncKey(k); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (f *DeltaFIFO) syncKey(key string) error { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	obj, exists, err := f.knownObjects.GetByKey(key) |  | ||||||
| 	if err != nil { |  | ||||||
| 		glog.Errorf("Unexpected error %v during lookup of key %v, unable to queue object for sync", err, key) |  | ||||||
| 		return nil |  | ||||||
| 	} else if !exists { |  | ||||||
| 		glog.Infof("Key %v does not exist in known objects store, unable to queue object for sync", key) |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// If we are doing Resync() and there is already an event queued for that object, |  | ||||||
| 	// we ignore the Resync for it. This is to avoid the race, in which the resync |  | ||||||
| 	// comes with the previous value of object (since queueing an event for the object |  | ||||||
| 	// doesn't trigger changing the underlying store <knownObjects>. |  | ||||||
| 	id, err := f.KeyOf(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	if len(f.items[id]) > 0 { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := f.queueActionLocked(Sync, obj); err != nil { |  | ||||||
| 		return fmt.Errorf("couldn't queue object: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // A KeyListerGetter is anything that knows how to list its keys and look up by key. |  | ||||||
| type KeyListerGetter interface { |  | ||||||
| 	KeyLister |  | ||||||
| 	KeyGetter |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // A KeyLister is anything that knows how to list its keys. |  | ||||||
| type KeyLister interface { |  | ||||||
| 	ListKeys() []string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // A KeyGetter is anything that knows how to get the value stored under a given key. |  | ||||||
| type KeyGetter interface { |  | ||||||
| 	GetByKey(key string) (interface{}, bool, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeltaCompressor is an algorithm that removes redundant changes. |  | ||||||
| type DeltaCompressor interface { |  | ||||||
| 	Compress(Deltas) Deltas |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeltaCompressorFunc should remove redundant changes; but changes that |  | ||||||
| // are redundant depend on one's desired semantics, so this is an |  | ||||||
| // injectable function. |  | ||||||
| // |  | ||||||
| // DeltaCompressorFunc adapts a raw function to be a DeltaCompressor. |  | ||||||
| type DeltaCompressorFunc func(Deltas) Deltas |  | ||||||
|  |  | ||||||
| // Compress just calls dc. |  | ||||||
| func (dc DeltaCompressorFunc) Compress(d Deltas) Deltas { |  | ||||||
| 	return dc(d) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeltaType is the type of a change (addition, deletion, etc) |  | ||||||
| type DeltaType string |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	Added   DeltaType = "Added" |  | ||||||
| 	Updated DeltaType = "Updated" |  | ||||||
| 	Deleted DeltaType = "Deleted" |  | ||||||
| 	// The other types are obvious. You'll get Sync deltas when: |  | ||||||
| 	//  * A watch expires/errors out and a new list/watch cycle is started. |  | ||||||
| 	//  * You've turned on periodic syncs. |  | ||||||
| 	// (Anything that trigger's DeltaFIFO's Replace() method.) |  | ||||||
| 	Sync DeltaType = "Sync" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Delta is the type stored by a DeltaFIFO. It tells you what change |  | ||||||
| // happened, and the object's state after* that change. |  | ||||||
| // |  | ||||||
| // [*] Unless the change is a deletion, and then you'll get the final |  | ||||||
| //     state of the object before it was deleted. |  | ||||||
| type Delta struct { |  | ||||||
| 	Type   DeltaType |  | ||||||
| 	Object interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Deltas is a list of one or more 'Delta's to an individual object. |  | ||||||
| // The oldest delta is at index 0, the newest delta is the last one. |  | ||||||
| type Deltas []Delta |  | ||||||
|  |  | ||||||
| // Oldest is a convenience function that returns the oldest delta, or |  | ||||||
| // nil if there are no deltas. |  | ||||||
| func (d Deltas) Oldest() *Delta { |  | ||||||
| 	if len(d) > 0 { |  | ||||||
| 		return &d[0] |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Newest is a convenience function that returns the newest delta, or |  | ||||||
| // nil if there are no deltas. |  | ||||||
| func (d Deltas) Newest() *Delta { |  | ||||||
| 	if n := len(d); n > 0 { |  | ||||||
| 		return &d[n-1] |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // copyDeltas returns a shallow copy of d; that is, it copies the slice but not |  | ||||||
| // the objects in the slice. This allows Get/List to return an object that we |  | ||||||
| // know won't be clobbered by a subsequent call to a delta compressor. |  | ||||||
| func copyDeltas(d Deltas) Deltas { |  | ||||||
| 	d2 := make(Deltas, len(d)) |  | ||||||
| 	copy(d2, d) |  | ||||||
| 	return d2 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeletedFinalStateUnknown is placed into a DeltaFIFO in the case where |  | ||||||
| // an object was deleted but the watch deletion event was missed. In this |  | ||||||
| // case we don't know the final "resting" state of the object, so there's |  | ||||||
| // a chance the included `Obj` is stale. |  | ||||||
| type DeletedFinalStateUnknown struct { |  | ||||||
| 	Key string |  | ||||||
| 	Obj interface{} |  | ||||||
| } |  | ||||||
							
								
								
									
										533
									
								
								pkg/client/cache/delta_fifo_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										533
									
								
								pkg/client/cache/delta_fifo_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,533 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"reflect" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // helper function to reduce stuttering |  | ||||||
| func testPop(f *DeltaFIFO) testFifoObject { |  | ||||||
| 	return Pop(f).(Deltas).Newest().Object.(testFifoObject) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // keyLookupFunc adapts a raw function to be a KeyLookup. |  | ||||||
| type keyLookupFunc func() []testFifoObject |  | ||||||
|  |  | ||||||
| // ListKeys just calls kl. |  | ||||||
| func (kl keyLookupFunc) ListKeys() []string { |  | ||||||
| 	result := []string{} |  | ||||||
| 	for _, fifoObj := range kl() { |  | ||||||
| 		result = append(result, fifoObj.name) |  | ||||||
| 	} |  | ||||||
| 	return result |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetByKey returns the key if it exists in the list returned by kl. |  | ||||||
| func (kl keyLookupFunc) GetByKey(key string) (interface{}, bool, error) { |  | ||||||
| 	for _, v := range kl() { |  | ||||||
| 		if v.name == key { |  | ||||||
| 			return v, true, nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil, false, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_basic(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
| 	const amount = 500 |  | ||||||
| 	go func() { |  | ||||||
| 		for i := 0; i < amount; i++ { |  | ||||||
| 			f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1)) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 	go func() { |  | ||||||
| 		for u := uint64(0); u < amount; u++ { |  | ||||||
| 			f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1)) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	lastInt := int(0) |  | ||||||
| 	lastUint := uint64(0) |  | ||||||
| 	for i := 0; i < amount*2; i++ { |  | ||||||
| 		switch obj := testPop(f).val.(type) { |  | ||||||
| 		case int: |  | ||||||
| 			if obj <= lastInt { |  | ||||||
| 				t.Errorf("got %v (int) out of order, last was %v", obj, lastInt) |  | ||||||
| 			} |  | ||||||
| 			lastInt = obj |  | ||||||
| 		case uint64: |  | ||||||
| 			if obj <= lastUint { |  | ||||||
| 				t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint) |  | ||||||
| 			} else { |  | ||||||
| 				lastUint = obj |  | ||||||
| 			} |  | ||||||
| 		default: |  | ||||||
| 			t.Fatalf("unexpected type %#v", obj) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_requeueOnPop(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	_, err := f.Pop(func(obj interface{}) error { |  | ||||||
| 		if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" { |  | ||||||
| 			t.Fatalf("unexpected object: %#v", obj) |  | ||||||
| 		} |  | ||||||
| 		return ErrRequeue{Err: nil} |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if _, ok, err := f.GetByKey("foo"); !ok || err != nil { |  | ||||||
| 		t.Fatalf("object should have been requeued: %t %v", ok, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_, err = f.Pop(func(obj interface{}) error { |  | ||||||
| 		if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" { |  | ||||||
| 			t.Fatalf("unexpected object: %#v", obj) |  | ||||||
| 		} |  | ||||||
| 		return ErrRequeue{Err: fmt.Errorf("test error")} |  | ||||||
| 	}) |  | ||||||
| 	if err == nil || err.Error() != "test error" { |  | ||||||
| 		t.Fatalf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if _, ok, err := f.GetByKey("foo"); !ok || err != nil { |  | ||||||
| 		t.Fatalf("object should have been requeued: %t %v", ok, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_, err = f.Pop(func(obj interface{}) error { |  | ||||||
| 		if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" { |  | ||||||
| 			t.Fatalf("unexpected object: %#v", obj) |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if _, ok, err := f.GetByKey("foo"); ok || err != nil { |  | ||||||
| 		t.Fatalf("object should have been removed: %t %v", ok, err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_compressorWorks(t *testing.T) { |  | ||||||
| 	oldestTypes := []DeltaType{} |  | ||||||
| 	f := NewDeltaFIFO( |  | ||||||
| 		testFifoObjectKeyFunc, |  | ||||||
| 		// This function just keeps the most recent delta |  | ||||||
| 		// and puts deleted ones in the list. |  | ||||||
| 		DeltaCompressorFunc(func(d Deltas) Deltas { |  | ||||||
| 			if n := len(d); n > 1 { |  | ||||||
| 				oldestTypes = append(oldestTypes, d[0].Type) |  | ||||||
| 				d = d[1:] |  | ||||||
| 			} |  | ||||||
| 			return d |  | ||||||
| 		}), |  | ||||||
| 		nil, |  | ||||||
| 	) |  | ||||||
| 	if f.HasSynced() { |  | ||||||
| 		t.Errorf("Expected HasSynced to be false before completion of initial population") |  | ||||||
| 	} |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Update(mkFifoObj("foo", 12)) |  | ||||||
| 	f.Replace([]interface{}{mkFifoObj("foo", 20)}, "0") |  | ||||||
| 	f.Delete(mkFifoObj("foo", 22)) |  | ||||||
| 	f.Add(mkFifoObj("foo", 25)) // flush the last one out |  | ||||||
| 	expect := []DeltaType{Added, Updated, Sync, Deleted} |  | ||||||
| 	if e, a := expect, oldestTypes; !reflect.DeepEqual(e, a) { |  | ||||||
| 		t.Errorf("Expected %#v, got %#v", e, a) |  | ||||||
| 	} |  | ||||||
| 	if e, a := (Deltas{{Added, mkFifoObj("foo", 25)}}), Pop(f).(Deltas); !reflect.DeepEqual(e, a) { |  | ||||||
| 		t.Fatalf("Expected %#v, got %#v", e, a) |  | ||||||
| 	} |  | ||||||
| 	if !f.HasSynced() { |  | ||||||
| 		t.Errorf("Expected HasSynced to be true after completion of initial population") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_addUpdate(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Update(mkFifoObj("foo", 12)) |  | ||||||
| 	f.Delete(mkFifoObj("foo", 15)) |  | ||||||
|  |  | ||||||
| 	if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) { |  | ||||||
| 		t.Errorf("Expected %+v, got %+v", e, a) |  | ||||||
| 	} |  | ||||||
| 	if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) { |  | ||||||
| 		t.Errorf("Expected %+v, got %+v", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	got := make(chan testFifoObject, 2) |  | ||||||
| 	go func() { |  | ||||||
| 		for { |  | ||||||
| 			obj := testPop(f) |  | ||||||
| 			t.Logf("got a thing %#v", obj) |  | ||||||
| 			t.Logf("D len: %v", len(f.queue)) |  | ||||||
| 			got <- obj |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	first := <-got |  | ||||||
| 	if e, a := 15, first.val; e != a { |  | ||||||
| 		t.Errorf("Didn't get updated value (%v), got %v", e, a) |  | ||||||
| 	} |  | ||||||
| 	select { |  | ||||||
| 	case unexpected := <-got: |  | ||||||
| 		t.Errorf("Got second value %v", unexpected.val) |  | ||||||
| 	case <-time.After(50 * time.Millisecond): |  | ||||||
| 	} |  | ||||||
| 	_, exists, _ := f.Get(mkFifoObj("foo", "")) |  | ||||||
| 	if exists { |  | ||||||
| 		t.Errorf("item did not get removed") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_enqueueingNoLister(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Update(mkFifoObj("bar", 15)) |  | ||||||
| 	f.Add(mkFifoObj("qux", 17)) |  | ||||||
| 	f.Delete(mkFifoObj("qux", 18)) |  | ||||||
|  |  | ||||||
| 	// This delete does not enqueue anything because baz doesn't exist. |  | ||||||
| 	f.Delete(mkFifoObj("baz", 20)) |  | ||||||
|  |  | ||||||
| 	expectList := []int{10, 15, 18} |  | ||||||
| 	for _, expect := range expectList { |  | ||||||
| 		if e, a := expect, testPop(f).val; e != a { |  | ||||||
| 			t.Errorf("Didn't get updated value (%v), got %v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if e, a := 0, len(f.items); e != a { |  | ||||||
| 		t.Errorf("queue unexpectedly not empty: %v != %v\n%#v", e, a, f.items) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_enqueueingWithLister(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO( |  | ||||||
| 		testFifoObjectKeyFunc, |  | ||||||
| 		nil, |  | ||||||
| 		keyLookupFunc(func() []testFifoObject { |  | ||||||
| 			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} |  | ||||||
| 		}), |  | ||||||
| 	) |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Update(mkFifoObj("bar", 15)) |  | ||||||
|  |  | ||||||
| 	// This delete does enqueue the deletion, because "baz" is in the key lister. |  | ||||||
| 	f.Delete(mkFifoObj("baz", 20)) |  | ||||||
|  |  | ||||||
| 	expectList := []int{10, 15, 20} |  | ||||||
| 	for _, expect := range expectList { |  | ||||||
| 		if e, a := expect, testPop(f).val; e != a { |  | ||||||
| 			t.Errorf("Didn't get updated value (%v), got %v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if e, a := 0, len(f.items); e != a { |  | ||||||
| 		t.Errorf("queue unexpectedly not empty: %v != %v", e, a) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_addReplace(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Replace([]interface{}{mkFifoObj("foo", 15)}, "0") |  | ||||||
| 	got := make(chan testFifoObject, 2) |  | ||||||
| 	go func() { |  | ||||||
| 		for { |  | ||||||
| 			got <- testPop(f) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	first := <-got |  | ||||||
| 	if e, a := 15, first.val; e != a { |  | ||||||
| 		t.Errorf("Didn't get updated value (%v), got %v", e, a) |  | ||||||
| 	} |  | ||||||
| 	select { |  | ||||||
| 	case unexpected := <-got: |  | ||||||
| 		t.Errorf("Got second value %v", unexpected.val) |  | ||||||
| 	case <-time.After(50 * time.Millisecond): |  | ||||||
| 	} |  | ||||||
| 	_, exists, _ := f.Get(mkFifoObj("foo", "")) |  | ||||||
| 	if exists { |  | ||||||
| 		t.Errorf("item did not get removed") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_ResyncNonExisting(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO( |  | ||||||
| 		testFifoObjectKeyFunc, |  | ||||||
| 		nil, |  | ||||||
| 		keyLookupFunc(func() []testFifoObject { |  | ||||||
| 			return []testFifoObject{mkFifoObj("foo", 5)} |  | ||||||
| 		}), |  | ||||||
| 	) |  | ||||||
| 	f.Delete(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Resync() |  | ||||||
|  |  | ||||||
| 	deltas := f.items["foo"] |  | ||||||
| 	if len(deltas) != 1 { |  | ||||||
| 		t.Fatalf("unexpected deltas length: %v", deltas) |  | ||||||
| 	} |  | ||||||
| 	if deltas[0].Type != Deleted { |  | ||||||
| 		t.Errorf("unexpected delta: %v", deltas[0]) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_DeleteExistingNonPropagated(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO( |  | ||||||
| 		testFifoObjectKeyFunc, |  | ||||||
| 		nil, |  | ||||||
| 		keyLookupFunc(func() []testFifoObject { |  | ||||||
| 			return []testFifoObject{} |  | ||||||
| 		}), |  | ||||||
| 	) |  | ||||||
| 	f.Add(mkFifoObj("foo", 5)) |  | ||||||
| 	f.Delete(mkFifoObj("foo", 6)) |  | ||||||
|  |  | ||||||
| 	deltas := f.items["foo"] |  | ||||||
| 	if len(deltas) != 2 { |  | ||||||
| 		t.Fatalf("unexpected deltas length: %v", deltas) |  | ||||||
| 	} |  | ||||||
| 	if deltas[len(deltas)-1].Type != Deleted { |  | ||||||
| 		t.Errorf("unexpected delta: %v", deltas[len(deltas)-1]) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_ReplaceMakesDeletions(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO( |  | ||||||
| 		testFifoObjectKeyFunc, |  | ||||||
| 		nil, |  | ||||||
| 		keyLookupFunc(func() []testFifoObject { |  | ||||||
| 			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} |  | ||||||
| 		}), |  | ||||||
| 	) |  | ||||||
| 	f.Delete(mkFifoObj("baz", 10)) |  | ||||||
| 	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") |  | ||||||
|  |  | ||||||
| 	expectedList := []Deltas{ |  | ||||||
| 		{{Deleted, mkFifoObj("baz", 10)}}, |  | ||||||
| 		{{Sync, mkFifoObj("foo", 5)}}, |  | ||||||
| 		// Since "bar" didn't have a delete event and wasn't in the Replace list |  | ||||||
| 		// it should get a tombstone key with the right Obj. |  | ||||||
| 		{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, expected := range expectedList { |  | ||||||
| 		cur := Pop(f).(Deltas) |  | ||||||
| 		if e, a := expected, cur; !reflect.DeepEqual(e, a) { |  | ||||||
| 			t.Errorf("Expected %#v, got %#v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_UpdateResyncRace(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO( |  | ||||||
| 		testFifoObjectKeyFunc, |  | ||||||
| 		nil, |  | ||||||
| 		keyLookupFunc(func() []testFifoObject { |  | ||||||
| 			return []testFifoObject{mkFifoObj("foo", 5)} |  | ||||||
| 		}), |  | ||||||
| 	) |  | ||||||
| 	f.Update(mkFifoObj("foo", 6)) |  | ||||||
| 	f.Resync() |  | ||||||
|  |  | ||||||
| 	expectedList := []Deltas{ |  | ||||||
| 		{{Updated, mkFifoObj("foo", 6)}}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, expected := range expectedList { |  | ||||||
| 		cur := Pop(f).(Deltas) |  | ||||||
| 		if e, a := expected, cur; !reflect.DeepEqual(e, a) { |  | ||||||
| 			t.Errorf("Expected %#v, got %#v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_HasSyncedCorrectOnDeletion(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO( |  | ||||||
| 		testFifoObjectKeyFunc, |  | ||||||
| 		nil, |  | ||||||
| 		keyLookupFunc(func() []testFifoObject { |  | ||||||
| 			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} |  | ||||||
| 		}), |  | ||||||
| 	) |  | ||||||
| 	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") |  | ||||||
|  |  | ||||||
| 	expectedList := []Deltas{ |  | ||||||
| 		{{Sync, mkFifoObj("foo", 5)}}, |  | ||||||
| 		// Since "bar" didn't have a delete event and wasn't in the Replace list |  | ||||||
| 		// it should get a tombstone key with the right Obj. |  | ||||||
| 		{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, expected := range expectedList { |  | ||||||
| 		if f.HasSynced() { |  | ||||||
| 			t.Errorf("Expected HasSynced to be false") |  | ||||||
| 		} |  | ||||||
| 		cur := Pop(f).(Deltas) |  | ||||||
| 		if e, a := expected, cur; !reflect.DeepEqual(e, a) { |  | ||||||
| 			t.Errorf("Expected %#v, got %#v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if f.HasSynced() { |  | ||||||
| 		t.Errorf("Expected HasSynced to be true") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_detectLineJumpers(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Add(mkFifoObj("bar", 1)) |  | ||||||
| 	f.Add(mkFifoObj("foo", 11)) |  | ||||||
| 	f.Add(mkFifoObj("foo", 13)) |  | ||||||
| 	f.Add(mkFifoObj("zab", 30)) |  | ||||||
|  |  | ||||||
| 	if e, a := 13, testPop(f).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line |  | ||||||
|  |  | ||||||
| 	if e, a := 1, testPop(f).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if e, a := 30, testPop(f).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if e, a := 14, testPop(f).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_addIfNotPresent(t *testing.T) { |  | ||||||
| 	f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("b", 3)) |  | ||||||
| 	b3 := Pop(f) |  | ||||||
| 	f.Add(mkFifoObj("c", 4)) |  | ||||||
| 	c4 := Pop(f) |  | ||||||
| 	if e, a := 0, len(f.items); e != a { |  | ||||||
| 		t.Fatalf("Expected %v, got %v items in queue", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("a", 1)) |  | ||||||
| 	f.Add(mkFifoObj("b", 2)) |  | ||||||
| 	f.AddIfNotPresent(b3) |  | ||||||
| 	f.AddIfNotPresent(c4) |  | ||||||
|  |  | ||||||
| 	if e, a := 3, len(f.items); a != e { |  | ||||||
| 		t.Fatalf("expected queue length %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	expectedValues := []int{1, 2, 4} |  | ||||||
| 	for _, expected := range expectedValues { |  | ||||||
| 		if actual := testPop(f).val; actual != expected { |  | ||||||
| 			t.Fatalf("expected value %d, got %d", expected, actual) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_KeyOf(t *testing.T) { |  | ||||||
| 	f := DeltaFIFO{keyFunc: testFifoObjectKeyFunc} |  | ||||||
|  |  | ||||||
| 	table := []struct { |  | ||||||
| 		obj interface{} |  | ||||||
| 		key string |  | ||||||
| 	}{ |  | ||||||
| 		{obj: testFifoObject{name: "A"}, key: "A"}, |  | ||||||
| 		{obj: DeletedFinalStateUnknown{Key: "B", Obj: nil}, key: "B"}, |  | ||||||
| 		{obj: Deltas{{Object: testFifoObject{name: "C"}}}, key: "C"}, |  | ||||||
| 		{obj: Deltas{{Object: DeletedFinalStateUnknown{Key: "D", Obj: nil}}}, key: "D"}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, item := range table { |  | ||||||
| 		got, err := f.KeyOf(item.obj) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Errorf("Unexpected error for %q: %v", item.obj, err) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if e, a := item.key, got; e != a { |  | ||||||
| 			t.Errorf("Expected %v, got %v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeltaFIFO_HasSynced(t *testing.T) { |  | ||||||
| 	tests := []struct { |  | ||||||
| 		actions        []func(f *DeltaFIFO) |  | ||||||
| 		expectedSynced bool |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			actions:        []func(f *DeltaFIFO){}, |  | ||||||
| 			expectedSynced: false, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *DeltaFIFO){ |  | ||||||
| 				func(f *DeltaFIFO) { f.Add(mkFifoObj("a", 1)) }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *DeltaFIFO){ |  | ||||||
| 				func(f *DeltaFIFO) { f.Replace([]interface{}{}, "0") }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *DeltaFIFO){ |  | ||||||
| 				func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: false, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *DeltaFIFO){ |  | ||||||
| 				func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, |  | ||||||
| 				func(f *DeltaFIFO) { Pop(f) }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: false, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *DeltaFIFO){ |  | ||||||
| 				func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, |  | ||||||
| 				func(f *DeltaFIFO) { Pop(f) }, |  | ||||||
| 				func(f *DeltaFIFO) { Pop(f) }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: true, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for i, test := range tests { |  | ||||||
| 		f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) |  | ||||||
|  |  | ||||||
| 		for _, action := range test.actions { |  | ||||||
| 			action(f) |  | ||||||
| 		} |  | ||||||
| 		if e, a := test.expectedSynced, f.HasSynced(); a != e { |  | ||||||
| 			t.Errorf("test case %v failed, expected: %v , got %v", i, e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										24
									
								
								pkg/client/cache/doc.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								pkg/client/cache/doc.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,24 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache is a client-side caching mechanism. It is useful for |  | ||||||
| // reducing the number of server calls you'd otherwise need to make. |  | ||||||
| // Reflector watches a server and updates a Store. Two stores are provided; |  | ||||||
| // one that simply caches objects (for example, to allow a scheduler to |  | ||||||
| // list currently available nodes), and one that additionally acts as |  | ||||||
| // a FIFO queue (for example, to allow a scheduler to process incoming |  | ||||||
| // pods). |  | ||||||
| package cache // import "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
							
								
								
									
										208
									
								
								pkg/client/cache/expiration_cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										208
									
								
								pkg/client/cache/expiration_cache.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,208 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" |  | ||||||
| 	"k8s.io/client-go/util/clock" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // ExpirationCache implements the store interface |  | ||||||
| //	1. All entries are automatically time stamped on insert |  | ||||||
| //		a. The key is computed based off the original item/keyFunc |  | ||||||
| //		b. The value inserted under that key is the timestamped item |  | ||||||
| //	2. Expiration happens lazily on read based on the expiration policy |  | ||||||
| //      a. No item can be inserted into the store while we're expiring |  | ||||||
| //		   *any* item in the cache. |  | ||||||
| //	3. Time-stamps are stripped off unexpired entries before return |  | ||||||
| // Note that the ExpirationCache is inherently slower than a normal |  | ||||||
| // threadSafeStore because it takes a write lock every time it checks if |  | ||||||
| // an item has expired. |  | ||||||
| type ExpirationCache struct { |  | ||||||
| 	cacheStorage     ThreadSafeStore |  | ||||||
| 	keyFunc          KeyFunc |  | ||||||
| 	clock            clock.Clock |  | ||||||
| 	expirationPolicy ExpirationPolicy |  | ||||||
| 	// expirationLock is a write lock used to guarantee that we don't clobber |  | ||||||
| 	// newly inserted objects because of a stale expiration timestamp comparison |  | ||||||
| 	expirationLock sync.Mutex |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ExpirationPolicy dictates when an object expires. Currently only abstracted out |  | ||||||
| // so unittests don't rely on the system clock. |  | ||||||
| type ExpirationPolicy interface { |  | ||||||
| 	IsExpired(obj *timestampedEntry) bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TTLPolicy implements a ttl based ExpirationPolicy. |  | ||||||
| type TTLPolicy struct { |  | ||||||
| 	//	 >0: Expire entries with an age > ttl |  | ||||||
| 	//	<=0: Don't expire any entry |  | ||||||
| 	Ttl time.Duration |  | ||||||
|  |  | ||||||
| 	// Clock used to calculate ttl expiration |  | ||||||
| 	Clock clock.Clock |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsExpired returns true if the given object is older than the ttl, or it can't |  | ||||||
| // determine its age. |  | ||||||
| func (p *TTLPolicy) IsExpired(obj *timestampedEntry) bool { |  | ||||||
| 	return p.Ttl > 0 && p.Clock.Since(obj.timestamp) > p.Ttl |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // timestampedEntry is the only type allowed in a ExpirationCache. |  | ||||||
| type timestampedEntry struct { |  | ||||||
| 	obj       interface{} |  | ||||||
| 	timestamp time.Time |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // getTimestampedEntry returns the timestampedEntry stored under the given key. |  | ||||||
| func (c *ExpirationCache) getTimestampedEntry(key string) (*timestampedEntry, bool) { |  | ||||||
| 	item, _ := c.cacheStorage.Get(key) |  | ||||||
| 	if tsEntry, ok := item.(*timestampedEntry); ok { |  | ||||||
| 		return tsEntry, true |  | ||||||
| 	} |  | ||||||
| 	return nil, false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // getOrExpire retrieves the object from the timestampedEntry if and only if it hasn't |  | ||||||
| // already expired. It holds a write lock across deletion. |  | ||||||
| func (c *ExpirationCache) getOrExpire(key string) (interface{}, bool) { |  | ||||||
| 	// Prevent all inserts from the time we deem an item as "expired" to when we |  | ||||||
| 	// delete it, so an un-expired item doesn't sneak in under the same key, just |  | ||||||
| 	// before the Delete. |  | ||||||
| 	c.expirationLock.Lock() |  | ||||||
| 	defer c.expirationLock.Unlock() |  | ||||||
| 	timestampedItem, exists := c.getTimestampedEntry(key) |  | ||||||
| 	if !exists { |  | ||||||
| 		return nil, false |  | ||||||
| 	} |  | ||||||
| 	if c.expirationPolicy.IsExpired(timestampedItem) { |  | ||||||
| 		glog.V(4).Infof("Entry %v: %+v has expired", key, timestampedItem.obj) |  | ||||||
| 		c.cacheStorage.Delete(key) |  | ||||||
| 		return nil, false |  | ||||||
| 	} |  | ||||||
| 	return timestampedItem.obj, true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetByKey returns the item stored under the key, or sets exists=false. |  | ||||||
| func (c *ExpirationCache) GetByKey(key string) (interface{}, bool, error) { |  | ||||||
| 	obj, exists := c.getOrExpire(key) |  | ||||||
| 	return obj, exists, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Get returns unexpired items. It purges the cache of expired items in the |  | ||||||
| // process. |  | ||||||
| func (c *ExpirationCache) Get(obj interface{}) (interface{}, bool, error) { |  | ||||||
| 	key, err := c.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	obj, exists := c.getOrExpire(key) |  | ||||||
| 	return obj, exists, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // List retrieves a list of unexpired items. It purges the cache of expired |  | ||||||
| // items in the process. |  | ||||||
| func (c *ExpirationCache) List() []interface{} { |  | ||||||
| 	items := c.cacheStorage.List() |  | ||||||
|  |  | ||||||
| 	list := make([]interface{}, 0, len(items)) |  | ||||||
| 	for _, item := range items { |  | ||||||
| 		obj := item.(*timestampedEntry).obj |  | ||||||
| 		if key, err := c.keyFunc(obj); err != nil { |  | ||||||
| 			list = append(list, obj) |  | ||||||
| 		} else if obj, exists := c.getOrExpire(key); exists { |  | ||||||
| 			list = append(list, obj) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return list |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListKeys returns a list of all keys in the expiration cache. |  | ||||||
| func (c *ExpirationCache) ListKeys() []string { |  | ||||||
| 	return c.cacheStorage.ListKeys() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Add timestamps an item and inserts it into the cache, overwriting entries |  | ||||||
| // that might exist under the same key. |  | ||||||
| func (c *ExpirationCache) Add(obj interface{}) error { |  | ||||||
| 	c.expirationLock.Lock() |  | ||||||
| 	defer c.expirationLock.Unlock() |  | ||||||
|  |  | ||||||
| 	key, err := c.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	c.cacheStorage.Add(key, ×tampedEntry{obj, c.clock.Now()}) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Update has not been implemented yet for lack of a use case, so this method |  | ||||||
| // simply calls `Add`. This effectively refreshes the timestamp. |  | ||||||
| func (c *ExpirationCache) Update(obj interface{}) error { |  | ||||||
| 	return c.Add(obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Delete removes an item from the cache. |  | ||||||
| func (c *ExpirationCache) Delete(obj interface{}) error { |  | ||||||
| 	c.expirationLock.Lock() |  | ||||||
| 	defer c.expirationLock.Unlock() |  | ||||||
| 	key, err := c.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	c.cacheStorage.Delete(key) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Replace will convert all items in the given list to TimestampedEntries |  | ||||||
| // before attempting the replace operation. The replace operation will |  | ||||||
| // delete the contents of the ExpirationCache `c`. |  | ||||||
| func (c *ExpirationCache) Replace(list []interface{}, resourceVersion string) error { |  | ||||||
| 	c.expirationLock.Lock() |  | ||||||
| 	defer c.expirationLock.Unlock() |  | ||||||
| 	items := map[string]interface{}{} |  | ||||||
| 	ts := c.clock.Now() |  | ||||||
| 	for _, item := range list { |  | ||||||
| 		key, err := c.keyFunc(item) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return KeyError{item, err} |  | ||||||
| 		} |  | ||||||
| 		items[key] = ×tampedEntry{item, ts} |  | ||||||
| 	} |  | ||||||
| 	c.cacheStorage.Replace(items, resourceVersion) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Resync will touch all objects to put them into the processing queue |  | ||||||
| func (c *ExpirationCache) Resync() error { |  | ||||||
| 	return c.cacheStorage.Resync() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewTTLStore creates and returns a ExpirationCache with a TTLPolicy |  | ||||||
| func NewTTLStore(keyFunc KeyFunc, ttl time.Duration) Store { |  | ||||||
| 	return &ExpirationCache{ |  | ||||||
| 		cacheStorage:     NewThreadSafeStore(Indexers{}, Indices{}), |  | ||||||
| 		keyFunc:          keyFunc, |  | ||||||
| 		clock:            clock.RealClock{}, |  | ||||||
| 		expirationPolicy: &TTLPolicy{ttl, clock.RealClock{}}, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										54
									
								
								pkg/client/cache/expiration_cache_fakes.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								pkg/client/cache/expiration_cache_fakes.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,54 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
| 	"k8s.io/client-go/util/clock" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type fakeThreadSafeMap struct { |  | ||||||
| 	ThreadSafeStore |  | ||||||
| 	deletedKeys chan<- string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *fakeThreadSafeMap) Delete(key string) { |  | ||||||
| 	if c.deletedKeys != nil { |  | ||||||
| 		c.ThreadSafeStore.Delete(key) |  | ||||||
| 		c.deletedKeys <- key |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type FakeExpirationPolicy struct { |  | ||||||
| 	NeverExpire     sets.String |  | ||||||
| 	RetrieveKeyFunc KeyFunc |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *FakeExpirationPolicy) IsExpired(obj *timestampedEntry) bool { |  | ||||||
| 	key, _ := p.RetrieveKeyFunc(obj) |  | ||||||
| 	return !p.NeverExpire.Has(key) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewFakeExpirationStore(keyFunc KeyFunc, deletedKeys chan<- string, expirationPolicy ExpirationPolicy, cacheClock clock.Clock) Store { |  | ||||||
| 	cacheStorage := NewThreadSafeStore(Indexers{}, Indices{}) |  | ||||||
| 	return &ExpirationCache{ |  | ||||||
| 		cacheStorage:     &fakeThreadSafeMap{cacheStorage, deletedKeys}, |  | ||||||
| 		keyFunc:          keyFunc, |  | ||||||
| 		clock:            cacheClock, |  | ||||||
| 		expirationPolicy: expirationPolicy, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										189
									
								
								pkg/client/cache/expiration_cache_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										189
									
								
								pkg/client/cache/expiration_cache_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,189 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"reflect" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" |  | ||||||
| 	"k8s.io/client-go/util/clock" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestTTLExpirationBasic(t *testing.T) { |  | ||||||
| 	testObj := testStoreObject{id: "foo", val: "bar"} |  | ||||||
| 	deleteChan := make(chan string, 1) |  | ||||||
| 	ttlStore := NewFakeExpirationStore( |  | ||||||
| 		testStoreKeyFunc, deleteChan, |  | ||||||
| 		&FakeExpirationPolicy{ |  | ||||||
| 			NeverExpire: sets.NewString(), |  | ||||||
| 			RetrieveKeyFunc: func(obj interface{}) (string, error) { |  | ||||||
| 				return obj.(*timestampedEntry).obj.(testStoreObject).id, nil |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		clock.RealClock{}, |  | ||||||
| 	) |  | ||||||
| 	err := ttlStore.Add(testObj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("Unable to add obj %#v", testObj) |  | ||||||
| 	} |  | ||||||
| 	item, exists, err := ttlStore.Get(testObj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("Failed to get from store, %v", err) |  | ||||||
| 	} |  | ||||||
| 	if exists || item != nil { |  | ||||||
| 		t.Errorf("Got unexpected item %#v", item) |  | ||||||
| 	} |  | ||||||
| 	key, _ := testStoreKeyFunc(testObj) |  | ||||||
| 	select { |  | ||||||
| 	case delKey := <-deleteChan: |  | ||||||
| 		if delKey != key { |  | ||||||
| 			t.Errorf("Unexpected delete for key %s", key) |  | ||||||
| 		} |  | ||||||
| 	case <-time.After(wait.ForeverTestTimeout): |  | ||||||
| 		t.Errorf("Unexpected timeout waiting on delete") |  | ||||||
| 	} |  | ||||||
| 	close(deleteChan) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReAddExpiredItem(t *testing.T) { |  | ||||||
| 	deleteChan := make(chan string, 1) |  | ||||||
| 	exp := &FakeExpirationPolicy{ |  | ||||||
| 		NeverExpire: sets.NewString(), |  | ||||||
| 		RetrieveKeyFunc: func(obj interface{}) (string, error) { |  | ||||||
| 			return obj.(*timestampedEntry).obj.(testStoreObject).id, nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	ttlStore := NewFakeExpirationStore( |  | ||||||
| 		testStoreKeyFunc, deleteChan, exp, clock.RealClock{}) |  | ||||||
| 	testKey := "foo" |  | ||||||
| 	testObj := testStoreObject{id: testKey, val: "bar"} |  | ||||||
| 	err := ttlStore.Add(testObj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("Unable to add obj %#v", testObj) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// This get will expire the item. |  | ||||||
| 	item, exists, err := ttlStore.Get(testObj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("Failed to get from store, %v", err) |  | ||||||
| 	} |  | ||||||
| 	if exists || item != nil { |  | ||||||
| 		t.Errorf("Got unexpected item %#v", item) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	key, _ := testStoreKeyFunc(testObj) |  | ||||||
| 	differentValue := "different_bar" |  | ||||||
| 	err = ttlStore.Add( |  | ||||||
| 		testStoreObject{id: testKey, val: differentValue}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("Failed to add second value") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	select { |  | ||||||
| 	case delKey := <-deleteChan: |  | ||||||
| 		if delKey != key { |  | ||||||
| 			t.Errorf("Unexpected delete for key %s", key) |  | ||||||
| 		} |  | ||||||
| 	case <-time.After(wait.ForeverTestTimeout): |  | ||||||
| 		t.Errorf("Unexpected timeout waiting on delete") |  | ||||||
| 	} |  | ||||||
| 	exp.NeverExpire = sets.NewString(testKey) |  | ||||||
| 	item, exists, err = ttlStore.GetByKey(testKey) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("Failed to get from store, %v", err) |  | ||||||
| 	} |  | ||||||
| 	if !exists || item == nil || item.(testStoreObject).val != differentValue { |  | ||||||
| 		t.Errorf("Got unexpected item %#v", item) |  | ||||||
| 	} |  | ||||||
| 	close(deleteChan) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestTTLList(t *testing.T) { |  | ||||||
| 	testObjs := []testStoreObject{ |  | ||||||
| 		{id: "foo", val: "bar"}, |  | ||||||
| 		{id: "foo1", val: "bar1"}, |  | ||||||
| 		{id: "foo2", val: "bar2"}, |  | ||||||
| 	} |  | ||||||
| 	expireKeys := sets.NewString(testObjs[0].id, testObjs[2].id) |  | ||||||
| 	deleteChan := make(chan string, len(testObjs)) |  | ||||||
| 	defer close(deleteChan) |  | ||||||
|  |  | ||||||
| 	ttlStore := NewFakeExpirationStore( |  | ||||||
| 		testStoreKeyFunc, deleteChan, |  | ||||||
| 		&FakeExpirationPolicy{ |  | ||||||
| 			NeverExpire: sets.NewString(testObjs[1].id), |  | ||||||
| 			RetrieveKeyFunc: func(obj interface{}) (string, error) { |  | ||||||
| 				return obj.(*timestampedEntry).obj.(testStoreObject).id, nil |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		clock.RealClock{}, |  | ||||||
| 	) |  | ||||||
| 	for _, obj := range testObjs { |  | ||||||
| 		err := ttlStore.Add(obj) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Errorf("Unable to add obj %#v", obj) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	listObjs := ttlStore.List() |  | ||||||
| 	if len(listObjs) != 1 || !reflect.DeepEqual(listObjs[0], testObjs[1]) { |  | ||||||
| 		t.Errorf("List returned unexpected results %#v", listObjs) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Make sure all our deletes come through in an acceptable rate (1/100ms) |  | ||||||
| 	for expireKeys.Len() != 0 { |  | ||||||
| 		select { |  | ||||||
| 		case delKey := <-deleteChan: |  | ||||||
| 			if !expireKeys.Has(delKey) { |  | ||||||
| 				t.Errorf("Unexpected delete for key %s", delKey) |  | ||||||
| 			} |  | ||||||
| 			expireKeys.Delete(delKey) |  | ||||||
| 		case <-time.After(wait.ForeverTestTimeout): |  | ||||||
| 			t.Errorf("Unexpected timeout waiting on delete") |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestTTLPolicy(t *testing.T) { |  | ||||||
| 	fakeTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) |  | ||||||
| 	ttl := 30 * time.Second |  | ||||||
| 	exactlyOnTTL := fakeTime.Add(-ttl) |  | ||||||
| 	expiredTime := fakeTime.Add(-(ttl + 1)) |  | ||||||
|  |  | ||||||
| 	policy := TTLPolicy{ttl, clock.NewFakeClock(fakeTime)} |  | ||||||
| 	fakeTimestampedEntry := ×tampedEntry{obj: struct{}{}, timestamp: exactlyOnTTL} |  | ||||||
| 	if policy.IsExpired(fakeTimestampedEntry) { |  | ||||||
| 		t.Errorf("TTL cache should not expire entries exactly on ttl") |  | ||||||
| 	} |  | ||||||
| 	fakeTimestampedEntry.timestamp = fakeTime |  | ||||||
| 	if policy.IsExpired(fakeTimestampedEntry) { |  | ||||||
| 		t.Errorf("TTL Cache should not expire entries before ttl") |  | ||||||
| 	} |  | ||||||
| 	fakeTimestampedEntry.timestamp = expiredTime |  | ||||||
| 	if !policy.IsExpired(fakeTimestampedEntry) { |  | ||||||
| 		t.Errorf("TTL Cache should expire entries older than ttl") |  | ||||||
| 	} |  | ||||||
| 	for _, ttl = range []time.Duration{0, -1} { |  | ||||||
| 		policy.Ttl = ttl |  | ||||||
| 		if policy.IsExpired(fakeTimestampedEntry) { |  | ||||||
| 			t.Errorf("TTL policy should only expire entries when initialized with a ttl > 0") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										102
									
								
								pkg/client/cache/fake_custom_store.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										102
									
								
								pkg/client/cache/fake_custom_store.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,102 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2016 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 cache |  | ||||||
|  |  | ||||||
| // FakeStore lets you define custom functions for store operations |  | ||||||
| type FakeCustomStore struct { |  | ||||||
| 	AddFunc      func(obj interface{}) error |  | ||||||
| 	UpdateFunc   func(obj interface{}) error |  | ||||||
| 	DeleteFunc   func(obj interface{}) error |  | ||||||
| 	ListFunc     func() []interface{} |  | ||||||
| 	ListKeysFunc func() []string |  | ||||||
| 	GetFunc      func(obj interface{}) (item interface{}, exists bool, err error) |  | ||||||
| 	GetByKeyFunc func(key string) (item interface{}, exists bool, err error) |  | ||||||
| 	ReplaceFunc  func(list []interface{}, resourceVerion string) error |  | ||||||
| 	ResyncFunc   func() error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Add calls the custom Add function if defined |  | ||||||
| func (f *FakeCustomStore) Add(obj interface{}) error { |  | ||||||
| 	if f.AddFunc != nil { |  | ||||||
| 		return f.AddFunc(obj) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Update calls the custom Update function if defined |  | ||||||
| func (f *FakeCustomStore) Update(obj interface{}) error { |  | ||||||
| 	if f.UpdateFunc != nil { |  | ||||||
| 		return f.Update(obj) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Delete calls the custom Delete function if defined |  | ||||||
| func (f *FakeCustomStore) Delete(obj interface{}) error { |  | ||||||
| 	if f.DeleteFunc != nil { |  | ||||||
| 		return f.DeleteFunc(obj) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // List calls the custom List function if defined |  | ||||||
| func (f *FakeCustomStore) List() []interface{} { |  | ||||||
| 	if f.ListFunc != nil { |  | ||||||
| 		return f.ListFunc() |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListKeys calls the custom ListKeys function if defined |  | ||||||
| func (f *FakeCustomStore) ListKeys() []string { |  | ||||||
| 	if f.ListKeysFunc != nil { |  | ||||||
| 		return f.ListKeysFunc() |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Get calls the custom Get function if defined |  | ||||||
| func (f *FakeCustomStore) Get(obj interface{}) (item interface{}, exists bool, err error) { |  | ||||||
| 	if f.GetFunc != nil { |  | ||||||
| 		return f.GetFunc(obj) |  | ||||||
| 	} |  | ||||||
| 	return nil, false, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetByKey calls the custom GetByKey function if defined |  | ||||||
| func (f *FakeCustomStore) GetByKey(key string) (item interface{}, exists bool, err error) { |  | ||||||
| 	if f.GetByKeyFunc != nil { |  | ||||||
| 		return f.GetByKeyFunc(key) |  | ||||||
| 	} |  | ||||||
| 	return nil, false, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Replace calls the custom Replace function if defined |  | ||||||
| func (f *FakeCustomStore) Replace(list []interface{}, resourceVersion string) error { |  | ||||||
| 	if f.ReplaceFunc != nil { |  | ||||||
| 		return f.ReplaceFunc(list, resourceVersion) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Resync calls the custom Resync function if defined |  | ||||||
| func (f *FakeCustomStore) Resync() error { |  | ||||||
| 	if f.ResyncFunc != nil { |  | ||||||
| 		return f.ResyncFunc() |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
							
								
								
									
										321
									
								
								pkg/client/cache/fifo.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										321
									
								
								pkg/client/cache/fifo.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,321 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // PopProcessFunc is passed to Pop() method of Queue interface. |  | ||||||
| // It is supposed to process the element popped from the queue. |  | ||||||
| type PopProcessFunc func(interface{}) error |  | ||||||
|  |  | ||||||
| // ErrRequeue may be returned by a PopProcessFunc to safely requeue |  | ||||||
| // the current item. The value of Err will be returned from Pop. |  | ||||||
| type ErrRequeue struct { |  | ||||||
| 	// Err is returned by the Pop function |  | ||||||
| 	Err error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e ErrRequeue) Error() string { |  | ||||||
| 	if e.Err == nil { |  | ||||||
| 		return "the popped item should be requeued without returning an error" |  | ||||||
| 	} |  | ||||||
| 	return e.Err.Error() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Queue is exactly like a Store, but has a Pop() method too. |  | ||||||
| type Queue interface { |  | ||||||
| 	Store |  | ||||||
|  |  | ||||||
| 	// Pop blocks until it has something to process. |  | ||||||
| 	// It returns the object that was process and the result of processing. |  | ||||||
| 	// The PopProcessFunc may return an ErrRequeue{...} to indicate the item |  | ||||||
| 	// should be requeued before releasing the lock on the queue. |  | ||||||
| 	Pop(PopProcessFunc) (interface{}, error) |  | ||||||
|  |  | ||||||
| 	// AddIfNotPresent adds a value previously |  | ||||||
| 	// returned by Pop back into the queue as long |  | ||||||
| 	// as nothing else (presumably more recent) |  | ||||||
| 	// has since been added. |  | ||||||
| 	AddIfNotPresent(interface{}) error |  | ||||||
|  |  | ||||||
| 	// Return true if the first batch of items has been popped |  | ||||||
| 	HasSynced() bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Helper function for popping from Queue. |  | ||||||
| // WARNING: Do NOT use this function in non-test code to avoid races |  | ||||||
| // unless you really really really really know what you are doing. |  | ||||||
| func Pop(queue Queue) interface{} { |  | ||||||
| 	var result interface{} |  | ||||||
| 	queue.Pop(func(obj interface{}) error { |  | ||||||
| 		result = obj |  | ||||||
| 		return nil |  | ||||||
| 	}) |  | ||||||
| 	return result |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // FIFO receives adds and updates from a Reflector, and puts them in a queue for |  | ||||||
| // FIFO order processing. If multiple adds/updates of a single item happen while |  | ||||||
| // an item is in the queue before it has been processed, it will only be |  | ||||||
| // processed once, and when it is processed, the most recent version will be |  | ||||||
| // processed. This can't be done with a channel. |  | ||||||
| // |  | ||||||
| // FIFO solves this use case: |  | ||||||
| //  * You want to process every object (exactly) once. |  | ||||||
| //  * You want to process the most recent version of the object when you process it. |  | ||||||
| //  * You do not want to process deleted objects, they should be removed from the queue. |  | ||||||
| //  * You do not want to periodically reprocess objects. |  | ||||||
| // Compare with DeltaFIFO for other use cases. |  | ||||||
| type FIFO struct { |  | ||||||
| 	lock sync.RWMutex |  | ||||||
| 	cond sync.Cond |  | ||||||
| 	// We depend on the property that items in the set are in the queue and vice versa. |  | ||||||
| 	items map[string]interface{} |  | ||||||
| 	queue []string |  | ||||||
|  |  | ||||||
| 	// populated is true if the first batch of items inserted by Replace() has been populated |  | ||||||
| 	// or Delete/Add/Update was called first. |  | ||||||
| 	populated bool |  | ||||||
| 	// initialPopulationCount is the number of items inserted by the first call of Replace() |  | ||||||
| 	initialPopulationCount int |  | ||||||
|  |  | ||||||
| 	// keyFunc is used to make the key used for queued item insertion and retrieval, and |  | ||||||
| 	// should be deterministic. |  | ||||||
| 	keyFunc KeyFunc |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	_ = Queue(&FIFO{}) // FIFO is a Queue |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Return true if an Add/Update/Delete/AddIfNotPresent are called first, |  | ||||||
| // or an Update called first but the first batch of items inserted by Replace() has been popped |  | ||||||
| func (f *FIFO) HasSynced() bool { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	return f.populated && f.initialPopulationCount == 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Add inserts an item, and puts it in the queue. The item is only enqueued |  | ||||||
| // if it doesn't already exist in the set. |  | ||||||
| func (f *FIFO) Add(obj interface{}) error { |  | ||||||
| 	id, err := f.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	f.populated = true |  | ||||||
| 	if _, exists := f.items[id]; !exists { |  | ||||||
| 		f.queue = append(f.queue, id) |  | ||||||
| 	} |  | ||||||
| 	f.items[id] = obj |  | ||||||
| 	f.cond.Broadcast() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // AddIfNotPresent inserts an item, and puts it in the queue. If the item is already |  | ||||||
| // present in the set, it is neither enqueued nor added to the set. |  | ||||||
| // |  | ||||||
| // This is useful in a single producer/consumer scenario so that the consumer can |  | ||||||
| // safely retry items without contending with the producer and potentially enqueueing |  | ||||||
| // stale items. |  | ||||||
| func (f *FIFO) AddIfNotPresent(obj interface{}) error { |  | ||||||
| 	id, err := f.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	f.addIfNotPresent(id, obj) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // addIfNotPresent assumes the fifo lock is already held and adds the the provided |  | ||||||
| // item to the queue under id if it does not already exist. |  | ||||||
| func (f *FIFO) addIfNotPresent(id string, obj interface{}) { |  | ||||||
| 	f.populated = true |  | ||||||
| 	if _, exists := f.items[id]; exists { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f.queue = append(f.queue, id) |  | ||||||
| 	f.items[id] = obj |  | ||||||
| 	f.cond.Broadcast() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Update is the same as Add in this implementation. |  | ||||||
| func (f *FIFO) Update(obj interface{}) error { |  | ||||||
| 	return f.Add(obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Delete removes an item. It doesn't add it to the queue, because |  | ||||||
| // this implementation assumes the consumer only cares about the objects, |  | ||||||
| // not the order in which they were created/added. |  | ||||||
| func (f *FIFO) Delete(obj interface{}) error { |  | ||||||
| 	id, err := f.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	f.populated = true |  | ||||||
| 	delete(f.items, id) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // List returns a list of all the items. |  | ||||||
| func (f *FIFO) List() []interface{} { |  | ||||||
| 	f.lock.RLock() |  | ||||||
| 	defer f.lock.RUnlock() |  | ||||||
| 	list := make([]interface{}, 0, len(f.items)) |  | ||||||
| 	for _, item := range f.items { |  | ||||||
| 		list = append(list, item) |  | ||||||
| 	} |  | ||||||
| 	return list |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListKeys returns a list of all the keys of the objects currently |  | ||||||
| // in the FIFO. |  | ||||||
| func (f *FIFO) ListKeys() []string { |  | ||||||
| 	f.lock.RLock() |  | ||||||
| 	defer f.lock.RUnlock() |  | ||||||
| 	list := make([]string, 0, len(f.items)) |  | ||||||
| 	for key := range f.items { |  | ||||||
| 		list = append(list, key) |  | ||||||
| 	} |  | ||||||
| 	return list |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Get returns the requested item, or sets exists=false. |  | ||||||
| func (f *FIFO) Get(obj interface{}) (item interface{}, exists bool, err error) { |  | ||||||
| 	key, err := f.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	return f.GetByKey(key) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetByKey returns the requested item, or sets exists=false. |  | ||||||
| func (f *FIFO) GetByKey(key string) (item interface{}, exists bool, err error) { |  | ||||||
| 	f.lock.RLock() |  | ||||||
| 	defer f.lock.RUnlock() |  | ||||||
| 	item, exists = f.items[key] |  | ||||||
| 	return item, exists, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Pop waits until an item is ready and processes it. If multiple items are |  | ||||||
| // ready, they are returned in the order in which they were added/updated. |  | ||||||
| // The item is removed from the queue (and the store) before it is processed, |  | ||||||
| // so if you don't successfully process it, it should be added back with |  | ||||||
| // AddIfNotPresent(). process function is called under lock, so it is safe |  | ||||||
| // update data structures in it that need to be in sync with the queue. |  | ||||||
| func (f *FIFO) Pop(process PopProcessFunc) (interface{}, error) { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
| 	for { |  | ||||||
| 		for len(f.queue) == 0 { |  | ||||||
| 			f.cond.Wait() |  | ||||||
| 		} |  | ||||||
| 		id := f.queue[0] |  | ||||||
| 		f.queue = f.queue[1:] |  | ||||||
| 		if f.initialPopulationCount > 0 { |  | ||||||
| 			f.initialPopulationCount-- |  | ||||||
| 		} |  | ||||||
| 		item, ok := f.items[id] |  | ||||||
| 		if !ok { |  | ||||||
| 			// Item may have been deleted subsequently. |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		delete(f.items, id) |  | ||||||
| 		err := process(item) |  | ||||||
| 		if e, ok := err.(ErrRequeue); ok { |  | ||||||
| 			f.addIfNotPresent(id, item) |  | ||||||
| 			err = e.Err |  | ||||||
| 		} |  | ||||||
| 		return item, err |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Replace will delete the contents of 'f', using instead the given map. |  | ||||||
| // 'f' takes ownership of the map, you should not reference the map again |  | ||||||
| // after calling this function. f's queue is reset, too; upon return, it |  | ||||||
| // will contain the items in the map, in no particular order. |  | ||||||
| func (f *FIFO) Replace(list []interface{}, resourceVersion string) error { |  | ||||||
| 	items := map[string]interface{}{} |  | ||||||
| 	for _, item := range list { |  | ||||||
| 		key, err := f.keyFunc(item) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return KeyError{item, err} |  | ||||||
| 		} |  | ||||||
| 		items[key] = item |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
|  |  | ||||||
| 	if !f.populated { |  | ||||||
| 		f.populated = true |  | ||||||
| 		f.initialPopulationCount = len(items) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f.items = items |  | ||||||
| 	f.queue = f.queue[:0] |  | ||||||
| 	for id := range items { |  | ||||||
| 		f.queue = append(f.queue, id) |  | ||||||
| 	} |  | ||||||
| 	if len(f.queue) > 0 { |  | ||||||
| 		f.cond.Broadcast() |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Resync will touch all objects to put them into the processing queue |  | ||||||
| func (f *FIFO) Resync() error { |  | ||||||
| 	f.lock.Lock() |  | ||||||
| 	defer f.lock.Unlock() |  | ||||||
|  |  | ||||||
| 	inQueue := sets.NewString() |  | ||||||
| 	for _, id := range f.queue { |  | ||||||
| 		inQueue.Insert(id) |  | ||||||
| 	} |  | ||||||
| 	for id := range f.items { |  | ||||||
| 		if !inQueue.Has(id) { |  | ||||||
| 			f.queue = append(f.queue, id) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if len(f.queue) > 0 { |  | ||||||
| 		f.cond.Broadcast() |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewFIFO returns a Store which can be used to queue up items to |  | ||||||
| // process. |  | ||||||
| func NewFIFO(keyFunc KeyFunc) *FIFO { |  | ||||||
| 	f := &FIFO{ |  | ||||||
| 		items:   map[string]interface{}{}, |  | ||||||
| 		queue:   []string{}, |  | ||||||
| 		keyFunc: keyFunc, |  | ||||||
| 	} |  | ||||||
| 	f.cond.L = &f.lock |  | ||||||
| 	return f |  | ||||||
| } |  | ||||||
							
								
								
									
										280
									
								
								pkg/client/cache/fifo_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										280
									
								
								pkg/client/cache/fifo_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,280 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"reflect" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func testFifoObjectKeyFunc(obj interface{}) (string, error) { |  | ||||||
| 	return obj.(testFifoObject).name, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type testFifoObject struct { |  | ||||||
| 	name string |  | ||||||
| 	val  interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func mkFifoObj(name string, val interface{}) testFifoObject { |  | ||||||
| 	return testFifoObject{name: name, val: val} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFO_basic(t *testing.T) { |  | ||||||
| 	f := NewFIFO(testFifoObjectKeyFunc) |  | ||||||
| 	const amount = 500 |  | ||||||
| 	go func() { |  | ||||||
| 		for i := 0; i < amount; i++ { |  | ||||||
| 			f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1)) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 	go func() { |  | ||||||
| 		for u := uint64(0); u < amount; u++ { |  | ||||||
| 			f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1)) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	lastInt := int(0) |  | ||||||
| 	lastUint := uint64(0) |  | ||||||
| 	for i := 0; i < amount*2; i++ { |  | ||||||
| 		switch obj := Pop(f).(testFifoObject).val.(type) { |  | ||||||
| 		case int: |  | ||||||
| 			if obj <= lastInt { |  | ||||||
| 				t.Errorf("got %v (int) out of order, last was %v", obj, lastInt) |  | ||||||
| 			} |  | ||||||
| 			lastInt = obj |  | ||||||
| 		case uint64: |  | ||||||
| 			if obj <= lastUint { |  | ||||||
| 				t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint) |  | ||||||
| 			} else { |  | ||||||
| 				lastUint = obj |  | ||||||
| 			} |  | ||||||
| 		default: |  | ||||||
| 			t.Fatalf("unexpected type %#v", obj) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFO_requeueOnPop(t *testing.T) { |  | ||||||
| 	f := NewFIFO(testFifoObjectKeyFunc) |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	_, err := f.Pop(func(obj interface{}) error { |  | ||||||
| 		if obj.(testFifoObject).name != "foo" { |  | ||||||
| 			t.Fatalf("unexpected object: %#v", obj) |  | ||||||
| 		} |  | ||||||
| 		return ErrRequeue{Err: nil} |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if _, ok, err := f.GetByKey("foo"); !ok || err != nil { |  | ||||||
| 		t.Fatalf("object should have been requeued: %t %v", ok, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_, err = f.Pop(func(obj interface{}) error { |  | ||||||
| 		if obj.(testFifoObject).name != "foo" { |  | ||||||
| 			t.Fatalf("unexpected object: %#v", obj) |  | ||||||
| 		} |  | ||||||
| 		return ErrRequeue{Err: fmt.Errorf("test error")} |  | ||||||
| 	}) |  | ||||||
| 	if err == nil || err.Error() != "test error" { |  | ||||||
| 		t.Fatalf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if _, ok, err := f.GetByKey("foo"); !ok || err != nil { |  | ||||||
| 		t.Fatalf("object should have been requeued: %t %v", ok, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_, err = f.Pop(func(obj interface{}) error { |  | ||||||
| 		if obj.(testFifoObject).name != "foo" { |  | ||||||
| 			t.Fatalf("unexpected object: %#v", obj) |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if _, ok, err := f.GetByKey("foo"); ok || err != nil { |  | ||||||
| 		t.Fatalf("object should have been removed: %t %v", ok, err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFO_addUpdate(t *testing.T) { |  | ||||||
| 	f := NewFIFO(testFifoObjectKeyFunc) |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Update(mkFifoObj("foo", 15)) |  | ||||||
|  |  | ||||||
| 	if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) { |  | ||||||
| 		t.Errorf("Expected %+v, got %+v", e, a) |  | ||||||
| 	} |  | ||||||
| 	if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) { |  | ||||||
| 		t.Errorf("Expected %+v, got %+v", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	got := make(chan testFifoObject, 2) |  | ||||||
| 	go func() { |  | ||||||
| 		for { |  | ||||||
| 			got <- Pop(f).(testFifoObject) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	first := <-got |  | ||||||
| 	if e, a := 15, first.val; e != a { |  | ||||||
| 		t.Errorf("Didn't get updated value (%v), got %v", e, a) |  | ||||||
| 	} |  | ||||||
| 	select { |  | ||||||
| 	case unexpected := <-got: |  | ||||||
| 		t.Errorf("Got second value %v", unexpected.val) |  | ||||||
| 	case <-time.After(50 * time.Millisecond): |  | ||||||
| 	} |  | ||||||
| 	_, exists, _ := f.Get(mkFifoObj("foo", "")) |  | ||||||
| 	if exists { |  | ||||||
| 		t.Errorf("item did not get removed") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFO_addReplace(t *testing.T) { |  | ||||||
| 	f := NewFIFO(testFifoObjectKeyFunc) |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Replace([]interface{}{mkFifoObj("foo", 15)}, "15") |  | ||||||
| 	got := make(chan testFifoObject, 2) |  | ||||||
| 	go func() { |  | ||||||
| 		for { |  | ||||||
| 			got <- Pop(f).(testFifoObject) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	first := <-got |  | ||||||
| 	if e, a := 15, first.val; e != a { |  | ||||||
| 		t.Errorf("Didn't get updated value (%v), got %v", e, a) |  | ||||||
| 	} |  | ||||||
| 	select { |  | ||||||
| 	case unexpected := <-got: |  | ||||||
| 		t.Errorf("Got second value %v", unexpected.val) |  | ||||||
| 	case <-time.After(50 * time.Millisecond): |  | ||||||
| 	} |  | ||||||
| 	_, exists, _ := f.Get(mkFifoObj("foo", "")) |  | ||||||
| 	if exists { |  | ||||||
| 		t.Errorf("item did not get removed") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFO_detectLineJumpers(t *testing.T) { |  | ||||||
| 	f := NewFIFO(testFifoObjectKeyFunc) |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("foo", 10)) |  | ||||||
| 	f.Add(mkFifoObj("bar", 1)) |  | ||||||
| 	f.Add(mkFifoObj("foo", 11)) |  | ||||||
| 	f.Add(mkFifoObj("foo", 13)) |  | ||||||
| 	f.Add(mkFifoObj("zab", 30)) |  | ||||||
|  |  | ||||||
| 	if e, a := 13, Pop(f).(testFifoObject).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line |  | ||||||
|  |  | ||||||
| 	if e, a := 1, Pop(f).(testFifoObject).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if e, a := 30, Pop(f).(testFifoObject).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if e, a := 14, Pop(f).(testFifoObject).val; a != e { |  | ||||||
| 		t.Fatalf("expected %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFO_addIfNotPresent(t *testing.T) { |  | ||||||
| 	f := NewFIFO(testFifoObjectKeyFunc) |  | ||||||
|  |  | ||||||
| 	f.Add(mkFifoObj("a", 1)) |  | ||||||
| 	f.Add(mkFifoObj("b", 2)) |  | ||||||
| 	f.AddIfNotPresent(mkFifoObj("b", 3)) |  | ||||||
| 	f.AddIfNotPresent(mkFifoObj("c", 4)) |  | ||||||
|  |  | ||||||
| 	if e, a := 3, len(f.items); a != e { |  | ||||||
| 		t.Fatalf("expected queue length %d, got %d", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	expectedValues := []int{1, 2, 4} |  | ||||||
| 	for _, expected := range expectedValues { |  | ||||||
| 		if actual := Pop(f).(testFifoObject).val; actual != expected { |  | ||||||
| 			t.Fatalf("expected value %d, got %d", expected, actual) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFO_HasSynced(t *testing.T) { |  | ||||||
| 	tests := []struct { |  | ||||||
| 		actions        []func(f *FIFO) |  | ||||||
| 		expectedSynced bool |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			actions:        []func(f *FIFO){}, |  | ||||||
| 			expectedSynced: false, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *FIFO){ |  | ||||||
| 				func(f *FIFO) { f.Add(mkFifoObj("a", 1)) }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *FIFO){ |  | ||||||
| 				func(f *FIFO) { f.Replace([]interface{}{}, "0") }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *FIFO){ |  | ||||||
| 				func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: false, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *FIFO){ |  | ||||||
| 				func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, |  | ||||||
| 				func(f *FIFO) { Pop(f) }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: false, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			actions: []func(f *FIFO){ |  | ||||||
| 				func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, |  | ||||||
| 				func(f *FIFO) { Pop(f) }, |  | ||||||
| 				func(f *FIFO) { Pop(f) }, |  | ||||||
| 			}, |  | ||||||
| 			expectedSynced: true, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for i, test := range tests { |  | ||||||
| 		f := NewFIFO(testFifoObjectKeyFunc) |  | ||||||
|  |  | ||||||
| 		for _, action := range test.actions { |  | ||||||
| 			action(f) |  | ||||||
| 		} |  | ||||||
| 		if e, a := test.expectedSynced, f.HasSynced(); a != e { |  | ||||||
| 			t.Errorf("test case %v failed, expected: %v , got %v", i, e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										85
									
								
								pkg/client/cache/index.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										85
									
								
								pkg/client/cache/index.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,85 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/meta" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Indexer is a storage interface that lets you list objects using multiple indexing functions |  | ||||||
| type Indexer interface { |  | ||||||
| 	Store |  | ||||||
| 	// Retrieve list of objects that match on the named indexing function |  | ||||||
| 	Index(indexName string, obj interface{}) ([]interface{}, error) |  | ||||||
| 	// ListIndexFuncValues returns the list of generated values of an Index func |  | ||||||
| 	ListIndexFuncValues(indexName string) []string |  | ||||||
| 	// ByIndex lists object that match on the named indexing function with the exact key |  | ||||||
| 	ByIndex(indexName, indexKey string) ([]interface{}, error) |  | ||||||
| 	// GetIndexer return the indexers |  | ||||||
| 	GetIndexers() Indexers |  | ||||||
|  |  | ||||||
| 	// AddIndexers adds more indexers to this store.  If you call this after you already have data |  | ||||||
| 	// in the store, the results are undefined. |  | ||||||
| 	AddIndexers(newIndexers Indexers) error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IndexFunc knows how to provide an indexed value for an object. |  | ||||||
| type IndexFunc func(obj interface{}) ([]string, error) |  | ||||||
|  |  | ||||||
| // IndexFuncToKeyFuncAdapter adapts an indexFunc to a keyFunc.  This is only useful if your index function returns |  | ||||||
| // unique values for every object.  This is conversion can create errors when more than one key is found.  You |  | ||||||
| // should prefer to make proper key and index functions. |  | ||||||
| func IndexFuncToKeyFuncAdapter(indexFunc IndexFunc) KeyFunc { |  | ||||||
| 	return func(obj interface{}) (string, error) { |  | ||||||
| 		indexKeys, err := indexFunc(obj) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return "", err |  | ||||||
| 		} |  | ||||||
| 		if len(indexKeys) > 1 { |  | ||||||
| 			return "", fmt.Errorf("too many keys: %v", indexKeys) |  | ||||||
| 		} |  | ||||||
| 		if len(indexKeys) == 0 { |  | ||||||
| 			return "", fmt.Errorf("unexpected empty indexKeys") |  | ||||||
| 		} |  | ||||||
| 		return indexKeys[0], nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	NamespaceIndex string = "namespace" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // MetaNamespaceIndexFunc is a default index function that indexes based on an object's namespace |  | ||||||
| func MetaNamespaceIndexFunc(obj interface{}) ([]string, error) { |  | ||||||
| 	meta, err := meta.Accessor(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return []string{""}, fmt.Errorf("object has no meta: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return []string{meta.GetNamespace()}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Index maps the indexed value to a set of keys in the store that match on that value |  | ||||||
| type Index map[string]sets.String |  | ||||||
|  |  | ||||||
| // Indexers maps a name to a IndexFunc |  | ||||||
| type Indexers map[string]IndexFunc |  | ||||||
|  |  | ||||||
| // Indices maps a name to an Index |  | ||||||
| type Indices map[string]Index |  | ||||||
							
								
								
									
										137
									
								
								pkg/client/cache/index_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										137
									
								
								pkg/client/cache/index_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,137 +0,0 @@ | |||||||
| /* |  | ||||||
| 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"strings" |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func testIndexFunc(obj interface{}) ([]string, error) { |  | ||||||
| 	pod := obj.(*v1.Pod) |  | ||||||
| 	return []string{pod.Labels["foo"]}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetIndexFuncValues(t *testing.T) { |  | ||||||
| 	index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"testmodes": testIndexFunc}) |  | ||||||
|  |  | ||||||
| 	pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Labels: map[string]string{"foo": "bar"}}} |  | ||||||
| 	pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Labels: map[string]string{"foo": "bar"}}} |  | ||||||
| 	pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Labels: map[string]string{"foo": "biz"}}} |  | ||||||
|  |  | ||||||
| 	index.Add(pod1) |  | ||||||
| 	index.Add(pod2) |  | ||||||
| 	index.Add(pod3) |  | ||||||
|  |  | ||||||
| 	keys := index.ListIndexFuncValues("testmodes") |  | ||||||
| 	if len(keys) != 2 { |  | ||||||
| 		t.Errorf("Expected 2 keys but got %v", len(keys)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, key := range keys { |  | ||||||
| 		if key != "bar" && key != "biz" { |  | ||||||
| 			t.Errorf("Expected only 'bar' or 'biz' but got %s", key) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func testUsersIndexFunc(obj interface{}) ([]string, error) { |  | ||||||
| 	pod := obj.(*v1.Pod) |  | ||||||
| 	usersString := pod.Annotations["users"] |  | ||||||
|  |  | ||||||
| 	return strings.Split(usersString, ","), nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestMultiIndexKeys(t *testing.T) { |  | ||||||
| 	index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"byUser": testUsersIndexFunc}) |  | ||||||
|  |  | ||||||
| 	pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Annotations: map[string]string{"users": "ernie,bert"}}} |  | ||||||
| 	pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Annotations: map[string]string{"users": "bert,oscar"}}} |  | ||||||
| 	pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Annotations: map[string]string{"users": "ernie,elmo"}}} |  | ||||||
|  |  | ||||||
| 	index.Add(pod1) |  | ||||||
| 	index.Add(pod2) |  | ||||||
| 	index.Add(pod3) |  | ||||||
|  |  | ||||||
| 	erniePods, err := index.ByIndex("byUser", "ernie") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(erniePods) != 2 { |  | ||||||
| 		t.Errorf("Expected 2 pods but got %v", len(erniePods)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bertPods, err := index.ByIndex("byUser", "bert") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(bertPods) != 2 { |  | ||||||
| 		t.Errorf("Expected 2 pods but got %v", len(bertPods)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	oscarPods, err := index.ByIndex("byUser", "oscar") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(oscarPods) != 1 { |  | ||||||
| 		t.Errorf("Expected 1 pods but got %v", len(erniePods)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ernieAndBertKeys, err := index.Index("byUser", pod1) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(ernieAndBertKeys) != 3 { |  | ||||||
| 		t.Errorf("Expected 3 pods but got %v", len(ernieAndBertKeys)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	index.Delete(pod3) |  | ||||||
| 	erniePods, err = index.ByIndex("byUser", "ernie") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(erniePods) != 1 { |  | ||||||
| 		t.Errorf("Expected 1 pods but got %v", len(erniePods)) |  | ||||||
| 	} |  | ||||||
| 	elmoPods, err := index.ByIndex("byUser", "elmo") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(elmoPods) != 0 { |  | ||||||
| 		t.Errorf("Expected 0 pods but got %v", len(elmoPods)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	obj, err := api.Scheme.DeepCopy(pod2) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	copyOfPod2 := obj.(*v1.Pod) |  | ||||||
| 	copyOfPod2.Annotations["users"] = "oscar" |  | ||||||
| 	index.Update(copyOfPod2) |  | ||||||
| 	bertPods, err = index.ByIndex("byUser", "bert") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(bertPods) != 1 { |  | ||||||
| 		t.Errorf("Expected 1 pods but got %v", len(bertPods)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										160
									
								
								pkg/client/cache/listers.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										160
									
								
								pkg/client/cache/listers.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,160 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/golang/glog" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/errors" |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/meta" |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/labels" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // AppendFunc is used to add a matching item to whatever list the caller is using |  | ||||||
| type AppendFunc func(interface{}) |  | ||||||
|  |  | ||||||
| func ListAll(store Store, selector labels.Selector, appendFn AppendFunc) error { |  | ||||||
| 	for _, m := range store.List() { |  | ||||||
| 		metadata, err := meta.Accessor(m) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if selector.Matches(labels.Set(metadata.GetLabels())) { |  | ||||||
| 			appendFn(m) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func ListAllByNamespace(indexer Indexer, namespace string, selector labels.Selector, appendFn AppendFunc) error { |  | ||||||
| 	if namespace == metav1.NamespaceAll { |  | ||||||
| 		for _, m := range indexer.List() { |  | ||||||
| 			metadata, err := meta.Accessor(m) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 			if selector.Matches(labels.Set(metadata.GetLabels())) { |  | ||||||
| 				appendFn(m) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	items, err := indexer.Index(NamespaceIndex, &metav1.ObjectMeta{Namespace: namespace}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		// Ignore error; do slow search without index. |  | ||||||
| 		glog.Warningf("can not retrieve list of objects using index : %v", err) |  | ||||||
| 		for _, m := range indexer.List() { |  | ||||||
| 			metadata, err := meta.Accessor(m) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 			if metadata.GetNamespace() == namespace && selector.Matches(labels.Set(metadata.GetLabels())) { |  | ||||||
| 				appendFn(m) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	for _, m := range items { |  | ||||||
| 		metadata, err := meta.Accessor(m) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if selector.Matches(labels.Set(metadata.GetLabels())) { |  | ||||||
| 			appendFn(m) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GenericLister is a lister skin on a generic Indexer |  | ||||||
| type GenericLister interface { |  | ||||||
| 	// List will return all objects across namespaces |  | ||||||
| 	List(selector labels.Selector) (ret []runtime.Object, err error) |  | ||||||
| 	// Get will attempt to retrieve assuming that name==key |  | ||||||
| 	Get(name string) (runtime.Object, error) |  | ||||||
| 	// ByNamespace will give you a GenericNamespaceLister for one namespace |  | ||||||
| 	ByNamespace(namespace string) GenericNamespaceLister |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GenericNamespaceLister is a lister skin on a generic Indexer |  | ||||||
| type GenericNamespaceLister interface { |  | ||||||
| 	// List will return all objects in this namespace |  | ||||||
| 	List(selector labels.Selector) (ret []runtime.Object, err error) |  | ||||||
| 	// Get will attempt to retrieve by namespace and name |  | ||||||
| 	Get(name string) (runtime.Object, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewGenericLister(indexer Indexer, resource schema.GroupResource) GenericLister { |  | ||||||
| 	return &genericLister{indexer: indexer, resource: resource} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type genericLister struct { |  | ||||||
| 	indexer  Indexer |  | ||||||
| 	resource schema.GroupResource |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *genericLister) List(selector labels.Selector) (ret []runtime.Object, err error) { |  | ||||||
| 	err = ListAll(s.indexer, selector, func(m interface{}) { |  | ||||||
| 		ret = append(ret, m.(runtime.Object)) |  | ||||||
| 	}) |  | ||||||
| 	return ret, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *genericLister) ByNamespace(namespace string) GenericNamespaceLister { |  | ||||||
| 	return &genericNamespaceLister{indexer: s.indexer, namespace: namespace, resource: s.resource} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *genericLister) Get(name string) (runtime.Object, error) { |  | ||||||
| 	obj, exists, err := s.indexer.GetByKey(name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	if !exists { |  | ||||||
| 		return nil, errors.NewNotFound(s.resource, name) |  | ||||||
| 	} |  | ||||||
| 	return obj.(runtime.Object), nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type genericNamespaceLister struct { |  | ||||||
| 	indexer   Indexer |  | ||||||
| 	namespace string |  | ||||||
| 	resource  schema.GroupResource |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *genericNamespaceLister) List(selector labels.Selector) (ret []runtime.Object, err error) { |  | ||||||
| 	err = ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { |  | ||||||
| 		ret = append(ret, m.(runtime.Object)) |  | ||||||
| 	}) |  | ||||||
| 	return ret, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *genericNamespaceLister) Get(name string) (runtime.Object, error) { |  | ||||||
| 	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	if !exists { |  | ||||||
| 		return nil, errors.NewNotFound(s.resource, name) |  | ||||||
| 	} |  | ||||||
| 	return obj.(runtime.Object), nil |  | ||||||
| } |  | ||||||
							
								
								
									
										162
									
								
								pkg/client/cache/listwatch.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										162
									
								
								pkg/client/cache/listwatch.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,162 +0,0 @@ | |||||||
| /* |  | ||||||
| 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/meta" |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/fields" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/watch" |  | ||||||
| 	restclient "k8s.io/client-go/rest" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // ListerWatcher is any object that knows how to perform an initial list and start a watch on a resource. |  | ||||||
| type ListerWatcher interface { |  | ||||||
| 	// List should return a list type object; the Items field will be extracted, and the |  | ||||||
| 	// ResourceVersion field will be used to start the watch in the right place. |  | ||||||
| 	List(options metav1.ListOptions) (runtime.Object, error) |  | ||||||
| 	// Watch should begin a watch at the specified version. |  | ||||||
| 	Watch(options metav1.ListOptions) (watch.Interface, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListFunc knows how to list resources |  | ||||||
| type ListFunc func(options metav1.ListOptions) (runtime.Object, error) |  | ||||||
|  |  | ||||||
| // WatchFunc knows how to watch resources |  | ||||||
| type WatchFunc func(options metav1.ListOptions) (watch.Interface, error) |  | ||||||
|  |  | ||||||
| // ListWatch knows how to list and watch a set of apiserver resources.  It satisfies the ListerWatcher interface. |  | ||||||
| // It is a convenience function for users of NewReflector, etc. |  | ||||||
| // ListFunc and WatchFunc must not be nil |  | ||||||
| type ListWatch struct { |  | ||||||
| 	ListFunc  ListFunc |  | ||||||
| 	WatchFunc WatchFunc |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Getter interface knows how to access Get method from RESTClient. |  | ||||||
| type Getter interface { |  | ||||||
| 	Get() *restclient.Request |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace and field selector. |  | ||||||
| func NewListWatchFromClient(c Getter, resource string, namespace string, fieldSelector fields.Selector) *ListWatch { |  | ||||||
| 	listFunc := func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 		return c.Get(). |  | ||||||
| 			Namespace(namespace). |  | ||||||
| 			Resource(resource). |  | ||||||
| 			VersionedParams(&options, metav1.ParameterCodec). |  | ||||||
| 			FieldsSelectorParam(fieldSelector). |  | ||||||
| 			Do(). |  | ||||||
| 			Get() |  | ||||||
| 	} |  | ||||||
| 	watchFunc := func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 		return c.Get(). |  | ||||||
| 			Prefix("watch"). |  | ||||||
| 			Namespace(namespace). |  | ||||||
| 			Resource(resource). |  | ||||||
| 			VersionedParams(&options, metav1.ParameterCodec). |  | ||||||
| 			FieldsSelectorParam(fieldSelector). |  | ||||||
| 			Watch() |  | ||||||
| 	} |  | ||||||
| 	return &ListWatch{ListFunc: listFunc, WatchFunc: watchFunc} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func timeoutFromListOptions(options metav1.ListOptions) time.Duration { |  | ||||||
| 	if options.TimeoutSeconds != nil { |  | ||||||
| 		return time.Duration(*options.TimeoutSeconds) * time.Second |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // List a set of apiserver resources |  | ||||||
| func (lw *ListWatch) List(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 	return lw.ListFunc(options) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Watch a set of apiserver resources |  | ||||||
| func (lw *ListWatch) Watch(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 	return lw.WatchFunc(options) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TODO: check for watch expired error and retry watch from latest point?  Same issue exists for Until. |  | ||||||
| func ListWatchUntil(timeout time.Duration, lw ListerWatcher, conditions ...watch.ConditionFunc) (*watch.Event, error) { |  | ||||||
| 	if len(conditions) == 0 { |  | ||||||
| 		return nil, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	list, err := lw.List(metav1.ListOptions{}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	initialItems, err := meta.ExtractList(list) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// use the initial items as simulated "adds" |  | ||||||
| 	var lastEvent *watch.Event |  | ||||||
| 	currIndex := 0 |  | ||||||
| 	passedConditions := 0 |  | ||||||
| 	for _, condition := range conditions { |  | ||||||
| 		// check the next condition against the previous event and short circuit waiting for the next watch |  | ||||||
| 		if lastEvent != nil { |  | ||||||
| 			done, err := condition(*lastEvent) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return lastEvent, err |  | ||||||
| 			} |  | ||||||
| 			if done { |  | ||||||
| 				passedConditions = passedConditions + 1 |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 	ConditionSucceeded: |  | ||||||
| 		for currIndex < len(initialItems) { |  | ||||||
| 			lastEvent = &watch.Event{Type: watch.Added, Object: initialItems[currIndex]} |  | ||||||
| 			currIndex++ |  | ||||||
|  |  | ||||||
| 			done, err := condition(*lastEvent) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return lastEvent, err |  | ||||||
| 			} |  | ||||||
| 			if done { |  | ||||||
| 				passedConditions = passedConditions + 1 |  | ||||||
| 				break ConditionSucceeded |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if passedConditions == len(conditions) { |  | ||||||
| 		return lastEvent, nil |  | ||||||
| 	} |  | ||||||
| 	remainingConditions := conditions[passedConditions:] |  | ||||||
|  |  | ||||||
| 	metaObj, err := meta.ListAccessor(list) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	currResourceVersion := metaObj.GetResourceVersion() |  | ||||||
|  |  | ||||||
| 	watchInterface, err := lw.Watch(metav1.ListOptions{ResourceVersion: currResourceVersion}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return watch.Until(timeout, watchInterface, remainingConditions...) |  | ||||||
| } |  | ||||||
							
								
								
									
										135
									
								
								pkg/client/cache/mutation_detector.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										135
									
								
								pkg/client/cache/mutation_detector.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,135 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2016 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strconv" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/diff" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var mutationDetectionEnabled = false |  | ||||||
|  |  | ||||||
| func init() { |  | ||||||
| 	mutationDetectionEnabled, _ = strconv.ParseBool(os.Getenv("KUBE_CACHE_MUTATION_DETECTOR")) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type CacheMutationDetector interface { |  | ||||||
| 	AddObject(obj interface{}) |  | ||||||
| 	Run(stopCh <-chan struct{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewCacheMutationDetector(name string) CacheMutationDetector { |  | ||||||
| 	if !mutationDetectionEnabled { |  | ||||||
| 		return dummyMutationDetector{} |  | ||||||
| 	} |  | ||||||
| 	return &defaultCacheMutationDetector{name: name, period: 1 * time.Second} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type dummyMutationDetector struct{} |  | ||||||
|  |  | ||||||
| func (dummyMutationDetector) Run(stopCh <-chan struct{}) { |  | ||||||
| } |  | ||||||
| func (dummyMutationDetector) AddObject(obj interface{}) { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // defaultCacheMutationDetector gives a way to detect if a cached object has been mutated |  | ||||||
| // It has a list of cached objects and their copies.  I haven't thought of a way |  | ||||||
| // to see WHO is mutating it, just that it's getting mutated. |  | ||||||
| type defaultCacheMutationDetector struct { |  | ||||||
| 	name   string |  | ||||||
| 	period time.Duration |  | ||||||
|  |  | ||||||
| 	lock       sync.Mutex |  | ||||||
| 	cachedObjs []cacheObj |  | ||||||
|  |  | ||||||
| 	// failureFunc is injectable for unit testing.  If you don't have it, the process will panic. |  | ||||||
| 	// This panic is intentional, since turning on this detection indicates you want a strong |  | ||||||
| 	// failure signal.  This failure is effectively a p0 bug and you can't trust process results |  | ||||||
| 	// after a mutation anyway. |  | ||||||
| 	failureFunc func(message string) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // cacheObj holds the actual object and a copy |  | ||||||
| type cacheObj struct { |  | ||||||
| 	cached interface{} |  | ||||||
| 	copied interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *defaultCacheMutationDetector) Run(stopCh <-chan struct{}) { |  | ||||||
| 	// we DON'T want protection from panics.  If we're running this code, we want to die |  | ||||||
| 	go func() { |  | ||||||
| 		for { |  | ||||||
| 			d.CompareObjects() |  | ||||||
|  |  | ||||||
| 			select { |  | ||||||
| 			case <-stopCh: |  | ||||||
| 				return |  | ||||||
| 			case <-time.After(d.period): |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // AddObject makes a deep copy of the object for later comparison.  It only works on runtime.Object |  | ||||||
| // but that covers the vast majority of our cached objects |  | ||||||
| func (d *defaultCacheMutationDetector) AddObject(obj interface{}) { |  | ||||||
| 	if _, ok := obj.(DeletedFinalStateUnknown); ok { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if _, ok := obj.(runtime.Object); !ok { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	copiedObj, err := api.Scheme.Copy(obj.(runtime.Object)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	d.lock.Lock() |  | ||||||
| 	defer d.lock.Unlock() |  | ||||||
| 	d.cachedObjs = append(d.cachedObjs, cacheObj{cached: obj, copied: copiedObj}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *defaultCacheMutationDetector) CompareObjects() { |  | ||||||
| 	d.lock.Lock() |  | ||||||
| 	defer d.lock.Unlock() |  | ||||||
|  |  | ||||||
| 	altered := false |  | ||||||
| 	for i, obj := range d.cachedObjs { |  | ||||||
| 		if !reflect.DeepEqual(obj.cached, obj.copied) { |  | ||||||
| 			fmt.Printf("CACHE %s[%d] ALTERED!\n%v\n", d.name, i, diff.ObjectDiff(obj.cached, obj.copied)) |  | ||||||
| 			altered = true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if altered { |  | ||||||
| 		msg := fmt.Sprintf("cache %s modified", d.name) |  | ||||||
| 		if d.failureFunc != nil { |  | ||||||
| 			d.failureFunc(msg) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		panic(msg) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										81
									
								
								pkg/client/cache/mutation_detector_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										81
									
								
								pkg/client/cache/mutation_detector_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,81 +0,0 @@ | |||||||
| // +build !race |  | ||||||
|  |  | ||||||
| /* |  | ||||||
| Copyright 2016 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/watch" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestMutationDetector(t *testing.T) { |  | ||||||
| 	fakeWatch := watch.NewFake() |  | ||||||
| 	lw := &testLW{ |  | ||||||
| 		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 			return fakeWatch, nil |  | ||||||
| 		}, |  | ||||||
| 		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 			return &v1.PodList{}, nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	pod := &v1.Pod{ |  | ||||||
| 		ObjectMeta: metav1.ObjectMeta{ |  | ||||||
| 			Name:   "anything", |  | ||||||
| 			Labels: map[string]string{"check": "foo"}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	stopCh := make(chan struct{}) |  | ||||||
| 	defer close(stopCh) |  | ||||||
| 	addReceived := make(chan bool) |  | ||||||
| 	mutationFound := make(chan bool) |  | ||||||
|  |  | ||||||
| 	informer := NewSharedInformer(lw, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer) |  | ||||||
| 	informer.cacheMutationDetector = &defaultCacheMutationDetector{ |  | ||||||
| 		name:   "name", |  | ||||||
| 		period: 1 * time.Second, |  | ||||||
| 		failureFunc: func(message string) { |  | ||||||
| 			mutationFound <- true |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	informer.AddEventHandler( |  | ||||||
| 		ResourceEventHandlerFuncs{ |  | ||||||
| 			AddFunc: func(obj interface{}) { |  | ||||||
| 				addReceived <- true |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	) |  | ||||||
| 	go informer.Run(stopCh) |  | ||||||
|  |  | ||||||
| 	fakeWatch.Add(pod) |  | ||||||
|  |  | ||||||
| 	select { |  | ||||||
| 	case <-addReceived: |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	pod.Labels["change"] = "true" |  | ||||||
|  |  | ||||||
| 	select { |  | ||||||
| 	case <-mutationFound: |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										48
									
								
								pkg/client/cache/processor_listener_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										48
									
								
								pkg/client/cache/processor_listener_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,48 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2016 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // TestPopReleaseLock tests that when processor listener blocks on chan, |  | ||||||
| // it should release the lock for pendingNotifications. |  | ||||||
| func TestPopReleaseLock(t *testing.T) { |  | ||||||
| 	pl := newProcessListener(nil) |  | ||||||
| 	stopCh := make(chan struct{}) |  | ||||||
| 	defer close(stopCh) |  | ||||||
| 	// make pop() block on nextCh: waiting for receiver to get notification. |  | ||||||
| 	pl.add(1) |  | ||||||
| 	go pl.pop(stopCh) |  | ||||||
|  |  | ||||||
| 	resultCh := make(chan struct{}) |  | ||||||
| 	go func() { |  | ||||||
| 		pl.lock.Lock() |  | ||||||
| 		close(resultCh) |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	select { |  | ||||||
| 	case <-resultCh: |  | ||||||
| 	case <-time.After(wait.ForeverTestTimeout): |  | ||||||
| 		t.Errorf("Timeout after %v", wait.ForeverTestTimeout) |  | ||||||
| 	} |  | ||||||
| 	pl.lock.Unlock() |  | ||||||
| } |  | ||||||
							
								
								
									
										417
									
								
								pkg/client/cache/reflector.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										417
									
								
								pkg/client/cache/reflector.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,417 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"math/rand" |  | ||||||
| 	"net" |  | ||||||
| 	"net/url" |  | ||||||
| 	"reflect" |  | ||||||
| 	"regexp" |  | ||||||
| 	goruntime "runtime" |  | ||||||
| 	"runtime/debug" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"syscall" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" |  | ||||||
| 	apierrs "k8s.io/apimachinery/pkg/api/errors" |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/meta" |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" |  | ||||||
| 	"k8s.io/apimachinery/pkg/watch" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Reflector watches a specified resource and causes all changes to be reflected in the given store. |  | ||||||
| type Reflector struct { |  | ||||||
| 	// name identifies this reflector. By default it will be a file:line if possible. |  | ||||||
| 	name string |  | ||||||
|  |  | ||||||
| 	// The type of object we expect to place in the store. |  | ||||||
| 	expectedType reflect.Type |  | ||||||
| 	// The destination to sync up with the watch source |  | ||||||
| 	store Store |  | ||||||
| 	// listerWatcher is used to perform lists and watches. |  | ||||||
| 	listerWatcher ListerWatcher |  | ||||||
| 	// period controls timing between one watch ending and |  | ||||||
| 	// the beginning of the next one. |  | ||||||
| 	period       time.Duration |  | ||||||
| 	resyncPeriod time.Duration |  | ||||||
| 	// now() returns current time - exposed for testing purposes |  | ||||||
| 	now func() time.Time |  | ||||||
| 	// lastSyncResourceVersion is the resource version token last |  | ||||||
| 	// observed when doing a sync with the underlying store |  | ||||||
| 	// it is thread safe, but not synchronized with the underlying store |  | ||||||
| 	lastSyncResourceVersion string |  | ||||||
| 	// lastSyncResourceVersionMutex guards read/write access to lastSyncResourceVersion |  | ||||||
| 	lastSyncResourceVersionMutex sync.RWMutex |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	// We try to spread the load on apiserver by setting timeouts for |  | ||||||
| 	// watch requests - it is random in [minWatchTimeout, 2*minWatchTimeout]. |  | ||||||
| 	// However, it can be modified to avoid periodic resync to break the |  | ||||||
| 	// TCP connection. |  | ||||||
| 	minWatchTimeout = 5 * time.Minute |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // NewNamespaceKeyedIndexerAndReflector creates an Indexer and a Reflector |  | ||||||
| // The indexer is configured to key on namespace |  | ||||||
| func NewNamespaceKeyedIndexerAndReflector(lw ListerWatcher, expectedType interface{}, resyncPeriod time.Duration) (indexer Indexer, reflector *Reflector) { |  | ||||||
| 	indexer = NewIndexer(MetaNamespaceKeyFunc, Indexers{"namespace": MetaNamespaceIndexFunc}) |  | ||||||
| 	reflector = NewReflector(lw, expectedType, indexer, resyncPeriod) |  | ||||||
| 	return indexer, reflector |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewReflector creates a new Reflector object which will keep the given store up to |  | ||||||
| // date with the server's contents for the given resource. Reflector promises to |  | ||||||
| // only put things in the store that have the type of expectedType, unless expectedType |  | ||||||
| // is nil. If resyncPeriod is non-zero, then lists will be executed after every |  | ||||||
| // resyncPeriod, so that you can use reflectors to periodically process everything as |  | ||||||
| // well as incrementally processing the things that change. |  | ||||||
| func NewReflector(lw ListerWatcher, expectedType interface{}, store Store, resyncPeriod time.Duration) *Reflector { |  | ||||||
| 	return NewNamedReflector(getDefaultReflectorName(internalPackages...), lw, expectedType, store, resyncPeriod) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewNamedReflector same as NewReflector, but with a specified name for logging |  | ||||||
| func NewNamedReflector(name string, lw ListerWatcher, expectedType interface{}, store Store, resyncPeriod time.Duration) *Reflector { |  | ||||||
| 	r := &Reflector{ |  | ||||||
| 		name:          name, |  | ||||||
| 		listerWatcher: lw, |  | ||||||
| 		store:         store, |  | ||||||
| 		expectedType:  reflect.TypeOf(expectedType), |  | ||||||
| 		period:        time.Second, |  | ||||||
| 		resyncPeriod:  resyncPeriod, |  | ||||||
| 		now:           time.Now, |  | ||||||
| 	} |  | ||||||
| 	return r |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // internalPackages are packages that ignored when creating a default reflector name. These packages are in the common |  | ||||||
| // call chains to NewReflector, so they'd be low entropy names for reflectors |  | ||||||
| var internalPackages = []string{"kubernetes/pkg/client/cache/", "/runtime/asm_"} |  | ||||||
|  |  | ||||||
| // getDefaultReflectorName walks back through the call stack until we find a caller from outside of the ignoredPackages |  | ||||||
| // it returns back a shortpath/filename:line to aid in identification of this reflector when it starts logging |  | ||||||
| func getDefaultReflectorName(ignoredPackages ...string) string { |  | ||||||
| 	name := "????" |  | ||||||
| 	const maxStack = 10 |  | ||||||
| 	for i := 1; i < maxStack; i++ { |  | ||||||
| 		_, file, line, ok := goruntime.Caller(i) |  | ||||||
| 		if !ok { |  | ||||||
| 			file, line, ok = extractStackCreator() |  | ||||||
| 			if !ok { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			i += maxStack |  | ||||||
| 		} |  | ||||||
| 		if hasPackage(file, ignoredPackages) { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		file = trimPackagePrefix(file) |  | ||||||
| 		name = fmt.Sprintf("%s:%d", file, line) |  | ||||||
| 		break |  | ||||||
| 	} |  | ||||||
| 	return name |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // hasPackage returns true if the file is in one of the ignored packages. |  | ||||||
| func hasPackage(file string, ignoredPackages []string) bool { |  | ||||||
| 	for _, ignoredPackage := range ignoredPackages { |  | ||||||
| 		if strings.Contains(file, ignoredPackage) { |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // trimPackagePrefix reduces duplicate values off the front of a package name. |  | ||||||
| func trimPackagePrefix(file string) string { |  | ||||||
| 	if l := strings.LastIndex(file, "k8s.io/kubernetes/pkg/"); l >= 0 { |  | ||||||
| 		return file[l+len("k8s.io/kubernetes/"):] |  | ||||||
| 	} |  | ||||||
| 	if l := strings.LastIndex(file, "/src/"); l >= 0 { |  | ||||||
| 		return file[l+5:] |  | ||||||
| 	} |  | ||||||
| 	if l := strings.LastIndex(file, "/pkg/"); l >= 0 { |  | ||||||
| 		return file[l+1:] |  | ||||||
| 	} |  | ||||||
| 	return file |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var stackCreator = regexp.MustCompile(`(?m)^created by (.*)\n\s+(.*):(\d+) \+0x[[:xdigit:]]+$`) |  | ||||||
|  |  | ||||||
| // extractStackCreator retrieves the goroutine file and line that launched this stack. Returns false |  | ||||||
| // if the creator cannot be located. |  | ||||||
| // TODO: Go does not expose this via runtime https://github.com/golang/go/issues/11440 |  | ||||||
| func extractStackCreator() (string, int, bool) { |  | ||||||
| 	stack := debug.Stack() |  | ||||||
| 	matches := stackCreator.FindStringSubmatch(string(stack)) |  | ||||||
| 	if matches == nil || len(matches) != 4 { |  | ||||||
| 		return "", 0, false |  | ||||||
| 	} |  | ||||||
| 	line, err := strconv.Atoi(matches[3]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", 0, false |  | ||||||
| 	} |  | ||||||
| 	return matches[2], line, true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Run starts a watch and handles watch events. Will restart the watch if it is closed. |  | ||||||
| // Run starts a goroutine and returns immediately. |  | ||||||
| func (r *Reflector) Run() { |  | ||||||
| 	glog.V(3).Infof("Starting reflector %v (%s) from %s", r.expectedType, r.resyncPeriod, r.name) |  | ||||||
| 	go wait.Until(func() { |  | ||||||
| 		if err := r.ListAndWatch(wait.NeverStop); err != nil { |  | ||||||
| 			utilruntime.HandleError(err) |  | ||||||
| 		} |  | ||||||
| 	}, r.period, wait.NeverStop) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RunUntil starts a watch and handles watch events. Will restart the watch if it is closed. |  | ||||||
| // RunUntil starts a goroutine and returns immediately. It will exit when stopCh is closed. |  | ||||||
| func (r *Reflector) RunUntil(stopCh <-chan struct{}) { |  | ||||||
| 	glog.V(3).Infof("Starting reflector %v (%s) from %s", r.expectedType, r.resyncPeriod, r.name) |  | ||||||
| 	go wait.Until(func() { |  | ||||||
| 		if err := r.ListAndWatch(stopCh); err != nil { |  | ||||||
| 			utilruntime.HandleError(err) |  | ||||||
| 		} |  | ||||||
| 	}, r.period, stopCh) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	// nothing will ever be sent down this channel |  | ||||||
| 	neverExitWatch <-chan time.Time = make(chan time.Time) |  | ||||||
|  |  | ||||||
| 	// Used to indicate that watching stopped so that a resync could happen. |  | ||||||
| 	errorResyncRequested = errors.New("resync channel fired") |  | ||||||
|  |  | ||||||
| 	// Used to indicate that watching stopped because of a signal from the stop |  | ||||||
| 	// channel passed in from a client of the reflector. |  | ||||||
| 	errorStopRequested = errors.New("Stop requested") |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // resyncChan returns a channel which will receive something when a resync is |  | ||||||
| // required, and a cleanup function. |  | ||||||
| func (r *Reflector) resyncChan() (<-chan time.Time, func() bool) { |  | ||||||
| 	if r.resyncPeriod == 0 { |  | ||||||
| 		return neverExitWatch, func() bool { return false } |  | ||||||
| 	} |  | ||||||
| 	// The cleanup function is required: imagine the scenario where watches |  | ||||||
| 	// always fail so we end up listing frequently. Then, if we don't |  | ||||||
| 	// manually stop the timer, we could end up with many timers active |  | ||||||
| 	// concurrently. |  | ||||||
| 	t := time.NewTimer(r.resyncPeriod) |  | ||||||
| 	return t.C, t.Stop |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListAndWatch first lists all items and get the resource version at the moment of call, |  | ||||||
| // and then use the resource version to watch. |  | ||||||
| // It returns error if ListAndWatch didn't even try to initialize watch. |  | ||||||
| func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error { |  | ||||||
| 	glog.V(3).Infof("Listing and watching %v from %s", r.expectedType, r.name) |  | ||||||
| 	var resourceVersion string |  | ||||||
| 	resyncCh, cleanup := r.resyncChan() |  | ||||||
| 	defer cleanup() |  | ||||||
|  |  | ||||||
| 	// Explicitly set "0" as resource version - it's fine for the List() |  | ||||||
| 	// to be served from cache and potentially be delayed relative to |  | ||||||
| 	// etcd contents. Reflector framework will catch up via Watch() eventually. |  | ||||||
| 	options := metav1.ListOptions{ResourceVersion: "0"} |  | ||||||
| 	list, err := r.listerWatcher.List(options) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("%s: Failed to list %v: %v", r.name, r.expectedType, err) |  | ||||||
| 	} |  | ||||||
| 	listMetaInterface, err := meta.ListAccessor(list) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("%s: Unable to understand list result %#v: %v", r.name, list, err) |  | ||||||
| 	} |  | ||||||
| 	resourceVersion = listMetaInterface.GetResourceVersion() |  | ||||||
| 	items, err := meta.ExtractList(list) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("%s: Unable to understand list result %#v (%v)", r.name, list, err) |  | ||||||
| 	} |  | ||||||
| 	if err := r.syncWith(items, resourceVersion); err != nil { |  | ||||||
| 		return fmt.Errorf("%s: Unable to sync list result: %v", r.name, err) |  | ||||||
| 	} |  | ||||||
| 	r.setLastSyncResourceVersion(resourceVersion) |  | ||||||
|  |  | ||||||
| 	resyncerrc := make(chan error, 1) |  | ||||||
| 	cancelCh := make(chan struct{}) |  | ||||||
| 	defer close(cancelCh) |  | ||||||
| 	go func() { |  | ||||||
| 		for { |  | ||||||
| 			select { |  | ||||||
| 			case <-resyncCh: |  | ||||||
| 			case <-stopCh: |  | ||||||
| 				return |  | ||||||
| 			case <-cancelCh: |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			glog.V(4).Infof("%s: forcing resync", r.name) |  | ||||||
| 			if err := r.store.Resync(); err != nil { |  | ||||||
| 				resyncerrc <- err |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			cleanup() |  | ||||||
| 			resyncCh, cleanup = r.resyncChan() |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		timemoutseconds := int64(minWatchTimeout.Seconds() * (rand.Float64() + 1.0)) |  | ||||||
| 		options = metav1.ListOptions{ |  | ||||||
| 			ResourceVersion: resourceVersion, |  | ||||||
| 			// We want to avoid situations of hanging watchers. Stop any wachers that do not |  | ||||||
| 			// receive any events within the timeout window. |  | ||||||
| 			TimeoutSeconds: &timemoutseconds, |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		w, err := r.listerWatcher.Watch(options) |  | ||||||
| 		if err != nil { |  | ||||||
| 			switch err { |  | ||||||
| 			case io.EOF: |  | ||||||
| 				// watch closed normally |  | ||||||
| 			case io.ErrUnexpectedEOF: |  | ||||||
| 				glog.V(1).Infof("%s: Watch for %v closed with unexpected EOF: %v", r.name, r.expectedType, err) |  | ||||||
| 			default: |  | ||||||
| 				utilruntime.HandleError(fmt.Errorf("%s: Failed to watch %v: %v", r.name, r.expectedType, err)) |  | ||||||
| 			} |  | ||||||
| 			// If this is "connection refused" error, it means that most likely apiserver is not responsive. |  | ||||||
| 			// It doesn't make sense to re-list all objects because most likely we will be able to restart |  | ||||||
| 			// watch where we ended. |  | ||||||
| 			// If that's the case wait and resend watch request. |  | ||||||
| 			if urlError, ok := err.(*url.Error); ok { |  | ||||||
| 				if opError, ok := urlError.Err.(*net.OpError); ok { |  | ||||||
| 					if errno, ok := opError.Err.(syscall.Errno); ok && errno == syscall.ECONNREFUSED { |  | ||||||
| 						time.Sleep(time.Second) |  | ||||||
| 						continue |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if err := r.watchHandler(w, &resourceVersion, resyncerrc, stopCh); err != nil { |  | ||||||
| 			if err != errorStopRequested { |  | ||||||
| 				glog.Warningf("%s: watch of %v ended with: %v", r.name, r.expectedType, err) |  | ||||||
| 			} |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // syncWith replaces the store's items with the given list. |  | ||||||
| func (r *Reflector) syncWith(items []runtime.Object, resourceVersion string) error { |  | ||||||
| 	found := make([]interface{}, 0, len(items)) |  | ||||||
| 	for _, item := range items { |  | ||||||
| 		found = append(found, item) |  | ||||||
| 	} |  | ||||||
| 	return r.store.Replace(found, resourceVersion) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // watchHandler watches w and keeps *resourceVersion up to date. |  | ||||||
| func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *string, errc chan error, stopCh <-chan struct{}) error { |  | ||||||
| 	start := time.Now() |  | ||||||
| 	eventCount := 0 |  | ||||||
|  |  | ||||||
| 	// Stopping the watcher should be idempotent and if we return from this function there's no way |  | ||||||
| 	// we're coming back in with the same watch interface. |  | ||||||
| 	defer w.Stop() |  | ||||||
|  |  | ||||||
| loop: |  | ||||||
| 	for { |  | ||||||
| 		select { |  | ||||||
| 		case <-stopCh: |  | ||||||
| 			return errorStopRequested |  | ||||||
| 		case err := <-errc: |  | ||||||
| 			return err |  | ||||||
| 		case event, ok := <-w.ResultChan(): |  | ||||||
| 			if !ok { |  | ||||||
| 				break loop |  | ||||||
| 			} |  | ||||||
| 			if event.Type == watch.Error { |  | ||||||
| 				return apierrs.FromObject(event.Object) |  | ||||||
| 			} |  | ||||||
| 			if e, a := r.expectedType, reflect.TypeOf(event.Object); e != nil && e != a { |  | ||||||
| 				utilruntime.HandleError(fmt.Errorf("%s: expected type %v, but watch event object had type %v", r.name, e, a)) |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			meta, err := meta.Accessor(event.Object) |  | ||||||
| 			if err != nil { |  | ||||||
| 				utilruntime.HandleError(fmt.Errorf("%s: unable to understand watch event %#v", r.name, event)) |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			newResourceVersion := meta.GetResourceVersion() |  | ||||||
| 			switch event.Type { |  | ||||||
| 			case watch.Added: |  | ||||||
| 				err := r.store.Add(event.Object) |  | ||||||
| 				if err != nil { |  | ||||||
| 					utilruntime.HandleError(fmt.Errorf("%s: unable to add watch event object (%#v) to store: %v", r.name, event.Object, err)) |  | ||||||
| 				} |  | ||||||
| 			case watch.Modified: |  | ||||||
| 				err := r.store.Update(event.Object) |  | ||||||
| 				if err != nil { |  | ||||||
| 					utilruntime.HandleError(fmt.Errorf("%s: unable to update watch event object (%#v) to store: %v", r.name, event.Object, err)) |  | ||||||
| 				} |  | ||||||
| 			case watch.Deleted: |  | ||||||
| 				// TODO: Will any consumers need access to the "last known |  | ||||||
| 				// state", which is passed in event.Object? If so, may need |  | ||||||
| 				// to change this. |  | ||||||
| 				err := r.store.Delete(event.Object) |  | ||||||
| 				if err != nil { |  | ||||||
| 					utilruntime.HandleError(fmt.Errorf("%s: unable to delete watch event object (%#v) from store: %v", r.name, event.Object, err)) |  | ||||||
| 				} |  | ||||||
| 			default: |  | ||||||
| 				utilruntime.HandleError(fmt.Errorf("%s: unable to understand watch event %#v", r.name, event)) |  | ||||||
| 			} |  | ||||||
| 			*resourceVersion = newResourceVersion |  | ||||||
| 			r.setLastSyncResourceVersion(newResourceVersion) |  | ||||||
| 			eventCount++ |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	watchDuration := time.Now().Sub(start) |  | ||||||
| 	if watchDuration < 1*time.Second && eventCount == 0 { |  | ||||||
| 		glog.V(4).Infof("%s: Unexpected watch close - watch lasted less than a second and no items received", r.name) |  | ||||||
| 		return errors.New("very short watch") |  | ||||||
| 	} |  | ||||||
| 	glog.V(4).Infof("%s: Watch close - %v total %v items received", r.name, r.expectedType, eventCount) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // LastSyncResourceVersion is the resource version observed when last sync with the underlying store |  | ||||||
| // The value returned is not synchronized with access to the underlying store and is not thread-safe |  | ||||||
| func (r *Reflector) LastSyncResourceVersion() string { |  | ||||||
| 	r.lastSyncResourceVersionMutex.RLock() |  | ||||||
| 	defer r.lastSyncResourceVersionMutex.RUnlock() |  | ||||||
| 	return r.lastSyncResourceVersion |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *Reflector) setLastSyncResourceVersion(v string) { |  | ||||||
| 	r.lastSyncResourceVersionMutex.Lock() |  | ||||||
| 	defer r.lastSyncResourceVersionMutex.Unlock() |  | ||||||
| 	r.lastSyncResourceVersion = v |  | ||||||
| } |  | ||||||
							
								
								
									
										389
									
								
								pkg/client/cache/reflector_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										389
									
								
								pkg/client/cache/reflector_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,389 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"math/rand" |  | ||||||
| 	"strconv" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" |  | ||||||
| 	"k8s.io/apimachinery/pkg/watch" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var nevererrc chan error |  | ||||||
|  |  | ||||||
| type testLW struct { |  | ||||||
| 	ListFunc  func(options metav1.ListOptions) (runtime.Object, error) |  | ||||||
| 	WatchFunc func(options metav1.ListOptions) (watch.Interface, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *testLW) List(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 	return t.ListFunc(options) |  | ||||||
| } |  | ||||||
| func (t *testLW) Watch(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 	return t.WatchFunc(options) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCloseWatchChannelOnError(t *testing.T) { |  | ||||||
| 	r := NewReflector(&testLW{}, &v1.Pod{}, NewStore(MetaNamespaceKeyFunc), 0) |  | ||||||
| 	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}} |  | ||||||
| 	fw := watch.NewFake() |  | ||||||
| 	r.listerWatcher = &testLW{ |  | ||||||
| 		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 			return fw, nil |  | ||||||
| 		}, |  | ||||||
| 		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	go r.ListAndWatch(wait.NeverStop) |  | ||||||
| 	fw.Error(pod) |  | ||||||
| 	select { |  | ||||||
| 	case _, ok := <-fw.ResultChan(): |  | ||||||
| 		if ok { |  | ||||||
| 			t.Errorf("Watch channel left open after cancellation") |  | ||||||
| 		} |  | ||||||
| 	case <-time.After(wait.ForeverTestTimeout): |  | ||||||
| 		t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String()) |  | ||||||
| 		break |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRunUntil(t *testing.T) { |  | ||||||
| 	stopCh := make(chan struct{}) |  | ||||||
| 	store := NewStore(MetaNamespaceKeyFunc) |  | ||||||
| 	r := NewReflector(&testLW{}, &v1.Pod{}, store, 0) |  | ||||||
| 	fw := watch.NewFake() |  | ||||||
| 	r.listerWatcher = &testLW{ |  | ||||||
| 		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 			return fw, nil |  | ||||||
| 		}, |  | ||||||
| 		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	r.RunUntil(stopCh) |  | ||||||
| 	// Synchronously add a dummy pod into the watch channel so we |  | ||||||
| 	// know the RunUntil go routine is in the watch handler. |  | ||||||
| 	fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}) |  | ||||||
| 	close(stopCh) |  | ||||||
| 	select { |  | ||||||
| 	case _, ok := <-fw.ResultChan(): |  | ||||||
| 		if ok { |  | ||||||
| 			t.Errorf("Watch channel left open after stopping the watch") |  | ||||||
| 		} |  | ||||||
| 	case <-time.After(wait.ForeverTestTimeout): |  | ||||||
| 		t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String()) |  | ||||||
| 		break |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReflectorResyncChan(t *testing.T) { |  | ||||||
| 	s := NewStore(MetaNamespaceKeyFunc) |  | ||||||
| 	g := NewReflector(&testLW{}, &v1.Pod{}, s, time.Millisecond) |  | ||||||
| 	a, _ := g.resyncChan() |  | ||||||
| 	b := time.After(wait.ForeverTestTimeout) |  | ||||||
| 	select { |  | ||||||
| 	case <-a: |  | ||||||
| 		t.Logf("got timeout as expected") |  | ||||||
| 	case <-b: |  | ||||||
| 		t.Errorf("resyncChan() is at least 99 milliseconds late??") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func BenchmarkReflectorResyncChanMany(b *testing.B) { |  | ||||||
| 	s := NewStore(MetaNamespaceKeyFunc) |  | ||||||
| 	g := NewReflector(&testLW{}, &v1.Pod{}, s, 25*time.Millisecond) |  | ||||||
| 	// The improvement to this (calling the timer's Stop() method) makes |  | ||||||
| 	// this benchmark about 40% faster. |  | ||||||
| 	for i := 0; i < b.N; i++ { |  | ||||||
| 		g.resyncPeriod = time.Duration(rand.Float64() * float64(time.Millisecond) * 25) |  | ||||||
| 		_, stop := g.resyncChan() |  | ||||||
| 		stop() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReflectorWatchHandlerError(t *testing.T) { |  | ||||||
| 	s := NewStore(MetaNamespaceKeyFunc) |  | ||||||
| 	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0) |  | ||||||
| 	fw := watch.NewFake() |  | ||||||
| 	go func() { |  | ||||||
| 		fw.Stop() |  | ||||||
| 	}() |  | ||||||
| 	var resumeRV string |  | ||||||
| 	err := g.watchHandler(fw, &resumeRV, nevererrc, wait.NeverStop) |  | ||||||
| 	if err == nil { |  | ||||||
| 		t.Errorf("unexpected non-error") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReflectorWatchHandler(t *testing.T) { |  | ||||||
| 	s := NewStore(MetaNamespaceKeyFunc) |  | ||||||
| 	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0) |  | ||||||
| 	fw := watch.NewFake() |  | ||||||
| 	s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) |  | ||||||
| 	s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}) |  | ||||||
| 	go func() { |  | ||||||
| 		fw.Add(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "rejected"}}) |  | ||||||
| 		fw.Delete(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) |  | ||||||
| 		fw.Modify(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "55"}}) |  | ||||||
| 		fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "baz", ResourceVersion: "32"}}) |  | ||||||
| 		fw.Stop() |  | ||||||
| 	}() |  | ||||||
| 	var resumeRV string |  | ||||||
| 	err := g.watchHandler(fw, &resumeRV, nevererrc, wait.NeverStop) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("unexpected error %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mkPod := func(id string, rv string) *v1.Pod { |  | ||||||
| 		return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	table := []struct { |  | ||||||
| 		Pod    *v1.Pod |  | ||||||
| 		exists bool |  | ||||||
| 	}{ |  | ||||||
| 		{mkPod("foo", ""), false}, |  | ||||||
| 		{mkPod("rejected", ""), false}, |  | ||||||
| 		{mkPod("bar", "55"), true}, |  | ||||||
| 		{mkPod("baz", "32"), true}, |  | ||||||
| 	} |  | ||||||
| 	for _, item := range table { |  | ||||||
| 		obj, exists, _ := s.Get(item.Pod) |  | ||||||
| 		if e, a := item.exists, exists; e != a { |  | ||||||
| 			t.Errorf("%v: expected %v, got %v", item.Pod, e, a) |  | ||||||
| 		} |  | ||||||
| 		if !exists { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if e, a := item.Pod.ResourceVersion, obj.(*v1.Pod).ResourceVersion; e != a { |  | ||||||
| 			t.Errorf("%v: expected %v, got %v", item.Pod, e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// RV should send the last version we see. |  | ||||||
| 	if e, a := "32", resumeRV; e != a { |  | ||||||
| 		t.Errorf("expected %v, got %v", e, a) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// last sync resource version should be the last version synced with store |  | ||||||
| 	if e, a := "32", g.LastSyncResourceVersion(); e != a { |  | ||||||
| 		t.Errorf("expected %v, got %v", e, a) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReflectorStopWatch(t *testing.T) { |  | ||||||
| 	s := NewStore(MetaNamespaceKeyFunc) |  | ||||||
| 	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0) |  | ||||||
| 	fw := watch.NewFake() |  | ||||||
| 	var resumeRV string |  | ||||||
| 	stopWatch := make(chan struct{}, 1) |  | ||||||
| 	stopWatch <- struct{}{} |  | ||||||
| 	err := g.watchHandler(fw, &resumeRV, nevererrc, stopWatch) |  | ||||||
| 	if err != errorStopRequested { |  | ||||||
| 		t.Errorf("expected stop error, got %q", err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReflectorListAndWatch(t *testing.T) { |  | ||||||
| 	createdFakes := make(chan *watch.FakeWatcher) |  | ||||||
|  |  | ||||||
| 	// The ListFunc says that it's at revision 1. Therefore, we expect our WatchFunc |  | ||||||
| 	// to get called at the beginning of the watch with 1, and again with 3 when we |  | ||||||
| 	// inject an error. |  | ||||||
| 	expectedRVs := []string{"1", "3"} |  | ||||||
| 	lw := &testLW{ |  | ||||||
| 		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 			rv := options.ResourceVersion |  | ||||||
| 			fw := watch.NewFake() |  | ||||||
| 			if e, a := expectedRVs[0], rv; e != a { |  | ||||||
| 				t.Errorf("Expected rv %v, but got %v", e, a) |  | ||||||
| 			} |  | ||||||
| 			expectedRVs = expectedRVs[1:] |  | ||||||
| 			// channel is not buffered because the for loop below needs to block. But |  | ||||||
| 			// we don't want to block here, so report the new fake via a go routine. |  | ||||||
| 			go func() { createdFakes <- fw }() |  | ||||||
| 			return fw, nil |  | ||||||
| 		}, |  | ||||||
| 		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	s := NewFIFO(MetaNamespaceKeyFunc) |  | ||||||
| 	r := NewReflector(lw, &v1.Pod{}, s, 0) |  | ||||||
| 	go r.ListAndWatch(wait.NeverStop) |  | ||||||
|  |  | ||||||
| 	ids := []string{"foo", "bar", "baz", "qux", "zoo"} |  | ||||||
| 	var fw *watch.FakeWatcher |  | ||||||
| 	for i, id := range ids { |  | ||||||
| 		if fw == nil { |  | ||||||
| 			fw = <-createdFakes |  | ||||||
| 		} |  | ||||||
| 		sendingRV := strconv.FormatUint(uint64(i+2), 10) |  | ||||||
| 		fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: sendingRV}}) |  | ||||||
| 		if sendingRV == "3" { |  | ||||||
| 			// Inject a failure. |  | ||||||
| 			fw.Stop() |  | ||||||
| 			fw = nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Verify we received the right ids with the right resource versions. |  | ||||||
| 	for i, id := range ids { |  | ||||||
| 		pod := Pop(s).(*v1.Pod) |  | ||||||
| 		if e, a := id, pod.Name; e != a { |  | ||||||
| 			t.Errorf("%v: Expected %v, got %v", i, e, a) |  | ||||||
| 		} |  | ||||||
| 		if e, a := strconv.FormatUint(uint64(i+2), 10), pod.ResourceVersion; e != a { |  | ||||||
| 			t.Errorf("%v: Expected %v, got %v", i, e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(expectedRVs) != 0 { |  | ||||||
| 		t.Error("called watchStarter an unexpected number of times") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReflectorListAndWatchWithErrors(t *testing.T) { |  | ||||||
| 	mkPod := func(id string, rv string) *v1.Pod { |  | ||||||
| 		return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}} |  | ||||||
| 	} |  | ||||||
| 	mkList := func(rv string, pods ...*v1.Pod) *v1.PodList { |  | ||||||
| 		list := &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: rv}} |  | ||||||
| 		for _, pod := range pods { |  | ||||||
| 			list.Items = append(list.Items, *pod) |  | ||||||
| 		} |  | ||||||
| 		return list |  | ||||||
| 	} |  | ||||||
| 	table := []struct { |  | ||||||
| 		list     *v1.PodList |  | ||||||
| 		listErr  error |  | ||||||
| 		events   []watch.Event |  | ||||||
| 		watchErr error |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			list: mkList("1"), |  | ||||||
| 			events: []watch.Event{ |  | ||||||
| 				{Type: watch.Added, Object: mkPod("foo", "2")}, |  | ||||||
| 				{Type: watch.Added, Object: mkPod("bar", "3")}, |  | ||||||
| 			}, |  | ||||||
| 		}, { |  | ||||||
| 			list: mkList("3", mkPod("foo", "2"), mkPod("bar", "3")), |  | ||||||
| 			events: []watch.Event{ |  | ||||||
| 				{Type: watch.Deleted, Object: mkPod("foo", "4")}, |  | ||||||
| 				{Type: watch.Added, Object: mkPod("qux", "5")}, |  | ||||||
| 			}, |  | ||||||
| 		}, { |  | ||||||
| 			listErr: fmt.Errorf("a list error"), |  | ||||||
| 		}, { |  | ||||||
| 			list:     mkList("5", mkPod("bar", "3"), mkPod("qux", "5")), |  | ||||||
| 			watchErr: fmt.Errorf("a watch error"), |  | ||||||
| 		}, { |  | ||||||
| 			list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")), |  | ||||||
| 			events: []watch.Event{ |  | ||||||
| 				{Type: watch.Added, Object: mkPod("baz", "6")}, |  | ||||||
| 			}, |  | ||||||
| 		}, { |  | ||||||
| 			list: mkList("6", mkPod("bar", "3"), mkPod("qux", "5"), mkPod("baz", "6")), |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	s := NewFIFO(MetaNamespaceKeyFunc) |  | ||||||
| 	for line, item := range table { |  | ||||||
| 		if item.list != nil { |  | ||||||
| 			// Test that the list is what currently exists in the store. |  | ||||||
| 			current := s.List() |  | ||||||
| 			checkMap := map[string]string{} |  | ||||||
| 			for _, item := range current { |  | ||||||
| 				pod := item.(*v1.Pod) |  | ||||||
| 				checkMap[pod.Name] = pod.ResourceVersion |  | ||||||
| 			} |  | ||||||
| 			for _, pod := range item.list.Items { |  | ||||||
| 				if e, a := pod.ResourceVersion, checkMap[pod.Name]; e != a { |  | ||||||
| 					t.Errorf("%v: expected %v, got %v for pod %v", line, e, a, pod.Name) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if e, a := len(item.list.Items), len(checkMap); e != a { |  | ||||||
| 				t.Errorf("%v: expected %v, got %v", line, e, a) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		watchRet, watchErr := item.events, item.watchErr |  | ||||||
| 		lw := &testLW{ |  | ||||||
| 			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 				if watchErr != nil { |  | ||||||
| 					return nil, watchErr |  | ||||||
| 				} |  | ||||||
| 				watchErr = fmt.Errorf("second watch") |  | ||||||
| 				fw := watch.NewFake() |  | ||||||
| 				go func() { |  | ||||||
| 					for _, e := range watchRet { |  | ||||||
| 						fw.Action(e.Type, e.Object) |  | ||||||
| 					} |  | ||||||
| 					fw.Stop() |  | ||||||
| 				}() |  | ||||||
| 				return fw, nil |  | ||||||
| 			}, |  | ||||||
| 			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 				return item.list, item.listErr |  | ||||||
| 			}, |  | ||||||
| 		} |  | ||||||
| 		r := NewReflector(lw, &v1.Pod{}, s, 0) |  | ||||||
| 		r.ListAndWatch(wait.NeverStop) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReflectorResync(t *testing.T) { |  | ||||||
| 	iteration := 0 |  | ||||||
| 	stopCh := make(chan struct{}) |  | ||||||
| 	rerr := errors.New("expected resync reached") |  | ||||||
| 	s := &FakeCustomStore{ |  | ||||||
| 		ResyncFunc: func() error { |  | ||||||
| 			iteration++ |  | ||||||
| 			if iteration == 2 { |  | ||||||
| 				return rerr |  | ||||||
| 			} |  | ||||||
| 			return nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	lw := &testLW{ |  | ||||||
| 		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { |  | ||||||
| 			fw := watch.NewFake() |  | ||||||
| 			return fw, nil |  | ||||||
| 		}, |  | ||||||
| 		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { |  | ||||||
| 			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "0"}}, nil |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	resyncPeriod := 1 * time.Millisecond |  | ||||||
| 	r := NewReflector(lw, &v1.Pod{}, s, resyncPeriod) |  | ||||||
| 	if err := r.ListAndWatch(stopCh); err != nil { |  | ||||||
| 		// error from Resync is not propaged up to here. |  | ||||||
| 		t.Errorf("expected error %v", err) |  | ||||||
| 	} |  | ||||||
| 	if iteration != 2 { |  | ||||||
| 		t.Errorf("exactly 2 iterations were expected, got: %v", iteration) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										421
									
								
								pkg/client/cache/shared_informer.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										421
									
								
								pkg/client/cache/shared_informer.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,421 +0,0 @@ | |||||||
| /* |  | ||||||
| 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" |  | ||||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" |  | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // if you use this, there is one behavior change compared to a standard Informer. |  | ||||||
| // When you receive a notification, the cache will be AT LEAST as fresh as the |  | ||||||
| // notification, but it MAY be more fresh.  You should NOT depend on the contents |  | ||||||
| // of the cache exactly matching the notification you've received in handler |  | ||||||
| // functions.  If there was a create, followed by a delete, the cache may NOT |  | ||||||
| // have your item.  This has advantages over the broadcaster since it allows us |  | ||||||
| // to share a common cache across many controllers. Extending the broadcaster |  | ||||||
| // would have required us keep duplicate caches for each watch. |  | ||||||
| type SharedInformer interface { |  | ||||||
| 	// events to a single handler are delivered sequentially, but there is no coordination between different handlers |  | ||||||
| 	// You may NOT add a handler *after* the SharedInformer is running.  That will result in an error being returned. |  | ||||||
| 	// TODO we should try to remove this restriction eventually. |  | ||||||
| 	AddEventHandler(handler ResourceEventHandler) error |  | ||||||
| 	GetStore() Store |  | ||||||
| 	// GetController gives back a synthetic interface that "votes" to start the informer |  | ||||||
| 	GetController() Controller |  | ||||||
| 	Run(stopCh <-chan struct{}) |  | ||||||
| 	HasSynced() bool |  | ||||||
| 	LastSyncResourceVersion() string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type SharedIndexInformer interface { |  | ||||||
| 	SharedInformer |  | ||||||
| 	// AddIndexers add indexers to the informer before it starts. |  | ||||||
| 	AddIndexers(indexers Indexers) error |  | ||||||
| 	GetIndexer() Indexer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewSharedInformer creates a new instance for the listwatcher. |  | ||||||
| // TODO: create a cache/factory of these at a higher level for the list all, watch all of a given resource that can |  | ||||||
| // be shared amongst all consumers. |  | ||||||
| func NewSharedInformer(lw ListerWatcher, objType runtime.Object, resyncPeriod time.Duration) SharedInformer { |  | ||||||
| 	return NewSharedIndexInformer(lw, objType, resyncPeriod, Indexers{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewSharedIndexInformer creates a new instance for the listwatcher. |  | ||||||
| // TODO: create a cache/factory of these at a higher level for the list all, watch all of a given resource that can |  | ||||||
| // be shared amongst all consumers. |  | ||||||
| func NewSharedIndexInformer(lw ListerWatcher, objType runtime.Object, resyncPeriod time.Duration, indexers Indexers) SharedIndexInformer { |  | ||||||
| 	sharedIndexInformer := &sharedIndexInformer{ |  | ||||||
| 		processor:             &sharedProcessor{}, |  | ||||||
| 		indexer:               NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers), |  | ||||||
| 		listerWatcher:         lw, |  | ||||||
| 		objectType:            objType, |  | ||||||
| 		fullResyncPeriod:      resyncPeriod, |  | ||||||
| 		cacheMutationDetector: NewCacheMutationDetector(fmt.Sprintf("%T", objType)), |  | ||||||
| 	} |  | ||||||
| 	return sharedIndexInformer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // InformerSynced is a function that can be used to determine if an informer has synced.  This is useful for determining if caches have synced. |  | ||||||
| type InformerSynced func() bool |  | ||||||
|  |  | ||||||
| // syncedPollPeriod controls how often you look at the status of your sync funcs |  | ||||||
| const syncedPollPeriod = 100 * time.Millisecond |  | ||||||
|  |  | ||||||
| // WaitForCacheSync waits for caches to populate.  It returns true if it was successful, false |  | ||||||
| // if the contoller should shutdown |  | ||||||
| func WaitForCacheSync(stopCh <-chan struct{}, cacheSyncs ...InformerSynced) bool { |  | ||||||
| 	err := wait.PollUntil(syncedPollPeriod, |  | ||||||
| 		func() (bool, error) { |  | ||||||
| 			for _, syncFunc := range cacheSyncs { |  | ||||||
| 				if !syncFunc() { |  | ||||||
| 					return false, nil |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			return true, nil |  | ||||||
| 		}, |  | ||||||
| 		stopCh) |  | ||||||
| 	if err != nil { |  | ||||||
| 		glog.V(2).Infof("stop requested") |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	glog.V(4).Infof("caches populated") |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type sharedIndexInformer struct { |  | ||||||
| 	indexer    Indexer |  | ||||||
| 	controller Controller |  | ||||||
|  |  | ||||||
| 	processor             *sharedProcessor |  | ||||||
| 	cacheMutationDetector CacheMutationDetector |  | ||||||
|  |  | ||||||
| 	// This block is tracked to handle late initialization of the controller |  | ||||||
| 	listerWatcher    ListerWatcher |  | ||||||
| 	objectType       runtime.Object |  | ||||||
| 	fullResyncPeriod time.Duration |  | ||||||
|  |  | ||||||
| 	started     bool |  | ||||||
| 	startedLock sync.Mutex |  | ||||||
|  |  | ||||||
| 	// blockDeltas gives a way to stop all event distribution so that a late event handler |  | ||||||
| 	// can safely join the shared informer. |  | ||||||
| 	blockDeltas sync.Mutex |  | ||||||
| 	// stopCh is the channel used to stop the main Run process.  We have to track it so that |  | ||||||
| 	// late joiners can have a proper stop |  | ||||||
| 	stopCh <-chan struct{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // dummyController hides the fact that a SharedInformer is different from a dedicated one |  | ||||||
| // where a caller can `Run`.  The run method is disonnected in this case, because higher |  | ||||||
| // level logic will decide when to start the SharedInformer and related controller. |  | ||||||
| // Because returning information back is always asynchronous, the legacy callers shouldn't |  | ||||||
| // notice any change in behavior. |  | ||||||
| type dummyController struct { |  | ||||||
| 	informer *sharedIndexInformer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (v *dummyController) Run(stopCh <-chan struct{}) { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (v *dummyController) HasSynced() bool { |  | ||||||
| 	return v.informer.HasSynced() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *dummyController) LastSyncResourceVersion() string { |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type updateNotification struct { |  | ||||||
| 	oldObj interface{} |  | ||||||
| 	newObj interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type addNotification struct { |  | ||||||
| 	newObj interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type deleteNotification struct { |  | ||||||
| 	oldObj interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) { |  | ||||||
| 	defer utilruntime.HandleCrash() |  | ||||||
|  |  | ||||||
| 	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, nil, s.indexer) |  | ||||||
|  |  | ||||||
| 	cfg := &Config{ |  | ||||||
| 		Queue:            fifo, |  | ||||||
| 		ListerWatcher:    s.listerWatcher, |  | ||||||
| 		ObjectType:       s.objectType, |  | ||||||
| 		FullResyncPeriod: s.fullResyncPeriod, |  | ||||||
| 		RetryOnError:     false, |  | ||||||
|  |  | ||||||
| 		Process: s.HandleDeltas, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	func() { |  | ||||||
| 		s.startedLock.Lock() |  | ||||||
| 		defer s.startedLock.Unlock() |  | ||||||
|  |  | ||||||
| 		s.controller = New(cfg) |  | ||||||
| 		s.started = true |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	s.stopCh = stopCh |  | ||||||
| 	s.cacheMutationDetector.Run(stopCh) |  | ||||||
| 	s.processor.run(stopCh) |  | ||||||
| 	s.controller.Run(stopCh) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) isStarted() bool { |  | ||||||
| 	s.startedLock.Lock() |  | ||||||
| 	defer s.startedLock.Unlock() |  | ||||||
| 	return s.started |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) HasSynced() bool { |  | ||||||
| 	s.startedLock.Lock() |  | ||||||
| 	defer s.startedLock.Unlock() |  | ||||||
|  |  | ||||||
| 	if s.controller == nil { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	return s.controller.HasSynced() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) LastSyncResourceVersion() string { |  | ||||||
| 	s.startedLock.Lock() |  | ||||||
| 	defer s.startedLock.Unlock() |  | ||||||
|  |  | ||||||
| 	if s.controller == nil { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	return s.controller.LastSyncResourceVersion() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) GetStore() Store { |  | ||||||
| 	return s.indexer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) GetIndexer() Indexer { |  | ||||||
| 	return s.indexer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) AddIndexers(indexers Indexers) error { |  | ||||||
| 	s.startedLock.Lock() |  | ||||||
| 	defer s.startedLock.Unlock() |  | ||||||
|  |  | ||||||
| 	if s.started { |  | ||||||
| 		return fmt.Errorf("informer has already started") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return s.indexer.AddIndexers(indexers) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) GetController() Controller { |  | ||||||
| 	return &dummyController{informer: s} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) AddEventHandler(handler ResourceEventHandler) error { |  | ||||||
| 	s.startedLock.Lock() |  | ||||||
| 	defer s.startedLock.Unlock() |  | ||||||
|  |  | ||||||
| 	if !s.started { |  | ||||||
| 		listener := newProcessListener(handler) |  | ||||||
| 		s.processor.listeners = append(s.processor.listeners, listener) |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// in order to safely join, we have to |  | ||||||
| 	// 1. stop sending add/update/delete notifications |  | ||||||
| 	// 2. do a list against the store |  | ||||||
| 	// 3. send synthetic "Add" events to the new handler |  | ||||||
| 	// 4. unblock |  | ||||||
| 	s.blockDeltas.Lock() |  | ||||||
| 	defer s.blockDeltas.Unlock() |  | ||||||
|  |  | ||||||
| 	listener := newProcessListener(handler) |  | ||||||
| 	s.processor.listeners = append(s.processor.listeners, listener) |  | ||||||
|  |  | ||||||
| 	go listener.run(s.stopCh) |  | ||||||
| 	go listener.pop(s.stopCh) |  | ||||||
|  |  | ||||||
| 	items := s.indexer.List() |  | ||||||
| 	for i := range items { |  | ||||||
| 		listener.add(addNotification{newObj: items[i]}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error { |  | ||||||
| 	s.blockDeltas.Lock() |  | ||||||
| 	defer s.blockDeltas.Unlock() |  | ||||||
|  |  | ||||||
| 	// from oldest to newest |  | ||||||
| 	for _, d := range obj.(Deltas) { |  | ||||||
| 		switch d.Type { |  | ||||||
| 		case Sync, Added, Updated: |  | ||||||
| 			s.cacheMutationDetector.AddObject(d.Object) |  | ||||||
| 			if old, exists, err := s.indexer.Get(d.Object); err == nil && exists { |  | ||||||
| 				if err := s.indexer.Update(d.Object); err != nil { |  | ||||||
| 					return err |  | ||||||
| 				} |  | ||||||
| 				s.processor.distribute(updateNotification{oldObj: old, newObj: d.Object}) |  | ||||||
| 			} else { |  | ||||||
| 				if err := s.indexer.Add(d.Object); err != nil { |  | ||||||
| 					return err |  | ||||||
| 				} |  | ||||||
| 				s.processor.distribute(addNotification{newObj: d.Object}) |  | ||||||
| 			} |  | ||||||
| 		case Deleted: |  | ||||||
| 			if err := s.indexer.Delete(d.Object); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 			s.processor.distribute(deleteNotification{oldObj: d.Object}) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type sharedProcessor struct { |  | ||||||
| 	listeners []*processorListener |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *sharedProcessor) distribute(obj interface{}) { |  | ||||||
| 	for _, listener := range p.listeners { |  | ||||||
| 		listener.add(obj) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *sharedProcessor) run(stopCh <-chan struct{}) { |  | ||||||
| 	for _, listener := range p.listeners { |  | ||||||
| 		go listener.run(stopCh) |  | ||||||
| 		go listener.pop(stopCh) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type processorListener struct { |  | ||||||
| 	// lock/cond protects access to 'pendingNotifications'. |  | ||||||
| 	lock sync.RWMutex |  | ||||||
| 	cond sync.Cond |  | ||||||
|  |  | ||||||
| 	// pendingNotifications is an unbounded slice that holds all notifications not yet distributed |  | ||||||
| 	// there is one per listener, but a failing/stalled listener will have infinite pendingNotifications |  | ||||||
| 	// added until we OOM. |  | ||||||
| 	// TODO This is no worse that before, since reflectors were backed by unbounded DeltaFIFOs, but |  | ||||||
| 	// we should try to do something better |  | ||||||
| 	pendingNotifications []interface{} |  | ||||||
|  |  | ||||||
| 	nextCh chan interface{} |  | ||||||
|  |  | ||||||
| 	handler ResourceEventHandler |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newProcessListener(handler ResourceEventHandler) *processorListener { |  | ||||||
| 	ret := &processorListener{ |  | ||||||
| 		pendingNotifications: []interface{}{}, |  | ||||||
| 		nextCh:               make(chan interface{}), |  | ||||||
| 		handler:              handler, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ret.cond.L = &ret.lock |  | ||||||
| 	return ret |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *processorListener) add(notification interface{}) { |  | ||||||
| 	p.lock.Lock() |  | ||||||
| 	defer p.lock.Unlock() |  | ||||||
|  |  | ||||||
| 	p.pendingNotifications = append(p.pendingNotifications, notification) |  | ||||||
| 	p.cond.Broadcast() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *processorListener) pop(stopCh <-chan struct{}) { |  | ||||||
| 	defer utilruntime.HandleCrash() |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		blockingGet := func() (interface{}, bool) { |  | ||||||
| 			p.lock.Lock() |  | ||||||
| 			defer p.lock.Unlock() |  | ||||||
|  |  | ||||||
| 			for len(p.pendingNotifications) == 0 { |  | ||||||
| 				// check if we're shutdown |  | ||||||
| 				select { |  | ||||||
| 				case <-stopCh: |  | ||||||
| 					return nil, true |  | ||||||
| 				default: |  | ||||||
| 				} |  | ||||||
| 				p.cond.Wait() |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			nt := p.pendingNotifications[0] |  | ||||||
| 			p.pendingNotifications = p.pendingNotifications[1:] |  | ||||||
| 			return nt, false |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		notification, stopped := blockingGet() |  | ||||||
| 		if stopped { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		select { |  | ||||||
| 		case <-stopCh: |  | ||||||
| 			return |  | ||||||
| 		case p.nextCh <- notification: |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *processorListener) run(stopCh <-chan struct{}) { |  | ||||||
| 	defer utilruntime.HandleCrash() |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		var next interface{} |  | ||||||
| 		select { |  | ||||||
| 		case <-stopCh: |  | ||||||
| 			func() { |  | ||||||
| 				p.lock.Lock() |  | ||||||
| 				defer p.lock.Unlock() |  | ||||||
| 				p.cond.Broadcast() |  | ||||||
| 			}() |  | ||||||
| 			return |  | ||||||
| 		case next = <-p.nextCh: |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		switch notification := next.(type) { |  | ||||||
| 		case updateNotification: |  | ||||||
| 			p.handler.OnUpdate(notification.oldObj, notification.newObj) |  | ||||||
| 		case addNotification: |  | ||||||
| 			p.handler.OnAdd(notification.newObj) |  | ||||||
| 		case deleteNotification: |  | ||||||
| 			p.handler.OnDelete(notification.oldObj) |  | ||||||
| 		default: |  | ||||||
| 			utilruntime.HandleError(fmt.Errorf("unrecognized notification: %#v", next)) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										240
									
								
								pkg/client/cache/store.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										240
									
								
								pkg/client/cache/store.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,240 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/meta" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Store is a generic object storage interface. Reflector knows how to watch a server |  | ||||||
| // and update a store. A generic store is provided, which allows Reflector to be used |  | ||||||
| // as a local caching system, and an LRU store, which allows Reflector to work like a |  | ||||||
| // queue of items yet to be processed. |  | ||||||
| // |  | ||||||
| // Store makes no assumptions about stored object identity; it is the responsibility |  | ||||||
| // of a Store implementation to provide a mechanism to correctly key objects and to |  | ||||||
| // define the contract for obtaining objects by some arbitrary key type. |  | ||||||
| type Store interface { |  | ||||||
| 	Add(obj interface{}) error |  | ||||||
| 	Update(obj interface{}) error |  | ||||||
| 	Delete(obj interface{}) error |  | ||||||
| 	List() []interface{} |  | ||||||
| 	ListKeys() []string |  | ||||||
| 	Get(obj interface{}) (item interface{}, exists bool, err error) |  | ||||||
| 	GetByKey(key string) (item interface{}, exists bool, err error) |  | ||||||
|  |  | ||||||
| 	// Replace will delete the contents of the store, using instead the |  | ||||||
| 	// given list. Store takes ownership of the list, you should not reference |  | ||||||
| 	// it after calling this function. |  | ||||||
| 	Replace([]interface{}, string) error |  | ||||||
| 	Resync() error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // KeyFunc knows how to make a key from an object. Implementations should be deterministic. |  | ||||||
| type KeyFunc func(obj interface{}) (string, error) |  | ||||||
|  |  | ||||||
| // KeyError will be returned any time a KeyFunc gives an error; it includes the object |  | ||||||
| // at fault. |  | ||||||
| type KeyError struct { |  | ||||||
| 	Obj interface{} |  | ||||||
| 	Err error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Error gives a human-readable description of the error. |  | ||||||
| func (k KeyError) Error() string { |  | ||||||
| 	return fmt.Sprintf("couldn't create key for object %+v: %v", k.Obj, k.Err) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ExplicitKey can be passed to MetaNamespaceKeyFunc if you have the key for |  | ||||||
| // the object but not the object itself. |  | ||||||
| type ExplicitKey string |  | ||||||
|  |  | ||||||
| // MetaNamespaceKeyFunc is a convenient default KeyFunc which knows how to make |  | ||||||
| // keys for API objects which implement meta.Interface. |  | ||||||
| // The key uses the format <namespace>/<name> unless <namespace> is empty, then |  | ||||||
| // it's just <name>. |  | ||||||
| // |  | ||||||
| // TODO: replace key-as-string with a key-as-struct so that this |  | ||||||
| // packing/unpacking won't be necessary. |  | ||||||
| func MetaNamespaceKeyFunc(obj interface{}) (string, error) { |  | ||||||
| 	if key, ok := obj.(ExplicitKey); ok { |  | ||||||
| 		return string(key), nil |  | ||||||
| 	} |  | ||||||
| 	meta, err := meta.Accessor(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", fmt.Errorf("object has no meta: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if len(meta.GetNamespace()) > 0 { |  | ||||||
| 		return meta.GetNamespace() + "/" + meta.GetName(), nil |  | ||||||
| 	} |  | ||||||
| 	return meta.GetName(), nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SplitMetaNamespaceKey returns the namespace and name that |  | ||||||
| // MetaNamespaceKeyFunc encoded into key. |  | ||||||
| // |  | ||||||
| // TODO: replace key-as-string with a key-as-struct so that this |  | ||||||
| // packing/unpacking won't be necessary. |  | ||||||
| func SplitMetaNamespaceKey(key string) (namespace, name string, err error) { |  | ||||||
| 	parts := strings.Split(key, "/") |  | ||||||
| 	switch len(parts) { |  | ||||||
| 	case 1: |  | ||||||
| 		// name only, no namespace |  | ||||||
| 		return "", parts[0], nil |  | ||||||
| 	case 2: |  | ||||||
| 		// namespace and name |  | ||||||
| 		return parts[0], parts[1], nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return "", "", fmt.Errorf("unexpected key format: %q", key) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // cache responsibilities are limited to: |  | ||||||
| //	1. Computing keys for objects via keyFunc |  | ||||||
| //  2. Invoking methods of a ThreadSafeStorage interface |  | ||||||
| type cache struct { |  | ||||||
| 	// cacheStorage bears the burden of thread safety for the cache |  | ||||||
| 	cacheStorage ThreadSafeStore |  | ||||||
| 	// keyFunc is used to make the key for objects stored in and retrieved from items, and |  | ||||||
| 	// should be deterministic. |  | ||||||
| 	keyFunc KeyFunc |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var _ Store = &cache{} |  | ||||||
|  |  | ||||||
| // Add inserts an item into the cache. |  | ||||||
| func (c *cache) Add(obj interface{}) error { |  | ||||||
| 	key, err := c.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	c.cacheStorage.Add(key, obj) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Update sets an item in the cache to its updated state. |  | ||||||
| func (c *cache) Update(obj interface{}) error { |  | ||||||
| 	key, err := c.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	c.cacheStorage.Update(key, obj) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Delete removes an item from the cache. |  | ||||||
| func (c *cache) Delete(obj interface{}) error { |  | ||||||
| 	key, err := c.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	c.cacheStorage.Delete(key) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // List returns a list of all the items. |  | ||||||
| // List is completely threadsafe as long as you treat all items as immutable. |  | ||||||
| func (c *cache) List() []interface{} { |  | ||||||
| 	return c.cacheStorage.List() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListKeys returns a list of all the keys of the objects currently |  | ||||||
| // in the cache. |  | ||||||
| func (c *cache) ListKeys() []string { |  | ||||||
| 	return c.cacheStorage.ListKeys() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetIndexers returns the indexers of cache |  | ||||||
| func (c *cache) GetIndexers() Indexers { |  | ||||||
| 	return c.cacheStorage.GetIndexers() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Index returns a list of items that match on the index function |  | ||||||
| // Index is thread-safe so long as you treat all items as immutable |  | ||||||
| func (c *cache) Index(indexName string, obj interface{}) ([]interface{}, error) { |  | ||||||
| 	return c.cacheStorage.Index(indexName, obj) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListIndexFuncValues returns the list of generated values of an Index func |  | ||||||
| func (c *cache) ListIndexFuncValues(indexName string) []string { |  | ||||||
| 	return c.cacheStorage.ListIndexFuncValues(indexName) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *cache) ByIndex(indexName, indexKey string) ([]interface{}, error) { |  | ||||||
| 	return c.cacheStorage.ByIndex(indexName, indexKey) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *cache) AddIndexers(newIndexers Indexers) error { |  | ||||||
| 	return c.cacheStorage.AddIndexers(newIndexers) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Get returns the requested item, or sets exists=false. |  | ||||||
| // Get is completely threadsafe as long as you treat all items as immutable. |  | ||||||
| func (c *cache) Get(obj interface{}) (item interface{}, exists bool, err error) { |  | ||||||
| 	key, err := c.keyFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, KeyError{obj, err} |  | ||||||
| 	} |  | ||||||
| 	return c.GetByKey(key) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetByKey returns the request item, or exists=false. |  | ||||||
| // GetByKey is completely threadsafe as long as you treat all items as immutable. |  | ||||||
| func (c *cache) GetByKey(key string) (item interface{}, exists bool, err error) { |  | ||||||
| 	item, exists = c.cacheStorage.Get(key) |  | ||||||
| 	return item, exists, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Replace will delete the contents of 'c', using instead the given list. |  | ||||||
| // 'c' takes ownership of the list, you should not reference the list again |  | ||||||
| // after calling this function. |  | ||||||
| func (c *cache) Replace(list []interface{}, resourceVersion string) error { |  | ||||||
| 	items := map[string]interface{}{} |  | ||||||
| 	for _, item := range list { |  | ||||||
| 		key, err := c.keyFunc(item) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return KeyError{item, err} |  | ||||||
| 		} |  | ||||||
| 		items[key] = item |  | ||||||
| 	} |  | ||||||
| 	c.cacheStorage.Replace(items, resourceVersion) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Resync touches all items in the store to force processing |  | ||||||
| func (c *cache) Resync() error { |  | ||||||
| 	return c.cacheStorage.Resync() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewStore returns a Store implemented simply with a map and a lock. |  | ||||||
| func NewStore(keyFunc KeyFunc) Store { |  | ||||||
| 	return &cache{ |  | ||||||
| 		cacheStorage: NewThreadSafeStore(Indexers{}, Indices{}), |  | ||||||
| 		keyFunc:      keyFunc, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewIndexer returns an Indexer implemented simply with a map and a lock. |  | ||||||
| func NewIndexer(keyFunc KeyFunc, indexers Indexers) Indexer { |  | ||||||
| 	return &cache{ |  | ||||||
| 		cacheStorage: NewThreadSafeStore(indexers, Indices{}), |  | ||||||
| 		keyFunc:      keyFunc, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										156
									
								
								pkg/client/cache/store_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										156
									
								
								pkg/client/cache/store_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,156 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Test public interface |  | ||||||
| func doTestStore(t *testing.T, store Store) { |  | ||||||
| 	mkObj := func(id string, val string) testStoreObject { |  | ||||||
| 		return testStoreObject{id: id, val: val} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	store.Add(mkObj("foo", "bar")) |  | ||||||
| 	if item, ok, _ := store.Get(mkObj("foo", "")); !ok { |  | ||||||
| 		t.Errorf("didn't find inserted item") |  | ||||||
| 	} else { |  | ||||||
| 		if e, a := "bar", item.(testStoreObject).val; e != a { |  | ||||||
| 			t.Errorf("expected %v, got %v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	store.Update(mkObj("foo", "baz")) |  | ||||||
| 	if item, ok, _ := store.Get(mkObj("foo", "")); !ok { |  | ||||||
| 		t.Errorf("didn't find inserted item") |  | ||||||
| 	} else { |  | ||||||
| 		if e, a := "baz", item.(testStoreObject).val; e != a { |  | ||||||
| 			t.Errorf("expected %v, got %v", e, a) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	store.Delete(mkObj("foo", "")) |  | ||||||
| 	if _, ok, _ := store.Get(mkObj("foo", "")); ok { |  | ||||||
| 		t.Errorf("found deleted item??") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Test List. |  | ||||||
| 	store.Add(mkObj("a", "b")) |  | ||||||
| 	store.Add(mkObj("c", "d")) |  | ||||||
| 	store.Add(mkObj("e", "e")) |  | ||||||
| 	{ |  | ||||||
| 		found := sets.String{} |  | ||||||
| 		for _, item := range store.List() { |  | ||||||
| 			found.Insert(item.(testStoreObject).val) |  | ||||||
| 		} |  | ||||||
| 		if !found.HasAll("b", "d", "e") { |  | ||||||
| 			t.Errorf("missing items, found: %v", found) |  | ||||||
| 		} |  | ||||||
| 		if len(found) != 3 { |  | ||||||
| 			t.Errorf("extra items") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Test Replace. |  | ||||||
| 	store.Replace([]interface{}{ |  | ||||||
| 		mkObj("foo", "foo"), |  | ||||||
| 		mkObj("bar", "bar"), |  | ||||||
| 	}, "0") |  | ||||||
|  |  | ||||||
| 	{ |  | ||||||
| 		found := sets.String{} |  | ||||||
| 		for _, item := range store.List() { |  | ||||||
| 			found.Insert(item.(testStoreObject).val) |  | ||||||
| 		} |  | ||||||
| 		if !found.HasAll("foo", "bar") { |  | ||||||
| 			t.Errorf("missing items") |  | ||||||
| 		} |  | ||||||
| 		if len(found) != 2 { |  | ||||||
| 			t.Errorf("extra items") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Test public interface |  | ||||||
| func doTestIndex(t *testing.T, indexer Indexer) { |  | ||||||
| 	mkObj := func(id string, val string) testStoreObject { |  | ||||||
| 		return testStoreObject{id: id, val: val} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Test Index |  | ||||||
| 	expected := map[string]sets.String{} |  | ||||||
| 	expected["b"] = sets.NewString("a", "c") |  | ||||||
| 	expected["f"] = sets.NewString("e") |  | ||||||
| 	expected["h"] = sets.NewString("g") |  | ||||||
| 	indexer.Add(mkObj("a", "b")) |  | ||||||
| 	indexer.Add(mkObj("c", "b")) |  | ||||||
| 	indexer.Add(mkObj("e", "f")) |  | ||||||
| 	indexer.Add(mkObj("g", "h")) |  | ||||||
| 	{ |  | ||||||
| 		for k, v := range expected { |  | ||||||
| 			found := sets.String{} |  | ||||||
| 			indexResults, err := indexer.Index("by_val", mkObj("", k)) |  | ||||||
| 			if err != nil { |  | ||||||
| 				t.Errorf("Unexpected error %v", err) |  | ||||||
| 			} |  | ||||||
| 			for _, item := range indexResults { |  | ||||||
| 				found.Insert(item.(testStoreObject).id) |  | ||||||
| 			} |  | ||||||
| 			items := v.List() |  | ||||||
| 			if !found.HasAll(items...) { |  | ||||||
| 				t.Errorf("missing items, index %s, expected %v but found %v", k, items, found.List()) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func testStoreKeyFunc(obj interface{}) (string, error) { |  | ||||||
| 	return obj.(testStoreObject).id, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func testStoreIndexFunc(obj interface{}) ([]string, error) { |  | ||||||
| 	return []string{obj.(testStoreObject).val}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func testStoreIndexers() Indexers { |  | ||||||
| 	indexers := Indexers{} |  | ||||||
| 	indexers["by_val"] = testStoreIndexFunc |  | ||||||
| 	return indexers |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type testStoreObject struct { |  | ||||||
| 	id  string |  | ||||||
| 	val string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCache(t *testing.T) { |  | ||||||
| 	doTestStore(t, NewStore(testStoreKeyFunc)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFIFOCache(t *testing.T) { |  | ||||||
| 	doTestStore(t, NewFIFO(testStoreKeyFunc)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUndeltaStore(t *testing.T) { |  | ||||||
| 	nop := func([]interface{}) {} |  | ||||||
| 	doTestStore(t, NewUndeltaStore(nop, testStoreKeyFunc)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestIndex(t *testing.T) { |  | ||||||
| 	doTestIndex(t, NewIndexer(testStoreKeyFunc, testStoreIndexers())) |  | ||||||
| } |  | ||||||
							
								
								
									
										288
									
								
								pkg/client/cache/thread_safe_store.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										288
									
								
								pkg/client/cache/thread_safe_store.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,288 +0,0 @@ | |||||||
| /* |  | ||||||
| Copyright 2014 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // ThreadSafeStore is an interface that allows concurrent access to a storage backend. |  | ||||||
| // TL;DR caveats: you must not modify anything returned by Get or List as it will break |  | ||||||
| // the indexing feature in addition to not being thread safe. |  | ||||||
| // |  | ||||||
| // The guarantees of thread safety provided by List/Get are only valid if the caller |  | ||||||
| // treats returned items as read-only. For example, a pointer inserted in the store |  | ||||||
| // through `Add` will be returned as is by `Get`. Multiple clients might invoke `Get` |  | ||||||
| // on the same key and modify the pointer in a non-thread-safe way. Also note that |  | ||||||
| // modifying objects stored by the indexers (if any) will *not* automatically lead |  | ||||||
| // to a re-index. So it's not a good idea to directly modify the objects returned by |  | ||||||
| // Get/List, in general. |  | ||||||
| type ThreadSafeStore interface { |  | ||||||
| 	Add(key string, obj interface{}) |  | ||||||
| 	Update(key string, obj interface{}) |  | ||||||
| 	Delete(key string) |  | ||||||
| 	Get(key string) (item interface{}, exists bool) |  | ||||||
| 	List() []interface{} |  | ||||||
| 	ListKeys() []string |  | ||||||
| 	Replace(map[string]interface{}, string) |  | ||||||
| 	Index(indexName string, obj interface{}) ([]interface{}, error) |  | ||||||
| 	ListIndexFuncValues(name string) []string |  | ||||||
| 	ByIndex(indexName, indexKey string) ([]interface{}, error) |  | ||||||
| 	GetIndexers() Indexers |  | ||||||
|  |  | ||||||
| 	// AddIndexers adds more indexers to this store.  If you call this after you already have data |  | ||||||
| 	// in the store, the results are undefined. |  | ||||||
| 	AddIndexers(newIndexers Indexers) error |  | ||||||
| 	Resync() error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // threadSafeMap implements ThreadSafeStore |  | ||||||
| type threadSafeMap struct { |  | ||||||
| 	lock  sync.RWMutex |  | ||||||
| 	items map[string]interface{} |  | ||||||
|  |  | ||||||
| 	// indexers maps a name to an IndexFunc |  | ||||||
| 	indexers Indexers |  | ||||||
| 	// indices maps a name to an Index |  | ||||||
| 	indices Indices |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) Add(key string, obj interface{}) { |  | ||||||
| 	c.lock.Lock() |  | ||||||
| 	defer c.lock.Unlock() |  | ||||||
| 	oldObject := c.items[key] |  | ||||||
| 	c.items[key] = obj |  | ||||||
| 	c.updateIndices(oldObject, obj, key) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) Update(key string, obj interface{}) { |  | ||||||
| 	c.lock.Lock() |  | ||||||
| 	defer c.lock.Unlock() |  | ||||||
| 	oldObject := c.items[key] |  | ||||||
| 	c.items[key] = obj |  | ||||||
| 	c.updateIndices(oldObject, obj, key) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) Delete(key string) { |  | ||||||
| 	c.lock.Lock() |  | ||||||
| 	defer c.lock.Unlock() |  | ||||||
| 	if obj, exists := c.items[key]; exists { |  | ||||||
| 		c.deleteFromIndices(obj, key) |  | ||||||
| 		delete(c.items, key) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) Get(key string) (item interface{}, exists bool) { |  | ||||||
| 	c.lock.RLock() |  | ||||||
| 	defer c.lock.RUnlock() |  | ||||||
| 	item, exists = c.items[key] |  | ||||||
| 	return item, exists |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) List() []interface{} { |  | ||||||
| 	c.lock.RLock() |  | ||||||
| 	defer c.lock.RUnlock() |  | ||||||
| 	list := make([]interface{}, 0, len(c.items)) |  | ||||||
| 	for _, item := range c.items { |  | ||||||
| 		list = append(list, item) |  | ||||||
| 	} |  | ||||||
| 	return list |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ListKeys returns a list of all the keys of the objects currently |  | ||||||
| // in the threadSafeMap. |  | ||||||
| func (c *threadSafeMap) ListKeys() []string { |  | ||||||
| 	c.lock.RLock() |  | ||||||
| 	defer c.lock.RUnlock() |  | ||||||
| 	list := make([]string, 0, len(c.items)) |  | ||||||
| 	for key := range c.items { |  | ||||||
| 		list = append(list, key) |  | ||||||
| 	} |  | ||||||
| 	return list |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) Replace(items map[string]interface{}, resourceVersion string) { |  | ||||||
| 	c.lock.Lock() |  | ||||||
| 	defer c.lock.Unlock() |  | ||||||
| 	c.items = items |  | ||||||
|  |  | ||||||
| 	// rebuild any index |  | ||||||
| 	c.indices = Indices{} |  | ||||||
| 	for key, item := range c.items { |  | ||||||
| 		c.updateIndices(nil, item, key) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Index returns a list of items that match on the index function |  | ||||||
| // Index is thread-safe so long as you treat all items as immutable |  | ||||||
| func (c *threadSafeMap) Index(indexName string, obj interface{}) ([]interface{}, error) { |  | ||||||
| 	c.lock.RLock() |  | ||||||
| 	defer c.lock.RUnlock() |  | ||||||
|  |  | ||||||
| 	indexFunc := c.indexers[indexName] |  | ||||||
| 	if indexFunc == nil { |  | ||||||
| 		return nil, fmt.Errorf("Index with name %s does not exist", indexName) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	indexKeys, err := indexFunc(obj) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	index := c.indices[indexName] |  | ||||||
|  |  | ||||||
| 	// need to de-dupe the return list.  Since multiple keys are allowed, this can happen. |  | ||||||
| 	returnKeySet := sets.String{} |  | ||||||
| 	for _, indexKey := range indexKeys { |  | ||||||
| 		set := index[indexKey] |  | ||||||
| 		for _, key := range set.UnsortedList() { |  | ||||||
| 			returnKeySet.Insert(key) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	list := make([]interface{}, 0, returnKeySet.Len()) |  | ||||||
| 	for absoluteKey := range returnKeySet { |  | ||||||
| 		list = append(list, c.items[absoluteKey]) |  | ||||||
| 	} |  | ||||||
| 	return list, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ByIndex returns a list of items that match an exact value on the index function |  | ||||||
| func (c *threadSafeMap) ByIndex(indexName, indexKey string) ([]interface{}, error) { |  | ||||||
| 	c.lock.RLock() |  | ||||||
| 	defer c.lock.RUnlock() |  | ||||||
|  |  | ||||||
| 	indexFunc := c.indexers[indexName] |  | ||||||
| 	if indexFunc == nil { |  | ||||||
| 		return nil, fmt.Errorf("Index with name %s does not exist", indexName) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	index := c.indices[indexName] |  | ||||||
|  |  | ||||||
| 	set := index[indexKey] |  | ||||||
| 	list := make([]interface{}, 0, set.Len()) |  | ||||||
| 	for _, key := range set.List() { |  | ||||||
| 		list = append(list, c.items[key]) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return list, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) ListIndexFuncValues(indexName string) []string { |  | ||||||
| 	c.lock.RLock() |  | ||||||
| 	defer c.lock.RUnlock() |  | ||||||
|  |  | ||||||
| 	index := c.indices[indexName] |  | ||||||
| 	names := make([]string, 0, len(index)) |  | ||||||
| 	for key := range index { |  | ||||||
| 		names = append(names, key) |  | ||||||
| 	} |  | ||||||
| 	return names |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) GetIndexers() Indexers { |  | ||||||
| 	return c.indexers |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) AddIndexers(newIndexers Indexers) error { |  | ||||||
| 	c.lock.Lock() |  | ||||||
| 	defer c.lock.Unlock() |  | ||||||
|  |  | ||||||
| 	if len(c.items) > 0 { |  | ||||||
| 		return fmt.Errorf("cannot add indexers to running index") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	oldKeys := sets.StringKeySet(c.indexers) |  | ||||||
| 	newKeys := sets.StringKeySet(newIndexers) |  | ||||||
|  |  | ||||||
| 	if oldKeys.HasAny(newKeys.List()...) { |  | ||||||
| 		return fmt.Errorf("indexer conflict: %v", oldKeys.Intersection(newKeys)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for k, v := range newIndexers { |  | ||||||
| 		c.indexers[k] = v |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // updateIndices modifies the objects location in the managed indexes, if this is an update, you must provide an oldObj |  | ||||||
| // updateIndices must be called from a function that already has a lock on the cache |  | ||||||
| func (c *threadSafeMap) updateIndices(oldObj interface{}, newObj interface{}, key string) error { |  | ||||||
| 	// if we got an old object, we need to remove it before we add it again |  | ||||||
| 	if oldObj != nil { |  | ||||||
| 		c.deleteFromIndices(oldObj, key) |  | ||||||
| 	} |  | ||||||
| 	for name, indexFunc := range c.indexers { |  | ||||||
| 		indexValues, err := indexFunc(newObj) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		index := c.indices[name] |  | ||||||
| 		if index == nil { |  | ||||||
| 			index = Index{} |  | ||||||
| 			c.indices[name] = index |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, indexValue := range indexValues { |  | ||||||
| 			set := index[indexValue] |  | ||||||
| 			if set == nil { |  | ||||||
| 				set = sets.String{} |  | ||||||
| 				index[indexValue] = set |  | ||||||
| 			} |  | ||||||
| 			set.Insert(key) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // deleteFromIndices removes the object from each of the managed indexes |  | ||||||
| // it is intended to be called from a function that already has a lock on the cache |  | ||||||
| func (c *threadSafeMap) deleteFromIndices(obj interface{}, key string) error { |  | ||||||
| 	for name, indexFunc := range c.indexers { |  | ||||||
| 		indexValues, err := indexFunc(obj) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		index := c.indices[name] |  | ||||||
| 		if index == nil { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		for _, indexValue := range indexValues { |  | ||||||
| 			set := index[indexValue] |  | ||||||
| 			if set != nil { |  | ||||||
| 				set.Delete(key) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *threadSafeMap) Resync() error { |  | ||||||
| 	// Nothing to do |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewThreadSafeStore(indexers Indexers, indices Indices) ThreadSafeStore { |  | ||||||
| 	return &threadSafeMap{ |  | ||||||
| 		items:    map[string]interface{}{}, |  | ||||||
| 		indexers: indexers, |  | ||||||
| 		indices:  indices, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										83
									
								
								pkg/client/cache/undelta_store.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								pkg/client/cache/undelta_store.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,83 +0,0 @@ | |||||||
| /* |  | ||||||
| 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 cache |  | ||||||
|  |  | ||||||
| // UndeltaStore listens to incremental updates and sends complete state on every change. |  | ||||||
| // It implements the Store interface so that it can receive a stream of mirrored objects |  | ||||||
| // from Reflector.  Whenever it receives any complete (Store.Replace) or incremental change |  | ||||||
| // (Store.Add, Store.Update, Store.Delete), it sends the complete state by calling PushFunc. |  | ||||||
| // It is thread-safe.  It guarantees that every change (Add, Update, Replace, Delete) results |  | ||||||
| // in one call to PushFunc, but sometimes PushFunc may be called twice with the same values. |  | ||||||
| // PushFunc should be thread safe. |  | ||||||
| type UndeltaStore struct { |  | ||||||
| 	Store |  | ||||||
| 	PushFunc func([]interface{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Assert that it implements the Store interface. |  | ||||||
| var _ Store = &UndeltaStore{} |  | ||||||
|  |  | ||||||
| // Note about thread safety.  The Store implementation (cache.cache) uses a lock for all methods. |  | ||||||
| // In the functions below, the lock gets released and reacquired betweend the {Add,Delete,etc} |  | ||||||
| // and the List.  So, the following can happen, resulting in two identical calls to PushFunc. |  | ||||||
| // time            thread 1                  thread 2 |  | ||||||
| // 0               UndeltaStore.Add(a) |  | ||||||
| // 1                                         UndeltaStore.Add(b) |  | ||||||
| // 2               Store.Add(a) |  | ||||||
| // 3                                         Store.Add(b) |  | ||||||
| // 4               Store.List() -> [a,b] |  | ||||||
| // 5                                         Store.List() -> [a,b] |  | ||||||
|  |  | ||||||
| func (u *UndeltaStore) Add(obj interface{}) error { |  | ||||||
| 	if err := u.Store.Add(obj); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	u.PushFunc(u.Store.List()) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (u *UndeltaStore) Update(obj interface{}) error { |  | ||||||
| 	if err := u.Store.Update(obj); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	u.PushFunc(u.Store.List()) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (u *UndeltaStore) Delete(obj interface{}) error { |  | ||||||
| 	if err := u.Store.Delete(obj); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	u.PushFunc(u.Store.List()) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (u *UndeltaStore) Replace(list []interface{}, resourceVersion string) error { |  | ||||||
| 	if err := u.Store.Replace(list, resourceVersion); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	u.PushFunc(u.Store.List()) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewUndeltaStore returns an UndeltaStore implemented with a Store. |  | ||||||
| func NewUndeltaStore(pushFunc func([]interface{}), keyFunc KeyFunc) *UndeltaStore { |  | ||||||
| 	return &UndeltaStore{ |  | ||||||
| 		Store:    NewStore(keyFunc), |  | ||||||
| 		PushFunc: pushFunc, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										131
									
								
								pkg/client/cache/undelta_store_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										131
									
								
								pkg/client/cache/undelta_store_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,131 +0,0 @@ | |||||||
| /* |  | ||||||
| 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 cache |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"reflect" |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // store_test.go checks that UndeltaStore conforms to the Store interface |  | ||||||
| // behavior.  This test just tests that it calls the push func in addition. |  | ||||||
|  |  | ||||||
| type testUndeltaObject struct { |  | ||||||
| 	name string |  | ||||||
| 	val  interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func testUndeltaKeyFunc(obj interface{}) (string, error) { |  | ||||||
| 	return obj.(testUndeltaObject).name, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
| var ( |  | ||||||
| 	o1 interface{}   = t{1} |  | ||||||
| 	o2 interface{}   = t{2} |  | ||||||
| 	l1 []interface{} = []interface{}{t{1}} |  | ||||||
| ) |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| func TestUpdateCallsPush(t *testing.T) { |  | ||||||
| 	mkObj := func(name string, val interface{}) testUndeltaObject { |  | ||||||
| 		return testUndeltaObject{name: name, val: val} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var got []interface{} |  | ||||||
| 	var callcount int = 0 |  | ||||||
| 	push := func(m []interface{}) { |  | ||||||
| 		callcount++ |  | ||||||
| 		got = m |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	u := NewUndeltaStore(push, testUndeltaKeyFunc) |  | ||||||
|  |  | ||||||
| 	u.Add(mkObj("a", 2)) |  | ||||||
| 	u.Update(mkObj("a", 1)) |  | ||||||
| 	if callcount != 2 { |  | ||||||
| 		t.Errorf("Expected 2 calls, got %d", callcount) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	l := []interface{}{mkObj("a", 1)} |  | ||||||
| 	if !reflect.DeepEqual(l, got) { |  | ||||||
| 		t.Errorf("Expected %#v, Got %#v", l, got) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeleteCallsPush(t *testing.T) { |  | ||||||
| 	mkObj := func(name string, val interface{}) testUndeltaObject { |  | ||||||
| 		return testUndeltaObject{name: name, val: val} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var got []interface{} |  | ||||||
| 	var callcount int = 0 |  | ||||||
| 	push := func(m []interface{}) { |  | ||||||
| 		callcount++ |  | ||||||
| 		got = m |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	u := NewUndeltaStore(push, testUndeltaKeyFunc) |  | ||||||
|  |  | ||||||
| 	u.Add(mkObj("a", 2)) |  | ||||||
| 	u.Delete(mkObj("a", "")) |  | ||||||
| 	if callcount != 2 { |  | ||||||
| 		t.Errorf("Expected 2 calls, got %d", callcount) |  | ||||||
| 	} |  | ||||||
| 	expected := []interface{}{} |  | ||||||
| 	if !reflect.DeepEqual(expected, got) { |  | ||||||
| 		t.Errorf("Expected %#v, Got %#v", expected, got) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReadsDoNotCallPush(t *testing.T) { |  | ||||||
| 	push := func(m []interface{}) { |  | ||||||
| 		t.Errorf("Unexpected call to push!") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	u := NewUndeltaStore(push, testUndeltaKeyFunc) |  | ||||||
|  |  | ||||||
| 	// These should not call push. |  | ||||||
| 	_ = u.List() |  | ||||||
| 	_, _, _ = u.Get(testUndeltaObject{"a", ""}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestReplaceCallsPush(t *testing.T) { |  | ||||||
| 	mkObj := func(name string, val interface{}) testUndeltaObject { |  | ||||||
| 		return testUndeltaObject{name: name, val: val} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var got []interface{} |  | ||||||
| 	var callcount int = 0 |  | ||||||
| 	push := func(m []interface{}) { |  | ||||||
| 		callcount++ |  | ||||||
| 		got = m |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	u := NewUndeltaStore(push, testUndeltaKeyFunc) |  | ||||||
|  |  | ||||||
| 	m := []interface{}{mkObj("a", 1)} |  | ||||||
|  |  | ||||||
| 	u.Replace(m, "0") |  | ||||||
| 	if callcount != 1 { |  | ||||||
| 		t.Errorf("Expected 1 calls, got %d", callcount) |  | ||||||
| 	} |  | ||||||
| 	expected := []interface{}{mkObj("a", 1)} |  | ||||||
| 	if !reflect.DeepEqual(expected, got) { |  | ||||||
| 		t.Errorf("Expected %#v, Got %#v", expected, got) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -35,7 +35,6 @@ go_library( | |||||||
|         "//pkg/apis/rbac/v1beta1:go_default_library", |         "//pkg/apis/rbac/v1beta1:go_default_library", | ||||||
|         "//pkg/apis/storage:go_default_library", |         "//pkg/apis/storage:go_default_library", | ||||||
|         "//pkg/apis/storage/v1beta1:go_default_library", |         "//pkg/apis/storage/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/informers/informers_generated/apps:go_default_library", |         "//pkg/client/informers/informers_generated/apps:go_default_library", | ||||||
| @@ -50,6 +49,7 @@ go_library( | |||||||
|         "//pkg/client/informers/informers_generated/storage:go_default_library", |         "//pkg/client/informers/informers_generated/storage:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", |         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,13 +16,13 @@ go_library( | |||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//pkg/apis/apps:go_default_library", |         "//pkg/apis/apps:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", |         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", | ||||||
|         "//pkg/client/listers/apps/internalversion:go_default_library", |         "//pkg/client/listers/apps/internalversion:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ import ( | |||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	apps "k8s.io/kubernetes/pkg/apis/apps" | 	apps "k8s.io/kubernetes/pkg/apis/apps" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | ||||||
| 	internalversion "k8s.io/kubernetes/pkg/client/listers/apps/internalversion" | 	internalversion "k8s.io/kubernetes/pkg/client/listers/apps/internalversion" | ||||||
|   | |||||||
| @@ -16,13 +16,13 @@ go_library( | |||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//pkg/apis/apps/v1beta1:go_default_library", |         "//pkg/apis/apps/v1beta1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", |         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", | ||||||
|         "//pkg/client/listers/apps/v1beta1:go_default_library", |         "//pkg/client/listers/apps/v1beta1:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ import ( | |||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	apps_v1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1" | 	apps_v1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | ||||||
| 	v1beta1 "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" | 	v1beta1 "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" | ||||||
|   | |||||||
| @@ -16,13 +16,13 @@ go_library( | |||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//pkg/apis/autoscaling:go_default_library", |         "//pkg/apis/autoscaling:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", |         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", | ||||||
|         "//pkg/client/listers/autoscaling/internalversion:go_default_library", |         "//pkg/client/listers/autoscaling/internalversion:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ import ( | |||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	autoscaling "k8s.io/kubernetes/pkg/apis/autoscaling" | 	autoscaling "k8s.io/kubernetes/pkg/apis/autoscaling" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | ||||||
| 	internalversion "k8s.io/kubernetes/pkg/client/listers/autoscaling/internalversion" | 	internalversion "k8s.io/kubernetes/pkg/client/listers/autoscaling/internalversion" | ||||||
|   | |||||||
| @@ -16,13 +16,13 @@ go_library( | |||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//pkg/apis/autoscaling/v1:go_default_library", |         "//pkg/apis/autoscaling/v1:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/clientset:go_default_library", |         "//pkg/client/clientset_generated/clientset:go_default_library", | ||||||
|         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", |         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", | ||||||
|         "//pkg/client/listers/autoscaling/v1:go_default_library", |         "//pkg/client/listers/autoscaling/v1:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ import ( | |||||||
| 	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	autoscaling_v1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1" | 	autoscaling_v1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | ||||||
| 	v1 "k8s.io/kubernetes/pkg/client/listers/autoscaling/v1" | 	v1 "k8s.io/kubernetes/pkg/client/listers/autoscaling/v1" | ||||||
|   | |||||||
| @@ -17,13 +17,13 @@ go_library( | |||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//pkg/apis/batch:go_default_library", |         "//pkg/apis/batch:go_default_library", | ||||||
|         "//pkg/client/cache:go_default_library", |  | ||||||
|         "//pkg/client/clientset_generated/internalclientset:go_default_library", |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", |         "//pkg/client/informers/informers_generated/internalinterfaces:go_default_library", | ||||||
|         "//pkg/client/listers/batch/internalversion:go_default_library", |         "//pkg/client/listers/batch/internalversion:go_default_library", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|  |         "//vendor:k8s.io/client-go/tools/cache", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ import ( | |||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	batch "k8s.io/kubernetes/pkg/apis/batch" | 	batch "k8s.io/kubernetes/pkg/apis/batch" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | ||||||
| 	internalversion "k8s.io/kubernetes/pkg/client/listers/batch/internalversion" | 	internalversion "k8s.io/kubernetes/pkg/client/listers/batch/internalversion" | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ import ( | |||||||
| 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||||
| 	watch "k8s.io/apimachinery/pkg/watch" | 	watch "k8s.io/apimachinery/pkg/watch" | ||||||
|  | 	cache "k8s.io/client-go/tools/cache" | ||||||
| 	batch "k8s.io/kubernetes/pkg/apis/batch" | 	batch "k8s.io/kubernetes/pkg/apis/batch" | ||||||
| 	cache "k8s.io/kubernetes/pkg/client/cache" |  | ||||||
| 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | 	internalinterfaces "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalinterfaces" | ||||||
| 	internalversion "k8s.io/kubernetes/pkg/client/listers/batch/internalversion" | 	internalversion "k8s.io/kubernetes/pkg/client/listers/batch/internalversion" | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue