Skip to content

Commit cd74819

Browse files
author
Shawn Hurley
committed
Adding ability to specify requeue period per GVK via watches file.
1 parent b97f09b commit cd74819

File tree

12 files changed

+279
-17
lines changed

12 files changed

+279
-17
lines changed

commands/operator-sdk/cmd/up/local.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"runtime"
2626
"strings"
2727
"syscall"
28+
"time"
2829

2930
"github.com/operator-framework/operator-sdk/commands/operator-sdk/cmd/cmdutil"
3031
ansibleOperator "github.com/operator-framework/operator-sdk/pkg/ansible/operator"
@@ -142,7 +143,7 @@ func upLocalAnsible() {
142143
}
143144

144145
// start the operator
145-
go ansibleOperator.Run(done, mgr, "./watches.yaml")
146+
go ansibleOperator.Run(done, mgr, "./watches.yaml", time.Minute)
146147

147148
// wait for either to finish
148149
err = <-done

pkg/ansible/operator/operator.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
// Run - A blocking function which starts a controller-runtime manager
3030
// It starts an Operator by reading in the values in `./watches.yaml`, adds a controller
3131
// to the manager, and finally running the manager.
32-
func Run(done chan error, mgr manager.Manager, watchesPath string) {
32+
func Run(done chan error, mgr manager.Manager, watchesPath string, reconcilePeriod time.Duration) {
3333
watches, err := runner.NewFromWatches(watchesPath)
3434
if err != nil {
3535
logrus.Error("Failed to get watches")
@@ -40,10 +40,16 @@ func Run(done chan error, mgr manager.Manager, watchesPath string) {
4040
c := signals.SetupSignalHandler()
4141

4242
for gvk, runner := range watches {
43-
controller.Add(mgr, controller.Options{
44-
GVK: gvk,
45-
Runner: runner,
46-
})
43+
o := controller.Options{
44+
GVK: gvk,
45+
Runner: runner,
46+
ReconcilePeriod: reconcilePeriod,
47+
}
48+
d, ok := runner.GetReconcilePeriod()
49+
if ok {
50+
o.ReconcilePeriod = d
51+
}
52+
controller.Add(mgr, o)
4753
}
4854
done <- mgr.Start(c)
4955
}

pkg/ansible/runner/runner.go

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"path/filepath"
2626
"strconv"
2727
"strings"
28+
"time"
2829

2930
"github.com/operator-framework/operator-sdk/pkg/ansible/paramconv"
3031
"github.com/operator-framework/operator-sdk/pkg/ansible/runner/eventapi"
@@ -40,17 +41,19 @@ import (
4041
type Runner interface {
4142
Run(*unstructured.Unstructured, string) (chan eventapi.JobEvent, error)
4243
GetFinalizer() (string, bool)
44+
GetReconcilePeriod() (time.Duration, bool)
4345
}
4446

4547
// watch holds data used to create a mapping of GVK to ansible playbook or role.
4648
// The mapping is used to compose an ansible operator.
4749
type watch struct {
48-
Version string `yaml:"version"`
49-
Group string `yaml:"group"`
50-
Kind string `yaml:"kind"`
51-
Playbook string `yaml:"playbook"`
52-
Role string `yaml:"role"`
53-
Finalizer *Finalizer `yaml:"finalizer"`
50+
Version string `yaml:"version"`
51+
Group string `yaml:"group"`
52+
Kind string `yaml:"kind"`
53+
Playbook string `yaml:"playbook"`
54+
Role string `yaml:"role"`
55+
ReconcilePeriod string `yaml:"reconcilePeriod"`
56+
Finalizer *Finalizer `yaml:"finalizer"`
5457
}
5558

5659
// Finalizer - Expose finalizer to be used by a user.
@@ -82,19 +85,28 @@ func NewFromWatches(path string) (map[schema.GroupVersionKind]Runner, error) {
8285
Version: w.Version,
8386
Kind: w.Kind,
8487
}
88+
var reconcilePeriod time.Duration
89+
if w.ReconcilePeriod != "" {
90+
d, err := time.ParseDuration(w.ReconcilePeriod)
91+
if err != nil {
92+
logrus.Warningf("unable to parse duration: %v - %v, setting to default", w.ReconcilePeriod, err)
93+
}
94+
reconcilePeriod = d
95+
}
96+
8597
// Check if schema is a duplicate
8698
if _, ok := m[s]; ok {
8799
return nil, fmt.Errorf("duplicate GVK: %v", s.String())
88100
}
89101
switch {
90102
case w.Playbook != "":
91-
r, err := NewForPlaybook(w.Playbook, s, w.Finalizer)
103+
r, err := NewForPlaybook(w.Playbook, s, w.Finalizer, reconcilePeriod)
92104
if err != nil {
93105
return nil, err
94106
}
95107
m[s] = r
96108
case w.Role != "":
97-
r, err := NewForRole(w.Role, s, w.Finalizer)
109+
r, err := NewForRole(w.Role, s, w.Finalizer, reconcilePeriod)
98110
if err != nil {
99111
return nil, err
100112
}
@@ -107,7 +119,7 @@ func NewFromWatches(path string) (map[schema.GroupVersionKind]Runner, error) {
107119
}
108120

109121
// NewForPlaybook returns a new Runner based on the path to an ansible playbook.
110-
func NewForPlaybook(path string, gvk schema.GroupVersionKind, finalizer *Finalizer) (Runner, error) {
122+
func NewForPlaybook(path string, gvk schema.GroupVersionKind, finalizer *Finalizer, reconcilePeriod time.Duration) (Runner, error) {
111123
if !filepath.IsAbs(path) {
112124
return nil, fmt.Errorf("playbook path must be absolute for %v", gvk)
113125
}
@@ -117,6 +129,7 @@ func NewForPlaybook(path string, gvk schema.GroupVersionKind, finalizer *Finaliz
117129
cmdFunc: func(ident, inputDirPath string) *exec.Cmd {
118130
return exec.Command("ansible-runner", "-vv", "-p", path, "-i", ident, "run", inputDirPath)
119131
},
132+
reconcilePeriod: reconcilePeriod,
120133
}
121134
err := r.addFinalizer(finalizer)
122135
if err != nil {
@@ -126,7 +139,7 @@ func NewForPlaybook(path string, gvk schema.GroupVersionKind, finalizer *Finaliz
126139
}
127140

128141
// NewForRole returns a new Runner based on the path to an ansible role.
129-
func NewForRole(path string, gvk schema.GroupVersionKind, finalizer *Finalizer) (Runner, error) {
142+
func NewForRole(path string, gvk schema.GroupVersionKind, finalizer *Finalizer, reconcilePeriod time.Duration) (Runner, error) {
130143
if !filepath.IsAbs(path) {
131144
return nil, fmt.Errorf("role path must be absolute for %v", gvk)
132145
}
@@ -138,6 +151,7 @@ func NewForRole(path string, gvk schema.GroupVersionKind, finalizer *Finalizer)
138151
rolePath, roleName := filepath.Split(path)
139152
return exec.Command("ansible-runner", "-vv", "--role", roleName, "--roles-path", rolePath, "--hosts", "localhost", "-i", ident, "run", inputDirPath)
140153
},
154+
reconcilePeriod: reconcilePeriod,
141155
}
142156
err := r.addFinalizer(finalizer)
143157
if err != nil {
@@ -153,6 +167,7 @@ type runner struct {
153167
Finalizer *Finalizer
154168
cmdFunc func(ident, inputDirPath string) *exec.Cmd // returns a Cmd that runs ansible-runner
155169
finalizerCmdFunc func(ident, inputDirPath string) *exec.Cmd
170+
reconcilePeriod time.Duration
156171
}
157172

158173
func (r *runner) Run(u *unstructured.Unstructured, kubeconfig string) (chan eventapi.JobEvent, error) {
@@ -224,6 +239,14 @@ func (r *runner) Run(u *unstructured.Unstructured, kubeconfig string) (chan even
224239
return receiver.Events, nil
225240
}
226241

242+
// GetReconcilePeriod - new reconcile period.
243+
func (r *runner) GetReconcilePeriod() (time.Duration, bool) {
244+
if r.reconcilePeriod == time.Duration(0) {
245+
return r.reconcilePeriod, false
246+
}
247+
return r.reconcilePeriod, true
248+
}
249+
227250
func (r *runner) GetFinalizer() (string, bool) {
228251
if r.Finalizer != nil {
229252
return r.Finalizer.Name, true

pkg/ansible/runner/runner_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package runner
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
"time"
7+
8+
"k8s.io/apimachinery/pkg/runtime/schema"
9+
)
10+
11+
func TestNewFromWatches(t *testing.T) {
12+
testCases := []struct {
13+
name string
14+
path string
15+
expectedMap map[schema.GroupVersionKind]runner
16+
shouldError bool
17+
}{
18+
{
19+
name: "error duplicate GVK",
20+
path: "testdata/duplicate_gvk.yaml",
21+
shouldError: true,
22+
},
23+
{
24+
name: "error no file",
25+
path: "testdata/please_don't_create_me_gvk.yaml",
26+
shouldError: true,
27+
},
28+
{
29+
name: "error invalid yaml",
30+
path: "testdata/invalid.yaml",
31+
shouldError: true,
32+
},
33+
{
34+
name: "error invalid playbook path",
35+
path: "testdata/invalid_playbook_path.yaml",
36+
shouldError: true,
37+
},
38+
{
39+
name: "error invalid playbook finalizer path",
40+
path: "testdata/invalid_finalizer_playbook_path.yaml",
41+
shouldError: true,
42+
},
43+
{
44+
name: "error invalid role path",
45+
path: "testdata/invalid_role_path.yaml",
46+
shouldError: true,
47+
},
48+
{
49+
name: "error invalid role finalizer path",
50+
path: "testdata/invalid_finalizer_role_path.yaml",
51+
shouldError: true,
52+
},
53+
{
54+
name: "valid watches file",
55+
path: "testdata/valid.yaml",
56+
expectedMap: map[schema.GroupVersionKind]runner{
57+
schema.GroupVersionKind{
58+
Version: "v1alpha1",
59+
Group: "app.example.com",
60+
Kind: "NoFinalizer",
61+
}: runner{
62+
GVK: schema.GroupVersionKind{
63+
Version: "v1alpha1",
64+
Group: "app.example.com",
65+
Kind: "NoFinalizer",
66+
},
67+
Path: "/opt/ansible/playbook.yaml",
68+
reconcilePeriod: time.Second * 2,
69+
},
70+
schema.GroupVersionKind{
71+
Version: "v1alpha1",
72+
Group: "app.example.com",
73+
Kind: "Playbook",
74+
}: runner{
75+
GVK: schema.GroupVersionKind{
76+
Version: "v1alpha1",
77+
Group: "app.example.com",
78+
Kind: "Playbook",
79+
},
80+
Path: "/opt/ansible/playbook.yaml",
81+
Finalizer: &Finalizer{
82+
Name: "finalizer.app.example.com",
83+
Role: "/opt/ansible/role",
84+
Vars: map[string]interface{}{"sentinel": "finalizer_running"},
85+
},
86+
},
87+
schema.GroupVersionKind{
88+
Version: "v1alpha1",
89+
Group: "app.example.com",
90+
Kind: "Role",
91+
}: runner{
92+
GVK: schema.GroupVersionKind{
93+
Version: "v1alpha1",
94+
Group: "app.example.com",
95+
Kind: "Role",
96+
},
97+
Path: "/opt/ansible/role",
98+
Finalizer: &Finalizer{
99+
Name: "finalizer.app.example.com",
100+
Playbook: "/opt/ansible/playbook.yaml",
101+
Vars: map[string]interface{}{"sentinel": "finalizer_running"},
102+
},
103+
},
104+
},
105+
},
106+
}
107+
108+
for _, tc := range testCases {
109+
t.Run(tc.name, func(t *testing.T) {
110+
m, err := NewFromWatches(tc.path)
111+
if err != nil && !tc.shouldError {
112+
t.Fatalf("err: %v occurred unexpectedly", err)
113+
}
114+
if err != nil && tc.shouldError {
115+
return
116+
}
117+
for k, expectedR := range tc.expectedMap {
118+
r, ok := m[k]
119+
if !ok {
120+
t.Fatalf("did not find expected GVK: %v", k)
121+
}
122+
run, ok := r.(*runner)
123+
if !ok {
124+
t.Fatalf("here: %#v", r)
125+
}
126+
if run.Path != expectedR.Path {
127+
t.Fatalf("GVK: %v unexpected path: %v expected path: %v", k, run.Path, expectedR.Path)
128+
}
129+
if run.GVK != expectedR.GVK {
130+
t.Fatalf("GVK: %v\nunexpected GVK: %#v\nexpected GVK: %#v", k, run.GVK, expectedR.GVK)
131+
}
132+
if run.Finalizer != expectedR.Finalizer {
133+
if run.Finalizer.Name != expectedR.Finalizer.Name || run.Finalizer.Playbook != expectedR.Finalizer.Playbook || run.Finalizer.Role != expectedR.Finalizer.Role || reflect.DeepEqual(run.Finalizer.Vars["sentinel"], expectedR.Finalizer.Vars["sentininel"]) {
134+
t.Fatalf("GVK: %v\nunexpected finalizer: %#v\nexpected finalizer: %#v", k, run.Finalizer, expectedR.Finalizer)
135+
}
136+
}
137+
if run.reconcilePeriod != expectedR.reconcilePeriod {
138+
t.Fatalf("GVK: %v unexpected reconcile period: %v expected reconcile period: %v", k, run.reconcilePeriod, expectedR.reconcilePeriod)
139+
}
140+
}
141+
})
142+
}
143+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
- version: v1alpha1
3+
group: app.example.com
4+
kind: Database
5+
playbook: /opt/ansible/playbook.yaml
6+
finalizer:
7+
name: finalizer.app.example.com
8+
vars:
9+
sentinel: finalizer_running
10+
- version: v1alpha1
11+
group: app.example.com
12+
kind: Database
13+
playbook: /opt/ansible/playbook.yaml
14+
finalizer:
15+
name: finalizer.app.example.com
16+
vars:
17+
sentinel: finalizer_running
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
version: v1alpha1
3+
group: app.example.com
4+
kind: Database
5+
playbook: /opt/ansible/playbook.yaml
6+
finalizer:
7+
name: finalizer.app.example.com
8+
vars:
9+
sentinel: finalizer_running
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
- version: v1alpha1
3+
group: app.example.com
4+
kind: Database
5+
playbook: /opt/ansible/playbook.yaml
6+
finalizer:
7+
name: finalizer.app.example.com
8+
playbook: opt/ansible/playbook.yaml
9+
vars:
10+
sentinel: finalizer_running
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
- version: v1alpha1
3+
group: app.example.com
4+
kind: Database
5+
playbook: /ansible/playbook.yaml
6+
finalizer:
7+
name: finalizer.app.example.com
8+
role: ansible/role
9+
vars:
10+
sentinel: finalizer_running
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
- version: v1alpha1
3+
group: app.example.com
4+
kind: Database
5+
playbook: opt/ansible/playbook.yaml
6+
finalizer:
7+
name: finalizer.app.example.com
8+
vars:
9+
sentinel: finalizer_running
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
- version: v1alpha1
3+
group: app.example.com
4+
kind: Database
5+
role: opt/ansible/playbook.yaml
6+
finalizer:
7+
name: finalizer.app.example.com
8+
vars:
9+
sentinel: finalizer_running

0 commit comments

Comments
 (0)