Skip to content

Commit a06b1dd

Browse files
committed
Add simple operator package
1 parent eb6b5de commit a06b1dd

File tree

6 files changed

+562
-0
lines changed

6 files changed

+562
-0
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package application
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
23+
"k8s.io/apimachinery/pkg/runtime"
24+
"k8s.io/client-go/rest"
25+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
26+
"sigs.k8s.io/controller-runtime/pkg/client/config"
27+
"sigs.k8s.io/controller-runtime/pkg/controller"
28+
"sigs.k8s.io/controller-runtime/pkg/handler"
29+
"sigs.k8s.io/controller-runtime/pkg/manager"
30+
"sigs.k8s.io/controller-runtime/pkg/predicate"
31+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
32+
"sigs.k8s.io/controller-runtime/pkg/source"
33+
)
34+
35+
// application is a simple Controller for a single API type. It will create a Manager for itself
36+
// if one is not provided.
37+
type application struct {
38+
mgr manager.Manager
39+
ctrl controller.Controller
40+
}
41+
42+
// Supporting mocking out functions for testing
43+
var getConfig = config.GetConfig
44+
var newController = controller.New
45+
var newManager = manager.New
46+
var getGvk = apiutil.GVKForObject
47+
48+
// Builder builds an Application Controller (e.g. Operator) and returns a manager.Manager to start it.
49+
type Builder struct {
50+
apiType runtime.Object
51+
mgr manager.Manager
52+
predicates []predicate.Predicate
53+
managedObjects []runtime.Object
54+
config *rest.Config
55+
reconcile reconcile.Reconciler
56+
ctrl controller.Controller
57+
}
58+
59+
// BuilderFor returns a new Builder for and BuilderFor
60+
func BuilderFor(apiType runtime.Object) *Builder {
61+
return &Builder{apiType: apiType}
62+
}
63+
64+
// Owns configures the Application Controller to respond to create / delete / update events for objects it managedObjects
65+
// - e.g. creates. apiType is an empty instance of an object matching the managed object type.
66+
func (b *Builder) Owns(apiType runtime.Object) *Builder {
67+
b.managedObjects = append(b.managedObjects, apiType)
68+
return b
69+
}
70+
71+
// WithConfig sets the Config to use for configuring clients. Defaults to the in-cluster config or to ~/.kube/config.
72+
func (b *Builder) WithConfig(config *rest.Config) *Builder {
73+
b.config = config
74+
return b
75+
}
76+
77+
// WithManager sets the Manager to use for registering the Controller. Defaults to a new manager.Manager.
78+
func (b *Builder) WithManager(m manager.Manager) *Builder {
79+
b.mgr = m
80+
return b
81+
}
82+
83+
// WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually
84+
// trigger reconciliations. For example, filtering on whether the resource version has changed.
85+
// Defaults to the empty list.
86+
func (b *Builder) WithEventFilter(p predicate.Predicate) *Builder {
87+
b.predicates = append(b.predicates, p)
88+
return b
89+
}
90+
91+
// WithReconciler sets the Reconcile called in response to create / update / delete events for the
92+
// BuilderFor type or Created object types.
93+
func (b *Builder) WithReconciler(r reconcile.Reconciler) *Builder {
94+
b.reconcile = r
95+
return b
96+
}
97+
98+
// Build builds the Application Controller and returns the Manager used to start it.
99+
func (b *Builder) Build() (manager.Manager, error) {
100+
if b.reconcile == nil {
101+
return nil, fmt.Errorf("must call WithReconciler to set Reconciler")
102+
}
103+
104+
// Set the Config
105+
if err := b.doConfig(); err != nil {
106+
return nil, err
107+
}
108+
109+
// Set the Manager
110+
if err := b.doManager(); err != nil {
111+
return nil, err
112+
}
113+
114+
// Set the Controller
115+
if err := b.doController(); err != nil {
116+
return nil, err
117+
}
118+
119+
a := &application{mgr: b.mgr, ctrl: b.ctrl}
120+
121+
// Reconcile type
122+
s := &source.Kind{Type: b.apiType}
123+
h := &handler.EnqueueRequestForObject{}
124+
err := a.ctrl.Watch(s, h, b.predicates...)
125+
if err != nil {
126+
return nil, err
127+
}
128+
129+
// Watch the managed types
130+
for _, t := range b.managedObjects {
131+
s := &source.Kind{Type: t}
132+
h := &handler.EnqueueRequestForOwner{
133+
OwnerType: b.apiType,
134+
IsController: true,
135+
}
136+
if err := a.ctrl.Watch(s, h, b.predicates...); err != nil {
137+
return nil, err
138+
}
139+
}
140+
141+
return a.mgr, nil
142+
}
143+
144+
func (b *Builder) doConfig() error {
145+
if b.config != nil {
146+
return nil
147+
}
148+
var err error
149+
b.config, err = getConfig()
150+
return err
151+
}
152+
153+
func (b *Builder) doManager() error {
154+
if b.mgr != nil {
155+
return nil
156+
}
157+
var err error
158+
b.mgr, err = newManager(b.config, manager.Options{})
159+
return err
160+
}
161+
162+
func (b *Builder) getControllerName() (string, error) {
163+
gvk, err := getGvk(b.apiType, b.mgr.GetScheme())
164+
if err != nil {
165+
return "", err
166+
}
167+
name := fmt.Sprintf("%s-application", strings.ToLower(gvk.Kind))
168+
return name, nil
169+
}
170+
171+
func (b *Builder) doController() error {
172+
name, err := b.getControllerName()
173+
if err != nil {
174+
return err
175+
}
176+
b.ctrl, err = newController(name, b.mgr, controller.Options{Reconciler: b.reconcile})
177+
return err
178+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package application
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo"
7+
. "github.com/onsi/gomega"
8+
"k8s.io/client-go/rest"
9+
"sigs.k8s.io/controller-runtime/pkg/envtest"
10+
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
11+
)
12+
13+
func TestSource(t *testing.T) {
14+
RegisterFailHandler(Fail)
15+
RunSpecsWithDefaultAndCustomReporters(t, "application Suite", []Reporter{envtest.NewlineReporter{}})
16+
}
17+
18+
var testenv *envtest.Environment
19+
var cfg *rest.Config
20+
21+
var _ = BeforeSuite(func(done Done) {
22+
logf.SetLogger(logf.ZapLogger(false))
23+
24+
testenv = &envtest.Environment{}
25+
26+
var err error
27+
cfg, err = testenv.Start()
28+
Expect(err).NotTo(HaveOccurred())
29+
30+
close(done)
31+
}, 60)
32+
33+
var _ = AfterSuite(func() {
34+
testenv.Stop()
35+
})

0 commit comments

Comments
 (0)