Skip to content

Commit 2f0f6d8

Browse files
committed
migrate operator-controller command handling to cobra
Signed-off-by: Omar Farag <[email protected]>
1 parent 7b86dda commit 2f0f6d8

File tree

1 file changed

+117
-81
lines changed

1 file changed

+117
-81
lines changed

cmd/operator-controller/main.go

Lines changed: 117 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929

3030
"github.com/containers/image/v5/types"
3131
"github.com/go-logr/logr"
32-
"github.com/spf13/pflag"
32+
"github.com/spf13/cobra"
3333
corev1 "k8s.io/api/core/v1"
3434
apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
3535
"k8s.io/apimachinery/pkg/fields"
@@ -76,8 +76,23 @@ var (
7676
setupLog = ctrl.Log.WithName("setup")
7777
defaultSystemNamespace = "olmv1-system"
7878
certWatcher *certwatcher.CertWatcher
79+
cfg = &config{}
7980
)
8081

82+
type config struct {
83+
metricsAddr string
84+
certFile string
85+
keyFile string
86+
enableLeaderElection bool
87+
probeAddr string
88+
cachePath string
89+
operatorControllerVersion bool
90+
systemNamespace string
91+
catalogdCasDir string
92+
pullCasDir string
93+
globalPullSecret string
94+
}
95+
8196
const authFilePrefix = "operator-controller-global-pull-secrets"
8297

8398
// podNamespace checks whether the controller is running in a Pod vs.
@@ -92,76 +107,90 @@ func podNamespace() string {
92107
return string(namespace)
93108
}
94109

