Merge pull request #1290 from chancez/disable_node_listing
Add flag to disabling node listing
This commit is contained in:
commit
cffeac0119
6 changed files with 350 additions and 17 deletions
5
Godeps/Godeps.json
generated
5
Godeps/Godeps.json
generated
|
@ -1274,6 +1274,11 @@
|
|||
"Comment": "v2.0.0-alpha.0-488-gc7ed6bc",
|
||||
"Rev": "c7ed6bc9c1c981e0f0bd09dc046c9b81ab855c24"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/client-go/tools/cache/testing",
|
||||
"Comment": "v2.0.0-alpha.0-488-gc7ed6bc9",
|
||||
"Rev": "c7ed6bc9c1c981e0f0bd09dc046c9b81ab855c24"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/client-go/tools/clientcmd",
|
||||
"Comment": "v2.0.0-alpha.0-488-gc7ed6bc",
|
||||
|
|
|
@ -52,11 +52,15 @@ Usage of :
|
|||
namespace/name. The controller uses the first node port of this Service for
|
||||
the default backend.
|
||||
--default-server-port int Default port to use for exposing the default server (catch all) (default 8181)
|
||||
--default-ssl-certificate string Name of the secret that contains a SSL certificate to be used as default for a HTTPS catch-all server
|
||||
--default-ssl-certificate string Name of the secret
|
||||
that contains a SSL certificate to be used as default for a HTTPS catch-all server
|
||||
--disable-node-list Disable querying nodes. If --force-namespace-isolation is true, this should also be set.
|
||||
--election-id string Election id to use for status update. (default "ingress-controller-leader")
|
||||
--enable-ssl-passthrough Enable SSL passthrough feature. Default is disabled
|
||||
--force-namespace-isolation Force namespace isolation. This flag is required to avoid the reference of secrets or configmaps located in a different namespace than the specified in the flag --watch-namespace.
|
||||
--health-check-path string Defines the URL to be used as health check inside in the default server in NGINX. (default "/healthz")
|
||||
--force-namespace-isolation Force namespace isolation. This flag is required to avoid the reference of secrets or
|
||||
configmaps located in a different namespace than the specified in the flag --watch-namespace.
|
||||
--health-check-path string Defines
|
||||
the URL to be used as health check inside in the default server in NGINX. (default "/healthz")
|
||||
--healthz-port int port for healthz endpoint. (default 10254)
|
||||
--http-port int Indicates the port to use for HTTP traffic (default 80)
|
||||
--https-port int Indicates the port to use for HTTPS traffic (default 443)
|
||||
|
@ -66,16 +70,28 @@ Usage of :
|
|||
--log_dir string If non-empty, write log files in this directory
|
||||
--logtostderr log to standard error instead of files
|
||||
--profiling Enable profiling via web interface host:port/debug/pprof/ (default true)
|
||||
--publish-service string Service fronting the ingress controllers. Takes the form namespace/name. The controller will set the endpoint records on the ingress objects to reflect those on the service.
|
||||
--publish-service string Service fronting the ingress controllers. Takes the form
|
||||
namespace/name. The controller will set the endpoint records on the
|
||||
ingress objects to reflect those on the service.
|
||||
--sort-backends Defines if backends and it's endpoints should be sorted
|
||||
--ssl-passtrough-proxy-port int Default port to use internally for SSL when SSL Passthgough is enabled (default 442)
|
||||
--status-port int Indicates the TCP port to use for exposing the nginx status page (default 18080)
|
||||
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
|
||||
--sync-period duration Relist and confirm cloud resources this often. Default is 10 minutes (default 10m0s)
|
||||
--tcp-services-configmap string Name of the ConfigMap that contains the definition of the TCP services to expose. The key in the map indicates the external port to be used. The value is the name of theservice with the format namespace/serviceName and the port of the service could be a number of the name of the port. The ports 80 and 443 are not allowed as external ports. This ports are reserved for the backend
|
||||
--udp-services-configmap string Name of the ConfigMap that contains the definition of the UDP services to expose. The key in the map indicates the external port to be used. The value is the name of theservice with the format namespace/serviceName and the port of the service could be a number of the name of the port.
|
||||
--update-status Indicates if the ingress controller should update the Ingress status IP/hostname. Default is true (default true)
|
||||
--update-status-on-shutdown Indicates if the ingress controller should update the Ingress status IP/hostname when the controller is being stopped. Default is true (default true)
|
||||
--tcp-services-configmap string Name of the ConfigMap that contains the definition of the TCP services to expose.
|
||||
The key in the map indicates the external port to be used. The value is the name of the
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
number of the name of the port.
|
||||
The ports 80 and 443 are not allowed as external ports. This ports are reserved for the backend
|
||||
--udp-services-configmap string Name of the ConfigMap that contains the definition of the UDP services to expose.
|
||||
The key in the map indicates the external port to be used. The value is the name of the
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
number of the name of the port.
|
||||
--update-status Indicates if the
|
||||
ingress controller should update the Ingress status IP/hostname. Default is true (default true)
|
||||
--update-status-on-shutdown Indicates if the
|
||||
ingress controller should update the Ingress status IP/hostname when the controller
|
||||
is being stopped. Default is true (default true)
|
||||
-v, --v Level log level for V logs
|
||||
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
|
||||
--watch-namespace string Namespace to watch for Ingress. Default is to watch all namespaces
|
||||
|
|
|
@ -39,6 +39,7 @@ import (
|
|||
"k8s.io/client-go/kubernetes/scheme"
|
||||
unversionedcore "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
fcache "k8s.io/client-go/tools/cache/testing"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
|
@ -130,6 +131,7 @@ type Configuration struct {
|
|||
ConfigMapName string
|
||||
|
||||
ForceNamespaceIsolation bool
|
||||
DisableNodeList bool
|
||||
|
||||
// optional
|
||||
TCPConfigMapName string
|
||||
|
@ -324,8 +326,14 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
cache.NewListWatchFromClient(ic.cfg.Client.CoreV1().RESTClient(), "services", ic.cfg.Namespace, fields.Everything()),
|
||||
&api.Service{}, ic.cfg.ResyncPeriod, cache.ResourceEventHandlerFuncs{})
|
||||
|
||||
var nodeListerWatcher cache.ListerWatcher
|
||||
if config.DisableNodeList {
|
||||
nodeListerWatcher = fcache.NewFakeControllerSource()
|
||||
} else {
|
||||
nodeListerWatcher = cache.NewListWatchFromClient(ic.cfg.Client.CoreV1().RESTClient(), "nodes", api.NamespaceAll, fields.Everything())
|
||||
}
|
||||
ic.nodeLister.Store, ic.nodeController = cache.NewInformer(
|
||||
cache.NewListWatchFromClient(ic.cfg.Client.CoreV1().RESTClient(), "nodes", api.NamespaceAll, fields.Everything()),
|
||||
nodeListerWatcher,
|
||||
&api.Node{}, ic.cfg.ResyncPeriod, cache.ResourceEventHandlerFuncs{})
|
||||
|
||||
if config.UpdateStatus {
|
||||
|
|
|
@ -56,14 +56,14 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
tcpConfigMapName = flags.String("tcp-services-configmap", "",
|
||||
`Name of the ConfigMap that contains the definition of the TCP services to expose.
|
||||
The key in the map indicates the external port to be used. The value is the name of the
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
number of the name of the port.
|
||||
The ports 80 and 443 are not allowed as external ports. This ports are reserved for the backend`)
|
||||
|
||||
udpConfigMapName = flags.String("udp-services-configmap", "",
|
||||
`Name of the ConfigMap that contains the definition of the UDP services to expose.
|
||||
The key in the map indicates the external port to be used. The value is the name of the
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
number of the name of the port.`)
|
||||
|
||||
resyncPeriod = flags.Duration("sync-period", 600*time.Second,
|
||||
|
@ -76,23 +76,26 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
|
||||
profiling = flags.Bool("profiling", true, `Enable profiling via web interface host:port/debug/pprof/`)
|
||||
|
||||
defSSLCertificate = flags.String("default-ssl-certificate", "", `Name of the secret
|
||||
defSSLCertificate = flags.String("default-ssl-certificate", "", `Name of the secret
|
||||
that contains a SSL certificate to be used as default for a HTTPS catch-all server`)
|
||||
|
||||
defHealthzURL = flags.String("health-check-path", "/healthz", `Defines
|
||||
defHealthzURL = flags.String("health-check-path", "/healthz", `Defines
|
||||
the URL to be used as health check inside in the default server in NGINX.`)
|
||||
|
||||
updateStatus = flags.Bool("update-status", true, `Indicates if the
|
||||
updateStatus = flags.Bool("update-status", true, `Indicates if the
|
||||
ingress controller should update the Ingress status IP/hostname. Default is true`)
|
||||
|
||||
electionID = flags.String("election-id", "ingress-controller-leader", `Election id to use for status update.`)
|
||||
|
||||
forceIsolation = flags.Bool("force-namespace-isolation", false,
|
||||
`Force namespace isolation. This flag is required to avoid the reference of secrets or
|
||||
`Force namespace isolation. This flag is required to avoid the reference of secrets or
|
||||
configmaps located in a different namespace than the specified in the flag --watch-namespace.`)
|
||||
|
||||
updateStatusOnShutdown = flags.Bool("update-status-on-shutdown", true, `Indicates if the
|
||||
ingress controller should update the Ingress status IP/hostname when the controller
|
||||
disableNodeList = flags.Bool("disable-node-list", false,
|
||||
`Disable querying nodes. If --force-namespace-isolation is true, this should also be set.`)
|
||||
|
||||
updateStatusOnShutdown = flags.Bool("update-status-on-shutdown", true, `Indicates if the
|
||||
ingress controller should update the Ingress status IP/hostname when the controller
|
||||
is being stopped. Default is true`)
|
||||
|
||||
sortBackends = flags.Bool("sort-backends", false,
|
||||
|
@ -183,6 +186,7 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
PublishService: *publishSvc,
|
||||
Backend: backend,
|
||||
ForceNamespaceIsolation: *forceIsolation,
|
||||
DisableNodeList: *disableNodeList,
|
||||
UpdateStatusOnShutdown: *updateStatusOnShutdown,
|
||||
SortBackends: *sortBackends,
|
||||
}
|
||||
|
|
36
vendor/k8s.io/client-go/tools/cache/testing/BUILD
generated
vendored
Normal file
36
vendor/k8s.io/client-go/tools/cache/testing/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["fake_controller_source_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["fake_controller_source.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
],
|
||||
)
|
264
vendor/k8s.io/client-go/tools/cache/testing/fake_controller_source.go
generated
vendored
Normal file
264
vendor/k8s.io/client-go/tools/cache/testing/fake_controller_source.go
generated
vendored
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
|
||||
func NewFakeControllerSource() *FakeControllerSource {
|
||||
return &FakeControllerSource{
|
||||
Items: map[nnu]runtime.Object{},
|
||||
Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull),
|
||||
}
|
||||
}
|
||||
|
||||
func NewFakePVControllerSource() *FakePVControllerSource {
|
||||
return &FakePVControllerSource{
|
||||
FakeControllerSource{
|
||||
Items: map[nnu]runtime.Object{},
|
||||
Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull),
|
||||
}}
|
||||
}
|
||||
|
||||
func NewFakePVCControllerSource() *FakePVCControllerSource {
|
||||
return &FakePVCControllerSource{
|
||||
FakeControllerSource{
|
||||
Items: map[nnu]runtime.Object{},
|
||||
Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull),
|
||||
}}
|
||||
}
|
||||
|
||||
// FakeControllerSource implements listing/watching for testing.
|
||||
type FakeControllerSource struct {
|
||||
lock sync.RWMutex
|
||||
Items map[nnu]runtime.Object
|
||||
changes []watch.Event // one change per resourceVersion
|
||||
Broadcaster *watch.Broadcaster
|
||||
}
|
||||
|
||||
type FakePVControllerSource struct {
|
||||
FakeControllerSource
|
||||
}
|
||||
|
||||
type FakePVCControllerSource struct {
|
||||
FakeControllerSource
|
||||
}
|
||||
|
||||
// namespace, name, uid to be used as a key.
|
||||
type nnu struct {
|
||||
namespace, name string
|
||||
uid types.UID
|
||||
}
|
||||
|
||||
// Add adds an object to the set and sends an add event to watchers.
|
||||
// obj's ResourceVersion is set.
|
||||
func (f *FakeControllerSource) Add(obj runtime.Object) {
|
||||
f.Change(watch.Event{Type: watch.Added, Object: obj}, 1)
|
||||
}
|
||||
|
||||
// Modify updates an object in the set and sends a modified event to watchers.
|
||||
// obj's ResourceVersion is set.
|
||||
func (f *FakeControllerSource) Modify(obj runtime.Object) {
|
||||
f.Change(watch.Event{Type: watch.Modified, Object: obj}, 1)
|
||||
}
|
||||
|
||||
// Delete deletes an object from the set and sends a delete event to watchers.
|
||||
// obj's ResourceVersion is set.
|
||||
func (f *FakeControllerSource) Delete(lastValue runtime.Object) {
|
||||
f.Change(watch.Event{Type: watch.Deleted, Object: lastValue}, 1)
|
||||
}
|
||||
|
||||
// AddDropWatch adds an object to the set but forgets to send an add event to
|
||||
// watchers.
|
||||
// obj's ResourceVersion is set.
|
||||
func (f *FakeControllerSource) AddDropWatch(obj runtime.Object) {
|
||||
f.Change(watch.Event{Type: watch.Added, Object: obj}, 0)
|
||||
}
|
||||
|
||||
// ModifyDropWatch updates an object in the set but forgets to send a modify
|
||||
// event to watchers.
|
||||
// obj's ResourceVersion is set.
|
||||
func (f *FakeControllerSource) ModifyDropWatch(obj runtime.Object) {
|
||||
f.Change(watch.Event{Type: watch.Modified, Object: obj}, 0)
|
||||
}
|
||||
|
||||
// DeleteDropWatch deletes an object from the set but forgets to send a delete
|
||||
// event to watchers.
|
||||
// obj's ResourceVersion is set.
|
||||
func (f *FakeControllerSource) DeleteDropWatch(lastValue runtime.Object) {
|
||||
f.Change(watch.Event{Type: watch.Deleted, Object: lastValue}, 0)
|
||||
}
|
||||
|
||||
func (f *FakeControllerSource) key(accessor metav1.Object) nnu {
|
||||
return nnu{accessor.GetNamespace(), accessor.GetName(), accessor.GetUID()}
|
||||
}
|
||||
|
||||
// Change records the given event (setting the object's resource version) and
|
||||
// sends a watch event with the specified probability.
|
||||
func (f *FakeControllerSource) Change(e watch.Event, watchProbability float64) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
|
||||
accessor, err := meta.Accessor(e.Object)
|
||||
if err != nil {
|
||||
panic(err) // this is test code only
|
||||
}
|
||||
|
||||
resourceVersion := len(f.changes) + 1
|
||||
accessor.SetResourceVersion(strconv.Itoa(resourceVersion))
|
||||
f.changes = append(f.changes, e)
|
||||
key := f.key(accessor)
|
||||
switch e.Type {
|
||||
case watch.Added, watch.Modified:
|
||||
f.Items[key] = e.Object
|
||||
case watch.Deleted:
|
||||
delete(f.Items, key)
|
||||
}
|
||||
|
||||
if rand.Float64() < watchProbability {
|
||||
f.Broadcaster.Action(e.Type, e.Object)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FakeControllerSource) getListItemsLocked() ([]runtime.Object, error) {
|
||||
list := make([]runtime.Object, 0, len(f.Items))
|
||||
for _, obj := range f.Items {
|
||||
// Must make a copy to allow clients to modify the object.
|
||||
// Otherwise, if they make a change and write it back, they
|
||||
// will inadvertently change our canonical copy (in
|
||||
// addition to racing with other clients).
|
||||
objCopy, err := scheme.Scheme.DeepCopy(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list = append(list, objCopy.(runtime.Object))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// List returns a list object, with its resource version set.
|
||||
func (f *FakeControllerSource) List(options metav1.ListOptions) (runtime.Object, error) {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
list, err := f.getListItemsLocked()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listObj := &v1.List{}
|
||||
if err := meta.SetList(listObj, list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listAccessor, err := meta.ListAccessor(listObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceVersion := len(f.changes)
|
||||
listAccessor.SetResourceVersion(strconv.Itoa(resourceVersion))
|
||||
return listObj, nil
|
||||
}
|
||||
|
||||
// List returns a list object, with its resource version set.
|
||||
func (f *FakePVControllerSource) List(options metav1.ListOptions) (runtime.Object, error) {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
list, err := f.FakeControllerSource.getListItemsLocked()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listObj := &v1.PersistentVolumeList{}
|
||||
if err := meta.SetList(listObj, list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listAccessor, err := meta.ListAccessor(listObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceVersion := len(f.changes)
|
||||
listAccessor.SetResourceVersion(strconv.Itoa(resourceVersion))
|
||||
return listObj, nil
|
||||
}
|
||||
|
||||
// List returns a list object, with its resource version set.
|
||||
func (f *FakePVCControllerSource) List(options metav1.ListOptions) (runtime.Object, error) {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
list, err := f.FakeControllerSource.getListItemsLocked()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listObj := &v1.PersistentVolumeClaimList{}
|
||||
if err := meta.SetList(listObj, list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listAccessor, err := meta.ListAccessor(listObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceVersion := len(f.changes)
|
||||
listAccessor.SetResourceVersion(strconv.Itoa(resourceVersion))
|
||||
return listObj, nil
|
||||
}
|
||||
|
||||
// Watch returns a watch, which will be pre-populated with all changes
|
||||
// after resourceVersion.
|
||||
func (f *FakeControllerSource) Watch(options metav1.ListOptions) (watch.Interface, error) {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
rc, err := strconv.Atoi(options.ResourceVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rc < len(f.changes) {
|
||||
changes := []watch.Event{}
|
||||
for _, c := range f.changes[rc:] {
|
||||
// Must make a copy to allow clients to modify the
|
||||
// object. Otherwise, if they make a change and write
|
||||
// it back, they will inadvertently change the our
|
||||
// canonical copy (in addition to racing with other
|
||||
// clients).
|
||||
objCopy, err := scheme.Scheme.DeepCopy(c.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes = append(changes, watch.Event{Type: c.Type, Object: objCopy.(runtime.Object)})
|
||||
}
|
||||
return f.Broadcaster.WatchWithPrefix(changes), nil
|
||||
} else if rc > len(f.changes) {
|
||||
return nil, errors.New("resource version in the future not supported by this fake")
|
||||
}
|
||||
return f.Broadcaster.Watch(), nil
|
||||
}
|
||||
|
||||
// Shutdown closes the underlying broadcaster, waiting for events to be
|
||||
// delivered. It's an error to call any method after calling shutdown. This is
|
||||
// enforced by Shutdown() leaving f locked.
|
||||
func (f *FakeControllerSource) Shutdown() {
|
||||
f.lock.Lock() // Purposely no unlock.
|
||||
f.Broadcaster.Shutdown()
|
||||
}
|
Loading…
Reference in a new issue