Skip to content

Commit 8756bbd

Browse files
authored
Merge pull request #488 from ykulazhenkov/ignore-updates
Revert "server-side apply" patch and apply new fix for never-ending sync loop issue
2 parents 3263ba2 + 81c6d05 commit 8756bbd

File tree

5 files changed

+92
-17
lines changed

5 files changed

+92
-17
lines changed

controllers/nicclusterpolicy_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ func (r *NicClusterPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
280280
ctl = ctl.Watches(ws[i], &handler.EnqueueRequestForOwner{
281281
IsController: true,
282282
OwnerType: &mellanoxv1alpha1.NicClusterPolicy{},
283-
})
283+
}, builder.WithPredicates(IgnoreSameContentPredicate{}))
284284
}
285285

286286
return ctl.Complete(r)

controllers/predicate.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ limitations under the License.
1717
package controllers
1818

1919
import (
20+
"reflect"
21+
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
2025
"sigs.k8s.io/controller-runtime/pkg/event"
2126
"sigs.k8s.io/controller-runtime/pkg/predicate"
2227

@@ -36,3 +41,35 @@ func (p MlnxLabelChangedPredicate) hasMlnxLabel(labels map[string]string) bool {
3641
func (p MlnxLabelChangedPredicate) Update(e event.UpdateEvent) bool {
3742
return p.hasMlnxLabel(e.ObjectOld.GetLabels()) != p.hasMlnxLabel(e.ObjectNew.GetLabels())
3843
}
44+
45+
// IgnoreSameContentPredicate filters updates if old and new object are the same,
46+
// ignores ResourceVersion and ManagedFields while comparing
47+
type IgnoreSameContentPredicate struct {
48+
predicate.Funcs
49+
}
50+
51+
func (p IgnoreSameContentPredicate) Update(e event.UpdateEvent) bool {
52+
if e.ObjectOld == nil {
53+
return false
54+
}
55+
if e.ObjectNew == nil {
56+
return false
57+
}
58+
oldObj := e.ObjectOld.DeepCopyObject().(client.Object)
59+
newObj := e.ObjectNew.DeepCopyObject().(client.Object)
60+
// ignore resource version
61+
oldObj.SetResourceVersion("")
62+
newObj.SetResourceVersion("")
63+
// ignore managed fields
64+
oldObj.SetManagedFields([]metav1.ManagedFieldsEntry{})
65+
newObj.SetManagedFields([]metav1.ManagedFieldsEntry{})
66+
oldUnstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(oldObj)
67+
if err != nil {
68+
return false
69+
}
70+
newUnstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(newObj)
71+
if err != nil {
72+
return false
73+
}
74+
return !reflect.DeepEqual(oldUnstr, newUnstr)
75+
}

main.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import (
3838

3939
mellanoxcomv1alpha1 "github.com/Mellanox/network-operator/api/v1alpha1"
4040
"github.com/Mellanox/network-operator/controllers"
41-
"github.com/Mellanox/network-operator/pkg/consts"
4241
"github.com/Mellanox/network-operator/pkg/migrate"
4342
// +kubebuilder:scaffold:imports
4443
)
@@ -110,11 +109,10 @@ func main() {
110109

111110
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
112111

113-
clientConf := ctrl.GetConfigOrDie()
114-
clientConf.UserAgent = consts.KubernetesClientUserAgent
115-
116112
stopCtx := ctrl.SetupSignalHandler()
117113

114+
clientConf := ctrl.GetConfigOrDie()
115+
118116
mgr, err := ctrl.NewManager(clientConf, ctrl.Options{
119117
Scheme: scheme,
120118
MetricsBindAddress: metricsAddr,

pkg/consts/consts.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,4 @@ const (
3232
NicClusterPolicyResourceName = "nic-cluster-policy"
3333
OfedDriverLabel = "nvidia.com/ofed-driver"
3434
StateLabel = "nvidia.network-operator.state"
35-
KubernetesClientUserAgent = "nvidia.network-operator"
3635
)

pkg/state/state_skel.go

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030
"k8s.io/apimachinery/pkg/runtime/schema"
3131
"k8s.io/apimachinery/pkg/types"
3232
"sigs.k8s.io/controller-runtime/pkg/client"
33-
"sigs.k8s.io/yaml"
3433

3534
"github.com/Mellanox/network-operator/pkg/consts"
3635
"github.com/Mellanox/network-operator/pkg/nodeinfo"
@@ -165,16 +164,10 @@ func (s *stateSkel) checkDeleteSupported(obj *unstructured.Unstructured) {
165164

166165
func (s *stateSkel) updateObj(obj *unstructured.Unstructured) error {
167166
log.V(consts.LogLevelInfo).Info("Updating Object", "Namespace:", obj.GetNamespace(), "Name:", obj.GetName())
168-
applyPatchData, err := yaml.Marshal(obj)
169-
if err != nil {
170-
log.V(consts.LogLevelError).Error(err, "failed to encode apply patch data")
171-
return err
172-
}
173-
patchUseForce := true
174-
// use server-side apply
175-
if err := s.client.Patch(context.TODO(), obj, client.RawPatch(types.ApplyPatchType, applyPatchData),
176-
client.FieldOwner(consts.KubernetesClientUserAgent),
177-
&client.PatchOptions{Force: &patchUseForce}); err != nil {
167+
// Note: Some objects may require update of the resource version
168+
// TODO: using Patch preserves runtime attributes. In the future consider using patch if relevant
169+
desired := obj.DeepCopy()
170+
if err := s.client.Update(context.TODO(), desired); err != nil {
178171
return errors.Wrap(err, "failed to update resource")
179172
}
180173
log.V(consts.LogLevelInfo).Info("Object updated successfully")
@@ -204,6 +197,16 @@ func (s *stateSkel) createOrUpdateObjs(
204197
return err
205198
}
206199

200+
currentObj := desiredObj.DeepCopy()
201+
if err := s.getObj(currentObj); err != nil {
202+
// Some error occurred
203+
return err
204+
}
205+
206+
if err := s.mergeObjects(desiredObj, currentObj); err != nil {
207+
return err
208+
}
209+
207210
// Object found, Update it
208211
if err := s.updateObj(desiredObj); err != nil {
209212
return err
@@ -266,6 +269,44 @@ func (s *stateSkel) deleteStateRelatedObjects() (bool, error) {
266269
return found, nil
267270
}
268271

272+
func (s *stateSkel) mergeObjects(updated, current *unstructured.Unstructured) error {
273+
// Set resource version
274+
// ResourceVersion must be passed unmodified back to the server.
275+
// ResourceVersion helps the kubernetes API server to implement optimistic concurrency for PUT operations
276+
// when two PUT requests are specifying the resourceVersion, one of the PUTs will fail.
277+
updated.SetResourceVersion(current.GetResourceVersion())
278+
279+
gvk := updated.GroupVersionKind()
280+
if gvk.Group == "" && gvk.Kind == "ServiceAccount" {
281+
return s.mergeServiceAccount(updated, current)
282+
}
283+
return nil
284+
}
285+
286+
// For Service Account, keep secrets if exists
287+
func (s *stateSkel) mergeServiceAccount(updated, current *unstructured.Unstructured) error {
288+
curSecrets, ok, err := unstructured.NestedSlice(current.Object, "secrets")
289+
if err != nil {
290+
return err
291+
}
292+
if ok {
293+
if err := unstructured.SetNestedField(updated.Object, curSecrets, "secrets"); err != nil {
294+
return err
295+
}
296+
}
297+
298+
curImagePullSecrets, ok, err := unstructured.NestedSlice(current.Object, "imagePullSecrets")
299+
if err != nil {
300+
return err
301+
}
302+
if ok {
303+
if err := unstructured.SetNestedField(updated.Object, curImagePullSecrets, "imagePullSecrets"); err != nil {
304+
return err
305+
}
306+
}
307+
return nil
308+
}
309+
269310
// Iterate over objects and check for their readiness
270311
func (s *stateSkel) getSyncState(objs []*unstructured.Unstructured) (SyncState, error) {
271312
log.V(consts.LogLevelInfo).Info("Checking related object states")

0 commit comments

Comments
 (0)