github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/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 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured" 26 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/json" 27 "github.com/spotmaxtech/k8s-apimachinery-v0260/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/community/contributors/design-proposals/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 merged = patchFieldValue 1186 case foundOriginal && foundPatch: 1187 merged, err = mergeSliceHandler(originalList, patchList, subschema, 1188 patchStrategy, patchMeta.GetPatchMergeKey(), false, mergeOptions) 1189 if err != nil { 1190 return err 1191 } 1192 case !foundOriginal && !foundPatch: 1193 continue 1194 } 1195 1196 // Split all items into patch items and server-only items and then enforce the order. 1197 var patchItems, serverOnlyItems []interface{} 1198 if len(patchMeta.GetPatchMergeKey()) == 0 { 1199 // Primitives doesn't need merge key to do partitioning. 1200 patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, typedSetElementOrderList) 1201 1202 } else { 1203 // Maps need merge key to do partitioning. 1204 patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, patchMeta.GetPatchMergeKey()) 1205 if err != nil { 1206 return err 1207 } 1208 } 1209 1210 elementType, err := sliceElementType(originalFieldValue, patchFieldValue) 1211 if err != nil { 1212 return err 1213 } 1214 kind := elementType.Kind() 1215 // normalize merged list 1216 // typedSetElementOrderList contains all the relative order in typedPatchList, 1217 // so don't need to use typedPatchList 1218 both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, patchMeta.GetPatchMergeKey(), kind) 1219 if err != nil { 1220 return err 1221 } 1222 original[originalKey] = both 1223 // delete patch list from patch to prevent process again in the future 1224 delete(patch, originalKey) 1225 } 1226 return nil 1227 } 1228 1229 // partitionPrimitivesByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not. 1230 func partitionPrimitivesByPresentInList(original, partitionBy []interface{}) ([]interface{}, []interface{}) { 1231 patch := make([]interface{}, 0, len(original)) 1232 serverOnly := make([]interface{}, 0, len(original)) 1233 inPatch := map[interface{}]bool{} 1234 for _, v := range partitionBy { 1235 inPatch[v] = true 1236 } 1237 for _, v := range original { 1238 if !inPatch[v] { 1239 serverOnly = append(serverOnly, v) 1240 } else { 1241 patch = append(patch, v) 1242 } 1243 } 1244 return patch, serverOnly 1245 } 1246 1247 // partitionMapsByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not. 1248 func partitionMapsByPresentInList(original, partitionBy []interface{}, mergeKey string) ([]interface{}, []interface{}, error) { 1249 patch := make([]interface{}, 0, len(original)) 1250 serverOnly := make([]interface{}, 0, len(original)) 1251 for _, v := range original { 1252 typedV, ok := v.(map[string]interface{}) 1253 if !ok { 1254 return nil, nil, mergepatch.ErrBadArgType(typedV, v) 1255 } 1256 mergeKeyValue, foundMergeKey := typedV[mergeKey] 1257 if !foundMergeKey { 1258 return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey) 1259 } 1260 _, _, found, err := findMapInSliceBasedOnKeyValue(partitionBy, mergeKey, mergeKeyValue) 1261 if err != nil { 1262 return nil, nil, err 1263 } 1264 if !found { 1265 serverOnly = append(serverOnly, v) 1266 } else { 1267 patch = append(patch, v) 1268 } 1269 } 1270 return patch, serverOnly, nil 1271 } 1272 1273 // Merge fields from a patch map into the original map. Note: This may modify 1274 // both the original map and the patch because getting a deep copy of a map in 1275 // golang is highly non-trivial. 1276 // flag mergeOptions.MergeParallelList controls if using the parallel list to delete or keeping the list. 1277 // If patch contains any null field (e.g. field_1: null) that is not 1278 // present in original, then to propagate it to the end result use 1279 // mergeOptions.IgnoreUnmatchedNulls == false. 1280 func mergeMap(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) (map[string]interface{}, error) { 1281 if v, ok := patch[directiveMarker]; ok { 1282 return handleDirectiveInMergeMap(v, patch) 1283 } 1284 1285 // nil is an accepted value for original to simplify logic in other places. 1286 // If original is nil, replace it with an empty map and then apply the patch. 1287 if original == nil { 1288 original = map[string]interface{}{} 1289 } 1290 1291 err := applyRetainKeysDirective(original, patch, mergeOptions) 1292 if err != nil { 1293 return nil, err 1294 } 1295 1296 // Process $setElementOrder list and other lists sharing the same key. 1297 // When not merging the directive, it will make sure $setElementOrder list exist only in original. 1298 // When merging the directive, it will process $setElementOrder and its patch list together. 1299 // This function will delete the merged elements from patch so they will not be reprocessed 1300 err = mergePatchIntoOriginal(original, patch, schema, mergeOptions) 1301 if err != nil { 1302 return nil, err 1303 } 1304 1305 // Start merging the patch into the original. 1306 for k, patchV := range patch { 1307 skipProcessing, isDeleteList, noPrefixKey, err := preprocessDeletionListForMerging(k, original, patchV, mergeOptions.MergeParallelList) 1308 if err != nil { 1309 return nil, err 1310 } 1311 if skipProcessing { 1312 continue 1313 } 1314 if len(noPrefixKey) > 0 { 1315 k = noPrefixKey 1316 } 1317 1318 // If the value of this key is null, delete the key if it exists in the 1319 // original. Otherwise, check if we want to preserve it or skip it. 1320 // Preserving the null value is useful when we want to send an explicit 1321 // delete to the API server. 1322 if patchV == nil { 1323 delete(original, k) 1324 if mergeOptions.IgnoreUnmatchedNulls { 1325 continue 1326 } 1327 } 1328 1329 _, ok := original[k] 1330 if !ok { 1331 if !isDeleteList { 1332 // If it's not in the original document, just take the patch value. 1333 if mergeOptions.IgnoreUnmatchedNulls { 1334 discardNullValuesFromPatch(patchV) 1335 } 1336 original[k] = patchV 1337 } 1338 continue 1339 } 1340 1341 originalType := reflect.TypeOf(original[k]) 1342 patchType := reflect.TypeOf(patchV) 1343 if originalType != patchType { 1344 if !isDeleteList { 1345 if mergeOptions.IgnoreUnmatchedNulls { 1346 discardNullValuesFromPatch(patchV) 1347 } 1348 original[k] = patchV 1349 } 1350 continue 1351 } 1352 // If they're both maps or lists, recurse into the value. 1353 switch originalType.Kind() { 1354 case reflect.Map: 1355 subschema, patchMeta, err2 := schema.LookupPatchMetadataForStruct(k) 1356 if err2 != nil { 1357 return nil, err2 1358 } 1359 _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 1360 if err2 != nil { 1361 return nil, err2 1362 } 1363 original[k], err = mergeMapHandler(original[k], patchV, subschema, patchStrategy, mergeOptions) 1364 case reflect.Slice: 1365 subschema, patchMeta, err2 := schema.LookupPatchMetadataForSlice(k) 1366 if err2 != nil { 1367 return nil, err2 1368 } 1369 _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 1370 if err2 != nil { 1371 return nil, err2 1372 } 1373 original[k], err = mergeSliceHandler(original[k], patchV, subschema, patchStrategy, patchMeta.GetPatchMergeKey(), isDeleteList, mergeOptions) 1374 default: 1375 original[k] = patchV 1376 } 1377 if err != nil { 1378 return nil, err 1379 } 1380 } 1381 return original, nil 1382 } 1383 1384 // discardNullValuesFromPatch discards all null property values from patch. 1385 // It traverses all slices and map types. 1386 func discardNullValuesFromPatch(patchV interface{}) { 1387 switch patchV := patchV.(type) { 1388 case map[string]interface{}: 1389 for k, v := range patchV { 1390 if v == nil { 1391 delete(patchV, k) 1392 } else { 1393 discardNullValuesFromPatch(v) 1394 } 1395 } 1396 case []interface{}: 1397 for _, v := range patchV { 1398 discardNullValuesFromPatch(v) 1399 } 1400 } 1401 } 1402 1403 // mergeMapHandler handles how to merge `patchV` whose key is `key` with `original` respecting 1404 // fieldPatchStrategy and mergeOptions. 1405 func mergeMapHandler(original, patch interface{}, schema LookupPatchMeta, 1406 fieldPatchStrategy string, mergeOptions MergeOptions) (map[string]interface{}, error) { 1407 typedOriginal, typedPatch, err := mapTypeAssertion(original, patch) 1408 if err != nil { 1409 return nil, err 1410 } 1411 1412 if fieldPatchStrategy != replaceDirective { 1413 return mergeMap(typedOriginal, typedPatch, schema, mergeOptions) 1414 } else { 1415 return typedPatch, nil 1416 } 1417 } 1418 1419 // mergeSliceHandler handles how to merge `patchV` whose key is `key` with `original` respecting 1420 // fieldPatchStrategy, fieldPatchMergeKey, isDeleteList and mergeOptions. 1421 func mergeSliceHandler(original, patch interface{}, schema LookupPatchMeta, 1422 fieldPatchStrategy, fieldPatchMergeKey string, isDeleteList bool, mergeOptions MergeOptions) ([]interface{}, error) { 1423 typedOriginal, typedPatch, err := sliceTypeAssertion(original, patch) 1424 if err != nil { 1425 return nil, err 1426 } 1427 1428 if fieldPatchStrategy == mergeDirective { 1429 return mergeSlice(typedOriginal, typedPatch, schema, fieldPatchMergeKey, mergeOptions, isDeleteList) 1430 } else { 1431 return typedPatch, nil 1432 } 1433 } 1434 1435 // Merge two slices together. Note: This may modify both the original slice and 1436 // the patch because getting a deep copy of a slice in golang is highly 1437 // non-trivial. 1438 func mergeSlice(original, patch []interface{}, schema LookupPatchMeta, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) { 1439 if len(original) == 0 && len(patch) == 0 { 1440 return original, nil 1441 } 1442 1443 // All the values must be of the same type, but not a list. 1444 t, err := sliceElementType(original, patch) 1445 if err != nil { 1446 return nil, err 1447 } 1448 1449 var merged []interface{} 1450 kind := t.Kind() 1451 // If the elements are not maps, merge the slices of scalars. 1452 if kind != reflect.Map { 1453 if mergeOptions.MergeParallelList && isDeleteList { 1454 return deleteFromSlice(original, patch), nil 1455 } 1456 // Maybe in the future add a "concat" mode that doesn't 1457 // deduplicate. 1458 both := append(original, patch...) 1459 merged = deduplicateScalars(both) 1460 1461 } else { 1462 if mergeKey == "" { 1463 return nil, fmt.Errorf("cannot merge lists without merge key for %s", schema.Name()) 1464 } 1465 1466 original, patch, err = mergeSliceWithSpecialElements(original, patch, mergeKey) 1467 if err != nil { 1468 return nil, err 1469 } 1470 1471 merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, schema, mergeOptions) 1472 if err != nil { 1473 return nil, err 1474 } 1475 } 1476 1477 // enforce the order 1478 var patchItems, serverOnlyItems []interface{} 1479 if len(mergeKey) == 0 { 1480 patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, patch) 1481 } else { 1482 patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, patch, mergeKey) 1483 if err != nil { 1484 return nil, err 1485 } 1486 } 1487 return normalizeElementOrder(patchItems, serverOnlyItems, patch, original, mergeKey, kind) 1488 } 1489 1490 // mergeSliceWithSpecialElements handles special elements with directiveMarker 1491 // before merging the slices. It returns a updated `original` and a patch without special elements. 1492 // original and patch must be slices of maps, they should be checked before calling this function. 1493 func mergeSliceWithSpecialElements(original, patch []interface{}, mergeKey string) ([]interface{}, []interface{}, error) { 1494 patchWithoutSpecialElements := []interface{}{} 1495 replace := false 1496 for _, v := range patch { 1497 typedV := v.(map[string]interface{}) 1498 patchType, ok := typedV[directiveMarker] 1499 if !ok { 1500 patchWithoutSpecialElements = append(patchWithoutSpecialElements, v) 1501 } else { 1502 switch patchType { 1503 case deleteDirective: 1504 mergeValue, ok := typedV[mergeKey] 1505 if ok { 1506 var err error 1507 original, err = deleteMatchingEntries(original, mergeKey, mergeValue) 1508 if err != nil { 1509 return nil, nil, err 1510 } 1511 } else { 1512 return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey) 1513 } 1514 case replaceDirective: 1515 replace = true 1516 // Continue iterating through the array to prune any other $patch elements. 1517 case mergeDirective: 1518 return nil, nil, fmt.Errorf("merging lists cannot yet be specified in the patch") 1519 default: 1520 return nil, nil, mergepatch.ErrBadPatchType(patchType, typedV) 1521 } 1522 } 1523 } 1524 if replace { 1525 return patchWithoutSpecialElements, nil, nil 1526 } 1527 return original, patchWithoutSpecialElements, nil 1528 } 1529 1530 // delete all matching entries (based on merge key) from a merging list 1531 func deleteMatchingEntries(original []interface{}, mergeKey string, mergeValue interface{}) ([]interface{}, error) { 1532 for { 1533 _, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue) 1534 if err != nil { 1535 return nil, err 1536 } 1537 1538 if !found { 1539 break 1540 } 1541 // Delete the element at originalKey. 1542 original = append(original[:originalKey], original[originalKey+1:]...) 1543 } 1544 return original, nil 1545 } 1546 1547 // mergeSliceWithoutSpecialElements merges slices with non-special elements. 1548 // original and patch must be slices of maps, they should be checked before calling this function. 1549 func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, schema LookupPatchMeta, mergeOptions MergeOptions) ([]interface{}, error) { 1550 for _, v := range patch { 1551 typedV := v.(map[string]interface{}) 1552 mergeValue, ok := typedV[mergeKey] 1553 if !ok { 1554 return nil, mergepatch.ErrNoMergeKey(typedV, mergeKey) 1555 } 1556 1557 // If we find a value with this merge key value in original, merge the 1558 // maps. Otherwise append onto original. 1559 originalMap, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue) 1560 if err != nil { 1561 return nil, err 1562 } 1563 1564 if found { 1565 var mergedMaps interface{} 1566 var err error 1567 // Merge into original. 1568 mergedMaps, err = mergeMap(originalMap, typedV, schema, mergeOptions) 1569 if err != nil { 1570 return nil, err 1571 } 1572 1573 original[originalKey] = mergedMaps 1574 } else { 1575 original = append(original, v) 1576 } 1577 } 1578 return original, nil 1579 } 1580 1581 // deleteFromSlice uses the parallel list to delete the items in a list of scalars 1582 func deleteFromSlice(current, toDelete []interface{}) []interface{} { 1583 toDeleteMap := map[interface{}]interface{}{} 1584 processed := make([]interface{}, 0, len(current)) 1585 for _, v := range toDelete { 1586 toDeleteMap[v] = true 1587 } 1588 for _, v := range current { 1589 if _, found := toDeleteMap[v]; !found { 1590 processed = append(processed, v) 1591 } 1592 } 1593 return processed 1594 } 1595 1596 // This method no longer panics if any element of the slice is not a map. 1597 func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{}) (map[string]interface{}, int, bool, error) { 1598 for k, v := range m { 1599 typedV, ok := v.(map[string]interface{}) 1600 if !ok { 1601 return nil, 0, false, fmt.Errorf("value for key %v is not a map", k) 1602 } 1603 1604 valueToMatch, ok := typedV[key] 1605 if ok && valueToMatch == value { 1606 return typedV, k, true, nil 1607 } 1608 } 1609 1610 return nil, 0, false, nil 1611 } 1612 1613 // This function takes a JSON map and sorts all the lists that should be merged 1614 // by key. This is needed by tests because in JSON, list order is significant, 1615 // but in Strategic Merge Patch, merge lists do not have significant order. 1616 // Sorting the lists allows for order-insensitive comparison of patched maps. 1617 func sortMergeListsByName(mapJSON []byte, schema LookupPatchMeta) ([]byte, error) { 1618 var m map[string]interface{} 1619 err := json.Unmarshal(mapJSON, &m) 1620 if err != nil { 1621 return nil, mergepatch.ErrBadJSONDoc 1622 } 1623 1624 newM, err := sortMergeListsByNameMap(m, schema) 1625 if err != nil { 1626 return nil, err 1627 } 1628 1629 return json.Marshal(newM) 1630 } 1631 1632 // Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in a map. 1633 func sortMergeListsByNameMap(s map[string]interface{}, schema LookupPatchMeta) (map[string]interface{}, error) { 1634 newS := map[string]interface{}{} 1635 for k, v := range s { 1636 if k == retainKeysDirective { 1637 typedV, ok := v.([]interface{}) 1638 if !ok { 1639 return nil, mergepatch.ErrBadPatchFormatForRetainKeys 1640 } 1641 v = sortScalars(typedV) 1642 } else if strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) { 1643 typedV, ok := v.([]interface{}) 1644 if !ok { 1645 return nil, mergepatch.ErrBadPatchFormatForPrimitiveList 1646 } 1647 v = sortScalars(typedV) 1648 } else if strings.HasPrefix(k, setElementOrderDirectivePrefix) { 1649 _, ok := v.([]interface{}) 1650 if !ok { 1651 return nil, mergepatch.ErrBadPatchFormatForSetElementOrderList 1652 } 1653 } else if k != directiveMarker { 1654 // recurse for map and slice. 1655 switch typedV := v.(type) { 1656 case map[string]interface{}: 1657 subschema, _, err := schema.LookupPatchMetadataForStruct(k) 1658 if err != nil { 1659 return nil, err 1660 } 1661 v, err = sortMergeListsByNameMap(typedV, subschema) 1662 if err != nil { 1663 return nil, err 1664 } 1665 case []interface{}: 1666 subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k) 1667 if err != nil { 1668 return nil, err 1669 } 1670 _, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies()) 1671 if err != nil { 1672 return nil, err 1673 } 1674 if patchStrategy == mergeDirective { 1675 var err error 1676 v, err = sortMergeListsByNameArray(typedV, subschema, patchMeta.GetPatchMergeKey(), true) 1677 if err != nil { 1678 return nil, err 1679 } 1680 } 1681 } 1682 } 1683 1684 newS[k] = v 1685 } 1686 1687 return newS, nil 1688 } 1689 1690 // Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in an array. 1691 func sortMergeListsByNameArray(s []interface{}, schema LookupPatchMeta, mergeKey string, recurse bool) ([]interface{}, error) { 1692 if len(s) == 0 { 1693 return s, nil 1694 } 1695 1696 // We don't support lists of lists yet. 1697 t, err := sliceElementType(s) 1698 if err != nil { 1699 return nil, err 1700 } 1701 1702 // If the elements are not maps... 1703 if t.Kind() != reflect.Map { 1704 // Sort the elements, because they may have been merged out of order. 1705 return deduplicateAndSortScalars(s), nil 1706 } 1707 1708 // Elements are maps - if one of the keys of the map is a map or a 1709 // list, we may need to recurse into it. 1710 newS := []interface{}{} 1711 for _, elem := range s { 1712 if recurse { 1713 typedElem := elem.(map[string]interface{}) 1714 newElem, err := sortMergeListsByNameMap(typedElem, schema) 1715 if err != nil { 1716 return nil, err 1717 } 1718 1719 newS = append(newS, newElem) 1720 } else { 1721 newS = append(newS, elem) 1722 } 1723 } 1724 1725 // Sort the maps. 1726 newS = sortMapsBasedOnField(newS, mergeKey) 1727 return newS, nil 1728 } 1729 1730 func sortMapsBasedOnField(m []interface{}, fieldName string) []interface{} { 1731 mapM := mapSliceFromSlice(m) 1732 ss := SortableSliceOfMaps{mapM, fieldName} 1733 sort.Sort(ss) 1734 newS := sliceFromMapSlice(ss.s) 1735 return newS 1736 } 1737 1738 func mapSliceFromSlice(m []interface{}) []map[string]interface{} { 1739 newM := []map[string]interface{}{} 1740 for _, v := range m { 1741 vt := v.(map[string]interface{}) 1742 newM = append(newM, vt) 1743 } 1744 1745 return newM 1746 } 1747 1748 func sliceFromMapSlice(s []map[string]interface{}) []interface{} { 1749 newS := []interface{}{} 1750 for _, v := range s { 1751 newS = append(newS, v) 1752 } 1753 1754 return newS 1755 } 1756 1757 type SortableSliceOfMaps struct { 1758 s []map[string]interface{} 1759 k string // key to sort on 1760 } 1761 1762 func (ss SortableSliceOfMaps) Len() int { 1763 return len(ss.s) 1764 } 1765 1766 func (ss SortableSliceOfMaps) Less(i, j int) bool { 1767 iStr := fmt.Sprintf("%v", ss.s[i][ss.k]) 1768 jStr := fmt.Sprintf("%v", ss.s[j][ss.k]) 1769 return sort.StringsAreSorted([]string{iStr, jStr}) 1770 } 1771 1772 func (ss SortableSliceOfMaps) Swap(i, j int) { 1773 tmp := ss.s[i] 1774 ss.s[i] = ss.s[j] 1775 ss.s[j] = tmp 1776 } 1777 1778 func deduplicateAndSortScalars(s []interface{}) []interface{} { 1779 s = deduplicateScalars(s) 1780 return sortScalars(s) 1781 } 1782 1783 func sortScalars(s []interface{}) []interface{} { 1784 ss := SortableSliceOfScalars{s} 1785 sort.Sort(ss) 1786 return ss.s 1787 } 1788 1789 func deduplicateScalars(s []interface{}) []interface{} { 1790 // Clever algorithm to deduplicate. 1791 length := len(s) - 1 1792 for i := 0; i < length; i++ { 1793 for j := i + 1; j <= length; j++ { 1794 if s[i] == s[j] { 1795 s[j] = s[length] 1796 s = s[0:length] 1797 length-- 1798 j-- 1799 } 1800 } 1801 } 1802 1803 return s 1804 } 1805 1806 type SortableSliceOfScalars struct { 1807 s []interface{} 1808 } 1809 1810 func (ss SortableSliceOfScalars) Len() int { 1811 return len(ss.s) 1812 } 1813 1814 func (ss SortableSliceOfScalars) Less(i, j int) bool { 1815 iStr := fmt.Sprintf("%v", ss.s[i]) 1816 jStr := fmt.Sprintf("%v", ss.s[j]) 1817 return sort.StringsAreSorted([]string{iStr, jStr}) 1818 } 1819 1820 func (ss SortableSliceOfScalars) Swap(i, j int) { 1821 tmp := ss.s[i] 1822 ss.s[i] = ss.s[j] 1823 ss.s[j] = tmp 1824 } 1825 1826 // Returns the type of the elements of N slice(s). If the type is different, 1827 // another slice or undefined, returns an error. 1828 func sliceElementType(slices ...[]interface{}) (reflect.Type, error) { 1829 var prevType reflect.Type 1830 for _, s := range slices { 1831 // Go through elements of all given slices and make sure they are all the same type. 1832 for _, v := range s { 1833 currentType := reflect.TypeOf(v) 1834 if prevType == nil { 1835 prevType = currentType 1836 // We don't support lists of lists yet. 1837 if prevType.Kind() == reflect.Slice { 1838 return nil, mergepatch.ErrNoListOfLists 1839 } 1840 } else { 1841 if prevType != currentType { 1842 return nil, fmt.Errorf("list element types are not identical: %v", fmt.Sprint(slices)) 1843 } 1844 prevType = currentType 1845 } 1846 } 1847 } 1848 1849 if prevType == nil { 1850 return nil, fmt.Errorf("no elements in any of the given slices") 1851 } 1852 1853 return prevType, nil 1854 } 1855 1856 // MergingMapsHaveConflicts returns true if the left and right JSON interface 1857 // objects overlap with different values in any key. All keys are required to be 1858 // strings. Since patches of the same Type have congruent keys, this is valid 1859 // for multiple patch types. This method supports strategic merge patch semantics. 1860 func MergingMapsHaveConflicts(left, right map[string]interface{}, schema LookupPatchMeta) (bool, error) { 1861 return mergingMapFieldsHaveConflicts(left, right, schema, "", "") 1862 } 1863 1864 func mergingMapFieldsHaveConflicts( 1865 left, right interface{}, 1866 schema LookupPatchMeta, 1867 fieldPatchStrategy, fieldPatchMergeKey string, 1868 ) (bool, error) { 1869 switch leftType := left.(type) { 1870 case map[string]interface{}: 1871 rightType, ok := right.(map[string]interface{}) 1872 if !ok { 1873 return true, nil 1874 } 1875 leftMarker, okLeft := leftType[directiveMarker] 1876 rightMarker, okRight := rightType[directiveMarker] 1877 // if one or the other has a directive marker, 1878 // then we need to consider that before looking at the individual keys, 1879 // since a directive operates on the whole map. 1880 if okLeft || okRight { 1881 // if one has a directive marker and the other doesn't, 1882 // then we have a conflict, since one is deleting or replacing the whole map, 1883 // and the other is doing things to individual keys. 1884 if okLeft != okRight { 1885 return true, nil 1886 } 1887 // if they both have markers, but they are not the same directive, 1888 // then we have a conflict because they're doing different things to the map. 1889 if leftMarker != rightMarker { 1890 return true, nil 1891 } 1892 } 1893 if fieldPatchStrategy == replaceDirective { 1894 return false, nil 1895 } 1896 // Check the individual keys. 1897 return mapsHaveConflicts(leftType, rightType, schema) 1898 1899 case []interface{}: 1900 rightType, ok := right.([]interface{}) 1901 if !ok { 1902 return true, nil 1903 } 1904 return slicesHaveConflicts(leftType, rightType, schema, fieldPatchStrategy, fieldPatchMergeKey) 1905 case string, float64, bool, int64, nil: 1906 return !reflect.DeepEqual(left, right), nil 1907 default: 1908 return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left)) 1909 } 1910 } 1911 1912 func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) { 1913 for key, leftValue := range typedLeft { 1914 if key != directiveMarker && key != retainKeysDirective { 1915 if rightValue, ok := typedRight[key]; ok { 1916 var subschema LookupPatchMeta 1917 var patchMeta PatchMeta 1918 var patchStrategy string 1919 var err error 1920 switch leftValue.(type) { 1921 case []interface{}: 1922 subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(key) 1923 if err != nil { 1924 return true, err 1925 } 1926 _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies) 1927 if err != nil { 1928 return true, err 1929 } 1930 case map[string]interface{}: 1931 subschema, patchMeta, err = schema.LookupPatchMetadataForStruct(key) 1932 if err != nil { 1933 return true, err 1934 } 1935 _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies) 1936 if err != nil { 1937 return true, err 1938 } 1939 } 1940 1941 if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, 1942 subschema, patchStrategy, patchMeta.GetPatchMergeKey()); hasConflicts { 1943 return true, err 1944 } 1945 } 1946 } 1947 } 1948 1949 return false, nil 1950 } 1951 1952 func slicesHaveConflicts( 1953 typedLeft, typedRight []interface{}, 1954 schema LookupPatchMeta, 1955 fieldPatchStrategy, fieldPatchMergeKey string, 1956 ) (bool, error) { 1957 elementType, err := sliceElementType(typedLeft, typedRight) 1958 if err != nil { 1959 return true, err 1960 } 1961 1962 if fieldPatchStrategy == mergeDirective { 1963 // Merging lists of scalars have no conflicts by definition 1964 // So we only need to check further if the elements are maps 1965 if elementType.Kind() != reflect.Map { 1966 return false, nil 1967 } 1968 1969 // Build a map for each slice and then compare the two maps 1970 leftMap, err := sliceOfMapsToMapOfMaps(typedLeft, fieldPatchMergeKey) 1971 if err != nil { 1972 return true, err 1973 } 1974 1975 rightMap, err := sliceOfMapsToMapOfMaps(typedRight, fieldPatchMergeKey) 1976 if err != nil { 1977 return true, err 1978 } 1979 1980 return mapsOfMapsHaveConflicts(leftMap, rightMap, schema) 1981 } 1982 1983 // Either we don't have type information, or these are non-merging lists 1984 if len(typedLeft) != len(typedRight) { 1985 return true, nil 1986 } 1987 1988 // Sort scalar slices to prevent ordering issues 1989 // We have no way to sort non-merging lists of maps 1990 if elementType.Kind() != reflect.Map { 1991 typedLeft = deduplicateAndSortScalars(typedLeft) 1992 typedRight = deduplicateAndSortScalars(typedRight) 1993 } 1994 1995 // Compare the slices element by element in order 1996 // This test will fail if the slices are not sorted 1997 for i := range typedLeft { 1998 if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], schema, "", ""); hasConflicts { 1999 return true, err 2000 } 2001 } 2002 2003 return false, nil 2004 } 2005 2006 func sliceOfMapsToMapOfMaps(slice []interface{}, mergeKey string) (map[string]interface{}, error) { 2007 result := make(map[string]interface{}, len(slice)) 2008 for _, value := range slice { 2009 typedValue, ok := value.(map[string]interface{}) 2010 if !ok { 2011 return nil, fmt.Errorf("invalid element type in merging list:%v", slice) 2012 } 2013 2014 mergeValue, ok := typedValue[mergeKey] 2015 if !ok { 2016 return nil, fmt.Errorf("cannot find merge key `%s` in merging list element:%v", mergeKey, typedValue) 2017 } 2018 2019 result[fmt.Sprintf("%s", mergeValue)] = typedValue 2020 } 2021 2022 return result, nil 2023 } 2024 2025 func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) { 2026 for key, leftValue := range typedLeft { 2027 if rightValue, ok := typedRight[key]; ok { 2028 if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, schema, "", ""); hasConflicts { 2029 return true, err 2030 } 2031 } 2032 } 2033 2034 return false, nil 2035 } 2036 2037 // CreateThreeWayMergePatch reconciles a modified configuration with an original configuration, 2038 // while preserving any changes or deletions made to the original configuration in the interim, 2039 // and not overridden by the current configuration. All three documents must be passed to the 2040 // method as json encoded content. It will return a strategic merge patch, or an error if any 2041 // of the documents is invalid, or if there are any preconditions that fail against the modified 2042 // configuration, or, if overwrite is false and there are conflicts between the modified and current 2043 // configurations. Conflicts are defined as keys changed differently from original to modified 2044 // than from original to current. In other words, a conflict occurs if modified changes any key 2045 // in a way that is different from how it is changed in current (e.g., deleting it, changing its 2046 // value). We also propagate values fields that do not exist in original but are explicitly 2047 // defined in modified. 2048 func CreateThreeWayMergePatch(original, modified, current []byte, schema LookupPatchMeta, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) { 2049 originalMap := map[string]interface{}{} 2050 if len(original) > 0 { 2051 if err := json.Unmarshal(original, &originalMap); err != nil { 2052 return nil, mergepatch.ErrBadJSONDoc 2053 } 2054 } 2055 2056 modifiedMap := map[string]interface{}{} 2057 if len(modified) > 0 { 2058 if err := json.Unmarshal(modified, &modifiedMap); err != nil { 2059 return nil, mergepatch.ErrBadJSONDoc 2060 } 2061 } 2062 2063 currentMap := map[string]interface{}{} 2064 if len(current) > 0 { 2065 if err := json.Unmarshal(current, ¤tMap); err != nil { 2066 return nil, mergepatch.ErrBadJSONDoc 2067 } 2068 } 2069 2070 // The patch is the difference from current to modified without deletions, plus deletions 2071 // from original to modified. To find it, we compute deletions, which are the deletions from 2072 // original to modified, and delta, which is the difference from current to modified without 2073 // deletions, and then apply delta to deletions as a patch, which should be strictly additive. 2074 deltaMapDiffOptions := DiffOptions{ 2075 IgnoreDeletions: true, 2076 SetElementOrder: true, 2077 } 2078 deltaMap, err := diffMaps(currentMap, modifiedMap, schema, deltaMapDiffOptions) 2079 if err != nil { 2080 return nil, err 2081 } 2082 deletionsMapDiffOptions := DiffOptions{ 2083 SetElementOrder: true, 2084 IgnoreChangesAndAdditions: true, 2085 } 2086 deletionsMap, err := diffMaps(originalMap, modifiedMap, schema, deletionsMapDiffOptions) 2087 if err != nil { 2088 return nil, err 2089 } 2090 2091 mergeOptions := MergeOptions{} 2092 patchMap, err := mergeMap(deletionsMap, deltaMap, schema, mergeOptions) 2093 if err != nil { 2094 return nil, err 2095 } 2096 2097 // Apply the preconditions to the patch, and return an error if any of them fail. 2098 for _, fn := range fns { 2099 if !fn(patchMap) { 2100 return nil, mergepatch.NewErrPreconditionFailed(patchMap) 2101 } 2102 } 2103 2104 // If overwrite is false, and the patch contains any keys that were changed differently, 2105 // then return a conflict error. 2106 if !overwrite { 2107 changeMapDiffOptions := DiffOptions{} 2108 changedMap, err := diffMaps(originalMap, currentMap, schema, changeMapDiffOptions) 2109 if err != nil { 2110 return nil, err 2111 } 2112 2113 hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, schema) 2114 if err != nil { 2115 return nil, err 2116 } 2117 2118 if hasConflicts { 2119 return nil, mergepatch.NewErrConflict(mergepatch.ToYAMLOrError(patchMap), mergepatch.ToYAMLOrError(changedMap)) 2120 } 2121 } 2122 2123 return json.Marshal(patchMap) 2124 } 2125 2126 func ItemAddedToModifiedSlice(original, modified string) bool { return original > modified } 2127 2128 func ItemRemovedFromModifiedSlice(original, modified string) bool { return original < modified } 2129 2130 func ItemMatchesOriginalAndModifiedSlice(original, modified string) bool { return original == modified } 2131 2132 func CreateDeleteDirective(mergeKey string, mergeKeyValue interface{}) map[string]interface{} { 2133 return map[string]interface{}{mergeKey: mergeKeyValue, directiveMarker: deleteDirective} 2134 } 2135 2136 func mapTypeAssertion(original, patch interface{}) (map[string]interface{}, map[string]interface{}, error) { 2137 typedOriginal, ok := original.(map[string]interface{}) 2138 if !ok { 2139 return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original) 2140 } 2141 typedPatch, ok := patch.(map[string]interface{}) 2142 if !ok { 2143 return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch) 2144 } 2145 return typedOriginal, typedPatch, nil 2146 } 2147 2148 func sliceTypeAssertion(original, patch interface{}) ([]interface{}, []interface{}, error) { 2149 typedOriginal, ok := original.([]interface{}) 2150 if !ok { 2151 return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original) 2152 } 2153 typedPatch, ok := patch.([]interface{}) 2154 if !ok { 2155 return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch) 2156 } 2157 return typedOriginal, typedPatch, nil 2158 } 2159 2160 // extractRetainKeysPatchStrategy process patch strategy, which is a string may contains multiple 2161 // patch strategies separated by ",". It returns a boolean var indicating if it has 2162 // retainKeys strategies and a string for the other strategy. 2163 func extractRetainKeysPatchStrategy(strategies []string) (bool, string, error) { 2164 switch len(strategies) { 2165 case 0: 2166 return false, "", nil 2167 case 1: 2168 singleStrategy := strategies[0] 2169 switch singleStrategy { 2170 case retainKeysStrategy: 2171 return true, "", nil 2172 default: 2173 return false, singleStrategy, nil 2174 } 2175 case 2: 2176 switch { 2177 case strategies[0] == retainKeysStrategy: 2178 return true, strategies[1], nil 2179 case strategies[1] == retainKeysStrategy: 2180 return true, strategies[0], nil 2181 default: 2182 return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies) 2183 } 2184 default: 2185 return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies) 2186 } 2187 } 2188 2189 // hasAdditionalNewField returns if original map has additional key with non-nil value than modified. 2190 func hasAdditionalNewField(original, modified map[string]interface{}) bool { 2191 for k, v := range original { 2192 if v == nil { 2193 continue 2194 } 2195 if _, found := modified[k]; !found { 2196 return true 2197 } 2198 } 2199 return false 2200 }