-
Notifications
You must be signed in to change notification settings - Fork 463
/
options.go
507 lines (436 loc) · 19.4 KB
/
options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
// Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/go-logr/logr"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/leaderelection/resourcelock"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/manager"
extensionscontroller "github.com/gardener/gardener/extensions/pkg/controller"
"github.com/gardener/gardener/pkg/logger"
)
const (
// LeaderElectionFlag is the name of the command line flag to specify whether to do leader election or not.
LeaderElectionFlag = "leader-election"
// LeaderElectionResourceLockFlag is the name of the command line flag to specify the resource type used for leader
// election.
LeaderElectionResourceLockFlag = "leader-election-resource-lock"
// LeaderElectionIDFlag is the name of the command line flag to specify the leader election ID.
LeaderElectionIDFlag = "leader-election-id"
// LeaderElectionNamespaceFlag is the name of the command line flag to specify the leader election namespace.
LeaderElectionNamespaceFlag = "leader-election-namespace"
// WebhookServerHostFlag is the name of the command line flag to specify the webhook config host for 'url' mode.
WebhookServerHostFlag = "webhook-config-server-host"
// WebhookServerPortFlag is the name of the command line flag to specify the webhook server port.
WebhookServerPortFlag = "webhook-config-server-port"
// WebhookCertDirFlag is the name of the command line flag to specify the webhook certificate directory.
WebhookCertDirFlag = "webhook-config-cert-dir"
// MetricsBindAddressFlag is the name of the command line flag to specify the TCP address that the controller
// should bind to for serving prometheus metrics.
// It can be set to "0" to disable the metrics serving.
MetricsBindAddressFlag = "metrics-bind-address"
// HealthBindAddressFlag is the name of the command line flag to specify the TCP address that the controller
// should bind to for serving health probes
HealthBindAddressFlag = "health-bind-address"
// MaxConcurrentReconcilesFlag is the name of the command line flag to specify the maximum number of
// concurrent reconciliations a controller can do.
MaxConcurrentReconcilesFlag = "max-concurrent-reconciles"
// KubeconfigFlag is the name of the command line flag to specify a kubeconfig used to retrieve
// a rest.Config for a manager.Manager.
KubeconfigFlag = clientcmd.RecommendedConfigPathFlag
// MasterURLFlag is the name of the command line flag to specify the master URL override for
// a rest.Config of a manager.Manager.
MasterURLFlag = "master"
// DisableFlag is the name of the command line flag to disable individual controllers.
DisableFlag = "disable-controllers"
// GardenerVersionFlag is the name of the command line flag containing the Gardener version.
GardenerVersionFlag = "gardener-version"
// LogLevelFlag is the name of the command line flag containing the log level.
LogLevelFlag = "log-level"
// LogFormatFlag is the name of the command line flag containing the log format.
LogFormatFlag = "log-format"
)
// LeaderElectionNameID returns a leader election ID for the given name.
func LeaderElectionNameID(name string) string {
return fmt.Sprintf("%s-leader-election", name)
}
// Flagger adds flags to a given FlagSet.
type Flagger interface {
// AddFlags adds the flags of this Flagger to the given FlagSet.
AddFlags(*pflag.FlagSet)
}
type prefixedFlagger struct {
prefix string
flagger Flagger
}
// AddFlags implements Flagger.AddFlags.
func (p *prefixedFlagger) AddFlags(fs *pflag.FlagSet) {
temp := pflag.NewFlagSet("", pflag.ExitOnError)
p.flagger.AddFlags(temp)
temp.VisitAll(func(flag *pflag.Flag) {
flag.Name = fmt.Sprintf("%s%s", p.prefix, flag.Name)
})
fs.AddFlagSet(temp)
}
// PrefixFlagger creates a flagger that prefixes all its flags with the given prefix.
func PrefixFlagger(prefix string, flagger Flagger) Flagger {
return &prefixedFlagger{prefix, flagger}
}
// PrefixOption creates an option that prefixes all its flags with the given prefix.
func PrefixOption(prefix string, option Option) Option {
return struct {
Flagger
Completer
}{PrefixFlagger(prefix, option), option}
}
// Completer completes some work.
type Completer interface {
// Complete completes the work, optionally returning an error.
Complete() error
}
// Option is a Flagger and Completer.
// It sets command line flags and does some work when the flags have been parsed, optionally producing
// an error.
type Option interface {
Flagger
Completer
}
// OptionAggregator is a builder that aggregates multiple options.
type OptionAggregator []Option
// NewOptionAggregator instantiates a new OptionAggregator and registers all given options.
func NewOptionAggregator(options ...Option) OptionAggregator {
var builder OptionAggregator
builder.Register(options...)
return builder
}
// Register registers the given options in this OptionAggregator.
func (b *OptionAggregator) Register(options ...Option) {
*b = append(*b, options...)
}
// AddFlags implements Flagger.AddFlags.
func (b *OptionAggregator) AddFlags(fs *pflag.FlagSet) {
for _, option := range *b {
option.AddFlags(fs)
}
}
// Complete implements Completer.Complete.
func (b *OptionAggregator) Complete() error {
for _, option := range *b {
if err := option.Complete(); err != nil {
return err
}
}
return nil
}
// ManagerOptions are command line options that can be set for manager.Options.
type ManagerOptions struct {
// LeaderElection is whether leader election is turned on or not.
LeaderElection bool
// LeaderElectionResourceLock is the resource type used for leader election (defaults to `leases`).
//
// When changing the default resource lock, please make sure to migrate via multilocks to
// avoid situations where multiple running instances of your controller have each acquired leadership
// through different resource locks (e.g. during upgrades) and thus act on the same resources concurrently.
// For example, if you want to migrate to the "leases" resource lock, you might do so by migrating
// to the respective multilock first ("configmapsleases" or "endpointsleases"), which will acquire
// a leader lock on both resources. After one release with the multilock as a default, you can
// go ahead and migrate to "leases". Please also keep in mind, that users might skip versions
// of your controller, so at least add a flashy release note when changing the default lock.
//
// Note: before controller-runtime version v0.7, the resource lock was set to "configmaps".
// Please keep this in mind, when planning a proper migration path for your controller.
LeaderElectionResourceLock string
// LeaderElectionID is the id to do leader election with.
LeaderElectionID string
// LeaderElectionNamespace is the namespace to do leader election in.
LeaderElectionNamespace string
// WebhookServerHost is the host for the webhook server.
WebhookServerHost string
// WebhookServerPort is the port for the webhook server.
WebhookServerPort int
// WebhookCertDir is the directory that contains the webhook server key and certificate.
WebhookCertDir string
// MetricsBindAddress is the TCP address that the controller should bind to for serving prometheus metrics.
MetricsBindAddress string
// HealthBindAddress is the TCP address that the controller should bind to for serving health probes.
HealthBindAddress string
// LogLevel defines the level/severity for the logs. Must be one of [info,debug,error]
LogLevel string
// LogFormat defines the format for the logs. Must be one of [json,text]
LogFormat string
config *ManagerConfig
}
// AddFlags implements Flagger.AddFlags.
func (m *ManagerOptions) AddFlags(fs *pflag.FlagSet) {
defaultLeaderElectionResourceLock := m.LeaderElectionResourceLock
if defaultLeaderElectionResourceLock == "" {
// explicitly default to leases if no default is specified
defaultLeaderElectionResourceLock = resourcelock.LeasesResourceLock
}
fs.BoolVar(&m.LeaderElection, LeaderElectionFlag, m.LeaderElection, "Whether to use leader election or not when running this controller manager.")
fs.StringVar(&m.LeaderElectionResourceLock, LeaderElectionResourceLockFlag, defaultLeaderElectionResourceLock, "Which resource type to use for leader election. "+
"Supported options are 'leases', 'endpointsleases' and 'configmapsleases'.")
fs.StringVar(&m.LeaderElectionID, LeaderElectionIDFlag, m.LeaderElectionID, "The leader election id to use.")
fs.StringVar(&m.LeaderElectionNamespace, LeaderElectionNamespaceFlag, m.LeaderElectionNamespace, "The namespace to do leader election in.")
fs.StringVar(&m.WebhookServerHost, WebhookServerHostFlag, m.WebhookServerHost, "The webhook server host.")
fs.IntVar(&m.WebhookServerPort, WebhookServerPortFlag, m.WebhookServerPort, "The webhook server port.")
fs.StringVar(&m.WebhookCertDir, WebhookCertDirFlag, m.WebhookCertDir, "The directory that contains the webhook server key and certificate.")
fs.StringVar(&m.MetricsBindAddress, MetricsBindAddressFlag, ":8080", "bind address for the metrics server")
fs.StringVar(&m.HealthBindAddress, HealthBindAddressFlag, ":8081", "bind address for the health server")
fs.StringVar(&m.LogLevel, LogLevelFlag, logger.InfoLevel, "The level/severity for the logs. Must be one of [info,debug,error]")
fs.StringVar(&m.LogFormat, LogFormatFlag, logger.FormatJSON, "The format for the logs. Must be one of [json,text]")
}
// Complete implements Completer.Complete.
func (m *ManagerOptions) Complete() error {
if !sets.NewString(logger.AllLogLevels...).Has(m.LogLevel) {
return fmt.Errorf("invalid --%s: %s", LogLevelFlag, m.LogLevel)
}
if !sets.NewString(logger.AllLogFormats...).Has(m.LogFormat) {
return fmt.Errorf("invalid --%s: %s", LogFormatFlag, m.LogFormat)
}
logger, err := logger.NewZapLogger(m.LogLevel, m.LogFormat)
if err != nil {
return fmt.Errorf("error instantiating zap logger: %w", err)
}
m.config = &ManagerConfig{m.LeaderElection, m.LeaderElectionResourceLock, m.LeaderElectionID, m.LeaderElectionNamespace, m.WebhookServerHost, m.WebhookServerPort, m.WebhookCertDir, m.MetricsBindAddress, m.HealthBindAddress, logger}
return nil
}
// Completed returns the completed ManagerConfig. Only call this if `Complete` was successful.
func (m *ManagerOptions) Completed() *ManagerConfig {
return m.config
}
// ManagerConfig is a completed manager configuration.
type ManagerConfig struct {
// LeaderElection is whether leader election is turned on or not.
LeaderElection bool
// LeaderElectionResourceLock is the resource type used for leader election.
LeaderElectionResourceLock string
// LeaderElectionID is the id to do leader election with.
LeaderElectionID string
// LeaderElectionNamespace is the namespace to do leader election in.
LeaderElectionNamespace string
// WebhookServerHost is the host for the webhook server.
WebhookServerHost string
// WebhookServerPort is the port for the webhook server.
WebhookServerPort int
// WebhookCertDir is the directory that contains the webhook server key and certificate.
WebhookCertDir string
// MetricsBindAddress is the TCP address that the controller should bind to for serving prometheus metrics.
MetricsBindAddress string
// HealthBindAddress is the TCP address that the controller should bind to for serving health probes.
HealthBindAddress string
// Logger is a logr.Logger compliant logger
Logger logr.Logger
}
// Apply sets the values of this ManagerConfig in the given manager.Options.
func (c *ManagerConfig) Apply(opts *manager.Options) {
opts.LeaderElection = c.LeaderElection
opts.LeaderElectionResourceLock = c.LeaderElectionResourceLock
opts.LeaderElectionID = c.LeaderElectionID
opts.LeaderElectionNamespace = c.LeaderElectionNamespace
opts.Host = c.WebhookServerHost
opts.Port = c.WebhookServerPort
opts.CertDir = c.WebhookCertDir
opts.MetricsBindAddress = c.MetricsBindAddress
opts.HealthProbeBindAddress = c.HealthBindAddress
opts.Logger = c.Logger
}
// Options initializes empty manager.Options, applies the set values and returns it.
func (c *ManagerConfig) Options() manager.Options {
var opts manager.Options
c.Apply(&opts)
return opts
}
// ControllerOptions are command line options that can be set for controller.Options.
type ControllerOptions struct {
// MaxConcurrentReconciles are the maximum concurrent reconciles.
MaxConcurrentReconciles int
config *ControllerConfig
}
// AddFlags implements Flagger.AddFlags.
func (c *ControllerOptions) AddFlags(fs *pflag.FlagSet) {
fs.IntVar(&c.MaxConcurrentReconciles, MaxConcurrentReconcilesFlag, c.MaxConcurrentReconciles, "The maximum number of concurrent reconciliations.")
}
// Complete implements Completer.Complete.
func (c *ControllerOptions) Complete() error {
c.config = &ControllerConfig{c.MaxConcurrentReconciles}
return nil
}
// Completed returns the completed ControllerConfig. Only call this if `Complete` was successful.
func (c *ControllerOptions) Completed() *ControllerConfig {
return c.config
}
// ControllerConfig is a completed controller configuration.
type ControllerConfig struct {
// MaxConcurrentReconciles is the maximum number of concurrent reconciles.
MaxConcurrentReconciles int
}
// Apply sets the values of this ControllerConfig in the given controller.Options.
func (c *ControllerConfig) Apply(opts *controller.Options) {
opts.MaxConcurrentReconciles = c.MaxConcurrentReconciles
}
// Options initializes empty controller.Options, applies the set values and returns it.
func (c *ControllerConfig) Options() controller.Options {
var opts controller.Options
c.Apply(&opts)
return opts
}
// RESTOptions are command line options that can be set for rest.Config.
type RESTOptions struct {
// Kubeconfig is the path to a kubeconfig.
Kubeconfig string
// MasterURL is an override for the URL in a kubeconfig. Only used if out-of-cluster.
MasterURL string
config *RESTConfig
}
// RESTConfig is a completed REST configuration.
type RESTConfig struct {
// Config is the rest.Config.
Config *rest.Config
}
var (
// BuildConfigFromFlags creates a build configuration from the given flags. Exposed for testing.
BuildConfigFromFlags = clientcmd.BuildConfigFromFlags
// InClusterConfig obtains the current in-cluster config. Exposed for testing.
InClusterConfig = rest.InClusterConfig
// Getenv obtains the environment variable with the given name. Exposed for testing.
Getenv = os.Getenv
// RecommendedHomeFile is the recommended location of the kubeconfig. Exposed for testing.
RecommendedHomeFile = clientcmd.RecommendedHomeFile
)
func (r *RESTOptions) buildConfig() (*rest.Config, error) {
// If a flag is specified with the config location, use that
if len(r.Kubeconfig) > 0 {
return BuildConfigFromFlags(r.MasterURL, r.Kubeconfig)
}
// If an env variable is specified with the config location, use that
if kubeconfig := Getenv(clientcmd.RecommendedConfigPathEnvVar); len(kubeconfig) > 0 {
return BuildConfigFromFlags(r.MasterURL, kubeconfig)
}
// If no explicit location, try the in-cluster config
if c, err := InClusterConfig(); err == nil {
return c, nil
}
return BuildConfigFromFlags("", RecommendedHomeFile)
}
// Complete implements RESTCompleter.Complete.
func (r *RESTOptions) Complete() error {
config, err := r.buildConfig()
if err != nil {
return err
}
r.config = &RESTConfig{config}
return nil
}
// Completed returns the completed RESTConfig. Only call this if `Complete` was successful.
func (r *RESTOptions) Completed() *RESTConfig {
return r.config
}
// AddFlags implements Flagger.AddFlags.
func (r *RESTOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&r.Kubeconfig, KubeconfigFlag, "", "Paths to a kubeconfig. Only required if out-of-cluster.")
fs.StringVar(&r.MasterURL, MasterURLFlag, "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
}
// SwitchOptions are options to build an AddToManager function that filters the disabled controllers.
type SwitchOptions struct {
Disabled []string
nameToAddToManager map[string]func(manager.Manager) error
addToManagerBuilder extensionscontroller.AddToManagerBuilder
}
// Register registers the given NameToControllerFuncs in the options.
func (d *SwitchOptions) Register(pairs ...NameToAddToManagerFunc) {
for _, pair := range pairs {
d.nameToAddToManager[pair.Name] = pair.Func
}
}
// NameToAddToManagerFunc binds a specific name to a controller's AddToManager function.
type NameToAddToManagerFunc struct {
Name string
Func func(manager.Manager) error
}
// Switch binds the given name to the given AddToManager function.
func Switch(name string, f func(manager.Manager) error) NameToAddToManagerFunc {
return NameToAddToManagerFunc{
Name: name,
Func: f,
}
}
// NewSwitchOptions creates new SwitchOptions with the given initial pairs.
func NewSwitchOptions(pairs ...NameToAddToManagerFunc) *SwitchOptions {
opts := SwitchOptions{nameToAddToManager: make(map[string]func(manager.Manager) error)}
opts.Register(pairs...)
return &opts
}
// AddFlags implements Option.
func (d *SwitchOptions) AddFlags(fs *pflag.FlagSet) {
controllerNames := make([]string, 0, len(d.nameToAddToManager))
for name := range d.nameToAddToManager {
controllerNames = append(controllerNames, name)
}
fs.StringSliceVar(&d.Disabled, DisableFlag, d.Disabled, fmt.Sprintf("List of controllers to disable %v", controllerNames))
}
// Complete implements Option.
func (d *SwitchOptions) Complete() error {
disabled := sets.NewString()
for _, disabledName := range d.Disabled {
if _, ok := d.nameToAddToManager[disabledName]; !ok {
return fmt.Errorf("cannot disable unknown controller %q", disabledName)
}
disabled.Insert(disabledName)
}
for name, addToManager := range d.nameToAddToManager {
if !disabled.Has(name) {
d.addToManagerBuilder.Register(addToManager)
}
}
return nil
}
// Completed returns the completed SwitchConfig. Call this only after successfully calling `Completed`.
func (d *SwitchOptions) Completed() *SwitchConfig {
return &SwitchConfig{d.addToManagerBuilder.AddToManager}
}
// SwitchConfig is the completed configuration of SwitchOptions.
type SwitchConfig struct {
AddToManager func(manager.Manager) error
}
// GeneralOptions are command line options that can be set for general configuration.
type GeneralOptions struct {
// GardenerVersion string
GardenerVersion string
config *GeneralConfig
}
// GeneralConfig is a completed general configuration.
type GeneralConfig struct {
// GardenerVersion string
GardenerVersion string
}
// Complete implements Complete.
func (r *GeneralOptions) Complete() error {
r.config = &GeneralConfig{r.GardenerVersion}
return nil
}
// Completed returns the completed GeneralConfig. Only call this if `Complete` was successful.
func (r *GeneralOptions) Completed() *GeneralConfig {
return r.config
}
// AddFlags implements Flagger.AddFlags.
func (r *GeneralOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&r.GardenerVersion, GardenerVersionFlag, "", "Version of the gardenlet.")
}