95-
func main() {
96-
var (
97-
metricsAddr string
98-
certFile string
99-
keyFile string
100-
enableLeaderElection bool
101-
probeAddr string
102-
cachePath string
103-
operatorControllerVersion bool
104-
systemNamespace string
105-
catalogdCasDir string
106-
pullCasDir string
107-
globalPullSecret string
108-
)
109-
flag.StringVar(&metricsAddr, "metrics-bind-address", "", "The address for the metrics endpoint. Requires tls-cert and tls-key. (Default: ':8443')")
110-
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
111-
flag.StringVar(&catalogdCasDir, "catalogd-cas-dir", "", "The directory of TLS certificate authorities to use for verifying HTTPS connections to the Catalogd web service.")
112-
flag.StringVar(&pullCasDir, "pull-cas-dir", "", "The directory of TLS certificate authorities to use for verifying HTTPS connections to image registries.")
113-
flag.StringVar(&certFile, "tls-cert", "", "The certificate file used for the metrics server. Required to enable the metrics server. Requires tls-key.")
114-
flag.StringVar(&keyFile, "tls-key", "", "The key file used for the metrics server. Required to enable the metrics server. Requires tls-cert")
115-
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
110+
var operatorControllerCmd = &cobra.Command{
111+
Use: "operator-controller",
112+
Short: "operator-controller is the central component of Operator Lifecycle Manager (OLM) v1",
113+
RunE: func(cmd *cobra.Command, args []string) error {
114+
if err := validateMetricsFlags(); err != nil {
115+
return fmt.Errorf("Error: %v\n", err)
116+
}
117+
return run()
118+
},
119+
}
120+
121+
var versionCommand = &cobra.Command{
122+
Use: "version",
123+
Short: "Prints version info of operator-controller",
124+
Run: func(cmd *cobra.Command, args []string) {
125+
fmt.Println(version.String())
126+
},
127+
}
128+
129+
func init() {
130+
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,
116140
"Enable leader election for controller manager. "+
117141
"Enabling this will ensure there is only one active controller manager.")
118-
flag.StringVar(&cachePath, "cache-path", "/var/cache", "The local directory path used for filesystem based caching")
119-
flag.BoolVar(&operatorControllerVersion, "version", false, "Prints operator-controller version information")
120-
flag.StringVar(&systemNamespace, "system-namespace", "", "Configures the namespace that gets used to deploy system resources.")
121-
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.BoolVar(&cfg.operatorControllerVersion, "version", false, "Prints operator-controller version information")
144+
flags.StringVar(&cfg.systemNamespace, "system-namespace", "", "Configures the namespace that gets used to deploy system resources.")
145+
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.")
146+
147+
//adds version sub command
148+
operatorControllerCmd.AddCommand(versionCommand)
122149

123150
klog.InitFlags(flag.CommandLine)
124151

125-
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
126-
features.OperatorControllerFeatureGate.AddFlag(pflag.CommandLine)
127-
pflag.Parse()
152+
//add klog flags to flagset
153+
flags.AddGoFlagSet(flag.CommandLine)
128154

129-
if operatorControllerVersion {
130-
fmt.Println(version.String())
131-
os.Exit(0)
132-
}
155+
//add feature gate flags to flagset
156+
features.OperatorControllerFeatureGate.AddFlag(flags)
133157

134-
if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
135-
setupLog.Error(nil, "unable to configure TLS certificates: tls-cert and tls-key flags must be used together")
136-
os.Exit(1)
158+
}
159+
160+
func validateMetricsFlags() error {
161+
if (cfg.certFile != "" && cfg.keyFile == "") || (cfg.certFile == "" && cfg.keyFile != "") {
162+
return fmt.Errorf("unable to configure TLS certificates: tls-cert and tls-key flags must be used together")
137163
}
138164

139-
if metricsAddr != "" && certFile == "" && keyFile == "" {
140-
setupLog.Error(nil, "metrics-bind-address requires tls-cert and tls-key flags to be set")
141-
os.Exit(1)
165+
if cfg.metricsAddr != "" && cfg.certFile == "" && cfg.keyFile == "" {
166+
return fmt.Errorf("metrics-bind-address requires tls-cert and tls-key flags to be set")
142167
}
143168

144-
if certFile != "" && keyFile != "" && metricsAddr == "" {
145-
metricsAddr = ":8443"
169+
if cfg.certFile != "" && cfg.keyFile != "" && cfg.metricsAddr == "" {
170+
cfg.metricsAddr = ":8443"
146171
}
172+
return nil
173+
}
147174

175+
func run() error {
148176
ctrl.SetLogger(textlogger.NewLogger(textlogger.NewConfig()))
149177

150178
setupLog.Info("starting up the controller", "version info", version.String())
151179

152180
authFilePath := filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s.json", authFilePrefix, apimachineryrand.String(8)))
153181
var globalPullSecretKey *k8stypes.NamespacedName
154-
if globalPullSecret != "" {
155-
secretParts := strings.Split(globalPullSecret, "/")
182+
if cfg.globalPullSecret != "" {
183+
secretParts := strings.Split(cfg.globalPullSecret, "/")
156184
if len(secretParts) != 2 {
157-
setupLog.Error(fmt.Errorf("incorrect number of components"), "value of global-pull-secret should be of the format <namespace>/<name>")
158-
os.Exit(1)
185+
err := fmt.Errorf("incorrect number of components")
186+
setupLog.Error(err, "value of global-pull-secret should be of the format <namespace>/<name>")
187+
return err
159188
}
160189
globalPullSecretKey = &k8stypes.NamespacedName{Name: secretParts[1], Namespace: secretParts[0]}
161190
}
162191

163-
if systemNamespace == "" {
164-
systemNamespace = podNamespace()
192+
if cfg.systemNamespace == "" {
193+
cfg.systemNamespace = podNamespace()
165194
}
166195

167196
setupLog.Info("set up manager")
@@ -171,7 +200,7 @@ func main() {
171200
&catalogd.ClusterCatalog{}: {Label: k8slabels.Everything()},
172201
},
173202
DefaultNamespaces: map[string]crcache.Config{
174-
systemNamespace: {LabelSelector: k8slabels.Everything()},
203+
cfg.systemNamespace: {LabelSelector: k8slabels.Everything()},
175204
},
176205
DefaultLabelSelector: k8slabels.Nothing(),
177206
}
@@ -189,19 +218,19 @@ func main() {
189218
}
190219

191220
metricsServerOptions := server.Options{}
192-
if len(certFile) > 0 && len(keyFile) > 0 {
193-
setupLog.Info("Starting metrics server with TLS enabled", "addr", metricsAddr, "tls-cert", certFile, "tls-key", keyFile)
221+
if len(cfg.certFile) > 0 && len(cfg.keyFile) > 0 {
222+
setupLog.Info("Starting metrics server with TLS enabled", "addr", cfg.metricsAddr, "tls-cert", cfg.certFile, "tls-key", cfg.keyFile)
194223

195-
metricsServerOptions.BindAddress = metricsAddr
224+
metricsServerOptions.BindAddress = cfg.metricsAddr
196225
metricsServerOptions.SecureServing = true
197226
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
198227

199228
// If the certificate files change, the watcher will reload them.
200229
var err error
201-
certWatcher, err = certwatcher.New(certFile, keyFile)
230+
certWatcher, err = certwatcher.New(cfg.certFile, cfg.keyFile)
202231
if err != nil {
203232
setupLog.Error(err, "Failed to initialize certificate watcher")
204-
os.Exit(1)
233+
return err
205234
}
206235

207236
metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) {
@@ -230,8 +259,8 @@ func main() {
230259
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
231260
Scheme: scheme.Scheme,
232261
Metrics: metricsServerOptions,
233-
HealthProbeBindAddress: probeAddr,
234-
LeaderElection: enableLeaderElection,
262+
HealthProbeBindAddress: cfg.probeAddr,
263+
LeaderElection: cfg.enableLeaderElection,
235264
LeaderElectionID: "9c4404e7.operatorframework.io",
236265
LeaderElectionReleaseOnCancel: true,
237266
// Recommended Leader Election values
@@ -255,19 +284,19 @@ func main() {
255284
})
256285
if err != nil {
257286
setupLog.Error(err, "unable to start manager")
258-
os.Exit(1)
287+
return err
259288
}
260289

261290
coreClient, err := corev1client.NewForConfig(mgr.GetConfig())
262291
if err != nil {
263292
setupLog.Error(err, "unable to create core client")
264-
os.Exit(1)
293+
return err
265294
}
266295
tokenGetter := authentication.NewTokenGetter(coreClient, authentication.WithExpirationDuration(1*time.Hour))
267296
clientRestConfigMapper := action.ServiceAccountRestConfigMapper(tokenGetter)
268297

269298
cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(),
270-
helmclient.StorageDriverMapper(action.ChunkedStorageDriverMapper(coreClient, mgr.GetAPIReader(), systemNamespace)),
299+
helmclient.StorageDriverMapper(action.ChunkedStorageDriverMapper(coreClient, mgr.GetAPIReader(), cfg.systemNamespace)),
271300
helmclient.ClientNamespaceMapper(func(obj client.Object) (string, error) {
272301
ext := obj.(*ocv1.ClusterExtension)
273302
return ext.Spec.Namespace, nil
@@ -276,42 +305,42 @@ func main() {
276305
)
277306
if err != nil {
278307
setupLog.Error(err, "unable to config for creating helm client")
279-
os.Exit(1)
308+
return err
280309
}
281310

282311
acg, err := action.NewWrappedActionClientGetter(cfgGetter,
283312
helmclient.WithFailureRollbacks(false),
284313
)
285314
if err != nil {
286315
setupLog.Error(err, "unable to create helm client")
287-
os.Exit(1)
316+
return err
288317
}
289318

290-
certPoolWatcher, err := httputil.NewCertPoolWatcher(catalogdCasDir, ctrl.Log.WithName("cert-pool"))
319+
certPoolWatcher, err := httputil.NewCertPoolWatcher(cfg.catalogdCasDir, ctrl.Log.WithName("cert-pool"))
291320
if err != nil {
292321
setupLog.Error(err, "unable to create CA certificate pool")
293-
os.Exit(1)
322+
return err
294323
}
295324

296325
if certWatcher != nil {
297326
setupLog.Info("Adding certificate watcher to manager")
298327
if err := mgr.Add(certWatcher); err != nil {
299328
setupLog.Error(err, "unable to add certificate watcher to manager")
300-
os.Exit(1)
329+
return err
301330
}
302331
}
303332

304-
if err := fsutil.EnsureEmptyDirectory(cachePath, 0700); err != nil {
333+
if err := fsutil.EnsureEmptyDirectory(cfg.cachePath, 0700); err != nil {
305334
setupLog.Error(err, "unable to ensure empty cache directory")
306-
os.Exit(1)
335+
return err
307336
}
308337

309338
unpacker := &source.ContainersImageRegistry{
310-
BaseCachePath: filepath.Join(cachePath, "unpack"),
339+
BaseCachePath: filepath.Join(cfg.cachePath, "unpack"),
311340
SourceContextFunc: func(logger logr.Logger) (*types.SystemContext, error) {
312341
srcContext := &types.SystemContext{
313-
DockerCertPath: pullCasDir,
314-
OCICertPath: pullCasDir,
342+
DockerCertPath: cfg.pullCasDir,
343+
OCICertPath: cfg.pullCasDir,
315344
}
316345
if _, err := os.Stat(authFilePath); err == nil && globalPullSecretKey != nil {
317346
logger.Info("using available authentication information for pulling image")
@@ -330,15 +359,15 @@ func main() {
330359
return crfinalizer.Result{}, unpacker.Cleanup(ctx, &source.BundleSource{Name: obj.GetName()})
331360
})); err != nil {
332361
setupLog.Error(err, "unable to register finalizer", "finalizerKey", controllers.ClusterExtensionCleanupUnpackCacheFinalizer)
333-
os.Exit(1)
362+
return err
334363
}
335364

336365
cl := mgr.GetClient()
337366

338-
catalogsCachePath := filepath.Join(cachePath, "catalogs")
367+
catalogsCachePath := filepath.Join(cfg.cachePath, "catalogs")
339368
if err := os.MkdirAll(catalogsCachePath, 0700); err != nil {
340369
setupLog.Error(err, "unable to create catalogs cache directory")
341-
os.Exit(1)
370+
return err
342371
}
343372
catalogClientBackend := cache.NewFilesystemCache(catalogsCachePath)
344373
catalogClient := catalogclient.New(catalogClientBackend, func() (*http.Client, error) {
@@ -364,7 +393,7 @@ func main() {
364393
aeClient, err := apiextensionsv1client.NewForConfig(mgr.GetConfig())
365394
if err != nil {
366395
setupLog.Error(err, "unable to create apiextensions client")
367-
os.Exit(1)
396+
return err
368397
}
369398

370399
preflights := []applier.Preflight{
@@ -384,7 +413,7 @@ func main() {
384413
}))
385414
if err != nil {
386415
setupLog.Error(err, "unable to register content manager cleanup finalizer")
387-
os.Exit(1)
416+
return err
388417
}
389418

390419
if err = (&controllers.ClusterExtensionReconciler{
@@ -397,7 +426,7 @@ func main() {
397426
Manager: cm,
398427
}).SetupWithManager(mgr); err != nil {
399428
setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension")
400-
os.Exit(1)
429+
return err
401430
}
402431

403432
if err = (&controllers.ClusterCatalogReconciler{
@@ -406,41 +435,48 @@ func main() {
406435
CatalogCachePopulator: catalogClient,
407436
}).SetupWithManager(mgr); err != nil {
408437
setupLog.Error(err, "unable to create controller", "controller", "ClusterCatalog")
409-
os.Exit(1)
438+
return err
410439
}
411440

412441
if globalPullSecretKey != nil {
413-
setupLog.Info("creating SecretSyncer controller for watching secret", "Secret", globalPullSecret)
442+
setupLog.Info("creating SecretSyncer controller for watching secret", "Secret", cfg.globalPullSecret)
414443
err := (&controllers.PullSecretReconciler{
415444
Client: mgr.GetClient(),
416445
AuthFilePath: authFilePath,
417446
SecretKey: *globalPullSecretKey,
418447
}).SetupWithManager(mgr)
419448
if err != nil {
420449
setupLog.Error(err, "unable to create controller", "controller", "SecretSyncer")
421-
os.Exit(1)
450+
return err
422451
}
423452
}
424453

425454
//+kubebuilder:scaffold:builder
426455

427456
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
428457
setupLog.Error(err, "unable to set up health check")
429-
os.Exit(1)
458+
return err
430459
}
431460
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
432461
setupLog.Error(err, "unable to set up ready check")
433-
os.Exit(1)
462+
return err
434463
}
435464

436465
setupLog.Info("starting manager")
437466
ctx := ctrl.SetupSignalHandler()
438467
if err := mgr.Start(ctx); err != nil {
439468
setupLog.Error(err, "problem running manager")
440-
os.Exit(1)
469+
return err
441470
}
442471
if err := os.Remove(authFilePath); err != nil {
443472
setupLog.Error(err, "failed to cleanup temporary auth file")
444-
os.Exit(1)
473+
return err
445474
}
475+
return nil
476+
}
477+
478+
func main() {
479+
480+
operatorControllerCmd.Execute()
481+
446482
}

0 commit comments

Comments
 (0)