github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/configuration/envfrom_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 25 "github.com/spf13/cast" 26 corev1 "k8s.io/api/core/v1" 27 apierrors "k8s.io/apimachinery/pkg/api/errors" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 30 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 31 "github.com/1aal/kubeblocks/pkg/configuration/core" 32 cfgutil "github.com/1aal/kubeblocks/pkg/configuration/util" 33 "github.com/1aal/kubeblocks/pkg/configuration/validate" 34 "github.com/1aal/kubeblocks/pkg/constant" 35 "github.com/1aal/kubeblocks/pkg/controller/component" 36 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 37 "github.com/1aal/kubeblocks/pkg/generics" 38 ) 39 40 func injectTemplateEnvFrom(cluster *appsv1alpha1.Cluster, component *component.SynthesizedComponent, podSpec *corev1.PodSpec, cli client.Client, ctx context.Context, localObjs []client.Object) error { 41 var err error 42 var cm *corev1.ConfigMap 43 44 injectConfigmap := func(envMap map[string]string, configSpec appsv1alpha1.ComponentConfigSpec, cmName string) error { 45 envConfigMap, err := createEnvFromConfigmap(cluster, component.Name, configSpec, client.ObjectKeyFromObject(cm), envMap, ctx, cli) 46 if err != nil { 47 return core.WrapError(err, "failed to generate env configmap[%s]", cmName) 48 } 49 injectEnvFrom(podSpec.Containers, configSpec.AsEnvFrom, envConfigMap.Name) 50 injectEnvFrom(podSpec.InitContainers, configSpec.AsEnvFrom, envConfigMap.Name) 51 return nil 52 } 53 54 for _, template := range component.ConfigTemplates { 55 if len(template.AsEnvFrom) == 0 || template.ConfigConstraintRef == "" { 56 continue 57 } 58 cmName := core.GetComponentCfgName(cluster.Name, component.Name, template.Name) 59 if cm, err = fetchConfigmap(localObjs, cmName, cluster.Namespace, cli, ctx); err != nil { 60 return err 61 } 62 cc, err := getConfigConstraint(template, cli, ctx) 63 if err != nil { 64 return err 65 } 66 envMap, err := fromConfigmapFiles(fromConfigSpec(template, cm), cm, cc.FormatterConfig) 67 if err != nil { 68 return err 69 } 70 if len(envMap) == 0 { 71 continue 72 } 73 if err := injectConfigmap(envMap, template, cmName); err != nil { 74 return err 75 } 76 } 77 return nil 78 } 79 80 func getConfigConstraint(template appsv1alpha1.ComponentConfigSpec, cli client.Client, ctx context.Context) (*appsv1alpha1.ConfigConstraintSpec, error) { 81 ccKey := client.ObjectKey{ 82 Namespace: "", 83 Name: template.ConfigConstraintRef, 84 } 85 cc := &appsv1alpha1.ConfigConstraint{} 86 if err := cli.Get(ctx, ccKey, cc); err != nil { 87 return nil, core.WrapError(err, "failed to get ConfigConstraint, key[%v]", ccKey) 88 } 89 if cc.Spec.FormatterConfig == nil { 90 return nil, core.MakeError("ConfigConstraint[%v] is not a formatter", cc.Name) 91 } 92 return &cc.Spec, nil 93 } 94 95 func fromConfigmapFiles(keys []string, cm *corev1.ConfigMap, formatter *appsv1alpha1.FormatterConfig) (map[string]string, error) { 96 mergeMap := func(dst, src map[string]string) { 97 for key, val := range src { 98 dst[key] = val 99 } 100 } 101 102 gEnvMap := make(map[string]string) 103 for _, file := range keys { 104 envMap, err := fromFileContent(formatter, cm.Data[file]) 105 if err != nil { 106 return nil, err 107 } 108 mergeMap(gEnvMap, envMap) 109 } 110 return gEnvMap, nil 111 } 112 113 func fetchConfigmap(localObjs []client.Object, cmName, namespace string, cli client.Client, ctx context.Context) (*corev1.ConfigMap, error) { 114 var ( 115 cmObj = &corev1.ConfigMap{} 116 cmKey = client.ObjectKey{Name: cmName, Namespace: namespace} 117 ) 118 119 localObject := findMatchedLocalObject(localObjs, cmKey, generics.ToGVK(cmObj)) 120 if localObject != nil { 121 return localObject.(*corev1.ConfigMap), nil 122 } 123 if err := cli.Get(ctx, cmKey, cmObj); err != nil { 124 return nil, err 125 } 126 return cmObj, nil 127 } 128 129 func createEnvFromConfigmap(cluster *appsv1alpha1.Cluster, componentName string, template appsv1alpha1.ComponentConfigSpec, originKey client.ObjectKey, envMap map[string]string, ctx context.Context, cli client.Client) (*corev1.ConfigMap, error) { 130 cmKey := client.ObjectKey{ 131 Name: core.GenerateEnvFromName(originKey.Name), 132 Namespace: originKey.Namespace, 133 } 134 cm := &corev1.ConfigMap{} 135 err := cli.Get(ctx, cmKey, cm) 136 if err == nil { 137 return cm, nil 138 } 139 if !apierrors.IsNotFound(err) { 140 return nil, err 141 } 142 cm.Name = cmKey.Name 143 cm.Namespace = cmKey.Namespace 144 cm.Data = envMap 145 cm.Labels = map[string]string{ 146 constant.CMTemplateNameLabelKey: template.Name, 147 constant.AppNameLabelKey: cluster.Spec.ClusterDefRef, 148 constant.AppInstanceLabelKey: cluster.Name, 149 constant.KBAppComponentLabelKey: componentName, 150 } 151 if err := intctrlutil.SetOwnerReference(cluster, cm); err != nil { 152 return nil, err 153 } 154 return cm, cli.Create(ctx, cm) 155 } 156 157 func CheckEnvFrom(container *corev1.Container, cmName string) bool { 158 for i := range container.EnvFrom { 159 source := &container.EnvFrom[i] 160 if source.ConfigMapRef != nil && source.ConfigMapRef.Name == cmName { 161 return true 162 } 163 } 164 return false 165 } 166 167 func injectEnvFrom(containers []corev1.Container, asEnvFrom []string, cmName string) { 168 sets := cfgutil.NewSet(asEnvFrom...) 169 for i := range containers { 170 container := &containers[i] 171 if sets.InArray(container.Name) && !CheckEnvFrom(container, cmName) { 172 container.EnvFrom = append(container.EnvFrom, 173 corev1.EnvFromSource{ 174 ConfigMapRef: &corev1.ConfigMapEnvSource{ 175 LocalObjectReference: corev1.LocalObjectReference{ 176 Name: cmName, 177 }}, 178 }) 179 } 180 } 181 } 182 183 func fromFileContent(format *appsv1alpha1.FormatterConfig, configContext string) (map[string]string, error) { 184 keyValue, err := validate.LoadConfigObjectFromContent(format.Format, configContext) 185 if err != nil { 186 return nil, err 187 } 188 envMap := make(map[string]string, len(keyValue)) 189 for key, v := range keyValue { 190 envMap[key] = cast.ToString(v) 191 } 192 return envMap, nil 193 } 194 195 func fromConfigSpec(configSpec appsv1alpha1.ComponentConfigSpec, cm *corev1.ConfigMap) []string { 196 keys := configSpec.Keys 197 if len(keys) == 0 { 198 keys = cfgutil.ToSet(cm.Data).AsSlice() 199 } 200 return keys 201 } 202 203 func SyncEnvConfigmap(configSpec appsv1alpha1.ComponentConfigSpec, cmObj *corev1.ConfigMap, cc *appsv1alpha1.ConfigConstraintSpec, cli client.Client, ctx context.Context) error { 204 if len(configSpec.AsEnvFrom) == 0 || cc == nil || cc.FormatterConfig == nil { 205 return nil 206 } 207 envMap, err := fromConfigmapFiles(fromConfigSpec(configSpec, cmObj), cmObj, cc.FormatterConfig) 208 if err != nil { 209 return err 210 } 211 if len(envMap) == 0 { 212 return nil 213 } 214 215 return updateEnvFromConfigmap(client.ObjectKeyFromObject(cmObj), envMap, cli, ctx) 216 } 217 218 func updateEnvFromConfigmap(origObj client.ObjectKey, envMap map[string]string, cli client.Client, ctx context.Context) error { 219 cmKey := client.ObjectKey{ 220 Name: core.GenerateEnvFromName(origObj.Name), 221 Namespace: origObj.Namespace, 222 } 223 cm := &corev1.ConfigMap{} 224 if err := cli.Get(ctx, cmKey, cm); err != nil { 225 return err 226 } 227 patch := client.MergeFrom(cm.DeepCopy()) 228 cm.Data = envMap 229 if err := cli.Patch(ctx, cm, patch); err != nil { 230 return err 231 } 232 return nil 233 }