forked from kubernetes/kubernetes
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request kubernetes#40942 from ymqytw/extract_util_from_SMP…
…atch Automatic merge from submit-queue (batch tested with PRs 40289, 40877, 40879, 39972, 40942) Extract util used by jsonmergepatch and SMPatch followup kubernetes#40666 (comment) Extract some util out of the `strategicMergePatch` to make `jsonMergePatch` doesn't depend on `strategicMergePatch`. ```release-note None ``` cc: @liggitt
- Loading branch information
Showing
13 changed files
with
351 additions
and
256 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
staging/src/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
Copyright 2017 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package mergepatch | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
var ErrBadJSONDoc = errors.New("Invalid JSON document") | ||
var ErrNoListOfLists = errors.New("Lists of lists are not supported") | ||
var ErrBadPatchFormatForPrimitiveList = errors.New("Invalid patch format of primitive list") | ||
|
||
// IsPreconditionFailed returns true if the provided error indicates | ||
// a precondition failed. | ||
func IsPreconditionFailed(err error) bool { | ||
_, ok := err.(ErrPreconditionFailed) | ||
return ok | ||
} | ||
|
||
type ErrPreconditionFailed struct { | ||
message string | ||
} | ||
|
||
func NewErrPreconditionFailed(target map[string]interface{}) ErrPreconditionFailed { | ||
s := fmt.Sprintf("precondition failed for: %v", target) | ||
return ErrPreconditionFailed{s} | ||
} | ||
|
||
func (err ErrPreconditionFailed) Error() string { | ||
return err.message | ||
} | ||
|
||
type ErrConflict struct { | ||
message string | ||
} | ||
|
||
func NewErrConflict(patch, current string) ErrConflict { | ||
s := fmt.Sprintf("patch:\n%s\nconflicts with changes made from original to current:\n%s\n", patch, current) | ||
return ErrConflict{s} | ||
} | ||
|
||
func (err ErrConflict) Error() string { | ||
return err.message | ||
} | ||
|
||
// IsConflict returns true if the provided error indicates | ||
// a conflict between the patch and the current configuration. | ||
func IsConflict(err error) bool { | ||
_, ok := err.(ErrConflict) | ||
return ok | ||
} |
126 changes: 126 additions & 0 deletions
126
staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
Copyright 2017 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package mergepatch | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/davecgh/go-spew/spew" | ||
"github.com/ghodss/yaml" | ||
) | ||
|
||
// PreconditionFunc asserts that an incompatible change is not present within a patch. | ||
type PreconditionFunc func(interface{}) bool | ||
|
||
// RequireKeyUnchanged returns a precondition function that fails if the provided key | ||
// is present in the patch (indicating that its value has changed). | ||
func RequireKeyUnchanged(key string) PreconditionFunc { | ||
return func(patch interface{}) bool { | ||
patchMap, ok := patch.(map[string]interface{}) | ||
if !ok { | ||
return true | ||
} | ||
|
||
// The presence of key means that its value has been changed, so the test fails. | ||
_, ok = patchMap[key] | ||
return !ok | ||
} | ||
} | ||
|
||
// RequireMetadataKeyUnchanged creates a precondition function that fails | ||
// if the metadata.key is present in the patch (indicating its value | ||
// has changed). | ||
func RequireMetadataKeyUnchanged(key string) PreconditionFunc { | ||
return func(patch interface{}) bool { | ||
patchMap, ok := patch.(map[string]interface{}) | ||
if !ok { | ||
return true | ||
} | ||
patchMap1, ok := patchMap["metadata"] | ||
if !ok { | ||
return true | ||
} | ||
patchMap2, ok := patchMap1.(map[string]interface{}) | ||
if !ok { | ||
return true | ||
} | ||
_, ok = patchMap2[key] | ||
return !ok | ||
} | ||
} | ||
|
||
func ToYAMLOrError(v interface{}) string { | ||
y, err := toYAML(v) | ||
if err != nil { | ||
return err.Error() | ||
} | ||
|
||
return y | ||
} | ||
|
||
func toYAML(v interface{}) (string, error) { | ||
y, err := yaml.Marshal(v) | ||
if err != nil { | ||
return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, spew.Sdump(v)) | ||
} | ||
|
||
return string(y), nil | ||
} | ||
|
||
// HasConflicts returns true if the left and right JSON interface objects overlap with | ||
// different values in any key. All keys are required to be strings. Since patches of the | ||
// same Type have congruent keys, this is valid for multiple patch types. This method | ||
// supports JSON merge patch semantics. | ||
func HasConflicts(left, right interface{}) (bool, error) { | ||
switch typedLeft := left.(type) { | ||
case map[string]interface{}: | ||
switch typedRight := right.(type) { | ||
case map[string]interface{}: | ||
for key, leftValue := range typedLeft { | ||
rightValue, ok := typedRight[key] | ||
if !ok { | ||
return false, nil | ||
} | ||
return HasConflicts(leftValue, rightValue) | ||
} | ||
|
||
return false, nil | ||
default: | ||
return true, nil | ||
} | ||
case []interface{}: | ||
switch typedRight := right.(type) { | ||
case []interface{}: | ||
if len(typedLeft) != len(typedRight) { | ||
return true, nil | ||
} | ||
|
||
for i := range typedLeft { | ||
return HasConflicts(typedLeft[i], typedRight[i]) | ||
} | ||
|
||
return false, nil | ||
default: | ||
return true, nil | ||
} | ||
case string, float64, bool, int, int64, nil: | ||
return !reflect.DeepEqual(left, right), nil | ||
default: | ||
return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left)) | ||
} | ||
} |
Oops, something went wrong.