github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/testutil/apps/clusterdef_factory.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 apps
    21  
    22  import (
    23  	corev1 "k8s.io/api/core/v1"
    24  
    25  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    26  )
    27  
    28  type ComponentDefTplType string
    29  
    30  const (
    31  	StatefulMySQLComponent    ComponentDefTplType = "stateful-mysql"
    32  	ConsensusMySQLComponent   ComponentDefTplType = "consensus-mysql"
    33  	ReplicationRedisComponent ComponentDefTplType = "replication-redis"
    34  	StatelessNginxComponent   ComponentDefTplType = "stateless-nginx"
    35  )
    36  
    37  type MockClusterDefFactory struct {
    38  	BaseFactory[appsv1alpha1.ClusterDefinition, *appsv1alpha1.ClusterDefinition, MockClusterDefFactory]
    39  }
    40  
    41  func getDefaultConnectionCredential() map[string]string {
    42  	return map[string]string{
    43  		"username":          "root",
    44  		"SVC_FQDN":          "$(SVC_FQDN)",
    45  		"HEADLESS_SVC_FQDN": "$(HEADLESS_SVC_FQDN)",
    46  		"RANDOM_PASSWD":     "$(RANDOM_PASSWD)",
    47  		"tcpEndpoint":       "tcp:$(SVC_FQDN):$(SVC_PORT_mysql)",
    48  		"paxosEndpoint":     "paxos:$(SVC_FQDN):$(SVC_PORT_paxos)",
    49  		"UUID":              "$(UUID)",
    50  		"UUID_B64":          "$(UUID_B64)",
    51  		"UUID_STR_B64":      "$(UUID_STR_B64)",
    52  		"UUID_HEX":          "$(UUID_HEX)",
    53  	}
    54  }
    55  
    56  func NewClusterDefFactory(name string) *MockClusterDefFactory {
    57  	f := &MockClusterDefFactory{}
    58  	f.Init("", name,
    59  		&appsv1alpha1.ClusterDefinition{
    60  			Spec: appsv1alpha1.ClusterDefinitionSpec{
    61  				ComponentDefs: []appsv1alpha1.ClusterComponentDefinition{},
    62  			},
    63  		}, f)
    64  	f.SetConnectionCredential(getDefaultConnectionCredential(), nil)
    65  	return f
    66  }
    67  
    68  func NewClusterDefFactoryWithConnCredential(name string) *MockClusterDefFactory {
    69  	f := NewClusterDefFactory(name)
    70  	f.AddComponentDef(StatefulMySQLComponent, "conn-cred")
    71  	f.SetConnectionCredential(getDefaultConnectionCredential(), &defaultSvcSpec)
    72  	return f
    73  }
    74  
    75  func (factory *MockClusterDefFactory) AddComponentDef(tplType ComponentDefTplType, compDefName string) *MockClusterDefFactory {
    76  	var component *appsv1alpha1.ClusterComponentDefinition
    77  	switch tplType {
    78  	case StatefulMySQLComponent:
    79  		component = &statefulMySQLComponent
    80  	case ConsensusMySQLComponent:
    81  		component = &consensusMySQLComponent
    82  	case ReplicationRedisComponent:
    83  		component = &replicationRedisComponent
    84  	case StatelessNginxComponent:
    85  		component = &statelessNginxComponent
    86  	}
    87  	factory.Get().Spec.ComponentDefs = append(factory.Get().Spec.ComponentDefs, *component)
    88  	comp := factory.getLastCompDef()
    89  	comp.Name = compDefName
    90  	return factory
    91  }
    92  
    93  func (factory *MockClusterDefFactory) AddServicePort(port int32) *MockClusterDefFactory {
    94  	comp := factory.getLastCompDef()
    95  	if comp == nil {
    96  		return nil
    97  	}
    98  	comp.Service = &appsv1alpha1.ServiceSpec{
    99  		Ports: []appsv1alpha1.ServicePort{{
   100  			Protocol: corev1.ProtocolTCP,
   101  			Port:     port,
   102  		}},
   103  	}
   104  	return factory
   105  }
   106  
   107  func (factory *MockClusterDefFactory) AddScriptTemplate(name,
   108  	configTemplateRef, namespace, volumeName string, mode *int32) *MockClusterDefFactory {
   109  	comp := factory.getLastCompDef()
   110  	if comp == nil {
   111  		return nil
   112  	}
   113  	comp.ScriptSpecs = append(comp.ScriptSpecs,
   114  		appsv1alpha1.ComponentTemplateSpec{
   115  			Name:        name,
   116  			TemplateRef: configTemplateRef,
   117  			Namespace:   namespace,
   118  			VolumeName:  volumeName,
   119  			DefaultMode: mode,
   120  		})
   121  	return factory
   122  }
   123  
   124  func (factory *MockClusterDefFactory) AddConfigTemplate(name,
   125  	configTemplateRef, configConstraintRef, namespace, volumeName string, asEnvFrom ...string) *MockClusterDefFactory {
   126  	comp := factory.getLastCompDef()
   127  	if comp == nil {
   128  		return nil
   129  	}
   130  	comp.ConfigSpecs = append(comp.ConfigSpecs,
   131  		appsv1alpha1.ComponentConfigSpec{
   132  			ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   133  				Name:        name,
   134  				TemplateRef: configTemplateRef,
   135  				Namespace:   namespace,
   136  				VolumeName:  volumeName,
   137  			},
   138  			ConfigConstraintRef: configConstraintRef,
   139  			AsEnvFrom:           asEnvFrom,
   140  		})
   141  	return factory
   142  }
   143  
   144  func (factory *MockClusterDefFactory) AddLogConfig(name, filePathPattern string) *MockClusterDefFactory {
   145  	comp := factory.getLastCompDef()
   146  	if comp == nil {
   147  		return nil
   148  	}
   149  	comp.LogConfigs = append(comp.LogConfigs, appsv1alpha1.LogConfig{
   150  		FilePathPattern: filePathPattern,
   151  		Name:            name,
   152  	})
   153  	return factory
   154  }
   155  
   156  func (factory *MockClusterDefFactory) AddContainerEnv(containerName string, envVar corev1.EnvVar) *MockClusterDefFactory {
   157  	comp := factory.getLastCompDef()
   158  	if comp == nil {
   159  		return nil
   160  	}
   161  	for i, container := range comp.PodSpec.Containers {
   162  		if container.Name == containerName {
   163  			c := comp.PodSpec.Containers[i]
   164  			c.Env = append(c.Env, envVar)
   165  			comp.PodSpec.Containers[i] = c
   166  			break
   167  		}
   168  	}
   169  	return factory
   170  }
   171  
   172  func (factory *MockClusterDefFactory) AddHorizontalScalePolicy(policy appsv1alpha1.HorizontalScalePolicy) *MockClusterDefFactory {
   173  	comp := factory.getLastCompDef()
   174  	if comp == nil {
   175  		return nil
   176  	}
   177  	comp.HorizontalScalePolicy = &policy
   178  	return factory
   179  }
   180  
   181  func (factory *MockClusterDefFactory) SetConnectionCredential(
   182  	connectionCredential map[string]string, svc *appsv1alpha1.ServiceSpec) *MockClusterDefFactory {
   183  	factory.Get().Spec.ConnectionCredential = connectionCredential
   184  	factory.SetServiceSpec(svc)
   185  	return factory
   186  }
   187  
   188  func (factory *MockClusterDefFactory) get1stCompDef() *appsv1alpha1.ClusterComponentDefinition {
   189  	if len(factory.Get().Spec.ComponentDefs) == 0 {
   190  		return nil
   191  	}
   192  	return &factory.Get().Spec.ComponentDefs[0]
   193  }
   194  
   195  func (factory *MockClusterDefFactory) getLastCompDef() *appsv1alpha1.ClusterComponentDefinition {
   196  	l := len(factory.Get().Spec.ComponentDefs)
   197  	if l == 0 {
   198  		return nil
   199  	}
   200  	comps := factory.Get().Spec.ComponentDefs
   201  	return &comps[l-1]
   202  }
   203  
   204  func (factory *MockClusterDefFactory) SetServiceSpec(svc *appsv1alpha1.ServiceSpec) *MockClusterDefFactory {
   205  	comp := factory.get1stCompDef()
   206  	if comp == nil {
   207  		return factory
   208  	}
   209  	comp.Service = svc
   210  	return factory
   211  }
   212  
   213  func (factory *MockClusterDefFactory) AddSystemAccountSpec(sysAccounts *appsv1alpha1.SystemAccountSpec) *MockClusterDefFactory {
   214  	comp := factory.getLastCompDef()
   215  	if comp == nil {
   216  		return factory
   217  	}
   218  	comp.SystemAccounts = sysAccounts
   219  	return factory
   220  }
   221  
   222  func (factory *MockClusterDefFactory) AddSwitchoverSpec(switchoverSpec *appsv1alpha1.SwitchoverSpec) *MockClusterDefFactory {
   223  	comp := factory.getLastCompDef()
   224  	if comp == nil {
   225  		return factory
   226  	}
   227  	comp.SwitchoverSpec = switchoverSpec
   228  	return factory
   229  }
   230  
   231  func (factory *MockClusterDefFactory) AddServiceRefDeclarations(serviceRefDeclarations []appsv1alpha1.ServiceRefDeclaration) *MockClusterDefFactory {
   232  	comp := factory.getLastCompDef()
   233  	if comp == nil {
   234  		return factory
   235  	}
   236  	comp.ServiceRefDeclarations = serviceRefDeclarations
   237  	return factory
   238  }
   239  
   240  func (factory *MockClusterDefFactory) AddInitContainerVolumeMounts(containerName string, volumeMounts []corev1.VolumeMount) *MockClusterDefFactory {
   241  	comp := factory.getLastCompDef()
   242  	if comp == nil {
   243  		return factory
   244  	}
   245  	comp.PodSpec.InitContainers = appendContainerVolumeMounts(comp.PodSpec.InitContainers, containerName, volumeMounts)
   246  	return factory
   247  }
   248  
   249  func (factory *MockClusterDefFactory) AddContainerVolumeMounts(containerName string, volumeMounts []corev1.VolumeMount) *MockClusterDefFactory {
   250  	comp := factory.getLastCompDef()
   251  	if comp == nil {
   252  		return factory
   253  	}
   254  	comp.PodSpec.Containers = appendContainerVolumeMounts(comp.PodSpec.Containers, containerName, volumeMounts)
   255  	return factory
   256  }
   257  
   258  func (factory *MockClusterDefFactory) AddReplicationSpec(replicationSpec *appsv1alpha1.ReplicationSetSpec) *MockClusterDefFactory {
   259  	comp := factory.getLastCompDef()
   260  	if comp == nil {
   261  		return factory
   262  	}
   263  	comp.ReplicationSpec = replicationSpec
   264  	return factory
   265  }
   266  
   267  func (factory *MockClusterDefFactory) AddComponentRef(ref *appsv1alpha1.ComponentDefRef) *MockClusterDefFactory {
   268  	comp := factory.getLastCompDef()
   269  	if comp == nil {
   270  		return factory
   271  	}
   272  	if len(comp.ComponentDefRef) == 0 {
   273  		comp.ComponentDefRef = make([]appsv1alpha1.ComponentDefRef, 0)
   274  	}
   275  	comp.ComponentDefRef = append(comp.ComponentDefRef, *ref)
   276  	return factory
   277  }
   278  
   279  func (factory *MockClusterDefFactory) AddNamedServicePort(name string, port int32) *MockClusterDefFactory {
   280  	comp := factory.getLastCompDef()
   281  	if comp == nil {
   282  		return nil
   283  	}
   284  	if comp.Service != nil {
   285  		comp.Service.Ports = append(comp.Service.Ports, appsv1alpha1.ServicePort{
   286  			Name:     name,
   287  			Protocol: corev1.ProtocolTCP,
   288  			Port:     port,
   289  		})
   290  		return factory
   291  	}
   292  	comp.Service = &appsv1alpha1.ServiceSpec{
   293  		Ports: []appsv1alpha1.ServicePort{{
   294  			Name:     name,
   295  			Protocol: corev1.ProtocolTCP,
   296  			Port:     port,
   297  		}},
   298  	}
   299  	return factory
   300  }
   301  
   302  // There are default volumeMounts for containers in clusterdefinition in pusrpose of a simple & fast creation,
   303  // but when mounts specified volumes in certain mountPaths, they may conflict with the default volumeMounts,
   304  // so here provides a way to overwrite the default volumeMounts.
   305  func appendContainerVolumeMounts(containers []corev1.Container, targetContainerName string, volumeMounts []corev1.VolumeMount) []corev1.Container {
   306  	for index := range containers {
   307  		c := &containers[index]
   308  		// remove the duplicated volumeMounts and overwrite the default mount path
   309  		if c.Name == targetContainerName {
   310  			mergedVolumeMounts := make([]corev1.VolumeMount, 0)
   311  			volumeMountsMap := make(map[string]corev1.VolumeMount)
   312  			for _, v := range c.VolumeMounts {
   313  				volumeMountsMap[v.Name] = v
   314  			}
   315  			for _, v := range volumeMounts {
   316  				volumeMountsMap[v.Name] = v
   317  			}
   318  			for _, v := range volumeMountsMap {
   319  				mergedVolumeMounts = append(mergedVolumeMounts, v)
   320  			}
   321  			c.VolumeMounts = mergedVolumeMounts
   322  			break
   323  		}
   324  	}
   325  	return containers
   326  }