...

Source file src/k8s.io/apimachinery/pkg/util/mergepatch/util.go

Documentation: k8s.io/apimachinery/pkg/util/mergepatch

     1  /*
     2  Copyright 2017 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 mergepatch
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  
    23  	"k8s.io/apimachinery/pkg/util/dump"
    24  	"sigs.k8s.io/yaml"
    25  )
    26  
    27  // PreconditionFunc asserts that an incompatible change is not present within a patch.
    28  type PreconditionFunc func(interface{}) bool
    29  
    30  // RequireKeyUnchanged returns a precondition function that fails if the provided key
    31  // is present in the patch (indicating that its value has changed).
    32  func RequireKeyUnchanged(key string) PreconditionFunc {
    33  	return func(patch interface{}) bool {
    34  		patchMap, ok := patch.(map[string]interface{})
    35  		if !ok {
    36  			return true
    37  		}
    38  
    39  		// The presence of key means that its value has been changed, so the test fails.
    40  		_, ok = patchMap[key]
    41  		return !ok
    42  	}
    43  }
    44  
    45  // RequireMetadataKeyUnchanged creates a precondition function that fails
    46  // if the metadata.key is present in the patch (indicating its value
    47  // has changed).
    48  func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
    49  	return func(patch interface{}) bool {
    50  		patchMap, ok := patch.(map[string]interface{})
    51  		if !ok {
    52  			return true
    53  		}
    54  		patchMap1, ok := patchMap["metadata"]
    55  		if !ok {
    56  			return true
    57  		}
    58  		patchMap2, ok := patchMap1.(map[string]interface{})
    59  		if !ok {
    60  			return true
    61  		}
    62  		_, ok = patchMap2[key]
    63  		return !ok
    64  	}
    65  }
    66  
    67  func ToYAMLOrError(v interface{}) string {
    68  	y, err := toYAML(v)
    69  	if err != nil {
    70  		return err.Error()
    71  	}
    72  
    73  	return y
    74  }
    75  
    76  func toYAML(v interface{}) (string, error) {
    77  	y, err := yaml.Marshal(v)
    78  	if err != nil {
    79  		return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, dump.Pretty(v))
    80  	}
    81  
    82  	return string(y), nil
    83  }
    84  
    85  // HasConflicts returns true if the left and right JSON interface objects overlap with
    86  // different values in any key. All keys are required to be strings. Since patches of the
    87  // same Type have congruent keys, this is valid for multiple patch types. This method
    88  // supports JSON merge patch semantics.
    89  //
    90  // NOTE: Numbers with different types (e.g. int(0) vs int64(0)) will be detected as conflicts.
    91  // Make sure the unmarshaling of left and right are consistent (e.g. use the same library).
    92  func HasConflicts(left, right interface{}) (bool, error) {
    93  	switch typedLeft := left.(type) {
    94  	case map[string]interface{}:
    95  		switch typedRight := right.(type) {
    96  		case map[string]interface{}:
    97  			for key, leftValue := range typedLeft {
    98  				rightValue, ok := typedRight[key]
    99  				if !ok {
   100  					continue
   101  				}
   102  				if conflict, err := HasConflicts(leftValue, rightValue); err != nil || conflict {
   103  					return conflict, err
   104  				}
   105  			}
   106  
   107  			return false, nil
   108  		default:
   109  			return true, nil
   110  		}
   111  	case []interface{}:
   112  		switch typedRight := right.(type) {
   113  		case []interface{}:
   114  			if len(typedLeft) != len(typedRight) {
   115  				return true, nil
   116  			}
   117  
   118  			for i := range typedLeft {
   119  				if conflict, err := HasConflicts(typedLeft[i], typedRight[i]); err != nil || conflict {
   120  					return conflict, err
   121  				}
   122  			}
   123  
   124  			return false, nil
   125  		default:
   126  			return true, nil
   127  		}
   128  	case string, float64, bool, int64, nil:
   129  		return !reflect.DeepEqual(left, right), nil
   130  	default:
   131  		return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
   132  	}
   133  }
   134  

View as plain text