github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/configuration/config_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 configuration 21 22 import ( 23 "context" 24 "fmt" 25 "strings" 26 27 corev1 "k8s.io/api/core/v1" 28 apierrors "k8s.io/apimachinery/pkg/api/errors" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 "sigs.k8s.io/controller-runtime/pkg/log" 31 32 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 33 cfgcm "github.com/1aal/kubeblocks/pkg/configuration/config_manager" 34 "github.com/1aal/kubeblocks/pkg/configuration/core" 35 cfgutil "github.com/1aal/kubeblocks/pkg/configuration/util" 36 "github.com/1aal/kubeblocks/pkg/constant" 37 "github.com/1aal/kubeblocks/pkg/controller/component" 38 "github.com/1aal/kubeblocks/pkg/controller/factory" 39 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 40 viper "github.com/1aal/kubeblocks/pkg/viperx" 41 ) 42 43 func createConfigObjects(cli client.Client, ctx context.Context, objs []client.Object) error { 44 for _, obj := range objs { 45 if err := cli.Create(ctx, obj); err != nil { 46 if !apierrors.IsAlreadyExists(err) { 47 return err 48 } 49 // for update script cm 50 if core.IsSchedulableConfigResource(obj) { 51 continue 52 } 53 if err := cli.Update(ctx, obj); err != nil { 54 return err 55 } 56 } 57 } 58 return nil 59 } 60 61 func updateResourceAnnotationsWithTemplate(obj client.Object, allTemplateAnnotations map[string]string) { 62 // full configmap upgrade 63 existLabels := make(map[string]string) 64 annotations := obj.GetAnnotations() 65 if annotations == nil { 66 annotations = make(map[string]string) 67 } 68 for key, val := range annotations { 69 if strings.HasPrefix(key, constant.ConfigurationTplLabelPrefixKey) { 70 existLabels[key] = val 71 } 72 } 73 74 // delete not exist configmap label 75 deletedLabels := cfgutil.MapKeyDifference(existLabels, allTemplateAnnotations) 76 for l := range deletedLabels.Iter() { 77 delete(annotations, l) 78 } 79 80 for key, val := range allTemplateAnnotations { 81 annotations[key] = val 82 } 83 obj.SetAnnotations(annotations) 84 } 85 86 // buildConfigManagerWithComponent build the configmgr sidecar container and update it 87 // into PodSpec if configuration reload option is on 88 func buildConfigManagerWithComponent(podSpec *corev1.PodSpec, configSpecs []appsv1alpha1.ComponentConfigSpec, 89 ctx context.Context, cli client.Client, cluster *appsv1alpha1.Cluster, component *component.SynthesizedComponent) error { 90 var err error 91 var buildParams *cfgcm.CfgManagerBuildParams 92 93 volumeDirs, usingConfigSpecs := getUsingVolumesByConfigSpecs(podSpec, configSpecs) 94 if len(volumeDirs) == 0 { 95 return nil 96 } 97 configSpecMetas, err := cfgcm.GetSupportReloadConfigSpecs(usingConfigSpecs, cli, ctx) 98 if err != nil { 99 return err 100 } 101 // Configmap uses subPath case: https://github.com/kubernetes/kubernetes/issues/50345 102 // The files are being updated on the host VM, but can't be updated in the container. 103 configSpecMetas = cfgcm.FilterSubPathVolumeMount(configSpecMetas, volumeDirs) 104 if len(configSpecMetas) == 0 { 105 return nil 106 } 107 if buildParams, err = buildConfigManagerParams(cli, ctx, cluster, component, configSpecMetas, volumeDirs, podSpec); err != nil { 108 return err 109 } 110 if buildParams == nil { 111 return nil 112 } 113 114 // This sidecar container will be able to view and signal processes from other containers 115 checkAndUpdateSharProcessNamespace(podSpec, buildParams, configSpecMetas) 116 container, err := factory.BuildCfgManagerContainer(buildParams, component) 117 if err != nil { 118 return err 119 } 120 updateEnvPath(container, buildParams) 121 updateCfgManagerVolumes(podSpec, buildParams) 122 123 // Add sidecar to podTemplate 124 podSpec.Containers = append(podSpec.Containers, *container) 125 if len(buildParams.ToolsContainers) > 0 { 126 podSpec.InitContainers = append(podSpec.InitContainers, buildParams.ToolsContainers...) 127 } 128 return nil 129 } 130 131 func checkAndUpdateSharProcessNamespace(podSpec *corev1.PodSpec, buildParams *cfgcm.CfgManagerBuildParams, configSpecMetas []cfgcm.ConfigSpecMeta) { 132 shared := cfgcm.NeedSharedProcessNamespace(configSpecMetas) 133 if shared { 134 podSpec.ShareProcessNamespace = func() *bool { b := true; return &b }() 135 } 136 buildParams.ShareProcessNamespace = shared 137 } 138 139 func updateEnvPath(container *corev1.Container, params *cfgcm.CfgManagerBuildParams) { 140 if len(params.ScriptVolume) == 0 { 141 return 142 } 143 scriptPath := make([]string, 0, len(params.ScriptVolume)) 144 for _, volume := range params.ScriptVolume { 145 if vm := cfgcm.FindVolumeMount(params.Volumes, volume.Name); vm != nil { 146 scriptPath = append(scriptPath, vm.MountPath) 147 } 148 } 149 if len(scriptPath) != 0 { 150 container.Env = append(container.Env, corev1.EnvVar{ 151 Name: cfgcm.KBConfigManagerPathEnv, 152 Value: strings.Join(scriptPath, ":"), 153 }) 154 } 155 } 156 157 func updateCfgManagerVolumes(podSpec *corev1.PodSpec, configManager *cfgcm.CfgManagerBuildParams) { 158 scriptVolumes := configManager.ScriptVolume 159 if len(scriptVolumes) == 0 && len(configManager.CMConfigVolumes) == 0 { 160 return 161 } 162 163 podVolumes := podSpec.Volumes 164 for _, vm := range []*[]corev1.Volume{ 165 &configManager.ScriptVolume, 166 &configManager.CMConfigVolumes, 167 } { 168 for i := range *vm { 169 podVolumes, _ = intctrlutil.CreateOrUpdateVolume(podVolumes, (*vm)[i].Name, func(string) corev1.Volume { 170 return (*vm)[i] 171 }, nil) 172 } 173 } 174 podSpec.Volumes = podVolumes 175 176 for volumeName, volume := range configManager.ConfigLazyRenderedVolumes { 177 usingContainers := intctrlutil.GetPodContainerWithVolumeMount(podSpec, volumeName) 178 for _, container := range usingContainers { 179 container.VolumeMounts = append(container.VolumeMounts, volume) 180 } 181 } 182 } 183 184 func getUsingVolumesByConfigSpecs(podSpec *corev1.PodSpec, configSpecs []appsv1alpha1.ComponentConfigSpec) ([]corev1.VolumeMount, []appsv1alpha1.ComponentConfigSpec) { 185 // Ignore useless configTemplate 186 usingConfigSpecs := make([]appsv1alpha1.ComponentConfigSpec, 0, len(configSpecs)) 187 config2Containers := make(map[string][]*corev1.Container) 188 for _, configSpec := range configSpecs { 189 usingContainers := intctrlutil.GetPodContainerWithVolumeMount(podSpec, configSpec.VolumeName) 190 if len(usingContainers) == 0 { 191 continue 192 } 193 usingConfigSpecs = append(usingConfigSpecs, configSpec) 194 config2Containers[configSpec.Name] = usingContainers 195 } 196 197 // No container using any config template 198 if len(usingConfigSpecs) == 0 { 199 log.Log.Info(fmt.Sprintf("configSpec config is not used by any container, and pass. configSpec configs: %v", configSpecs)) 200 return nil, nil 201 } 202 203 // Find out which configurations are used by the container 204 volumeDirs := make([]corev1.VolumeMount, 0, len(configSpecs)+1) 205 for _, configSpec := range usingConfigSpecs { 206 // Ignore config template, e.g scripts configmap 207 if !core.NeedReloadVolume(configSpec) { 208 continue 209 } 210 sets := cfgutil.NewSet() 211 for _, container := range config2Containers[configSpec.Name] { 212 volume := intctrlutil.GetVolumeMountByVolume(container, configSpec.VolumeName) 213 if volume != nil && !sets.InArray(volume.Name) { 214 volumeDirs = append(volumeDirs, *volume) 215 sets.Add(volume.Name) 216 } 217 } 218 } 219 return volumeDirs, usingConfigSpecs 220 } 221 222 func buildConfigManagerParams(cli client.Client, ctx context.Context, cluster *appsv1alpha1.Cluster, comp *component.SynthesizedComponent, configSpecBuildParams []cfgcm.ConfigSpecMeta, volumeDirs []corev1.VolumeMount, podSpec *corev1.PodSpec) (*cfgcm.CfgManagerBuildParams, error) { 223 cfgManagerParams := &cfgcm.CfgManagerBuildParams{ 224 ManagerName: constant.ConfigSidecarName, 225 CharacterType: comp.CharacterType, 226 ComponentName: comp.Name, 227 SecreteName: component.GenerateConnCredential(cluster.Name), 228 EnvConfigName: component.GenerateComponentEnvName(cluster.Name, comp.Name), 229 Image: viper.GetString(constant.KBToolsImage), 230 Volumes: volumeDirs, 231 Cluster: cluster, 232 ConfigSpecsBuildParams: configSpecBuildParams, 233 ConfigLazyRenderedVolumes: make(map[string]corev1.VolumeMount), 234 } 235 236 if err := cfgcm.BuildConfigManagerContainerParams(cli, ctx, cfgManagerParams, volumeDirs); err != nil { 237 return nil, err 238 } 239 if err := buildConfigToolsContainer(cfgManagerParams, podSpec, comp); err != nil { 240 return nil, err 241 } 242 return cfgManagerParams, nil 243 }