diff --git a/cmd/operator/main.go b/cmd/operator/main.go index e7f6767be7..06b3f48d58 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -19,11 +19,18 @@ package main import ( "knative.dev/operator/pkg/reconciler/knativeeventing" "knative.dev/operator/pkg/reconciler/knativeserving" + kubefilteredfactory "knative.dev/pkg/client/injection/kube/informers/factory/filtered" "knative.dev/pkg/injection/sharedmain" + "knative.dev/pkg/signals" ) func main() { - sharedmain.Main("knative-operator", + ctx := signals.NewContext() + ctx = kubefilteredfactory.WithSelectors(ctx, + knativeserving.Selector, + knativeeventing.Selector, + ) + sharedmain.MainWithContext(ctx, "knative-operator", knativeserving.NewController, knativeeventing.NewController, ) diff --git a/pkg/reconciler/common/transformers.go b/pkg/reconciler/common/transformers.go index 9ca49aeb4d..e45bd1b987 100644 --- a/pkg/reconciler/common/transformers.go +++ b/pkg/reconciler/common/transformers.go @@ -83,3 +83,16 @@ func InjectNamespace(manifest *mf.Manifest, instance base.KComponent, extra ...m *manifest = m return nil } + +// InjectLabel adds the given key and value as label. +func InjectLabel(key, value string) mf.Transformer { + return func(u *unstructured.Unstructured) error { + curr := u.GetLabels() + if curr == nil { + curr = map[string]string{} + } + curr[key] = value + u.SetLabels(curr) + return nil + } +} diff --git a/pkg/reconciler/knativeeventing/controller.go b/pkg/reconciler/knativeeventing/controller.go index cf341fa33f..bc1340ebff 100644 --- a/pkg/reconciler/knativeeventing/controller.go +++ b/pkg/reconciler/knativeeventing/controller.go @@ -28,13 +28,23 @@ import ( knereconciler "knative.dev/operator/pkg/client/injection/reconciler/operator/v1beta1/knativeeventing" "knative.dev/operator/pkg/reconciler/common" kubeclient "knative.dev/pkg/client/injection/kube/client" - deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment" + deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/filtered" + configmapinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered" "knative.dev/pkg/configmap" "knative.dev/pkg/controller" "knative.dev/pkg/injection" "knative.dev/pkg/logging" ) +const ( + // SelectorKey is the key of the selector for the KnativeEventing resources. + SelectorKey = "app.kubernetes.io/name" + // SelectorValue is the value of the selector for the KnativeEventing resources. + SelectorValue = "knative-eventing" + // Selector is the selector for the KnativeEventing resources. + Selector = SelectorKey + "=" + SelectorValue +) + // NewController initializes the controller and is called by the generated code // Registers eventhandlers to enqueue events func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { @@ -45,7 +55,8 @@ func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl func NewExtendedController(generator common.ExtensionGenerator) injection.ControllerConstructor { return func(ctx context.Context, cmw configmap.Watcher) *controller.Impl { knativeEventingInformer := knativeEventinginformer.Get(ctx) - deploymentInformer := deploymentinformer.Get(ctx) + deploymentInformer := deploymentinformer.Get(ctx, Selector) + configMapInformer := configmapinformer.Get(ctx, Selector) kubeClient := kubeclient.Get(ctx) logger := logging.FromContext(ctx) @@ -72,6 +83,10 @@ func NewExtendedController(generator common.ExtensionGenerator) injection.Contro FilterFunc: controller.FilterControllerGVK(v1beta1.SchemeGroupVersion.WithKind("KnativeEventing")), Handler: controller.HandleAll(impl.EnqueueControllerOf), }) + configMapInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: controller.FilterControllerGVK(v1beta1.SchemeGroupVersion.WithKind("KnativeEventing")), + Handler: controller.HandleAll(impl.EnqueueControllerOf), + }) return impl } diff --git a/pkg/reconciler/knativeeventing/knativeeventing.go b/pkg/reconciler/knativeeventing/knativeeventing.go index dab94829f2..cdecea40a7 100644 --- a/pkg/reconciler/knativeeventing/knativeeventing.go +++ b/pkg/reconciler/knativeeventing/knativeeventing.go @@ -160,6 +160,8 @@ func (r *Reconciler) transform(ctx context.Context, manifest *mf.Manifest, comp kec.DefaultBrokerConfigMapTransform(instance, logger), kec.SinkBindingSelectionModeTransform(instance, logger), kec.ReplicasEnvVarsTransform(manifest.Client), + // Ensure all resources have the selector applied so that the controller re-queues applied resources when they change. + common.InjectLabel(SelectorKey, SelectorValue), } extra = append(extra, r.extension.Transformers(instance)...) return common.Transform(ctx, manifest, instance, extra...) diff --git a/pkg/reconciler/knativeserving/controller.go b/pkg/reconciler/knativeserving/controller.go index c2b7f3f59b..1e8260816a 100644 --- a/pkg/reconciler/knativeserving/controller.go +++ b/pkg/reconciler/knativeserving/controller.go @@ -28,7 +28,8 @@ import ( knsreconciler "knative.dev/operator/pkg/client/injection/reconciler/operator/v1beta1/knativeserving" "knative.dev/operator/pkg/reconciler/common" kubeclient "knative.dev/pkg/client/injection/kube/client" - deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment" + deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/filtered" + configmapinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered" "knative.dev/pkg/configmap" "knative.dev/pkg/controller" "knative.dev/pkg/injection" @@ -41,11 +42,21 @@ func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl return NewExtendedController(common.NoExtension)(ctx, cmw) } +const ( + // SelectorKey is the key of the selector for the KnativeServing resources. + SelectorKey = "app.kubernetes.io/name" + // SelectorValue is the value of the selector for the KnativeServing resources. + SelectorValue = "knative-serving" + // Selector is the selector for the KnativeServing resources. + Selector = SelectorKey + "=" + SelectorValue +) + // NewExtendedController returns a controller extended to a specific platform func NewExtendedController(generator common.ExtensionGenerator) injection.ControllerConstructor { return func(ctx context.Context, cmw configmap.Watcher) *controller.Impl { knativeServingInformer := knativeServinginformer.Get(ctx) - deploymentInformer := deploymentinformer.Get(ctx) + deploymentInformer := deploymentinformer.Get(ctx, Selector) + configMapInformer := configmapinformer.Get(ctx, Selector) kubeClient := kubeclient.Get(ctx) logger := logging.FromContext(ctx) @@ -72,6 +83,10 @@ func NewExtendedController(generator common.ExtensionGenerator) injection.Contro FilterFunc: controller.FilterControllerGVK(v1beta1.SchemeGroupVersion.WithKind("KnativeServing")), Handler: controller.HandleAll(impl.EnqueueControllerOf), }) + configMapInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: controller.FilterControllerGVK(v1beta1.SchemeGroupVersion.WithKind("KnativeServing")), + Handler: controller.HandleAll(impl.EnqueueControllerOf), + }) return impl } diff --git a/pkg/reconciler/knativeserving/knativeserving.go b/pkg/reconciler/knativeserving/knativeserving.go index cf54f4aeb0..6810585b52 100644 --- a/pkg/reconciler/knativeserving/knativeserving.go +++ b/pkg/reconciler/knativeserving/knativeserving.go @@ -142,6 +142,8 @@ func (r *Reconciler) transform(ctx context.Context, manifest *mf.Manifest, comp extra := []mf.Transformer{ ksc.CustomCertsTransform(instance, logger), ksc.AggregationRuleTransform(manifest.Client), + // Ensure all resources have the selector applied so that the controller re-queues applied resources when they change. + common.InjectLabel(SelectorKey, SelectorValue), } extra = append(extra, r.extension.Transformers(instance)...) extra = append(extra, ingress.Transformers(ctx, instance)...) diff --git a/test/e2e/knativeeventingdeployment_test.go b/test/e2e/knativeeventingdeployment_test.go index e2dc052cd6..6296d5294c 100644 --- a/test/e2e/knativeeventingdeployment_test.go +++ b/test/e2e/knativeeventingdeployment_test.go @@ -53,6 +53,7 @@ func TestKnativeEventingDeployment(t *testing.T) { t.Run("restore", func(t *testing.T) { resources.AssertKEOperatorCRReadyStatus(t, clients, names) resources.DeleteAndVerifyEventingDeployments(t, clients, names) + resources.DeleteAndVerifyEventingConfigMaps(t, clients, names) }) // Delete the KnativeEventing to see if all resources will be removed diff --git a/test/e2e/knativeservingdeployment_test.go b/test/e2e/knativeservingdeployment_test.go index 6221e057cf..2f4e796fed 100644 --- a/test/e2e/knativeservingdeployment_test.go +++ b/test/e2e/knativeservingdeployment_test.go @@ -58,6 +58,7 @@ func TestKnativeServingDeployment(t *testing.T) { t.Run("restore", func(t *testing.T) { resources.AssertKSOperatorCRReadyStatus(t, clients, names) resources.DeleteAndVerifyDeployments(t, clients, names) + resources.DeleteAndVerifyConfigMaps(t, clients, names) }) // Delete the KnativeServing to see if all resources will be removed diff --git a/test/resources/verify.go b/test/resources/verify.go index 96df0ad327..bacbb49a2d 100644 --- a/test/resources/verify.go +++ b/test/resources/verify.go @@ -211,6 +211,47 @@ func DeleteAndVerifyDeployments(t *testing.T, clients *test.Clients, names test. t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name) } +// DeleteAndVerifyConfigMaps verify whether all the ConfigMaps for knative serving are able to recreate, when they are deleted. +func DeleteAndVerifyConfigMaps(t *testing.T, clients *test.Clients, names test.ResourceNames) { + cmList, err := clients.KubeClient.CoreV1().ConfigMaps(names.Namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + t.Fatalf("Failed to get any ConfigMap under the namespace %q: %v", + test.ServingOperatorNamespace, err) + } + if len(cmList.Items) == 0 { + t.Fatalf("No ConfigMap under the namespace %q was found", + test.ServingOperatorNamespace) + } + // Delete the first ConfigMap and verify the operator recreates it + configMap := cmList.Items[0] + if err := clients.KubeClient.CoreV1().ConfigMaps(configMap.Namespace).Delete(context.TODO(), configMap.Name, metav1.DeleteOptions{}); err != nil { + t.Fatalf("Failed to delete ConfigMap %s/%s: %v", configMap.Namespace, configMap.Name, err) + } + + waitErr := wait.PollUntilContextTimeout(context.TODO(), Interval, Timeout, true, func(context.Context) (bool, error) { + _, err := clients.KubeClient.CoreV1().ConfigMaps(configMap.Namespace).Get(context.TODO(), configMap.Name, metav1.GetOptions{}) + if err != nil { + // If the ConfigMap is not found, we continue to wait for the availability. + if apierrs.IsNotFound(err) { + return false, nil + } + return false, err + } + // For ConfigMaps, we consider it recreated as soon as it reappears. + return true, nil + }) + + if waitErr != nil { + t.Fatalf("The ConfigMap %s/%s failed to be recreated: %v", configMap.Namespace, configMap.Name, err) + } + + if _, err := WaitForKnativeServingState(clients.KnativeServing(), test.OperatorName, + IsKnativeServingReady); err != nil { + t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.OperatorName, err) + } + t.Logf("The ConfigMap %s/%s was successfully recreated.", configMap.Namespace, configMap.Name) +} + // KSOperatorCRDelete deletes tha KnativeServing to see if all resources will be deleted func KSOperatorCRDelete(t *testing.T, clients *test.Clients, names test.ResourceNames) { if err := clients.KnativeServing().Delete(context.TODO(), names.KnativeServing, metav1.DeleteOptions{}); err != nil { @@ -399,6 +440,46 @@ func DeleteAndVerifyEventingDeployments(t *testing.T, clients *test.Clients, nam t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name) } +// DeleteAndVerifyEventingConfigMaps verify whether all the ConfigMaps for knative eventing are able to recreate, when they are deleted. +func DeleteAndVerifyEventingConfigMaps(t *testing.T, clients *test.Clients, names test.ResourceNames) { + cmList, err := clients.KubeClient.CoreV1().ConfigMaps(names.Namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + t.Fatalf("Failed to get any ConfigMap under the namespace %q: %v", + test.EventingOperatorNamespace, err) + } + if len(cmList.Items) == 0 { + t.Fatalf("No ConfigMap under the namespace %q was found", + test.EventingOperatorNamespace) + } + // Delete the first ConfigMap and verify the operator recreates it + configMap := cmList.Items[0] + if err := clients.KubeClient.CoreV1().ConfigMaps(configMap.Namespace).Delete(context.TODO(), configMap.Name, metav1.DeleteOptions{}); err != nil { + t.Fatalf("Failed to delete ConfigMap %s/%s: %v", configMap.Namespace, configMap.Name, err) + } + + waitErr := wait.PollUntilContextTimeout(context.TODO(), Interval, Timeout, true, func(context.Context) (bool, error) { + _, err := clients.KubeClient.CoreV1().ConfigMaps(configMap.Namespace).Get(context.TODO(), configMap.Name, metav1.GetOptions{}) + if err != nil { + // If the ConfigMap is not found, we continue to wait for the availability. + if apierrs.IsNotFound(err) { + return false, nil + } + return false, err + } + return true, nil + }) + + if waitErr != nil { + t.Fatalf("The ConfigMap %s/%s failed to reach the desired state: %v", configMap.Namespace, configMap.Name, err) + } + + if _, err := WaitForKnativeEventingState(clients.KnativeEventing(), test.OperatorName, + IsKnativeEventingReady); err != nil { + t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.OperatorName, err) + } + t.Logf("The ConfigMap %s/%s reached the desired state.", configMap.Namespace, configMap.Name) +} + // KEOperatorCRDelete deletes tha KnativeEventing to see if all resources will be deleted func KEOperatorCRDelete(t *testing.T, clients *test.Clients, names test.ResourceNames) { if err := clients.KnativeEventing().Delete(context.TODO(), names.KnativeEventing, metav1.DeleteOptions{}); err != nil { diff --git a/vendor/knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/deployment.go b/vendor/knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/filtered/deployment.go similarity index 55% rename from vendor/knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/deployment.go rename to vendor/knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/filtered/deployment.go index 9b34756a27..89de6fb7e4 100644 --- a/vendor/knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/deployment.go +++ b/vendor/knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/filtered/deployment.go @@ -16,37 +16,50 @@ limitations under the License. // Code generated by injection-gen. DO NOT EDIT. -package deployment +package filtered import ( context "context" v1 "k8s.io/client-go/informers/apps/v1" - factory "knative.dev/pkg/client/injection/kube/informers/factory" + filtered "knative.dev/pkg/client/injection/kube/informers/factory/filtered" controller "knative.dev/pkg/controller" injection "knative.dev/pkg/injection" logging "knative.dev/pkg/logging" ) func init() { - injection.Default.RegisterInformer(withInformer) + injection.Default.RegisterFilteredInformers(withInformer) } // Key is used for associating the Informer inside the context.Context. -type Key struct{} +type Key struct { + Selector string +} -func withInformer(ctx context.Context) (context.Context, controller.Informer) { - f := factory.Get(ctx) - inf := f.Apps().V1().Deployments() - return context.WithValue(ctx, Key{}, inf), inf.Informer() +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := filtered.Get(ctx, selector) + inf := f.Apps().V1().Deployments() + ctx = context.WithValue(ctx, Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs } // Get extracts the typed informer from the context. -func Get(ctx context.Context) v1.DeploymentInformer { - untyped := ctx.Value(Key{}) +func Get(ctx context.Context, selector string) v1.DeploymentInformer { + untyped := ctx.Value(Key{Selector: selector}) if untyped == nil { - logging.FromContext(ctx).Panic( - "Unable to fetch k8s.io/client-go/informers/apps/v1.DeploymentInformer from context.") + logging.FromContext(ctx).Panicf( + "Unable to fetch k8s.io/client-go/informers/apps/v1.DeploymentInformer with selector %s from context.", selector) } return untyped.(v1.DeploymentInformer) } diff --git a/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered/configmap.go b/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered/configmap.go new file mode 100644 index 0000000000..c67b4d2f8c --- /dev/null +++ b/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered/configmap.go @@ -0,0 +1,65 @@ +/* +Copyright 2022 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package filtered + +import ( + context "context" + + v1 "k8s.io/client-go/informers/core/v1" + filtered "knative.dev/pkg/client/injection/kube/informers/factory/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterFilteredInformers(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct { + Selector string +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := filtered.Get(ctx, selector) + inf := f.Core().V1().ConfigMaps() + ctx = context.WithValue(ctx, Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context, selector string) v1.ConfigMapInformer { + untyped := ctx.Value(Key{Selector: selector}) + if untyped == nil { + logging.FromContext(ctx).Panicf( + "Unable to fetch k8s.io/client-go/informers/core/v1.ConfigMapInformer with selector %s from context.", selector) + } + return untyped.(v1.ConfigMapInformer) +} diff --git a/vendor/knative.dev/pkg/client/injection/kube/informers/factory/factory.go b/vendor/knative.dev/pkg/client/injection/kube/informers/factory/filtered/filtered_factory.go similarity index 54% rename from vendor/knative.dev/pkg/client/injection/kube/informers/factory/factory.go rename to vendor/knative.dev/pkg/client/injection/kube/informers/factory/filtered/filtered_factory.go index 8bcb84dcc3..621a200537 100644 --- a/vendor/knative.dev/pkg/client/injection/kube/informers/factory/factory.go +++ b/vendor/knative.dev/pkg/client/injection/kube/informers/factory/filtered/filtered_factory.go @@ -16,11 +16,12 @@ limitations under the License. // Code generated by injection-gen. DO NOT EDIT. -package factory +package filteredFactory import ( context "context" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" informers "k8s.io/client-go/informers" client "knative.dev/pkg/client/injection/kube/client" controller "knative.dev/pkg/controller" @@ -33,24 +34,45 @@ func init() { } // Key is used as the key for associating information with a context.Context. -type Key struct{} +type Key struct { + Selector string +} + +type LabelKey struct{} + +func WithSelectors(ctx context.Context, selector ...string) context.Context { + return context.WithValue(ctx, LabelKey{}, selector) +} func withInformerFactory(ctx context.Context) context.Context { c := client.Get(ctx) - opts := make([]informers.SharedInformerOption, 0, 1) - if injection.HasNamespaceScope(ctx) { - opts = append(opts, informers.WithNamespace(injection.GetNamespaceScope(ctx))) + untyped := ctx.Value(LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") } - return context.WithValue(ctx, Key{}, - informers.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) + labelSelectors := untyped.([]string) + for _, selector := range labelSelectors { + selectorVal := selector + opts := []informers.SharedInformerOption{} + if injection.HasNamespaceScope(ctx) { + opts = append(opts, informers.WithNamespace(injection.GetNamespaceScope(ctx))) + } + opts = append(opts, informers.WithTweakListOptions(func(l *v1.ListOptions) { + l.LabelSelector = selectorVal + })) + ctx = context.WithValue(ctx, Key{Selector: selectorVal}, + informers.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) + } + return ctx } // Get extracts the InformerFactory from the context. -func Get(ctx context.Context) informers.SharedInformerFactory { - untyped := ctx.Value(Key{}) +func Get(ctx context.Context, selector string) informers.SharedInformerFactory { + untyped := ctx.Value(Key{Selector: selector}) if untyped == nil { - logging.FromContext(ctx).Panic( - "Unable to fetch k8s.io/client-go/informers.SharedInformerFactory from context.") + logging.FromContext(ctx).Panicf( + "Unable to fetch k8s.io/client-go/informers.SharedInformerFactory with selector %s from context.", selector) } return untyped.(informers.SharedInformerFactory) } diff --git a/vendor/modules.txt b/vendor/modules.txt index a3f07eeaff..4732797a4f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1556,8 +1556,9 @@ knative.dev/pkg/client/injection/ducks/duck/v1/addressable knative.dev/pkg/client/injection/ducks/duck/v1/authstatus knative.dev/pkg/client/injection/kube/client knative.dev/pkg/client/injection/kube/client/fake -knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment -knative.dev/pkg/client/injection/kube/informers/factory +knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/filtered +knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered +knative.dev/pkg/client/injection/kube/informers/factory/filtered knative.dev/pkg/codegen/cmd/injection-gen knative.dev/pkg/codegen/cmd/injection-gen/args knative.dev/pkg/codegen/cmd/injection-gen/generators