github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/internal/bundle/parameters_test.go (about) 1 package bundle 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "strings" 8 "testing" 9 10 "github.com/docker/app/internal/image" 11 12 "github.com/deislabs/cnab-go/bundle" 13 "github.com/deislabs/cnab-go/bundle/definition" 14 "github.com/deislabs/cnab-go/claim" 15 "github.com/docker/app/internal" 16 "github.com/docker/app/internal/packager" 17 "github.com/docker/app/internal/store" 18 "gotest.tools/assert" 19 "gotest.tools/assert/cmp" 20 "gotest.tools/fs" 21 ) 22 23 func TestWithLoadFiles(t *testing.T) { 24 tmpDir := fs.NewDir(t, 25 t.Name(), 26 fs.WithFile("params.yaml", `param1: 27 param2: value1 28 param3: 3 29 overridden: bar`)) 30 defer tmpDir.Remove() 31 32 var bundle *bundle.Bundle 33 actual := map[string]string{ 34 "overridden": "foo", 35 } 36 err := WithFileParameters([]string{tmpDir.Join("params.yaml")})( 37 &MergeBundleConfig{ 38 bundle: bundle, 39 params: actual, 40 }) 41 assert.NilError(t, err) 42 expected := map[string]string{ 43 "param1.param2": "value1", 44 "param3": "3", 45 "overridden": "bar", 46 } 47 assert.Assert(t, cmp.DeepEqual(actual, expected)) 48 } 49 50 func TestWithCommandLineParameters(t *testing.T) { 51 var bundle *bundle.Bundle 52 actual := map[string]string{ 53 "overridden": "foo", 54 } 55 56 err := WithCommandLineParameters([]string{"param1.param2=value1", "param3=3", "overridden=bar"})( 57 &MergeBundleConfig{ 58 bundle: bundle, 59 params: actual, 60 }) 61 assert.NilError(t, err) 62 expected := map[string]string{ 63 "param1.param2": "value1", 64 "param3": "3", 65 "overridden": "bar", 66 } 67 assert.Assert(t, cmp.DeepEqual(actual, expected)) 68 } 69 70 type bundleOperator func(*bundle.Bundle) 71 72 func prepareBundleWithParameters(b *bundle.Bundle) { 73 if b.Parameters != nil { 74 return 75 } 76 b.Parameters = map[string]bundle.Parameter{} 77 b.Definitions = definition.Definitions{} 78 } 79 80 func withParameter(name, typ string) bundleOperator { 81 return func(b *bundle.Bundle) { 82 prepareBundleWithParameters(b) 83 b.Parameters[name] = bundle.Parameter{ 84 Definition: name, 85 } 86 b.Definitions[name] = &definition.Schema{ 87 Type: typ, 88 } 89 } 90 } 91 92 func withParameterAndDefault(name, typ string, def interface{}) bundleOperator { 93 return func(b *bundle.Bundle) { 94 prepareBundleWithParameters(b) 95 b.Parameters[name] = bundle.Parameter{ 96 Definition: name, 97 } 98 b.Definitions[name] = &definition.Schema{ 99 Type: typ, 100 Default: def, 101 } 102 } 103 } 104 105 func withParameterAndValues(name, typ string, allowedValues []interface{}) bundleOperator { 106 return func(b *bundle.Bundle) { 107 prepareBundleWithParameters(b) 108 b.Parameters[name] = bundle.Parameter{ 109 Definition: name, 110 } 111 b.Definitions[name] = &definition.Schema{ 112 Type: typ, 113 Enum: allowedValues, 114 } 115 } 116 } 117 118 func prepareBundle(ops ...bundleOperator) *image.AppImage { 119 b := image.FromBundle(&bundle.Bundle{}) 120 for _, op := range ops { 121 op(b.Bundle) 122 } 123 return b 124 } 125 126 func TestWithOrchestratorParameters(t *testing.T) { 127 testCases := []struct { 128 name string 129 bundle *image.AppImage 130 expected map[string]string 131 }{ 132 { 133 name: "AppImage with orchestrator params", 134 bundle: prepareBundle(withParameter(internal.ParameterOrchestratorName, "string"), withParameter(internal.ParameterKubernetesNamespaceName, "string")), 135 expected: map[string]string{ 136 internal.ParameterOrchestratorName: "kubernetes", 137 internal.ParameterKubernetesNamespaceName: "my-namespace", 138 }, 139 }, 140 { 141 name: "AppImage without orchestrator params", 142 bundle: prepareBundle(), 143 expected: map[string]string{}, 144 }, 145 } 146 147 for _, testCase := range testCases { 148 t.Run(testCase.name, func(t *testing.T) { 149 actual := map[string]string{} 150 err := WithOrchestratorParameters("kubernetes", "my-namespace")(&MergeBundleConfig{ 151 bundle: testCase.bundle.Bundle, 152 params: actual, 153 }) 154 assert.NilError(t, err) 155 assert.Assert(t, cmp.DeepEqual(actual, testCase.expected)) 156 }) 157 } 158 } 159 160 func TestMergeBundleParameters(t *testing.T) { 161 t.Run("Override Order", func(t *testing.T) { 162 first := func(c *MergeBundleConfig) error { 163 c.params["param"] = "first" 164 return nil 165 } 166 second := func(c *MergeBundleConfig) error { 167 c.params["param"] = "second" 168 return nil 169 } 170 bundle := prepareBundle(withParameterAndDefault("param", "string", "default")) 171 i := &store.Installation{ 172 Claim: claim.Claim{Bundle: bundle.Bundle}, 173 RelocationMap: bundle.RelocationMap, 174 } 175 err := MergeBundleParameters(i, 176 first, 177 second, 178 ) 179 assert.NilError(t, err) 180 expected := map[string]interface{}{ 181 "param": "second", 182 } 183 assert.Assert(t, cmp.DeepEqual(i.Parameters, expected)) 184 }) 185 186 t.Run("Default values", func(t *testing.T) { 187 bundle := prepareBundle(withParameterAndDefault("param", "string", "default")) 188 i := &store.Installation{ 189 Claim: claim.Claim{Bundle: bundle.Bundle}, 190 RelocationMap: bundle.RelocationMap, 191 } 192 err := MergeBundleParameters(i) 193 assert.NilError(t, err) 194 expected := map[string]interface{}{ 195 "param": "default", 196 } 197 assert.Assert(t, cmp.DeepEqual(i.Parameters, expected)) 198 }) 199 200 t.Run("Converting values", func(t *testing.T) { 201 withIntValue := func(c *MergeBundleConfig) error { 202 c.params["param"] = "1" 203 return nil 204 } 205 bundle := prepareBundle(withParameter("param", "integer")) 206 i := &store.Installation{ 207 Claim: claim.Claim{Bundle: bundle.Bundle}, 208 RelocationMap: bundle.RelocationMap, 209 } 210 err := MergeBundleParameters(i, withIntValue) 211 assert.NilError(t, err) 212 expected := map[string]interface{}{ 213 "param": 1, 214 } 215 assert.Assert(t, cmp.DeepEqual(i.Parameters, expected)) 216 }) 217 218 t.Run("Default values", func(t *testing.T) { 219 bundle := prepareBundle(withParameterAndDefault("param", "string", "default")) 220 i := &store.Installation{ 221 Claim: claim.Claim{Bundle: bundle.Bundle}, 222 RelocationMap: bundle.RelocationMap, 223 } 224 err := MergeBundleParameters(i) 225 assert.NilError(t, err) 226 expected := map[string]interface{}{ 227 "param": "default", 228 } 229 assert.Assert(t, cmp.DeepEqual(i.Parameters, expected)) 230 }) 231 232 t.Run("Undefined parameter throws warning", func(t *testing.T) { 233 withUndefined := func(c *MergeBundleConfig) error { 234 c.params["param"] = "1" 235 return nil 236 } 237 bundle := prepareBundle() 238 i := &store.Installation{ 239 Claim: claim.Claim{Bundle: bundle.Bundle}, 240 RelocationMap: bundle.RelocationMap, 241 } 242 buf := new(bytes.Buffer) 243 err := MergeBundleParameters(i, withUndefined, WithErrorWriter(buf)) 244 assert.NilError(t, err) 245 assert.Assert(t, strings.Contains(buf.String(), "is not defined in the bundle")) 246 }) 247 248 t.Run("Warn on undefined parameter", func(t *testing.T) { 249 withUndefined := func(c *MergeBundleConfig) error { 250 c.params["param"] = "1" 251 return nil 252 } 253 w := bytes.NewBuffer(nil) 254 withStdErr := func(c *MergeBundleConfig) error { 255 c.stderr = w 256 return nil 257 } 258 bundle := prepareBundle() 259 i := &store.Installation{ 260 Claim: claim.Claim{Bundle: bundle.Bundle}, 261 RelocationMap: bundle.RelocationMap, 262 } 263 err := MergeBundleParameters(i, withUndefined, withStdErr) 264 assert.NilError(t, err) 265 assert.Equal(t, w.String(), "Warning: parameter \"param\" is not defined in the bundle\n") 266 }) 267 268 t.Run("Invalid type is rejected", func(t *testing.T) { 269 withIntValue := func(c *MergeBundleConfig) error { 270 c.params["param"] = "foo" 271 return nil 272 } 273 bundle := prepareBundle(withParameter("param", "integer")) 274 i := &store.Installation{ 275 Claim: claim.Claim{Bundle: bundle.Bundle}, 276 RelocationMap: bundle.RelocationMap, 277 } 278 err := MergeBundleParameters(i, withIntValue) 279 assert.ErrorContains(t, err, "invalid value for parameter") 280 }) 281 282 t.Run("Invalid value is rejected", func(t *testing.T) { 283 withInvalidValue := func(c *MergeBundleConfig) error { 284 c.params["param"] = "invalid" 285 return nil 286 } 287 bundle := prepareBundle(withParameterAndValues("param", "string", []interface{}{"valid"})) 288 i := &store.Installation{ 289 Claim: claim.Claim{Bundle: bundle.Bundle}, 290 RelocationMap: bundle.RelocationMap, 291 } 292 err := MergeBundleParameters(i, withInvalidValue) 293 assert.ErrorContains(t, err, "invalid value for parameter") 294 }) 295 } 296 297 func TestLabels(t *testing.T) { 298 expected := packager.DockerAppArgs{ 299 Labels: map[string]string{ 300 "label": "value", 301 }, 302 } 303 expectedStr, err := json.Marshal(expected) 304 assert.NilError(t, err) 305 306 labels := []string{ 307 "label=value", 308 } 309 op := WithLabels(labels) 310 311 config := &MergeBundleConfig{ 312 bundle: &bundle.Bundle{ 313 Parameters: map[string]bundle.Parameter{ 314 internal.ParameterArgs: {}, 315 }, 316 }, 317 params: map[string]string{}, 318 } 319 err = op(config) 320 assert.NilError(t, err) 321 fmt.Println(config.params) 322 l := config.params[internal.ParameterArgs] 323 assert.Equal(t, l, string(expectedStr)) 324 } 325 326 func TestInvalidLabels(t *testing.T) { 327 labels := []string{ 328 "com.docker.app.label=value", 329 } 330 op := WithLabels(labels) 331 err := op(&MergeBundleConfig{}) 332 assert.ErrorContains(t, err, fmt.Sprintf("labels cannot start with %q", internal.Namespace)) 333 }