github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/component/lorry_utils.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 "encoding/json" 24 "fmt" 25 "strconv" 26 27 corev1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/util/intstr" 29 30 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 31 "github.com/1aal/kubeblocks/pkg/constant" 32 "github.com/1aal/kubeblocks/pkg/controller/builder" 33 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 34 viper "github.com/1aal/kubeblocks/pkg/viperx" 35 ) 36 37 const ( 38 // http://localhost:<port>/v1.0/bindings/<binding_type> 39 // checkRoleURIFormat = "/v1.0/bindings/%s?operation=checkRole&workloadType=%s" 40 checkRoleURIFormat = "/v1.0/checkrole" 41 checkRunningURIFormat = "/v1.0/bindings/%s?operation=checkRunning" 42 checkStatusURIFormat = "/v1.0/bindings/%s?operation=checkStatus" 43 volumeProtectionURIFormat = "/v1.0/bindings/%s?operation=volumeProtection" 44 45 dataVolume = "data" 46 ) 47 48 var ( 49 // default probe setting for volume protection. 50 defaultVolumeProtectionProbe = appsv1alpha1.ClusterDefinitionProbe{ 51 PeriodSeconds: 60, 52 TimeoutSeconds: 5, 53 FailureThreshold: 3, 54 } 55 ) 56 57 func buildLorryContainers(reqCtx intctrlutil.RequestCtx, component *SynthesizedComponent) error { 58 container := buildBasicContainer() 59 var lorryContainers []corev1.Container 60 componentProbes := component.Probes 61 if componentProbes == nil { 62 return nil 63 } 64 reqCtx.Log.V(3).Info("lorry", "settings", componentProbes) 65 lorrySvcHTTPPort := viper.GetInt32("PROBE_SERVICE_HTTP_PORT") 66 // override by new env name 67 if viper.IsSet("LORRY_SERVICE_HTTP_PORT") { 68 lorrySvcHTTPPort = viper.GetInt32("LORRY_SERVICE_HTTP_PORT") 69 } 70 availablePorts, err := getAvailableContainerPorts(component.PodSpec.Containers, []int32{lorrySvcHTTPPort}) 71 lorrySvcHTTPPort = availablePorts[0] 72 if err != nil { 73 reqCtx.Log.Info("get lorry container port failed", "error", err) 74 return err 75 } 76 lorrySvcGRPCPort := viper.GetInt("PROBE_SERVICE_GRPC_PORT") 77 78 if componentProbes.RoleProbe != nil && (component.RSMSpec == nil || component.RSMSpec.RoleProbe == nil) { 79 roleChangedContainer := container.DeepCopy() 80 buildRoleProbeContainer(component, roleChangedContainer, componentProbes.RoleProbe, int(lorrySvcHTTPPort)) 81 lorryContainers = append(lorryContainers, *roleChangedContainer) 82 } 83 84 if componentProbes.StatusProbe != nil { 85 statusProbeContainer := container.DeepCopy() 86 buildStatusProbeContainer(component.CharacterType, statusProbeContainer, componentProbes.StatusProbe, int(lorrySvcHTTPPort)) 87 lorryContainers = append(lorryContainers, *statusProbeContainer) 88 } 89 90 if componentProbes.RunningProbe != nil { 91 runningProbeContainer := container.DeepCopy() 92 buildRunningProbeContainer(component.CharacterType, runningProbeContainer, componentProbes.RunningProbe, int(lorrySvcHTTPPort)) 93 lorryContainers = append(lorryContainers, *runningProbeContainer) 94 } 95 96 if volumeProtectionEnabled(component) { 97 c := container.DeepCopy() 98 buildVolumeProtectionProbeContainer(component.CharacterType, c, int(lorrySvcHTTPPort)) 99 lorryContainers = append(lorryContainers, *c) 100 } 101 102 // inject WeSyncer(currently part of Lorry) in cluster controller. 103 // as all the above features share the lorry service, only one lorry need to be injected. 104 // if none of the above feature enabled, WeSyncer still need to be injected for the HA feature functions well. 105 if len(lorryContainers) == 0 { 106 weSyncerContainer := container.DeepCopy() 107 buildWeSyncerContainer(weSyncerContainer, int(lorrySvcHTTPPort)) 108 lorryContainers = append(lorryContainers, *weSyncerContainer) 109 } 110 111 buildLorryServiceContainer(component, &lorryContainers[0], int(lorrySvcHTTPPort), lorrySvcGRPCPort) 112 113 reqCtx.Log.V(1).Info("lorry", "containers", lorryContainers) 114 component.PodSpec.Containers = append(component.PodSpec.Containers, lorryContainers...) 115 return nil 116 } 117 118 func buildBasicContainer() *corev1.Container { 119 return builder.NewContainerBuilder("string"). 120 SetImage("infracreate-registry.cn-zhangjiakou.cr.aliyuncs.com/google_containers/pause:3.6"). 121 SetImagePullPolicy(corev1.PullIfNotPresent). 122 AddCommands("/pause"). 123 AddEnv(corev1.EnvVar{ 124 Name: "KB_SERVICE_USER", 125 ValueFrom: &corev1.EnvVarSource{ 126 SecretKeyRef: &corev1.SecretKeySelector{ 127 Key: "username", 128 LocalObjectReference: corev1.LocalObjectReference{Name: "$(CONN_CREDENTIAL_SECRET_NAME)"}, 129 }, 130 }}, 131 corev1.EnvVar{ 132 Name: "KB_SERVICE_PASSWORD", 133 ValueFrom: &corev1.EnvVarSource{ 134 SecretKeyRef: &corev1.SecretKeySelector{ 135 Key: "password", 136 LocalObjectReference: corev1.LocalObjectReference{Name: "$(CONN_CREDENTIAL_SECRET_NAME)"}, 137 }, 138 }, 139 }). 140 SetStartupProbe(corev1.Probe{ 141 ProbeHandler: corev1.ProbeHandler{ 142 TCPSocket: &corev1.TCPSocketAction{Port: intstr.FromInt(3501)}, 143 }}). 144 GetObject() 145 } 146 147 func buildLorryServiceContainer(component *SynthesizedComponent, container *corev1.Container, lorrySvcHTTPPort, lorrySvcGRPCPort int) { 148 container.Image = viper.GetString(constant.KBToolsImage) 149 container.ImagePullPolicy = corev1.PullPolicy(viper.GetString(constant.KBImagePullPolicy)) 150 container.Command = []string{"lorry", 151 "--port", strconv.Itoa(lorrySvcHTTPPort), 152 "--config-path", "/config/lorry/components/", 153 "--grpcport", strconv.Itoa(lorrySvcGRPCPort), 154 } 155 156 if len(component.PodSpec.Containers) > 0 { 157 mainContainer := component.PodSpec.Containers[0] 158 if len(mainContainer.Ports) > 0 { 159 port := mainContainer.Ports[0] 160 dbPort := port.ContainerPort 161 container.Env = append(container.Env, corev1.EnvVar{ 162 Name: constant.KBEnvServicePort, 163 Value: strconv.Itoa(int(dbPort)), 164 ValueFrom: nil, 165 }) 166 } 167 168 dataVolumeName := dataVolume 169 for _, v := range component.VolumeTypes { 170 if v.Type == appsv1alpha1.VolumeTypeData { 171 dataVolumeName = v.Name 172 } 173 } 174 for _, volumeMount := range mainContainer.VolumeMounts { 175 if volumeMount.Name != dataVolumeName { 176 continue 177 } 178 vm := volumeMount.DeepCopy() 179 container.VolumeMounts = []corev1.VolumeMount{*vm} 180 container.Env = append(container.Env, corev1.EnvVar{ 181 Name: constant.KBEnvDataPath, 182 Value: vm.MountPath, 183 ValueFrom: nil, 184 }) 185 } 186 } 187 188 secretName := fmt.Sprintf("%s-conn-credential", component.ClusterName) 189 container.Env = append(container.Env, 190 corev1.EnvVar{ 191 Name: constant.KBEnvCharacterType, 192 Value: component.CharacterType, 193 ValueFrom: nil, 194 }, 195 corev1.EnvVar{ 196 Name: constant.KBEnvWorkloadType, 197 Value: string(component.WorkloadType), 198 ValueFrom: nil, 199 }, 200 corev1.EnvVar{ 201 Name: constant.KBEnvServiceUser, 202 ValueFrom: &corev1.EnvVarSource{ 203 SecretKeyRef: &corev1.SecretKeySelector{ 204 LocalObjectReference: corev1.LocalObjectReference{ 205 Name: secretName, 206 }, 207 Key: constant.AccountNameForSecret, 208 }, 209 }, 210 }, 211 corev1.EnvVar{ 212 Name: constant.KBEnvServicePassword, 213 ValueFrom: &corev1.EnvVarSource{ 214 SecretKeyRef: &corev1.SecretKeySelector{ 215 LocalObjectReference: corev1.LocalObjectReference{ 216 Name: secretName, 217 }, 218 Key: constant.AccountPasswdForSecret, 219 }, 220 }, 221 }) 222 223 container.Ports = []corev1.ContainerPort{ 224 { 225 ContainerPort: int32(lorrySvcHTTPPort), 226 Name: constant.LorryHTTPPortName, 227 Protocol: "TCP", 228 }, 229 { 230 ContainerPort: int32(lorrySvcGRPCPort), 231 Name: constant.LorryGRPCPortName, 232 Protocol: "TCP", 233 }, 234 } 235 236 // pass the volume protection spec to lorry container through env. 237 if volumeProtectionEnabled(component) { 238 container.Env = append(container.Env, env4VolumeProtection(*component.VolumeProtection)) 239 } 240 } 241 242 func buildWeSyncerContainer(weSyncerContainer *corev1.Container, probeSvcHTTPPort int) { 243 weSyncerContainer.Name = constant.WeSyncerContainerName 244 weSyncerContainer.StartupProbe.TCPSocket.Port = intstr.FromInt(probeSvcHTTPPort) 245 } 246 247 func buildRoleProbeContainer(component *SynthesizedComponent, roleChangedContainer *corev1.Container, 248 probeSetting *appsv1alpha1.ClusterDefinitionProbe, probeSvcHTTPPort int) { 249 roleChangedContainer.Name = constant.RoleProbeContainerName 250 httpGet := &corev1.HTTPGetAction{} 251 httpGet.Path = checkRoleURIFormat 252 httpGet.Port = intstr.FromInt(probeSvcHTTPPort) 253 probe := &corev1.Probe{} 254 probe.Exec = nil 255 probe.HTTPGet = httpGet 256 probe.PeriodSeconds = probeSetting.PeriodSeconds 257 probe.TimeoutSeconds = probeSetting.TimeoutSeconds 258 probe.FailureThreshold = probeSetting.FailureThreshold 259 roleChangedContainer.ReadinessProbe = probe 260 roleChangedContainer.StartupProbe.TCPSocket.Port = intstr.FromInt(probeSvcHTTPPort) 261 } 262 263 func buildStatusProbeContainer(characterType string, statusProbeContainer *corev1.Container, 264 probeSetting *appsv1alpha1.ClusterDefinitionProbe, probeSvcHTTPPort int) { 265 statusProbeContainer.Name = constant.StatusProbeContainerName 266 probe := &corev1.Probe{} 267 httpGet := &corev1.HTTPGetAction{} 268 httpGet.Path = fmt.Sprintf(checkStatusURIFormat, characterType) 269 httpGet.Port = intstr.FromInt(probeSvcHTTPPort) 270 probe.HTTPGet = httpGet 271 probe.PeriodSeconds = probeSetting.PeriodSeconds 272 probe.TimeoutSeconds = probeSetting.TimeoutSeconds 273 probe.FailureThreshold = probeSetting.FailureThreshold 274 statusProbeContainer.ReadinessProbe = probe 275 statusProbeContainer.StartupProbe.TCPSocket.Port = intstr.FromInt(probeSvcHTTPPort) 276 } 277 278 func buildRunningProbeContainer(characterType string, runningProbeContainer *corev1.Container, 279 probeSetting *appsv1alpha1.ClusterDefinitionProbe, probeSvcHTTPPort int) { 280 runningProbeContainer.Name = constant.RunningProbeContainerName 281 probe := &corev1.Probe{} 282 httpGet := &corev1.HTTPGetAction{} 283 httpGet.Path = fmt.Sprintf(checkRunningURIFormat, characterType) 284 httpGet.Port = intstr.FromInt(probeSvcHTTPPort) 285 probe.HTTPGet = httpGet 286 probe.PeriodSeconds = probeSetting.PeriodSeconds 287 probe.TimeoutSeconds = probeSetting.TimeoutSeconds 288 probe.FailureThreshold = probeSetting.FailureThreshold 289 runningProbeContainer.ReadinessProbe = probe 290 runningProbeContainer.StartupProbe.TCPSocket.Port = intstr.FromInt(probeSvcHTTPPort) 291 } 292 293 func volumeProtectionEnabled(component *SynthesizedComponent) bool { 294 return component.VolumeProtection != nil 295 } 296 297 func buildVolumeProtectionProbeContainer(characterType string, c *corev1.Container, probeSvcHTTPPort int) { 298 c.Name = constant.VolumeProtectionProbeContainerName 299 probe := &corev1.Probe{} 300 httpGet := &corev1.HTTPGetAction{} 301 httpGet.Path = fmt.Sprintf(volumeProtectionURIFormat, characterType) 302 httpGet.Port = intstr.FromInt(probeSvcHTTPPort) 303 probe.HTTPGet = httpGet 304 probe.PeriodSeconds = defaultVolumeProtectionProbe.PeriodSeconds 305 probe.TimeoutSeconds = defaultVolumeProtectionProbe.TimeoutSeconds 306 probe.FailureThreshold = defaultVolumeProtectionProbe.FailureThreshold 307 c.ReadinessProbe = probe 308 c.StartupProbe.TCPSocket.Port = intstr.FromInt(probeSvcHTTPPort) 309 } 310 311 func env4VolumeProtection(spec appsv1alpha1.VolumeProtectionSpec) corev1.EnvVar { 312 value, err := json.Marshal(spec) 313 if err != nil { 314 panic(fmt.Sprintf("marshal volume protection spec error: %s", err.Error())) 315 } 316 return corev1.EnvVar{ 317 Name: constant.KBEnvVolumeProtectionSpec, 318 Value: string(value), 319 } 320 }