Skip to content

Commit 6df4f01

Browse files
committed
Move operator package under patterns/
1 parent b8854f6 commit 6df4f01

File tree

5 files changed

+94
-116
lines changed

5 files changed

+94
-116
lines changed

pkg/operator/doc.go renamed to pkg/patterns/operator/doc.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ limitations under the License.
2222
// An Operator is a Controller that implements the operational logic for an Application. It is often used
2323
// to take off-the-shelf OSS applications, and make them Kubernetes native.
2424
//
25-
// // Create an Operator
26-
// op, err := operator.New(config.GetConfigOrDie())
25+
// op, err := operator.New(&appsv1.ReplicaSet{}, &corev1.Pod{})
2726
// if err != nil {
2827
// log.Fatal(err)
2928
// }
3029
//
31-
//
3230
// // Implement the Operator logic for a single type - e.g. ReplicaSet
3331
// rsRecFn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
3432
// // Read the ReplicaSet
@@ -43,9 +41,5 @@ limitations under the License.
4341
// return reconcile.Result{}, nil
4442
// })
4543
//
46-
// // Handle events (e.g. create, update, delete operations) for a type and owned types - e.g. ReplicaSets and Pods
47-
// err = op.HandleType(rsRecFn, &appsv1.ReplicaSet{}, &corev1.Pod{})
48-
// if err != nil {
49-
// log.Fatal(err)
50-
// }
44+
// log.Fatal(op.Start(rsRecFn))
5145
package operator

pkg/operator/example_test.go renamed to pkg/patterns/operator/example_test.go

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ package operator_test
1818

1919
import (
2020
"context"
21-
"log"
22-
2321
"fmt"
22+
"log"
2423

2524
"github.com/kubernetes-sigs/controller-runtime/pkg/client"
26-
"github.com/kubernetes-sigs/controller-runtime/pkg/client/config"
27-
"github.com/kubernetes-sigs/controller-runtime/pkg/operator"
25+
"github.com/kubernetes-sigs/controller-runtime/pkg/patterns/operator"
2826
"github.com/kubernetes-sigs/controller-runtime/pkg/reconcile"
2927
appsv1 "k8s.io/api/apps/v1"
3028
corev1 "k8s.io/api/core/v1"
@@ -33,24 +31,21 @@ import (
3331

3432
// This example creates a simple Operator that is configured for Deployments and ReplicaSets.
3533
//
36-
// * Create a new Operator
34+
// * Create a new Operator for ReplicaSets. Watch Pods owned by ReplicaSets and Reconcile the ReplicaSet.
3735
//
38-
// * Setup ReplicaSet handling
39-
//
40-
// * Setup Deployment handling
41-
//
42-
// * Start the Operator
36+
// * Start the Operator with the Reconcile function.
4337
func ExampleOperator() {
44-
// Create a new Operator
45-
op, err := operator.New(config.GetConfigOrDie())
38+
// Create a new Operator for ReplicaSet
39+
40+
op, err := operator.New(&appsv1.ReplicaSet{}, &corev1.Pod{})
4641
if err != nil {
4742
log.Fatal(err)
4843
}
4944

45+
// Implement the business logic:
5046
// This function will be called when there is a change to a ReplicaSet or a Pod with an OwnerReference
51-
// to a ReplicaSet. It will contain the Namespace / Name of the ReplicaSet.
47+
// to a ReplicaSet.
5248
//
53-
// Reconcile performs the following actions
5449
// * Read the ReplicaSet
5550
// * Read the Pods
5651
// * Set a Label on the ReplicaSet with the Pod count
@@ -80,35 +75,10 @@ func ExampleOperator() {
8075
return reconcile.Result{}, err
8176
}
8277

78+
// Done
8379
return reconcile.Result{}, nil
8480
})
8581

86-
// Watch ReplicaSets and Pods owned by ReplicaSets and call recFn with the Namespace / Name of the ReplicaSet
87-
err = op.HandleType(rsRecFn, &appsv1.ReplicaSet{}, &corev1.Pod{})
88-
if err != nil {
89-
log.Fatal(err)
90-
}
91-
92-
// This function will be called when there is a change to a Deployment or a ReplicaSet with an OwnerReference
93-
// to a Deployment. It will contain the Namespace / Name of the Deployment.
94-
depRecFn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
95-
dep := &appsv1.Deployment{}
96-
err := op.GetClient().Get(context.TODO(), req.NamespacedName, dep)
97-
if err != nil {
98-
return reconcile.Result{}, err
99-
}
100-
101-
log.Printf("TODO: Implement Deployment Reconcile here.")
102-
return reconcile.Result{}, nil
103-
})
104-
105-
// Watch Deployments and ReplicaSets owned by Deployments and call recFn with the Namespace / Name of the
106-
// Deployment
107-
err = op.HandleType(depRecFn, &appsv1.Deployment{}, &appsv1.ReplicaSet{})
108-
if err != nil {
109-
log.Fatal(err)
110-
}
111-
11282
// Start the Operator
113-
log.Fatal(op.Start())
83+
log.Fatal(op.Start(rsRecFn))
11484
}

