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  }