github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/common/expand_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 common 21 22 import ( 23 "testing" 24 25 corev1 "k8s.io/api/core/v1" 26 ) 27 28 // forked from k8s.io/kubernetes/third_party/forked/golang/expansion 29 func TestMapReference(t *testing.T) { 30 envs := []corev1.EnvVar{ 31 { 32 Name: "FOO", 33 Value: "bar", 34 }, 35 { 36 Name: "ZOO", 37 Value: "$(FOO)-1", 38 }, 39 { 40 Name: "BLU", 41 Value: "$(ZOO)-2", 42 }, 43 } 44 45 declaredEnv := map[string]string{ 46 "FOO": "bar", 47 "ZOO": "$(FOO)-1", 48 "BLU": "$(ZOO)-2", 49 } 50 51 serviceEnv := map[string]string{} 52 53 mapping := MappingFuncFor(declaredEnv, serviceEnv) 54 55 for _, env := range envs { 56 declaredEnv[env.Name] = Expand(env.Value, mapping) 57 } 58 59 expectedEnv := map[string]string{ 60 "FOO": "bar", 61 "ZOO": "bar-1", 62 "BLU": "bar-1-2", 63 } 64 65 for k, v := range expectedEnv { 66 if e, a := v, declaredEnv[k]; e != a { 67 t.Errorf("Expected %v, got %v", e, a) 68 } else { 69 delete(declaredEnv, k) 70 } 71 } 72 73 if len(declaredEnv) != 0 { 74 t.Errorf("Unexpected keys in declared env: %v", declaredEnv) 75 } 76 } 77 78 func TestMapping(t *testing.T) { 79 context := map[string]string{ 80 "VAR_A": "A", 81 "VAR_B": "B", 82 "VAR_C": "C", 83 "VAR_REF": "$(VAR_A)", 84 "VAR_EMPTY": "", 85 } 86 mapping := MappingFuncFor(context) 87 88 doExpansionTest(t, mapping) 89 } 90 91 func TestMappingDual(t *testing.T) { 92 context := map[string]string{ 93 "VAR_A": "A", 94 "VAR_EMPTY": "", 95 } 96 context2 := map[string]string{ 97 "VAR_B": "B", 98 "VAR_C": "C", 99 "VAR_REF": "$(VAR_A)", 100 } 101 mapping := MappingFuncFor(context, context2) 102 103 doExpansionTest(t, mapping) 104 } 105 106 func doExpansionTest(t *testing.T, mapping func(string) string) { 107 cases := []struct { 108 name string 109 input string 110 expected string 111 }{ 112 { 113 name: "whole string", 114 input: "$(VAR_A)", 115 expected: "A", 116 }, 117 { 118 name: "repeat", 119 input: "$(VAR_A)-$(VAR_A)", 120 expected: "A-A", 121 }, 122 { 123 name: "beginning", 124 input: "$(VAR_A)-1", 125 expected: "A-1", 126 }, 127 { 128 name: "middle", 129 input: "___$(VAR_B)___", 130 expected: "___B___", 131 }, 132 { 133 name: "end", 134 input: "___$(VAR_C)", 135 expected: "___C", 136 }, 137 { 138 name: "compound", 139 input: "$(VAR_A)_$(VAR_B)_$(VAR_C)", 140 expected: "A_B_C", 141 }, 142 { 143 name: "escape & expand", 144 input: "$$(VAR_B)_$(VAR_A)", 145 expected: "$(VAR_B)_A", 146 }, 147 { 148 name: "compound escape", 149 input: "$$(VAR_A)_$$(VAR_B)", 150 expected: "$(VAR_A)_$(VAR_B)", 151 }, 152 { 153 name: "mixed in escapes", 154 input: "f000-$$VAR_A", 155 expected: "f000-$VAR_A", 156 }, 157 { 158 name: "backslash escape ignored", 159 input: "foo\\$(VAR_C)bar", 160 expected: "foo\\Cbar", 161 }, 162 { 163 name: "backslash escape ignored", 164 input: "foo\\\\$(VAR_C)bar", 165 expected: "foo\\\\Cbar", 166 }, 167 { 168 name: "lots of backslashes", 169 input: "foo\\\\\\\\$(VAR_A)bar", 170 expected: "foo\\\\\\\\Abar", 171 }, 172 { 173 name: "nested var references", 174 input: "$(VAR_A$(VAR_B))", 175 expected: "$(VAR_A$(VAR_B))", 176 }, 177 { 178 name: "nested var references second type", 179 input: "$(VAR_A$(VAR_B)", 180 expected: "$(VAR_A$(VAR_B)", 181 }, 182 { 183 name: "value is a reference", 184 input: "$(VAR_REF)", 185 expected: "$(VAR_A)", 186 }, 187 { 188 name: "value is a reference x 2", 189 input: "%%$(VAR_REF)--$(VAR_REF)%%", 190 expected: "%%$(VAR_A)--$(VAR_A)%%", 191 }, 192 { 193 name: "empty var", 194 input: "foo$(VAR_EMPTY)bar", 195 expected: "foobar", 196 }, 197 { 198 name: "unterminated expression", 199 input: "foo$(VAR_Awhoops!", 200 expected: "foo$(VAR_Awhoops!", 201 }, 202 { 203 name: "expression without operator", 204 input: "f00__(VAR_A)__", 205 expected: "f00__(VAR_A)__", 206 }, 207 { 208 name: "shell special vars pass through", 209 input: "$?_boo_$!", 210 expected: "$?_boo_$!", 211 }, 212 { 213 name: "bare operators are ignored", 214 input: "$VAR_A", 215 expected: "$VAR_A", 216 }, 217 { 218 name: "undefined vars are passed through", 219 input: "$(VAR_DNE)", 220 expected: "$(VAR_DNE)", 221 }, 222 { 223 name: "multiple (even) operators, var undefined", 224 input: "$$$$$$(BIG_MONEY)", 225 expected: "$$$(BIG_MONEY)", 226 }, 227 { 228 name: "multiple (even) operators, var defined", 229 input: "$$$$$$(VAR_A)", 230 expected: "$$$(VAR_A)", 231 }, 232 { 233 name: "multiple (odd) operators, var undefined", 234 input: "$$$$$$$(GOOD_ODDS)", 235 expected: "$$$$(GOOD_ODDS)", 236 }, 237 { 238 name: "multiple (odd) operators, var defined", 239 input: "$$$$$$$(VAR_A)", 240 expected: "$$$A", 241 }, 242 { 243 name: "missing open expression", 244 input: "$VAR_A)", 245 expected: "$VAR_A)", 246 }, 247 { 248 name: "shell syntax ignored", 249 input: "${VAR_A}", 250 expected: "${VAR_A}", 251 }, 252 { 253 name: "trailing incomplete expression not consumed", 254 input: "$(VAR_B)_______$(A", 255 expected: "B_______$(A", 256 }, 257 { 258 name: "trailing incomplete expression, no content, is not consumed", 259 input: "$(VAR_C)_______$(", 260 expected: "C_______$(", 261 }, 262 { 263 name: "operator at end of input string is preserved", 264 input: "$(VAR_A)foobarzab$", 265 expected: "Afoobarzab$", 266 }, 267 { 268 name: "shell escaped incomplete expr", 269 input: "foo-\\$(VAR_A", 270 expected: "foo-\\$(VAR_A", 271 }, 272 { 273 name: "lots of $( in middle", 274 input: "--$($($($($--", 275 expected: "--$($($($($--", 276 }, 277 { 278 name: "lots of $( in beginning", 279 input: "$($($($($--foo$(", 280 expected: "$($($($($--foo$(", 281 }, 282 { 283 name: "lots of $( at end", 284 input: "foo0--$($($($(", 285 expected: "foo0--$($($($(", 286 }, 287 { 288 name: "escaped operators in variable names are not escaped", 289 input: "$(foo$$var)", 290 expected: "$(foo$$var)", 291 }, 292 { 293 name: "newline not expanded", 294 input: "\n", 295 expected: "\n", 296 }, 297 } 298 299 for _, tc := range cases { 300 expanded := Expand(tc.input, mapping) 301 if e, a := tc.expected, expanded; e != a { 302 t.Errorf("%v: expected %q, got %q", tc.name, e, a) 303 } 304 } 305 }