@@ -30,7 +30,7 @@ import (
30
30
31
31
"github.com/containers/image/v5/types"
32
32
"github.com/sirupsen/logrus"
33
- "github.com/spf13/pflag "
33
+ "github.com/spf13/cobra "
34
34
corev1 "k8s.io/api/core/v1"
35
35
apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
36
36
"k8s.io/apimachinery/pkg/fields"
78
78
setupLog = ctrl .Log .WithName ("setup" )
79
79
defaultSystemNamespace = "olmv1-system"
80
80
certWatcher * certwatcher.CertWatcher
81
+ cfg = & config {}
81
82
)
82
83
84
+ type config struct {
85
+ metricsAddr string
86
+ certFile string
87
+ keyFile string
88
+ enableLeaderElection bool
89
+ probeAddr string
90
+ cachePath string
91
+ systemNamespace string
92
+ catalogdCasDir string
93
+ pullCasDir string
94
+ globalPullSecret string
95
+ }
96
+
83
97
const authFilePrefix = "operator-controller-global-pull-secrets"
84
98
85
99
// podNamespace checks whether the controller is running in a Pod vs.
@@ -94,83 +108,94 @@ func podNamespace() string {
94
108
return string (namespace )
95
109
}
96
110
97
- func main () {
98
- var (
99
- metricsAddr string
100
- certFile string
101
- keyFile string
102
- enableLeaderElection bool
103
- probeAddr string
104
- cachePath string
105
- operatorControllerVersion bool
106
- systemNamespace string
107
- catalogdCasDir string
108
- pullCasDir string
109
- globalPullSecret string
110
- )
111
- flag .StringVar (& metricsAddr , "metrics-bind-address" , "" , "The address for the metrics endpoint. Requires tls-cert and tls-key. (Default: ':8443')" )
112
- flag .StringVar (& probeAddr , "health-probe-bind-address" , ":8081" , "The address the probe endpoint binds to." )
113
- flag .StringVar (& catalogdCasDir , "catalogd-cas-dir" , "" , "The directory of TLS certificate authorities to use for verifying HTTPS connections to the Catalogd web service." )
114
- flag .StringVar (& pullCasDir , "pull-cas-dir" , "" , "The directory of TLS certificate authorities to use for verifying HTTPS connections to image registries." )
115
- flag .StringVar (& certFile , "tls-cert" , "" , "The certificate file used for the metrics server. Required to enable the metrics server. Requires tls-key." )
116
- flag .StringVar (& keyFile , "tls-key" , "" , "The key file used for the metrics server. Required to enable the metrics server. Requires tls-cert" )
117
- flag .BoolVar (& enableLeaderElection , "leader-elect" , false ,
111
+ var operatorControllerCmd = & cobra.Command {
112
+ Use : "operator-controller" ,
113
+ Short : "operator-controller is the central component of Operator Lifecycle Manager (OLM) v1" ,
114
+ RunE : func (cmd * cobra.Command , args []string ) error {
115
+ if err := validateMetricsFlags (); err != nil {
116
+ return err
117
+ }
118
+ return run ()
119
+ },
120
+ }
121
+
122
+ var versionCommand = & cobra.Command {
123
+ Use : "version" ,
124
+ Short : "Prints operator-controller version information" ,
125
+ Run : func (cmd * cobra.Command , args []string ) {
126
+ fmt .Println (version .String ())
127
+ },
128
+ }
129
+
130
+ func init () {
131
+ //create flagset, the collection of flags for this command
132
+ flags := operatorControllerCmd .Flags ()
133
+ flags .StringVar (& cfg .metricsAddr , "metrics-bind-address" , "" , "The address for the metrics endpoint. Requires tls-cert and tls-key. (Default: ':8443')" )
134
+ flags .StringVar (& cfg .probeAddr , "health-probe-bind-address" , ":8081" , "The address the probe endpoint binds to." )
135
+ flags .StringVar (& cfg .catalogdCasDir , "catalogd-cas-dir" , "" , "The directory of TLS certificate authorities to use for verifying HTTPS connections to the Catalogd web service." )
136
+ flags .StringVar (& cfg .pullCasDir , "pull-cas-dir" , "" , "The directory of TLS certificate authorities to use for verifying HTTPS connections to image registries." )
137
+ flags .StringVar (& cfg .certFile , "tls-cert" , "" , "The certificate file used for the metrics server. Required to enable the metrics server. Requires tls-key." )
138
+ flags .StringVar (& cfg .keyFile , "tls-key" , "" , "The key file used for the metrics server. Required to enable the metrics server. Requires tls-cert" )
139
+ flags .BoolVar (& cfg .enableLeaderElection , "leader-elect" , false ,
118
140
"Enable leader election for controller manager. " +
119
141
"Enabling this will ensure there is only one active controller manager." )
120
- flag .StringVar (& cachePath , "cache-path" , "/var/cache" , "The local directory path used for filesystem based caching" )
121
- flag .BoolVar (& operatorControllerVersion , "version" , false , "Prints operator-controller version information" )
122
- flag .StringVar (& systemNamespace , "system-namespace" , "" , "Configures the namespace that gets used to deploy system resources." )
123
- flag .StringVar (& globalPullSecret , "global-pull-secret" , "" , "The <namespace>/<name> of the global pull secret that is going to be used to pull bundle images." )
142
+ flags .StringVar (& cfg .cachePath , "cache-path" , "/var/cache" , "The local directory path used for filesystem based caching" )
143
+ flags .StringVar (& cfg .systemNamespace , "system-namespace" , "" , "Configures the namespace that gets used to deploy system resources." )
144
+ flags .StringVar (& cfg .globalPullSecret , "global-pull-secret" , "" , "The <namespace>/<name> of the global pull secret that is going to be used to pull bundle images." )
145
+
146
+ //adds version sub command
147
+ operatorControllerCmd .AddCommand (versionCommand )
124
148
125
149
klog .InitFlags (flag .CommandLine )
126
150
if klog .V (4 ).Enabled () {
127
151
logrus .SetLevel (logrus .DebugLevel )
128
152
}
129
153
130
- pflag .CommandLine .AddGoFlagSet (flag .CommandLine )
131
- features .OperatorControllerFeatureGate .AddFlag (pflag .CommandLine )
132
- pflag .Parse ()
154
+ //add klog flags to flagset
155
+ flags .AddGoFlagSet (flag .CommandLine )
133
156
134
- if operatorControllerVersion {
135
- fmt .Println (version .String ())
136
- os .Exit (0 )
137
- }
138
-
139
- if (certFile != "" && keyFile == "" ) || (certFile == "" && keyFile != "" ) {
157
+ //add feature gate flags to flagset
158
+ features .OperatorControllerFeatureGate .AddFlag (flags )
159
+ }
160
+ func validateMetricsFlags () error {
161
+ if (cfg .certFile != "" && cfg .keyFile == "" ) || (cfg .certFile == "" && cfg .keyFile != "" ) {
140
162
setupLog .Error (errors .New ("missing TLS configuration" ),
141
163
"tls-cert and tls-key flags must be used together" ,
142
- "certFile" , certFile , "keyFile" , keyFile )
143
- os . Exit ( 1 )
164
+ "certFile" , cfg . certFile , "keyFile" , cfg . keyFile )
165
+ return fmt . Errorf ( "unable to configure TLS certificates: tls-cert and tls-key flags must be used together" )
144
166
}
145
167
146
- if metricsAddr != "" && certFile == "" && keyFile == "" {
168
+ if cfg . metricsAddr != "" && cfg . certFile == "" && cfg . keyFile == "" {
147
169
setupLog .Error (errors .New ("invalid metrics configuration" ),
148
170
"metrics-bind-address requires tls-cert and tls-key flags to be set" ,
149
- "metricsAddr" , metricsAddr , "certFile" , certFile , "keyFile" , keyFile )
150
- os . Exit ( 1 )
171
+ "metricsAddr" , cfg . metricsAddr , "certFile" , cfg . certFile , "keyFile" , cfg . keyFile )
172
+ return fmt . Errorf ( "metrics-bind-address requires tls-cert and tls-key flags to be set" )
151
173
}
152
174
153
- if certFile != "" && keyFile != "" && metricsAddr == "" {
154
- metricsAddr = ":8443"
175
+ if cfg . certFile != "" && cfg . keyFile != "" && cfg . metricsAddr == "" {
176
+ cfg . metricsAddr = ":8443"
155
177
}
156
-
178
+ return nil
179
+ }
180
+ func run () error {
157
181
ctrl .SetLogger (textlogger .NewLogger (textlogger .NewConfig ()))
158
182
159
183
setupLog .Info ("starting up the controller" , "version info" , version .String ())
160
184
161
185
authFilePath := filepath .Join (os .TempDir (), fmt .Sprintf ("%s-%s.json" , authFilePrefix , apimachineryrand .String (8 )))
162
186
var globalPullSecretKey * k8stypes.NamespacedName
163
- if globalPullSecret != "" {
164
- secretParts := strings .Split (globalPullSecret , "/" )
187
+ if cfg . globalPullSecret != "" {
188
+ secretParts := strings .Split (cfg . globalPullSecret , "/" )
165
189
if len (secretParts ) != 2 {
166
- setupLog .Error (fmt .Errorf ("incorrect number of components" ), "value of global-pull-secret should be of the format <namespace>/<name>" )
167
- os .Exit (1 )
190
+ err := fmt .Errorf ("incorrect number of components" )
191
+ setupLog .Error (err , "value of global-pull-secret should be of the format <namespace>/<name>" )
192
+ return err
168
193
}
169
194
globalPullSecretKey = & k8stypes.NamespacedName {Name : secretParts [1 ], Namespace : secretParts [0 ]}
170
195
}
171
196
172
- if systemNamespace == "" {
173
- systemNamespace = podNamespace ()
197
+ if cfg . systemNamespace == "" {
198
+ cfg . systemNamespace = podNamespace ()
174
199
}
175
200
176
201
setupLog .Info ("set up manager" )
@@ -180,7 +205,7 @@ func main() {
180
205
& catalogd.ClusterCatalog {}: {Label : k8slabels .Everything ()},
181
206
},
182
207
DefaultNamespaces : map [string ]crcache.Config {
183
- systemNamespace : {LabelSelector : k8slabels .Everything ()},
208
+ cfg . systemNamespace : {LabelSelector : k8slabels .Everything ()},
184
209
},
185
210
DefaultLabelSelector : k8slabels .Nothing (),
186
211
}
@@ -198,19 +223,19 @@ func main() {
198
223
}
199
224
200
225
metricsServerOptions := server.Options {}
201
- if len (certFile ) > 0 && len (keyFile ) > 0 {
202
- setupLog .Info ("Starting metrics server with TLS enabled" , "addr" , metricsAddr , "tls-cert" , certFile , "tls-key" , keyFile )
226
+ if len (cfg . certFile ) > 0 && len (cfg . keyFile ) > 0 {
227
+ setupLog .Info ("Starting metrics server with TLS enabled" , "addr" , cfg . metricsAddr , "tls-cert" , cfg . certFile , "tls-key" , cfg . keyFile )
203
228
204
- metricsServerOptions .BindAddress = metricsAddr
229
+ metricsServerOptions .BindAddress = cfg . metricsAddr
205
230
metricsServerOptions .SecureServing = true
206
231
metricsServerOptions .FilterProvider = filters .WithAuthenticationAndAuthorization
207
232
208
233
// If the certificate files change, the watcher will reload them.
209
234
var err error
210
- certWatcher , err = certwatcher .New (certFile , keyFile )
235
+ certWatcher , err = certwatcher .New (cfg . certFile , cfg . keyFile )
211
236
if err != nil {
212
237
setupLog .Error (err , "Failed to initialize certificate watcher" )
213
- os . Exit ( 1 )
238
+ return err
214
239
}
215
240
216
241
metricsServerOptions .TLSOpts = append (metricsServerOptions .TLSOpts , func (config * tls.Config ) {
@@ -239,8 +264,8 @@ func main() {
239
264
mgr , err := ctrl .NewManager (ctrl .GetConfigOrDie (), ctrl.Options {
240
265
Scheme : scheme .Scheme ,
241
266
Metrics : metricsServerOptions ,
242
- HealthProbeBindAddress : probeAddr ,
243
- LeaderElection : enableLeaderElection ,
267
+ HealthProbeBindAddress : cfg . probeAddr ,
268
+ LeaderElection : cfg . enableLeaderElection ,
244
269
LeaderElectionID : "9c4404e7.operatorframework.io" ,
245
270
LeaderElectionReleaseOnCancel : true ,
246
271
// Recommended Leader Election values
@@ -264,19 +289,19 @@ func main() {
264
289
})
265
290
if err != nil {
266
291
setupLog .Error (err , "unable to start manager" )
267
- os . Exit ( 1 )
292
+ return err
268
293
}
269
294
270
295
coreClient , err := corev1client .NewForConfig (mgr .GetConfig ())
271
296
if err != nil {
272
297
setupLog .Error (err , "unable to create core client" )
273
- os . Exit ( 1 )
298
+ return err
274
299
}
275
300
tokenGetter := authentication .NewTokenGetter (coreClient , authentication .WithExpirationDuration (1 * time .Hour ))
276
301
clientRestConfigMapper := action .ServiceAccountRestConfigMapper (tokenGetter )
277
302
278
303
cfgGetter , err := helmclient .NewActionConfigGetter (mgr .GetConfig (), mgr .GetRESTMapper (),
279
- helmclient .StorageDriverMapper (action .ChunkedStorageDriverMapper (coreClient , mgr .GetAPIReader (), systemNamespace )),
304
+ helmclient .StorageDriverMapper (action .ChunkedStorageDriverMapper (coreClient , mgr .GetAPIReader (), cfg . systemNamespace )),
280
305
helmclient .ClientNamespaceMapper (func (obj client.Object ) (string , error ) {
281
306
ext := obj .(* ocv1.ClusterExtension )
282
307
return ext .Spec .Namespace , nil
@@ -285,42 +310,42 @@ func main() {
285
310
)
286
311
if err != nil {
287
312
setupLog .Error (err , "unable to config for creating helm client" )
288
- os . Exit ( 1 )
313
+ return err
289
314
}
290
315
291
316
acg , err := action .NewWrappedActionClientGetter (cfgGetter ,
292
317
helmclient .WithFailureRollbacks (false ),
293
318
)
294
319
if err != nil {
295
320
setupLog .Error (err , "unable to create helm client" )
296
- os . Exit ( 1 )
321
+ return err
297
322
}
298
323
299
- certPoolWatcher , err := httputil .NewCertPoolWatcher (catalogdCasDir , ctrl .Log .WithName ("cert-pool" ))
324
+ certPoolWatcher , err := httputil .NewCertPoolWatcher (cfg . catalogdCasDir , ctrl .Log .WithName ("cert-pool" ))
300
325
if err != nil {
301
326
setupLog .Error (err , "unable to create CA certificate pool" )
302
- os . Exit ( 1 )
327
+ return err
303
328
}
304
329
305
330
if certWatcher != nil {
306
331
setupLog .Info ("Adding certificate watcher to manager" )
307
332
if err := mgr .Add (certWatcher ); err != nil {
308
333
setupLog .Error (err , "unable to add certificate watcher to manager" )
309
- os . Exit ( 1 )
334
+ return err
310
335
}
311
336
}
312
337
313
- if err := fsutil .EnsureEmptyDirectory (cachePath , 0700 ); err != nil {
338
+ if err := fsutil .EnsureEmptyDirectory (cfg . cachePath , 0700 ); err != nil {
314
339
setupLog .Error (err , "unable to ensure empty cache directory" )
315
- os . Exit ( 1 )
340
+ return err
316
341
}
317
342
318
- imageCache := imageutil .BundleCache (filepath .Join (cachePath , "unpack" ))
343
+ imageCache := imageutil .BundleCache (filepath .Join (cfg . cachePath , "unpack" ))
319
344
imagePuller := & imageutil.ContainersImagePuller {
320
345
SourceCtxFunc : func (ctx context.Context ) (* types.SystemContext , error ) {
321
346
srcContext := & types.SystemContext {
322
- DockerCertPath : pullCasDir ,
323
- OCICertPath : pullCasDir ,
347
+ DockerCertPath : cfg . pullCasDir ,
348
+ OCICertPath : cfg . pullCasDir ,
324
349
}
325
350
logger := log .FromContext (ctx )
326
351
if _ , err := os .Stat (authFilePath ); err == nil && globalPullSecretKey != nil {
@@ -340,15 +365,15 @@ func main() {
340
365
return crfinalizer.Result {}, imageCache .Delete (ctx , obj .GetName ())
341
366
})); err != nil {
342
367
setupLog .Error (err , "unable to register finalizer" , "finalizerKey" , controllers .ClusterExtensionCleanupUnpackCacheFinalizer )
343
- os . Exit ( 1 )
368
+ return err
344
369
}
345
370
346
371
cl := mgr .GetClient ()
347
372
348
- catalogsCachePath := filepath .Join (cachePath , "catalogs" )
373
+ catalogsCachePath := filepath .Join (cfg . cachePath , "catalogs" )
349
374
if err := os .MkdirAll (catalogsCachePath , 0700 ); err != nil {
350
375
setupLog .Error (err , "unable to create catalogs cache directory" )
351
- os . Exit ( 1 )
376
+ return err
352
377
}
353
378
catalogClientBackend := cache .NewFilesystemCache (catalogsCachePath )
354
379
catalogClient := catalogclient .New (catalogClientBackend , func () (* http.Client , error ) {
@@ -374,7 +399,7 @@ func main() {
374
399
aeClient , err := apiextensionsv1client .NewForConfig (mgr .GetConfig ())
375
400
if err != nil {
376
401
setupLog .Error (err , "unable to create apiextensions client" )
377
- os . Exit ( 1 )
402
+ return err
378
403
}
379
404
380
405
preflights := []applier.Preflight {
@@ -394,7 +419,7 @@ func main() {
394
419
}))
395
420
if err != nil {
396
421
setupLog .Error (err , "unable to register content manager cleanup finalizer" )
397
- os . Exit ( 1 )
422
+ return err
398
423
}
399
424
400
425
if err = (& controllers.ClusterExtensionReconciler {
@@ -408,7 +433,7 @@ func main() {
408
433
Manager : cm ,
409
434
}).SetupWithManager (mgr ); err != nil {
410
435
setupLog .Error (err , "unable to create controller" , "controller" , "ClusterExtension" )
411
- os . Exit ( 1 )
436
+ return err
412
437
}
413
438
414
439
if err = (& controllers.ClusterCatalogReconciler {
@@ -417,41 +442,49 @@ func main() {
417
442
CatalogCachePopulator : catalogClient ,
418
443
}).SetupWithManager (mgr ); err != nil {
419
444
setupLog .Error (err , "unable to create controller" , "controller" , "ClusterCatalog" )
420
- os . Exit ( 1 )
445
+ return err
421
446
}
422
447
423
448
if globalPullSecretKey != nil {
424
- setupLog .Info ("creating SecretSyncer controller for watching secret" , "Secret" , globalPullSecret )
449
+ setupLog .Info ("creating SecretSyncer controller for watching secret" , "Secret" , cfg . globalPullSecret )
425
450
err := (& controllers.PullSecretReconciler {
426
451
Client : mgr .GetClient (),
427
452
AuthFilePath : authFilePath ,
428
453
SecretKey : * globalPullSecretKey ,
429
454
}).SetupWithManager (mgr )
430
455
if err != nil {
431
456
setupLog .Error (err , "unable to create controller" , "controller" , "SecretSyncer" )
432
- os . Exit ( 1 )
457
+ return err
433
458
}
434
459
}
435
460
436
461
//+kubebuilder:scaffold:builder
437
462
438
463
if err := mgr .AddHealthzCheck ("healthz" , healthz .Ping ); err != nil {
439
464
setupLog .Error (err , "unable to set up health check" )
440
- os . Exit ( 1 )
465
+ return err
441
466
}
442
467
if err := mgr .AddReadyzCheck ("readyz" , healthz .Ping ); err != nil {
443
468
setupLog .Error (err , "unable to set up ready check" )
444
- os . Exit ( 1 )
469
+ return err
445
470
}
446
471
447
472
setupLog .Info ("starting manager" )
448
473
ctx := ctrl .SetupSignalHandler ()
449
474
if err := mgr .Start (ctx ); err != nil {
450
475
setupLog .Error (err , "problem running manager" )
451
- os . Exit ( 1 )
476
+ return err
452
477
}
453
478
if err := os .Remove (authFilePath ); err != nil {
454
479
setupLog .Error (err , "failed to cleanup temporary auth file" )
480
+ return err
481
+ }
482
+ return nil
483
+ }
484
+
485
+ func main () {
486
+ if err := operatorControllerCmd .Execute (); err != nil {
487
+ fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
455
488
os .Exit (1 )
456
489
}
457
490
}
0 commit comments