github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/configuration/builtin_env_test.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 "strconv" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 28 corev1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/api/resource" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 coreclient "sigs.k8s.io/controller-runtime/pkg/client" 32 33 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 34 ctrlcomp "github.com/1aal/kubeblocks/pkg/controller/component" 35 testutil "github.com/1aal/kubeblocks/pkg/testutil/k8s" 36 ) 37 38 var _ = Describe("tpl env template", func() { 39 40 patroniTemplate := ` 41 bootstrap: 42 initdb: 43 - auth-host: md5 44 - auth-local: trust 45 ` 46 const ( 47 cmTemplateName = "patroni-template-config" 48 cmConfigFileName = "postgresql.yaml" 49 ) 50 51 var ( 52 podSpec *corev1.PodSpec 53 cfgTemplate []appsv1alpha1.ComponentConfigSpec 54 component *ctrlcomp.SynthesizedComponent 55 cluster *appsv1alpha1.Cluster 56 57 mockClient *testutil.K8sClientMockHelper 58 ) 59 60 BeforeEach(func() { 61 mockClient = testutil.NewK8sMockClient() 62 63 mockClient.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]coreclient.Object{ 64 &corev1.ConfigMap{ 65 ObjectMeta: metav1.ObjectMeta{ 66 Name: cmTemplateName, 67 Namespace: "default", 68 }, 69 Data: map[string]string{ 70 cmConfigFileName: patroniTemplate, 71 }}, 72 &corev1.ConfigMap{ 73 ObjectMeta: metav1.ObjectMeta{ 74 Name: "my-config-env", 75 Namespace: "default", 76 }, 77 Data: map[string]string{ 78 "KB_0_HOSTNAME": "my-mysql-0.my-mysql-headless", 79 "KB_FOLLOWERS": "", 80 "KB_LEADER": "my-mysql-0", 81 "KB_REPLICA_COUNT": "1", 82 "LOOP_REFERENCE_A": "$(LOOP_REFERENCE_B)", 83 "LOOP_REFERENCE_B": "$(LOOP_REFERENCE_C)", 84 "LOOP_REFERENCE_C": "$(LOOP_REFERENCE_A)", 85 }}, 86 &corev1.Secret{ 87 ObjectMeta: metav1.ObjectMeta{ 88 Name: "my-conn-credential", 89 Namespace: "default", 90 }, 91 Data: map[string][]byte{ 92 "password": []byte("4zrqfl2r"), 93 "username": []byte("root"), 94 }}, 95 }), testutil.WithAnyTimes())) 96 97 // 2 configmap and 2 secret 98 // Add any setup steps that needs to be executed before each test 99 podSpec = &corev1.PodSpec{ 100 Containers: []corev1.Container{ 101 { 102 Name: "mytest", 103 Env: []corev1.EnvVar{ 104 { 105 Name: "KB_CLUSTER_NAME", 106 Value: "my", 107 }, 108 { 109 Name: "KB_COMP_NAME", 110 Value: "mysql", 111 }, 112 { 113 Name: "MEMORY_SIZE", 114 ValueFrom: &corev1.EnvVarSource{ 115 ResourceFieldRef: &corev1.ResourceFieldSelector{ 116 ContainerName: "mytest", 117 Resource: "limits.memory", 118 }, 119 }, 120 }, 121 { 122 Name: "CPU", 123 ValueFrom: &corev1.EnvVarSource{ 124 ResourceFieldRef: &corev1.ResourceFieldSelector{ 125 Resource: "limits.cpu", 126 }, 127 }, 128 }, 129 { 130 Name: "CPU2", 131 ValueFrom: &corev1.EnvVarSource{ 132 ResourceFieldRef: &corev1.ResourceFieldSelector{ 133 ContainerName: "not_exist_container", 134 Resource: "limits.memory", 135 }, 136 }, 137 }, 138 { 139 Name: "MYSQL_USER", 140 ValueFrom: &corev1.EnvVarSource{ 141 SecretKeyRef: &corev1.SecretKeySelector{ 142 LocalObjectReference: corev1.LocalObjectReference{ 143 Name: "my-conn-credential", 144 }, 145 Key: "username", 146 }, 147 }, 148 }, 149 { 150 Name: "MYSQL_PASSWORD", 151 ValueFrom: &corev1.EnvVarSource{ 152 SecretKeyRef: &corev1.SecretKeySelector{ 153 LocalObjectReference: corev1.LocalObjectReference{ 154 Name: "$(CONN_CREDENTIAL_SECRET_NAME)", 155 }, 156 Key: "password", 157 }, 158 }, 159 }, 160 { 161 Name: "SPILO_CONFIGURATION", 162 ValueFrom: &corev1.EnvVarSource{ 163 ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ 164 LocalObjectReference: corev1.LocalObjectReference{ 165 Name: cmTemplateName, 166 }, 167 Key: cmConfigFileName, 168 }, 169 }, 170 }, 171 }, 172 EnvFrom: []corev1.EnvFromSource{ 173 { 174 ConfigMapRef: &corev1.ConfigMapEnvSource{ 175 LocalObjectReference: corev1.LocalObjectReference{ 176 Name: "my-config-env", 177 }, 178 }, 179 }, 180 { 181 SecretRef: &corev1.SecretEnvSource{ 182 LocalObjectReference: corev1.LocalObjectReference{ 183 Name: "my-secret-env", 184 }, 185 }, 186 }, 187 }, 188 Resources: corev1.ResourceRequirements{ 189 Limits: map[corev1.ResourceName]resource.Quantity{ 190 corev1.ResourceMemory: resource.MustParse("8Gi"), 191 corev1.ResourceCPU: resource.MustParse("4"), 192 }, 193 }, 194 }, 195 { 196 Name: "invalid_container", 197 }, 198 }, 199 } 200 cluster = &appsv1alpha1.Cluster{ 201 ObjectMeta: metav1.ObjectMeta{ 202 Name: "my", 203 UID: "b006a20c-fb03-441c-bffa-2605cad7e297", 204 }, 205 } 206 component = &ctrlcomp.SynthesizedComponent{ 207 Name: "mysql", 208 ClusterName: cluster.Name, 209 } 210 cfgTemplate = []appsv1alpha1.ComponentConfigSpec{{ 211 ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{ 212 Name: "mysql-config-8.0.2", 213 TemplateRef: "mysql-config", 214 VolumeName: "config1", 215 }, 216 ConfigConstraintRef: "mysql-config-8.0.2-constraint", 217 }} 218 }) 219 220 AfterEach(func() { 221 mockClient.Finish() 222 }) 223 224 // for test GetContainerWithVolumeMount 225 Context("ConfigTemplateBuilder built-in env test", func() { 226 It("test built-in function", func() { 227 cfgBuilder := newTemplateBuilder( 228 "my_test", 229 "default", 230 &appsv1alpha1.Cluster{ 231 ObjectMeta: metav1.ObjectMeta{ 232 Name: "my_test", 233 Namespace: "default", 234 }, 235 }, 236 nil, ctx, mockClient.Client(), 237 ) 238 239 localObjs := []coreclient.Object{ 240 &corev1.ConfigMap{ 241 ObjectMeta: metav1.ObjectMeta{ 242 Name: cmTemplateName, 243 Namespace: "default", 244 }, 245 Data: map[string]string{ 246 cmConfigFileName: patroniTemplate, 247 }}, 248 } 249 Expect(cfgBuilder.injectBuiltInObjectsAndFunctions(podSpec, cfgTemplate, component, localObjs)).Should(BeNil()) 250 251 rendered, err := cfgBuilder.render(map[string]string{ 252 // KB_CLUSTER_NAME, KB_COMP_NAME from env 253 // MYSQL_USER,MYSQL_PASSWORD from valueFrom secret key 254 // SPILO_CONFIGURATION from valueFrom configmap key 255 // KB_LEADER from envFrom configmap 256 // MEMORY_SIZE, CPU from resourceFieldRef 257 "my": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"KB_CLUSTER_NAME\" }}", 258 "mysql": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"KB_COMP_NAME\" }}", 259 "root": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MYSQL_USER\" }}", 260 "4zrqfl2r": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MYSQL_PASSWORD\" }}", 261 patroniTemplate: "{{ getEnvByName ( index $.podSpec.containers 0 ) \"SPILO_CONFIGURATION\" }}", 262 "my-mysql-0": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"KB_LEADER\" }}", 263 264 strconv.Itoa(4): "{{ getEnvByName ( index $.podSpec.containers 0 ) \"CPU\" }}", 265 strconv.Itoa(8 * 1024 * 1024 * 1024): "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MEMORY_SIZE\" }}", 266 }) 267 268 Expect(err).Should(Succeed()) 269 for key, value := range rendered { 270 Expect(key).Should(BeEquivalentTo(value)) 271 } 272 273 _, err = cfgBuilder.render(map[string]string{ 274 "error": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"CPU2\" }}", 275 }) 276 Expect(err).ShouldNot(Succeed()) 277 Expect(err.Error()).Should(ContainSubstring("not found named[not_exist_container] container")) 278 279 _, err = cfgBuilder.render(map[string]string{ 280 "error_loop_reference": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"LOOP_REFERENCE_A\" }}", 281 }) 282 Expect(err).ShouldNot(Succeed()) 283 Expect(err.Error()).Should(ContainSubstring("too many reference count, maybe there is a cycled reference")) 284 }) 285 }) 286 })