forked from gardener-attic/gardener-extensions
/
options.go
369 lines (313 loc) · 12.3 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
// 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"
extensionscontroller "github.com/gardener/gardener-extensions/pkg/controller"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"os"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/manager"
)
const (
// LeaderElectionFlag is the name of the command line flag to specify whether to do leader election or not.
LeaderElectionFlag = "leader-election"
// 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"
// 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"
)
// 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
// LeaderElectionID is the id to do leader election with.
LeaderElectionID string
// LeaderElectionNamespace is the namespace to do leader election in.
LeaderElectionNamespace string
config *ManagerConfig
}
// AddFlags implements Flagger.AddFlags.
func (m *ManagerOptions) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&m.LeaderElection, LeaderElectionFlag, m.LeaderElection, "Whether to use leader election or not when running this controller manager.")
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.")
}
// Complete implements Completer.Complete.
func (m *ManagerOptions) Complete() error {
m.config = &ManagerConfig{m.LeaderElection, m.LeaderElectionID, m.LeaderElectionNamespace}
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
// LeaderElectionID is the id to do leader election with.
LeaderElectionID string
// LeaderElectionNamespace is the namespace to do leader election in.
LeaderElectionNamespace string
}
// Apply sets the values of this ManagerConfig in the given manager.Options.
func (c *ManagerConfig) Apply(opts *manager.Options) {
opts.LeaderElection = c.LeaderElection
opts.LeaderElectionID = c.LeaderElectionID
opts.LeaderElectionNamespace = c.LeaderElectionNamespace
}
// 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) {
fs.StringSliceVar(&d.Disabled, DisableFlag, d.Disabled, "List of controllers to disable")
}
// 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
}