@@ -19,6 +19,7 @@ package main
1919import (
2020 "crypto/tls"
2121 "flag"
22+ "fmt"
2223 "os"
2324
2425 // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
@@ -58,53 +59,113 @@ func init() {
5859 // +kubebuilder:scaffold:scheme
5960}
6061
61- // nolint:gocyclo
62+ // Command interface for subcommands
63+ type Command interface {
64+ Name () string
65+ FlagSet () * flag.FlagSet
66+ Run () error
67+ }
68+
69+ var commands = []Command {
70+ & ManagerCommand {},
71+ // Add new commands here
72+ }
73+
6274func main () {
63- var metricsAddr string
64- var metricsCertPath , metricsCertName , metricsCertKey string
65- var webhookCertPath , webhookCertName , webhookCertKey string
66- var enableLeaderElection bool
67- var probeAddr string
68- var secureMetrics bool
69- var enableHTTP2 bool
75+ if len (os .Args ) < 2 {
76+ printUsage ()
77+ os .Exit (1 )
78+ }
79+
80+ cmdName := os .Args [1 ]
81+ for _ , cmd := range commands {
82+ if cmd .Name () == cmdName {
83+ _ = cmd .FlagSet ().Parse (os .Args [2 :])
84+ if err := cmd .Run (); err != nil {
85+ fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
86+ os .Exit (1 )
87+ }
88+ return
89+ }
90+ }
91+
92+ fmt .Fprintf (os .Stderr , "Unknown command: %s\n " , cmdName )
93+ printUsage ()
94+ os .Exit (1 )
95+ }
96+
97+ func printUsage () {
98+ fmt .Fprintf (os .Stderr , "Usage: %s <command>\n \n Available commands:\n " , os .Args [0 ])
99+ for _ , cmd := range commands {
100+ fmt .Fprintf (os .Stderr , " %s\n " , cmd .Name ())
101+ }
102+ }
103+
104+ // ManagerCommand implements the manager subcommand
105+ type ManagerCommand struct {
106+ metricsAddr string
107+ metricsCertPath string
108+ metricsCertName string
109+ metricsCertKey string
110+ webhookCertPath string
111+ webhookCertName string
112+ webhookCertKey string
113+ enableLeaderElection bool
114+ probeAddr string
115+ secureMetrics bool
116+ enableHTTP2 bool
117+ fs * flag.FlagSet
118+ zapOpts zap.Options
119+ }
120+
121+ func (c * ManagerCommand ) Name () string {
122+ return "manager"
123+ }
124+
125+ func (c * ManagerCommand ) FlagSet () * flag.FlagSet {
126+ if c .fs == nil {
127+ c .fs = flag .NewFlagSet ("manager" , flag .ExitOnError )
128+ c .fs .StringVar (& c .metricsAddr , "metrics-bind-address" , "0" , "The address the metrics endpoint binds to. " +
129+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service." )
130+ c .fs .StringVar (& c .probeAddr , "health-probe-bind-address" , ":8081" , "The address the probe endpoint binds to." )
131+ c .fs .BoolVar (& c .enableLeaderElection , "leader-elect" , false ,
132+ "Enable leader election for controller manager. " +
133+ "Enabling this will ensure there is only one active controller manager." )
134+ c .fs .BoolVar (& c .secureMetrics , "metrics-secure" , true ,
135+ "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead." )
136+ c .fs .StringVar (& c .webhookCertPath , "webhook-cert-path" , "" , "The directory that contains the webhook certificate." )
137+ c .fs .StringVar (& c .webhookCertName , "webhook-cert-name" , "tls.crt" , "The name of the webhook certificate file." )
138+ c .fs .StringVar (& c .webhookCertKey , "webhook-cert-key" , "tls.key" , "The name of the webhook key file." )
139+ c .fs .StringVar (& c .metricsCertPath , "metrics-cert-path" , "" ,
140+ "The directory that contains the metrics server certificate." )
141+ c .fs .StringVar (& c .metricsCertName , "metrics-cert-name" , "tls.crt" , "The name of the metrics server certificate file." )
142+ c .fs .StringVar (& c .metricsCertKey , "metrics-cert-key" , "tls.key" , "The name of the metrics server key file." )
143+ c .fs .BoolVar (& c .enableHTTP2 , "enable-http2" , false ,
144+ "If set, HTTP/2 will be enabled for the metrics and webhook servers" )
145+
146+ c .zapOpts = zap.Options {Development : true }
147+ c .zapOpts .BindFlags (c .fs )
148+ }
149+ return c .fs
150+ }
151+
152+ func (c * ManagerCommand ) Run () error {
153+ ctrl .SetLogger (zap .New (zap .UseFlagOptions (& c .zapOpts )))
154+
70155 var tlsOpts []func (* tls.Config )
71- flag .StringVar (& metricsAddr , "metrics-bind-address" , "0" , "The address the metrics endpoint binds to. " +
72- "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service." )
73- flag .StringVar (& probeAddr , "health-probe-bind-address" , ":8081" , "The address the probe endpoint binds to." )
74- flag .BoolVar (& enableLeaderElection , "leader-elect" , false ,
75- "Enable leader election for controller manager. " +
76- "Enabling this will ensure there is only one active controller manager." )
77- flag .BoolVar (& secureMetrics , "metrics-secure" , true ,
78- "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead." )
79- flag .StringVar (& webhookCertPath , "webhook-cert-path" , "" , "The directory that contains the webhook certificate." )
80- flag .StringVar (& webhookCertName , "webhook-cert-name" , "tls.crt" , "The name of the webhook certificate file." )
81- flag .StringVar (& webhookCertKey , "webhook-cert-key" , "tls.key" , "The name of the webhook key file." )
82- flag .StringVar (& metricsCertPath , "metrics-cert-path" , "" ,
83- "The directory that contains the metrics server certificate." )
84- flag .StringVar (& metricsCertName , "metrics-cert-name" , "tls.crt" , "The name of the metrics server certificate file." )
85- flag .StringVar (& metricsCertKey , "metrics-cert-key" , "tls.key" , "The name of the metrics server key file." )
86- flag .BoolVar (& enableHTTP2 , "enable-http2" , false ,
87- "If set, HTTP/2 will be enabled for the metrics and webhook servers" )
88- opts := zap.Options {
89- Development : true ,
90- }
91- opts .BindFlags (flag .CommandLine )
92- flag .Parse ()
93-
94- ctrl .SetLogger (zap .New (zap .UseFlagOptions (& opts )))
95156
96157 // if the enable-http2 flag is false (the default), http/2 should be disabled
97158 // due to its vulnerabilities. More specifically, disabling http/2 will
98159 // prevent from being vulnerable to the HTTP/2 Stream Cancellation and
99160 // Rapid Reset CVEs. For more information see:
100161 // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
101162 // - https://github.com/advisories/GHSA-4374-p667-p6c8
102- disableHTTP2 := func (c * tls.Config ) {
163+ disableHTTP2 := func (config * tls.Config ) {
103164 setupLog .Info ("disabling http/2" )
104- c .NextProtos = []string {"http/1.1" }
165+ config .NextProtos = []string {"http/1.1" }
105166 }
106167
107- if ! enableHTTP2 {
168+ if ! c . enableHTTP2 {
108169 tlsOpts = append (tlsOpts , disableHTTP2 )
109170 }
110171
@@ -114,13 +175,13 @@ func main() {
114175 TLSOpts : webhookTLSOpts ,
115176 }
116177
117- if len (webhookCertPath ) > 0 {
178+ if len (c . webhookCertPath ) > 0 {
118179 setupLog .Info ("Initializing webhook certificate watcher using provided certificates" ,
119- "webhook-cert-path" , webhookCertPath , "webhook-cert-name" , webhookCertName , "webhook-cert-key" , webhookCertKey )
180+ "webhook-cert-path" , c . webhookCertPath , "webhook-cert-name" , c . webhookCertName , "webhook-cert-key" , c . webhookCertKey )
120181
121- webhookServerOptions .CertDir = webhookCertPath
122- webhookServerOptions .CertName = webhookCertName
123- webhookServerOptions .KeyName = webhookCertKey
182+ webhookServerOptions .CertDir = c . webhookCertPath
183+ webhookServerOptions .CertName = c . webhookCertName
184+ webhookServerOptions .KeyName = c . webhookCertKey
124185 }
125186
126187 webhookServer := webhook .NewServer (webhookServerOptions )
@@ -130,12 +191,12 @@ func main() {
130191 // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/server
131192 // - https://book.kubebuilder.io/reference/metrics.html
132193 metricsServerOptions := metricsserver.Options {
133- BindAddress : metricsAddr ,
134- SecureServing : secureMetrics ,
194+ BindAddress : c . metricsAddr ,
195+ SecureServing : c . secureMetrics ,
135196 TLSOpts : tlsOpts ,
136197 }
137198
138- if secureMetrics {
199+ if c . secureMetrics {
139200 // FilterProvider is used to protect the metrics endpoint with authn/authz.
140201 // These configurations ensure that only authorized users and service accounts
141202 // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
@@ -151,13 +212,13 @@ func main() {
151212 // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates
152213 // managed by cert-manager for the metrics server.
153214 // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification.
154- if len (metricsCertPath ) > 0 {
215+ if len (c . metricsCertPath ) > 0 {
155216 setupLog .Info ("Initializing metrics certificate watcher using provided certificates" ,
156- "metrics-cert-path" , metricsCertPath , "metrics-cert-name" , metricsCertName , "metrics-cert-key" , metricsCertKey )
217+ "metrics-cert-path" , c . metricsCertPath , "metrics-cert-name" , c . metricsCertName , "metrics-cert-key" , c . metricsCertKey )
157218
158- metricsServerOptions .CertDir = metricsCertPath
159- metricsServerOptions .CertName = metricsCertName
160- metricsServerOptions .KeyName = metricsCertKey
219+ metricsServerOptions .CertDir = c . metricsCertPath
220+ metricsServerOptions .CertName = c . metricsCertName
221+ metricsServerOptions .KeyName = c . metricsCertKey
161222 }
162223
163224 mgr , err := ctrl .NewManager (ctrl .GetConfigOrDie (), ctrl.Options {
@@ -171,8 +232,8 @@ func main() {
171232 Scheme : scheme ,
172233 Metrics : metricsServerOptions ,
173234 WebhookServer : webhookServer ,
174- HealthProbeBindAddress : probeAddr ,
175- LeaderElection : enableLeaderElection ,
235+ HealthProbeBindAddress : c . probeAddr ,
236+ LeaderElection : c . enableLeaderElection ,
176237 LeaderElectionID : "3509939c.harvesterhci.io" ,
177238 // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
178239 // when the Manager ends. This requires the binary to immediately end when the
@@ -188,7 +249,7 @@ func main() {
188249 })
189250 if err != nil {
190251 setupLog .Error (err , "unable to start manager" )
191- os . Exit ( 1 )
252+ return err
192253 }
193254
194255 ctx := ctrl .SetupSignalHandler ()
@@ -199,38 +260,40 @@ func main() {
199260 Log : logf .FromContext (ctx ).WithName ("upgrade-plan-controller" ),
200261 }).SetupWithManager (mgr ); err != nil {
201262 setupLog .Error (err , "unable to create controller" , "controller" , "UpgradePlan" )
202- os . Exit ( 1 )
263+ return err
203264 }
204265 if err := (& controller.VersionReconciler {
205266 Client : mgr .GetClient (),
206267 Scheme : mgr .GetScheme (),
207268 Log : logf .FromContext (ctx ).WithName ("version-controller" ),
208269 }).SetupWithManager (mgr ); err != nil {
209270 setupLog .Error (err , "unable to create controller" , "controller" , "Version" )
210- os . Exit ( 1 )
271+ return err
211272 }
212273 if err := (& controller.JobReconciler {
213274 Client : mgr .GetClient (),
214275 Scheme : mgr .GetScheme (),
215276 Log : logf .FromContext (ctx ).WithName ("job-controller" ),
216277 }).SetupWithManager (mgr ); err != nil {
217278 setupLog .Error (err , "unable to create controller" , "controller" , "Job" )
218- os . Exit ( 1 )
279+ return err
219280 }
220281 // +kubebuilder:scaffold:builder
221282
222283 if err := mgr .AddHealthzCheck ("healthz" , healthz .Ping ); err != nil {
223284 setupLog .Error (err , "unable to set up health check" )
224- os . Exit ( 1 )
285+ return err
225286 }
226287 if err := mgr .AddReadyzCheck ("readyz" , healthz .Ping ); err != nil {
227288 setupLog .Error (err , "unable to set up ready check" )
228- os . Exit ( 1 )
289+ return err
229290 }
230291
231292 setupLog .Info ("starting manager" )
232293 if err := mgr .Start (ctx ); err != nil {
233294 setupLog .Error (err , "problem running manager" )
234- os . Exit ( 1 )
295+ return err
235296 }
297+
298+ return nil
236299}
0 commit comments