github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/configuration/config_template.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 "encoding/json" 25 "fmt" 26 27 corev1 "k8s.io/api/core/v1" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 30 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 31 "github.com/1aal/kubeblocks/pkg/constant" 32 ictrlclient "github.com/1aal/kubeblocks/pkg/controller/client" 33 "github.com/1aal/kubeblocks/pkg/controller/component" 34 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 35 "github.com/1aal/kubeblocks/pkg/gotemplate" 36 viper "github.com/1aal/kubeblocks/pkg/viperx" 37 ) 38 39 // General Built-in objects 40 const ( 41 builtinClusterObject = "cluster" 42 builtinComponentObject = "component" 43 builtinPodObject = "podSpec" 44 builtinClusterVersionObject = "version" 45 builtinComponentResourceObject = "componentResource" 46 builtinClusterDomainObject = "clusterDomain" 47 ) 48 49 // General Built-in functions 50 const ( 51 builtInGetVolumeFunctionName = "getVolumePathByName" 52 builtInGetPvcFunctionName = "getPVCByName" 53 builtInGetEnvFunctionName = "getEnvByName" 54 builtInGetArgFunctionName = "getArgByName" 55 builtInGetPortFunctionName = "getPortByName" 56 builtInGetContainerFunctionName = "getContainerByName" 57 builtInGetContainerCPUFunctionName = "getContainerCPU" 58 builtInGetPVCSizeByNameFunctionName = "getComponentPVCSizeByName" 59 builtInGetPVCSizeFunctionName = "getPVCSize" 60 builtInGetContainerMemoryFunctionName = "getContainerMemory" 61 builtInGetContainerRequestMemoryFunctionName = "getContainerRequestMemory" 62 63 // BuiltinMysqlCalBufferFunctionName Mysql Built-in 64 // TODO: This function migrate to configuration template 65 builtInMysqlCalBufferFunctionName = "callBufferSizeByResource" 66 67 // TLS Built-in 68 builtInGetCAFile = "getCAFile" 69 builtInGetCertFile = "getCertFile" 70 builtInGetKeyFile = "getKeyFile" 71 ) 72 73 type ResourceDefinition struct { 74 MemorySize int64 `json:"memorySize,omitempty"` 75 CoreNum int64 `json:"coreNum,omitempty"` 76 } 77 78 type componentTemplateValues struct { 79 TypeName string 80 ServiceName string 81 Replicas int32 82 83 // Container *corev1.Container 84 Resource *ResourceDefinition 85 ConfigSpecs []appsv1alpha1.ComponentConfigSpec 86 } 87 88 type configTemplateBuilder struct { 89 namespace string 90 clusterName string 91 templateName string 92 93 // Global Var 94 componentValues *componentTemplateValues 95 builtInFunctions *gotemplate.BuiltInObjectsFunc 96 97 // cluster object 98 component *component.SynthesizedComponent 99 clusterVersion *appsv1alpha1.ClusterVersion 100 cluster *appsv1alpha1.Cluster 101 podSpec *corev1.PodSpec 102 103 ctx context.Context 104 cli ictrlclient.ReadonlyClient 105 } 106 107 func newTemplateBuilder( 108 clusterName, namespace string, 109 cluster *appsv1alpha1.Cluster, 110 version *appsv1alpha1.ClusterVersion, 111 ctx context.Context, 112 cli ictrlclient.ReadonlyClient) *configTemplateBuilder { 113 return &configTemplateBuilder{ 114 namespace: namespace, 115 clusterName: clusterName, 116 cluster: cluster, 117 clusterVersion: version, 118 templateName: "KbTemplate", 119 ctx: ctx, 120 cli: cli, 121 } 122 } 123 124 func (c *configTemplateBuilder) setTemplateName(templateName string) { 125 c.templateName = templateName 126 } 127 128 func (c *configTemplateBuilder) formatError(file string, err error) error { 129 return fmt.Errorf("failed to render configuration template[cm:%s][key:%s], error: [%v]", c.templateName, file, err) 130 } 131 132 func (c *configTemplateBuilder) render(configs map[string]string) (map[string]string, error) { 133 rendered := make(map[string]string, len(configs)) 134 values, err := c.builtinObjectsAsValues() 135 if err != nil { 136 return nil, err 137 } 138 engine := gotemplate.NewTplEngine(values, c.builtInFunctions, c.templateName, c.cli, c.ctx) 139 for file, configContext := range configs { 140 newContext, err := engine.Render(configContext) 141 if err != nil { 142 return nil, c.formatError(file, err) 143 } 144 rendered[file] = newContext 145 } 146 return rendered, nil 147 } 148 149 func (c *configTemplateBuilder) builtinObjectsAsValues() (*gotemplate.TplValues, error) { 150 // preHandle the component 151 var v Visitor = &ComponentVisitor{component: c.component} 152 // v = NewDecoratedVisitor(v, resolveServiceReferences(c.cli, c.ctx, c.namespace)) 153 if err := v.Visit(resolveServiceReferences(c.cli, c.ctx)); err != nil { 154 return nil, err 155 } 156 157 builtInObjs := map[string]interface{}{ 158 builtinClusterObject: c.cluster, 159 builtinComponentObject: c.component, 160 builtinPodObject: c.podSpec, 161 builtinComponentResourceObject: c.componentValues.Resource, 162 builtinClusterVersionObject: c.clusterVersion, 163 builtinClusterDomainObject: viper.GetString(constant.KubernetesClusterDomainEnv), 164 } 165 b, err := json.Marshal(builtInObjs) 166 if err != nil { 167 return nil, err 168 } 169 var tplValue gotemplate.TplValues 170 if err = json.Unmarshal(b, &tplValue); err != nil { 171 return nil, err 172 } 173 return &tplValue, nil 174 } 175 176 func (c *configTemplateBuilder) injectBuiltInObjectsAndFunctions( 177 podSpec *corev1.PodSpec, 178 configs []appsv1alpha1.ComponentConfigSpec, 179 component *component.SynthesizedComponent, 180 localObjs []client.Object) error { 181 if err := c.injectBuiltInObjects(podSpec, component, configs); err != nil { 182 return err 183 } 184 if err := c.injectBuiltInFunctions(component, localObjs); err != nil { 185 return err 186 } 187 return nil 188 } 189 190 func (c *configTemplateBuilder) injectBuiltInFunctions(component *component.SynthesizedComponent, localObjs []client.Object) error { 191 // TODO add built-in function 192 c.builtInFunctions = BuiltInCustomFunctions(c, component, localObjs) 193 // other logic here 194 return nil 195 } 196 197 func (c *configTemplateBuilder) injectBuiltInObjects(podSpec *corev1.PodSpec, component *component.SynthesizedComponent, configSpecs []appsv1alpha1.ComponentConfigSpec) error { 198 var resource *ResourceDefinition 199 container := intctrlutil.GetContainerByConfigSpec(podSpec, configSpecs) 200 if container != nil && len(container.Resources.Limits) > 0 { 201 resource = &ResourceDefinition{ 202 MemorySize: intctrlutil.GetMemorySize(*container), 203 CoreNum: intctrlutil.GetCoreNum(*container), 204 } 205 } 206 c.componentValues = &componentTemplateValues{ 207 TypeName: component.CompDefName, 208 Replicas: component.Replicas, 209 Resource: resource, 210 ConfigSpecs: configSpecs, 211 } 212 c.podSpec = podSpec 213 c.component = component 214 return nil 215 }