@@ -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
166165func (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
270311func (s * stateSkel ) getSyncState (objs []* unstructured.Unstructured ) (SyncState , error ) {
271312 log .V (consts .LogLevelInfo ).Info ("Checking related object states" )
0 commit comments