Skip to content

Commit 8631041

Browse files
djzagerjmrodri
authored andcommitted
ansible: pull watches into own package (#1523)
* ansible: pull watches into own package In the same way that Helm has a watches package responsible for loading the watches file, Ansible should be structured similiarly to separate concerns, primarily, loading and validating the watches and creating runners. * temporarily remove runner tests * Fix error msg formatting * Add unit tests for the ansible runner package - Move the runner struct next to the functions that accept it (back to where it was before) - Remove fields from runner that aren't needed anymore since we load the watches first, we can put manageStatus, reconcilePeriod, watchDependentResources, watchClusterScopedResources on the controller * Finalizer can specify vars with no role/playbook It is valid for a watch to have a finalizer with no role/playbook so long as it specifies vars to be run with the playbook/role specified on the watch. * Remove extraneous comments and constants
1 parent 34edd0a commit 8631041

18 files changed

+736
-504
lines changed

pkg/ansible/operator/operator.go

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,78 +29,80 @@ import (
2929
"github.com/operator-framework/operator-sdk/pkg/ansible/flags"
3030
"github.com/operator-framework/operator-sdk/pkg/ansible/proxy/controllermap"
3131
"github.com/operator-framework/operator-sdk/pkg/ansible/runner"
32+
"github.com/operator-framework/operator-sdk/pkg/ansible/watches"
3233

3334
"sigs.k8s.io/controller-runtime/pkg/manager"
3435
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
3536
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
3637
)
3738

39+
var log = logf.Log.WithName("manager")
40+
3841
// Run - A blocking function which starts a controller-runtime manager
3942
// It starts an Operator by reading in the values in `./watches.yaml`, adds a controller
4043
// to the manager, and finally running the manager.
4144
func Run(done chan error, mgr manager.Manager, f *flags.AnsibleOperatorFlags, cMap *controllermap.ControllerMap) {
42-
watches, err := runner.NewFromWatches(f.WatchesFile)
45+
watchesSlice, err := watches.Load(f.WatchesFile)
4346
if err != nil {
44-
logf.Log.WithName("manager").Error(err, "Failed to get watches")
47+
log.Error(err, "Failed to parse watches")
4548
done <- err
4649
return
4750
}
4851
rand.Seed(time.Now().Unix())
4952
c := signals.SetupSignalHandler()
5053

51-
for gvk, runner := range watches {
52-
53-
// if the WORKER_* environment variable is set, use that value.
54-
// Otherwise, use the value from the CLI. This is definitely
55-
// counter-intuitive but it allows the operator admin adjust the
56-
// number of workers based on their cluster resources. While the
57-
// author may use the CLI option to specify a suggested
58-
// configuration for the operator.
59-
maxWorkers := getMaxWorkers(gvk, f.MaxWorkers)
54+
for _, watch := range watchesSlice {
55+
runner, err := runner.New(watch)
56+
if err != nil {
57+
log.Error(err, "Failed to create runner")
58+
done <- err
59+
return
60+
}
6061

6162
o := controller.Options{
62-
GVK: gvk,
63-
Runner: runner,
64-
ManageStatus: runner.GetManageStatus(),
65-
MaxWorkers: maxWorkers,
66-
}
67-
applyFlagsToControllerOptions(f, &o)
68-
if d, ok := runner.GetReconcilePeriod(); ok {
69-
o.ReconcilePeriod = d
63+
GVK: watch.GroupVersionKind,
64+
Runner: runner,
65+
ManageStatus: watch.ManageStatus,
66+
MaxWorkers: getMaxWorkers(watch.GroupVersionKind, f.MaxWorkers),
67+
ReconcilePeriod: watch.ReconcilePeriod,
7068
}
69+
7170
ctr := controller.Add(mgr, o)
7271
if ctr == nil {
7372
done <- errors.New("failed to add controller")
7473
return
7574
}
7675
cMap.Store(o.GVK, &controllermap.Contents{Controller: *ctr,
77-
WatchDependentResources: runner.GetWatchDependentResources(),
78-
WatchClusterScopedResources: runner.GetWatchClusterScopedResources(),
76+
WatchDependentResources: watch.WatchDependentResources,
77+
WatchClusterScopedResources: watch.WatchClusterScopedResources,
7978
OwnerWatchMap: controllermap.NewWatchMap(),
8079
AnnotationWatchMap: controllermap.NewWatchMap(),
8180
})
8281
}
8382
done <- mgr.Start(c)
8483
}
8584

86-
func getMaxWorkers(gvk schema.GroupVersionKind, defvalue int) int {
87-
envvar := formatEnvVar(gvk.Kind, gvk.Group)
88-
maxWorkers, err := strconv.Atoi(os.Getenv(envvar))
89-
if err != nil {
90-
// we don't care why we couldn't parse it just use one.
91-
// maybe we should log that we are defaulting to 1.
92-
logf.Log.WithName("manager").V(0).Info(fmt.Sprintf("Using default value for workers %d", defvalue))
93-
return defvalue
85+
// if the WORKER_* environment variable is set, use that value.
86+
// Otherwise, use the value from the CLI. This is definitely
87+
// counter-intuitive but it allows the operator admin adjust the
88+
// number of workers based on their cluster resources. While the
89+
// author may use the CLI option to specify a suggested
90+
// configuration for the operator.
91+
func getMaxWorkers(gvk schema.GroupVersionKind, defValue int) int {
92+
envVar := strings.ToUpper(strings.Replace(
93+
fmt.Sprintf("WORKER_%s_%s", gvk.Kind, gvk.Group),
94+
".",
95+
"_",
96+
-1,
97+
))
98+
switch maxWorkers, err := strconv.Atoi(os.Getenv(envVar)); {
99+
case maxWorkers <= 1:
100+
return 1
101+
case err != nil:
102+
// we don't care why we couldn't parse it just use default
103+
log.Info("Failed to parse %v from environment. Using default %v", envVar, defValue)
104+
return defValue
105+
default:
106+
return maxWorkers
94107
}
95-
96-
return maxWorkers
97-
}
98-
99-
func formatEnvVar(kind string, group string) string {
100-
envvar := fmt.Sprintf("WORKER_%s_%s", kind, group)
101-
return strings.ToUpper(strings.Replace(envvar, ".", "_", -1))
102-
}
103-
104-
func applyFlagsToControllerOptions(f *flags.AnsibleOperatorFlags, o *controller.Options) {
105-
o.ReconcilePeriod = f.ReconcilePeriod
106108
}

pkg/ansible/operator/operator_test.go

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,47 +26,14 @@ import (
2626

2727
// TODO: add a test for the Run method
2828

29-
func TestFormatEnvVar(t *testing.T) {
30-
testCases := []struct {
31-
name string
32-
kind string
33-
group string
34-
expected string
35-
}{
36-
{
37-
name: "easy path",
38-
kind: "FooCluster",
39-
group: "cache.example.com",
40-
expected: "WORKER_FOOCLUSTER_CACHE_EXAMPLE_COM",
41-
},
42-
{
43-
name: "missing kind",
44-
kind: "",
45-
group: "cache.example.com",
46-
expected: "WORKER__CACHE_EXAMPLE_COM",
47-
},
48-
{
49-
name: "missing group",
50-
kind: "FooCluster",
51-
group: "",
52-
expected: "WORKER_FOOCLUSTER_",
53-
},
54-
}
55-
56-
for _, tc := range testCases {
57-
t.Run(tc.name, func(t *testing.T) {
58-
assert.Equal(t, tc.expected, formatEnvVar(tc.kind, tc.group))
59-
})
60-
}
61-
}
62-
6329
func TestMaxWorkers(t *testing.T) {
6430
testCases := []struct {
6531
name string
6632
gvk schema.GroupVersionKind
6733
defvalue int
6834
expected int
6935
setenvvar bool
36+
envvar string
7037
}{
7138
{
7239
name: "no env, use default value",
@@ -78,6 +45,7 @@ func TestMaxWorkers(t *testing.T) {
7845
defvalue: 1,
7946
expected: 1,
8047
setenvvar: false,
48+
envvar: "WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM",
8149
},
8250
{
8351
name: "env set to 3, expect 3",
@@ -89,14 +57,15 @@ func TestMaxWorkers(t *testing.T) {
8957
defvalue: 1,
9058
expected: 3,
9159
setenvvar: true,
60+
envvar: "WORKER_MEMCACHESERVICE_CACHE_EXAMPLE_COM",
9261
},
9362
}
9463

9564
for _, tc := range testCases {
9665
t.Run(tc.name, func(t *testing.T) {
97-
os.Unsetenv(formatEnvVar(tc.gvk.Kind, tc.gvk.Group))
66+
os.Unsetenv(tc.envvar)
9867
if tc.setenvvar {
99-
os.Setenv(formatEnvVar(tc.gvk.Kind, tc.gvk.Group), strconv.Itoa(tc.expected))
68+
os.Setenv(tc.envvar, strconv.Itoa(tc.expected))
10069
}
10170
assert.Equal(t, tc.expected, getMaxWorkers(tc.gvk, tc.defvalue))
10271
})

0 commit comments

Comments
 (0)