github.com/docker/libcompose@v0.4.1-0.20210616120443-2a046c0bdbf2/config/interpolation_test.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "gopkg.in/yaml.v2" 11 ) 12 13 func testInterpolatedLine(t *testing.T, expectedLine, interpolatedLine string, envVariables map[string]string) { 14 interpolatedLine, _ = parseLine(interpolatedLine, func(s string) string { 15 return envVariables[s] 16 }) 17 18 assert.Equal(t, expectedLine, interpolatedLine) 19 } 20 21 func testInvalidInterpolatedLine(t *testing.T, line string) { 22 _, success := parseLine(line, func(string) string { 23 return "" 24 }) 25 26 assert.Equal(t, false, success) 27 } 28 29 func testInterpolatedDefault(t *testing.T, line string, delim string, expectedVar string, expectedVal string) { 30 envVar, _ := parseLine(line, func(env string) string { return env }) 31 pos := strings.Index(line, delim) 32 envDefault, _, _ := parseDefaultValue(line, pos) 33 assert.Equal(t, expectedVal, envDefault) 34 assert.Equal(t, expectedVar, envVar) 35 } 36 37 func TestParseLine(t *testing.T) { 38 variables := map[string]string{ 39 "A": "ABC", 40 "X": "XYZ", 41 "E": "", 42 "lower": "WORKED", 43 "MiXeD": "WORKED", 44 "split_VaLue": "WORKED", 45 "9aNumber": "WORKED", 46 "a9Number": "WORKED", 47 "defTest": "WORKED", 48 } 49 50 testInterpolatedDefault(t, "${defVar:-defVal}", ":-", "defVar", "defVal") 51 testInterpolatedDefault(t, "${defVar2-defVal2}", "-", "defVar2", "defVal2") 52 testInterpolatedDefault(t, "${defVar:-def:Val}", ":-", "defVar", "def:Val") 53 testInterpolatedDefault(t, "${defVar:-def-Val}", ":-", "defVar", "def-Val") 54 55 testInterpolatedLine(t, "WORKED", "$lower", variables) 56 testInterpolatedLine(t, "WORKED", "${MiXeD}", variables) 57 testInterpolatedLine(t, "WORKED", "${split_VaLue}", variables) 58 // make sure variable name is parsed correctly with default value 59 testInterpolatedLine(t, "WORKED", "${defTest:-sometest}", variables) 60 testInterpolatedLine(t, "WORKED", "${defTest-sometest}", variables) 61 // Starting with a number isn't valid 62 testInterpolatedLine(t, "", "$9aNumber", variables) 63 testInterpolatedLine(t, "WORKED", "$a9Number", variables) 64 65 testInterpolatedLine(t, "ABC", "$A", variables) 66 testInterpolatedLine(t, "ABC", "${A}", variables) 67 68 testInterpolatedLine(t, "ABC DE", "$A DE", variables) 69 testInterpolatedLine(t, "ABCDE", "${A}DE", variables) 70 71 testInterpolatedLine(t, "$A", "$$A", variables) 72 testInterpolatedLine(t, "${A}", "$${A}", variables) 73 74 testInterpolatedLine(t, "$ABC", "$$${A}", variables) 75 testInterpolatedLine(t, "$ABC", "$$$A", variables) 76 77 testInterpolatedLine(t, "ABC XYZ", "$A $X", variables) 78 testInterpolatedLine(t, "ABCXYZ", "$A$X", variables) 79 testInterpolatedLine(t, "ABCXYZ", "${A}${X}", variables) 80 81 testInterpolatedLine(t, "", "$B", variables) 82 testInterpolatedLine(t, "", "${B}", variables) 83 testInterpolatedLine(t, "", "$ADE", variables) 84 85 testInterpolatedLine(t, "", "$E", variables) 86 testInterpolatedLine(t, "", "${E}", variables) 87 88 testInvalidInterpolatedLine(t, "${df:val}") 89 testInvalidInterpolatedLine(t, "${") 90 testInvalidInterpolatedLine(t, "$}") 91 testInvalidInterpolatedLine(t, "${}") 92 testInvalidInterpolatedLine(t, "${ }") 93 testInvalidInterpolatedLine(t, "${A }") 94 testInvalidInterpolatedLine(t, "${ A}") 95 testInvalidInterpolatedLine(t, "${A!}") 96 testInvalidInterpolatedLine(t, "$!") 97 } 98 99 type MockEnvironmentLookup struct { 100 Variables map[string]string 101 } 102 103 func (m MockEnvironmentLookup) Lookup(key string, config *ServiceConfig) []string { 104 return []string{fmt.Sprintf("%s=%s", key, m.Variables[key])} 105 } 106 107 func testInterpolatedConfig(t *testing.T, expectedConfig, interpolatedConfig string, envVariables map[string]string) { 108 for k, v := range envVariables { 109 os.Setenv(k, v) 110 } 111 112 expectedConfigBytes := []byte(expectedConfig) 113 interpolatedConfigBytes := []byte(interpolatedConfig) 114 115 expectedData := make(RawServiceMap) 116 interpolatedData := make(RawServiceMap) 117 118 yaml.Unmarshal(expectedConfigBytes, &expectedData) 119 yaml.Unmarshal(interpolatedConfigBytes, &interpolatedData) 120 121 _ = InterpolateRawServiceMap(&interpolatedData, MockEnvironmentLookup{envVariables}) 122 123 for k := range envVariables { 124 os.Unsetenv(k) 125 } 126 127 assert.Equal(t, expectedData, interpolatedData) 128 } 129 130 func testInvalidInterpolatedConfig(t *testing.T, interpolatedConfig string) { 131 interpolatedConfigBytes := []byte(interpolatedConfig) 132 interpolatedData := make(RawServiceMap) 133 yaml.Unmarshal(interpolatedConfigBytes, &interpolatedData) 134 135 err := InterpolateRawServiceMap(&interpolatedData, new(MockEnvironmentLookup)) 136 assert.NotNil(t, err) 137 } 138 139 func TestInterpolate(t *testing.T) { 140 testInterpolatedConfig(t, 141 `web: 142 # unbracketed name 143 image: busybox 144 145 # array element 146 ports: 147 - "80:8000" 148 149 # dictionary item value 150 labels: 151 mylabel: "myvalue" 152 153 # unset value 154 hostname: "host-" 155 156 # escaped interpolation 157 command: "${ESCAPED}"`, 158 `web: 159 # unbracketed name 160 image: $IMAGE 161 162 # array element 163 ports: 164 - "${HOST_PORT}:8000" 165 166 # dictionary item value 167 labels: 168 mylabel: "${LABEL_VALUE}" 169 170 # unset value 171 hostname: "host-${UNSET_VALUE}" 172 173 # escaped interpolation 174 command: "$${ESCAPED}"`, map[string]string{ 175 "IMAGE": "busybox", 176 "HOST_PORT": "80", 177 "LABEL_VALUE": "myvalue", 178 }) 179 180 // Same as above, but testing with equal signs in variables 181 testInterpolatedConfig(t, 182 `web: 183 # unbracketed name 184 image: =busybox 185 186 # array element 187 ports: 188 - "=:8000" 189 190 # dictionary item value 191 labels: 192 mylabel: "myvalue==" 193 194 # unset value 195 hostname: "host-" 196 197 # escaped interpolation 198 command: "${ESCAPED}"`, 199 `web: 200 # unbracketed name 201 image: $IMAGE 202 203 # array element 204 ports: 205 - "${HOST_PORT}:8000" 206 207 # dictionary item value 208 labels: 209 mylabel: "${LABEL_VALUE}" 210 211 # unset value 212 hostname: "host-${UNSET_VALUE}" 213 214 # escaped interpolation 215 command: "$${ESCAPED}"`, map[string]string{ 216 "IMAGE": "=busybox", 217 "HOST_PORT": "=", 218 "LABEL_VALUE": "myvalue==", 219 }) 220 // same as above but with default values 221 testInterpolatedConfig(t, 222 `web: 223 # unbracketed name 224 image: busybox 225 226 # array element 227 ports: 228 - "80:8000" 229 230 # dictionary item value 231 labels: 232 mylabel: "my-val:ue" 233 234 # unset value 235 hostname: "host-" 236 237 # escaped interpolation 238 command: "${ESCAPED}"`, 239 240 `web: 241 # unbracketed name 242 image: ${IMAGE:-busybox} 243 244 # array element 245 ports: 246 - "${HOST_PORT:-80}:8000" 247 248 # dictionary item value 249 labels: 250 mylabel: "${LABEL_VALUE-my-val:ue}" 251 252 # unset value 253 hostname: "host-${UNSET_VALUE}" 254 255 # escaped interpolation 256 command: "$${ESCAPED}"`, map[string]string{}) 257 258 testInvalidInterpolatedConfig(t, 259 `web: 260 image: "${"`) 261 262 testInvalidInterpolatedConfig(t, 263 `web: 264 image: busybox 265 266 # array element 267 ports: 268 - "${}:8000"`) 269 270 testInvalidInterpolatedConfig(t, 271 `web: 272 image: busybox 273 274 # array element 275 ports: 276 - "80:8000" 277 278 # dictionary item value 279 labels: 280 mylabel: "${ LABEL_VALUE}"`) 281 }