Skip to content

Commit 352633f

Browse files
[v1.6.x] {ansible,helm}-operator run: add component config flag --config (#4780)
Co-authored-by: Eric Stroczynski <[email protected]>
1 parent 9ae0494 commit 352633f

File tree

11 files changed

+502
-81
lines changed

11 files changed

+502
-81
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
entries:
2+
- description: >
3+
For Ansible- and Helm-based operators, add the `--config` flag, which was mistakenly
4+
not added to either ansible-/helm-operator binary when file support was originally added.
5+
kind: bugfix
6+
migration:
7+
header: Add the manager config patch to config/default/kustomization.yaml
8+
body: >
9+
The scaffolded `--config` flag was not added to either ansible-/helm-operator binary
10+
when [config file](https://master.book.kubebuilder.io/component-config-tutorial/tutorial.html)
11+
support was originally added, so does not currently work.
12+
The `--config` flag supports configuration of both binaries by file;
13+
this method of configuration only applies to the underlying
14+
[controller manager](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Manager),
15+
not the operator as a whole. To optionally configure the operator's Deployment
16+
with a config file, make the following update to config/default/kustomization.yaml:
17+
18+
```diff
19+
# If you want your controller-manager to expose the /metrics
20+
# endpoint w/o any authn/z, please comment the following line.
21+
- manager_auth_proxy_patch.yaml
22+
+
23+
+# Mount the controller config file for loading manager configurations
24+
+# through a ComponentConfig type
25+
+- manager_config_patch.yaml
26+
```
27+
28+
This feature is opt-in: flags can be used as-is or to override config file values.

internal/ansible/flags/flag.go

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"time"
2020

2121
"github.com/spf13/pflag"
22+
"k8s.io/client-go/tools/leaderelection/resourcelock"
23+
"sigs.k8s.io/controller-runtime/pkg/manager"
2224
)
2325

2426
// Flags - Options to be used by an ansible operator
@@ -37,18 +39,26 @@ type Flags struct {
3739
LeaderElectionNamespace string
3840
GracefulShutdownTimeout time.Duration
3941
AnsibleArgs string
42+
43+
// Path to a controller-runtime componentconfig file.
44+
// If this is empty, use default values.
45+
ManagerConfigPath string
46+
47+
// If not nil, used to deduce which flags were set in the CLI.
48+
flagSet *pflag.FlagSet
4049
}
4150

42-
const AnsibleRolesPathEnvVar = "ANSIBLE_ROLES_PATH"
43-
const AnsibleCollectionsPathEnvVar = "ANSIBLE_COLLECTIONS_PATH"
51+
const (
52+
AnsibleRolesPathEnvVar = "ANSIBLE_ROLES_PATH"
53+
AnsibleCollectionsPathEnvVar = "ANSIBLE_COLLECTIONS_PATH"
54+
)
4455

4556
// AddTo - Add the ansible operator flags to the the flagset
4657
func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
47-
flagSet.DurationVar(&f.ReconcilePeriod,
48-
"reconcile-period",
49-
time.Minute,
50-
"Default reconcile period for controllers",
51-
)
58+
// Store flagset internally to be used for lookups later.
59+
f.flagSet = flagSet
60+
61+
// Ansible flags.
5262
flagSet.StringVar(&f.WatchesFile,
5363
"watches-file",
5464
"./watches.yaml",
@@ -59,11 +69,6 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
5969
true,
6070
"The ansible operator will inject owner references unless this flag is false",
6171
)
62-
flagSet.IntVar(&f.MaxConcurrentReconciles,
63-
"max-concurrent-reconciles",
64-
runtime.NumCPU(),
65-
"Maximum number of concurrent reconciles for controllers. Overridden by environment variable.",
66-
)
6772
flagSet.IntVar(&f.AnsibleVerbosity,
6873
"ansible-verbosity",
6974
2,
@@ -77,9 +82,36 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
7782
flagSet.StringVar(&f.AnsibleCollectionsPath,
7883
"ansible-collections-path",
7984
"",
80-
"Path to installed Ansible Collections. If set, collections should be located in {{value}}/ansible_collections/. If unset, collections are assumed to be in ~/.ansible/collections or /usr/share/ansible/collections.",
85+
"Path to installed Ansible Collections. If set, collections should be located in {{value}}/ansible_collections/. "+
86+
"If unset, collections are assumed to be in ~/.ansible/collections or /usr/share/ansible/collections.",
87+
)
88+
flagSet.StringVar(&f.AnsibleArgs,
89+
"ansible-args",
90+
"",
91+
"Ansible args. Allows user to specify arbitrary arguments for ansible-based operators.",
92+
)
93+
94+
// Controller flags.
95+
flagSet.DurationVar(&f.ReconcilePeriod,
96+
"reconcile-period",
97+
time.Minute,
98+
"Default reconcile period for controllers",
99+
)
100+
flagSet.IntVar(&f.MaxConcurrentReconciles,
101+
"max-concurrent-reconciles",
102+
runtime.NumCPU(),
103+
"Maximum number of concurrent reconciles for controllers. Overridden by environment variable.",
104+
)
105+
106+
// Controller manager flags.
107+
flagSet.StringVar(&f.ManagerConfigPath,
108+
"config",
109+
"",
110+
"The controller will load its initial configuration from this file. "+
111+
"Omit this flag to use the default configuration values. "+
112+
"Command-line flags override configuration from this file.",
81113
)
82-
// todo:remove it for 2.0.0
114+
// TODO(2.0.0): remove
83115
flagSet.StringVar(&f.MetricsBindAddress,
84116
"metrics-addr",
85117
":8080",
@@ -91,15 +123,14 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
91123
":8080",
92124
"The address the metric endpoint binds to",
93125
)
94-
// todo: for Go/Helm the port used is: 8081
126+
// TODO(2.0.0): for Go/Helm the port used is: 8081
95127
// update it to keep the project aligned to the other
96-
// types for 2.0
97128
flagSet.StringVar(&f.ProbeAddr,
98129
"health-probe-bind-address",
99130
":6789",
100131
"The address the probe endpoint binds to.",
101132
)
102-
// todo:remove it for 2.0.0
133+
// TODO(2.0.0): remove
103134
flagSet.BoolVar(&f.LeaderElection,
104135
"enable-leader-election",
105136
false,
@@ -131,9 +162,43 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
131162
"The amount of time that will be spent waiting"+
132163
" for runners to gracefully exit.",
133164
)
134-
flagSet.StringVar(&f.AnsibleArgs,
135-
"ansible-args",
136-
"",
137-
"Ansible args. Allows user to specify arbitrary arguments for ansible-based operators.",
138-
)
165+
}
166+
167+
// ToManagerOptions uses the flag set in f to configure options.
168+
// Values of options take precedence over flag defaults,
169+
// as values are assume to have been explicitly set.
170+
func (f *Flags) ToManagerOptions(options manager.Options) manager.Options {
171+
// Alias FlagSet.Changed so options are still updated when fields are empty.
172+
changed := func(flagName string) bool {
173+
return f.flagSet.Changed(flagName)
174+
}
175+
if f.flagSet == nil {
176+
changed = func(flagName string) bool { return false }
177+
}
178+
179+
// TODO(2.0.0): remove metrics-addr
180+
if changed("metrics-bind-address") || changed("metrics-addr") || options.MetricsBindAddress == "" {
181+
options.MetricsBindAddress = f.MetricsBindAddress
182+
}
183+
if changed("health-probe-bind-address") || options.HealthProbeBindAddress == "" {
184+
options.HealthProbeBindAddress = f.ProbeAddr
185+
}
186+
// TODO(2.0.0): remove enable-leader-election
187+
if changed("leader-elect") || changed("enable-leader-election") || !options.LeaderElection {
188+
options.LeaderElection = f.LeaderElection
189+
}
190+
if changed("leader-election-id") || options.LeaderElectionID == "" {
191+
options.LeaderElectionID = f.LeaderElectionID
192+
}
193+
if changed("leader-election-namespace") || options.LeaderElectionNamespace == "" {
194+
options.LeaderElectionNamespace = f.LeaderElectionNamespace
195+
}
196+
if options.LeaderElectionResourceLock == "" {
197+
options.LeaderElectionResourceLock = resourcelock.ConfigMapsResourceLock
198+
}
199+
if changed("graceful-shutdown-timeout") || options.GracefulShutdownTimeout == nil {
200+
options.GracefulShutdownTimeout = &f.GracefulShutdownTimeout
201+
}
202+
203+
return options
139204
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2021 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package flags_test
16+
17+
import (
18+
. "github.com/onsi/ginkgo"
19+
. "github.com/onsi/gomega"
20+
"github.com/spf13/pflag"
21+
"sigs.k8s.io/controller-runtime/pkg/manager"
22+
23+
"github.com/operator-framework/operator-sdk/internal/ansible/flags"
24+
)
25+
26+
var _ = Describe("Flags", func() {
27+
Describe("ToManagerOptions", func() {
28+
var (
29+
f *flags.Flags
30+
flagSet *pflag.FlagSet
31+
options manager.Options
32+
)
33+
BeforeEach(func() {
34+
f = &flags.Flags{}
35+
flagSet = pflag.NewFlagSet("test", pflag.ExitOnError)
36+
f.AddTo(flagSet)
37+
})
38+
39+
When("the flag is set", func() {
40+
It("uses the flag value when corresponding option value is empty", func() {
41+
expOptionValue := ":5678"
42+
options.MetricsBindAddress = ""
43+
parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
44+
Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
45+
})
46+
It("uses the flag value when corresponding option value is not empty", func() {
47+
expOptionValue := ":5678"
48+
options.MetricsBindAddress = ":1234"
49+
parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
50+
Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
51+
})
52+
})
53+
When("the flag is not set", func() {
54+
It("uses the default flag value when corresponding option value is empty", func() {
55+
expOptionValue := ":8080"
56+
options.MetricsBindAddress = ""
57+
parseArgs(flagSet)
58+
Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
59+
})
60+
It("uses the option value when corresponding option value is not empty", func() {
61+
expOptionValue := ":1234"
62+
options.MetricsBindAddress = expOptionValue
63+
parseArgs(flagSet)
64+
Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
65+
})
66+
})
67+
})
68+
})
69+
70+
func parseArgs(fs *pflag.FlagSet, extraArgs ...string) {
71+
Expect(fs.Parse(extraArgs)).To(Succeed())
72+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2021 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package flags_test
16+
17+
import (
18+
"testing"
19+
20+
. "github.com/onsi/ginkgo"
21+
. "github.com/onsi/gomega"
22+
)
23+
24+
func TestFlags(t *testing.T) {
25+
RegisterFailHandler(Fail)
26+
RunSpecs(t, "Flags Suite")
27+
}

0 commit comments

Comments
 (0)