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 }