pkg/operator/operator.go renamed to pkg/patterns/operator/operator.go

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,93 +22,97 @@ import (
2222

2323
"github.com/kubernetes-sigs/controller-runtime/pkg/client"
2424
"github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
25+
"github.com/kubernetes-sigs/controller-runtime/pkg/client/config"
2526
"github.com/kubernetes-sigs/controller-runtime/pkg/controller"
2627
"github.com/kubernetes-sigs/controller-runtime/pkg/handler"
28+
internalcontroller "github.com/kubernetes-sigs/controller-runtime/pkg/internal/controller"
2729
"github.com/kubernetes-sigs/controller-runtime/pkg/manager"
2830
"github.com/kubernetes-sigs/controller-runtime/pkg/reconcile"
2931
"github.com/kubernetes-sigs/controller-runtime/pkg/runtime/signals"
3032
"github.com/kubernetes-sigs/controller-runtime/pkg/source"
3133
"k8s.io/apimachinery/pkg/runtime"
32-
"k8s.io/apimachinery/pkg/runtime/schema"
33-
"k8s.io/client-go/rest"
3434
)
3535

3636
// Operator is a very simple Controller that embeds a Manager.
3737
type Operator struct {
3838
mrg manager.Manager
3939
stopFn func() <-chan struct{}
40-
41-
// Allow deps to be mocked out for testing
42-
newController func(name string, mrg manager.Manager, options controller.Options) (controller.Controller, error)
43-
getGvk func(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error)
40+
ctrl *internalcontroller.Controller
4441
}
4542

46-
// New returns a new Operator
47-
func New(config *rest.Config) (*Operator, error) {
48-
mrg, err := manager.New(config, manager.Options{})
49-
if err != nil {
50-
return nil, err
51-
}
43+
var noop = reconcile.Func(func(reconcile.Request) (reconcile.Result, error) {
44+
return reconcile.Result{}, nil
45+
})
5246

53-
o := &Operator{
54-
mrg: mrg,
55-
stopFn: signals.SetupSignalHandler,
56-
newController: controller.New,
57-
getGvk: apiutil.GVKForObject,
58-
}
59-
return o, nil
60-
}
47+
var getConfig = config.GetConfig
48+
var newController = controller.New
49+
var newManager = manager.New
50+
var getGvk = apiutil.GVKForObject
6151

