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  }