Add the ability to scope informers to a namespace with injection. (#626)

This commit is contained in:
Matt Moore 2019-09-03 07:17:00 -07:00 committed by Knative Prow Robot
parent 8cae700d29
commit 524f6c5bb2
13 changed files with 277 additions and 15 deletions

View File

@ -64,11 +64,15 @@ func (g *factoryGenerator) GenerateType(c *generator.Context, t *types.Type, w i
klog.V(5).Infof("processing type %v", t)
m := map[string]interface{}{
"cachingClientGet": c.Universe.Type(types.Name{Package: g.cachingClientSetPackage, Name: "Get"}),
"informersNewSharedInformerFactory": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "NewSharedInformerFactory"}),
"informersSharedInformerFactory": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "SharedInformerFactory"}),
"injectionRegisterInformerFactory": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "Default.RegisterInformerFactory"}),
"controllerGetResyncPeriod": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "GetResyncPeriod"}),
"cachingClientGet": c.Universe.Type(types.Name{Package: g.cachingClientSetPackage, Name: "Get"}),
"informersNewSharedInformerFactoryWithOptions": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "NewSharedInformerFactoryWithOptions"}),
"informersSharedInformerOption": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "SharedInformerOption"}),
"informersWithNamespace": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "WithNamespace"}),
"informersSharedInformerFactory": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "SharedInformerFactory"}),
"injectionRegisterInformerFactory": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "Default.RegisterInformerFactory"}),
"injectionHasNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "HasNamespaceScope"}),
"injectionGetNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "GetNamespaceScope"}),
"controllerGetResyncPeriod": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "GetResyncPeriod"}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
@ -90,8 +94,12 @@ type Key struct{}
func withInformerFactory(ctx context.Context) context.Context {
c := {{.cachingClientGet|raw}}(ctx)
opts := make([]{{.informersSharedInformerOption|raw}}, 0, 1)
if {{.injectionHasNamespace|raw}}(ctx) {
opts = append(opts, {{.informersWithNamespace|raw}}({{.injectionGetNamespace|raw}}(ctx)))
}
return context.WithValue(ctx, Key{},
{{.informersNewSharedInformerFactory|raw}}(c, {{.controllerGetResyncPeriod|raw}}(ctx)))
{{.informersNewSharedInformerFactoryWithOptions|raw}}(c, {{.controllerGetResyncPeriod|raw}}(ctx), opts...))
}
// Get extracts the InformerFactory from the context.

View File

