vendor: bump to kube 1.10/master
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
a85ea609db
commit
f317ffce5b
535 changed files with 52955 additions and 17528 deletions
1
vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
generated
vendored
1
vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
generated
vendored
|
@ -29,6 +29,7 @@ var (
|
|||
ErrBadPatchFormatForRetainKeys = errors.New("invalid patch format of retainKeys")
|
||||
ErrBadPatchFormatForSetElementOrderList = errors.New("invalid patch format of setElementOrder list")
|
||||
ErrPatchContentNotMatchRetainKeys = errors.New("patch content doesn't match retainKeys list")
|
||||
ErrUnsupportedStrategicMergePatchFormat = errors.New("strategic merge patch format is not supported")
|
||||
)
|
||||
|
||||
func ErrNoMergeKey(m map[string]interface{}, k string) error {
|
||||
|
|
12
vendor/k8s.io/apimachinery/pkg/util/net/http.go
generated
vendored
12
vendor/k8s.io/apimachinery/pkg/util/net/http.go
generated
vendored
|
@ -276,17 +276,7 @@ func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error
|
|||
}
|
||||
|
||||
return func(req *http.Request) (*url.URL, error) {
|
||||
host := req.URL.Host
|
||||
// for some urls, the Host is already the host, not the host:port
|
||||
if net.ParseIP(host) == nil {
|
||||
var err error
|
||||
host, _, err = net.SplitHostPort(req.URL.Host)
|
||||
if err != nil {
|
||||
return delegate(req)
|
||||
}
|
||||
}
|
||||
|
||||
ip := net.ParseIP(host)
|
||||
ip := net.ParseIP(req.URL.Hostname())
|
||||
if ip == nil {
|
||||
return delegate(req)
|
||||
}
|
||||
|
|
49
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
generated
vendored
Normal file
49
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
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 strategicpatch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type LookupPatchMetaError struct {
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e LookupPatchMetaError) Error() string {
|
||||
return fmt.Sprintf("LookupPatchMetaError(%s): %v", e.Path, e.Err)
|
||||
}
|
||||
|
||||
type FieldNotFoundError struct {
|
||||
Path string
|
||||
Field string
|
||||
}
|
||||
|
||||
func (e FieldNotFoundError) Error() string {
|
||||
return fmt.Sprintf("unable to find api field %q in %s", e.Field, e.Path)
|
||||
}
|
||||
|
||||
type InvalidTypeError struct {
|
||||
Path string
|
||||
Expected string
|
||||
Actual string
|
||||
}
|
||||
|
||||
func (e InvalidTypeError) Error() string {
|
||||
return fmt.Sprintf("invalid type for %s: got %q, expected %q", e.Path, e.Actual, e.Expected)
|
||||
}
|
194
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
generated
vendored
Normal file
194
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
generated
vendored
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
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 strategicpatch
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/mergepatch"
|
||||
forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
|
||||
openapi "k8s.io/kube-openapi/pkg/util/proto"
|
||||
)
|
||||
|
||||
type PatchMeta struct {
|
||||
patchStrategies []string
|
||||
patchMergeKey string
|
||||
}
|
||||
|
||||
func (pm PatchMeta) GetPatchStrategies() []string {
|
||||
if pm.patchStrategies == nil {
|
||||
return []string{}
|
||||
}
|
||||
return pm.patchStrategies
|
||||
}
|
||||
|
||||
func (pm PatchMeta) SetPatchStrategies(ps []string) {
|
||||
pm.patchStrategies = ps
|
||||
}
|
||||
|
||||
func (pm PatchMeta) GetPatchMergeKey() string {
|
||||
return pm.patchMergeKey
|
||||
}
|
||||
|
||||
func (pm PatchMeta) SetPatchMergeKey(pmk string) {
|
||||
pm.patchMergeKey = pmk
|
||||
}
|
||||
|
||||
type LookupPatchMeta interface {
|
||||
// LookupPatchMetadataForStruct gets subschema and the patch metadata (e.g. patch strategy and merge key) for map.
|
||||
LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error)
|
||||
// LookupPatchMetadataForSlice get subschema and the patch metadata for slice.
|
||||
LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error)
|
||||
// Get the type name of the field
|
||||
Name() string
|
||||
}
|
||||
|
||||
type PatchMetaFromStruct struct {
|
||||
T reflect.Type
|
||||
}
|
||||
|
||||
func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) {
|
||||
t, err := getTagStructType(dataStruct)
|
||||
return PatchMetaFromStruct{T: t}, err
|
||||
}
|
||||
|
||||
var _ LookupPatchMeta = PatchMetaFromStruct{}
|
||||
|
||||
func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
|
||||
fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key)
|
||||
if err != nil {
|
||||
return nil, PatchMeta{}, err
|
||||
}
|
||||
|
||||
return PatchMetaFromStruct{T: fieldType},
|
||||
PatchMeta{
|
||||
patchStrategies: fieldPatchStrategies,
|
||||
patchMergeKey: fieldPatchMergeKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
|
||||
subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key)
|
||||
if err != nil {
|
||||
return nil, PatchMeta{}, err
|
||||
}
|
||||
elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct)
|
||||
t := elemPatchMetaFromStruct.T
|
||||
|
||||
var elemType reflect.Type
|
||||
switch t.Kind() {
|
||||
// If t is an array or a slice, get the element type.
|
||||
// If element is still an array or a slice, return an error.
|
||||
// Otherwise, return element type.
|
||||
case reflect.Array, reflect.Slice:
|
||||
elemType = t.Elem()
|
||||
if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice {
|
||||
return nil, PatchMeta{}, errors.New("unexpected slice of slice")
|
||||
}
|
||||
// If t is an pointer, get the underlying element.
|
||||
// If the underlying element is neither an array nor a slice, the pointer is pointing to a slice,
|
||||
// e.g. https://github.com/kubernetes/kubernetes/blob/bc22e206c79282487ea0bf5696d5ccec7e839a76/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go#L2782-L2822
|
||||
// If the underlying element is either an array or a slice, return its element type.
|
||||
case reflect.Ptr:
|
||||
t = t.Elem()
|
||||
if t.Kind() == reflect.Array || t.Kind() == reflect.Slice {
|
||||
t = t.Elem()
|
||||
}
|
||||
elemType = t
|
||||
default:
|
||||
return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String())
|
||||
}
|
||||
|
||||
return PatchMetaFromStruct{T: elemType}, patchMeta, nil
|
||||
}
|
||||
|
||||
func (s PatchMetaFromStruct) Name() string {
|
||||
return s.T.Kind().String()
|
||||
}
|
||||
|
||||
func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
|
||||
if dataStruct == nil {
|
||||
return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
|
||||
}
|
||||
|
||||
t := reflect.TypeOf(dataStruct)
|
||||
// Get the underlying type for pointers
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type {
|
||||
t, err := getTagStructType(dataStruct)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
type PatchMetaFromOpenAPI struct {
|
||||
Schema openapi.Schema
|
||||
}
|
||||
|
||||
func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI {
|
||||
return PatchMetaFromOpenAPI{Schema: s}
|
||||
}
|
||||
|
||||
var _ LookupPatchMeta = PatchMetaFromOpenAPI{}
|
||||
|
||||
func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
|
||||
if s.Schema == nil {
|
||||
return nil, PatchMeta{}, nil
|
||||
}
|
||||
kindItem := NewKindItem(key, s.Schema.GetPath())
|
||||
s.Schema.Accept(kindItem)
|
||||
|
||||
err := kindItem.Error()
|
||||
if err != nil {
|
||||
return nil, PatchMeta{}, err
|
||||
}
|
||||
return PatchMetaFromOpenAPI{Schema: kindItem.subschema},
|
||||
kindItem.patchmeta, nil
|
||||
}
|
||||
|
||||
func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
|
||||
if s.Schema == nil {
|
||||
return nil, PatchMeta{}, nil
|
||||
}
|
||||
sliceItem := NewSliceItem(key, s.Schema.GetPath())
|
||||
s.Schema.Accept(sliceItem)
|
||||
|
||||
err := sliceItem.Error()
|
||||
if err != nil {
|
||||
return nil, PatchMeta{}, err
|
||||
}
|
||||
return PatchMetaFromOpenAPI{Schema: sliceItem.subschema},
|
||||
sliceItem.patchmeta, nil
|
||||
}
|
||||
|
||||
func (s PatchMetaFromOpenAPI) Name() string {
|
||||
schema := s.Schema
|
||||
return schema.GetName()
|
||||
}
|
320
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
generated
vendored
320
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
generated
vendored
|
@ -22,9 +22,9 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/apimachinery/pkg/util/mergepatch"
|
||||
forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
|
||||
)
|
||||
|
||||
// An alternate implementation of JSON Merge Patch
|
||||
|
@ -92,6 +92,16 @@ type MergeOptions struct {
|
|||
// return a patch that yields the modified document when applied to the original document, or an error
|
||||
// if either of the two documents is invalid.
|
||||
func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
|
||||
schema, err := NewPatchMetaFromStruct(dataStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTwoWayMergePatchUsingLookupPatchMeta(original, modified, schema, fns...)
|
||||
}
|
||||
|
||||
func CreateTwoWayMergePatchUsingLookupPatchMeta(
|
||||
original, modified []byte, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
|
||||
originalMap := map[string]interface{}{}
|
||||
if len(original) > 0 {
|
||||
if err := json.Unmarshal(original, &originalMap); err != nil {
|
||||
|
@ -106,7 +116,7 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, f
|
|||
}
|
||||
}
|
||||
|
||||
patchMap, err := CreateTwoWayMergeMapPatch(originalMap, modifiedMap, dataStruct, fns...)
|
||||
patchMap, err := CreateTwoWayMergeMapPatchUsingLookupPatchMeta(originalMap, modifiedMap, schema, fns...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -118,15 +128,19 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, f
|
|||
// encoded JSONMap.
|
||||
// The serialized version of the map can then be passed to StrategicMergeMapPatch.
|
||||
func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) (JSONMap, error) {
|
||||
t, err := getTagStructType(dataStruct)
|
||||
schema, err := NewPatchMetaFromStruct(dataStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified, schema, fns...)
|
||||
}
|
||||
|
||||
func CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified JSONMap, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) (JSONMap, error) {
|
||||
diffOptions := DiffOptions{
|
||||
SetElementOrder: true,
|
||||
}
|
||||
patchMap, err := diffMaps(original, modified, t, diffOptions)
|
||||
patchMap, err := diffMaps(original, modified, schema, diffOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -151,12 +165,9 @@ func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{
|
|||
// - IFF list of primitives && merge strategy - use parallel deletion list
|
||||
// - IFF list of maps or primitives with replace strategy (default) - set patch value to the value in modified
|
||||
// - Build $retainKeys directive for fields with retainKeys patch strategy
|
||||
func diffMaps(original, modified map[string]interface{}, t reflect.Type, diffOptions DiffOptions) (map[string]interface{}, error) {
|
||||
func diffMaps(original, modified map[string]interface{}, schema LookupPatchMeta, diffOptions DiffOptions) (map[string]interface{}, error) {
|
||||
patch := map[string]interface{}{}
|
||||
// Get the underlying type for pointers
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
// This will be used to build the $retainKeys directive sent in the patch
|
||||
retainKeysList := make([]interface{}, 0, len(modified))
|
||||
|
||||
|
@ -198,10 +209,10 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, diffOpt
|
|||
switch originalValueTyped := originalValue.(type) {
|
||||
case map[string]interface{}:
|
||||
modifiedValueTyped := modifiedValue.(map[string]interface{})
|
||||
err = handleMapDiff(key, originalValueTyped, modifiedValueTyped, patch, t, diffOptions)
|
||||
err = handleMapDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions)
|
||||
case []interface{}:
|
||||
modifiedValueTyped := modifiedValue.([]interface{})
|
||||
err = handleSliceDiff(key, originalValueTyped, modifiedValueTyped, patch, t, diffOptions)
|
||||
err = handleSliceDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions)
|
||||
default:
|
||||
replacePatchFieldIfNotEqual(key, originalValue, modifiedValue, patch, diffOptions)
|
||||
}
|
||||
|
@ -248,8 +259,9 @@ func handleDirectiveMarker(key string, originalValue, modifiedValue interface{},
|
|||
// patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue
|
||||
// diffOptions contains multiple options to control how we do the diff.
|
||||
func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]interface{},
|
||||
t reflect.Type, diffOptions DiffOptions) error {
|
||||
fieldType, fieldPatchStrategies, _, err := forkedjson.LookupPatchMetadata(t, key)
|
||||
schema LookupPatchMeta, diffOptions DiffOptions) error {
|
||||
subschema, patchMeta, err := schema.LookupPatchMetadataForStruct(key)
|
||||
|
||||
if err != nil {
|
||||
// We couldn't look up metadata for the field
|
||||
// If the values are identical, this doesn't matter, no patch is needed
|
||||
|
@ -259,7 +271,7 @@ func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]in
|
|||
// Otherwise, return the error
|
||||
return err
|
||||
}
|
||||
retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
|
||||
retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -271,7 +283,7 @@ func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]in
|
|||
patch[key] = modifiedValue
|
||||
}
|
||||
default:
|
||||
patchValue, err := diffMaps(originalValue, modifiedValue, fieldType, diffOptions)
|
||||
patchValue, err := diffMaps(originalValue, modifiedValue, subschema, diffOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -290,8 +302,8 @@ func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]in
|
|||
// patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue
|
||||
// diffOptions contains multiple options to control how we do the diff.
|
||||
func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, patch map[string]interface{},
|
||||
t reflect.Type, diffOptions DiffOptions) error {
|
||||
fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, key)
|
||||
schema LookupPatchMeta, diffOptions DiffOptions) error {
|
||||
subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(key)
|
||||
if err != nil {
|
||||
// We couldn't look up metadata for the field
|
||||
// If the values are identical, this doesn't matter, no patch is needed
|
||||
|
@ -301,7 +313,7 @@ func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, pat
|
|||
// Otherwise, return the error
|
||||
return err
|
||||
}
|
||||
retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
|
||||
retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -309,7 +321,7 @@ func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, pat
|
|||
// Merge the 2 slices using mergePatchKey
|
||||
case mergeDirective:
|
||||
diffOptions.BuildRetainKeysDirective = retainKeys
|
||||
addList, deletionList, setOrderList, err := diffLists(originalValue, modifiedValue, fieldType.Elem(), fieldPatchMergeKey, diffOptions)
|
||||
addList, deletionList, setOrderList, err := diffLists(originalValue, modifiedValue, subschema, patchMeta.GetPatchMergeKey(), diffOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -536,7 +548,7 @@ func normalizeSliceOrder(toSort, order []interface{}, mergeKey string, kind refl
|
|||
// another list to set the order of the list
|
||||
// Only list of primitives with merge strategy will generate a parallel deletion list.
|
||||
// These two lists should yield modified when applied to original, for lists with merge semantics.
|
||||
func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, []interface{}, error) {
|
||||
func diffLists(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, []interface{}, error) {
|
||||
if len(original) == 0 {
|
||||
// Both slices are empty - do nothing
|
||||
if len(modified) == 0 || diffOptions.IgnoreChangesAndAdditions {
|
||||
|
@ -556,7 +568,7 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string
|
|||
kind := elementType.Kind()
|
||||
switch kind {
|
||||
case reflect.Map:
|
||||
patchList, deleteList, err = diffListsOfMaps(original, modified, t, mergeKey, diffOptions)
|
||||
patchList, deleteList, err = diffListsOfMaps(original, modified, schema, mergeKey, diffOptions)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
@ -702,15 +714,15 @@ func compareListValuesAtIndex(list1Inbounds, list2Inbounds bool, list1Value, lis
|
|||
// diffListsOfMaps takes a pair of lists and
|
||||
// returns a (recursive) strategic merge patch list contains additions and changes and
|
||||
// a deletion list contains deletions
|
||||
func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, error) {
|
||||
func diffListsOfMaps(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, error) {
|
||||
patch := make([]interface{}, 0, len(modified))
|
||||
deletionList := make([]interface{}, 0, len(original))
|
||||
|
||||
originalSorted, err := sortMergeListsByNameArray(original, t, mergeKey, false)
|
||||
originalSorted, err := sortMergeListsByNameArray(original, schema, mergeKey, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
modifiedSorted, err := sortMergeListsByNameArray(modified, t, mergeKey, false)
|
||||
modifiedSorted, err := sortMergeListsByNameArray(modified, schema, mergeKey, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -745,7 +757,7 @@ func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey
|
|||
switch {
|
||||
case bothInBounds && ItemMatchesOriginalAndModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString):
|
||||
// Merge key values are equal, so recurse
|
||||
patchValue, err := diffMaps(originalElement, modifiedElement, t, diffOptions)
|
||||
patchValue, err := diffMaps(originalElement, modifiedElement, schema, diffOptions)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -798,6 +810,15 @@ func getMapAndMergeKeyValueByIndex(index int, mergeKey string, listOfMaps []inte
|
|||
// must be json encoded content. A patch can be created from an original and a modified document
|
||||
// by calling CreateStrategicMergePatch.
|
||||
func StrategicMergePatch(original, patch []byte, dataStruct interface{}) ([]byte, error) {
|
||||
schema, err := NewPatchMetaFromStruct(dataStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return StrategicMergePatchUsingLookupPatchMeta(original, patch, schema)
|
||||
}
|
||||
|
||||
func StrategicMergePatchUsingLookupPatchMeta(original, patch []byte, schema LookupPatchMeta) ([]byte, error) {
|
||||
originalMap, err := handleUnmarshal(original)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -807,7 +828,7 @@ func StrategicMergePatch(original, patch []byte, dataStruct interface{}) ([]byte
|
|||
return nil, err
|
||||
}
|
||||
|
||||
result, err := StrategicMergeMapPatch(originalMap, patchMap, dataStruct)
|
||||
result, err := StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -828,38 +849,35 @@ func handleUnmarshal(j []byte) (map[string]interface{}, error) {
|
|||
return m, nil
|
||||
}
|
||||
|
||||
// StrategicMergePatch applies a strategic merge patch. The original and patch documents
|
||||
// StrategicMergeMapPatch applies a strategic merge patch. The original and patch documents
|
||||
// must be JSONMap. A patch can be created from an original and modified document by
|
||||
// calling CreateTwoWayMergeMapPatch.
|
||||
// Warning: the original and patch JSONMap objects are mutated by this function and should not be reused.
|
||||
func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JSONMap, error) {
|
||||
t, err := getTagStructType(dataStruct)
|
||||
schema, err := NewPatchMetaFromStruct(dataStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We need the go struct tags `patchMergeKey` and `patchStrategy` for fields that support a strategic merge patch.
|
||||
// For native resources, we can easily figure out these tags since we know the fields.
|
||||
|
||||
// Because custom resources are decoded as Unstructured and because we're missing the metadata about how to handle
|
||||
// each field in a strategic merge patch, we can't find the go struct tags. Hence, we can't easily do a strategic merge
|
||||
// for custom resources. So we should fail fast and return an error.
|
||||
if _, ok := dataStruct.(*unstructured.Unstructured); ok {
|
||||
return nil, mergepatch.ErrUnsupportedStrategicMergePatchFormat
|
||||
}
|
||||
|
||||
return StrategicMergeMapPatchUsingLookupPatchMeta(original, patch, schema)
|
||||
}
|
||||
|
||||
func StrategicMergeMapPatchUsingLookupPatchMeta(original, patch JSONMap, schema LookupPatchMeta) (JSONMap, error) {
|
||||
mergeOptions := MergeOptions{
|
||||
MergeParallelList: true,
|
||||
IgnoreUnmatchedNulls: true,
|
||||
}
|
||||
return mergeMap(original, patch, t, mergeOptions)
|
||||
}
|
||||
|
||||
func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
|
||||
if dataStruct == nil {
|
||||
return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
|
||||
}
|
||||
|
||||
t := reflect.TypeOf(dataStruct)
|
||||
// Get the underlying type for pointers
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
return mergeMap(original, patch, schema, mergeOptions)
|
||||
}
|
||||
|
||||
// handleDirectiveInMergeMap handles the patch directive when merging 2 maps.
|
||||
|
@ -1067,7 +1085,7 @@ func applyRetainKeysDirective(original, patch map[string]interface{}, options Me
|
|||
// The precedence is $setElementOrder > order in patch list > order in live list.
|
||||
// This function will delete the item after merging it to prevent process it again in the future.
|
||||
// Ref: https://git.k8s.io/community/contributors/design-proposals/cli/preserve-order-in-strategic-merge-patch.md
|
||||
func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Type, mergeOptions MergeOptions) error {
|
||||
func mergePatchIntoOriginal(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) error {
|
||||
for key, patchV := range patch {
|
||||
// Do nothing if there is no ordering directive
|
||||
if !strings.HasPrefix(key, setElementOrderDirectivePrefix) {
|
||||
|
@ -1094,9 +1112,9 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
|
|||
var (
|
||||
ok bool
|
||||
originalFieldValue, patchFieldValue, merged []interface{}
|
||||
patchStrategy, mergeKey string
|
||||
patchStrategies []string
|
||||
fieldType reflect.Type
|
||||
patchStrategy string
|
||||
patchMeta PatchMeta
|
||||
subschema LookupPatchMeta
|
||||
)
|
||||
typedSetElementOrderList, ok := setElementOrderInPatch.([]interface{})
|
||||
if !ok {
|
||||
|
@ -1122,16 +1140,16 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
|
|||
return mergepatch.ErrBadArgType(patchFieldValue, patchList)
|
||||
}
|
||||
}
|
||||
fieldType, patchStrategies, mergeKey, err = forkedjson.LookupPatchMetadata(t, originalKey)
|
||||
subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(originalKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, patchStrategy, err = extractRetainKeysPatchStrategy(patchStrategies)
|
||||
_, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check for consistency between the element order list and the field it applies to
|
||||
err = validatePatchWithSetOrderList(patchFieldValue, typedSetElementOrderList, mergeKey)
|
||||
err = validatePatchWithSetOrderList(patchFieldValue, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1144,8 +1162,8 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
|
|||
// list was added
|
||||
merged = patchFieldValue
|
||||
case foundOriginal && foundPatch:
|
||||
merged, err = mergeSliceHandler(originalList, patchList, fieldType,
|
||||
patchStrategy, mergeKey, false, mergeOptions)
|
||||
merged, err = mergeSliceHandler(originalList, patchList, subschema,
|
||||
patchStrategy, patchMeta.GetPatchMergeKey(), false, mergeOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1155,13 +1173,13 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
|
|||
|
||||
// Split all items into patch items and server-only items and then enforce the order.
|
||||
var patchItems, serverOnlyItems []interface{}
|
||||
if len(mergeKey) == 0 {
|
||||
if len(patchMeta.GetPatchMergeKey()) == 0 {
|
||||
// Primitives doesn't need merge key to do partitioning.
|
||||
patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, typedSetElementOrderList)
|
||||
|
||||
} else {
|
||||
// Maps need merge key to do partitioning.
|
||||
patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, mergeKey)
|
||||
patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1175,7 +1193,7 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
|
|||
// normalize merged list
|
||||
// typedSetElementOrderList contains all the relative order in typedPatchList,
|
||||
// so don't need to use typedPatchList
|
||||
both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, mergeKey, kind)
|
||||
both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, patchMeta.GetPatchMergeKey(), kind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1237,7 +1255,7 @@ func partitionMapsByPresentInList(original, partitionBy []interface{}, mergeKey
|
|||
// If patch contains any null field (e.g. field_1: null) that is not
|
||||
// present in original, then to propagate it to the end result use
|
||||
// mergeOptions.IgnoreUnmatchedNulls == false.
|
||||
func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptions MergeOptions) (map[string]interface{}, error) {
|
||||
func mergeMap(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) (map[string]interface{}, error) {
|
||||
if v, ok := patch[directiveMarker]; ok {
|
||||
return handleDirectiveInMergeMap(v, patch)
|
||||
}
|
||||
|
@ -1257,7 +1275,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
|
|||
// When not merging the directive, it will make sure $setElementOrder list exist only in original.
|
||||
// When merging the directive, it will process $setElementOrder and its patch list together.
|
||||
// This function will delete the merged elements from patch so they will not be reprocessed
|
||||
err = mergePatchIntoOriginal(original, patch, t, mergeOptions)
|
||||
err = mergePatchIntoOriginal(original, patch, schema, mergeOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1295,11 +1313,6 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
|
|||
continue
|
||||
}
|
||||
|
||||
// If the data type is a pointer, resolve the element.
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
originalType := reflect.TypeOf(original[k])
|
||||
patchType := reflect.TypeOf(patchV)
|
||||
if originalType != patchType {
|
||||
|
@ -1307,22 +1320,27 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
|
|||
continue
|
||||
}
|
||||
// If they're both maps or lists, recurse into the value.
|
||||
// First find the fieldPatchStrategy and fieldPatchMergeKey.
|
||||
fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch originalType.Kind() {
|
||||
case reflect.Map:
|
||||
|
||||
original[k], err = mergeMapHandler(original[k], patchV, fieldType, patchStrategy, mergeOptions)
|
||||
subschema, patchMeta, err := schema.LookupPatchMetadataForStruct(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
original[k], err = mergeMapHandler(original[k], patchV, subschema, patchStrategy, mergeOptions)
|
||||
case reflect.Slice:
|
||||
original[k], err = mergeSliceHandler(original[k], patchV, fieldType, patchStrategy, fieldPatchMergeKey, isDeleteList, mergeOptions)
|
||||
subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
original[k], err = mergeSliceHandler(original[k], patchV, subschema, patchStrategy, patchMeta.GetPatchMergeKey(), isDeleteList, mergeOptions)
|
||||
default:
|
||||
original[k] = patchV
|
||||
}
|
||||
|
@ -1335,7 +1353,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
|
|||
|
||||
// mergeMapHandler handles how to merge `patchV` whose key is `key` with `original` respecting
|
||||
// fieldPatchStrategy and mergeOptions.
|
||||
func mergeMapHandler(original, patch interface{}, fieldType reflect.Type,
|
||||
func mergeMapHandler(original, patch interface{}, schema LookupPatchMeta,
|
||||
fieldPatchStrategy string, mergeOptions MergeOptions) (map[string]interface{}, error) {
|
||||
typedOriginal, typedPatch, err := mapTypeAssertion(original, patch)
|
||||
if err != nil {
|
||||
|
@ -1343,7 +1361,7 @@ func mergeMapHandler(original, patch interface{}, fieldType reflect.Type,
|
|||
}
|
||||
|
||||
if fieldPatchStrategy != replaceDirective {
|
||||
return mergeMap(typedOriginal, typedPatch, fieldType, mergeOptions)
|
||||
return mergeMap(typedOriginal, typedPatch, schema, mergeOptions)
|
||||
} else {
|
||||
return typedPatch, nil
|
||||
}
|
||||
|
@ -1351,7 +1369,7 @@ func mergeMapHandler(original, patch interface{}, fieldType reflect.Type,
|
|||
|
||||
// mergeSliceHandler handles how to merge `patchV` whose key is `key` with `original` respecting
|
||||
// fieldPatchStrategy, fieldPatchMergeKey, isDeleteList and mergeOptions.
|
||||
func mergeSliceHandler(original, patch interface{}, fieldType reflect.Type,
|
||||
func mergeSliceHandler(original, patch interface{}, schema LookupPatchMeta,
|
||||
fieldPatchStrategy, fieldPatchMergeKey string, isDeleteList bool, mergeOptions MergeOptions) ([]interface{}, error) {
|
||||
typedOriginal, typedPatch, err := sliceTypeAssertion(original, patch)
|
||||
if err != nil {
|
||||
|
@ -1359,8 +1377,7 @@ func mergeSliceHandler(original, patch interface{}, fieldType reflect.Type,
|
|||
}
|
||||
|
||||
if fieldPatchStrategy == mergeDirective {
|
||||
elemType := fieldType.Elem()
|
||||
return mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey, mergeOptions, isDeleteList)
|
||||
return mergeSlice(typedOriginal, typedPatch, schema, fieldPatchMergeKey, mergeOptions, isDeleteList)
|
||||
} else {
|
||||
return typedPatch, nil
|
||||
}
|
||||
|
@ -1369,7 +1386,7 @@ func mergeSliceHandler(original, patch interface{}, fieldType reflect.Type,
|
|||
// Merge two slices together. Note: This may modify both the original slice and
|
||||
// the patch because getting a deep copy of a slice in golang is highly
|
||||
// non-trivial.
|
||||
func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) {
|
||||
func mergeSlice(original, patch []interface{}, schema LookupPatchMeta, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) {
|
||||
if len(original) == 0 && len(patch) == 0 {
|
||||
return original, nil
|
||||
}
|
||||
|
@ -1394,7 +1411,7 @@ func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey s
|
|||
|
||||
} else {
|
||||
if mergeKey == "" {
|
||||
return nil, fmt.Errorf("cannot merge lists without merge key for type %s", elemType.Kind().String())
|
||||
return nil, fmt.Errorf("cannot merge lists without merge key for %s", schema.Name())
|
||||
}
|
||||
|
||||
original, patch, err = mergeSliceWithSpecialElements(original, patch, mergeKey)
|
||||
|
@ -1402,7 +1419,7 @@ func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey s
|
|||
return nil, err
|
||||
}
|
||||
|
||||
merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, elemType, mergeOptions)
|
||||
merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, schema, mergeOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1480,7 +1497,7 @@ func deleteMatchingEntries(original []interface{}, mergeKey string, mergeValue i
|
|||
|
||||
// mergeSliceWithoutSpecialElements merges slices with non-special elements.
|
||||
// original and patch must be slices of maps, they should be checked before calling this function.
|
||||
func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, elemType reflect.Type, mergeOptions MergeOptions) ([]interface{}, error) {
|
||||
func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, schema LookupPatchMeta, mergeOptions MergeOptions) ([]interface{}, error) {
|
||||
for _, v := range patch {
|
||||
typedV := v.(map[string]interface{})
|
||||
mergeValue, ok := typedV[mergeKey]
|
||||
|
@ -1499,7 +1516,7 @@ func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey st
|
|||
var mergedMaps interface{}
|
||||
var err error
|
||||
// Merge into original.
|
||||
mergedMaps, err = mergeMap(originalMap, typedV, elemType, mergeOptions)
|
||||
mergedMaps, err = mergeMap(originalMap, typedV, schema, mergeOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1532,7 +1549,7 @@ func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{
|
|||
for k, v := range m {
|
||||
typedV, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, 0, false, fmt.Errorf("value for key %v is not a map.", k)
|
||||
return nil, 0, false, fmt.Errorf("value for key %v is not a map", k)
|
||||
}
|
||||
|
||||
valueToMatch, ok := typedV[key]
|
||||
|
@ -1548,14 +1565,14 @@ func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{
|
|||
// by key. This is needed by tests because in JSON, list order is significant,
|
||||
// but in Strategic Merge Patch, merge lists do not have significant order.
|
||||
// Sorting the lists allows for order-insensitive comparison of patched maps.
|
||||
func sortMergeListsByName(mapJSON []byte, dataStruct interface{}) ([]byte, error) {
|
||||
func sortMergeListsByName(mapJSON []byte, schema LookupPatchMeta) ([]byte, error) {
|
||||
var m map[string]interface{}
|
||||
err := json.Unmarshal(mapJSON, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, mergepatch.ErrBadJSONDoc
|
||||
}
|
||||
|
||||
newM, err := sortMergeListsByNameMap(m, reflect.TypeOf(dataStruct))
|
||||
newM, err := sortMergeListsByNameMap(m, schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1564,7 +1581,7 @@ func sortMergeListsByName(mapJSON []byte, dataStruct interface{}) ([]byte, error
|
|||
}
|
||||
|
||||
// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in a map.
|
||||
func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[string]interface{}, error) {
|
||||
func sortMergeListsByNameMap(s map[string]interface{}, schema LookupPatchMeta) (map[string]interface{}, error) {
|
||||
newS := map[string]interface{}{}
|
||||
for k, v := range s {
|
||||
if k == retainKeysDirective {
|
||||
|
@ -1585,26 +1602,29 @@ func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[stri
|
|||
return nil, mergepatch.ErrBadPatchFormatForSetElementOrderList
|
||||
}
|
||||
} else if k != directiveMarker {
|
||||
fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If v is a map or a merge slice, recurse.
|
||||
if typedV, ok := v.(map[string]interface{}); ok {
|
||||
var err error
|
||||
v, err = sortMergeListsByNameMap(typedV, fieldType)
|
||||
// recurse for map and slice.
|
||||
switch typedV := v.(type) {
|
||||
case map[string]interface{}:
|
||||
subschema, _, err := schema.LookupPatchMetadataForStruct(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err = sortMergeListsByNameMap(typedV, subschema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case []interface{}:
|
||||
subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if typedV, ok := v.([]interface{}); ok {
|
||||
if patchStrategy == mergeDirective {
|
||||
var err error
|
||||
v, err = sortMergeListsByNameArray(typedV, fieldType.Elem(), fieldPatchMergeKey, true)
|
||||
v, err = sortMergeListsByNameArray(typedV, subschema, patchMeta.GetPatchMergeKey(), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1619,7 +1639,7 @@ func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[stri
|
|||
}
|
||||
|
||||
// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in an array.
|
||||
func sortMergeListsByNameArray(s []interface{}, elemType reflect.Type, mergeKey string, recurse bool) ([]interface{}, error) {
|
||||
func sortMergeListsByNameArray(s []interface{}, schema LookupPatchMeta, mergeKey string, recurse bool) ([]interface{}, error) {
|
||||
if len(s) == 0 {
|
||||
return s, nil
|
||||
}
|
||||
|
@ -1642,7 +1662,7 @@ func sortMergeListsByNameArray(s []interface{}, elemType reflect.Type, mergeKey
|
|||
for _, elem := range s {
|
||||
if recurse {
|
||||
typedElem := elem.(map[string]interface{})
|
||||
newElem, err := sortMergeListsByNameMap(typedElem, elemType)
|
||||
newElem, err := sortMergeListsByNameMap(typedElem, schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1788,18 +1808,13 @@ func sliceElementType(slices ...[]interface{}) (reflect.Type, error) {
|
|||
// 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 strategic merge patch semantics.
|
||||
func MergingMapsHaveConflicts(left, right map[string]interface{}, dataStruct interface{}) (bool, error) {
|
||||
t, err := getTagStructType(dataStruct)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return mergingMapFieldsHaveConflicts(left, right, t, "", "")
|
||||
func MergingMapsHaveConflicts(left, right map[string]interface{}, schema LookupPatchMeta) (bool, error) {
|
||||
return mergingMapFieldsHaveConflicts(left, right, schema, "", "")
|
||||
}
|
||||
|
||||
func mergingMapFieldsHaveConflicts(
|
||||
left, right interface{},
|
||||
fieldType reflect.Type,
|
||||
schema LookupPatchMeta,
|
||||
fieldPatchStrategy, fieldPatchMergeKey string,
|
||||
) (bool, error) {
|
||||
switch leftType := left.(type) {
|
||||
|
@ -1830,15 +1845,14 @@ func mergingMapFieldsHaveConflicts(
|
|||
return false, nil
|
||||
}
|
||||
// Check the individual keys.
|
||||
return mapsHaveConflicts(leftType, rightType, fieldType)
|
||||
return mapsHaveConflicts(leftType, rightType, schema)
|
||||
|
||||
case []interface{}:
|
||||
rightType, ok := right.([]interface{})
|
||||
if !ok {
|
||||
return true, nil
|
||||
}
|
||||
return slicesHaveConflicts(leftType, rightType, fieldType, fieldPatchStrategy, fieldPatchMergeKey)
|
||||
|
||||
return slicesHaveConflicts(leftType, rightType, schema, fieldPatchStrategy, fieldPatchMergeKey)
|
||||
case string, float64, bool, int, int64, nil:
|
||||
return !reflect.DeepEqual(left, right), nil
|
||||
default:
|
||||
|
@ -1846,21 +1860,37 @@ func mergingMapFieldsHaveConflicts(
|
|||
}
|
||||
}
|
||||
|
||||
func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) {
|
||||
func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
|
||||
for key, leftValue := range typedLeft {
|
||||
if key != directiveMarker && key != retainKeysDirective {
|
||||
if rightValue, ok := typedRight[key]; ok {
|
||||
fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(structType, key)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
_, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
|
||||
if err != nil {
|
||||
return true, err
|
||||
var subschema LookupPatchMeta
|
||||
var patchMeta PatchMeta
|
||||
var patchStrategy string
|
||||
var err error
|
||||
switch leftValue.(type) {
|
||||
case []interface{}:
|
||||
subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(key)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
_, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
case map[string]interface{}:
|
||||
subschema, patchMeta, err = schema.LookupPatchMetadataForStruct(key)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
_, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue,
|
||||
fieldType, patchStrategy, fieldPatchMergeKey); hasConflicts {
|
||||
subschema, patchStrategy, patchMeta.GetPatchMergeKey()); hasConflicts {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
@ -1872,7 +1902,7 @@ func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType
|
|||
|
||||
func slicesHaveConflicts(
|
||||
typedLeft, typedRight []interface{},
|
||||
fieldType reflect.Type,
|
||||
schema LookupPatchMeta,
|
||||
fieldPatchStrategy, fieldPatchMergeKey string,
|
||||
) (bool, error) {
|
||||
elementType, err := sliceElementType(typedLeft, typedRight)
|
||||
|
@ -1880,7 +1910,6 @@ func slicesHaveConflicts(
|
|||
return true, err
|
||||
}
|
||||
|
||||
valueType := fieldType.Elem()
|
||||
if fieldPatchStrategy == mergeDirective {
|
||||
// Merging lists of scalars have no conflicts by definition
|
||||
// So we only need to check further if the elements are maps
|
||||
|
@ -1899,7 +1928,7 @@ func slicesHaveConflicts(
|
|||
return true, err
|
||||
}
|
||||
|
||||
return mapsOfMapsHaveConflicts(leftMap, rightMap, valueType)
|
||||
return mapsOfMapsHaveConflicts(leftMap, rightMap, schema)
|
||||
}
|
||||
|
||||
// Either we don't have type information, or these are non-merging lists
|
||||
|
@ -1917,7 +1946,7 @@ func slicesHaveConflicts(
|
|||
// Compare the slices element by element in order
|
||||
// This test will fail if the slices are not sorted
|
||||
for i := range typedLeft {
|
||||
if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], valueType, "", ""); hasConflicts {
|
||||
if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], schema, "", ""); hasConflicts {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
@ -1944,10 +1973,10 @@ func sliceOfMapsToMapOfMaps(slice []interface{}, mergeKey string) (map[string]in
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) {
|
||||
func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
|
||||
for key, leftValue := range typedLeft {
|
||||
if rightValue, ok := typedRight[key]; ok {
|
||||
if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, structType, "", ""); hasConflicts {
|
||||
if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, schema, "", ""); hasConflicts {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
@ -1967,7 +1996,7 @@ func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, struc
|
|||
// in a way that is different from how it is changed in current (e.g., deleting it, changing its
|
||||
// value). We also propagate values fields that do not exist in original but are explicitly
|
||||
// defined in modified.
|
||||
func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
|
||||
func CreateThreeWayMergePatch(original, modified, current []byte, schema LookupPatchMeta, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
|
||||
originalMap := map[string]interface{}{}
|
||||
if len(original) > 0 {
|
||||
if err := json.Unmarshal(original, &originalMap); err != nil {
|
||||
|
@ -1989,11 +2018,6 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
|
|||
}
|
||||
}
|
||||
|
||||
t, err := getTagStructType(dataStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The patch is the difference from current to modified without deletions, plus deletions
|
||||
// from original to modified. To find it, we compute deletions, which are the deletions from
|
||||
// original to modified, and delta, which is the difference from current to modified without
|
||||
|
@ -2002,7 +2026,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
|
|||
IgnoreDeletions: true,
|
||||
SetElementOrder: true,
|
||||
}
|
||||
deltaMap, err := diffMaps(currentMap, modifiedMap, t, deltaMapDiffOptions)
|
||||
deltaMap, err := diffMaps(currentMap, modifiedMap, schema, deltaMapDiffOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2010,13 +2034,13 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
|
|||
SetElementOrder: true,
|
||||
IgnoreChangesAndAdditions: true,
|
||||
}
|
||||
deletionsMap, err := diffMaps(originalMap, modifiedMap, t, deletionsMapDiffOptions)
|
||||
deletionsMap, err := diffMaps(originalMap, modifiedMap, schema, deletionsMapDiffOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mergeOptions := MergeOptions{}
|
||||
patchMap, err := mergeMap(deletionsMap, deltaMap, t, mergeOptions)
|
||||
patchMap, err := mergeMap(deletionsMap, deltaMap, schema, mergeOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2032,12 +2056,12 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
|
|||
// then return a conflict error.
|
||||
if !overwrite {
|
||||
changeMapDiffOptions := DiffOptions{}
|
||||
changedMap, err := diffMaps(originalMap, currentMap, t, changeMapDiffOptions)
|
||||
changedMap, err := diffMaps(originalMap, currentMap, schema, changeMapDiffOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, dataStruct)
|
||||
hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
193
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
generated
vendored
Normal file
193
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
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 strategicpatch
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/mergepatch"
|
||||
openapi "k8s.io/kube-openapi/pkg/util/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
patchStrategyOpenapiextensionKey = "x-kubernetes-patch-strategy"
|
||||
patchMergeKeyOpenapiextensionKey = "x-kubernetes-patch-merge-key"
|
||||
)
|
||||
|
||||
type LookupPatchItem interface {
|
||||
openapi.SchemaVisitor
|
||||
|
||||
Error() error
|
||||
Path() *openapi.Path
|
||||
}
|
||||
|
||||
type kindItem struct {
|
||||
key string
|
||||
path *openapi.Path
|
||||
err error
|
||||
patchmeta PatchMeta
|
||||
subschema openapi.Schema
|
||||
hasVisitKind bool
|
||||
}
|
||||
|
||||
func NewKindItem(key string, path *openapi.Path) *kindItem {
|
||||
return &kindItem{
|
||||
key: key,
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
var _ LookupPatchItem = &kindItem{}
|
||||
|
||||
func (item *kindItem) Error() error {
|
||||
return item.err
|
||||
}
|
||||
|
||||
func (item *kindItem) Path() *openapi.Path {
|
||||
return item.path
|
||||
}
|
||||
|
||||
func (item *kindItem) VisitPrimitive(schema *openapi.Primitive) {
|
||||
item.err = errors.New("expected kind, but got primitive")
|
||||
}
|
||||
|
||||
func (item *kindItem) VisitArray(schema *openapi.Array) {
|
||||
item.err = errors.New("expected kind, but got slice")
|
||||
}
|
||||
|
||||
func (item *kindItem) VisitMap(schema *openapi.Map) {
|
||||
item.err = errors.New("expected kind, but got map")
|
||||
}
|
||||
|
||||
func (item *kindItem) VisitReference(schema openapi.Reference) {
|
||||
if !item.hasVisitKind {
|
||||
schema.SubSchema().Accept(item)
|
||||
}
|
||||
}
|
||||
|
||||
func (item *kindItem) VisitKind(schema *openapi.Kind) {
|
||||
subschema, ok := schema.Fields[item.key]
|
||||
if !ok {
|
||||
item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
|
||||
return
|
||||
}
|
||||
|
||||
mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
|
||||
if err != nil {
|
||||
item.err = err
|
||||
return
|
||||
}
|
||||
item.patchmeta = PatchMeta{
|
||||
patchStrategies: patchStrategies,
|
||||
patchMergeKey: mergeKey,
|
||||
}
|
||||
item.subschema = subschema
|
||||
}
|
||||
|
||||
type sliceItem struct {
|
||||
key string
|
||||
path *openapi.Path
|
||||
err error
|
||||
patchmeta PatchMeta
|
||||
subschema openapi.Schema
|
||||
hasVisitKind bool
|
||||
}
|
||||
|
||||
func NewSliceItem(key string, path *openapi.Path) *sliceItem {
|
||||
return &sliceItem{
|
||||
key: key,
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
var _ LookupPatchItem = &sliceItem{}
|
||||
|
||||
func (item *sliceItem) Error() error {
|
||||
return item.err
|
||||
}
|
||||
|
||||
func (item *sliceItem) Path() *openapi.Path {
|
||||
return item.path
|
||||
}
|
||||
|
||||
func (item *sliceItem) VisitPrimitive(schema *openapi.Primitive) {
|
||||
item.err = errors.New("expected slice, but got primitive")
|
||||
}
|
||||
|
||||
func (item *sliceItem) VisitArray(schema *openapi.Array) {
|
||||
if !item.hasVisitKind {
|
||||
item.err = errors.New("expected visit kind first, then visit array")
|
||||
}
|
||||
subschema := schema.SubType
|
||||
item.subschema = subschema
|
||||
}
|
||||
|
||||
func (item *sliceItem) VisitMap(schema *openapi.Map) {
|
||||
item.err = errors.New("expected slice, but got map")
|
||||
}
|
||||
|
||||
func (item *sliceItem) VisitReference(schema openapi.Reference) {
|
||||
if !item.hasVisitKind {
|
||||
schema.SubSchema().Accept(item)
|
||||
} else {
|
||||
item.subschema = schema.SubSchema()
|
||||
}
|
||||
}
|
||||
|
||||
func (item *sliceItem) VisitKind(schema *openapi.Kind) {
|
||||
subschema, ok := schema.Fields[item.key]
|
||||
if !ok {
|
||||
item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
|
||||
return
|
||||
}
|
||||
|
||||
mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
|
||||
if err != nil {
|
||||
item.err = err
|
||||
return
|
||||
}
|
||||
item.patchmeta = PatchMeta{
|
||||
patchStrategies: patchStrategies,
|
||||
patchMergeKey: mergeKey,
|
||||
}
|
||||
item.hasVisitKind = true
|
||||
subschema.Accept(item)
|
||||
}
|
||||
|
||||
func parsePatchMetadata(extensions map[string]interface{}) (string, []string, error) {
|
||||
ps, foundPS := extensions[patchStrategyOpenapiextensionKey]
|
||||
var patchStrategies []string
|
||||
var mergeKey, patchStrategy string
|
||||
var ok bool
|
||||
if foundPS {
|
||||
patchStrategy, ok = ps.(string)
|
||||
if ok {
|
||||
patchStrategies = strings.Split(patchStrategy, ",")
|
||||
} else {
|
||||
return "", nil, mergepatch.ErrBadArgType(patchStrategy, ps)
|
||||
}
|
||||
}
|
||||
mk, foundMK := extensions[patchMergeKeyOpenapiextensionKey]
|
||||
if foundMK {
|
||||
mergeKey, ok = mk.(string)
|
||||
if !ok {
|
||||
return "", nil, mergepatch.ErrBadArgType(mergeKey, mk)
|
||||
}
|
||||
}
|
||||
return mergeKey, patchStrategies, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue