in controllers/common.go [60:113]
func reconcileDesiredObjectUIDs(ctx context.Context, kubeClient client.Client, logger logr.Logger,
owner metav1.Object, scheme *runtime.Scheme, desiredObjects ...client.Object) (map[types.UID]client.Object, error) {
var errs []error
existingObjectMap := make(map[types.UID]client.Object)
var existingObjectList []client.Object
for _, desired := range desiredObjects {
l := logger.WithValues(
"object_name", desired.GetName(),
"object_kind", desired.GetObjectKind(),
)
if isNamespaceScoped(desired) {
if setErr := ctrl.SetControllerReference(owner, desired, scheme); setErr != nil {
l.Error(setErr, "failed to set controller owner reference to desired")
errs = append(errs, setErr)
continue
}
}
// existing is an object the controller runtime will hydrate for us
// we obtain the existing object by deep copying the desired object because it's the most convenient way
existing := desired.DeepCopyObject().(client.Object)
existingObjectList = append(existingObjectList, existing) //uid are not assigned yet
mutateFn := manifests.MutateFuncFor(existing, desired)
var op controllerutil.OperationResult
crudErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
result, createOrUpdateErr := ctrl.CreateOrUpdate(ctx, kubeClient, existing, mutateFn)
op = result
return createOrUpdateErr
})
if crudErr != nil && errors.Is(crudErr, manifests.ImmutableChangeErr) {
l.Error(crudErr, "detected immutable field change, trying to delete, new object will be created on next reconcile", "existing", existing.GetName())
delErr := kubeClient.Delete(ctx, existing)
if delErr != nil {
return nil, delErr
}
continue
} else if crudErr != nil {
l.Error(crudErr, "failed to configure desired")
errs = append(errs, crudErr)
continue
}
l.V(1).Info(fmt.Sprintf("desired has been %s", op))
}
if len(errs) > 0 {
return nil, fmt.Errorf("failed to create objects for %s: %w", owner.GetName(), errors.Join(errs...))
}
for _, obj := range existingObjectList {
existingObjectMap[obj.GetUID()] = obj
}
return existingObjectMap, nil
}