@ -66,14 +66,18 @@ func (g *fakeFactoryGenerator) GenerateType(c *generator.Context, t *types.Type,
klog.V(5).Infof("processing type %v", t)
m := map[string]interface{}{
"factoryKey": c.Universe.Type(types.Name{Package: g.factoryInjectionPkg, Name: "Key"}),
"factoryGet": c.Universe.Function(types.Name{Package: g.factoryInjectionPkg, Name: "Get"}),
"clientGet": c.Universe.Function(types.Name{Package: g.fakeClientInjectionPkg, Name: "Get"}),
"informersNewSharedInformerFactory": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "NewSharedInformerFactory"}),
"factoryKey": c.Universe.Type(types.Name{Package: g.factoryInjectionPkg, Name: "Key"}),
"factoryGet": c.Universe.Function(types.Name{Package: g.factoryInjectionPkg, Name: "Get"}),
"clientGet": c.Universe.Function(types.Name{Package: g.fakeClientInjectionPkg, Name: "Get"}),
"informersNewSharedInformerFactoryWithOptions": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "NewSharedInformerFactoryWithOptions"}),
"informersSharedInformerOption": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "SharedInformerOption"}),
"informersWithNamespace": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "WithNamespace"}),
"injectionRegisterInformerFactory": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/injection",
Name: "Fake.RegisterInformerFactory",
}),
"injectionHasNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "HasNamespaceScope"}),
"injectionGetNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "GetNamespaceScope"}),
"controllerGetResyncPeriod": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "GetResyncPeriod"}),
}
@ -91,7 +95,11 @@ func init() {
func withInformerFactory(ctx context.Context) context.Context {
c := {{.clientGet|raw}}(ctx)
opts := make([]{{.informersSharedInformerOption|raw}}, 0, 1)
if {{.injectionHasNamespace|raw}}(ctx) {
opts = append(opts, {{.informersWithNamespace|raw}}({{.injectionGetNamespace|raw}}(ctx)))
}
return context.WithValue(ctx, {{.factoryKey|raw}}{},
{{.informersNewSharedInformerFactory|raw}}(c, {{.controllerGetResyncPeriod|raw}}(ctx)))
{{.informersNewSharedInformerFactoryWithOptions|raw}}(c, {{.controllerGetResyncPeriod|raw}}(ctx), opts...))
}
`

49
injection/context.go Normal file
View File

@ -0,0 +1,49 @@
/*
Copyright 2019 The Knative 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 injection
import (
"context"
)
// nsKey is the key that namespaces are associated with on
// contexts returned by WithNamespaceScope.
type nsKey struct{}
// WithNamespaceScope associates a namespace scoping with the
// provided context, which will scope the informers produced
// by the downstream informer factories.
func WithNamespaceScope(ctx context.Context, namespace string) context.Context {
return context.WithValue(ctx, nsKey{}, namespace)
}
// HasNamespaceScope determines whether the provided context has
// been scoped to a particular namespace.
func HasNamespaceScope(ctx context.Context) bool {
return GetNamespaceScope(ctx) != ""
}
// GetNamespaceScope accesses the namespace associated with the
// provided context. This should be called when the injection
// logic is setting up shared informer factories.
func GetNamespaceScope(ctx context.Context) string {
value := ctx.Value(nsKey{})
if value == nil {
return ""
}
return value.(string)
}

41
injection/context_test.go Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright 2019 The Knative 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 injection
import (
"context"
"testing"
)
func TestGetBaseline(t *testing.T) {
ctx := context.Background()
if HasNamespaceScope(ctx) {
t.Error("HasNamespaceScope() = true, wanted false")
}
want := "this-is-the-best-ns-evar"
ctx = WithNamespaceScope(ctx, want)
if !HasNamespaceScope(ctx) {
t.Error("HasNamespaceScope() = false, wanted true")
}
if got := GetNamespaceScope(ctx); got != want {
t.Errorf("GetNamespaceScope() = %v, wanted %v", got, want)
}
}

View File

@ -65,4 +65,41 @@ limitations under the License.
// dave.NewController,
// )
// }
//
// If you want to adapt the above to run the controller within a single
// namespace, you can instead do something like:
//
// package main
//
// import (
// // The set of controllers this controller process runs.
// // Linking these will register their transitive dependencies, after
// // which the shared main can set up the rest.
// "github.com/knative/foo/pkg/reconciler/matt"
// "github.com/knative/foo/pkg/reconciler/scott"
// "github.com/knative/foo/pkg/reconciler/ville"
// "github.com/knative/foo/pkg/reconciler/dave"
//
// // This defines the shared main for injected controllers.
// "knative.dev/pkg/injection/sharedmain"
//
// // These are used to set up the context.
// "knative.dev/pkg/injection"
// "knative.dev/pkg/signals"
// )
//
// func main() {
// // Scope the shared informer factories to the provided namespace.
// ctx := injection.WithNamespace(signals.NewContext(), "the-namespace")
//
// // Use our initial context when setting up the controllers.
// sharedmain.MainWithContext(ctx, "mycomponent",
// // We pass in the list of controllers to construct, and that's it!
// // If we forget to add this, go will complain about the unused import.
// matt.NewController,
// scott.NewController,
// ville.NewController,
// dave.NewController,
// )
// }
package injection

View File

@ -38,8 +38,12 @@ type Key struct{}
func withInformerFactory(ctx context.Context) context.Context {
axc := apiextclient.Get(ctx)
opts := make([]informers.SharedInformerOption, 0, 1)
if injection.HasNamespaceScope(ctx) {
opts = append(opts, informers.WithNamespace(injection.GetNamespaceScope(ctx)))
}
return context.WithValue(ctx, Key{},
informers.NewSharedInformerFactory(axc, controller.GetResyncPeriod(ctx)))
informers.NewSharedInformerFactoryWithOptions(axc, controller.GetResyncPeriod(ctx), opts...))
}
// Get extracts the Kubernetes Api Extensions InformerFactory from the context.

View File

@ -64,3 +64,28 @@ func TestRegistration(t *testing.T) {
t.Error("Get() = nil, wanted non-nil")
}
}
func TestRegistrationWithNamespace(t *testing.T) {
ctx := context.Background()
ctx = injection.WithNamespaceScope(ctx, "secret-sauce")
// Check how many informer factories have registered.
inffs := injection.Default.GetInformerFactories()
if want, got := 1, len(inffs); want != got {
t.Errorf("GetInformerFactories() = %d, wanted %d", want, got)
}
// Setup the informers.
var infs []controller.Informer
ctx, infs = injection.Default.SetupInformers(ctx, &rest.Config{})
// We should see that a single informer was set up.
if want, got := 0, len(infs); want != got {
t.Errorf("SetupInformers() = %d, wanted %d", want, got)
}
// Get our informer from the context.
if inf := Get(ctx); inf == nil {
t.Error("Get() = nil, wanted non-nil")
}
}

View File

@ -35,6 +35,10 @@ func init() {
func withInformerFactory(ctx context.Context) context.Context {
kc := fake.Get(ctx)
opts := make([]informers.SharedInformerOption, 0, 1)
if injection.HasNamespaceScope(ctx) {
opts = append(opts, informers.WithNamespace(injection.GetNamespaceScope(ctx)))
}
return context.WithValue(ctx, factory.Key{},
informers.NewSharedInformerFactory(kc, controller.GetResyncPeriod(ctx)))
informers.NewSharedInformerFactoryWithOptions(kc, controller.GetResyncPeriod(ctx), opts...))
}

View File

@ -64,3 +64,29 @@ func TestRegistration(t *testing.T) {
t.Error("Get() = nil, wanted non-nil")
}
}
func TestRegistrationWithNamespace(t *testing.T) {
ctx := context.Background()
ctx = injection.WithNamespaceScope(ctx, "fake-news")
// Check how many informer factories have registered.
inffs := injection.Fake.GetInformerFactories()
if want, got := 1, len(inffs); want != got {
t.Errorf("GetInformerFactories() = %d, wanted %d", want, got)
}
// Setup the informers.
var infs []controller.Informer
ctx, infs = injection.Fake.SetupInformers(ctx, &rest.Config{})
// We should see that a single informer was set up.
if want, got := 0, len(infs); want != got {
t.Errorf("SetupInformers() = %d, wanted %d", want, got)
}
// Get our informer from the context.
if inf := Get(ctx); inf == nil {
t.Error("Get() = nil, wanted non-nil")
}
}

View File

@ -37,8 +37,12 @@ type Key struct{}
func withInformerFactory(ctx context.Context) context.Context {
kc := kubeclient.Get(ctx)
opts := make([]informers.SharedInformerOption, 0, 1)
if injection.HasNamespaceScope(ctx) {
opts = append(opts, informers.WithNamespace(injection.GetNamespaceScope(ctx)))
}
return context.WithValue(ctx, Key{},
informers.NewSharedInformerFactory(kc, controller.GetResyncPeriod(ctx)))
informers.NewSharedInformerFactoryWithOptions(kc, controller.GetResyncPeriod(ctx), opts...))
}
// Get extracts the Kubernetes InformerFactory from the context.

View File

@ -64,3 +64,29 @@ func TestRegistration(t *testing.T) {
t.Error("Get() = nil, wanted non-nil")
}
}
func TestRegistrationWithNamespace(t *testing.T) {
ctx := context.Background()
ctx = injection.WithNamespaceScope(ctx, "secret-sauce")
// Check how many informer factories have registered.
inffs := injection.Default.GetInformerFactories()
if want, got := 1, len(inffs); want != got {
t.Errorf("GetInformerFactories() = %d, wanted %d", want, got)
}
// Setup the informers.
var infs []controller.Informer
ctx, infs = injection.Default.SetupInformers(ctx, &rest.Config{})
// We should see that a single informer was set up.
if want, got := 0, len(infs); want != got {
t.Errorf("SetupInformers() = %d, wanted %d", want, got)
}
// Get our informer from the context.
if inf := Get(ctx); inf == nil {
t.Error("Get() = nil, wanted non-nil")
}
}

View File

@ -35,6 +35,10 @@ func init() {
func withInformerFactory(ctx context.Context) context.Context {
kc := fake.Get(ctx)
opts := make([]informers.SharedInformerOption, 0, 1)
if injection.HasNamespaceScope(ctx) {
opts = append(opts, informers.WithNamespace(injection.GetNamespaceScope(ctx)))
}
return context.WithValue(ctx, factory.Key{},
informers.NewSharedInformerFactory(kc, controller.GetResyncPeriod(ctx)))
informers.NewSharedInformerFactoryWithOptions(kc, controller.GetResyncPeriod(ctx), opts...))
}

View File

@ -64,3 +64,29 @@ func TestRegistration(t *testing.T) {
t.Error("Get() = nil, wanted non-nil")
}
}
func TestRegistrationWithNamespace(t *testing.T) {
ctx := context.Background()
ctx = injection.WithNamespaceScope(ctx, "fake-news")
// Check how many informer factories have registered.
inffs := injection.Fake.GetInformerFactories()
if want, got := 1, len(inffs); want != got {
t.Errorf("GetInformerFactories() = %d, wanted %d", want, got)
}
// Setup the informers.
var infs []controller.Informer
ctx, infs = injection.Fake.SetupInformers(ctx, &rest.Config{})
// We should see that a single informer was set up.
if want, got := 0, len(infs); want != got {
t.Errorf("SetupInformers() = %d, wanted %d", want, got)
}
// Get our informer from the context.
if inf := Get(ctx); inf == nil {
t.Error("Get() = nil, wanted non-nil")
}
}