From 7fdbe948987153bd4fcfca8cb04c8fc2eeb6d554 Mon Sep 17 00:00:00 2001 From: Shawn Hurley Date: Wed, 13 Feb 2019 11:35:07 -0500 Subject: [PATCH] adding APIReader to the manager and injection. --- pkg/manager/internal.go | 12 +++++- pkg/manager/manager.go | 11 ++++++ pkg/manager/manager_test.go | 5 +++ pkg/runtime/inject/inject.go | 14 +++++++ pkg/runtime/inject/inject_test.go | 63 +++++++++++++++++++++++++------ 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/pkg/manager/internal.go b/pkg/manager/internal.go index ac235adecd..cf680e1e53 100644 --- a/pkg/manager/internal.go +++ b/pkg/manager/internal.go @@ -33,10 +33,10 @@ import ( "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/internal/log" "sigs.k8s.io/controller-runtime/pkg/metrics" "sigs.k8s.io/controller-runtime/pkg/recorder" "sigs.k8s.io/controller-runtime/pkg/runtime/inject" - logf "sigs.k8s.io/controller-runtime/pkg/internal/log" "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types" ) @@ -61,6 +61,9 @@ type controllerManager struct { // client is the client injected into Controllers (and EventHandlers, Sources and Predicates). client client.Client + // apiReader is the reader that will make requests to the api server and not the cache. + apiReader client.Reader + // fieldIndexes knows how to add field indexes over the Cache used by this controller, // which can later be consumed via field selectors from the injected client. fieldIndexes client.FieldIndexer @@ -124,6 +127,9 @@ func (cm *controllerManager) SetFields(i interface{}) error { if _, err := inject.ClientInto(cm.client, i); err != nil { return err } + if _, err := inject.APIReaderInto(cm.apiReader, i); err != nil { + return err + } if _, err := inject.SchemeInto(cm.scheme, i); err != nil { return err } @@ -177,6 +183,10 @@ func (cm *controllerManager) GetRESTMapper() meta.RESTMapper { return cm.mapper } +func (cm *controllerManager) GetAPIReader() client.Reader { + return cm.apiReader +} + func (cm *controllerManager) serveMetrics(stop <-chan struct{}) { handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{ ErrorHandling: promhttp.HTTPErrorOnError, diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 8d346df6c1..96b7027924 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -79,6 +79,11 @@ type Manager interface { // GetRESTMapper returns a RESTMapper GetRESTMapper() meta.RESTMapper + + // GetAPIReader returns a reader that will be configured to use the API server. + // This should be used sparingly and only when the client does not fit your + // use case. + GetAPIReader() client.Reader } // Options are the arguments for creating a new Manager @@ -180,6 +185,11 @@ func New(config *rest.Config, options Options) (Manager, error) { return nil, err } + apiReader, err := client.New(config, client.Options{Scheme: options.Scheme, Mapper: mapper}) + if err != nil { + return nil, err + } + writeObj, err := options.NewClient(cache, config, client.Options{Scheme: options.Scheme, Mapper: mapper}) if err != nil { return nil, err @@ -224,6 +234,7 @@ func New(config *rest.Config, options Options) (Manager, error) { cache: cache, fieldIndexes: cache, client: writeObj, + apiReader: apiReader, recorderProvider: recorderProvider, resourceLock: resourceLock, mapper: mapper, diff --git a/pkg/manager/manager_test.go b/pkg/manager/manager_test.go index a5a63e3e4f..e3b7753671 100644 --- a/pkg/manager/manager_test.go +++ b/pkg/manager/manager_test.go @@ -606,6 +606,11 @@ var _ = Describe("manger.Manager", func() { Expect(err).NotTo(HaveOccurred()) Expect(m.GetRecorder("test")).NotTo(BeNil()) }) + It("should provide a function to get the APIReader", func() { + m, err := New(cfg, Options{}) + Expect(err).NotTo(HaveOccurred()) + Expect(m.GetAPIReader()).NotTo(BeNil()) + }) }) var _ reconcile.Reconciler = &failRec{} diff --git a/pkg/runtime/inject/inject.go b/pkg/runtime/inject/inject.go index 7d1fbd4a6d..34c7d213b6 100644 --- a/pkg/runtime/inject/inject.go +++ b/pkg/runtime/inject/inject.go @@ -40,6 +40,20 @@ func CacheInto(c cache.Cache, i interface{}) (bool, error) { return false, nil } +// APIReader is used by the Manager to inject the APIReader into necessary types. +type APIReader interface { + InjectAPIReader(client.Reader) error +} + +// APIReaderInto will set APIReader on i and return the result if it implements APIReaderInto. +// Returns false if i does not implement APIReader +func APIReaderInto(reader client.Reader, i interface{}) (bool, error) { + if s, ok := i.(APIReader); ok { + return true, s.InjectAPIReader(reader) + } + return false, nil +} + // Config is used by the ControllerManager to inject Config into Sources, EventHandlers, Predicates, and // Reconciles type Config interface { diff --git a/pkg/runtime/inject/inject_test.go b/pkg/runtime/inject/inject_test.go index 4a18a73898..6f96ab5f63 100644 --- a/pkg/runtime/inject/inject_test.go +++ b/pkg/runtime/inject/inject_test.go @@ -150,6 +150,27 @@ var _ = Describe("runtime inject", func() { Expect(res).To(Equal(true)) }) + It("should set api reader", func() { + apiReader := &client.DelegatingReader{} + + By("Validating injecting client") + res, err := APIReaderInto(apiReader, instance) + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal(true)) + Expect(apiReader).To(Equal(instance.GetAPIReader())) + + By("Returning false if the type does not implement inject.Client") + res, err = APIReaderInto(apiReader, uninjectable) + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal(false)) + Expect(uninjectable.GetAPIReader()).To(BeNil()) + + By("Returning an error if client injection fails") + res, err = APIReaderInto(nil, instance) + Expect(err).To(Equal(errInjectFail)) + Expect(res).To(Equal(true)) + }) + It("should set dependencies", func() { f := func(interface{}) error { return nil } @@ -175,12 +196,13 @@ var _ = Describe("runtime inject", func() { }) type testSource struct { - scheme *runtime.Scheme - cache cache.Cache - config *rest.Config - client client.Client - f Func - stop <-chan struct{} + scheme *runtime.Scheme + cache cache.Cache + config *rest.Config + client client.Client + apiReader client.Reader + f Func + stop <-chan struct{} } func (s *testSource) InjectCache(c cache.Cache) error { @@ -223,6 +245,14 @@ func (s *testSource) InjectStopChannel(stop <-chan struct{}) error { return fmt.Errorf("injection fails") } +func (s *testSource) InjectAPIReader(reader client.Reader) error { + if reader != nil { + s.apiReader = reader + return nil + } + return fmt.Errorf("injection fails") +} + func (s *testSource) InjectFunc(f Func) error { if f != nil { s.f = f @@ -247,6 +277,10 @@ func (s *testSource) GetClient() client.Client { return s.client } +func (s *testSource) GetAPIReader() client.Reader { + return s.apiReader +} + func (s *testSource) GetFunc() Func { return s.f } @@ -256,12 +290,13 @@ func (s *testSource) GetStop() <-chan struct{} { } type failSource struct { - scheme *runtime.Scheme - cache cache.Cache - config *rest.Config - client client.Client - f Func - stop <-chan struct{} + scheme *runtime.Scheme + cache cache.Cache + config *rest.Config + client client.Client + apiReader client.Reader + f Func + stop <-chan struct{} } func (s *failSource) GetCache() cache.Cache { @@ -280,6 +315,10 @@ func (s *failSource) GetClient() client.Client { return s.client } +func (s *failSource) GetAPIReader() client.Reader { + return s.apiReader +} + func (s *failSource) GetFunc() Func { return s.f }