util.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package mergepatch
  14. import (
  15. "fmt"
  16. "reflect"
  17. "github.com/davecgh/go-spew/spew"
  18. "github.com/ghodss/yaml"
  19. )
  20. // PreconditionFunc asserts that an incompatible change is not present within a patch.
  21. type PreconditionFunc func(interface{}) bool
  22. // RequireKeyUnchanged returns a precondition function that fails if the provided key
  23. // is present in the patch (indicating that its value has changed).
  24. func RequireKeyUnchanged(key string) PreconditionFunc {
  25. return func(patch interface{}) bool {
  26. patchMap, ok := patch.(map[string]interface{})
  27. if !ok {
  28. return true
  29. }
  30. // The presence of key means that its value has been changed, so the test fails.
  31. _, ok = patchMap[key]
  32. return !ok
  33. }
  34. }
  35. // RequireMetadataKeyUnchanged creates a precondition function that fails
  36. // if the metadata.key is present in the patch (indicating its value
  37. // has changed).
  38. func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
  39. return func(patch interface{}) bool {
  40. patchMap, ok := patch.(map[string]interface{})
  41. if !ok {
  42. return true
  43. }
  44. patchMap1, ok := patchMap["metadata"]
  45. if !ok {
  46. return true
  47. }
  48. patchMap2, ok := patchMap1.(map[string]interface{})
  49. if !ok {
  50. return true
  51. }
  52. _, ok = patchMap2[key]
  53. return !ok
  54. }
  55. }
  56. func ToYAMLOrError(v interface{}) string {
  57. y, err := toYAML(v)
  58. if err != nil {
  59. return err.Error()
  60. }
  61. return y
  62. }
  63. func toYAML(v interface{}) (string, error) {
  64. y, err := yaml.Marshal(v)
  65. if err != nil {
  66. return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, spew.Sdump(v))
  67. }
  68. return string(y), nil
  69. }
  70. // HasConflicts returns true if the left and right JSON interface objects overlap with
  71. // different values in any key. All keys are required to be strings. Since patches of the
  72. // same Type have congruent keys, this is valid for multiple patch types. This method
  73. // supports JSON merge patch semantics.
  74. //
  75. // NOTE: Numbers with different types (e.g. int(0) vs int64(0)) will be detected as conflicts.
  76. // Make sure the unmarshaling of left and right are consistent (e.g. use the same library).
  77. func HasConflicts(left, right interface{}) (bool, error) {
  78. switch typedLeft := left.(type) {
  79. case map[string]interface{}:
  80. switch typedRight := right.(type) {
  81. case map[string]interface{}:
  82. for key, leftValue := range typedLeft {
  83. rightValue, ok := typedRight[key]
  84. if !ok {
  85. continue
  86. }
  87. if conflict, err := HasConflicts(leftValue, rightValue); err != nil || conflict {
  88. return conflict, err
  89. }
  90. }
  91. return false, nil
  92. default:
  93. return true, nil
  94. }
  95. case []interface{}:
  96. switch typedRight := right.(type) {
  97. case []interface{}:
  98. if len(typedLeft) != len(typedRight) {
  99. return true, nil
  100. }
  101. for i := range typedLeft {
  102. if conflict, err := HasConflicts(typedLeft[i], typedRight[i]); err != nil || conflict {
  103. return conflict, err
  104. }
  105. }
  106. return false, nil
  107. default:
  108. return true, nil
  109. }
  110. case string, float64, bool, int, int64, nil:
  111. return !reflect.DeepEqual(left, right), nil
  112. default:
  113. return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
  114. }
  115. }