@@ -25,6 +25,7 @@ import (
25
25
26
26
"github.com/spf13/cobra"
27
27
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
+ apimachruntime "k8s.io/apimachinery/pkg/runtime"
28
29
"k8s.io/client-go/rest"
29
30
ctrl "sigs.k8s.io/controller-runtime"
30
31
"sigs.k8s.io/controller-runtime/pkg/cache"
@@ -45,6 +46,10 @@ import (
45
46
"github.com/operator-framework/operator-sdk/internal/helm/watches"
46
47
"github.com/operator-framework/operator-sdk/internal/util/k8sutil"
47
48
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"
48
53
)
49
54
50
55
var log = logf .Log .WithName ("cmd" )
@@ -136,6 +141,23 @@ func run(cmd *cobra.Command, f *flags.Flags) {
136
141
// Set default manager options
137
142
options = f .ToManagerOptions (options )
138
143
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
+
139
161
if options .NewClient == nil {
140
162
options .NewClient = func (cache cache.Cache , config * rest.Config , options client.Options , uncachedObjects ... client.Object ) (client.Client , error ) {
141
163
// Create the Client for Write operations.
@@ -152,27 +174,6 @@ func run(cmd *cobra.Command, f *flags.Flags) {
152
174
})
153
175
}
154
176
}
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
- }
176
177
177
178
mgr , err := manager .New (cfg , options )
178
179
if err != nil {
@@ -189,11 +190,6 @@ func run(cmd *cobra.Command, f *flags.Flags) {
189
190
os .Exit (1 )
190
191
}
191
192
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
- }
197
193
acg , err := helmClient .NewActionConfigGetter (mgr .GetConfig (), mgr .GetRESTMapper (), mgr .GetLogger ())
198
194
if err != nil {
199
195
log .Error (err , "Failed to create Helm action config getter" )
@@ -207,7 +203,6 @@ func run(cmd *cobra.Command, f *flags.Flags) {
207
203
}
208
204
209
205
err := controller .Add (mgr , controller.WatchOptions {
210
- Namespace : namespace ,
211
206
GVK : w .GroupVersionKind ,
212
207
ManagerFactory : release .NewManagerFactory (mgr , acg , w .ChartDir ),
213
208
ReconcilePeriod : reconcilePeriod ,
@@ -250,3 +245,85 @@ func exitIfUnsupported(options manager.Options) {
250
245
os .Exit (1 )
251
246
}
252
247
}
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
+ } else {
258
+ if strings .Contains (namespace , "," ) {
259
+ log .Info ("Watching multiple namespaces." )
260
+ return strings .Split (namespace , "," )
261
+ } else {
262
+ log .Info ("Watching single namespace." )
263
+ return []string {namespace }
264
+ }
265
+ }
266
+ } else if defaultNamespace == "" {
267
+ log .Info (fmt .Sprintf ("Watch namespaces not configured by environment variable %s or file. " +
268
+ "Watching all namespaces." , k8sutil .WatchNamespaceEnvVar ))
269
+ return []string {metav1 .NamespaceAll }
270
+ }
271
+ return []string {defaultNamespace }
272
+ }
273
+
274
+ func buildNewCacheFunc (watchNamespaces []string , ws []watches.Watch , sch * apimachruntime.Scheme ) (cache.NewCacheFunc , error ) {
275
+ // setCacheOptions is an anonymous function that updates the cache selectors
276
+ // and namespace configuration of the cache options that are passed into it.
277
+ // we use this anonymous function in the NewCache function that we build and
278
+ // return from buildNewCacheFunc.
279
+ setCacheOptions := func (options * cache.Options ) error {
280
+ selectorsByObject := cache.SelectorsByObject {}
281
+ chartNames := make ([]string , 0 , len (ws ))
282
+ for _ , w := range ws {
283
+ sch .AddKnownTypeWithName (w .GroupVersionKind , & unstructured.Unstructured {})
284
+
285
+ crObj := & unstructured.Unstructured {}
286
+ crObj .SetGroupVersionKind (w .GroupVersionKind )
287
+ sel , err := metav1 .LabelSelectorAsSelector (& w .Selector )
288
+ if err != nil {
289
+ return fmt .Errorf ("unable to parse watch selector for %s: %v" , w .GroupVersionKind , err )
290
+ }
291
+ selectorsByObject [crObj ] = cache.ObjectSelector {Label : sel }
292
+
293
+ chrt , err := loader .LoadDir (w .ChartDir )
294
+ if err != nil {
295
+ return fmt .Errorf ("unable to load chart for %s: %v" , w .GroupVersionKind , err )
296
+ }
297
+ chartNames = append (chartNames , chrt .Name ())
298
+ }
299
+
300
+ req , err := labels .NewRequirement ("helm.sdk.operatorframework.io/chart" , selection .In , chartNames )
301
+ if err != nil {
302
+ return fmt .Errorf ("unable to create label requirement for cache default selector: %v" , err )
303
+ }
304
+ defaultSelector := labels .NewSelector ().Add (* req )
305
+
306
+ if len (watchNamespaces ) == 1 {
307
+ options .Namespace = watchNamespaces [0 ]
308
+ }
309
+ options .SelectorsByObject = selectorsByObject
310
+ options .DefaultSelector = cache.ObjectSelector {Label : defaultSelector }
311
+ return nil
312
+ }
313
+
314
+ // Define the NewCache function to use based on the number of namespaces. If there are multiple namespaces,
315
+ // use a MultiNamespacedCacheBuilder. Otherwise, use the standard New function.
316
+ newCache := cache .New
317
+ if len (watchNamespaces ) > 1 {
318
+ newCache = cache .MultiNamespacedCacheBuilder (watchNamespaces )
319
+ }
320
+
321
+ // Return a custom NewCache function that uses our setCacheOptions function
322
+ // to set the cache options as the cache is being created.
323
+ return func (config * rest.Config , opts cache.Options ) (cache.Cache , error ) {
324
+ if err := setCacheOptions (& opts ); err != nil {
325
+ return nil , err
326
+ }
327
+ return newCache (config , opts )
328
+ }, nil
329
+ }
0 commit comments