Skip to content

Commit f9b5d66

Browse files
committed
fix(controller): accept legacy manifest flags
1 parent 395b5aa commit f9b5d66

3 files changed

Lines changed: 316 additions & 175 deletions

File tree

kubernetes/cmd/controller/main.go

Lines changed: 43 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ import (
2020
"fmt"
2121
"os"
2222
"path/filepath"
23-
"strconv"
2423
"strings"
25-
"time"
2624

2725
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2826
// to ensure that exec-entrypoint and run can make use of them.
@@ -34,7 +32,6 @@ import (
3432
ctrl "sigs.k8s.io/controller-runtime"
3533
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
3634
"sigs.k8s.io/controller-runtime/pkg/healthz"
37-
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3835
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
3936
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
4037
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -53,58 +50,6 @@ const (
5350
defaultPoolConcurrency = 16
5451
)
5552

56-
type ConcurrencyConfig map[string]int
57-
58-
func (c *ConcurrencyConfig) String() string {
59-
if *c == nil {
60-
return ""
61-
}
62-
parts := make([]string, 0, len(*c))
63-
for k, v := range *c {
64-
parts = append(parts, fmt.Sprintf("%s=%d", k, v))
65-
}
66-
return strings.Join(parts, ";")
67-
}
68-
69-
func (c *ConcurrencyConfig) Set(value string) error {
70-
if *c == nil {
71-
*c = make(ConcurrencyConfig)
72-
}
73-
if value == "" {
74-
return nil
75-
}
76-
pairs := strings.Split(value, ";")
77-
for _, pair := range pairs {
78-
pair = strings.TrimSpace(pair)
79-
if pair == "" {
80-
continue
81-
}
82-
kv := strings.SplitN(pair, "=", 2)
83-
if len(kv) != 2 {
84-
return fmt.Errorf("invalid concurrency config format: %s, expected format: controller=value", pair)
85-
}
86-
name := strings.TrimSpace(kv[0])
87-
val, err := strconv.Atoi(strings.TrimSpace(kv[1]))
88-
if err != nil {
89-
return fmt.Errorf("invalid concurrency value for %s: %v", name, err)
90-
}
91-
if val <= 0 {
92-
return fmt.Errorf("concurrency value must be positive for %s: %d", name, val)
93-
}
94-
(*c)[name] = val
95-
}
96-
return nil
97-
}
98-
99-
func (c *ConcurrencyConfig) Get(name string, defaultVal int) int {
100-
if *c != nil {
101-
if v, ok := (*c)[name]; ok {
102-
return v
103-
}
104-
}
105-
return defaultVal
106-
}
107-
10853
var (
10954
scheme = runtime.NewScheme()
11055
setupLog = ctrl.Log.WithName("setup")
@@ -132,107 +77,30 @@ func init() {
13277

13378
// nolint:gocyclo
13479
func main() {
135-
var metricsAddr string
136-
var metricsCertPath, metricsCertName, metricsCertKey string
137-
var webhookCertPath, webhookCertName, webhookCertKey string
138-
var enableLeaderElection bool
139-
var probeAddr string
140-
var secureMetrics bool
141-
var enableHTTP2 bool
142-
var allowWeakTLSKeyLengths bool
14380
var tlsOpts []func(*tls.Config)
14481

145-
// Log file options
146-
var enableFileLog bool
147-
var logFilePath string
148-
var logMaxSize int
149-
var logMaxBackups int
150-
var logMaxAge int
151-
var logCompress bool
152-
153-
// Kubernetes client rate limiter options
154-
var kubeClientQPS float64
155-
var kubeClientBurst int
156-
157-
// Controller concurrency options
158-
var concurrencyConfig ConcurrencyConfig
159-
160-
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
161-
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
162-
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
163-
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
164-
"Enable leader election for controller manager. "+
165-
"Enabling this will ensure there is only one active controller manager.")
166-
flag.BoolVar(&secureMetrics, "metrics-secure", true,
167-
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
168-
flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.")
169-
flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.")
170-
flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.")
171-
flag.StringVar(&metricsCertPath, "metrics-cert-path", "",
172-
"The directory that contains the metrics server certificate.")
173-
flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.")
174-
flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.")
175-
flag.BoolVar(&enableHTTP2, "enable-http2", false,
176-
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
177-
flag.BoolVar(
178-
&allowWeakTLSKeyLengths,
179-
"allow-weak-tls-keylengths",
180-
false,
181-
"If set, allows TLS certificates below NIST 2030 minimum key/hash lengths (not recommended).",
182-
)
183-
184-
// Log file flags
185-
flag.BoolVar(&enableFileLog, "enable-file-log", false, "Enable log output to file")
186-
flag.StringVar(&logFilePath, "log-file-path", "/var/log/sandbox-controller/controller.log", "Path to the log file")
187-
flag.IntVar(&logMaxSize, "log-max-size", 100, "Maximum size in megabytes of the log file before it gets rotated")
188-
flag.IntVar(&logMaxBackups, "log-max-backups", 10, "Maximum number of old log files to retain")
189-
flag.IntVar(&logMaxAge, "log-max-age", 30, "Maximum number of days to retain old log files")
190-
flag.BoolVar(&logCompress, "log-compress", true, "Compress determines if the rotated log files should be compressed using gzip")
191-
flag.Float64Var(&kubeClientQPS, "kube-client-qps", 100, "QPS for Kubernetes client rate limiter.")
192-
flag.IntVar(&kubeClientBurst, "kube-client-burst", 200, "Burst for Kubernetes client rate limiter.")
193-
flag.Var(&concurrencyConfig, "concurrency", "Controller concurrency settings in format: controller1=N;controller2=M. "+
194-
"Available controllers: batchsandbox, pool. "+
195-
"Example: --concurrency='batchsandbox=32;pool=128'")
196-
197-
// Image committer
198-
var imageCommitterImage string
199-
flag.StringVar(&imageCommitterImage, "image-committer-image", "image-committer:dev", "The image used for commit operations (contains nerdctl tool).")
200-
201-
// Commit job timeout
202-
var commitJobTimeout time.Duration
203-
flag.DurationVar(&commitJobTimeout, "commit-job-timeout", 10*time.Minute, "The timeout duration for commit jobs.")
204-
205-
var snapshotRegistry string
206-
flag.StringVar(&snapshotRegistry, "snapshot-registry", "", "OCI registry for snapshot images (e.g., registry.example.com/snapshots).")
207-
208-
var snapshotRegistryInsecure bool
209-
flag.BoolVar(&snapshotRegistryInsecure, "snapshot-registry-insecure", false, "Use insecure registry mode when pushing snapshot images.")
210-
211-
var snapshotPushSecret string
212-
flag.StringVar(&snapshotPushSecret, "snapshot-push-secret", "", "K8s Secret name for pushing snapshots to registry.")
213-
214-
var resumePullSecret string
215-
flag.StringVar(&resumePullSecret, "resume-pull-secret", "", "K8s Secret name for pulling snapshot images during resume.")
216-
217-
opts := zap.Options{}
218-
opts.BindFlags(flag.CommandLine)
82+
options := &controllerOptions{}
83+
options.bindFlags(flag.CommandLine)
21984

22085
flag.Parse()
22186

22287
// Setup logger with file rotation support
22388
logOpts := logging.Options{
224-
Development: opts.Development,
225-
EnableFileOutput: enableFileLog,
226-
LogFilePath: logFilePath,
227-
MaxSize: logMaxSize,
228-
MaxBackups: logMaxBackups,
229-
MaxAge: logMaxAge,
230-
Compress: logCompress,
231-
ZapOptions: opts,
89+
Development: options.zapOptions.Development,
90+
EnableFileOutput: options.enableFileLog,
91+
LogFilePath: options.logFilePath,
92+
MaxSize: options.logMaxSize,
93+
MaxBackups: options.logMaxBackups,
94+
MaxAge: options.logMaxAge,
95+
Compress: options.logCompress,
96+
ZapOptions: options.zapOptions,
23297
}
23398

23499
logger := logging.NewLoggerWithZapOptions(logOpts)
235100
ctrl.SetLogger(logger)
101+
if options.legacyKlogVerbosity != "" {
102+
setupLog.Info("deprecated --v flag ignored; use --zap-log-level instead", "v", options.legacyKlogVerbosity)
103+
}
236104

237105
// if the enable-http2 flag is false (the default), http/2 should be disabled
238106
// due to its vulnerabilities. More specifically, disabling http/2 will
@@ -249,7 +117,7 @@ func main() {
249117
c.MinVersion = tls.VersionTLS12
250118
})
251119

252-
if !enableHTTP2 {
120+
if !options.enableHTTP2 {
253121
tlsOpts = append(tlsOpts, disableHTTP2)
254122
}
255123

@@ -259,10 +127,10 @@ func main() {
259127
// Initial webhook TLS options
260128
webhookTLSOpts := tlsOpts
261129

262-
if len(webhookCertPath) > 0 {
263-
webhookCertFile := filepath.Join(webhookCertPath, webhookCertName)
264-
webhookKeyFile := filepath.Join(webhookCertPath, webhookCertKey)
265-
if !allowWeakTLSKeyLengths {
130+
if len(options.webhookCertPath) > 0 {
131+
webhookCertFile := filepath.Join(options.webhookCertPath, options.webhookCertName)
132+
webhookKeyFile := filepath.Join(options.webhookCertPath, options.webhookCertKey)
133+
if !options.allowWeakTLSKeyLengths {
266134
if err := cryptoutil.ValidateCertificateKeyPair(webhookCertFile, webhookKeyFile); err != nil {
267135
setupLog.Error(err, "Webhook certificate does not meet NIST minimum key/hash requirements",
268136
"webhook-cert-file", webhookCertFile, "webhook-key-file", webhookKeyFile)
@@ -271,7 +139,7 @@ func main() {
271139
}
272140

273141
setupLog.Info("Initializing webhook certificate watcher using provided certificates",
274-
"webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey)
142+
"webhook-cert-path", options.webhookCertPath, "webhook-cert-name", options.webhookCertName, "webhook-cert-key", options.webhookCertKey)
275143

276144
var err error
277145
webhookCertWatcher, err = certwatcher.New(
@@ -289,7 +157,7 @@ func main() {
289157
if err != nil {
290158
return nil, err
291159
}
292-
if allowWeakTLSKeyLengths {
160+
if options.allowWeakTLSKeyLengths {
293161
return cert, nil
294162
}
295163
if err := cryptoutil.ValidateTLSCertificate(webhookCertFile, cert); err != nil {
@@ -309,12 +177,12 @@ func main() {
309177
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/server
310178
// - https://book.kubebuilder.io/reference/metrics.html
311179
metricsServerOptions := metricsserver.Options{
312-
BindAddress: metricsAddr,
313-
SecureServing: secureMetrics,
180+
BindAddress: options.metricsAddr,
181+
SecureServing: options.secureMetrics,
314182
TLSOpts: tlsOpts,
315183
}
316184

317-
if secureMetrics {
185+
if options.secureMetrics {
318186
// FilterProvider is used to protect the metrics endpoint with authn/authz.
319187
// These configurations ensure that only authorized users and service accounts
320188
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
@@ -330,10 +198,10 @@ func main() {
330198
// - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates
331199
// managed by cert-manager for the metrics server.
332200
// - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification.
333-
if len(metricsCertPath) > 0 {
334-
metricsCertFile := filepath.Join(metricsCertPath, metricsCertName)
335-
metricsKeyFile := filepath.Join(metricsCertPath, metricsCertKey)
336-
if !allowWeakTLSKeyLengths && metricsAddr != "0" && secureMetrics {
201+
if len(options.metricsCertPath) > 0 {
202+
metricsCertFile := filepath.Join(options.metricsCertPath, options.metricsCertName)
203+
metricsKeyFile := filepath.Join(options.metricsCertPath, options.metricsCertKey)
204+
if !options.allowWeakTLSKeyLengths && options.metricsAddr != "0" && options.secureMetrics {
337205
if err := cryptoutil.ValidateCertificateKeyPair(metricsCertFile, metricsKeyFile); err != nil {
338206
setupLog.Error(err, "Metrics certificate does not meet NIST minimum key/hash requirements",
339207
"metrics-cert-file", metricsCertFile, "metrics-key-file", metricsKeyFile)
@@ -342,7 +210,7 @@ func main() {
342210
}
343211

344212
setupLog.Info("Initializing metrics certificate watcher using provided certificates",
345-
"metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey)
213+
"metrics-cert-path", options.metricsCertPath, "metrics-cert-name", options.metricsCertName, "metrics-cert-key", options.metricsCertKey)
346214

347215
var err error
348216
metricsCertWatcher, err = certwatcher.New(
@@ -360,7 +228,7 @@ func main() {
360228
if err != nil {
361229
return nil, err
362230
}
363-
if allowWeakTLSKeyLengths {
231+
if options.allowWeakTLSKeyLengths {
364232
return cert, nil
365233
}
366234
if err := cryptoutil.ValidateTLSCertificate(metricsCertFile, cert); err != nil {
@@ -373,19 +241,19 @@ func main() {
373241

374242
config := ctrl.GetConfigOrDie()
375243
// Set client rate limiter if specified
376-
if kubeClientQPS > 0 {
377-
config.QPS = float32(kubeClientQPS)
244+
if options.kubeClientQPS > 0 {
245+
config.QPS = float32(options.kubeClientQPS)
378246
}
379-
if kubeClientBurst > 0 {
380-
config.Burst = kubeClientBurst
247+
if options.kubeClientBurst > 0 {
248+
config.Burst = options.kubeClientBurst
381249
}
382250

383251
mgr, err := ctrl.NewManager(config, ctrl.Options{
384252
Scheme: scheme,
385253
Metrics: metricsServerOptions,
386254
WebhookServer: webhookServer,
387-
HealthProbeBindAddress: probeAddr,
388-
LeaderElection: enableLeaderElection,
255+
HealthProbeBindAddress: options.probeAddr,
256+
LeaderElection: options.enableLeaderElection,
389257
LeaderElectionID: "2fa1c467.opensandbox.io",
390258
// LeaderElectionReleaseOnCancel causes the leader to voluntarily release the lease
391259
// when the Manager is stopped, allowing a new leader to acquire it without waiting
@@ -407,8 +275,8 @@ func main() {
407275
batchSandboxKindName = strings.ToLower(getKindFromType(&sandboxv1alpha1.BatchSandbox{}))
408276
poolKindName = strings.ToLower(getKindFromType(&sandboxv1alpha1.Pool{}))
409277
)
410-
batchSandboxConcurrency := concurrencyConfig.Get(batchSandboxKindName, defaultBatchSandboxConcurrency)
411-
poolConcurrency := concurrencyConfig.Get(poolKindName, defaultPoolConcurrency)
278+
batchSandboxConcurrency := options.concurrencyConfig.Get(batchSandboxKindName, defaultBatchSandboxConcurrency)
279+
poolConcurrency := options.concurrencyConfig.Get(poolKindName, defaultPoolConcurrency)
412280
setupLog.Info("controller concurrency configured", batchSandboxKindName, batchSandboxConcurrency, poolKindName, poolConcurrency)
413281

414282
profileStore := poolassign.NewProfileStore()
@@ -422,7 +290,7 @@ func main() {
422290
Client: mgr.GetClient(),
423291
Scheme: mgr.GetScheme(),
424292
Recorder: mgr.GetEventRecorderFor("batchsandbox-controller"),
425-
ResumePullSecret: resumePullSecret,
293+
ResumePullSecret: options.resumePullSecret,
426294
ProfileStore: profileStore,
427295
}).SetupWithManager(mgr, batchSandboxConcurrency); err != nil {
428296
setupLog.Error(err, "unable to create controller", "controller", "BatchSandbox")
@@ -442,11 +310,11 @@ func main() {
442310
Client: mgr.GetClient(),
443311
Scheme: mgr.GetScheme(),
444312
Recorder: mgr.GetEventRecorderFor("sandboxsnapshot-controller"),
445-
ImageCommitterImage: imageCommitterImage,
446-
CommitJobTimeout: commitJobTimeout,
447-
SnapshotRegistry: snapshotRegistry,
448-
SnapshotRegistryInsecure: snapshotRegistryInsecure,
449-
SnapshotPushSecret: snapshotPushSecret,
313+
ImageCommitterImage: options.imageCommitterImage,
314+
CommitJobTimeout: options.commitJobTimeout,
315+
SnapshotRegistry: options.snapshotRegistry,
316+
SnapshotRegistryInsecure: options.snapshotRegistryInsecure,
317+
SnapshotPushSecret: options.snapshotPushSecret,
450318
}).SetupWithManager(mgr); err != nil {
451319
setupLog.Error(err, "unable to create controller", "controller", "SandboxSnapshot")
452320
os.Exit(1)

0 commit comments

Comments
 (0)