@@ -122,13 +122,16 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa
122122// this is a CREATE call).
123123func (s * Updater ) Update (liveObject , newObject * typed.TypedValue , version fieldpath.APIVersion , managers fieldpath.ManagedFields , manager string ) (* typed.TypedValue , fieldpath.ManagedFields , error ) {
124124 var err error
125+ managers , err = s .fixupManagedFields (liveObject , managers )
126+ if err != nil {
127+ return nil , fieldpath.ManagedFields {}, err
128+ }
125129 if s .enableUnions {
126130 newObject , err = liveObject .NormalizeUnions (newObject )
127131 if err != nil {
128132 return nil , fieldpath.ManagedFields {}, err
129133 }
130134 }
131- managers = shallowCopyManagers (managers )
132135 managers , compare , err := s .update (liveObject , newObject , version , managers , manager , true )
133136 if err != nil {
134137 return nil , fieldpath.ManagedFields {}, err
@@ -141,24 +144,63 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp
141144 if ignored == nil {
142145 ignored = fieldpath .NewSet ()
143146 }
144- managers [manager ] = fieldpath .NewVersionedSet (
145- managers [manager ].Set ().Union (compare .Modified ).Union (compare .Added ).Difference (compare .Removed ).RecursiveDifference (ignored ),
146- version ,
147- false ,
148- )
147+ managers [manager ] = fieldpath .NewVersionedSet (managers [manager ].Set ().Union (compare .Modified ).Union (compare .Added ).Difference (compare .Removed ).RecursiveDifference (ignored ), version , false )
149148 if managers [manager ].Set ().Empty () {
150149 delete (managers , manager )
151150 }
152151 return newObject , managers , nil
153152}
154153
154+ // fixupManagedFields attempts to fix up the managed fields to be compatible with the live state of
155+ // the schema.
156+ // This is needed when an "unversioned" change are made to a schema that impact the managedFields.
157+ // By "unversionsed" we mean that a specific version of a schema was directly modified (introduction
158+ // of new versions does not require fixup).
159+ //
160+ // Supported schema changes:
161+ // - Changing a type to "atomic": All field manager that managed any granular fields become managers
162+ // of the atomic to ensure a conflict if any of the fields they previously managed are changed.
163+ // Granular managed fields of the struct are removed.
164+ //
165+ // TODO: Changes to add support for:
166+ // - Change a type from "atomic" to "granular", "set" or "map". All field manager that managed the
167+ // atomic become managers of all child fields to ensure a conflict if any child field is changed.
168+ //
169+ // Potential future optimization: Write a SHA-256 hash to managedFields that can be compared
170+ // with the current hash of the live schemas to determine if any fixup is needed.
171+ func (s * Updater ) fixupManagedFields (liveObject * typed.TypedValue , managers fieldpath.ManagedFields ) (fieldpath.ManagedFields , error ) {
172+ result := fieldpath.ManagedFields {}
173+ for manager , versionedSet := range managers {
174+ tv , err := s .Converter .Convert (liveObject , versionedSet .APIVersion ())
175+ if s .Converter .IsMissingVersionError (err ) { // okay to skip, obsolete versions will be deleted automatically anyway
176+ continue
177+ }
178+ if err != nil {
179+ return nil , err
180+ }
181+ fixedFieldSet , err := typed .FixupFieldManagers (versionedSet .Set (), tv )
182+ if err != nil {
183+ return nil , err
184+ }
185+ if fixedFieldSet != nil {
186+ result [manager ] = fieldpath .NewVersionedSet (fixedFieldSet , versionedSet .APIVersion (), versionedSet .Applied ())
187+ } else {
188+ result [manager ] = versionedSet
189+ }
190+ }
191+ return result , nil
192+ }
193+
155194// Apply should be called when Apply is run, given the current object as
156195// well as the configuration that is applied. This will merge the object
157196// and return it. If the object hasn't changed, nil is returned (the
158197// managers can still have changed though).
159198func (s * Updater ) Apply (liveObject , configObject * typed.TypedValue , version fieldpath.APIVersion , managers fieldpath.ManagedFields , manager string , force bool ) (* typed.TypedValue , fieldpath.ManagedFields , error ) {
160- managers = shallowCopyManagers (managers )
161199 var err error
200+ managers , err = s .fixupManagedFields (liveObject , managers )
201+ if err != nil {
202+ return nil , fieldpath.ManagedFields {}, err
203+ }
162204 if s .enableUnions {
163205 configObject , err = configObject .NormalizeUnionsApply (configObject )
164206 if err != nil {
@@ -204,14 +246,6 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel
204246 return newObject , managers , nil
205247}
206248
207- func shallowCopyManagers (managers fieldpath.ManagedFields ) fieldpath.ManagedFields {
208- newManagers := fieldpath.ManagedFields {}
209- for manager , set := range managers {
210- newManagers [manager ] = set
211- }
212- return newManagers
213- }
214-
215249// prune will remove a field, list or map item, iff:
216250// * applyingManager applied it last time
217251// * applyingManager didn't apply it this time
0 commit comments