github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/component/component.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 component
    21  
    22  import (
    23  	"fmt"
    24  	"strings"
    25  
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  
    29  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    30  	"github.com/1aal/kubeblocks/pkg/class"
    31  	cfgcore "github.com/1aal/kubeblocks/pkg/configuration/core"
    32  	"github.com/1aal/kubeblocks/pkg/constant"
    33  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    34  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    35  )
    36  
    37  func BuildComponent(reqCtx intctrlutil.RequestCtx,
    38  	clsMgr *class.Manager,
    39  	cluster *appsv1alpha1.Cluster,
    40  	clusterDef *appsv1alpha1.ClusterDefinition,
    41  	clusterCompDef *appsv1alpha1.ClusterComponentDefinition,
    42  	clusterCompSpec *appsv1alpha1.ClusterComponentSpec,
    43  	serviceReferences map[string]*appsv1alpha1.ServiceDescriptor,
    44  	clusterCompVers ...*appsv1alpha1.ClusterComponentVersion,
    45  ) (*SynthesizedComponent, error) {
    46  	return buildComponent(reqCtx, clsMgr, cluster, clusterDef, clusterCompDef, clusterCompSpec, serviceReferences, clusterCompVers...)
    47  }
    48  
    49  // buildComponent generates a new Component object, which is a mixture of
    50  // component-related configs from input Cluster, ClusterDef and ClusterVersion.
    51  func buildComponent(reqCtx intctrlutil.RequestCtx,
    52  	clsMgr *class.Manager,
    53  	cluster *appsv1alpha1.Cluster,
    54  	clusterDef *appsv1alpha1.ClusterDefinition,
    55  	clusterCompDef *appsv1alpha1.ClusterComponentDefinition,
    56  	clusterCompSpec *appsv1alpha1.ClusterComponentSpec,
    57  	serviceReferences map[string]*appsv1alpha1.ServiceDescriptor,
    58  	clusterCompVers ...*appsv1alpha1.ClusterComponentVersion,
    59  ) (*SynthesizedComponent, error) {
    60  	hasSimplifiedAPI := func() bool {
    61  		return cluster.Spec.Replicas != nil ||
    62  			!cluster.Spec.Resources.CPU.IsZero() ||
    63  			!cluster.Spec.Resources.Memory.IsZero() ||
    64  			!cluster.Spec.Storage.Size.IsZero() ||
    65  			cluster.Spec.Monitor.MonitoringInterval != nil ||
    66  			cluster.Spec.Network != nil ||
    67  			len(cluster.Spec.Tenancy) > 0 ||
    68  			len(cluster.Spec.AvailabilityPolicy) > 0
    69  	}
    70  
    71  	fillSimplifiedAPI := func() {
    72  		// fill simplified api only to first defined component
    73  		if len(clusterDef.Spec.ComponentDefs) == 0 ||
    74  			clusterDef.Spec.ComponentDefs[0].Name != clusterCompDef.Name {
    75  			return
    76  		}
    77  		// return if none of simplified api is defined
    78  		if !hasSimplifiedAPI() {
    79  			return
    80  		}
    81  		if clusterCompSpec == nil {
    82  			clusterCompSpec = &appsv1alpha1.ClusterComponentSpec{}
    83  			clusterCompSpec.Name = clusterCompDef.Name
    84  		}
    85  		if cluster.Spec.Replicas != nil {
    86  			clusterCompSpec.Replicas = *cluster.Spec.Replicas
    87  		}
    88  		dataVolumeName := "data"
    89  		for _, v := range clusterCompDef.VolumeTypes {
    90  			if v.Type == appsv1alpha1.VolumeTypeData {
    91  				dataVolumeName = v.Name
    92  			}
    93  		}
    94  		if !cluster.Spec.Resources.CPU.IsZero() || !cluster.Spec.Resources.Memory.IsZero() {
    95  			clusterCompSpec.Resources.Limits = corev1.ResourceList{}
    96  		}
    97  		if !cluster.Spec.Resources.CPU.IsZero() {
    98  			clusterCompSpec.Resources.Limits["cpu"] = cluster.Spec.Resources.CPU
    99  		}
   100  		if !cluster.Spec.Resources.Memory.IsZero() {
   101  			clusterCompSpec.Resources.Limits["memory"] = cluster.Spec.Resources.Memory
   102  		}
   103  		if !cluster.Spec.Storage.Size.IsZero() {
   104  			clusterCompSpec.VolumeClaimTemplates = []appsv1alpha1.ClusterComponentVolumeClaimTemplate{
   105  				{
   106  					Name: dataVolumeName,
   107  					Spec: appsv1alpha1.PersistentVolumeClaimSpec{
   108  						AccessModes: []corev1.PersistentVolumeAccessMode{
   109  							corev1.ReadWriteOnce,
   110  						},
   111  						Resources: corev1.ResourceRequirements{
   112  							Requests: corev1.ResourceList{
   113  								"storage": cluster.Spec.Storage.Size,
   114  							},
   115  						},
   116  					},
   117  				},
   118  			}
   119  		}
   120  		if cluster.Spec.Monitor.MonitoringInterval != nil {
   121  			if len(cluster.Spec.Monitor.MonitoringInterval.StrVal) == 0 && cluster.Spec.Monitor.MonitoringInterval.IntVal == 0 {
   122  				clusterCompSpec.Monitor = false
   123  			} else {
   124  				clusterCompSpec.Monitor = true
   125  				// TODO: should also set interval
   126  			}
   127  		}
   128  		if cluster.Spec.Network != nil {
   129  			clusterCompSpec.Services = []appsv1alpha1.ClusterComponentService{}
   130  			if cluster.Spec.Network.HostNetworkAccessible {
   131  				svc := appsv1alpha1.ClusterComponentService{
   132  					Name:        "vpc",
   133  					ServiceType: "LoadBalancer",
   134  				}
   135  				switch getCloudProvider() {
   136  				case CloudProviderAWS:
   137  					svc.Annotations = map[string]string{
   138  						"service.beta.kubernetes.io/aws-load-balancer-type":     "nlb",
   139  						"service.beta.kubernetes.io/aws-load-balancer-internal": "true",
   140  					}
   141  				case CloudProviderGCP:
   142  					svc.Annotations = map[string]string{
   143  						"networking.gke.io/load-balancer-type": "Internal",
   144  					}
   145  				case CloudProviderAliyun:
   146  					svc.Annotations = map[string]string{
   147  						"service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type": "intranet",
   148  					}
   149  				case CloudProviderAzure:
   150  					svc.Annotations = map[string]string{
   151  						"service.beta.kubernetes.io/azure-load-balancer-internal": "true",
   152  					}
   153  				}
   154  				clusterCompSpec.Services = append(clusterCompSpec.Services, svc)
   155  			}
   156  			if cluster.Spec.Network.PubliclyAccessible {
   157  				svc := appsv1alpha1.ClusterComponentService{
   158  					Name:        "public",
   159  					ServiceType: "LoadBalancer",
   160  				}
   161  				switch getCloudProvider() {
   162  				case CloudProviderAWS:
   163  					svc.Annotations = map[string]string{
   164  						"service.beta.kubernetes.io/aws-load-balancer-type":     "nlb",
   165  						"service.beta.kubernetes.io/aws-load-balancer-internal": "false",
   166  					}
   167  				case CloudProviderAliyun:
   168  					svc.Annotations = map[string]string{
   169  						"service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type": "internet",
   170  					}
   171  				case CloudProviderAzure:
   172  					svc.Annotations = map[string]string{
   173  						"service.beta.kubernetes.io/azure-load-balancer-internal": "false",
   174  					}
   175  				}
   176  				clusterCompSpec.Services = append(clusterCompSpec.Services, svc)
   177  			}
   178  		}
   179  	}
   180  
   181  	// priority: cluster.spec.componentSpecs > simplified api (e.g. cluster.spec.storage etc.) > cluster template
   182  	if clusterCompSpec == nil {
   183  		fillSimplifiedAPI()
   184  	}
   185  	if clusterCompSpec == nil {
   186  		return nil, nil
   187  	}
   188  
   189  	var err error
   190  	// make a copy of clusterCompDef
   191  	clusterCompDefObj := clusterCompDef.DeepCopy()
   192  	component := &SynthesizedComponent{
   193  		ClusterDefName:        clusterDef.Name,
   194  		ClusterName:           cluster.Name,
   195  		ClusterUID:            string(cluster.UID),
   196  		Name:                  clusterCompSpec.Name,
   197  		CompDefName:           clusterCompDefObj.Name,
   198  		CharacterType:         clusterCompDefObj.CharacterType,
   199  		WorkloadType:          clusterCompDefObj.WorkloadType,
   200  		StatelessSpec:         clusterCompDefObj.StatelessSpec,
   201  		StatefulSpec:          clusterCompDefObj.StatefulSpec,
   202  		ConsensusSpec:         clusterCompDefObj.ConsensusSpec,
   203  		ReplicationSpec:       clusterCompDefObj.ReplicationSpec,
   204  		RSMSpec:               clusterCompDefObj.RSMSpec,
   205  		PodSpec:               clusterCompDefObj.PodSpec,
   206  		Probes:                clusterCompDefObj.Probes,
   207  		LogConfigs:            clusterCompDefObj.LogConfigs,
   208  		HorizontalScalePolicy: clusterCompDefObj.HorizontalScalePolicy,
   209  		ConfigTemplates:       clusterCompDefObj.ConfigSpecs,
   210  		ScriptTemplates:       clusterCompDefObj.ScriptSpecs,
   211  		VolumeTypes:           clusterCompDefObj.VolumeTypes,
   212  		VolumeProtection:      clusterCompDefObj.VolumeProtectionSpec,
   213  		CustomLabelSpecs:      clusterCompDefObj.CustomLabelSpecs,
   214  		SwitchoverSpec:        clusterCompDefObj.SwitchoverSpec,
   215  		StatefulSetWorkload:   clusterCompDefObj.GetStatefulSetWorkload(),
   216  		MinAvailable:          clusterCompSpec.GetMinAvailable(clusterCompDefObj.GetMinAvailable()),
   217  		Replicas:              clusterCompSpec.Replicas,
   218  		EnabledLogs:           clusterCompSpec.EnabledLogs,
   219  		TLS:                   clusterCompSpec.TLS,
   220  		Issuer:                clusterCompSpec.Issuer,
   221  		ComponentDef:          clusterCompSpec.ComponentDefRef,
   222  		ServiceAccountName:    clusterCompSpec.ServiceAccountName,
   223  	}
   224  
   225  	if len(clusterCompVers) > 0 && clusterCompVers[0] != nil {
   226  		// only accept 1st ClusterVersion override context
   227  		clusterCompVer := clusterCompVers[0]
   228  		component.ConfigTemplates = cfgcore.MergeConfigTemplates(clusterCompVer.ConfigSpecs, component.ConfigTemplates)
   229  		// override component.PodSpec.InitContainers and component.PodSpec.Containers
   230  		for _, c := range clusterCompVer.VersionsCtx.InitContainers {
   231  			component.PodSpec.InitContainers = appendOrOverrideContainerAttr(component.PodSpec.InitContainers, c)
   232  		}
   233  		for _, c := range clusterCompVer.VersionsCtx.Containers {
   234  			component.PodSpec.Containers = appendOrOverrideContainerAttr(component.PodSpec.Containers, c)
   235  		}
   236  		// override component.SwitchoverSpec
   237  		overrideSwitchoverSpecAttr(component.SwitchoverSpec, clusterCompVer.SwitchoverSpec)
   238  	}
   239  
   240  	// handle component.PodSpec extra settings
   241  	// set affinity and tolerations
   242  	affinity := BuildAffinity(cluster, clusterCompSpec)
   243  	if component.PodSpec.Affinity, err = BuildPodAffinity(cluster, affinity, component); err != nil {
   244  		reqCtx.Log.Error(err, "build pod affinity failed.")
   245  		return nil, err
   246  	}
   247  	component.PodSpec.TopologySpreadConstraints = BuildPodTopologySpreadConstraints(cluster, affinity, component)
   248  	if component.PodSpec.Tolerations, err = BuildTolerations(cluster, clusterCompSpec); err != nil {
   249  		reqCtx.Log.Error(err, "build pod tolerations failed.")
   250  		return nil, err
   251  	}
   252  
   253  	if clusterCompSpec.VolumeClaimTemplates != nil {
   254  		component.VolumeClaimTemplates = clusterCompSpec.ToVolumeClaimTemplates()
   255  	}
   256  
   257  	if clusterCompSpec.Resources.Requests != nil || clusterCompSpec.Resources.Limits != nil {
   258  		component.PodSpec.Containers[0].Resources = clusterCompSpec.Resources
   259  	}
   260  	if err = updateResources(cluster, component, *clusterCompSpec, clsMgr); err != nil {
   261  		reqCtx.Log.Error(err, "update class resources failed")
   262  		return nil, err
   263  	}
   264  
   265  	if clusterCompDefObj.Service != nil {
   266  		service := corev1.Service{Spec: clusterCompDefObj.Service.ToSVCSpec()}
   267  		service.Spec.Type = corev1.ServiceTypeClusterIP
   268  		component.Services = append(component.Services, service)
   269  		for _, item := range clusterCompSpec.Services {
   270  			service = corev1.Service{
   271  				ObjectMeta: metav1.ObjectMeta{
   272  					Name:        item.Name,
   273  					Annotations: item.Annotations,
   274  				},
   275  				Spec: service.Spec,
   276  			}
   277  			service.Spec.Type = item.ServiceType
   278  			component.Services = append(component.Services, service)
   279  		}
   280  	}
   281  
   282  	buildMonitorConfig(clusterCompDefObj, clusterCompSpec, component)
   283  
   284  	// lorry container requires a service account with adequate privileges.
   285  	// If lorry required and the serviceAccountName is not set,
   286  	// a default serviceAccountName will be assigned.
   287  	if component.ServiceAccountName == "" && component.Probes != nil {
   288  		component.ServiceAccountName = "kb-" + component.ClusterName
   289  	}
   290  	// set component.PodSpec.ServiceAccountName
   291  	component.PodSpec.ServiceAccountName = component.ServiceAccountName
   292  	if err = buildLorryContainers(reqCtx, component); err != nil {
   293  		reqCtx.Log.Error(err, "build probe container failed.")
   294  		return nil, err
   295  	}
   296  
   297  	replaceContainerPlaceholderTokens(component, GetEnvReplacementMapForConnCredential(cluster.GetName()))
   298  
   299  	if err = buildComponentRef(clusterDef, cluster, clusterCompDefObj, clusterCompSpec, component); err != nil {
   300  		reqCtx.Log.Error(err, "failed to merge componentRef")
   301  		return nil, err
   302  	}
   303  
   304  	if serviceReferences != nil {
   305  		component.ServiceReferences = serviceReferences
   306  	}
   307  
   308  	return component, nil
   309  }
   310  
   311  // appendOrOverrideContainerAttr appends targetContainer to compContainers or overrides the attributes of compContainers with a given targetContainer,
   312  // if targetContainer does not exist in compContainers, it will be appended. otherwise it will be updated with the attributes of the target container.
   313  func appendOrOverrideContainerAttr(compContainers []corev1.Container, targetContainer corev1.Container) []corev1.Container {
   314  	index, compContainer := intctrlutil.GetContainerByName(compContainers, targetContainer.Name)
   315  	if compContainer == nil {
   316  		compContainers = append(compContainers, targetContainer)
   317  	} else {
   318  		doContainerAttrOverride(&compContainers[index], targetContainer)
   319  	}
   320  	return compContainers
   321  }
   322  
   323  // doContainerAttrOverride overrides the attributes in compContainer with the attributes in container.
   324  func doContainerAttrOverride(compContainer *corev1.Container, container corev1.Container) {
   325  	if compContainer == nil {
   326  		return
   327  	}
   328  	if container.Image != "" {
   329  		compContainer.Image = container.Image
   330  	}
   331  	if len(container.Command) != 0 {
   332  		compContainer.Command = container.Command
   333  	}
   334  	if len(container.Args) != 0 {
   335  		compContainer.Args = container.Args
   336  	}
   337  	if container.WorkingDir != "" {
   338  		compContainer.WorkingDir = container.WorkingDir
   339  	}
   340  	if len(container.Ports) != 0 {
   341  		compContainer.Ports = container.Ports
   342  	}
   343  	if len(container.EnvFrom) != 0 {
   344  		compContainer.EnvFrom = container.EnvFrom
   345  	}
   346  	if len(container.Env) != 0 {
   347  		compContainer.Env = container.Env
   348  	}
   349  	if container.Resources.Limits != nil || container.Resources.Requests != nil {
   350  		compContainer.Resources = container.Resources
   351  	}
   352  	if len(container.VolumeMounts) != 0 {
   353  		compContainer.VolumeMounts = container.VolumeMounts
   354  	}
   355  	if len(container.VolumeDevices) != 0 {
   356  		compContainer.VolumeDevices = container.VolumeDevices
   357  	}
   358  	if container.LivenessProbe != nil {
   359  		compContainer.LivenessProbe = container.LivenessProbe
   360  	}
   361  	if container.ReadinessProbe != nil {
   362  		compContainer.ReadinessProbe = container.ReadinessProbe
   363  	}
   364  	if container.StartupProbe != nil {
   365  		compContainer.StartupProbe = container.StartupProbe
   366  	}
   367  	if container.Lifecycle != nil {
   368  		compContainer.Lifecycle = container.Lifecycle
   369  	}
   370  	if container.TerminationMessagePath != "" {
   371  		compContainer.TerminationMessagePath = container.TerminationMessagePath
   372  	}
   373  	if container.TerminationMessagePolicy != "" {
   374  		compContainer.TerminationMessagePolicy = container.TerminationMessagePolicy
   375  	}
   376  	if container.ImagePullPolicy != "" {
   377  		compContainer.ImagePullPolicy = container.ImagePullPolicy
   378  	}
   379  	if container.SecurityContext != nil {
   380  		compContainer.SecurityContext = container.SecurityContext
   381  	}
   382  }
   383  
   384  // GetEnvReplacementMapForConnCredential gets the replacement map for connect credential
   385  func GetEnvReplacementMapForConnCredential(clusterName string) map[string]string {
   386  	return map[string]string{
   387  		constant.KBConnCredentialPlaceHolder: GenerateConnCredential(clusterName),
   388  	}
   389  }
   390  
   391  func replaceContainerPlaceholderTokens(component *SynthesizedComponent, namedValuesMap map[string]string) {
   392  	// replace env[].valueFrom.secretKeyRef.name variables
   393  	for _, cc := range [][]corev1.Container{component.PodSpec.InitContainers, component.PodSpec.Containers} {
   394  		for _, c := range cc {
   395  			c.Env = ReplaceSecretEnvVars(namedValuesMap, c.Env)
   396  		}
   397  	}
   398  }
   399  
   400  // GetReplacementMapForBuiltInEnv gets the replacement map for KubeBlocks built-in environment variables.
   401  func GetReplacementMapForBuiltInEnv(clusterName, clusterUID, componentName string) map[string]string {
   402  	cc := fmt.Sprintf("%s-%s", clusterName, componentName)
   403  	replacementMap := map[string]string{
   404  		constant.KBClusterNamePlaceHolder:     clusterName,
   405  		constant.KBCompNamePlaceHolder:        componentName,
   406  		constant.KBClusterCompNamePlaceHolder: cc,
   407  		constant.KBComponentEnvCMPlaceHolder:  fmt.Sprintf("%s-env", cc),
   408  	}
   409  	if len(clusterUID) > 8 {
   410  		replacementMap[constant.KBClusterUIDPostfix8PlaceHolder] = clusterUID[len(clusterUID)-8:]
   411  	} else {
   412  		replacementMap[constant.KBClusterUIDPostfix8PlaceHolder] = clusterUID
   413  	}
   414  	return replacementMap
   415  }
   416  
   417  // ReplaceNamedVars replaces the placeholder in targetVar if it is match and returns the replaced result
   418  func ReplaceNamedVars(namedValuesMap map[string]string, targetVar string, limits int, matchAll bool) string {
   419  	for placeHolderKey, mappingValue := range namedValuesMap {
   420  		r := strings.Replace(targetVar, placeHolderKey, mappingValue, limits)
   421  		// early termination on matching, when matchAll = false
   422  		if r != targetVar && !matchAll {
   423  			return r
   424  		}
   425  		targetVar = r
   426  	}
   427  	return targetVar
   428  }
   429  
   430  // ReplaceSecretEnvVars replaces the env secret value with namedValues and returns new envs
   431  func ReplaceSecretEnvVars(namedValuesMap map[string]string, envs []corev1.EnvVar) []corev1.EnvVar {
   432  	newEnvs := make([]corev1.EnvVar, 0, len(envs))
   433  	for _, e := range envs {
   434  		if e.ValueFrom == nil || e.ValueFrom.SecretKeyRef == nil {
   435  			continue
   436  		}
   437  		name := ReplaceNamedVars(namedValuesMap, e.ValueFrom.SecretKeyRef.Name, 1, false)
   438  		if name != e.ValueFrom.SecretKeyRef.Name {
   439  			e.ValueFrom.SecretKeyRef.Name = name
   440  		}
   441  		newEnvs = append(newEnvs, e)
   442  	}
   443  	return newEnvs
   444  }
   445  
   446  func GenerateConnCredential(clusterName string) string {
   447  	return fmt.Sprintf("%s-conn-credential", clusterName)
   448  }
   449  
   450  func GenerateDefaultServiceDescriptorName(clusterName string) string {
   451  	return fmt.Sprintf("kbsd-%s", GenerateConnCredential(clusterName))
   452  }
   453  
   454  // overrideSwitchoverSpecAttr overrides the attributes in switchoverSpec with the attributes of SwitchoverShortSpec in clusterVersion.
   455  func overrideSwitchoverSpecAttr(switchoverSpec *appsv1alpha1.SwitchoverSpec, cvSwitchoverSpec *appsv1alpha1.SwitchoverShortSpec) {
   456  	if switchoverSpec == nil || cvSwitchoverSpec == nil || cvSwitchoverSpec.CmdExecutorConfig == nil {
   457  		return
   458  	}
   459  	applyCmdExecutorConfig := func(cmdExecutorConfig *appsv1alpha1.CmdExecutorConfig) {
   460  		if cmdExecutorConfig == nil {
   461  			return
   462  		}
   463  		if len(cvSwitchoverSpec.CmdExecutorConfig.Image) > 0 {
   464  			cmdExecutorConfig.Image = cvSwitchoverSpec.CmdExecutorConfig.Image
   465  		}
   466  		if len(cvSwitchoverSpec.CmdExecutorConfig.Env) > 0 {
   467  			cmdExecutorConfig.Env = cvSwitchoverSpec.CmdExecutorConfig.Env
   468  		}
   469  	}
   470  	if switchoverSpec.WithCandidate != nil {
   471  		applyCmdExecutorConfig(switchoverSpec.WithCandidate.CmdExecutorConfig)
   472  	}
   473  	if switchoverSpec.WithoutCandidate != nil {
   474  		applyCmdExecutorConfig(switchoverSpec.WithoutCandidate.CmdExecutorConfig)
   475  	}
   476  }
   477  
   478  func GenerateComponentEnvName(clusterName, componentName string) string {
   479  	return fmt.Sprintf("%s-%s-env", clusterName, componentName)
   480  }
   481  
   482  func updateResources(cluster *appsv1alpha1.Cluster, component *SynthesizedComponent, clusterCompSpec appsv1alpha1.ClusterComponentSpec, clsMgr *class.Manager) error {
   483  	if ignoreResourceConstraint(cluster) {
   484  		return nil
   485  	}
   486  
   487  	if clsMgr == nil {
   488  		return nil
   489  	}
   490  
   491  	expectResources, err := clsMgr.GetResources(cluster.Spec.ClusterDefRef, &clusterCompSpec)
   492  	if err != nil || expectResources == nil {
   493  		return err
   494  	}
   495  
   496  	actualResources := component.PodSpec.Containers[0].Resources
   497  	if actualResources.Requests == nil {
   498  		actualResources.Requests = corev1.ResourceList{}
   499  	}
   500  	if actualResources.Limits == nil {
   501  		actualResources.Limits = corev1.ResourceList{}
   502  	}
   503  	for k, v := range expectResources {
   504  		actualResources.Requests[k] = v
   505  		actualResources.Limits[k] = v
   506  	}
   507  	component.PodSpec.Containers[0].Resources = actualResources
   508  	return nil
   509  }
   510  
   511  func getCloudProvider() CloudProvider {
   512  	k8sVersion := viper.GetString(constant.CfgKeyServerInfo)
   513  	if strings.Contains(k8sVersion, "eks") {
   514  		return CloudProviderAWS
   515  	}
   516  	if strings.Contains(k8sVersion, "gke") {
   517  		return CloudProviderGCP
   518  	}
   519  	if strings.Contains(k8sVersion, "aliyun") {
   520  		return CloudProviderAliyun
   521  	}
   522  	if strings.Contains(k8sVersion, "tke") {
   523  		return CloudProviderTencent
   524  	}
   525  	return CloudProviderUnknown
   526  }
   527  
   528  func GetConfigSpecByName(component *SynthesizedComponent, configSpec string) *appsv1alpha1.ComponentConfigSpec {
   529  	for i := range component.ConfigTemplates {
   530  		template := &component.ConfigTemplates[i]
   531  		if template.Name == configSpec {
   532  			return template
   533  		}
   534  	}
   535  	return nil
   536  }