Skip to content

Commit 4855d31

Browse files
committed
PR review round 1
1 parent fea475a commit 4855d31

File tree

3 files changed

+145
-14
lines changed

3 files changed

+145
-14
lines changed

pkg/controller/controllerutil/controllerutil.go

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -114,43 +114,62 @@ const ( // They should complete the sentence "Deployment default/foo has been ..
114114
OperationUpdated = "updated"
115115
)
116116

117-
// CreateOrUpdate creates or updates a kuberenes resource. It takes in a key and
118-
// a placeholder for the existing object and returns the modified object
119-
func CreateOrUpdate(ctx context.Context, c client.Client, key client.ObjectKey, existing runtime.Object, t TransformFn) (runtime.Object, OperationType, error) {
117+
// CreateOrUpdate creates or updates a kubernetes resource. It takes in a key and
118+
// a placeholder for the existing object and returns the operation executed
119+
func CreateOrUpdate(ctx context.Context, c client.Client, key client.ObjectKey, existing runtime.Object, t TransformFn) (OperationType, error) {
120120
err := c.Get(ctx, key, existing)
121121
var obj runtime.Object
122122

123123
if errors.IsNotFound(err) {
124-
obj, err = t(existing)
124+
// Create a new zero value object so that the in parameter of
125+
// TransformFn is always a "clean" object, with only Name and Namespace
126+
// set
127+
zero := reflect.New(reflect.TypeOf(existing).Elem()).Interface()
128+
129+
// Set Namespace and Name from the lookup key
130+
zmeta, ok := zero.(v1.Object)
131+
if !ok {
132+
return OperationNoop, fmt.Errorf("is not a %T a metav1.Object, cannot call CreateOrUpdate", zero)
133+
}
134+
zmeta.SetNamespace(key.Namespace)
135+
zmeta.SetName(key.Name)
136+
137+
// Apply the TransformFn
138+
obj, err = t(zero.(runtime.Object))
125139
if err != nil {
126-
return nil, OperationNoop, err
140+
return OperationNoop, err
127141
}
128142

143+
// Create the new object
129144
err = c.Create(ctx, obj)
130-
131145
if err != nil {
132-
return nil, OperationNoop, err
146+
return OperationNoop, err
133147
}
134-
return obj, OperationCreated, err
148+
149+
return OperationCreated, err
135150
} else if err != nil {
136-
return nil, OperationNoop, err
151+
return OperationNoop, err
137152
} else {
138153
obj, err = t(existing.DeepCopyObject())
139154
if err != nil {
140-
return nil, OperationNoop, err
155+
return OperationNoop, err
141156
}
142157

143158
if !reflect.DeepEqual(existing, obj) {
144159
err = c.Update(ctx, obj)
145160
if err != nil {
146-
return nil, OperationNoop, err
161+
return OperationNoop, err
147162
}
148-
return obj, OperationUpdated, err
163+
164+
return OperationUpdated, err
149165
}
150-
return obj, OperationNoop, nil
166+
167+
return OperationNoop, nil
151168
}
152169
}
153170

154171
// TransformFn is a function which take in a kubernetes object and returns the
155-
// desired state of that object
172+
// desired state of that object.
173+
// It is safe to mutate the object inside this function, since it's always
174+
// called with an object's deep copy.
156175
type TransformFn func(in runtime.Object) (runtime.Object, error)

pkg/controller/controllerutil/controllerutil_suite_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,33 @@ import (
55

66
. "github.com/onsi/ginkgo"
77
. "github.com/onsi/gomega"
8+
9+
"k8s.io/client-go/rest"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
"sigs.k8s.io/controller-runtime/pkg/envtest"
812
)
913

1014
func TestControllerutil(t *testing.T) {
1115
RegisterFailHandler(Fail)
1216
RunSpecs(t, "Controllerutil Suite")
1317
}
18+
19+
var t *envtest.Environment
20+
var cfg *rest.Config
21+
var c client.Client
22+
23+
var _ = BeforeSuite(func() {
24+
var err error
25+
26+
t = &envtest.Environment{}
27+
28+
cfg, err = t.Start()
29+
Expect(err).NotTo(HaveOccurred())
30+
31+
c, err = client.New(cfg, client.Options{})
32+
Expect(err).NotTo(HaveOccurred())
33+
})
34+
35+
var _ = AfterSuite(func() {
36+
t.Stop()
37+
})

pkg/controller/controllerutil/controllerutil_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package controllerutil_test
22

33
import (
4+
"context"
5+
46
. "github.com/onsi/ginkgo"
57
. "github.com/onsi/gomega"
68
appsv1 "k8s.io/api/apps/v1"
9+
corev1 "k8s.io/api/core/v1"
710
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
811
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
912
"k8s.io/apimachinery/pkg/runtime"
13+
"k8s.io/apimachinery/pkg/types"
1014
"k8s.io/client-go/kubernetes/scheme"
1115
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1216
)
@@ -92,10 +96,94 @@ var _ = Describe("Controllerutil", func() {
9296
}))
9397
})
9498
})
99+
100+
Describe("CreateOrUpdate", func() {
101+
102+
It("creates a new object if one doesn't exists", func() {
103+
deplKey := types.NamespacedName{Name: "test-create", Namespace: "default"}
104+
depl := &appsv1.Deployment{}
105+
106+
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deplKey, depl, createDeployment)
107+
108+
By("returning OperationCreated")
109+
Expect(op).Should(BeEquivalentTo(controllerutil.OperationCreated))
110+
111+
By("returning returning no error")
112+
Expect(err).ShouldNot(HaveOccurred())
113+
114+
By("actually having the deployment created")
115+
fetched := &appsv1.Deployment{}
116+
Expect(c.Get(context.TODO(), deplKey, fetched)).Should(Succeed())
117+
})
118+
119+
It("update existing object", func() {
120+
deplKey := types.NamespacedName{Name: "test-update", Namespace: "default"}
121+
d, _ := createDeployment(&appsv1.Deployment{})
122+
depl := d.(*appsv1.Deployment)
123+
depl.Name = "test-update"
124+
depl.Namespace = "default"
125+
126+
var scale int32 = 2
127+
128+
Expect(c.Create(context.TODO(), depl)).Should(Succeed())
129+
130+
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deplKey, &appsv1.Deployment{}, deploymentScaler(scale))
131+
132+
By("returning OperationUpdated")
133+
Expect(op).Should(BeEquivalentTo(controllerutil.OperationUpdated))
134+
135+
By("returning returning no error")
136+
Expect(err).ShouldNot(HaveOccurred())
137+
138+
By("actually having the deployment scaled")
139+
fetched := &appsv1.Deployment{}
140+
Expect(c.Get(context.TODO(), deplKey, fetched)).Should(Succeed())
141+
Expect(*fetched.Spec.Replicas).To(Equal(scale))
142+
})
143+
144+
It("updates only changed objects", func() {
145+
deplKey := types.NamespacedName{Name: "test-idempotency", Namespace: "default"}
146+
depl := &appsv1.Deployment{}
147+
148+
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deplKey, depl, createDeployment)
149+
Expect(op).Should(BeEquivalentTo(controllerutil.OperationCreated))
150+
Expect(err).ShouldNot(HaveOccurred())
151+
152+
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deplKey, depl, deploymentIdentity)
153+
154+
By("returning OperationNoop")
155+
Expect(op).Should(BeEquivalentTo(controllerutil.OperationNoop))
156+
157+
By("returning returning no error")
158+
Expect(err).ShouldNot(HaveOccurred())
159+
})
160+
})
95161
})
96162

97163
var _ metav1.Object = &errMetaObj{}
98164

99165
type errMetaObj struct {
100166
metav1.ObjectMeta
101167
}
168+
169+
var createDeployment controllerutil.TransformFn = func(in runtime.Object) (runtime.Object, error) {
170+
out := in.(*appsv1.Deployment)
171+
out.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}
172+
out.Spec.Template.ObjectMeta.Labels = map[string]string{"foo": "bar"}
173+
out.Spec.Template.Spec.Containers = []corev1.Container{corev1.Container{Name: "foo", Image: "busybox"}}
174+
return out, nil
175+
}
176+
177+
var deploymentIdentity controllerutil.TransformFn = func(in runtime.Object) (runtime.Object, error) {
178+
return in, nil
179+
}
180+
181+
func deploymentScaler(replicas int32) controllerutil.TransformFn {
182+
fn := func(in runtime.Object) (runtime.Object, error) {
183+
d, _ := createDeployment(in)
184+
out := d.(*appsv1.Deployment)
185+
out.Spec.Replicas = &replicas
186+
return out, nil
187+
}
188+
return fn
189+
}

0 commit comments

Comments
 (0)