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 }