k8s.io/apimachinery@v0.29.2/pkg/util/strategicpatch/patch.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package strategicpatch 18 19 import ( 20 "fmt" 21 "reflect" 22 "sort" 23 "strings" 24 25 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 26 "k8s.io/apimachinery/pkg/util/json" 27 "k8s.io/apimachinery/pkg/util/mergepatch" 28 ) 29 30 // An alternate implementation of JSON Merge Patch 31 // (https://tools.ietf.org/html/rfc7386) which supports the ability to annotate 32 // certain fields with metadata that indicates whether the elements of JSON 33 // lists should be merged or replaced. 34 // 35 // For more information, see the PATCH section of docs/devel/api-conventions.md. 36 // 37 // Some of the content of this package was borrowed with minor adaptations from 38 // evanphx/json-patch and openshift/origin. 39 40 const ( 41 directiveMarker = "$patch" 42 deleteDirective = "delete" 43 replaceDirective = "replace" 44 mergeDirective = "merge" 45 46 retainKeysStrategy = "retainKeys" 47 48 deleteFromPrimitiveListDirectivePrefix = "$deleteFromPrimitiveList" 49 retainKeysDirective = "$" + retainKeysStrategy 50 setElementOrderDirectivePrefix = "$setElementOrder" 51 ) 52 53 // JSONMap is a representations of JSON object encoded as map[string]interface{} 54 // where the children can be either map[string]interface{}, []interface{} or 55 // primitive type). 56 // Operating on JSONMap representation is much faster as it doesn't require any 57 // json marshaling and/or unmarshaling operations. 58 type JSONMap map[string]interface{} 59 60 type DiffOptions struct { 61 // SetElementOrder determines whether we generate the $setElementOrder parallel list. 62 SetElementOrder bool 63 // IgnoreChangesAndAdditions indicates if we keep the changes and additions in the patch. 64 IgnoreChangesAndAdditions bool 65 // IgnoreDeletions indicates if we keep the deletions in the patch. 66 IgnoreDeletions bool 67 // We introduce a new value retainKeys for patchStrategy. 68 // It indicates that all fields needing to be preserved must be 69 // present in the `retainKeys` list. 70 // And the fields that are present will be merged with live object. 71 // All the missing fields will be cleared when patching. 72 BuildRetainKeysDirective bool 73 } 74 75 type MergeOptions struct { 76 // MergeParallelList indicates if we are merging the parallel list. 77 // We don't merge parallel list when calling mergeMap() in CreateThreeWayMergePatch() 78 // which is called client-side. 79 // We merge parallel list iff when calling mergeMap() in StrategicMergeMapPatch() 80 // which is called server-side 81 MergeParallelList bool 82 // IgnoreUnmatchedNulls indicates if we should process the unmatched nulls. 83 IgnoreUnmatchedNulls bool 84 } 85 86 // The following code is adapted from github.com/openshift/origin/pkg/util/jsonmerge. 87 // Instead of defining a Delta that holds an original, a patch and a set of preconditions, 88 // the reconcile method accepts a set of preconditions as an argument. 89 90 // CreateTwoWayMergePatch creates a patch that can be passed to StrategicMergePatch from an original 91 // document and a modified document, which are passed to the method as json encoded content. It will 92 // return a patch that yields the modified document when applied to the original document, or an error 93 // if either of the two documents is invalid. 94 func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) ([]byte, error) { 95 schema, err := NewPatchMetaFromStruct(dataStruct) 96 if err != nil { 97 return nil, err 98 } 99 100 return CreateTwoWayMergePatchUsingLookupPatchMeta(original, modified, schema, fns...) 101 } 102 103 func CreateTwoWayMergePatchUsingLookupPatchMeta( 104 original, modified []byte, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) ([]byte, error) { 105 originalMap := map[string]interface{}{} 106 if len(original) > 0 { 107 if err := json.Unmarshal(original, &originalMap); err != nil { 108 return nil, mergepatch.ErrBadJSONDoc 109 } 110 } 111 112 modifiedMap := map[string]interface{}{} 113 if len(modified) > 0 { 114 if err := json.Unmarshal(modified, &modifiedMap); err != nil { 115 return nil, mergepatch.ErrBadJSONDoc 116 } 117 } 118 119 patchMap, err := CreateTwoWayMergeMapPatchUsingLookupPatchMeta(originalMap, modifiedMap, schema, fns...) 120 if err != nil { 121 return nil, err 122 } 123 124 return json.Marshal(patchMap) 125 } 126 127 // CreateTwoWayMergeMapPatch creates a patch from an original and modified JSON objects, 128 // encoded JSONMap. 129 // The serialized version of the map can then be passed to StrategicMergeMapPatch. 130 func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) (JSONMap, error) { 131 schema, err := NewPatchMetaFromStruct(dataStruct) 132 if err != nil { 133 return nil, err 134 } 135 136 return CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified, schema, fns...) 137 } 138 139 func CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified JSONMap, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) (JSONMap, error) { 140 diffOptions := DiffOptions{ 141 SetElementOrder: true, 142 } 143 patchMap, err := diffMaps(original, modified, schema, diffOptions) 144 if err != nil { 145 return nil, err 146 } 147 148 // Apply the preconditions to the patch, and return an error if any of them fail. 149 for _, fn := range fns { 150 if !fn(patchMap) { 151 return nil, mergepatch.NewErrPreconditionFailed(patchMap) 152 } 153 } 154 155 return patchMap, nil 156 } 157 158 // Returns a (recursive) strategic merge patch that yields modified when applied to original. 159 // Including: 160 // - Adding fields to the patch present in modified, missing from original 161 // - Setting fields to the patch present in modified and original with different values 162 // - Delete fields present in original, missing from modified through 163 // - IFF map field - set to nil in patch 164 // - IFF list of maps && merge strategy - use deleteDirective for the elements 165 // - IFF list of primitives && merge strategy - use parallel deletion list 166 // - IFF list of maps or primitives with replace strategy (default) - set patch value to the value in modified 167 // - Build $retainKeys directive for fields with retainKeys patch strategy 168 func diffMaps(original, modified map[string]interface{}, schema LookupPatchMeta, diffOptions DiffOptions) (map[string]interface{}, error) { 169 patch := map[string]interface{}{} 170 171 // This will be used to build the $retainKeys directive sent in the patch 172 retainKeysList := make([]interface{}, 0, len(modified)) 173 174 // Compare each value in the modified map against the value in the original map 175 for key, modifiedValue := range modified { 176 // Get the underlying type for pointers 177 if diffOptions.BuildRetainKeysDirective && modifiedValue != nil { 178 retainKeysList = append(retainKeysList, key) 179 } 180 181 originalValue, ok := original[key] 182 if !ok { 183 // Key was added, so add to patch 184 if !diffOptions.IgnoreChangesAndAdditions { 185 patch[key] = modifiedValue 186 } 187 continue 188 } 189 190 // The patch may have a patch directive 191 // TODO: figure out if we need this. This shouldn't be needed by apply. When would the original map have patch directives in it? 192 foundDirectiveMarker, err := handleDirectiveMarker(key, originalValue, modifiedValue, patch) 193 if err != nil { 194 return nil, err 195 } 196 if foundDirectiveMarker { 197 continue 198 } 199 200 if reflect.TypeOf(originalValue) != reflect.TypeOf(modifiedValue) { 201 // Types have changed, so add to patch 202 if !diffOptions.IgnoreChangesAndAdditions { 203 patch[key] = modifiedValue 204 } 205 continue 206 } 207 208 // Types are the same, so compare values 209 switch originalValueTyped := originalValue.(type) { 210 case map[string]interface{}: 211 modifiedValueTyped := modifiedValue.(map[string]interface{}) 212 err = handleMapDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions) 213 case []interface{}: 214 modifiedValueTyped := modifiedValue.([]interface{}) 215 err = handleSliceDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions) 216 default: 217 replacePatchFieldIfNotEqual(key, originalValue, modifiedValue, patch, diffOptions) 218 } 219 if err != nil { 220 return nil, err 221 } 222 } 223 224 updatePatchIfMissing(original, modified, patch, diffOptions) 225 // Insert the retainKeysList iff there are values present in the retainKeysList and 226 // either of the following is true: 227 // - the patch is not empty 228 // - there are additional field in original that need to be cleared 229 if len(retainKeysList) > 0 && 230 (len(patch) > 0 || hasAdditionalNewField(original, modified)) { 231 patch[retainKeysDirective] = sortScalars(retainKeysList) 232 } 233 return patch, nil 234 } 235 236 // handleDirectiveMarker handles how to diff directive marker between 2 objects 237 func handleDirectiveMarker(key string, originalValue, modifiedValue interface{}, patch map[string]interface{}) (bool, error) { 238 if key == directiveMarker { 239 originalString, ok := originalValue.(string) 240 if !ok { 241 return false, fmt.Errorf("invalid value for special key: %s", directiveMarker) 242 } 243 modifiedString, ok := modifiedValue.(string) 244 if !ok { 245 return false, fmt.Errorf("invalid value for special key: %s", directiveMarker) 246 } 247 if modifiedString != originalString { 248 patch[directiveMarker] = modifiedValue 249 } 250 return true, nil 251 } 252 return false, nil 253 } 254 255 // handleMapDiff diff between 2 maps `originalValueTyped` and `modifiedValue`, 256 // puts the diff in the `patch` associated with `key` 257 // key is the key associated with originalValue and modifiedValue. 258 // originalValue, modifiedValue are the old and new value respectively.They are both maps 259 // patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue 260 // diffOptions contains multiple options to control how we do the diff. 261 func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]interface{}, 262 schema LookupPatchMeta, diffOptions DiffOptions) error { 263 subschema, patchMeta, err := schema.LookupPatchMetadataForStruct(key) 264 265 if err != nil { 266 // We couldn't look up metadata for the field 267 // If the values are identical, this doesn't matter, no patch is needed 268 if reflect.DeepEqual(originalValue, modifiedValue) { 269 return nil 270 } 271 // Otherwise, return the error 272 return err 273 } 274 retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 275 if err != nil { 276 return err 277 } 278 diffOptions.BuildRetainKeysDirective = retainKeys 279 switch patchStrategy { 280 // The patch strategic from metadata tells us to replace the entire object instead of diffing it 281 case replaceDirective: 282 if !diffOptions.IgnoreChangesAndAdditions { 283 patch[key] = modifiedValue 284 } 285 default: 286 patchValue, err := diffMaps(originalValue, modifiedValue, subschema, diffOptions) 287 if err != nil { 288 return err 289 } 290 // Maps were not identical, use provided patch value 291 if len(patchValue) > 0 { 292 patch[key] = patchValue 293 } 294 } 295 return nil 296 } 297 298 // handleSliceDiff diff between 2 slices `originalValueTyped` and `modifiedValue`, 299 // puts the diff in the `patch` associated with `key` 300 // key is the key associated with originalValue and modifiedValue. 301 // originalValue, modifiedValue are the old and new value respectively.They are both slices 302 // patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue 303 // diffOptions contains multiple options to control how we do the diff. 304 func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, patch map[string]interface{}, 305 schema LookupPatchMeta, diffOptions DiffOptions) error { 306 subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(key) 307 if err != nil { 308 // We couldn't look up metadata for the field 309 // If the values are identical, this doesn't matter, no patch is needed 310 if reflect.DeepEqual(originalValue, modifiedValue) { 311 return nil 312 } 313 // Otherwise, return the error 314 return err 315 } 316 retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 317 if err != nil { 318 return err 319 } 320 switch patchStrategy { 321 // Merge the 2 slices using mergePatchKey 322 case mergeDirective: 323 diffOptions.BuildRetainKeysDirective = retainKeys 324 addList, deletionList, setOrderList, err := diffLists(originalValue, modifiedValue, subschema, patchMeta.GetPatchMergeKey(), diffOptions) 325 if err != nil { 326 return err 327 } 328 if len(addList) > 0 { 329 patch[key] = addList 330 } 331 // generate a parallel list for deletion 332 if len(deletionList) > 0 { 333 parallelDeletionListKey := fmt.Sprintf("%s/%s", deleteFromPrimitiveListDirectivePrefix, key) 334 patch[parallelDeletionListKey] = deletionList 335 } 336 if len(setOrderList) > 0 { 337 parallelSetOrderListKey := fmt.Sprintf("%s/%s", setElementOrderDirectivePrefix, key) 338 patch[parallelSetOrderListKey] = setOrderList 339 } 340 default: 341 replacePatchFieldIfNotEqual(key, originalValue, modifiedValue, patch, diffOptions) 342 } 343 return nil 344 } 345 346 // replacePatchFieldIfNotEqual updates the patch if original and modified are not deep equal 347 // if diffOptions.IgnoreChangesAndAdditions is false. 348 // original is the old value, maybe either the live cluster object or the last applied configuration 349 // modified is the new value, is always the users new config 350 func replacePatchFieldIfNotEqual(key string, original, modified interface{}, 351 patch map[string]interface{}, diffOptions DiffOptions) { 352 if diffOptions.IgnoreChangesAndAdditions { 353 // Ignoring changes - do nothing 354 return 355 } 356 if reflect.DeepEqual(original, modified) { 357 // Contents are identical - do nothing 358 return 359 } 360 // Create a patch to replace the old value with the new one 361 patch[key] = modified 362 } 363 364 // updatePatchIfMissing iterates over `original` when ignoreDeletions is false. 365 // Clear the field whose key is not present in `modified`. 366 // original is the old value, maybe either the live cluster object or the last applied configuration 367 // modified is the new value, is always the users new config 368 func updatePatchIfMissing(original, modified, patch map[string]interface{}, diffOptions DiffOptions) { 369 if diffOptions.IgnoreDeletions { 370 // Ignoring deletion - do nothing 371 return 372 } 373 // Add nils for deleted values 374 for key := range original { 375 if _, found := modified[key]; !found { 376 patch[key] = nil 377 } 378 } 379 } 380 381 // validateMergeKeyInLists checks if each map in the list has the mentryerge key. 382 func validateMergeKeyInLists(mergeKey string, lists ...[]interface{}) error { 383 for _, list := range lists { 384 for _, item := range list { 385 m, ok := item.(map[string]interface{}) 386 if !ok { 387 return mergepatch.ErrBadArgType(m, item) 388 } 389 if _, ok = m[mergeKey]; !ok { 390 return mergepatch.ErrNoMergeKey(m, mergeKey) 391 } 392 } 393 } 394 return nil 395 } 396 397 // normalizeElementOrder sort `patch` list by `patchOrder` and sort `serverOnly` list by `serverOrder`. 398 // Then it merges the 2 sorted lists. 399 // It guarantee the relative order in the patch list and in the serverOnly list is kept. 400 // `patch` is a list of items in the patch, and `serverOnly` is a list of items in the live object. 401 // `patchOrder` is the order we want `patch` list to have and 402 // `serverOrder` is the order we want `serverOnly` list to have. 403 // kind is the kind of each item in the lists `patch` and `serverOnly`. 404 func normalizeElementOrder(patch, serverOnly, patchOrder, serverOrder []interface{}, mergeKey string, kind reflect.Kind) ([]interface{}, error) { 405 patch, err := normalizeSliceOrder(patch, patchOrder, mergeKey, kind) 406 if err != nil { 407 return nil, err 408 } 409 serverOnly, err = normalizeSliceOrder(serverOnly, serverOrder, mergeKey, kind) 410 if err != nil { 411 return nil, err 412 } 413 all := mergeSortedSlice(serverOnly, patch, serverOrder, mergeKey, kind) 414 415 return all, nil 416 } 417 418 // mergeSortedSlice merges the 2 sorted lists by serverOrder with best effort. 419 // It will insert each item in `left` list to `right` list. In most cases, the 2 lists will be interleaved. 420 // The relative order of left and right are guaranteed to be kept. 421 // They have higher precedence than the order in the live list. 422 // The place for a item in `left` is found by: 423 // scan from the place of last insertion in `right` to the end of `right`, 424 // the place is before the first item that is greater than the item we want to insert. 425 // example usage: using server-only items as left and patch items as right. We insert server-only items 426 // to patch list. We use the order of live object as record for comparison. 427 func mergeSortedSlice(left, right, serverOrder []interface{}, mergeKey string, kind reflect.Kind) []interface{} { 428 // Returns if l is less than r, and if both have been found. 429 // If l and r both present and l is in front of r, l is less than r. 430 less := func(l, r interface{}) (bool, bool) { 431 li := index(serverOrder, l, mergeKey, kind) 432 ri := index(serverOrder, r, mergeKey, kind) 433 if li >= 0 && ri >= 0 { 434 return li < ri, true 435 } else { 436 return false, false 437 } 438 } 439 440 // left and right should be non-overlapping. 441 size := len(left) + len(right) 442 i, j := 0, 0 443 s := make([]interface{}, size, size) 444 445 for k := 0; k < size; k++ { 446 if i >= len(left) && j < len(right) { 447 // have items left in `right` list 448 s[k] = right[j] 449 j++ 450 } else if j >= len(right) && i < len(left) { 451 // have items left in `left` list 452 s[k] = left[i] 453 i++ 454 } else { 455 // compare them if i and j are both in bound 456 less, foundBoth := less(left[i], right[j]) 457 if foundBoth && less { 458 s[k] = left[i] 459 i++ 460 } else { 461 s[k] = right[j] 462 j++ 463 } 464 } 465 } 466 return s 467 } 468 469 // index returns the index of the item in the given items, or -1 if it doesn't exist 470 // l must NOT be a slice of slices, this should be checked before calling. 471 func index(l []interface{}, valToLookUp interface{}, mergeKey string, kind reflect.Kind) int { 472 var getValFn func(interface{}) interface{} 473 // Get the correct `getValFn` based on item `kind`. 474 // It should return the value of merge key for maps and 475 // return the item for other kinds. 476 switch kind { 477 case reflect.Map: 478 getValFn = func(item interface{}) interface{} { 479 typedItem, ok := item.(map[string]interface{}) 480 if !ok { 481 return nil 482 } 483 val := typedItem[mergeKey] 484 return val 485 } 486 default: 487 getValFn = func(item interface{}) interface{} { 488 return item 489 } 490 } 491 492 for i, v := range l { 493 if getValFn(valToLookUp) == getValFn(v) { 494 return i 495 } 496 } 497 return -1 498 } 499 500 // extractToDeleteItems takes a list and 501 // returns 2 lists: one contains items that should be kept and the other contains items to be deleted. 502 func extractToDeleteItems(l []interface{}) ([]interface{}, []interface{}, error) { 503 var nonDelete, toDelete []interface{} 504 for _, v := range l { 505 m, ok := v.(map[string]interface{}) 506 if !ok { 507 return nil, nil, mergepatch.ErrBadArgType(m, v) 508 } 509 510 directive, foundDirective := m[directiveMarker] 511 if foundDirective && directive == deleteDirective { 512 toDelete = append(toDelete, v) 513 } else { 514 nonDelete = append(nonDelete, v) 515 } 516 } 517 return nonDelete, toDelete, nil 518 } 519 520 // normalizeSliceOrder sort `toSort` list by `order` 521 func normalizeSliceOrder(toSort, order []interface{}, mergeKey string, kind reflect.Kind) ([]interface{}, error) { 522 var toDelete []interface{} 523 if kind == reflect.Map { 524 // make sure each item in toSort, order has merge key 525 err := validateMergeKeyInLists(mergeKey, toSort, order) 526 if err != nil { 527 return nil, err 528 } 529 toSort, toDelete, err = extractToDeleteItems(toSort) 530 if err != nil { 531 return nil, err 532 } 533 } 534 535 sort.SliceStable(toSort, func(i, j int) bool { 536 if ii := index(order, toSort[i], mergeKey, kind); ii >= 0 { 537 if ij := index(order, toSort[j], mergeKey, kind); ij >= 0 { 538 return ii < ij 539 } 540 } 541 return true 542 }) 543 toSort = append(toSort, toDelete...) 544 return toSort, nil 545 } 546 547 // Returns a (recursive) strategic merge patch, a parallel deletion list if necessary and 548 // another list to set the order of the list 549 // Only list of primitives with merge strategy will generate a parallel deletion list. 550 // These two lists should yield modified when applied to original, for lists with merge semantics. 551 func diffLists(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, []interface{}, error) { 552 if len(original) == 0 { 553 // Both slices are empty - do nothing 554 if len(modified) == 0 || diffOptions.IgnoreChangesAndAdditions { 555 return nil, nil, nil, nil 556 } 557 558 // Old slice was empty - add all elements from the new slice 559 return modified, nil, nil, nil 560 } 561 562 elementType, err := sliceElementType(original, modified) 563 if err != nil { 564 return nil, nil, nil, err 565 } 566 567 var patchList, deleteList, setOrderList []interface{} 568 kind := elementType.Kind() 569 switch kind { 570 case reflect.Map: 571 patchList, deleteList, err = diffListsOfMaps(original, modified, schema, mergeKey, diffOptions) 572 if err != nil { 573 return nil, nil, nil, err 574 } 575 patchList, err = normalizeSliceOrder(patchList, modified, mergeKey, kind) 576 if err != nil { 577 return nil, nil, nil, err 578 } 579 orderSame, err := isOrderSame(original, modified, mergeKey) 580 if err != nil { 581 return nil, nil, nil, err 582 } 583 // append the deletions to the end of the patch list. 584 patchList = append(patchList, deleteList...) 585 deleteList = nil 586 // generate the setElementOrder list when there are content changes or order changes 587 if diffOptions.SetElementOrder && 588 ((!diffOptions.IgnoreChangesAndAdditions && (len(patchList) > 0 || !orderSame)) || 589 (!diffOptions.IgnoreDeletions && len(patchList) > 0)) { 590 // Generate a list of maps that each item contains only the merge key. 591 setOrderList = make([]interface{}, len(modified)) 592 for i, v := range modified { 593 typedV := v.(map[string]interface{}) 594 setOrderList[i] = map[string]interface{}{ 595 mergeKey: typedV[mergeKey], 596 } 597 } 598 } 599 case reflect.Slice: 600 // Lists of Lists are not permitted by the api 601 return nil, nil, nil, mergepatch.ErrNoListOfLists 602 default: 603 patchList, deleteList, err = diffListsOfScalars(original, modified, diffOptions) 604 if err != nil { 605 return nil, nil, nil, err 606 } 607 patchList, err = normalizeSliceOrder(patchList, modified, mergeKey, kind) 608 // generate the setElementOrder list when there are content changes or order changes 609 if diffOptions.SetElementOrder && ((!diffOptions.IgnoreDeletions && len(deleteList) > 0) || 610 (!diffOptions.IgnoreChangesAndAdditions && !reflect.DeepEqual(original, modified))) { 611 setOrderList = modified 612 } 613 } 614 return patchList, deleteList, setOrderList, err 615 } 616 617 // isOrderSame checks if the order in a list has changed 618 func isOrderSame(original, modified []interface{}, mergeKey string) (bool, error) { 619 if len(original) != len(modified) { 620 return false, nil 621 } 622 for i, modifiedItem := range modified { 623 equal, err := mergeKeyValueEqual(original[i], modifiedItem, mergeKey) 624 if err != nil || !equal { 625 return equal, err 626 } 627 } 628 return true, nil 629 } 630 631 // diffListsOfScalars returns 2 lists, the first one is addList and the second one is deletionList. 632 // Argument diffOptions.IgnoreChangesAndAdditions controls if calculate addList. true means not calculate. 633 // Argument diffOptions.IgnoreDeletions controls if calculate deletionList. true means not calculate. 634 // original may be changed, but modified is guaranteed to not be changed 635 func diffListsOfScalars(original, modified []interface{}, diffOptions DiffOptions) ([]interface{}, []interface{}, error) { 636 modifiedCopy := make([]interface{}, len(modified)) 637 copy(modifiedCopy, modified) 638 // Sort the scalars for easier calculating the diff 639 originalScalars := sortScalars(original) 640 modifiedScalars := sortScalars(modifiedCopy) 641 642 originalIndex, modifiedIndex := 0, 0 643 addList := []interface{}{} 644 deletionList := []interface{}{} 645 646 for { 647 originalInBounds := originalIndex < len(originalScalars) 648 modifiedInBounds := modifiedIndex < len(modifiedScalars) 649 if !originalInBounds && !modifiedInBounds { 650 break 651 } 652 // we need to compare the string representation of the scalar, 653 // because the scalar is an interface which doesn't support either < or > 654 // And that's how func sortScalars compare scalars. 655 var originalString, modifiedString string 656 var originalValue, modifiedValue interface{} 657 if originalInBounds { 658 originalValue = originalScalars[originalIndex] 659 originalString = fmt.Sprintf("%v", originalValue) 660 } 661 if modifiedInBounds { 662 modifiedValue = modifiedScalars[modifiedIndex] 663 modifiedString = fmt.Sprintf("%v", modifiedValue) 664 } 665 666 originalV, modifiedV := compareListValuesAtIndex(originalInBounds, modifiedInBounds, originalString, modifiedString) 667 switch { 668 case originalV == nil && modifiedV == nil: 669 originalIndex++ 670 modifiedIndex++ 671 case originalV != nil && modifiedV == nil: 672 if !diffOptions.IgnoreDeletions { 673 deletionList = append(deletionList, originalValue) 674 } 675 originalIndex++ 676 case originalV == nil && modifiedV != nil: 677 if !diffOptions.IgnoreChangesAndAdditions { 678 addList = append(addList, modifiedValue) 679 } 680 modifiedIndex++ 681 default: 682 return nil, nil, fmt.Errorf("Unexpected returned value from compareListValuesAtIndex: %v and %v", originalV, modifiedV) 683 } 684 } 685 686 return addList, deduplicateScalars(deletionList), nil 687 } 688 689 // If first return value is non-nil, list1 contains an element not present in list2 690 // If second return value is non-nil, list2 contains an element not present in list1 691 func compareListValuesAtIndex(list1Inbounds, list2Inbounds bool, list1Value, list2Value string) (interface{}, interface{}) { 692 bothInBounds := list1Inbounds && list2Inbounds 693 switch { 694 // scalars are identical 695 case bothInBounds && list1Value == list2Value: 696 return nil, nil 697 // only list2 is in bound 698 case !list1Inbounds: 699 fallthrough 700 // list2 has additional scalar 701 case bothInBounds && list1Value > list2Value: 702 return nil, list2Value 703 // only original is in bound 704 case !list2Inbounds: 705 fallthrough 706 // original has additional scalar 707 case bothInBounds && list1Value < list2Value: 708 return list1Value, nil 709 default: 710 return nil, nil 711 } 712 } 713 714 // diffListsOfMaps takes a pair of lists and 715 // returns a (recursive) strategic merge patch list contains additions and changes and 716 // a deletion list contains deletions 717 func diffListsOfMaps(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, error) { 718 patch := make([]interface{}, 0, len(modified)) 719 deletionList := make([]interface{}, 0, len(original)) 720 721 originalSorted, err := sortMergeListsByNameArray(original, schema, mergeKey, false) 722 if err != nil { 723 return nil, nil, err 724 } 725 modifiedSorted, err := sortMergeListsByNameArray(modified, schema, mergeKey, false) 726 if err != nil { 727 return nil, nil, err 728 } 729 730 originalIndex, modifiedIndex := 0, 0 731 for { 732 originalInBounds := originalIndex < len(originalSorted) 733 modifiedInBounds := modifiedIndex < len(modifiedSorted) 734 bothInBounds := originalInBounds && modifiedInBounds 735 if !originalInBounds && !modifiedInBounds { 736 break 737 } 738 739 var originalElementMergeKeyValueString, modifiedElementMergeKeyValueString string 740 var originalElementMergeKeyValue, modifiedElementMergeKeyValue interface{} 741 var originalElement, modifiedElement map[string]interface{} 742 if originalInBounds { 743 originalElement, originalElementMergeKeyValue, err = getMapAndMergeKeyValueByIndex(originalIndex, mergeKey, originalSorted) 744 if err != nil { 745 return nil, nil, err 746 } 747 originalElementMergeKeyValueString = fmt.Sprintf("%v", originalElementMergeKeyValue) 748 } 749 if modifiedInBounds { 750 modifiedElement, modifiedElementMergeKeyValue, err = getMapAndMergeKeyValueByIndex(modifiedIndex, mergeKey, modifiedSorted) 751 if err != nil { 752 return nil, nil, err 753 } 754 modifiedElementMergeKeyValueString = fmt.Sprintf("%v", modifiedElementMergeKeyValue) 755 } 756 757 switch { 758 case bothInBounds && ItemMatchesOriginalAndModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString): 759 // Merge key values are equal, so recurse 760 patchValue, err := diffMaps(originalElement, modifiedElement, schema, diffOptions) 761 if err != nil { 762 return nil, nil, err 763 } 764 if len(patchValue) > 0 { 765 patchValue[mergeKey] = modifiedElementMergeKeyValue 766 patch = append(patch, patchValue) 767 } 768 originalIndex++ 769 modifiedIndex++ 770 // only modified is in bound 771 case !originalInBounds: 772 fallthrough 773 // modified has additional map 774 case bothInBounds && ItemAddedToModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString): 775 if !diffOptions.IgnoreChangesAndAdditions { 776 patch = append(patch, modifiedElement) 777 } 778 modifiedIndex++ 779 // only original is in bound 780 case !modifiedInBounds: 781 fallthrough 782 // original has additional map 783 case bothInBounds && ItemRemovedFromModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString): 784 if !diffOptions.IgnoreDeletions { 785 // Item was deleted, so add delete directive 786 deletionList = append(deletionList, CreateDeleteDirective(mergeKey, originalElementMergeKeyValue)) 787 } 788 originalIndex++ 789 } 790 } 791 792 return patch, deletionList, nil 793 } 794 795 // getMapAndMergeKeyValueByIndex return a map in the list and its merge key value given the index of the map. 796 func getMapAndMergeKeyValueByIndex(index int, mergeKey string, listOfMaps []interface{}) (map[string]interface{}, interface{}, error) { 797 m, ok := listOfMaps[index].(map[string]interface{}) 798 if !ok { 799 return nil, nil, mergepatch.ErrBadArgType(m, listOfMaps[index]) 800 } 801 802 val, ok := m[mergeKey] 803 if !ok { 804 return nil, nil, mergepatch.ErrNoMergeKey(m, mergeKey) 805 } 806 return m, val, nil 807 } 808 809 // StrategicMergePatch applies a strategic merge patch. The patch and the original document 810 // must be json encoded content. A patch can be created from an original and a modified document 811 // by calling CreateStrategicMergePatch. 812 func StrategicMergePatch(original, patch []byte, dataStruct interface{}) ([]byte, error) { 813 schema, err := NewPatchMetaFromStruct(dataStruct) 814 if err != nil { 815 return nil, err 816 } 817 818 return StrategicMergePatchUsingLookupPatchMeta(original, patch, schema) 819 } 820 821 func StrategicMergePatchUsingLookupPatchMeta(original, patch []byte, schema LookupPatchMeta) ([]byte, error) { 822 originalMap, err := handleUnmarshal(original) 823 if err != nil { 824 return nil, err 825 } 826 patchMap, err := handleUnmarshal(patch) 827 if err != nil { 828 return nil, err 829 } 830 831 result, err := StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, schema) 832 if err != nil { 833 return nil, err 834 } 835 836 return json.Marshal(result) 837 } 838 839 func handleUnmarshal(j []byte) (map[string]interface{}, error) { 840 if j == nil { 841 j = []byte("{}") 842 } 843 844 m := map[string]interface{}{} 845 err := json.Unmarshal(j, &m) 846 if err != nil { 847 return nil, mergepatch.ErrBadJSONDoc 848 } 849 return m, nil 850 } 851 852 // StrategicMergeMapPatch applies a strategic merge patch. The original and patch documents 853 // must be JSONMap. A patch can be created from an original and modified document by 854 // calling CreateTwoWayMergeMapPatch. 855 // Warning: the original and patch JSONMap objects are mutated by this function and should not be reused. 856 func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JSONMap, error) { 857 schema, err := NewPatchMetaFromStruct(dataStruct) 858 if err != nil { 859 return nil, err 860 } 861 862 // We need the go struct tags `patchMergeKey` and `patchStrategy` for fields that support a strategic merge patch. 863 // For native resources, we can easily figure out these tags since we know the fields. 864 865 // Because custom resources are decoded as Unstructured and because we're missing the metadata about how to handle 866 // each field in a strategic merge patch, we can't find the go struct tags. Hence, we can't easily do a strategic merge 867 // for custom resources. So we should fail fast and return an error. 868 if _, ok := dataStruct.(*unstructured.Unstructured); ok { 869 return nil, mergepatch.ErrUnsupportedStrategicMergePatchFormat 870 } 871 872 return StrategicMergeMapPatchUsingLookupPatchMeta(original, patch, schema) 873 } 874 875 func StrategicMergeMapPatchUsingLookupPatchMeta(original, patch JSONMap, schema LookupPatchMeta) (JSONMap, error) { 876 mergeOptions := MergeOptions{ 877 MergeParallelList: true, 878 IgnoreUnmatchedNulls: true, 879 } 880 return mergeMap(original, patch, schema, mergeOptions) 881 } 882 883 // MergeStrategicMergeMapPatchUsingLookupPatchMeta merges strategic merge 884 // patches retaining `null` fields and parallel lists. If 2 patches change the 885 // same fields and the latter one will override the former one. If you don't 886 // want that happen, you need to run func MergingMapsHaveConflicts before 887 // merging these patches. Applying the resulting merged merge patch to a JSONMap 888 // yields the same as merging each strategic merge patch to the JSONMap in 889 // succession. 890 func MergeStrategicMergeMapPatchUsingLookupPatchMeta(schema LookupPatchMeta, patches ...JSONMap) (JSONMap, error) { 891 mergeOptions := MergeOptions{ 892 MergeParallelList: false, 893 IgnoreUnmatchedNulls: false, 894 } 895 merged := JSONMap{} 896 var err error 897 for _, patch := range patches { 898 merged, err = mergeMap(merged, patch, schema, mergeOptions) 899 if err != nil { 900 return nil, err 901 } 902 } 903 return merged, nil 904 } 905 906 // handleDirectiveInMergeMap handles the patch directive when merging 2 maps. 907 func handleDirectiveInMergeMap(directive interface{}, patch map[string]interface{}) (map[string]interface{}, error) { 908 if directive == replaceDirective { 909 // If the patch contains "$patch: replace", don't merge it, just use the 910 // patch directly. Later on, we can add a single level replace that only 911 // affects the map that the $patch is in. 912 delete(patch, directiveMarker) 913 return patch, nil 914 } 915 916 if directive == deleteDirective { 917 // If the patch contains "$patch: delete", don't merge it, just return 918 // an empty map. 919 return map[string]interface{}{}, nil 920 } 921 922 return nil, mergepatch.ErrBadPatchType(directive, patch) 923 } 924 925 func containsDirectiveMarker(item interface{}) bool { 926 m, ok := item.(map[string]interface{}) 927 if ok { 928 if _, foundDirectiveMarker := m[directiveMarker]; foundDirectiveMarker { 929 return true 930 } 931 } 932 return false 933 } 934 935 func mergeKeyValueEqual(left, right interface{}, mergeKey string) (bool, error) { 936 if len(mergeKey) == 0 { 937 return left == right, nil 938 } 939 typedLeft, ok := left.(map[string]interface{}) 940 if !ok { 941 return false, mergepatch.ErrBadArgType(typedLeft, left) 942 } 943 typedRight, ok := right.(map[string]interface{}) 944 if !ok { 945 return false, mergepatch.ErrBadArgType(typedRight, right) 946 } 947 mergeKeyLeft, ok := typedLeft[mergeKey] 948 if !ok { 949 return false, mergepatch.ErrNoMergeKey(typedLeft, mergeKey) 950 } 951 mergeKeyRight, ok := typedRight[mergeKey] 952 if !ok { 953 return false, mergepatch.ErrNoMergeKey(typedRight, mergeKey) 954 } 955 return mergeKeyLeft == mergeKeyRight, nil 956 } 957 958 // extractKey trims the prefix and return the original key 959 func extractKey(s, prefix string) (string, error) { 960 substrings := strings.SplitN(s, "/", 2) 961 if len(substrings) <= 1 || substrings[0] != prefix { 962 switch prefix { 963 case deleteFromPrimitiveListDirectivePrefix: 964 return "", mergepatch.ErrBadPatchFormatForPrimitiveList 965 case setElementOrderDirectivePrefix: 966 return "", mergepatch.ErrBadPatchFormatForSetElementOrderList 967 default: 968 return "", fmt.Errorf("fail to find unknown prefix %q in %s\n", prefix, s) 969 } 970 } 971 return substrings[1], nil 972 } 973 974 // validatePatchUsingSetOrderList verifies: 975 // the relative order of any two items in the setOrderList list matches that in the patch list. 976 // the items in the patch list must be a subset or the same as the $setElementOrder list (deletions are ignored). 977 func validatePatchWithSetOrderList(patchList, setOrderList interface{}, mergeKey string) error { 978 typedSetOrderList, ok := setOrderList.([]interface{}) 979 if !ok { 980 return mergepatch.ErrBadPatchFormatForSetElementOrderList 981 } 982 typedPatchList, ok := patchList.([]interface{}) 983 if !ok { 984 return mergepatch.ErrBadPatchFormatForSetElementOrderList 985 } 986 if len(typedSetOrderList) == 0 || len(typedPatchList) == 0 { 987 return nil 988 } 989 990 var nonDeleteList []interface{} 991 var err error 992 if len(mergeKey) > 0 { 993 nonDeleteList, _, err = extractToDeleteItems(typedPatchList) 994 if err != nil { 995 return err 996 } 997 } else { 998 nonDeleteList = typedPatchList 999 } 1000 1001 patchIndex, setOrderIndex := 0, 0 1002 for patchIndex < len(nonDeleteList) && setOrderIndex < len(typedSetOrderList) { 1003 if containsDirectiveMarker(nonDeleteList[patchIndex]) { 1004 patchIndex++ 1005 continue 1006 } 1007 mergeKeyEqual, err := mergeKeyValueEqual(nonDeleteList[patchIndex], typedSetOrderList[setOrderIndex], mergeKey) 1008 if err != nil { 1009 return err 1010 } 1011 if mergeKeyEqual { 1012 patchIndex++ 1013 } 1014 setOrderIndex++ 1015 } 1016 // If patchIndex is inbound but setOrderIndex if out of bound mean there are items mismatching between the patch list and setElementOrder list. 1017 // the second check is a sanity check, and should always be true if the first is true. 1018 if patchIndex < len(nonDeleteList) && setOrderIndex >= len(typedSetOrderList) { 1019 return fmt.Errorf("The order in patch list:\n%v\n doesn't match %s list:\n%v\n", typedPatchList, setElementOrderDirectivePrefix, setOrderList) 1020 } 1021 return nil 1022 } 1023 1024 // preprocessDeletionListForMerging preprocesses the deletion list. 1025 // it returns shouldContinue, isDeletionList, noPrefixKey 1026 func preprocessDeletionListForMerging(key string, original map[string]interface{}, 1027 patchVal interface{}, mergeDeletionList bool) (bool, bool, string, error) { 1028 // If found a parallel list for deletion and we are going to merge the list, 1029 // overwrite the key to the original key and set flag isDeleteList 1030 foundParallelListPrefix := strings.HasPrefix(key, deleteFromPrimitiveListDirectivePrefix) 1031 if foundParallelListPrefix { 1032 if !mergeDeletionList { 1033 original[key] = patchVal 1034 return true, false, "", nil 1035 } 1036 originalKey, err := extractKey(key, deleteFromPrimitiveListDirectivePrefix) 1037 return false, true, originalKey, err 1038 } 1039 return false, false, "", nil 1040 } 1041 1042 // applyRetainKeysDirective looks for a retainKeys directive and applies to original 1043 // - if no directive exists do nothing 1044 // - if directive is found, clear keys in original missing from the directive list 1045 // - validate that all keys present in the patch are present in the retainKeys directive 1046 // note: original may be another patch request, e.g. applying the add+modified patch to the deletions patch. In this case it may have directives 1047 func applyRetainKeysDirective(original, patch map[string]interface{}, options MergeOptions) error { 1048 retainKeysInPatch, foundInPatch := patch[retainKeysDirective] 1049 if !foundInPatch { 1050 return nil 1051 } 1052 // cleanup the directive 1053 delete(patch, retainKeysDirective) 1054 1055 if !options.MergeParallelList { 1056 // If original is actually a patch, make sure the retainKeys directives are the same in both patches if present in both. 1057 // If not present in the original patch, copy from the modified patch. 1058 retainKeysInOriginal, foundInOriginal := original[retainKeysDirective] 1059 if foundInOriginal { 1060 if !reflect.DeepEqual(retainKeysInOriginal, retainKeysInPatch) { 1061 // This error actually should never happen. 1062 return fmt.Errorf("%v and %v are not deep equal: this may happen when calculating the 3-way diff patch", retainKeysInOriginal, retainKeysInPatch) 1063 } 1064 } else { 1065 original[retainKeysDirective] = retainKeysInPatch 1066 } 1067 return nil 1068 } 1069 1070 retainKeysList, ok := retainKeysInPatch.([]interface{}) 1071 if !ok { 1072 return mergepatch.ErrBadPatchFormatForRetainKeys 1073 } 1074 1075 // validate patch to make sure all fields in the patch are present in the retainKeysList. 1076 // The map is used only as a set, the value is never referenced 1077 m := map[interface{}]struct{}{} 1078 for _, v := range retainKeysList { 1079 m[v] = struct{}{} 1080 } 1081 for k, v := range patch { 1082 if v == nil || strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) || 1083 strings.HasPrefix(k, setElementOrderDirectivePrefix) { 1084 continue 1085 } 1086 // If there is an item present in the patch but not in the retainKeys list, 1087 // the patch is invalid. 1088 if _, found := m[k]; !found { 1089 return mergepatch.ErrBadPatchFormatForRetainKeys 1090 } 1091 } 1092 1093 // clear not present fields 1094 for k := range original { 1095 if _, found := m[k]; !found { 1096 delete(original, k) 1097 } 1098 } 1099 return nil 1100 } 1101 1102 // mergePatchIntoOriginal processes $setElementOrder list. 1103 // When not merging the directive, it will make sure $setElementOrder list exist only in original. 1104 // When merging the directive, it will try to find the $setElementOrder list and 1105 // its corresponding patch list, validate it and merge it. 1106 // Then, sort them by the relative order in setElementOrder, patch list and live list. 1107 // The precedence is $setElementOrder > order in patch list > order in live list. 1108 // This function will delete the item after merging it to prevent process it again in the future. 1109 // Ref: https://git.k8s.io/design-proposals-archive/cli/preserve-order-in-strategic-merge-patch.md 1110 func mergePatchIntoOriginal(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) error { 1111 for key, patchV := range patch { 1112 // Do nothing if there is no ordering directive 1113 if !strings.HasPrefix(key, setElementOrderDirectivePrefix) { 1114 continue 1115 } 1116 1117 setElementOrderInPatch := patchV 1118 // Copies directive from the second patch (`patch`) to the first patch (`original`) 1119 // and checks they are equal and delete the directive in the second patch 1120 if !mergeOptions.MergeParallelList { 1121 setElementOrderListInOriginal, ok := original[key] 1122 if ok { 1123 // check if the setElementOrder list in original and the one in patch matches 1124 if !reflect.DeepEqual(setElementOrderListInOriginal, setElementOrderInPatch) { 1125 return mergepatch.ErrBadPatchFormatForSetElementOrderList 1126 } 1127 } else { 1128 // move the setElementOrder list from patch to original 1129 original[key] = setElementOrderInPatch 1130 } 1131 } 1132 delete(patch, key) 1133 1134 var ( 1135 ok bool 1136 originalFieldValue, patchFieldValue, merged []interface{} 1137 patchStrategy string 1138 patchMeta PatchMeta 1139 subschema LookupPatchMeta 1140 ) 1141 typedSetElementOrderList, ok := setElementOrderInPatch.([]interface{}) 1142 if !ok { 1143 return mergepatch.ErrBadArgType(typedSetElementOrderList, setElementOrderInPatch) 1144 } 1145 // Trim the setElementOrderDirectivePrefix to get the key of the list field in original. 1146 originalKey, err := extractKey(key, setElementOrderDirectivePrefix) 1147 if err != nil { 1148 return err 1149 } 1150 // try to find the list with `originalKey` in `original` and `modified` and merge them. 1151 originalList, foundOriginal := original[originalKey] 1152 patchList, foundPatch := patch[originalKey] 1153 if foundOriginal { 1154 originalFieldValue, ok = originalList.([]interface{}) 1155 if !ok { 1156 return mergepatch.ErrBadArgType(originalFieldValue, originalList) 1157 } 1158 } 1159 if foundPatch { 1160 patchFieldValue, ok = patchList.([]interface{}) 1161 if !ok { 1162 return mergepatch.ErrBadArgType(patchFieldValue, patchList) 1163 } 1164 } 1165 subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(originalKey) 1166 if err != nil { 1167 return err 1168 } 1169 _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 1170 if err != nil { 1171 return err 1172 } 1173 // Check for consistency between the element order list and the field it applies to 1174 err = validatePatchWithSetOrderList(patchFieldValue, typedSetElementOrderList, patchMeta.GetPatchMergeKey()) 1175 if err != nil { 1176 return err 1177 } 1178 1179 switch { 1180 case foundOriginal && !foundPatch: 1181 // no change to list contents 1182 merged = originalFieldValue 1183 case !foundOriginal && foundPatch: 1184 // list was added 1185 v, keep := removeDirectives(patchFieldValue) 1186 if !keep { 1187 // Shouldn't be possible since patchFieldValue is a slice 1188 continue 1189 } 1190 1191 merged = v.([]interface{}) 1192 case foundOriginal && foundPatch: 1193 merged, err = mergeSliceHandler(originalList, patchList, subschema, 1194 patchStrategy, patchMeta.GetPatchMergeKey(), false, mergeOptions) 1195 if err != nil { 1196 return err 1197 } 1198 case !foundOriginal && !foundPatch: 1199 continue 1200 } 1201 1202 // Split all items into patch items and server-only items and then enforce the order. 1203 var patchItems, serverOnlyItems []interface{} 1204 if len(patchMeta.GetPatchMergeKey()) == 0 { 1205 // Primitives doesn't need merge key to do partitioning. 1206 patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, typedSetElementOrderList) 1207 1208 } else { 1209 // Maps need merge key to do partitioning. 1210 patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, patchMeta.GetPatchMergeKey()) 1211 if err != nil { 1212 return err 1213 } 1214 } 1215 1216 elementType, err := sliceElementType(originalFieldValue, patchFieldValue) 1217 if err != nil { 1218 return err 1219 } 1220 kind := elementType.Kind() 1221 // normalize merged list 1222 // typedSetElementOrderList contains all the relative order in typedPatchList, 1223 // so don't need to use typedPatchList 1224 both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, patchMeta.GetPatchMergeKey(), kind) 1225 if err != nil { 1226 return err 1227 } 1228 original[originalKey] = both 1229 // delete patch list from patch to prevent process again in the future 1230 delete(patch, originalKey) 1231 } 1232 return nil 1233 } 1234 1235 // partitionPrimitivesByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not. 1236 func partitionPrimitivesByPresentInList(original, partitionBy []interface{}) ([]interface{}, []interface{}) { 1237 patch := make([]interface{}, 0, len(original)) 1238 serverOnly := make([]interface{}, 0, len(original)) 1239 inPatch := map[interface{}]bool{} 1240 for _, v := range partitionBy { 1241 inPatch[v] = true 1242 } 1243 for _, v := range original { 1244 if !inPatch[v] { 1245 serverOnly = append(serverOnly, v) 1246 } else { 1247 patch = append(patch, v) 1248 } 1249 } 1250 return patch, serverOnly 1251 } 1252 1253 // partitionMapsByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not. 1254 func partitionMapsByPresentInList(original, partitionBy []interface{}, mergeKey string) ([]interface{}, []interface{}, error) { 1255 patch := make([]interface{}, 0, len(original)) 1256 serverOnly := make([]interface{}, 0, len(original)) 1257 for _, v := range original { 1258 typedV, ok := v.(map[string]interface{}) 1259 if !ok { 1260 return nil, nil, mergepatch.ErrBadArgType(typedV, v) 1261 } 1262 mergeKeyValue, foundMergeKey := typedV[mergeKey] 1263 if !foundMergeKey { 1264 return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey) 1265 } 1266 _, _, found, err := findMapInSliceBasedOnKeyValue(partitionBy, mergeKey, mergeKeyValue) 1267 if err != nil { 1268 return nil, nil, err 1269 } 1270 if !found { 1271 serverOnly = append(serverOnly, v) 1272 } else { 1273 patch = append(patch, v) 1274 } 1275 } 1276 return patch, serverOnly, nil 1277 } 1278 1279 // Removes directives from an object and returns value to use instead and whether 1280 // or not the field/index should even be kept 1281 // May modify input 1282 func removeDirectives(obj interface{}) (interface{}, bool) { 1283 if obj == nil { 1284 return obj, true 1285 } else if typedV, ok := obj.(map[string]interface{}); ok { 1286 if _, hasDirective := typedV[directiveMarker]; hasDirective { 1287 return nil, false 1288 } 1289 1290 for k, v := range typedV { 1291 var keep bool 1292 typedV[k], keep = removeDirectives(v) 1293 if !keep { 1294 delete(typedV, k) 1295 } 1296 } 1297 return typedV, true 1298 } else if typedV, ok := obj.([]interface{}); ok { 1299 var res []interface{} 1300 if typedV != nil { 1301 // Make sure res is non-nil if patch is non-nil 1302 res = []interface{}{} 1303 } 1304 for _, v := range typedV { 1305 if newV, keep := removeDirectives(v); keep { 1306 res = append(res, newV) 1307 } 1308 } 1309 return res, true 1310 } else { 1311 return obj, true 1312 } 1313 } 1314 1315 // Merge fields from a patch map into the original map. Note: This may modify 1316 // both the original map and the patch because getting a deep copy of a map in 1317 // golang is highly non-trivial. 1318 // flag mergeOptions.MergeParallelList controls if using the parallel list to delete or keeping the list. 1319 // If patch contains any null field (e.g. field_1: null) that is not 1320 // present in original, then to propagate it to the end result use 1321 // mergeOptions.IgnoreUnmatchedNulls == false. 1322 func mergeMap(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) (map[string]interface{}, error) { 1323 if v, ok := patch[directiveMarker]; ok { 1324 return handleDirectiveInMergeMap(v, patch) 1325 } 1326 1327 // nil is an accepted value for original to simplify logic in other places. 1328 // If original is nil, replace it with an empty map and then apply the patch. 1329 if original == nil { 1330 original = map[string]interface{}{} 1331 } 1332 1333 err := applyRetainKeysDirective(original, patch, mergeOptions) 1334 if err != nil { 1335 return nil, err 1336 } 1337 1338 // Process $setElementOrder list and other lists sharing the same key. 1339 // When not merging the directive, it will make sure $setElementOrder list exist only in original. 1340 // When merging the directive, it will process $setElementOrder and its patch list together. 1341 // This function will delete the merged elements from patch so they will not be reprocessed 1342 err = mergePatchIntoOriginal(original, patch, schema, mergeOptions) 1343 if err != nil { 1344 return nil, err 1345 } 1346 1347 // Start merging the patch into the original. 1348 for k, patchV := range patch { 1349 skipProcessing, isDeleteList, noPrefixKey, err := preprocessDeletionListForMerging(k, original, patchV, mergeOptions.MergeParallelList) 1350 if err != nil { 1351 return nil, err 1352 } 1353 if skipProcessing { 1354 continue 1355 } 1356 if len(noPrefixKey) > 0 { 1357 k = noPrefixKey 1358 } 1359 1360 // If the value of this key is null, delete the key if it exists in the 1361 // original. Otherwise, check if we want to preserve it or skip it. 1362 // Preserving the null value is useful when we want to send an explicit 1363 // delete to the API server. 1364 if patchV == nil { 1365 delete(original, k) 1366 if mergeOptions.IgnoreUnmatchedNulls { 1367 continue 1368 } 1369 } 1370 1371 _, ok := original[k] 1372 if !ok { 1373 if !isDeleteList { 1374 // If it's not in the original document, just take the patch value. 1375 if mergeOptions.IgnoreUnmatchedNulls { 1376 discardNullValuesFromPatch(patchV) 1377 } 1378 original[k], ok = removeDirectives(patchV) 1379 if !ok { 1380 delete(original, k) 1381 } 1382 } 1383 continue 1384 } 1385 1386 originalType := reflect.TypeOf(original[k]) 1387 patchType := reflect.TypeOf(patchV) 1388 if originalType != patchType { 1389 if !isDeleteList { 1390 if mergeOptions.IgnoreUnmatchedNulls { 1391 discardNullValuesFromPatch(patchV) 1392 } 1393 original[k], ok = removeDirectives(patchV) 1394 if !ok { 1395 delete(original, k) 1396 } 1397 } 1398 continue 1399 } 1400 // If they're both maps or lists, recurse into the value. 1401 switch originalType.Kind() { 1402 case reflect.Map: 1403 subschema, patchMeta, err2 := schema.LookupPatchMetadataForStruct(k) 1404 if err2 != nil { 1405 return nil, err2 1406 } 1407 _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 1408 if err2 != nil { 1409 return nil, err2 1410 } 1411 original[k], err = mergeMapHandler(original[k], patchV, subschema, patchStrategy, mergeOptions) 1412 case reflect.Slice: 1413 subschema, patchMeta, err2 := schema.LookupPatchMetadataForSlice(k) 1414 if err2 != nil { 1415 return nil, err2 1416 } 1417 _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 1418 if err2 != nil { 1419 return nil, err2 1420 } 1421 original[k], err = mergeSliceHandler(original[k], patchV, subschema, patchStrategy, patchMeta.GetPatchMergeKey(), isDeleteList, mergeOptions) 1422 default: 1423 original[k], ok = removeDirectives(patchV) 1424 if !ok { 1425 // if patchV itself is a directive, then don't keep it 1426 delete(original, k) 1427 } 1428 } 1429 if err != nil { 1430 return nil, err 1431 } 1432 } 1433 return original, nil 1434 } 1435 1436 // discardNullValuesFromPatch discards all null property values from patch. 1437 // It traverses all slices and map types. 1438 func discardNullValuesFromPatch(patchV interface{}) { 1439 switch patchV := patchV.(type) { 1440 case map[string]interface{}: 1441 for k, v := range patchV { 1442 if v == nil { 1443 delete(patchV, k) 1444 } else { 1445 discardNullValuesFromPatch(v) 1446 } 1447 } 1448 case []interface{}: 1449 for _, v := range patchV { 1450 discardNullValuesFromPatch(v) 1451 } 1452 } 1453 } 1454 1455 // mergeMapHandler handles how to merge `patchV` whose key is `key` with `original` respecting 1456 // fieldPatchStrategy and mergeOptions. 1457 func mergeMapHandler(original, patch interface{}, schema LookupPatchMeta, 1458 fieldPatchStrategy string, mergeOptions MergeOptions) (map[string]interface{}, error) { 1459 typedOriginal, typedPatch, err := mapTypeAssertion(original, patch) 1460 if err != nil { 1461 return nil, err 1462 } 1463 1464 if fieldPatchStrategy != replaceDirective { 1465 return mergeMap(typedOriginal, typedPatch, schema, mergeOptions) 1466 } else { 1467 return typedPatch, nil 1468 } 1469 } 1470 1471 // mergeSliceHandler handles how to merge `patchV` whose key is `key` with `original` respecting 1472 // fieldPatchStrategy, fieldPatchMergeKey, isDeleteList and mergeOptions. 1473 func mergeSliceHandler(original, patch interface{}, schema LookupPatchMeta, 1474 fieldPatchStrategy, fieldPatchMergeKey string, isDeleteList bool, mergeOptions MergeOptions) ([]interface{}, error) { 1475 typedOriginal, typedPatch, err := sliceTypeAssertion(original, patch) 1476 if err != nil { 1477 return nil, err 1478 } 1479 1480 // Delete lists are handled the same way regardless of what the field's patch strategy is 1481 if fieldPatchStrategy == mergeDirective || isDeleteList { 1482 return mergeSlice(typedOriginal, typedPatch, schema, fieldPatchMergeKey, mergeOptions, isDeleteList) 1483 } else { 1484 return typedPatch, nil 1485 } 1486 } 1487 1488 // Merge two slices together. Note: This may modify both the original slice and 1489 // the patch because getting a deep copy of a slice in golang is highly 1490 // non-trivial. 1491 func mergeSlice(original, patch []interface{}, schema LookupPatchMeta, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) { 1492 if len(original) == 0 && len(patch) == 0 { 1493 return original, nil 1494 } 1495 1496 // All the values must be of the same type, but not a list. 1497 t, err := sliceElementType(original, patch) 1498 if err != nil { 1499 return nil, err 1500 } 1501 1502 var merged []interface{} 1503 kind := t.Kind() 1504 // If the elements are not maps, merge the slices of scalars. 1505 if kind != reflect.Map { 1506 if mergeOptions.MergeParallelList && isDeleteList { 1507 return deleteFromSlice(original, patch), nil 1508 } 1509 // Maybe in the future add a "concat" mode that doesn't 1510 // deduplicate. 1511 both := append(original, patch...) 1512 merged = deduplicateScalars(both) 1513 1514 } else { 1515 if mergeKey == "" { 1516 return nil, fmt.Errorf("cannot merge lists without merge key for %s", schema.Name()) 1517 } 1518 1519 original, patch, err = mergeSliceWithSpecialElements(original, patch, mergeKey) 1520 if err != nil { 1521 return nil, err 1522 } 1523 1524 merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, schema, mergeOptions) 1525 if err != nil { 1526 return nil, err 1527 } 1528 } 1529 1530 // enforce the order 1531 var patchItems, serverOnlyItems []interface{} 1532 if len(mergeKey) == 0 { 1533 patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, patch) 1534 } else { 1535 patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, patch, mergeKey) 1536 if err != nil { 1537 return nil, err 1538 } 1539 } 1540 return normalizeElementOrder(patchItems, serverOnlyItems, patch, original, mergeKey, kind) 1541 } 1542 1543 // mergeSliceWithSpecialElements handles special elements with directiveMarker 1544 // before merging the slices. It returns a updated `original` and a patch without special elements. 1545 // original and patch must be slices of maps, they should be checked before calling this function. 1546 func mergeSliceWithSpecialElements(original, patch []interface{}, mergeKey string) ([]interface{}, []interface{}, error) { 1547 patchWithoutSpecialElements := []interface{}{} 1548 replace := false 1549 for _, v := range patch { 1550 typedV := v.(map[string]interface{}) 1551 patchType, ok := typedV[directiveMarker] 1552 if !ok { 1553 patchWithoutSpecialElements = append(patchWithoutSpecialElements, v) 1554 } else { 1555 switch patchType { 1556 case deleteDirective: 1557 mergeValue, ok := typedV[mergeKey] 1558 if ok { 1559 var err error 1560 original, err = deleteMatchingEntries(original, mergeKey, mergeValue) 1561 if err != nil { 1562 return nil, nil, err 1563 } 1564 } else { 1565 return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey) 1566 } 1567 case replaceDirective: 1568 replace = true 1569 // Continue iterating through the array to prune any other $patch elements. 1570 case mergeDirective: 1571 return nil, nil, fmt.Errorf("merging lists cannot yet be specified in the patch") 1572 default: 1573 return nil, nil, mergepatch.ErrBadPatchType(patchType, typedV) 1574 } 1575 } 1576 } 1577 if replace { 1578 return patchWithoutSpecialElements, nil, nil 1579 } 1580 return original, patchWithoutSpecialElements, nil 1581 } 1582 1583 // delete all matching entries (based on merge key) from a merging list 1584 func deleteMatchingEntries(original []interface{}, mergeKey string, mergeValue interface{}) ([]interface{}, error) { 1585 for { 1586 _, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue) 1587 if err != nil { 1588 return nil, err 1589 } 1590 1591 if !found { 1592 break 1593 } 1594 // Delete the element at originalKey. 1595 original = append(original[:originalKey], original[originalKey+1:]...) 1596 } 1597 return original, nil 1598 } 1599 1600 // mergeSliceWithoutSpecialElements merges slices with non-special elements. 1601 // original and patch must be slices of maps, they should be checked before calling this function. 1602 func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, schema LookupPatchMeta, mergeOptions MergeOptions) ([]interface{}, error) { 1603 for _, v := range patch { 1604 typedV := v.(map[string]interface{}) 1605 mergeValue, ok := typedV[mergeKey] 1606 if !ok { 1607 return nil, mergepatch.ErrNoMergeKey(typedV, mergeKey) 1608 } 1609 1610 // If we find a value with this merge key value in original, merge the 1611 // maps. Otherwise append onto original. 1612 originalMap, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue) 1613 if err != nil { 1614 return nil, err 1615 } 1616 1617 if found { 1618 var mergedMaps interface{} 1619 var err error 1620 // Merge into original. 1621 mergedMaps, err = mergeMap(originalMap, typedV, schema, mergeOptions) 1622 if err != nil { 1623 return nil, err 1624 } 1625 1626 original[originalKey] = mergedMaps 1627 } else { 1628 original = append(original, v) 1629 } 1630 } 1631 return original, nil 1632 } 1633 1634 // deleteFromSlice uses the parallel list to delete the items in a list of scalars 1635 func deleteFromSlice(current, toDelete []interface{}) []interface{} { 1636 toDeleteMap := map[interface{}]interface{}{} 1637 processed := make([]interface{}, 0, len(current)) 1638 for _, v := range toDelete { 1639 toDeleteMap[v] = true 1640 } 1641 for _, v := range current { 1642 if _, found := toDeleteMap[v]; !found { 1643 processed = append(processed, v) 1644 } 1645 } 1646 return processed 1647 } 1648 1649 // This method no longer panics if any element of the slice is not a map. 1650 func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{}) (map[string]interface{}, int, bool, error) { 1651 for k, v := range m { 1652 typedV, ok := v.(map[string]interface{}) 1653 if !ok { 1654 return nil, 0, false, fmt.Errorf("value for key %v is not a map", k) 1655 } 1656 1657 valueToMatch, ok := typedV[key] 1658 if ok && valueToMatch == value { 1659 return typedV, k, true, nil 1660 } 1661 } 1662 1663 return nil, 0, false, nil 1664 } 1665 1666 // This function takes a JSON map and sorts all the lists that should be merged 1667 // by key. This is needed by tests because in JSON, list order is significant, 1668 // but in Strategic Merge Patch, merge lists do not have significant order. 1669 // Sorting the lists allows for order-insensitive comparison of patched maps. 1670 func sortMergeListsByName(mapJSON []byte, schema LookupPatchMeta) ([]byte, error) { 1671 var m map[string]interface{} 1672 err := json.Unmarshal(mapJSON, &m) 1673 if err != nil { 1674 return nil, mergepatch.ErrBadJSONDoc 1675 } 1676 1677 newM, err := sortMergeListsByNameMap(m, schema) 1678 if err != nil { 1679 return nil, err 1680 } 1681 1682 return json.Marshal(newM) 1683 } 1684 1685 // Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in a map. 1686 func sortMergeListsByNameMap(s map[string]interface{}, schema LookupPatchMeta) (map[string]interface{}, error) { 1687 newS := map[string]interface{}{} 1688 for k, v := range s { 1689 if k == retainKeysDirective { 1690 typedV, ok := v.([]interface{}) 1691 if !ok { 1692 return nil, mergepatch.ErrBadPatchFormatForRetainKeys 1693 } 1694 v = sortScalars(typedV) 1695 } else if strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) { 1696 typedV, ok := v.([]interface{}) 1697 if !ok { 1698 return nil, mergepatch.ErrBadPatchFormatForPrimitiveList 1699 } 1700 v = sortScalars(typedV) 1701 } else if strings.HasPrefix(k, setElementOrderDirectivePrefix) { 1702 _, ok := v.([]interface{}) 1703 if !ok { 1704 return nil, mergepatch.ErrBadPatchFormatForSetElementOrderList 1705 } 1706 } else if k != directiveMarker { 1707 // recurse for map and slice. 1708 switch typedV := v.(type) { 1709 case map[string]interface{}: 1710 subschema, _, err := schema.LookupPatchMetadataForStruct(k) 1711 if err != nil { 1712 return nil, err 1713 } 1714 v, err = sortMergeListsByNameMap(typedV, subschema) 1715 if err != nil { 1716 return nil, err 1717 } 1718 case []interface{}: 1719 subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k) 1720 if err != nil { 1721 return nil, err 1722 } 1723 _, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 1724 if err != nil { 1725 return nil, err 1726 } 1727 if patchStrategy == mergeDirective { 1728 var err error 1729 v, err = sortMergeListsByNameArray(typedV, subschema, patchMeta.GetPatchMergeKey(), true) 1730 if err != nil { 1731 return nil, err 1732 } 1733 } 1734 } 1735 } 1736 1737 newS[k] = v 1738 } 1739 1740 return newS, nil 1741 } 1742 1743 // Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in an array. 1744 func sortMergeListsByNameArray(s []interface{}, schema LookupPatchMeta, mergeKey string, recurse bool) ([]interface{}, error) { 1745 if len(s) == 0 { 1746 return s, nil 1747 } 1748 1749 // We don't support lists of lists yet. 1750 t, err := sliceElementType(s) 1751 if err != nil { 1752 return nil, err 1753 } 1754 1755 // If the elements are not maps... 1756 if t.Kind() != reflect.Map { 1757 // Sort the elements, because they may have been merged out of order. 1758 return deduplicateAndSortScalars(s), nil 1759 } 1760 1761 // Elements are maps - if one of the keys of the map is a map or a 1762 // list, we may need to recurse into it. 1763 newS := []interface{}{} 1764 for _, elem := range s { 1765 if recurse { 1766 typedElem := elem.(map[string]interface{}) 1767 newElem, err := sortMergeListsByNameMap(typedElem, schema) 1768 if err != nil { 1769 return nil, err 1770 } 1771 1772 newS = append(newS, newElem) 1773 } else { 1774 newS = append(newS, elem) 1775 } 1776 } 1777 1778 // Sort the maps. 1779 newS = sortMapsBasedOnField(newS, mergeKey) 1780 return newS, nil 1781 } 1782 1783 func sortMapsBasedOnField(m []interface{}, fieldName string) []interface{} { 1784 mapM := mapSliceFromSlice(m) 1785 ss := SortableSliceOfMaps{mapM, fieldName} 1786 sort.Sort(ss) 1787 newS := sliceFromMapSlice(ss.s) 1788 return newS 1789 } 1790 1791 func mapSliceFromSlice(m []interface{}) []map[string]interface{} { 1792 newM := []map[string]interface{}{} 1793 for _, v := range m { 1794 vt := v.(map[string]interface{}) 1795 newM = append(newM, vt) 1796 } 1797 1798 return newM 1799 } 1800 1801 func sliceFromMapSlice(s []map[string]interface{}) []interface{} { 1802 newS := []interface{}{} 1803 for _, v := range s { 1804 newS = append(newS, v) 1805 } 1806 1807 return newS 1808 } 1809 1810 type SortableSliceOfMaps struct { 1811 s []map[string]interface{} 1812 k string // key to sort on 1813 } 1814 1815 func (ss SortableSliceOfMaps) Len() int { 1816 return len(ss.s) 1817 } 1818 1819 func (ss SortableSliceOfMaps) Less(i, j int) bool { 1820 iStr := fmt.Sprintf("%v", ss.s[i][ss.k]) 1821 jStr := fmt.Sprintf("%v", ss.s[j][ss.k]) 1822 return sort.StringsAreSorted([]string{iStr, jStr}) 1823 } 1824 1825 func (ss SortableSliceOfMaps) Swap(i, j int) { 1826 tmp := ss.s[i] 1827 ss.s[i] = ss.s[j] 1828 ss.s[j] = tmp 1829 } 1830 1831 func deduplicateAndSortScalars(s []interface{}) []interface{} { 1832 s = deduplicateScalars(s) 1833 return sortScalars(s) 1834 } 1835 1836 func sortScalars(s []interface{}) []interface{} { 1837 ss := SortableSliceOfScalars{s} 1838 sort.Sort(ss) 1839 return ss.s 1840 } 1841 1842 func deduplicateScalars(s []interface{}) []interface{} { 1843 // Clever algorithm to deduplicate. 1844 length := len(s) - 1 1845 for i := 0; i < length; i++ { 1846 for j := i + 1; j <= length; j++ { 1847 if s[i] == s[j] { 1848 s[j] = s[length] 1849 s = s[0:length] 1850 length-- 1851 j-- 1852 } 1853 } 1854 } 1855 1856 return s 1857 } 1858 1859 type SortableSliceOfScalars struct { 1860 s []interface{} 1861 } 1862 1863 func (ss SortableSliceOfScalars) Len() int { 1864 return len(ss.s) 1865 } 1866 1867 func (ss SortableSliceOfScalars) Less(i, j int) bool { 1868 iStr := fmt.Sprintf("%v", ss.s[i]) 1869 jStr := fmt.Sprintf("%v", ss.s[j]) 1870 return sort.StringsAreSorted([]string{iStr, jStr}) 1871 } 1872 1873 func (ss SortableSliceOfScalars) Swap(i, j int) { 1874 tmp := ss.s[i] 1875 ss.s[i] = ss.s[j] 1876 ss.s[j] = tmp 1877 } 1878 1879 // Returns the type of the elements of N slice(s). If the type is different, 1880 // another slice or undefined, returns an error. 1881 func sliceElementType(slices ...[]interface{}) (reflect.Type, error) { 1882 var prevType reflect.Type 1883 for _, s := range slices { 1884 // Go through elements of all given slices and make sure they are all the same type. 1885 for _, v := range s { 1886 currentType := reflect.TypeOf(v) 1887 if prevType == nil { 1888 prevType = currentType 1889 // We don't support lists of lists yet. 1890 if prevType.Kind() == reflect.Slice { 1891 return nil, mergepatch.ErrNoListOfLists 1892 } 1893 } else { 1894 if prevType != currentType { 1895 return nil, fmt.Errorf("list element types are not identical: %v", fmt.Sprint(slices)) 1896 } 1897 prevType = currentType 1898 } 1899 } 1900 } 1901 1902 if prevType == nil { 1903 return nil, fmt.Errorf("no elements in any of the given slices") 1904 } 1905 1906 return prevType, nil 1907 } 1908 1909 // MergingMapsHaveConflicts returns true if the left and right JSON interface 1910 // objects overlap with different values in any key. All keys are required to be 1911 // strings. Since patches of the same Type have congruent keys, this is valid 1912 // for multiple patch types. This method supports strategic merge patch semantics. 1913 func MergingMapsHaveConflicts(left, right map[string]interface{}, schema LookupPatchMeta) (bool, error) { 1914 return mergingMapFieldsHaveConflicts(left, right, schema, "", "") 1915 } 1916 1917 func mergingMapFieldsHaveConflicts( 1918 left, right interface{}, 1919 schema LookupPatchMeta, 1920 fieldPatchStrategy, fieldPatchMergeKey string, 1921 ) (bool, error) { 1922 switch leftType := left.(type) { 1923 case map[string]interface{}: 1924 rightType, ok := right.(map[string]interface{}) 1925 if !ok { 1926 return true, nil 1927 } 1928 leftMarker, okLeft := leftType[directiveMarker] 1929 rightMarker, okRight := rightType[directiveMarker] 1930 // if one or the other has a directive marker, 1931 // then we need to consider that before looking at the individual keys, 1932 // since a directive operates on the whole map. 1933 if okLeft || okRight { 1934 // if one has a directive marker and the other doesn't, 1935 // then we have a conflict, since one is deleting or replacing the whole map, 1936 // and the other is doing things to individual keys. 1937 if okLeft != okRight { 1938 return true, nil 1939 } 1940 // if they both have markers, but they are not the same directive, 1941 // then we have a conflict because they're doing different things to the map. 1942 if leftMarker != rightMarker { 1943 return true, nil 1944 } 1945 } 1946 if fieldPatchStrategy == replaceDirective { 1947 return false, nil 1948 } 1949 // Check the individual keys. 1950 return mapsHaveConflicts(leftType, rightType, schema) 1951 1952 case []interface{}: 1953 rightType, ok := right.([]interface{}) 1954 if !ok { 1955 return true, nil 1956 } 1957 return slicesHaveConflicts(leftType, rightType, schema, fieldPatchStrategy, fieldPatchMergeKey) 1958 case string, float64, bool, int64, nil: 1959 return !reflect.DeepEqual(left, right), nil 1960 default: 1961 return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left)) 1962 } 1963 } 1964 1965 func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) { 1966 for key, leftValue := range typedLeft { 1967 if key != directiveMarker && key != retainKeysDirective { 1968 if rightValue, ok := typedRight[key]; ok { 1969 var subschema LookupPatchMeta 1970 var patchMeta PatchMeta 1971 var patchStrategy string 1972 var err error 1973 switch leftValue.(type) { 1974 case []interface{}: 1975 subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(key) 1976 if err != nil { 1977 return true, err 1978 } 1979 _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies) 1980 if err != nil { 1981 return true, err 1982 } 1983 case map[string]interface{}: 1984 subschema, patchMeta, err = schema.LookupPatchMetadataForStruct(key) 1985 if err != nil { 1986 return true, err 1987 } 1988 _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies) 1989 if err != nil { 1990 return true, err 1991 } 1992 } 1993 1994 if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, 1995 subschema, patchStrategy, patchMeta.GetPatchMergeKey()); hasConflicts { 1996 return true, err 1997 } 1998 } 1999 } 2000 } 2001 2002 return false, nil 2003 } 2004 2005 func slicesHaveConflicts( 2006 typedLeft, typedRight []interface{}, 2007 schema LookupPatchMeta, 2008 fieldPatchStrategy, fieldPatchMergeKey string, 2009 ) (bool, error) { 2010 elementType, err := sliceElementType(typedLeft, typedRight) 2011 if err != nil { 2012 return true, err 2013 } 2014 2015 if fieldPatchStrategy == mergeDirective { 2016 // Merging lists of scalars have no conflicts by definition 2017 // So we only need to check further if the elements are maps 2018 if elementType.Kind() != reflect.Map { 2019 return false, nil 2020 } 2021 2022 // Build a map for each slice and then compare the two maps 2023 leftMap, err := sliceOfMapsToMapOfMaps(typedLeft, fieldPatchMergeKey) 2024 if err != nil { 2025 return true, err 2026 } 2027 2028 rightMap, err := sliceOfMapsToMapOfMaps(typedRight, fieldPatchMergeKey) 2029 if err != nil { 2030 return true, err 2031 } 2032 2033 return mapsOfMapsHaveConflicts(leftMap, rightMap, schema) 2034 } 2035 2036 // Either we don't have type information, or these are non-merging lists 2037 if len(typedLeft) != len(typedRight) { 2038 return true, nil 2039 } 2040 2041 // Sort scalar slices to prevent ordering issues 2042 // We have no way to sort non-merging lists of maps 2043 if elementType.Kind() != reflect.Map { 2044 typedLeft = deduplicateAndSortScalars(typedLeft) 2045 typedRight = deduplicateAndSortScalars(typedRight) 2046 } 2047 2048 // Compare the slices element by element in order 2049 // This test will fail if the slices are not sorted 2050 for i := range typedLeft { 2051 if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], schema, "", ""); hasConflicts { 2052 return true, err 2053 } 2054 } 2055 2056 return false, nil 2057 } 2058 2059 func sliceOfMapsToMapOfMaps(slice []interface{}, mergeKey string) (map[string]interface{}, error) { 2060 result := make(map[string]interface{}, len(slice)) 2061 for _, value := range slice { 2062 typedValue, ok := value.(map[string]interface{}) 2063 if !ok { 2064 return nil, fmt.Errorf("invalid element type in merging list:%v", slice) 2065 } 2066 2067 mergeValue, ok := typedValue[mergeKey] 2068 if !ok { 2069 return nil, fmt.Errorf("cannot find merge key `%s` in merging list element:%v", mergeKey, typedValue) 2070 } 2071 2072 result[fmt.Sprintf("%s", mergeValue)] = typedValue 2073 } 2074 2075 return result, nil 2076 } 2077 2078 func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) { 2079 for key, leftValue := range typedLeft { 2080 if rightValue, ok := typedRight[key]; ok { 2081 if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, schema, "", ""); hasConflicts { 2082 return true, err 2083 } 2084 } 2085 } 2086 2087 return false, nil 2088 } 2089 2090 // CreateThreeWayMergePatch reconciles a modified configuration with an original configuration, 2091 // while preserving any changes or deletions made to the original configuration in the interim, 2092 // and not overridden by the current configuration. All three documents must be passed to the 2093 // method as json encoded content. It will return a strategic merge patch, or an error if any 2094 // of the documents is invalid, or if there are any preconditions that fail against the modified 2095 // configuration, or, if overwrite is false and there are conflicts between the modified and current 2096 // configurations. Conflicts are defined as keys changed differently from original to modified 2097 // than from original to current. In other words, a conflict occurs if modified changes any key 2098 // in a way that is different from how it is changed in current (e.g., deleting it, changing its 2099 // value). We also propagate values fields that do not exist in original but are explicitly 2100 // defined in modified. 2101 func CreateThreeWayMergePatch(original, modified, current []byte, schema LookupPatchMeta, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) { 2102 originalMap := map[string]interface{}{} 2103 if len(original) > 0 { 2104 if err := json.Unmarshal(original, &originalMap); err != nil { 2105 return nil, mergepatch.ErrBadJSONDoc 2106 } 2107 } 2108 2109 modifiedMap := map[string]interface{}{} 2110 if len(modified) > 0 { 2111 if err := json.Unmarshal(modified, &modifiedMap); err != nil { 2112 return nil, mergepatch.ErrBadJSONDoc 2113 } 2114 } 2115 2116 currentMap := map[string]interface{}{} 2117 if len(current) > 0 { 2118 if err := json.Unmarshal(current, ¤tMap); err != nil { 2119 return nil, mergepatch.ErrBadJSONDoc 2120 } 2121 } 2122 2123 // The patch is the difference from current to modified without deletions, plus deletions 2124 // from original to modified. To find it, we compute deletions, which are the deletions from 2125 // original to modified, and delta, which is the difference from current to modified without 2126 // deletions, and then apply delta to deletions as a patch, which should be strictly additive. 2127 deltaMapDiffOptions := DiffOptions{ 2128 IgnoreDeletions: true, 2129 SetElementOrder: true, 2130 } 2131 deltaMap, err := diffMaps(currentMap, modifiedMap, schema, deltaMapDiffOptions) 2132 if err != nil { 2133 return nil, err 2134 } 2135 deletionsMapDiffOptions := DiffOptions{ 2136 SetElementOrder: true, 2137 IgnoreChangesAndAdditions: true, 2138 } 2139 deletionsMap, err := diffMaps(originalMap, modifiedMap, schema, deletionsMapDiffOptions) 2140 if err != nil { 2141 return nil, err 2142 } 2143 2144 mergeOptions := MergeOptions{} 2145 patchMap, err := mergeMap(deletionsMap, deltaMap, schema, mergeOptions) 2146 if err != nil { 2147 return nil, err 2148 } 2149 2150 // Apply the preconditions to the patch, and return an error if any of them fail. 2151 for _, fn := range fns { 2152 if !fn(patchMap) { 2153 return nil, mergepatch.NewErrPreconditionFailed(patchMap) 2154 } 2155 } 2156 2157 // If overwrite is false, and the patch contains any keys that were changed differently, 2158 // then return a conflict error. 2159 if !overwrite { 2160 changeMapDiffOptions := DiffOptions{} 2161 changedMap, err := diffMaps(originalMap, currentMap, schema, changeMapDiffOptions) 2162 if err != nil { 2163 return nil, err 2164 } 2165 2166 hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, schema) 2167 if err != nil { 2168 return nil, err 2169 } 2170 2171 if hasConflicts { 2172 return nil, mergepatch.NewErrConflict(mergepatch.ToYAMLOrError(patchMap), mergepatch.ToYAMLOrError(changedMap)) 2173 } 2174 } 2175 2176 return json.Marshal(patchMap) 2177 } 2178 2179 func ItemAddedToModifiedSlice(original, modified string) bool { return original > modified } 2180 2181 func ItemRemovedFromModifiedSlice(original, modified string) bool { return original < modified } 2182 2183 func ItemMatchesOriginalAndModifiedSlice(original, modified string) bool { return original == modified } 2184 2185 func CreateDeleteDirective(mergeKey string, mergeKeyValue interface{}) map[string]interface{} { 2186 return map[string]interface{}{mergeKey: mergeKeyValue, directiveMarker: deleteDirective} 2187 } 2188 2189 func mapTypeAssertion(original, patch interface{}) (map[string]interface{}, map[string]interface{}, error) { 2190 typedOriginal, ok := original.(map[string]interface{}) 2191 if !ok { 2192 return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original) 2193 } 2194 typedPatch, ok := patch.(map[string]interface{}) 2195 if !ok { 2196 return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch) 2197 } 2198 return typedOriginal, typedPatch, nil 2199 } 2200 2201 func sliceTypeAssertion(original, patch interface{}) ([]interface{}, []interface{}, error) { 2202 typedOriginal, ok := original.([]interface{}) 2203 if !ok { 2204 return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original) 2205 } 2206 typedPatch, ok := patch.([]interface{}) 2207 if !ok { 2208 return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch) 2209 } 2210 return typedOriginal, typedPatch, nil 2211 } 2212 2213 // extractRetainKeysPatchStrategy process patch strategy, which is a string may contains multiple 2214 // patch strategies separated by ",". It returns a boolean var indicating if it has 2215 // retainKeys strategies and a string for the other strategy. 2216 func extractRetainKeysPatchStrategy(strategies []string) (bool, string, error) { 2217 switch len(strategies) { 2218 case 0: 2219 return false, "", nil 2220 case 1: 2221 singleStrategy := strategies[0] 2222 switch singleStrategy { 2223 case retainKeysStrategy: 2224 return true, "", nil 2225 default: 2226 return false, singleStrategy, nil 2227 } 2228 case 2: 2229 switch { 2230 case strategies[0] == retainKeysStrategy: 2231 return true, strategies[1], nil 2232 case strategies[1] == retainKeysStrategy: 2233 return true, strategies[0], nil 2234 default: 2235 return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies) 2236 } 2237 default: 2238 return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies) 2239 } 2240 } 2241 2242 // hasAdditionalNewField returns if original map has additional key with non-nil value than modified. 2243 func hasAdditionalNewField(original, modified map[string]interface{}) bool { 2244 for k, v := range original { 2245 if v == nil { 2246 continue 2247 } 2248 if _, found := modified[k]; !found { 2249 return true 2250 } 2251 } 2252 return false 2253 }