Skip to content

Commit 9defce6

Browse files
ansible: rename cmd line flag and add global variable (#3435)
This commit: * Deprecates --max-workers flag * Adds --max-concurrent-reconciles flag * Adds MAX_CONCURRENT_RECONCILES_<group>_<kind> global variable * Update relevant documentation
1 parent 9f6ccd6 commit 9defce6

File tree

6 files changed

+153
-48
lines changed

6 files changed

+153
-48
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
entries:
2+
- description: >
3+
Add "--max-concurrent-reconciles" flag and "MAX_CONCURRENT_RECONCILES_<Kind>_<Group>" global variable
4+
to ansible binary. Also add deprecate message for "--max-workers" flag and "WORKERS_<Kind>_<Group>"
5+
global variable.
6+
kind: "addition"

cmd/ansible-operator/main.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,13 @@ func printVersion() {
6767

6868
func main() {
6969
f := &flags.Flags{}
70-
f.AddTo(pflag.CommandLine)
70+
err := f.AddTo(pflag.CommandLine)
71+
if err != nil {
72+
log.Error(err, "Failed to add ansible command line flags")
73+
}
7174
pflag.Parse()
7275
logf.SetLogger(zap.Logger())
76+
warningMsgForDeprecatedFlags()
7377

7478
printVersion()
7579

@@ -127,7 +131,7 @@ func main() {
127131

128132
var gvks []schema.GroupVersionKind
129133
cMap := controllermap.NewControllerMap()
130-
watches, err := watches.Load(f.WatchesFile, f.MaxWorkers, f.AnsibleVerbosity)
134+
watches, err := watches.Load(f.WatchesFile, f.MaxConcurrentReconciles, f.AnsibleVerbosity)
131135
if err != nil {
132136
log.Error(err, "Failed to load watches.")
133137
os.Exit(1)
@@ -296,3 +300,15 @@ func getAnsibleDebugLog() bool {
296300
}
297301
return val
298302
}
303+
304+
// warningMsgForDeprecatedFlags logs warning messages if deprecated flags are used. Currently,
305+
// "--max-workers" is deprecated. Any value provided using this flags will not be used. Instead
306+
// users are directed to use "--max-concurrent-reconciles".
307+
func warningMsgForDeprecatedFlags() {
308+
if pflag.Lookup("max-workers").Changed {
309+
log.Info("Flag --max-workers has been deprecated, use --max-concurrent-reconciles instead")
310+
if pflag.Lookup("max-concurrent-reconciles").Changed {
311+
log.Info("Ignoring --max-workers since --max-concurrent-reconciles is set")
312+
}
313+
}
314+
}

pkg/ansible/flags/flag.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package flags
1616

1717
import (
18+
"fmt"
1819
"runtime"
1920
"time"
2021

@@ -25,21 +26,21 @@ import (
2526

2627
// Flags - Options to be used by an ansible operator
2728
type Flags struct {
28-
ReconcilePeriod time.Duration
29-
WatchesFile string
30-
InjectOwnerRef bool
31-
MaxWorkers int
32-
AnsibleVerbosity int
33-
AnsibleRolesPath string
34-
AnsibleCollectionsPath string
29+
ReconcilePeriod time.Duration
30+
WatchesFile string
31+
InjectOwnerRef bool
32+
MaxConcurrentReconciles int
33+
MaxWorkers int
34+
AnsibleVerbosity int
35+
AnsibleRolesPath string
36+
AnsibleCollectionsPath string
3537
}
3638

3739
const AnsibleRolesPathEnvVar = "ANSIBLE_ROLES_PATH"
3840
const AnsibleCollectionsPathEnvVar = "ANSIBLE_COLLECTIONS_PATH"
3941

4042
// AddTo - Add the ansible operator flags to the the flagset
41-
// helpTextPrefix will allow you add a prefix to default help text. Joined by a space.
42-
func (f *Flags) AddTo(flagSet *pflag.FlagSet, helpTextPrefix ...string) {
43+
func (f *Flags) AddTo(flagSet *pflag.FlagSet) error {
4344
flagSet.AddFlagSet(zap.FlagSet())
4445
flagSet.DurationVar(&f.ReconcilePeriod,
4546
"reconcile-period",
@@ -61,6 +62,11 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet, helpTextPrefix ...string) {
6162
runtime.NumCPU(),
6263
"Maximum number of workers to use. Overridden by environment variable.",
6364
)
65+
flagSet.IntVar(&f.MaxConcurrentReconciles,
66+
"max-concurrent-reconciles",
67+
runtime.NumCPU(),
68+
"Maximum number of concurrent reconciles for controllers. Overridden by environment variable.",
69+
)
6470
flagSet.IntVar(&f.AnsibleVerbosity,
6571
"ansible-verbosity",
6672
2,
@@ -76,4 +82,9 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet, helpTextPrefix ...string) {
7682
"",
7783
"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.",
7884
)
85+
err := flagSet.MarkDeprecated("max-workers", "use --max-concurrent-reconciles instead.")
86+
if err != nil {
87+
return fmt.Errorf("flag cannot be deprecated %v", err)
88+
}
89+
return nil
7990
}

pkg/ansible/watches/watches.go

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"io/ioutil"
2323
"os"
2424
"path/filepath"
25+
"runtime"
2526
"strconv"
2627
"strings"
2728
"time"
@@ -79,8 +80,8 @@ var (
7980
selectorDefault = metav1.LabelSelector{}
8081

8182
// these are overridden by cmdline flags
82-
maxWorkersDefault = 1
83-
ansibleVerbosityDefault = 2
83+
maxConcurrentReconcilesDefault = runtime.NumCPU()
84+
ansibleVerbosityDefault = 2
8485
)
8586

8687
// Creates, populates, and returns a LabelSelector object. Used in Unmarshal().
@@ -178,7 +179,7 @@ func (w *Watch) setValuesFromAlias(tmp alias) error {
178179
w.Role = tmp.Role
179180
w.Vars = tmp.Vars
180181
w.MaxRunnerArtifacts = tmp.MaxRunnerArtifacts
181-
w.MaxWorkers = getMaxWorkers(gvk, maxWorkersDefault)
182+
w.MaxWorkers = getMaxConcurrentReconciles(gvk, maxConcurrentReconcilesDefault)
182183
w.ReconcilePeriod = tmp.ReconcilePeriod.Duration
183184
w.ManageStatus = *tmp.ManageStatus
184185
w.WatchDependentResources = *tmp.WatchDependentResources
@@ -305,7 +306,7 @@ func New(gvk schema.GroupVersionKind, role, playbook string, vars map[string]int
305306
Role: role,
306307
Vars: vars,
307308
MaxRunnerArtifacts: maxRunnerArtifactsDefault,
308-
MaxWorkers: maxWorkersDefault,
309+
MaxWorkers: maxConcurrentReconcilesDefault,
309310
ReconcilePeriod: reconcilePeriodDefault.Duration,
310311
ManageStatus: manageStatusDefault,
311312
WatchDependentResources: watchDependentResourcesDefault,
@@ -318,8 +319,8 @@ func New(gvk schema.GroupVersionKind, role, playbook string, vars map[string]int
318319
}
319320

320321
// Load - loads a slice of Watches from the watches file from the CLI
321-
func Load(path string, maxWorkers, ansibleVerbosity int) ([]Watch, error) {
322-
maxWorkersDefault = maxWorkers
322+
func Load(path string, maxReconciler, ansibleVerbosity int) ([]Watch, error) {
323+
maxConcurrentReconcilesDefault = maxReconciler
323324
ansibleVerbosityDefault = ansibleVerbosity
324325
b, err := ioutil.ReadFile(path)
325326
if err != nil {
@@ -403,19 +404,23 @@ func verifyAnsiblePath(playbook string, role string) error {
403404
// number of workers based on their cluster resources. While the
404405
// author may use the CLI option to specify a suggested
405406
// configuration for the operator.
406-
func getMaxWorkers(gvk schema.GroupVersionKind, defValue int) int {
407-
envVar := strings.ToUpper(strings.Replace(
407+
func getMaxConcurrentReconciles(gvk schema.GroupVersionKind, defValue int) int {
408+
envVarMaxWorker := strings.ToUpper(strings.ReplaceAll(
408409
fmt.Sprintf("WORKER_%s_%s", gvk.Kind, gvk.Group),
409410
".",
410411
"_",
411-
-1,
412412
))
413-
maxWorkers := getIntegerEnvWithDefault(envVar, defValue)
414-
if maxWorkers <= 0 {
415-
log.Info("Value %v not valid. Using default %v", maxWorkers, defValue)
413+
envVarMaxReconciler := strings.ToUpper(strings.ReplaceAll(
414+
fmt.Sprintf("MAX_CONCURRENT_RECONCILES_%s_%s", gvk.Kind, gvk.Group),
415+
".",
416+
"_",
417+
))
418+
envVal := getIntegerEnvMaxReconcile(envVarMaxWorker, envVarMaxReconciler, defValue)
419+
if envVal <= 0 {
420+
log.Info("Value %v not valid. Using default %v", envVal, defValue)
416421
return defValue
417422
}
418-
return maxWorkers
423+
return envVal
419424
}
420425

421426
// if the ANSIBLE_VERBOSITY_* environment variable is set, use that value.
@@ -457,3 +462,32 @@ func getIntegerEnvWithDefault(envVar string, defValue int) int {
457462
}
458463
return val
459464
}
465+
466+
// getIntegerEnvMaxReconcile looks for global variable "MAX_CONCURRENT_RECONCILES_<group>_<kind>",
467+
// if not present it checks for "WORKER_<group>_<kind>" and logs deprecation message
468+
// if required. If both of them are not set, we use the default value passed on by command line
469+
// flags.
470+
func getIntegerEnvMaxReconcile(envVarMaxWorker, envVarMaxReconciler string, defValue int) int {
471+
val := defValue
472+
if envValRecon, ok := os.LookupEnv(envVarMaxReconciler); ok {
473+
if i, err := strconv.Atoi(envValRecon); err != nil {
474+
log.Info("Could not parse environment variable as an integer; using default value",
475+
"envVar", envVarMaxReconciler, "default", defValue)
476+
} else {
477+
val = i
478+
}
479+
} else if !ok {
480+
if envValWorker, ok := os.LookupEnv(envVarMaxWorker); ok {
481+
deprecationMsg := fmt.Sprintf("Environment variable %s is deprecated, use %s instead", envVarMaxWorker, envVarMaxReconciler)
482+
log.Info(deprecationMsg)
483+
if i, err := strconv.Atoi(envValWorker); err != nil {
484+
log.Info("Could not parse environment variable as an integer; using default value",
485+
"envVar", envVarMaxWorker, "default", defValue)
486+
} else {
487+
val = i
488+
}
489+
}
490+
}
491+
return val
492+
493+
}

pkg/ansible/watches/watches_test.go

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ func TestNew(t *testing.T) {
6565
t.Fatalf("Unexpected maxRunnerArtifacts %v expected %v", watch.MaxRunnerArtifacts,
6666
maxRunnerArtifactsDefault)
6767
}
68-
if watch.MaxWorkers != maxWorkersDefault {
69-
t.Fatalf("Unexpected maxWorkers %v expected %v", watch.MaxWorkers, maxWorkersDefault)
68+
if watch.MaxWorkers != maxConcurrentReconcilesDefault {
69+
t.Fatalf("Unexpected maxWorkers %v expected %v", watch.MaxWorkers, maxConcurrentReconcilesDefault)
7070
}
7171
if watch.ReconcilePeriod != expectedReconcilePeriod {
7272
t.Fatalf("Unexpected reconcilePeriod %v expected %v", watch.ReconcilePeriod,
@@ -569,8 +569,7 @@ func TestMaxWorkers(t *testing.T) {
569569
defValue int
570570
expectedValue int
571571
setEnv bool
572-
envKey string
573-
envValue int
572+
envVarMap map[string]int
574573
}{
575574
{
576575
name: "no env, use default value",
@@ -582,7 +581,9 @@ func TestMaxWorkers(t *testing.T) {
582581
defValue: 1,
583582
expectedValue: 1,
584583
setEnv: false,
585-
envKey: "WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM",
584+
envVarMap: map[string]int{
585+
"WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM": 0,
586+
},
586587
},
587588
{
588589
name: "invalid env, use default value",
@@ -594,11 +595,12 @@ func TestMaxWorkers(t *testing.T) {
594595
defValue: 1,
595596
expectedValue: 1,
596597
setEnv: true,
597-
envKey: "WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM",
598-
envValue: 0,
598+
envVarMap: map[string]int{
599+
"WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM": 0,
600+
},
599601
},
600602
{
601-
name: "env set to 3, expect 3",
603+
name: "worker_%s_%s env set to 3, expect 3",
602604
gvk: schema.GroupVersionKind{
603605
Group: "cache.example.com",
604606
Version: "v1alpha1",
@@ -607,18 +609,50 @@ func TestMaxWorkers(t *testing.T) {
607609
defValue: 1,
608610
expectedValue: 3,
609611
setEnv: true,
610-
envKey: "WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM",
611-
envValue: 3,
612+
envVarMap: map[string]int{
613+
"WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM": 3,
614+
},
615+
},
616+
{
617+
name: "max_concurrent_reconciler_%s_%s set to 2, expect 2",
618+
gvk: schema.GroupVersionKind{
619+
Group: "cache.example.com",
620+
Version: "v1alpha1",
621+
Kind: "MemCacheService",
622+
},
623+
defValue: 1,
624+
expectedValue: 2,
625+
setEnv: true,
626+
envVarMap: map[string]int{
627+
"MAX_CONCURRENT_RECONCILES_MEMCACHESERVICE_CACHE_EXAMPLE_COM": 2,
628+
},
629+
},
630+
{
631+
name: "set multiple env variables",
632+
gvk: schema.GroupVersionKind{
633+
Group: "cache.example.com",
634+
Version: "v1alpha1",
635+
Kind: "MemCacheService",
636+
},
637+
defValue: 1,
638+
expectedValue: 3,
639+
setEnv: true,
640+
envVarMap: map[string]int{
641+
"MAX_CONCURRENT_RECONCILES_MEMCACHESERVICE_CACHE_EXAMPLE_COM": 3,
642+
"WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM": 1,
643+
},
612644
},
613645
}
614646

615647
for _, tc := range testCases {
616648
t.Run(tc.name, func(t *testing.T) {
617-
os.Unsetenv(tc.envKey)
618-
if tc.setEnv {
619-
os.Setenv(tc.envKey, strconv.Itoa(tc.envValue))
649+
for key, val := range tc.envVarMap {
650+
os.Unsetenv(key)
651+
if tc.setEnv {
652+
os.Setenv(key, strconv.Itoa(val))
653+
}
620654
}
621-
workers := getMaxWorkers(tc.gvk, tc.defValue)
655+
workers := getMaxConcurrentReconciles(tc.gvk, tc.defValue)
622656
if tc.expectedValue != workers {
623657
t.Fatalf("Unexpected MaxWorkers: %v expected MaxWorkers: %v", workers, tc.expectedValue)
624658
}

website/content/en/docs/ansible/reference/advanced_options.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ If you have created resources without owner reference injection, it is
2929
possible to manually to update resources following [this
3030
guide.](../retroactively-owned-resources)
3131

32-
## Max Workers
32+
## Max Concurrent Reconciles
3333

34-
Increasing the number of workers allows events to be processed
34+
Increasing the number of concurrent reconciles allows events to be processed
3535
concurrently, which can improve reconciliation performance.
3636

37-
Worker maximums can be set in two ways. Operator **authors and admins**
38-
can set the max workers default by including extra args to the operator
39-
container in `deploy/operator.yaml`. (Otherwise, the default is 1 worker.)
37+
The maximum number of concurrent reconciles can be set in two ways. Operator **authors and admins**
38+
can set the max concurrent reconciles default by including extra args to the operator
39+
container in `deploy/operator.yaml`. (Otherwise, the default is the maximum number of logical CPUs available for the process obtained using `runtime.NumCPU()`.)
4040

4141
**NOTE:** Admins using OLM should use the environment variable instead
4242
of the extra args.
@@ -46,12 +46,14 @@ of the extra args.
4646
image: "quay.io/asmacdo/memcached-operator:v0.0.0"
4747
imagePullPolicy: "Always"
4848
args:
49-
- "--max-workers"
49+
- "--max-concurrent-reconciles"
5050
- "3"
5151
```
52+
**Note**
53+
Previously, `--max-workers` was used and is replaced by `--max-concurrent-reconciles`.
5254

5355
Operator **admins** can override the value by setting an environment
54-
variable in the format `WORKER_<kind>_<group>`. This variable must be
56+
variable in the format `MAX_CONCURRENT_RECONCILES_<kind>_<group>`. This variable must be
5557
all uppercase, and periods (e.g. in the group name) are replaced with underscores.
5658

5759
For the memcached operator example, the component parts are retrieved
@@ -68,7 +70,7 @@ metadata:
6870
```
6971

7072
From this data, we can see that the environment variable will be
71-
`WORKER_MEMCACHED_CACHE_EXAMPLE_COM`, which we can then add to
73+
`MAX_CONCURRENT_RECONCILES_MEMCACHED_CACHE_EXAMPLE_COM`, which we can then add to
7274
`deploy/operator.yaml`:
7375

7476
``` yaml
@@ -77,13 +79,15 @@ From this data, we can see that the environment variable will be
7779
imagePullPolicy: "Always"
7880
args:
7981
# This default is overridden.
80-
- "--max-workers"
82+
- "--max-reconciles"
8183
- "3"
8284
env:
8385
# This value is used
84-
- name: WORKER_MEMCACHED_CACHE_EXAMPLE_COM
86+
- name: MAX_CONCURRENT_RECONCILES_MEMCACHED_CACHE_EXAMPLE_COM
8587
value: "6"
8688
```
89+
**Note**
90+
Previously, the naming convention for global variable was `WORKERS_%s_%s`. It has been updated to `MAX_CONCURRENT_RECONCILES_%s_%s`. Currently, though we accept inputs to both the variables, `MAX_CONCURRENT_RECONCILES_%s_%s` takes precedence over the formerly used one.
8791

8892
## Ansible Verbosity
8993

0 commit comments

Comments
 (0)