Skip to content

Commit 95e0bce

Browse files
authored
Undo unstructured updates in Reconcile (#1036)
Originating issue: [IBMPrivateCloud/roadmap#66707](https://github.ibm.com/IBMPrivateCloud/roadmap/issues/66707) Signed-off-by: Rob Hundley <[email protected]>
1 parent e9d1b0a commit 95e0bce

File tree

1 file changed

+61
-10
lines changed

1 file changed

+61
-10
lines changed

controllers/common/secondary.go

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ import (
2323

2424
"github.com/opdev/subreconciler"
2525
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
26-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27-
"k8s.io/apimachinery/pkg/runtime"
2826
"k8s.io/apimachinery/pkg/runtime/schema"
2927
"k8s.io/apimachinery/pkg/types"
3028
ctrl "sigs.k8s.io/controller-runtime"
@@ -33,6 +31,11 @@ import (
3331
logf "sigs.k8s.io/controller-runtime/pkg/log"
3432
)
3533

34+
// Generator is an interface that wraps the Generate method.
35+
//
36+
// It takes a context assumed to be scoped to the current reconcile loop and a
37+
// client.Object that is the recipient of the new object state. It returns an
38+
// error if something goes wrong over the course of performing this operation.
3639
type Generator interface {
3740
Generate(context.Context, client.Object) error
3841
}
@@ -43,6 +46,20 @@ type Generator interface {
4346
// controller logic.
4447
type GenerateFn[T client.Object] func(SecondaryReconciler, context.Context, T) error
4548

49+
// Modifier is an interface that wraps the Modify method.
50+
//
51+
// It takes a context assumed to be scoped to the current reconcile loop and two
52+
// client.Object arguments - the first should be the observed object that
53+
// represents what is on the cluster currently, and the second is what the
54+
// reconciler determines should be installed on the cluster instead.
55+
//
56+
// If there are relevant differences between the two objects, the first is
57+
// updated with the values from the second so that they are now the same in
58+
// those relevant ways.
59+
//
60+
// Returns an bool representing whether a change was made to the first
61+
// client.Object as well as an error if something goes wrong over the course of
62+
// performing this operation.
4663
type Modifier interface {
4764
Modify(context.Context, client.Object, client.Object) (bool, error)
4865
}
@@ -54,6 +71,12 @@ type Modifier interface {
5471
// itself.
5572
type ModifyFn[T client.Object] func(SecondaryReconciler, context.Context, T, T) (bool, error)
5673

74+
// WriteResponder is an interface that wraps the OnWrite method.
75+
//
76+
// It takes a context assumed to be scoped to the current reconcile loop and
77+
// performs any work that needs to be done after a write is performed.
78+
//
79+
// Returns an error if something goes wrong.
5780
type WriteResponder interface {
5881
OnWrite(context.Context) error
5982
}
@@ -114,32 +137,42 @@ type secondaryReconciler[T client.Object] struct {
114137
onWrite OnWriteFn[T] // function that is run after a write is made to this secondary object
115138
}
116139

140+
// GetName returns the name of the secondary object.
117141
func (s *secondaryReconciler[T]) GetName() string {
118142
return s.name
119143
}
120144

145+
// GetNamespace returns the namespace of the secondary object.
121146
func (s *secondaryReconciler[T]) GetNamespace() string {
122147
return s.namespace
123148
}
124149

150+
// GetEmptyObject returns an empty object of the same GVK as the secondary
151+
// object.
125152
func (s *secondaryReconciler[T]) GetEmptyObject() client.Object {
126153
rType := reflect.TypeFor[T]().Elem()
127154
return reflect.New(rType).Interface().(T)
128155
}
129156

157+
// GetKind returns the kind of the secondary object.
130158
func (s *secondaryReconciler[T]) GetKind() string {
131159
gvk, _ := apiutil.GVKForObject(s.GetEmptyObject(), s.Scheme())
132160
return gvk.Kind
133161
}
134162

163+
// GetPrimary returns the primary object that triggers this secondary object's
164+
// reconciliation.
135165
func (s *secondaryReconciler[T]) GetPrimary() client.Object {
136166
return s.primary
137167
}
138168

169+
// GetClient returns the client used for reconciling the secondary object.
139170
func (s *secondaryReconciler[T]) GetClient() client.Client {
140171
return s.Client
141172
}
142173

174+
// Generate creates a new instance of the secondary object with values derived
175+
// from the secondaryReconciler's settings.
143176
func (s *secondaryReconciler[T]) Generate(ctx context.Context, obj client.Object) (err error) {
144177
objT, ok := obj.(T)
145178
if !ok {
@@ -151,6 +184,10 @@ func (s *secondaryReconciler[T]) Generate(ctx context.Context, obj client.Object
151184
return s.generate(s, ctx, objT)
152185
}
153186

187+
// Modify compares observed and generated, and, if there are relevant
188+
// qualities that differ between them, observed is updated to match generated in
189+
// those qualities. Returns a boolean for whether observed was updated and an
190+
// error if an error was encountered while attempting this operation.
154191
func (s *secondaryReconciler[T]) Modify(ctx context.Context, observed, generated client.Object) (modified bool, err error) {
155192
if s.modify == nil {
156193
return
@@ -171,13 +208,17 @@ func (s *secondaryReconciler[T]) Modify(ctx context.Context, observed, generated
171208
return s.modify(s, ctx, observedT, generatedT)
172209
}
173210

211+
// OnWrite is a function that is executed when any write is made to the cluster,
212+
// e.g. an object is created or updated.
174213
func (s *secondaryReconciler[T]) OnWrite(ctx context.Context) (err error) {
175214
if s.onWrite == nil {
176215
return
177216
}
178217
return s.onWrite(s, ctx)
179218
}
180219

220+
// Reconcile performs reconciliation related to the creation or modification of
221+
// the secondary object that the secondaryReconciler is targeted for.
181222
func (s *secondaryReconciler[T]) Reconcile(ctx context.Context) (result *ctrl.Result, err error) {
182223
reqLogger := logf.FromContext(ctx, "Object.Namespace", s.GetNamespace(), "Object.Kind", s.GetKind(), "Object.Name", s.GetName())
183224
debugLogger := logf.FromContext(ctx, "Object.Namespace", s.GetNamespace(), "Object.Kind", s.GetKind(), "Object.Name", s.GetName()).V(1)
@@ -214,15 +255,8 @@ func (s *secondaryReconciler[T]) Reconcile(ctx context.Context) (result *ctrl.Re
214255
return subreconciler.ContinueReconciling()
215256
}
216257

217-
u := &unstructured.Unstructured{}
218-
u.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(observed)
219-
if err != nil {
220-
reqLogger.Info("Failed to convert Object to unstructured", "reason", err.Error())
221-
return subreconciler.RequeueWithError(err)
222-
}
223-
224258
reqLogger.Info("Updates found; updating the Object")
225-
if err = s.Update(ctx, u); err != nil {
259+
if err = s.Update(ctx, observed); err != nil {
226260
reqLogger.Info("Failed to update Object", "msg", err.Error())
227261
return subreconciler.RequeueWithDelay(DefaultLowerWait)
228262
}
@@ -239,12 +273,16 @@ type SecondaryReconcilerBuilder[T client.Object] struct {
239273
s *secondaryReconciler[T]
240274
}
241275

276+
// NewSecondaryReconcilerBuilder creates a new builder for a SecondaryReconciler.
242277
func NewSecondaryReconcilerBuilder[T client.Object]() *SecondaryReconcilerBuilder[T] {
243278
return &SecondaryReconcilerBuilder[T]{
244279
s: &secondaryReconciler[T]{},
245280
}
246281
}
247282

283+
// Build creates a new SecondaryReconciler using the configurations supplied by
284+
// other builder functions. Returns an error if no client or GVK is configured
285+
// on the SecondaryReconciler.
248286
func (b *SecondaryReconcilerBuilder[T]) Build() (*secondaryReconciler[T], error) {
249287
if b.s.Client == nil {
250288
return nil, fmt.Errorf("failed to build secondary reconciler: no client defined")
@@ -257,6 +295,9 @@ func (b *SecondaryReconcilerBuilder[T]) Build() (*secondaryReconciler[T], error)
257295
return b.s, nil
258296
}
259297

298+
// MustBuild creates a new SecondaryReconciler using the configurations supplied
299+
// by other builder functions. Panics if a client or GVK is not configured on
300+
// the SecondaryReconciler.
260301
func (b *SecondaryReconcilerBuilder[T]) MustBuild() *secondaryReconciler[T] {
261302
if b.s.Client == nil {
262303
panic("failed to build secondary reconciler: no client defined")
@@ -269,21 +310,29 @@ func (b *SecondaryReconcilerBuilder[T]) MustBuild() *secondaryReconciler[T] {
269310
return b.s
270311
}
271312

313+
// WithName sets the name of the secondary object that the
314+
// SecondaryReconciler is targeting.
272315
func (b *SecondaryReconcilerBuilder[T]) WithName(name string) *SecondaryReconcilerBuilder[T] {
273316
b.s.name = name
274317
return b
275318
}
276319

320+
// WithName sets the namespace of the secondary object that the
321+
// SecondaryReconciler is targeting.
277322
func (b *SecondaryReconcilerBuilder[T]) WithNamespace(namespace string) *SecondaryReconcilerBuilder[T] {
278323
b.s.namespace = namespace
279324
return b
280325
}
281326

327+
// WithPrimary sets the primary object for the secondary object that the
328+
// SecondaryReconciler is targeting.
282329
func (b *SecondaryReconcilerBuilder[T]) WithPrimary(primary client.Object) *SecondaryReconcilerBuilder[T] {
283330
b.s.primary = primary
284331
return b
285332
}
286333

334+
// WithClient sets the client to be used for interacting with the cluster while
335+
// reconciling the secondary object.
287336
func (b *SecondaryReconcilerBuilder[T]) WithClient(cl client.Client) *SecondaryReconcilerBuilder[T] {
288337
b.s.Client = cl
289338
return b
@@ -343,10 +392,12 @@ type SecondaryReconcilerFn subreconciler.Fn
343392

344393
var _ Subreconciler = SecondaryReconcilerFn(func(ctx context.Context) (result *ctrl.Result, err error) { return })
345394

395+
// Reconcile implements Subreconciler.
346396
func (f SecondaryReconcilerFn) Reconcile(ctx context.Context) (result *ctrl.Result, err error) {
347397
return f(ctx)
348398
}
349399

400+
// NewSecondaryReconcilerFn creates a new SecondaryReconcilerFn from a subreconciler.FnWithRequest.
350401
func NewSecondaryReconcilerFn(req ctrl.Request, fn subreconciler.FnWithRequest) SecondaryReconcilerFn {
351402
return func(ctx context.Context) (result *ctrl.Result, err error) {
352403
return fn(ctx, req)

0 commit comments

Comments
 (0)