github.com/banzaicloud/operator-tools@v0.28.10/pkg/resources/overlay.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 resources 16 17 import ( 18 "emperror.dev/errors" 19 ypatch "github.com/cppforlife/go-patch/patch" 20 "gopkg.in/yaml.v2" 21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 "k8s.io/apimachinery/pkg/runtime" 23 "k8s.io/apimachinery/pkg/runtime/schema" 24 k8syaml "sigs.k8s.io/yaml" 25 26 "github.com/banzaicloud/operator-tools/pkg/types" 27 "github.com/banzaicloud/operator-tools/pkg/utils" 28 ) 29 30 type GroupVersionKind struct { 31 Group string `json:"group,omitempty"` 32 Version string `json:"version,omitempty"` 33 Kind string `json:"kind,omitempty"` 34 } 35 36 // +kubebuilder:object:generate=true 37 type K8SResourceOverlay struct { 38 GVK *GroupVersionKind `json:"groupVersionKind,omitempty"` 39 ObjectKey types.ObjectKey `json:"objectKey,omitempty"` 40 Patches []K8SResourceOverlayPatch `json:"patches,omitempty"` 41 } 42 43 // +kubebuilder:object:generate=true 44 type OverlayPatchType string 45 46 const ( 47 ReplaceOverlayPatchType OverlayPatchType = "replace" 48 DeleteOverlayPatchType OverlayPatchType = "remove" 49 ) 50 51 // +kubebuilder:object:generate=true 52 type K8SResourceOverlayPatch struct { 53 Type OverlayPatchType `json:"type,omitempty"` 54 Path *string `json:"path,omitempty"` 55 Value *string `json:"value,omitempty"` 56 ParseValue bool `json:"parseValue,omitempty"` 57 } 58 59 func PatchYAMLModifier(overlay K8SResourceOverlay, parser *ObjectParser) (ObjectModifierFunc, error) { 60 if len(overlay.Patches) == 0 { 61 return func(o runtime.Object) (runtime.Object, error) { 62 return o, nil 63 }, nil 64 } 65 66 var opsDefinitions []ypatch.OpDefinition 67 for _, patch := range overlay.Patches { 68 var value interface{} 69 if patch.ParseValue { 70 err := yaml.Unmarshal([]byte(utils.PointerToString(patch.Value)), &value) 71 if err != nil { 72 return nil, errors.WrapIf(err, "could not unmarshal value") 73 } 74 } else { 75 value = interface{}(patch.Value) 76 } 77 78 op := ypatch.OpDefinition{ 79 Type: string(patch.Type), 80 Path: patch.Path, 81 } 82 if patch.Type == ReplaceOverlayPatchType { 83 op.Value = &value 84 } 85 opsDefinitions = append(opsDefinitions, op) 86 } 87 88 return func(o runtime.Object) (runtime.Object, error) { 89 var ok bool 90 var meta metav1.Object 91 92 if overlay.GVK != nil { 93 gvk := o.GetObjectKind().GroupVersionKind() 94 if overlay.GVK.Group != "" && overlay.GVK.Group != gvk.Group { 95 return o, nil 96 } 97 if overlay.GVK.Version != "" && overlay.GVK.Version != gvk.Version { 98 return o, nil 99 } 100 if overlay.GVK.Kind != "" && overlay.GVK.Kind != gvk.Kind { 101 return o, nil 102 } 103 } 104 105 if meta, ok = o.(metav1.Object); !ok { 106 return o, nil 107 } 108 109 if (overlay.ObjectKey.Name != "" && meta.GetName() != overlay.ObjectKey.Name) || (overlay.ObjectKey.Namespace != "" && meta.GetNamespace() != overlay.ObjectKey.Namespace) { 110 return o, nil 111 } 112 113 y, err := k8syaml.Marshal(o) 114 if err != nil { 115 return o, errors.WrapIf(err, "could not marshal runtime object") 116 } 117 118 ops, err := ypatch.NewOpsFromDefinitions(opsDefinitions) 119 if err != nil { 120 return o, errors.WrapIf(err, "could not init patch ops from definitions") 121 } 122 123 var in interface{} 124 err = yaml.Unmarshal(y, &in) 125 if err != nil { 126 return o, errors.WrapIf(err, "could not unmarshal resource yaml") 127 } 128 129 res, err := ops.Apply(in) 130 if err != nil { 131 return o, errors.WrapIf(err, "could not apply patch ops") 132 } 133 134 y, err = yaml.Marshal(res) 135 if err != nil { 136 return o, errors.WrapIf(err, "could not marshal patched object to yaml") 137 } 138 139 o, err = parser.ParseYAMLToK8sObject(y) 140 if err != nil { 141 return o, errors.WrapIf(err, "could not parse runtime object from yaml") 142 } 143 144 return o, nil 145 }, nil 146 } 147 148 func ConvertGVK(gvk schema.GroupVersionKind) GroupVersionKind { 149 return GroupVersionKind{ 150 Group: gvk.Group, 151 Version: gvk.Version, 152 Kind: gvk.Kind, 153 } 154 }