@@ -25,6 +25,7 @@ import (
2525
2626 "github.com/spf13/cobra"
2727 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+ apimachruntime "k8s.io/apimachinery/pkg/runtime"
2829 "k8s.io/client-go/rest"
2930 ctrl "sigs.k8s.io/controller-runtime"
3031 "sigs.k8s.io/controller-runtime/pkg/cache"
@@ -45,6 +46,10 @@ import (
4546 "github.com/operator-framework/operator-sdk/internal/helm/watches"
4647 "github.com/operator-framework/operator-sdk/internal/util/k8sutil"
4748 sdkVersion "github.com/operator-framework/operator-sdk/internal/version"
49+ "helm.sh/helm/v3/pkg/chart/loader"
50+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
51+ "k8s.io/apimachinery/pkg/labels"
52+ "k8s.io/apimachinery/pkg/selection"
4853)
4954
5055var log = logf .Log .WithName ("cmd" )
@@ -136,6 +141,23 @@ func run(cmd *cobra.Command, f *flags.Flags) {
136141 // Set default manager options
137142 options = f .ToManagerOptions (options )
138143
144+ if options .Scheme == nil {
145+ options .Scheme = apimachruntime .NewScheme ()
146+ }
147+
148+ ws , err := watches .Load (f .WatchesFile )
149+ if err != nil {
150+ log .Error (err , "Failed to create new manager factories." )
151+ os .Exit (1 )
152+ }
153+
154+ watchNamespaces := getWatchNamespaces (options .Namespace )
155+ options .NewCache , err = buildNewCacheFunc (watchNamespaces , ws , options .Scheme )
156+ if err != nil {
157+ log .Error (err , "Failed to create NewCache function for manager." )
158+ os .Exit (1 )
159+ }
160+
139161 if options .NewClient == nil {
140162 options .NewClient = func (cache cache.Cache , config * rest.Config , options client.Options , uncachedObjects ... client.Object ) (client.Client , error ) {
141163 // Create the Client for Write operations.
@@ -152,27 +174,6 @@ func run(cmd *cobra.Command, f *flags.Flags) {
152174 })
153175 }
154176 }
155- namespace , found := os .LookupEnv (k8sutil .WatchNamespaceEnvVar )
156- log = log .WithValues ("Namespace" , namespace )
157- if found {
158- log .V (1 ).Info (fmt .Sprintf ("Setting namespace with value in %s" , k8sutil .WatchNamespaceEnvVar ))
159- if namespace == metav1 .NamespaceAll {
160- log .Info ("Watching all namespaces." )
161- options .Namespace = metav1 .NamespaceAll
162- } else {
163- if strings .Contains (namespace , "," ) {
164- log .Info ("Watching multiple namespaces." )
165- options .NewCache = cache .MultiNamespacedCacheBuilder (strings .Split (namespace , "," ))
166- } else {
167- log .Info ("Watching single namespace." )
168- options .Namespace = namespace
169- }
170- }
171- } else if options .Namespace == "" {
172- log .Info (fmt .Sprintf ("Watch namespaces not configured by environment variable %s or file. " +
173- "Watching all namespaces." , k8sutil .WatchNamespaceEnvVar ))
174- options .Namespace = metav1 .NamespaceAll
175- }
176177
177178 mgr , err := manager .New (cfg , options )
178179 if err != nil {
@@ -189,11 +190,6 @@ func run(cmd *cobra.Command, f *flags.Flags) {
189190 os .Exit (1 )
190191 }
191192
192- ws , err := watches .Load (f .WatchesFile )
193- if err != nil {
194- log .Error (err , "Failed to create new manager factories." )
195- os .Exit (1 )
196- }
197193 acg , err := helmClient .NewActionConfigGetter (mgr .GetConfig (), mgr .GetRESTMapper (), mgr .GetLogger ())
198194 if err != nil {
199195 log .Error (err , "Failed to create Helm action config getter" )
@@ -207,7 +203,6 @@ func run(cmd *cobra.Command, f *flags.Flags) {
207203 }
208204
209205 err := controller .Add (mgr , controller.WatchOptions {
210- Namespace : namespace ,
211206 GVK : w .GroupVersionKind ,
212207 ManagerFactory : release .NewManagerFactory (mgr , acg , w .ChartDir ),
213208 ReconcilePeriod : reconcilePeriod ,
@@ -250,3 +245,84 @@ func exitIfUnsupported(options manager.Options) {
250245 os .Exit (1 )
251246 }
252247}
248+
249+ func getWatchNamespaces (defaultNamespace string ) []string {
250+ namespace , found := os .LookupEnv (k8sutil .WatchNamespaceEnvVar )
251+ log = log .WithValues ("Namespace" , namespace )
252+ if found {
253+ log .V (1 ).Info (fmt .Sprintf ("Setting namespace with value in %s" , k8sutil .WatchNamespaceEnvVar ))
254+ if namespace == metav1 .NamespaceAll {
255+ log .Info ("Watching all namespaces." )
256+ return []string {metav1 .NamespaceAll }
257+ }
258+ if strings .Contains (namespace , "," ) {
259+ log .Info ("Watching multiple namespaces." )
260+ return strings .Split (namespace , "," )
261+ }
262+ log .Info ("Watching single namespace." )
263+ return []string {namespace }
264+ }
265+ if defaultNamespace == "" {
266+ log .Info (fmt .Sprintf ("Watch namespaces not configured by environment variable %s or file. " +
267+ "Watching all namespaces." , k8sutil .WatchNamespaceEnvVar ))
268+ return []string {metav1 .NamespaceAll }
269+ }
270+ return []string {defaultNamespace }
271+ }
272+
273+ func buildNewCacheFunc (watchNamespaces []string , ws []watches.Watch , sch * apimachruntime.Scheme ) (cache.NewCacheFunc , error ) {
274+ // setCacheOptions is an anonymous function that updates the cache selectors
275+ // and namespace configuration of the cache options that are passed into it.
276+ // we use this anonymous function in the NewCache function that we build and
277+ // return from buildNewCacheFunc.
278+ setCacheOptions := func (options * cache.Options ) error {
279+ selectorsByObject := cache.SelectorsByObject {}
280+ chartNames := make ([]string , 0 , len (ws ))
281+ for _ , w := range ws {
282+ sch .AddKnownTypeWithName (w .GroupVersionKind , & unstructured.Unstructured {})
283+
284+ crObj := & unstructured.Unstructured {}
285+ crObj .SetGroupVersionKind (w .GroupVersionKind )
286+ sel , err := metav1 .LabelSelectorAsSelector (& w .Selector )
287+ if err != nil {
288+ return fmt .Errorf ("unable to parse watch selector for %s: %v" , w .GroupVersionKind , err )
289+ }
290+ selectorsByObject [crObj ] = cache.ObjectSelector {Label : sel }
291+
292+ chrt , err := loader .LoadDir (w .ChartDir )
293+ if err != nil {
294+ return fmt .Errorf ("unable to load chart for %s: %v" , w .GroupVersionKind , err )
295+ }
296+ chartNames = append (chartNames , chrt .Name ())
297+ }
298+
299+ req , err := labels .NewRequirement ("helm.sdk.operatorframework.io/chart" , selection .In , chartNames )
300+ if err != nil {
301+ return fmt .Errorf ("unable to create label requirement for cache default selector: %v" , err )
302+ }
303+ defaultSelector := labels .NewSelector ().Add (* req )
304+
305+ if len (watchNamespaces ) == 1 {
306+ options .Namespace = watchNamespaces [0 ]
307+ }
308+ options .SelectorsByObject = selectorsByObject
309+ options .DefaultSelector = cache.ObjectSelector {Label : defaultSelector }
310+ return nil
311+ }
312+
313+ // Define the NewCache function to use based on the number of namespaces. If there are multiple namespaces,
314+ // use a MultiNamespacedCacheBuilder. Otherwise, use the standard New function.
315+ newCache := cache .New
316+ if len (watchNamespaces ) > 1 {
317+ newCache = cache .MultiNamespacedCacheBuilder (watchNamespaces )
318+ }
319+
320+ // Return a custom NewCache function that uses our setCacheOptions function
321+ // to set the cache options as the cache is being created.
322+ return func (config * rest.Config , opts cache.Options ) (cache.Cache , error ) {
323+ if err := setCacheOptions (& opts ); err != nil {
324+ return nil , err
325+ }
326+ return newCache (config , opts )
327+ }, nil
328+ }
0 commit comments