github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/configuration/template_merger.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package configuration 21 22 import ( 23 "context" 24 25 "sigs.k8s.io/controller-runtime/pkg/client" 26 27 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 28 "github.com/1aal/kubeblocks/pkg/configuration/core" 29 ) 30 31 type TemplateMerger interface { 32 33 // Merge merges the baseData with the data from the template. 34 Merge(baseData map[string]string, updatedData map[string]string) (map[string]string, error) 35 36 // renderTemplate renders the template and returns the data. 37 renderTemplate() (map[string]string, error) 38 } 39 40 type mergeContext struct { 41 template appsv1alpha1.ConfigTemplateExtension 42 configSpec appsv1alpha1.ComponentConfigSpec 43 ccSpec *appsv1alpha1.ConfigConstraintSpec 44 45 builder *configTemplateBuilder 46 ctx context.Context 47 client client.Client 48 } 49 50 func (m *mergeContext) renderTemplate() (map[string]string, error) { 51 templateSpec := appsv1alpha1.ComponentTemplateSpec{ 52 // Name: m.template.Name, 53 Namespace: m.template.Namespace, 54 TemplateRef: m.template.TemplateRef, 55 } 56 configs, err := renderConfigMapTemplate(m.builder, templateSpec, m.ctx, m.client) 57 if err != nil { 58 return nil, err 59 } 60 if err := validateRawData(configs, m.configSpec, m.ccSpec); err != nil { 61 return nil, err 62 } 63 return configs, nil 64 } 65 66 type noneOp struct { 67 *mergeContext 68 } 69 70 func (n noneOp) Merge(_ map[string]string, updatedData map[string]string) (map[string]string, error) { 71 return updatedData, nil 72 } 73 74 type configPatcher struct { 75 *mergeContext 76 } 77 78 type configReplaceMerger struct { 79 *mergeContext 80 } 81 82 type configOnlyAddMerger struct { 83 *mergeContext 84 } 85 86 func (c *configPatcher) Merge(baseData map[string]string, updatedData map[string]string) (map[string]string, error) { 87 formatter := c.ccSpec.FormatterConfig 88 configPatch, err := core.TransformConfigPatchFromData(updatedData, formatter.Format, c.configSpec.Keys) 89 if err != nil { 90 return nil, err 91 } 92 if !configPatch.IsModify { 93 return baseData, nil 94 } 95 96 r := make(map[string]string) 97 params := core.GenerateVisualizedParamsList(configPatch, formatter, nil) 98 for key, patch := range splitParameters(params) { 99 v, ok := baseData[key] 100 if !ok { 101 r[key] = updatedData[key] 102 continue 103 } 104 newConfig, err := core.ApplyConfigPatch([]byte(v), patch, formatter) 105 if err != nil { 106 return nil, err 107 } 108 r[key] = newConfig 109 } 110 return r, err 111 } 112 113 func (c *configReplaceMerger) Merge(baseData map[string]string, updatedData map[string]string) (map[string]string, error) { 114 return core.MergeUpdatedConfig(baseData, updatedData), nil 115 } 116 117 func (c *configOnlyAddMerger) Merge(baseData map[string]string, updatedData map[string]string) (map[string]string, error) { 118 return nil, core.MakeError("not implemented") 119 } 120 121 func NewTemplateMerger(template appsv1alpha1.ConfigTemplateExtension, ctx context.Context, cli client.Client, builder *configTemplateBuilder, configSpec appsv1alpha1.ComponentConfigSpec, ccSpec *appsv1alpha1.ConfigConstraintSpec) (TemplateMerger, error) { 122 templateData := &mergeContext{ 123 configSpec: configSpec, 124 template: template, 125 ctx: ctx, 126 client: cli, 127 builder: builder, 128 ccSpec: ccSpec, 129 } 130 131 var merger TemplateMerger 132 switch template.Policy { 133 default: 134 return nil, core.MakeError("unknown template policy: %s", template.Policy) 135 case appsv1alpha1.NoneMergePolicy: 136 merger = &noneOp{templateData} 137 case appsv1alpha1.PatchPolicy: 138 merger = &configPatcher{templateData} 139 case appsv1alpha1.OnlyAddPolicy: 140 merger = &configOnlyAddMerger{templateData} 141 case appsv1alpha1.ReplacePolicy: 142 merger = &configReplaceMerger{templateData} 143 } 144 return merger, nil 145 } 146 147 func mergerConfigTemplate(template *appsv1alpha1.LegacyRenderedTemplateSpec, 148 builder *configTemplateBuilder, 149 configSpec appsv1alpha1.ComponentConfigSpec, 150 baseData map[string]string, 151 ctx context.Context, cli client.Client) (map[string]string, error) { 152 if configSpec.ConfigConstraintRef == "" { 153 return nil, core.MakeError("ConfigConstraintRef require not empty, configSpec[%v]", configSpec.Name) 154 } 155 ccObj := &appsv1alpha1.ConfigConstraint{} 156 ccKey := client.ObjectKey{ 157 Namespace: "", 158 Name: configSpec.ConfigConstraintRef, 159 } 160 if err := cli.Get(ctx, ccKey, ccObj); err != nil { 161 return nil, core.WrapError(err, "failed to get ConfigConstraint, key[%v]", configSpec) 162 } 163 if ccObj.Spec.FormatterConfig == nil { 164 return nil, core.MakeError("importedConfigTemplate require ConfigConstraint.Spec.FormatterConfig, configSpec[%v]", configSpec) 165 } 166 167 templateMerger, err := NewTemplateMerger(template.ConfigTemplateExtension, ctx, cli, builder, configSpec, &ccObj.Spec) 168 if err != nil { 169 return nil, err 170 } 171 data, err := templateMerger.renderTemplate() 172 if err != nil { 173 return nil, err 174 } 175 if len(data) == 0 { 176 return nil, nil 177 } 178 return templateMerger.Merge(baseData, data) 179 } 180 181 func splitParameters(params []core.VisualizedParam) map[string]map[string]*string { 182 r := make(map[string]map[string]*string) 183 for _, param := range params { 184 if _, ok := r[param.Key]; !ok { 185 r[param.Key] = make(map[string]*string) 186 } 187 for _, kv := range param.Parameters { 188 r[param.Key][kv.Key] = kv.Value 189 } 190 } 191 return r 192 }