github.com/banzaicloud/operator-tools@v0.28.10/pkg/merge/merge.go (about)

     1  // Copyright © 2020 Banzai Cloud
     2  //
     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  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package merge
    16  
    17  import (
    18  	"encoding/json"
    19  	"reflect"
    20  
    21  	"emperror.dev/errors"
    22  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    23  )
    24  
    25  // Merge merges `overrides` into `base` using the SMP (structural merge patch) approach.
    26  // - It intentionally does not remove fields present in base but missing from overrides
    27  // - It merges slices only if the `patchStrategy:"merge"` tag is present and the `patchMergeKey` identifies the unique field
    28  func Merge(base, overrides interface{}) error {
    29  	baseBytes, err := json.Marshal(base)
    30  	if err != nil {
    31  		return errors.Wrap(err, "failed to convert current object to byte sequence")
    32  	}
    33  
    34  	overrideBytes, err := json.Marshal(overrides)
    35  	if err != nil {
    36  		return errors.Wrap(err, "failed to convert current object to byte sequence")
    37  	}
    38  
    39  	patchMeta, err := strategicpatch.NewPatchMetaFromStruct(base)
    40  	if err != nil {
    41  		return errors.WrapIf(err, "failed to produce patch meta from struct")
    42  	}
    43  	patch, err := strategicpatch.CreateThreeWayMergePatch(overrideBytes, overrideBytes, baseBytes, patchMeta, true)
    44  	if err != nil {
    45  		return errors.WrapIf(err, "failed to create three way merge patch")
    46  	}
    47  
    48  	merged, err := strategicpatch.StrategicMergePatchUsingLookupPatchMeta(baseBytes, patch, patchMeta)
    49  	if err != nil {
    50  		return errors.WrapIf(err, "failed to apply patch")
    51  	}
    52  
    53  	valueOfBase := reflect.Indirect(reflect.ValueOf(base))
    54  	into := reflect.New(valueOfBase.Type())
    55  	if err := json.Unmarshal(merged, into.Interface()); err != nil {
    56  		return err
    57  	}
    58  	if !valueOfBase.CanSet() {
    59  		return errors.New("unable to set unmarshalled value into base object")
    60  	}
    61  	valueOfBase.Set(reflect.Indirect(into))
    62  	return nil
    63  }