github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/configuration/builtin_functions.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 "encoding/json" 24 "fmt" 25 "math" 26 27 corev1 "k8s.io/api/core/v1" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 30 "github.com/1aal/kubeblocks/pkg/controller/component" 31 "github.com/1aal/kubeblocks/pkg/controller/factory" 32 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 33 "github.com/1aal/kubeblocks/pkg/gotemplate" 34 ) 35 36 func toJSONObject[T corev1.VolumeSource | corev1.Container | corev1.ContainerPort](obj T) (interface{}, error) { 37 b, err := json.Marshal(obj) 38 if err != nil { 39 return nil, err 40 } 41 42 var jsonObj any 43 if err := json.Unmarshal(b, &jsonObj); err != nil { 44 return nil, err 45 } 46 47 return jsonObj, nil 48 } 49 50 func fromJSONObject[T any](args interface{}) (*T, error) { 51 b, err := json.Marshal(args) 52 if err != nil { 53 return nil, err 54 } 55 56 var container T 57 if err := json.Unmarshal(b, &container); err != nil { 58 return nil, err 59 } 60 61 return &container, nil 62 } 63 64 func fromJSONArray[T corev1.Container | corev1.Volume | corev1.PersistentVolumeClaimTemplate](args interface{}) ([]T, error) { 65 b, err := json.Marshal(args) 66 if err != nil { 67 return nil, err 68 } 69 70 var list []T 71 if err := json.Unmarshal(b, &list); err != nil { 72 return nil, err 73 } 74 75 return list, nil 76 } 77 78 const emptyString = "" 79 80 // calReverseRebaseBuffer Cal reserved memory for system 81 func calReverseRebaseBuffer(memSizeMB, cpuNum int64) int64 { 82 const ( 83 rebaseMemorySize = int64(2048) 84 reverseRebaseBufferSize = 285 85 ) 86 87 // MIN(RDS ins class for mem / 2, 2048) 88 r1 := int64(math.Min(float64(memSizeMB>>1), float64(rebaseMemorySize))) 89 // MAX(RDS ins class for CPU * 64, RDS ins class for mem / 64) 90 r2 := int64(math.Max(float64(cpuNum<<6), float64(memSizeMB>>6))) 91 return r1 + r2 + memSizeMB>>6 + reverseRebaseBufferSize 92 } 93 94 // template built-in functions 95 // calMysqlPoolSizeByResource Cal mysql buffer size 96 func calMysqlPoolSizeByResource(resource *ResourceDefinition, isShared bool) string { 97 const ( 98 defaultPoolSize = "128M" 99 minBufferSizeMB = 128 100 smallClassMemorySize = int64(1024 * 1024 * 1024) 101 ) 102 103 if resource == nil || resource.CoreNum == 0 || resource.MemorySize == 0 { 104 return defaultPoolSize 105 } 106 107 // small instance class 108 // mem_size <= 1G or 109 // core <= 2 110 if resource.MemorySize <= smallClassMemorySize { 111 return defaultPoolSize 112 } 113 114 memSizeMB := resource.MemorySize / 1024 / 1024 115 maxBufferSize := int32(memSizeMB * 80 / 100) 116 totalMemorySize := memSizeMB 117 118 if !isShared { 119 reverseBuffer := calReverseRebaseBuffer(memSizeMB, resource.CoreNum) 120 totalMemorySize = memSizeMB - reverseBuffer 121 122 // for small instance class 123 if resource.CoreNum <= 2 { 124 totalMemorySize -= 128 125 } 126 } 127 128 if totalMemorySize <= minBufferSizeMB { 129 return defaultPoolSize 130 } 131 132 // (total_memory - reverseBuffer) * 75 133 bufferSize := int32(totalMemorySize * 75 / 100) 134 if bufferSize > maxBufferSize { 135 bufferSize = maxBufferSize 136 } 137 138 // https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size 139 // Buffer size require aligned 128MB or 1G 140 var alignedSize int32 = 128 141 if bufferSize > 1024 { 142 alignedSize = 1024 143 } 144 145 bufferSize /= alignedSize 146 bufferSize *= alignedSize 147 return fmt.Sprintf("%dM", bufferSize) 148 } 149 150 // calDBPoolSize for specific engine: mysql 151 func calDBPoolSize(args interface{}) (string, error) { 152 container, err := fromJSONObject[corev1.Container](args) 153 if err != nil { 154 return "", err 155 } 156 if len(container.Resources.Limits) == 0 { 157 return "", nil 158 } 159 resource := ResourceDefinition{ 160 MemorySize: intctrlutil.GetMemorySize(*container), 161 CoreNum: intctrlutil.GetCoreNum(*container), 162 } 163 return calMysqlPoolSizeByResource(&resource, false), nil 164 165 } 166 167 // getPodContainerByName gets pod container by name 168 func getPodContainerByName(args []interface{}, containerName string) (interface{}, error) { 169 containers, err := fromJSONArray[corev1.Container](args) 170 if err != nil { 171 return nil, err 172 } 173 for _, v := range containers { 174 if v.Name == containerName { 175 return toJSONObject(v) 176 } 177 } 178 return nil, nil 179 } 180 181 // getVolumeMountPathByName gets volume mount path by name 182 func getVolumeMountPathByName(args interface{}, volumeName string) (string, error) { 183 container, err := fromJSONObject[corev1.Container](args) 184 if err != nil { 185 return "", err 186 } 187 for _, v := range container.VolumeMounts { 188 if v.Name == volumeName { 189 return v.MountPath, nil 190 } 191 } 192 return "", nil 193 } 194 195 // getPVCByName gets pvc by name 196 func getPVCByName(args []interface{}, volumeName string) (interface{}, error) { 197 volumes, err := fromJSONArray[corev1.Volume](args) 198 if err != nil { 199 return nil, err 200 } 201 for _, v := range volumes { 202 if v.Name == volumeName { 203 return toJSONObject(v.VolumeSource) 204 } 205 } 206 return nil, nil 207 } 208 209 // getContainerCPU gets container cpu limit 210 func getContainerCPU(args interface{}) (int64, error) { 211 container, err := fromJSONObject[corev1.Container](args) 212 if err != nil { 213 return 0, err 214 } 215 return intctrlutil.GetCoreNum(*container), nil 216 } 217 218 // getContainerMemory gets container memory limit 219 func getContainerMemory(args interface{}) (int64, error) { 220 container, err := fromJSONObject[corev1.Container](args) 221 if err != nil { 222 return 0, err 223 } 224 return intctrlutil.GetMemorySize(*container), nil 225 } 226 227 // getContainerRequestMemory gets container memory request 228 func getContainerRequestMemory(args interface{}) (int64, error) { 229 container, err := fromJSONObject[corev1.Container](args) 230 if err != nil { 231 return 0, err 232 } 233 return intctrlutil.GetRequestMemorySize(*container), nil 234 } 235 236 // getArgByName get arg by name 237 func getArgByName(args interface{}, argName string) string { 238 // TODO Support parse command args 239 return emptyString 240 } 241 242 // getPortByName get port by name 243 func getPortByName(args interface{}, portName string) (interface{}, error) { 244 container, err := fromJSONObject[corev1.Container](args) 245 if err != nil { 246 return nil, err 247 } 248 for _, v := range container.Ports { 249 if v.Name == portName { 250 return toJSONObject(v) 251 } 252 } 253 254 return nil, nil 255 } 256 257 // getComponentPVCSizeByName gets pvc size by name 258 func getComponentPVCSizeByName(args interface{}, pvcName string) (int64, error) { 259 component, err := fromJSONObject[component.SynthesizedComponent](args) 260 if err != nil { 261 return -1, err 262 } 263 for _, v := range component.VolumeClaimTemplates { 264 if v.Name == pvcName { 265 return intctrlutil.GetStorageSizeFromPersistentVolume(v), nil 266 } 267 } 268 return -1, nil 269 } 270 271 // getPVCSize gets pvc size by name 272 func getPVCSize(args interface{}) (int64, error) { 273 pvcTemp, err := fromJSONObject[corev1.PersistentVolumeClaimTemplate](args) 274 if err != nil { 275 return -1, err 276 } 277 return intctrlutil.GetStorageSizeFromPersistentVolume(*pvcTemp), nil 278 } 279 280 // getCAFile gets CA file 281 func getCAFile() string { 282 return factory.MountPath + "/" + factory.CAName 283 } 284 285 // getCertFile gets cert file 286 func getCertFile() string { 287 return factory.MountPath + "/" + factory.CertName 288 } 289 290 // getKeyFile gets key file 291 func getKeyFile() string { 292 return factory.MountPath + "/" + factory.KeyName 293 } 294 295 // BuiltInCustomFunctions builds a map of customized functions for KubeBlocks 296 func BuiltInCustomFunctions(c *configTemplateBuilder, component *component.SynthesizedComponent, localObjs []client.Object) *gotemplate.BuiltInObjectsFunc { 297 return &gotemplate.BuiltInObjectsFunc{ 298 builtInMysqlCalBufferFunctionName: calDBPoolSize, 299 builtInGetVolumeFunctionName: getVolumeMountPathByName, 300 builtInGetPvcFunctionName: getPVCByName, 301 builtInGetEnvFunctionName: wrapGetEnvByName(c, component, localObjs), 302 builtInGetPortFunctionName: getPortByName, 303 builtInGetArgFunctionName: getArgByName, 304 builtInGetContainerFunctionName: getPodContainerByName, 305 builtInGetContainerCPUFunctionName: getContainerCPU, 306 builtInGetPVCSizeByNameFunctionName: getComponentPVCSizeByName, 307 builtInGetPVCSizeFunctionName: getPVCSize, 308 builtInGetContainerMemoryFunctionName: getContainerMemory, 309 builtInGetContainerRequestMemoryFunctionName: getContainerRequestMemory, 310 builtInGetCAFile: getCAFile, 311 builtInGetCertFile: getCertFile, 312 builtInGetKeyFile: getKeyFile, 313 } 314 315 }