62-
// HandleType configures the Operator for a Kubernetes Resource type.
52+
// New returns a new Operator for a Kubernetes Resource type.
6353
//
6454
// * Watch for changes (e.g. create,update,delete operations) to objects of the Resource type
6555
//
6656
// * Watch for changes to objects created or managed by the Resource type.
6757
//
6858
// * Reconcile the object that was changed or that owns the changed objects.
6959
//
70-
// impl: the Reconcile implementation. reconcile.Reconcile is a function that may be called at anytime with the
71-
// name / Namespace of an object. When called, it will ensure that the state of the system matches what is
72-
// specified in the object at the time reconcile is called.
73-
//
7460
// reconcileObject: the type of the Kubernetes object that will be Watched and Reconciled - e.g. &v1.ReplicaSet{}
7561
//
7662
// ownedTypes: list of types of objects that may be created or managed by the reconcileObject. These
7763
// objects must have the OwnersReference set to the owning object in the ObjectMeta - e.g. &v1.Pod{}
78-
func (o *Operator) HandleType(impl reconcile.Reconcile,
79-
reconcileObject runtime.Object, ownedTypes ...runtime.Object) error {
64+
func New(reconcileObject runtime.Object, ownedTypes ...runtime.Object) (*Operator, error) {
65+
c, err := getConfig()
66+
if err != nil {
67+
return nil, err
68+
}
69+
mrg, err := newManager(c, manager.Options{})
70+
if err != nil {
71+
return nil, err
72+
}
8073

81-
gvk, err := o.getGvk(reconcileObject, o.mrg.GetScheme())
74+
o := &Operator{
75+
mrg: mrg,
76+
stopFn: signals.SetupSignalHandler,
77+
}
78+
79+
gvk, err := getGvk(reconcileObject, o.mrg.GetScheme())
8280
if err != nil {
83-
return err
81+
return nil, err
8482
}
8583

8684
// Create the controller
8785
name := fmt.Sprintf("%s-operator", strings.ToLower(gvk.Kind))
88-
c, err := o.newController(name, o.mrg, controller.Options{Reconcile: impl})
86+
ctrl, err := newController(name, mrg, controller.Options{Reconcile: noop})
8987
if err != nil {
90-
return err
88+
return nil, err
9189
}
90+
o.ctrl = ctrl.(*internalcontroller.Controller)
9291

9392
// Reconcile type
94-
err = c.Watch(&source.Kind{Type: reconcileObject}, &handler.Enqueue{})
93+
err = o.ctrl.Watch(&source.Kind{Type: reconcileObject}, &handler.Enqueue{})
9594
if err != nil {
96-
return err
95+
return nil, err
9796
}
9897

9998
// Watch the managed types
10099
for _, t := range ownedTypes {
101-
err = c.Watch(&source.Kind{Type: t}, &handler.EnqueueOwner{OwnerType: reconcileObject, IsController: true})
100+
err = o.ctrl.Watch(&source.Kind{Type: t}, &handler.EnqueueOwner{OwnerType: reconcileObject, IsController: true})
102101
if err != nil {
103-
return err
102+
return nil, err
104103
}
105104
}
106105

107-
return err
106+
return o, nil
108107
}
109108

110109
// Start starts the Operator and blocks until the program is shutdown.
111-
func (o *Operator) Start() error {
110+
//
111+
// call: the Reconcile implementation. reconcile.Reconcile is a function that may be called at anytime with the
112+
// name / Namespace of an object. When called, it will ensure that the state of the system matches what is
113+
// specified in the object at the time reconcile is called.
114+
func (o *Operator) Start(call reconcile.Reconcile) error {
115+
o.ctrl.Do = call
112116
return o.mrg.Start(o.stopFn())
113117
}
114118

pkg/operator/operator_test.go renamed to pkg/patterns/operator/operator_test.go

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"context"
77

8+
"github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
89
"github.com/kubernetes-sigs/controller-runtime/pkg/controller"
910
"github.com/kubernetes-sigs/controller-runtime/pkg/manager"
1011
"github.com/kubernetes-sigs/controller-runtime/pkg/reconcile"
@@ -16,80 +17,90 @@ import (
1617
"k8s.io/apimachinery/pkg/runtime"
1718
"k8s.io/apimachinery/pkg/runtime/schema"
1819
"k8s.io/apimachinery/pkg/types"
20+
"k8s.io/client-go/rest"
1921
)
2022

2123
var _ = Describe("Operator", func() {
2224
var stop chan struct{}
23-
var r reconcile.Func
2425

2526
BeforeEach(func() {
2627
stop = make(chan struct{})
27-
r = reconcile.Func(func(reconcile.Request) (reconcile.Result, error) { return reconcile.Result{}, nil })
28+
getConfig = func() (*rest.Config, error) { return cfg, nil }
29+
newController = controller.New
30+
newManager = manager.New
31+
getGvk = apiutil.GVKForObject
2832
})
2933

3034
AfterEach(func() {
3135
close(stop)
3236
})
3337

34-
Describe("New", func() {
35-
It("should fail if the Config is invalid", func() {
36-
instance, err := New(nil)
37-
Expect(err).To(HaveOccurred())
38-
Expect(instance).To(BeNil())
38+
Describe("noop", func() {
39+
It("should return run", func() {
40+
_, err := noop.Reconcile(reconcile.Request{})
41+
Expect(err).NotTo(HaveOccurred())
3942
})
4043
})
4144

42-
Describe("HandleType", func() {
45+
Describe("New", func() {
4346
It("should return success if given valid objects", func() {
44-
instance, err := New(cfg)
47+
instance, err := New(&appsv1.ReplicaSet{}, &appsv1.ReplicaSet{})
4548
Expect(err).NotTo(HaveOccurred())
49+
Expect(instance).NotTo(BeNil())
50+
})
4651

47-
err = instance.HandleType(r, &appsv1.ReplicaSet{}, &appsv1.ReplicaSet{})
48-
Expect(err).NotTo(HaveOccurred())
52+
It("should return an error if the Config is invalid", func() {
53+
getConfig = func() (*rest.Config, error) { return cfg, fmt.Errorf("expected error") }
54+
instance, err := New(&appsv1.ReplicaSet{}, &appsv1.ReplicaSet{})
55+
Expect(err).To(HaveOccurred())
56+
Expect(instance).To(BeNil())
4957
})
5058

5159
It("should return an error if there is no GVK for an object", func() {
52-
instance, err := New(cfg)
53-
Expect(err).NotTo(HaveOccurred())
60+
instance, err := New(fakeType(""), &appsv1.ReplicaSet{})
61+
Expect(err).To(HaveOccurred())
62+
Expect(instance).To(BeNil())
5463

55-
err = instance.HandleType(r, fakeType(""), &appsv1.ReplicaSet{})
64+
instance, err = New(&appsv1.ReplicaSet{}, fakeType(""))
5665
Expect(err).To(HaveOccurred())
66+
Expect(instance).To(BeNil())
67+
})
5768

58-
err = instance.HandleType(r, &appsv1.ReplicaSet{}, fakeType(""))
69+
It("should return an error if it cannot create the manager", func() {
70+
newManager = func(config *rest.Config, options manager.Options) (manager.Manager, error) {
71+
return nil, fmt.Errorf("expected error")
72+
}
73+
instance, err := New(&appsv1.ReplicaSet{}, &appsv1.ReplicaSet{})
5974
Expect(err).To(HaveOccurred())
75+
Expect(instance).To(BeNil())
6076
})
6177

6278
It("should return an error if it cannot create the controller", func() {
63-
instance, err := New(cfg)
64-
Expect(err).NotTo(HaveOccurred())
65-
66-
instance.newController = func(name string, mrg manager.Manager, options controller.Options) (
79+
newController = func(name string, mrg manager.Manager, options controller.Options) (
6780
controller.Controller, error) {
6881

6982
return nil, fmt.Errorf("expected error")
7083
}
71-
err = instance.HandleType(r, &appsv1.ReplicaSet{}, &appsv1.ReplicaSet{})
84+
instance, err := New(&appsv1.ReplicaSet{}, &appsv1.ReplicaSet{})
7285
Expect(err).To(HaveOccurred())
86+
Expect(instance).To(BeNil())
7387
})
7488

7589
It("should return an error if it cannot call handle", func() {
76-
instance, err := New(cfg)
77-
Expect(err).NotTo(HaveOccurred())
78-
7990
// Prevent getGVK from failing so watch fails
80-
instance.getGvk = func(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) {
91+
getGvk = func(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) {
8192
return schema.GroupVersionKind{Kind: "foo"}, nil
8293
}
83-
err = instance.HandleType(r, fakeType(""), &appsv1.ReplicaSet{})
94+
instance, err := New(fakeType(""), &appsv1.ReplicaSet{})
8495
Expect(err).To(HaveOccurred())
96+
Expect(instance).To(BeNil())
8597
})
8698
})
8799

88-
Describe("Running", func() {
89-
100+
Describe("Start", func() {
90101
It("should Reconcile objects", func(done Done) {
91102
By("Creating the Operator")
92-
instance, err := New(cfg)
103+
instance, err := New(&appsv1.Deployment{}, &appsv1.ReplicaSet{})
93104
Expect(err).NotTo(HaveOccurred())
94105

95106
// set the stopFn so we shutdown at the end of the test instead of waiting for a signal
@@ -103,13 +114,12 @@ var _ = Describe("Operator", func() {
103114
ch <- r
104115
return reconcile.Result{}, nil
105116
})
106-
err = instance.HandleType(fn, &appsv1.Deployment{}, &appsv1.ReplicaSet{})
107117
Expect(err).NotTo(HaveOccurred())
108118

109119
By("Starting the Operator")
110120
go func() {
111121
defer GinkgoRecover()
112-
Expect(instance.Start()).NotTo(HaveOccurred())
122+
Expect(instance.Start(fn)).NotTo(HaveOccurred())
113123
By("Stopping the Operator")
114124
}()
115125

0 commit comments

Comments
 (0)