get.porter.sh/porter@v1.3.0/pkg/runtime/runtime_manifest_test.go (about) 1 package runtime 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "sort" 8 "testing" 9 10 "get.porter.sh/porter/pkg" 11 "get.porter.sh/porter/pkg/cnab" 12 "get.porter.sh/porter/pkg/config" 13 "get.porter.sh/porter/pkg/experimental" 14 "get.porter.sh/porter/pkg/manifest" 15 "get.porter.sh/porter/pkg/portercontext" 16 "get.porter.sh/porter/tests" 17 "github.com/cnabio/cnab-go/bundle" 18 "github.com/cnabio/cnab-go/bundle/definition" 19 "github.com/cnabio/cnab-to-oci/relocation" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func runtimeManifestFromStepYaml(t *testing.T, testConfig *config.TestConfig, stepYaml string) *RuntimeManifest { 25 mContent := []byte(stepYaml) 26 require.NoError(t, testConfig.FileSystem.WriteFile("/cnab/app/porter.yaml", mContent, pkg.FileModeWritable)) 27 m, err := manifest.ReadManifest(testConfig.Context, "/cnab/app/porter.yaml", testConfig.Config) 28 require.NoError(t, err, "ReadManifest failed") 29 cfg := NewConfigFor(testConfig.Config) 30 return NewRuntimeManifest(cfg, cnab.ActionInstall, m) 31 } 32 33 func TestResolveMapParam(t *testing.T) { 34 ctx := context.Background() 35 testConfig := config.NewTestConfig(t) 36 testConfig.Setenv("PERSON", "Ralpha") 37 testConfig.Setenv("CONTACT", "{ \"name\": \"Breta\" }") 38 39 mContent := `schemaVersion: 1.0.0-alpha.2 40 parameters: 41 - name: person 42 - name: place 43 applyTo: [install] 44 - name: contact 45 type: object 46 47 install: 48 - mymixin: 49 Parameters: 50 Thing: ${ bundle.parameters.person } 51 ObjectName: ${ bundle.parameters.contact.name } 52 Object: '${ bundle.parameters.contact }' 53 ` 54 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 55 s := rm.Install[0] 56 57 err := rm.ResolveStep(ctx, 0, s) 58 require.NoError(t, err) 59 60 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 61 mixin := s.Data["mymixin"].(map[string]interface{}) 62 require.IsType(t, mixin["Parameters"], map[string]interface{}{}, "Data.mymixin.Parameters has incorrect type") 63 pms := mixin["Parameters"].(map[string]interface{}) 64 require.IsType(t, "string", pms["Thing"], "Data.mymixin.Parameters.Thing has incorrect type") 65 val := pms["Thing"].(string) 66 67 assert.Equal(t, "Ralpha", val) 68 assert.NotContains(t, "place", pms, "parameters that don't apply to the current action should not be resolved") 69 70 // Asserting `bundle.parameters.contact.name` works. 71 require.IsType(t, "string", pms["ObjectName"], "Data.mymixin.Parameters.ObjectName has incorrect type") 72 contactName := pms["ObjectName"].(string) 73 require.IsType(t, "string", contactName, "Data.mymixin.Parameters.ObjectName.name has incorrect type") 74 assert.Equal(t, "Breta", contactName) 75 76 // Asserting `bundle.parameters.contact` evaluates to the JSON string 77 // representation of the object. 78 require.IsType(t, "string", pms["Object"], "Data.mymixin.Parameters.Object has incorrect type") 79 contact := pms["Object"].(string) 80 require.IsType(t, "string", contact, "Data.mymixin.Parameters.Object has incorrect type") 81 assert.Equal(t, "{\"name\":\"Breta\"}", contact) 82 83 err = rm.Initialize(ctx) 84 require.NoError(t, err) 85 } 86 func TestStateBagUnpack(t *testing.T) { 87 ctx := context.Background() 88 testConfig := config.NewTestConfig(t) 89 testConfig.Setenv("PERSON", "Ralpha") 90 91 mContent := `schemaVersion: 1.0.0-alpha.2 92 parameters: 93 - name: person 94 - name: place 95 applyTo: [install] 96 97 install: 98 - mymixin: 99 Parameters: 100 Thing: ${ bundle.parameters.person } 101 state: 102 - name: foo 103 path: foo/state.json 104 ` 105 tests := []struct { 106 name string 107 stateContent string 108 expErr error 109 }{ 110 { 111 name: "/porter/state.tgz is empty file", 112 stateContent: "", 113 expErr: nil, 114 }, 115 { 116 name: "/porter/state.tgz has null string", 117 stateContent: "null", 118 expErr: nil, 119 }, 120 { 121 name: "/porter/state.tgz has newline", 122 stateContent: "\n", 123 expErr: io.ErrUnexpectedEOF, 124 }, 125 } 126 for _, test := range tests { 127 t.Run(test.name, func(t *testing.T) { 128 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 129 require.NoError(t, testConfig.FileSystem.WriteFile("/porter/state.tgz", []byte(test.stateContent), pkg.FileModeWritable)) 130 s := rm.Install[0] 131 132 err := rm.ResolveStep(ctx, 0, s) 133 require.NoError(t, err) 134 135 err = rm.Initialize(ctx) 136 if test.expErr == nil { 137 require.NoError(t, err) 138 } else { 139 require.Contains(t, err.Error(), test.expErr.Error()) 140 } 141 if test.stateContent != "null" { 142 err = testConfig.FileSystem.Remove("/porter/state.tgz") 143 require.NoError(t, err) 144 } 145 }) 146 } 147 } 148 149 func TestResolvePathParam(t *testing.T) { 150 ctx := context.Background() 151 testConfig := config.NewTestConfig(t) 152 153 mContent := `schemaVersion: 1.0.0-alpha.2 154 parameters: 155 - name: person 156 path: person.txt 157 158 install: 159 - mymixin: 160 Parameters: 161 Thing: ${ bundle.parameters.person } 162 ` 163 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 164 s := rm.Install[0] 165 166 err := rm.ResolveStep(ctx, 0, s) 167 require.NoError(t, err) 168 169 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 170 mixin := s.Data["mymixin"].(map[string]interface{}) 171 require.IsType(t, mixin["Parameters"], map[string]interface{}{}, "Data.mymixin.Parameters has incorrect type") 172 pms := mixin["Parameters"].(map[string]interface{}) 173 require.IsType(t, "string", pms["Thing"], "Data.mymixin.Parameters.Thing has incorrect type") 174 val := pms["Thing"].(string) 175 176 assert.Equal(t, "person.txt", val) 177 } 178 179 func TestMetadataAvailableForTemplating(t *testing.T) { 180 ctx := context.Background() 181 c := config.NewTestConfig(t) 182 183 c.TestContext.AddTestFile("testdata/metadata-substitution.yaml", config.Name) 184 m, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 185 require.NoError(t, err, "LoadManifestFrom") 186 cfg := NewConfigFor(c.Config) 187 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 188 189 s := rm.Install[0] 190 err = rm.ResolveStep(ctx, 0, s) 191 require.NoError(t, err) 192 193 pms, ok := s.Data["exec"].(map[string]interface{}) 194 require.True(t, ok) 195 cmd := pms["command"].(string) 196 assert.Equal(t, "echo \"name:porter-hello version:0.1.0 description:An example Porter configuration image:jeremyrickard/porter-hello:porter-39a022ca907e26c3d8fffabd4bb8dbbc\"", cmd) 197 } 198 199 func TestDependencyMetadataAvailableForTemplating(t *testing.T) { 200 ctx := context.Background() 201 c := config.NewTestConfig(t) 202 c.TestContext.AddTestFile("testdata/dep-metadata-substitution.yaml", config.Name) 203 204 m, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 205 require.NoError(t, err, "LoadManifestFrom") 206 cfg := NewConfigFor(c.Config) 207 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 208 rm.bundles = map[string]cnab.ExtendedBundle{ 209 "mysql": cnab.NewBundle(bundle.Bundle{ 210 Name: "Azure MySQL", 211 Description: "Azure MySQL database as a service", 212 Version: "v1.0.0", 213 }), 214 } 215 216 s := rm.Install[0] 217 err = rm.ResolveStep(ctx, 0, s) 218 require.NoError(t, err) 219 220 pms, ok := s.Data["exec"].(map[string]interface{}) 221 require.True(t, ok) 222 cmd := pms["command"].(string) 223 assert.Equal(t, "echo \"dep name: Azure MySQL dep version: v1.0.0 dep description: Azure MySQL database as a service\"", cmd) 224 } 225 226 func TestResolveMapParamUnknown(t *testing.T) { 227 ctx := context.Background() 228 testConfig := config.NewTestConfig(t) 229 230 mContent := `schemaVersion: 1.0.0 231 install: 232 - mymixin: 233 Parameters: 234 Thing: ${bundle.parameters.person} 235 ` 236 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 237 s := rm.Install[0] 238 239 err := rm.ResolveStep(ctx, 0, s) 240 require.Error(t, err) 241 tests.RequireErrorContains(t, err, "missing variable \"person\"") 242 } 243 244 func TestResolveArrayUnknown(t *testing.T) { 245 ctx := context.Background() 246 testConfig := config.NewTestConfig(t) 247 248 mContent := `schemaVersion: 1.0.0 249 parameters: 250 - name: name 251 252 install: 253 - exec: 254 Arguments: 255 - ${bundle.parameters.person} 256 ` 257 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 258 s := rm.Install[0] 259 260 err := rm.ResolveStep(ctx, 0, s) 261 require.Error(t, err) 262 assert.Contains(t, err.Error(), `missing variable "person"`) 263 } 264 265 func TestResolveArray(t *testing.T) { 266 ctx := context.Background() 267 testConfig := config.NewTestConfig(t) 268 testConfig.Setenv("PERSON", "Ralpha") 269 270 mContent := `schemaVersion: 1.0.0 271 parameters: 272 - name: person 273 274 install: 275 - mymixin: 276 Arguments: 277 - ${ bundle.parameters.person } 278 ` 279 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 280 s := rm.Install[0] 281 282 err := rm.ResolveStep(ctx, 0, s) 283 require.NoError(t, err) 284 285 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 286 mixin := s.Data["mymixin"].(map[string]interface{}) 287 require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type") 288 args := mixin["Arguments"].([]interface{}) 289 290 assert.Equal(t, "Ralpha", args[0].(string)) 291 } 292 293 func TestResolveSensitiveParameter(t *testing.T) { 294 ctx := context.Background() 295 testConfig := config.NewTestConfig(t) 296 testConfig.Setenv("SENSITIVE_PARAM", "deliciou$dubonnet") 297 testConfig.Setenv("SENSITIVE_OBJECT", "{ \"secret\": \"this_is_secret\" }") 298 testConfig.Setenv("REGULAR_PARAM", "regular param value") 299 300 mContent := `schemaVersion: 1.0.0 301 parameters: 302 - name: sensitive_param 303 sensitive: true 304 - name: sensitive_object 305 sensitive: true 306 type: object 307 - name: regular_param 308 309 install: 310 - mymixin: 311 Arguments: 312 - ${ bundle.parameters.sensitive_param } 313 - '${ bundle.parameters.sensitive_object }' 314 - ${ bundle.parameters.regular_param } 315 ` 316 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 317 s := rm.Install[0] 318 319 // Prior to resolving step values, this method should return an empty string array 320 assert.Equal(t, rm.GetSensitiveValues(), []string{}) 321 322 err := rm.ResolveStep(ctx, 0, s) 323 require.NoError(t, err) 324 325 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 326 mixin := s.Data["mymixin"].(map[string]interface{}) 327 require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type") 328 args := mixin["Arguments"].([]interface{}) 329 330 require.Len(t, args, 3) 331 assert.Equal(t, "deliciou$dubonnet", args[0]) 332 assert.Equal(t, "{\"secret\":\"this_is_secret\"}", args[1]) 333 assert.Equal(t, "regular param value", args[2]) 334 335 // There should now be one sensitive value tracked under the manifest 336 assert.ElementsMatch(t, []string{"deliciou$dubonnet", "{\"secret\":\"this_is_secret\"}"}, rm.GetSensitiveValues()) 337 } 338 339 func TestResolveCredential(t *testing.T) { 340 ctx := context.Background() 341 testConfig := config.NewTestConfig(t) 342 testConfig.Setenv("PASSWORD", "deliciou$dubonnet") 343 344 mContent := `schemaVersion: 1.0.0 345 credentials: 346 - name: password 347 env: PASSWORD 348 349 install: 350 - mymixin: 351 Arguments: 352 - ${ bundle.credentials.password } 353 ` 354 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 355 s := rm.Install[0] 356 357 // Prior to resolving step values, this method should return an empty string array 358 assert.Equal(t, rm.GetSensitiveValues(), []string{}) 359 360 err := rm.ResolveStep(ctx, 0, s) 361 require.NoError(t, err) 362 363 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 364 mixin := s.Data["mymixin"].(map[string]interface{}) 365 require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type") 366 args := mixin["Arguments"].([]interface{}) 367 368 assert.Equal(t, "deliciou$dubonnet", args[0]) 369 // There should now be a sensitive value tracked under the manifest 370 assert.Equal(t, []string{"deliciou$dubonnet"}, rm.GetSensitiveValues()) 371 } 372 373 func TestResolveStep_DependencyOutput(t *testing.T) { 374 ctx := context.Background() 375 testConfig := config.NewTestConfig(t) 376 testConfig.Setenv("PORTER_MYSQL_PASSWORD_DEP_OUTPUT", "password") 377 testConfig.Setenv("PORTER_MYSQL_ROOT_PASSWORD_DEP_OUTPUT", "mysql-password") 378 379 mContent := `schemaVersion: 1.0.0 380 dependencies: 381 requires: 382 - name: mysql 383 bundle: 384 reference: "getporter/porter-mysql" 385 386 install: 387 - mymixin: 388 Arguments: 389 - ${ bundle.dependencies.mysql.outputs.password } 390 - ${ bundle.dependencies.mysql.outputs.root-password } 391 ` 392 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 393 ps := cnab.ParameterSources{} 394 ps.SetParameterFromDependencyOutput("porter-mysql-password", "mysql", "password") 395 ps.SetParameterFromDependencyOutput("porter-mysql-root-password", "mysql", "root-password") 396 rm.bundle = cnab.NewBundle(bundle.Bundle{ 397 Custom: map[string]interface{}{ 398 cnab.ParameterSourcesExtensionKey: ps, 399 }, 400 RequiredExtensions: []string{cnab.ParameterSourcesExtensionKey}, 401 }) 402 403 rm.bundles = map[string]cnab.ExtendedBundle{ 404 "mysql": cnab.NewBundle(bundle.Bundle{ 405 Outputs: map[string]bundle.Output{ 406 "password": { 407 Definition: "password", 408 }, 409 "root-password": { 410 Definition: "root-password", 411 }, 412 }, 413 Definitions: map[string]*definition.Schema{ 414 "password": {WriteOnly: makeBoolPtr(true)}, 415 "root-password": {WriteOnly: makeBoolPtr(true)}, 416 }, 417 }), 418 } 419 420 s := rm.Install[0] 421 err := rm.ResolveStep(ctx, 0, s) 422 require.NoError(t, err) 423 424 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 425 mixin := s.Data["mymixin"].(map[string]interface{}) 426 require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type") 427 args := mixin["Arguments"].([]interface{}) 428 429 assert.Equal(t, []interface{}{"password", "mysql-password"}, args, "Incorrect template args passed to the mixin step") 430 431 // There should now be a sensitive value tracked under the manifest 432 gotSensitiveValues := rm.GetSensitiveValues() 433 sort.Strings(gotSensitiveValues) 434 assert.Equal(t, []string{"mysql-password", "password"}, gotSensitiveValues, "Incorrect values were marked as sensitive") 435 } 436 437 func TestResolveStep_DependencyMappedOutput(t *testing.T) { 438 ctx := context.Background() 439 testConfig := config.NewTestConfig(t) 440 testConfig.SetExperimentalFlags(experimental.FlagDependenciesV2) 441 442 mContent := `schemaVersion: 1.0.0 443 dependencies: 444 requires: 445 - name: mysql 446 bundle: 447 reference: "getporter/porter-mysql" 448 outputs: 449 mappedOutput: Mapped 450 451 install: 452 - mymixin: 453 Arguments: 454 - ${ bundle.dependencies.mysql.outputs.mappedOutput } 455 ` 456 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 457 rm.bundles = map[string]cnab.ExtendedBundle{ 458 "mysql": cnab.NewBundle(bundle.Bundle{}), 459 } 460 461 s := rm.Install[0] 462 err := rm.ResolveStep(ctx, 0, s) 463 require.NoError(t, err) 464 465 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 466 mixin := s.Data["mymixin"].(map[string]interface{}) 467 require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type") 468 args := mixin["Arguments"].([]interface{}) 469 470 assert.Equal(t, []interface{}{"Mapped"}, args, "Incorrect template args passed to the mixin step") 471 } 472 473 func TestResolveStep_DependencyTemplatedMappedOutput(t *testing.T) { 474 ctx := context.Background() 475 testConfig := config.NewTestConfig(t) 476 testConfig.SetExperimentalFlags(experimental.FlagDependenciesV2) 477 testConfig.Setenv("PORTER_MYSQL_PASSWORD_DEP_OUTPUT", "password") 478 479 mContent := `schemaVersion: 1.0.0 480 dependencies: 481 requires: 482 - name: mysql 483 bundle: 484 reference: "getporter/porter-mysql" 485 outputs: 486 mappedOutput: ${ bundle.dependencies.mysql.outputs.password } 487 488 install: 489 - mymixin: 490 Arguments: 491 - ${ bundle.dependencies.mysql.outputs.mappedOutput } 492 ` 493 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 494 ps := cnab.ParameterSources{} 495 ps.SetParameterFromDependencyOutput("porter-mysql-password", "mysql", "password") 496 rm.bundle = cnab.NewBundle(bundle.Bundle{ 497 Custom: map[string]interface{}{ 498 cnab.ParameterSourcesExtensionKey: ps, 499 }, 500 RequiredExtensions: []string{cnab.ParameterSourcesExtensionKey}, 501 }) 502 503 rm.bundles = map[string]cnab.ExtendedBundle{ 504 "mysql": cnab.NewBundle(bundle.Bundle{ 505 Outputs: map[string]bundle.Output{ 506 "password": { 507 Definition: "password", 508 }, 509 }, 510 Definitions: map[string]*definition.Schema{ 511 "password": {WriteOnly: makeBoolPtr(true)}, 512 }, 513 }), 514 } 515 516 s := rm.Install[0] 517 err := rm.ResolveStep(ctx, 0, s) 518 require.NoError(t, err) 519 520 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 521 mixin := s.Data["mymixin"].(map[string]interface{}) 522 require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type") 523 args := mixin["Arguments"].([]interface{}) 524 525 assert.Equal(t, []interface{}{"password"}, args, "Incorrect template args passed to the mixin step") 526 } 527 528 func TestResolveStep_DependencyTemplatedMappedOutput_OutputVariable(t *testing.T) { 529 ctx := context.Background() 530 testConfig := config.NewTestConfig(t) 531 testConfig.SetExperimentalFlags(experimental.FlagDependenciesV2) 532 testConfig.Setenv("PORTER_MYSQL_PASSWORD_DEP_OUTPUT", "password") 533 534 mContent := `schemaVersion: 1.0.0 535 dependencies: 536 requires: 537 - name: mysql 538 bundle: 539 reference: "getporter/porter-mysql" 540 outputs: 541 mappedOutput: combined-${ outputs.password } 542 543 install: 544 - mymixin: 545 Arguments: 546 - ${ bundle.dependencies.mysql.outputs.mappedOutput } 547 ` 548 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 549 ps := cnab.ParameterSources{} 550 ps.SetParameterFromDependencyOutput("porter-mysql-password", "mysql", "password") 551 rm.bundle = cnab.NewBundle(bundle.Bundle{ 552 Custom: map[string]interface{}{ 553 cnab.ParameterSourcesExtensionKey: ps, 554 }, 555 RequiredExtensions: []string{cnab.ParameterSourcesExtensionKey}, 556 }) 557 558 rm.bundles = map[string]cnab.ExtendedBundle{ 559 "mysql": cnab.NewBundle(bundle.Bundle{ 560 Outputs: map[string]bundle.Output{ 561 "password": { 562 Definition: "password", 563 }, 564 }, 565 Definitions: map[string]*definition.Schema{ 566 "password": {WriteOnly: makeBoolPtr(true)}, 567 }, 568 }), 569 } 570 571 s := rm.Install[0] 572 err := rm.ResolveStep(ctx, 0, s) 573 require.NoError(t, err) 574 575 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type") 576 mixin := s.Data["mymixin"].(map[string]interface{}) 577 require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type") 578 args := mixin["Arguments"].([]interface{}) 579 580 assert.Equal(t, []interface{}{"combined-password"}, args, "Incorrect template args passed to the mixin step") 581 } 582 583 func TestResolveInMainDict(t *testing.T) { 584 ctx := context.Background() 585 c := config.NewTestConfig(t) 586 587 c.TestContext.AddTestFile("testdata/param-test-in-block.yaml", config.Name) 588 589 m, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 590 require.NoError(t, err, "could not load manifest") 591 592 cfg := NewConfigFor(c.Config) 593 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 594 595 installStep := rm.Install[0] 596 597 rm.config.Setenv("COMMAND", "echo hello world") 598 err = rm.ResolveStep(ctx, 0, installStep) 599 require.NoError(t, err) 600 601 require.IsType(t, map[string]interface{}{}, installStep.Data["exec"], "Data.exec has the wrong type") 602 exec := installStep.Data["exec"].(map[string]interface{}) 603 command := exec["command"] 604 require.IsType(t, "string", command, "Data.exec.command has the wrong type") 605 cmdVal := command.(string) 606 607 assert.Equal(t, "echo hello world", cmdVal) 608 } 609 610 func TestResolveSliceWithAMap(t *testing.T) { 611 ctx := context.Background() 612 c := config.NewTestConfig(t) 613 614 c.TestContext.AddTestFile("testdata/slice-test.yaml", config.Name) 615 616 m, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 617 require.NoError(t, err, "could not load manifest") 618 619 cfg := NewConfigFor(c.Config) 620 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 621 622 installStep := rm.Install[0] 623 624 rm.config.Setenv("COMMAND", "echo hello world") 625 err = rm.ResolveStep(ctx, 0, installStep) 626 require.NoError(t, err) 627 628 require.NotNil(t, installStep.Data) 629 exec := installStep.Data["exec"].(map[string]interface{}) 630 require.NotNil(t, exec) 631 flags := exec["flags"].(map[string]interface{}) 632 require.Len(t, flags, 1) 633 assert.Equal(t, "echo hello world", flags["c"].(string)) 634 } 635 636 func TestResolveMissingStepOutputs(t *testing.T) { 637 ctx := context.Background() 638 testConfig := config.NewTestConfig(t) 639 640 mContent := `schemaVersion: 1.0.0 641 install: 642 - mymixin: 643 Arguments: 644 - jdbc://${bundle.outputs.database_url}:${bundle.outputs.database_port} 645 ` 646 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 647 s := rm.Install[0] 648 649 err := rm.ResolveStep(ctx, 0, s) 650 tests.RequireErrorContains(t, err, `missing variable "database_url"`) 651 } 652 653 func TestResolveSensitiveOutputs(t *testing.T) { 654 ctx := context.Background() 655 testConfig := config.NewTestConfig(t) 656 mContent := `schemaVersion: 1.0.0 657 outputs: 658 - name: username 659 - name: password 660 sensitive: true 661 662 install: 663 - mymixin: 664 Arguments: 665 - ${ bundle.outputs.username } 666 - ${ bundle.outputs.password } 667 ` 668 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 669 rm.outputs = map[string]string{ 670 "username": "sally", 671 "password": "top$ecret!", 672 } 673 s := rm.Install[0] 674 675 err := rm.ResolveStep(ctx, 0, s) 676 require.NoError(t, err) 677 678 require.IsType(t, s.Data["mymixin"], map[string]interface{}{}, "Data.mymixin has the wrong type") 679 mixin := s.Data["mymixin"].(map[string]interface{}) 680 require.IsType(t, []interface{}{}, mixin["Arguments"], "Data.mymixin.Arguments has the wrong type") 681 args := mixin["Arguments"].([]interface{}) 682 683 require.Len(t, args, 2) 684 require.Equal(t, "sally", args[0]) 685 require.Equal(t, "top$ecret!", args[1]) 686 687 // There should be only one sensitive value being tracked 688 require.Equal(t, []string{"top$ecret!"}, rm.GetSensitiveValues()) 689 } 690 691 func TestManifest_ResolveBundleName(t *testing.T) { 692 ctx := context.Background() 693 testConfig := config.NewTestConfig(t) 694 mContent := `schemaVersion: 1.0.0 695 name: mybuns 696 697 install: 698 - mymixin: 699 Arguments: 700 - ${ bundle.name } 701 ` 702 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 703 s := rm.Install[0] 704 705 err := rm.ResolveStep(ctx, 0, s) 706 require.NoError(t, err) 707 708 require.IsType(t, s.Data["mymixin"], map[string]interface{}{}, "Data.mymixin has the wrong type") 709 mixin := s.Data["mymixin"].(map[string]interface{}) 710 require.IsType(t, []interface{}{}, mixin["Arguments"], "Data.mymixin.Arguments has the wrong type") 711 args := mixin["Arguments"].([]interface{}) 712 713 assert.Equal(t, "mybuns", args[0].(string)) 714 } 715 716 func TestReadManifest_Validate_BundleOutput(t *testing.T) { 717 c := config.NewTestConfig(t) 718 719 c.TestContext.AddTestFile("testdata/outputs/bundle-outputs.yaml", config.Name) 720 721 wantOutputs := manifest.OutputDefinitions{ 722 "mysql-root-password": { 723 Name: "mysql-root-password", 724 Schema: definition.Schema{ 725 Description: "The root MySQL password", 726 Type: "string", 727 }, 728 }, 729 "mysql-password": { 730 Name: "mysql-password", 731 Schema: definition.Schema{ 732 Type: "string", 733 }, 734 ApplyTo: []string{ 735 "install", 736 "upgrade", 737 }, 738 }, 739 } 740 741 m, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 742 require.NoError(t, err, "could not load manifest") 743 744 require.Equal(t, wantOutputs, m.Outputs) 745 } 746 747 func TestReadManifest_Validate_BundleOutput_Error(t *testing.T) { 748 c := config.NewTestConfig(t) 749 750 c.TestContext.AddTestFile("testdata/outputs/bundle-outputs-error.yaml", config.Name) 751 752 _, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 753 require.Error(t, err) 754 } 755 756 func TestDependencyV1_Validate(t *testing.T) { 757 testcases := []struct { 758 name string 759 dep manifest.Dependency 760 wantOutput string 761 wantError string 762 }{ 763 { 764 name: "version in reference", 765 dep: manifest.Dependency{Name: "mysql", Bundle: manifest.BundleCriteria{Reference: "deislabs/azure-mysql:5.7"}}, 766 wantOutput: "", 767 wantError: "", 768 }, { 769 name: "version ranges", 770 dep: manifest.Dependency{Name: "mysql", Bundle: manifest.BundleCriteria{Reference: "deislabs/azure-mysql", Version: "5.7.x-6"}}, 771 wantOutput: "", 772 wantError: "", 773 }, { 774 name: "missing reference", 775 dep: manifest.Dependency{Name: "mysql", Bundle: manifest.BundleCriteria{Reference: ""}}, 776 wantOutput: "", 777 wantError: `reference is required for dependency "mysql"`, 778 }, { 779 name: "version not specified", 780 dep: manifest.Dependency{Name: "mysql", Bundle: manifest.BundleCriteria{Reference: "deislabs/azure-mysql", Version: ""}}, 781 wantOutput: "", 782 wantError: `reference for dependency "mysql" can specify only a repository, without a digest or tag, when a version constraint is specified`, 783 }, { // When a range is specified, but also a default version, we use the default version when we can't find a matching version from the range 784 name: "default version and range specified", 785 dep: manifest.Dependency{Name: "mysql", Bundle: manifest.BundleCriteria{Reference: "deislabs/azure-mysql:5.7", Version: "5.7.x-6"}}, 786 wantOutput: "", 787 wantError: "", 788 }, 789 } 790 791 for _, tc := range testcases { 792 t.Run(tc.name, func(t *testing.T) { 793 pCtx := portercontext.NewTestContext(t) 794 795 err := tc.dep.Validate(pCtx.Context) 796 797 if tc.wantError == "" { 798 require.NoError(t, err) 799 } else { 800 tests.RequireErrorContains(t, err, tc.wantError) 801 } 802 803 gotOutput := pCtx.GetOutput() 804 if gotOutput != "" { 805 require.Equal(t, tc.wantOutput, gotOutput) 806 } 807 }) 808 } 809 } 810 811 func TestManifest_ApplyStepOutputs(t *testing.T) { 812 c := config.NewTestConfig(t) 813 814 c.TestContext.AddTestFileFromRoot("pkg/manifest/testdata/porter-with-templating.yaml", config.Name) 815 816 m, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 817 require.NoError(t, err, "could not load manifest") 818 819 cfg := NewConfigFor(c.Config) 820 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 821 822 err = rm.ApplyStepOutputs(map[string]string{"name": "world"}) 823 require.NoError(t, err) 824 825 assert.Contains(t, rm.outputs, "name") 826 assert.Equal(t, "world", rm.outputs["name"]) 827 } 828 829 func makeBoolPtr(value bool) *bool { 830 return &value 831 } 832 833 func TestManifest_ResolveImageMap(t *testing.T) { 834 ctx := context.Background() 835 c := config.NewTestConfig(t) 836 c.TestContext.AddTestFile("testdata/porter-images.yaml", config.Name) 837 838 m, err := manifest.LoadManifestFrom(context.Background(), c.Config, config.Name) 839 require.NoError(t, err, "could not load manifest") 840 841 cfg := NewConfigFor(c.Config) 842 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 843 expectedImage, ok := m.ImageMap["something"] 844 require.True(t, ok, "couldn't get expected image") 845 expectedRef := fmt.Sprintf("%s@%s", expectedImage.Repository, expectedImage.Digest) 846 step := rm.Install[0] 847 err = rm.ResolveStep(ctx, 0, step) 848 assert.NoError(t, err, "Should have successfully resolved step") 849 s := step.Data["searcher"].(map[string]interface{}) 850 assert.NotNil(t, s) 851 img, ok := s["image"] 852 assert.True(t, ok, "should have found image") 853 val := fmt.Sprintf("%v", img) 854 assert.Equal(t, expectedRef, val) 855 856 repo, ok := s["repo"] 857 assert.True(t, ok, "should have found repo") 858 val = fmt.Sprintf("%v", repo) 859 assert.Equal(t, expectedImage.Repository, val) 860 861 digest, ok := s["digest"] 862 assert.True(t, ok, "should have found content digest") 863 val = fmt.Sprintf("%v", digest) 864 assert.Equal(t, expectedImage.Digest, val) 865 866 tag, ok := s["tag"] 867 assert.True(t, ok, "should have found tag") 868 val = fmt.Sprintf("%v", tag) 869 assert.Equal(t, expectedImage.Tag, val) 870 } 871 872 func TestManifest_ResolveImageMapMissingKey(t *testing.T) { 873 // Try to access an images entry that doesn't exist 874 ctx := context.Background() 875 testConfig := config.NewTestConfig(t) 876 mContent := `schemaVersion: 1.0.0-alpha.2 877 images: 878 something: 879 repository: "blah/blah" 880 digest: "sha1234:cafebab" 881 882 install: 883 - mymixin: 884 Arguments: 885 - ${ bundle.images.notsomething.digest } 886 ` 887 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 888 s := rm.Install[0] 889 890 err := rm.ResolveStep(ctx, 0, s) 891 tests.RequireErrorContains(t, err, `missing variable "notsomething"`) 892 } 893 894 func TestResolveImage(t *testing.T) { 895 tests := []struct { 896 name string 897 reference string 898 want manifest.MappedImage 899 }{ 900 { 901 name: "canonical reference", 902 reference: "getporter/porter-hello@sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63", 903 want: manifest.MappedImage{ 904 Repository: "getporter/porter-hello", 905 Digest: "sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63", 906 }, 907 }, 908 { 909 name: "tagged reference", 910 reference: "ghcr.io/getporter/examples/porter-hello:v0.2.0", 911 want: manifest.MappedImage{ 912 Repository: "ghcr.io/getporter/examples/porter-hello", 913 Tag: "v0.2.0", 914 }, 915 }, 916 { 917 name: "named reference", 918 reference: "getporter/porter-hello", 919 want: manifest.MappedImage{ 920 Repository: "getporter/porter-hello", 921 Tag: "latest", 922 }, 923 }, 924 { 925 name: "the one with a hostname", 926 reference: "deislabs.io/getporter/porter-hello", 927 want: manifest.MappedImage{ 928 Repository: "deislabs.io/getporter/porter-hello", 929 Tag: "latest", 930 }, 931 }, 932 { 933 name: "the one with a hostname and port", 934 reference: "deislabs.io:9090/getporter/porter-hello:foo", 935 want: manifest.MappedImage{ 936 Repository: "deislabs.io:9090/getporter/porter-hello", 937 Tag: "foo", 938 }, 939 }, 940 { 941 942 name: "tagged and digested", 943 reference: "ghcr.io/getporter/examples/porter-hello:v0.2.0@sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63", 944 want: manifest.MappedImage{ 945 Repository: "ghcr.io/getporter/examples/porter-hello", 946 Tag: "v0.2.0", 947 Digest: "sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63", 948 }, 949 }, 950 } 951 for _, test := range tests { 952 t.Run(test.name, func(t *testing.T) { 953 got := &manifest.MappedImage{} 954 err := resolveImage(got, test.reference) 955 require.NoError(t, err) 956 assert.Equal(t, test.want.Repository, got.Repository) 957 assert.Equal(t, test.want.Tag, got.Tag) 958 assert.Equal(t, test.want.Digest, got.Digest) 959 }) 960 } 961 } 962 963 func TestResolveImageErrors(t *testing.T) { 964 tests := []struct { 965 name string 966 reference string 967 want string 968 }{ 969 { 970 name: "no algo digest", 971 reference: "getporter/porter-hello@8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63", 972 want: "invalid reference format", 973 }, 974 { 975 name: "bad digest", 976 reference: "getporter/porter-hello@sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f", 977 want: "invalid checksum digest length", 978 }, 979 { 980 name: "bad digest algo", 981 reference: "getporter/porter-hello@sha356:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63", 982 want: "unsupported digest algorithm", 983 }, 984 { 985 name: "malformed tagged ref", 986 reference: "getporter/porter-hello@latest", 987 want: "invalid reference format", 988 }, 989 { 990 name: "too many ports tagged ref", 991 reference: "deislabs:8080:8080/porter-hello:latest", 992 want: "invalid reference format", 993 }, 994 } 995 for _, test := range tests { 996 t.Run(test.name, func(t *testing.T) { 997 got := &manifest.MappedImage{} 998 err := resolveImage(got, test.reference) 999 require.Error(t, err) 1000 assert.Contains(t, err.Error(), test.want) 1001 }) 1002 } 1003 } 1004 1005 func TestResolveImageWithUpdatedBundle(t *testing.T) { 1006 m := &manifest.Manifest{ 1007 ImageMap: map[string]manifest.MappedImage{ 1008 "machine": manifest.MappedImage{ 1009 Repository: "deislabs/ghost", 1010 Tag: "latest", 1011 Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041", 1012 }, 1013 }, 1014 } 1015 1016 img := bundle.Image{} 1017 img.Image = "blah/ghost:latest" 1018 img.Digest = "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041" 1019 bun := cnab.NewBundle(bundle.Bundle{ 1020 Images: map[string]bundle.Image{ 1021 "machine": img, 1022 }, 1023 }) 1024 1025 reloMap := relocation.ImageRelocationMap{} 1026 1027 cfg := NewConfigFor(config.NewTestConfig(t).Config) 1028 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 1029 err := rm.ResolveImages(bun, reloMap) 1030 require.NoError(t, err) 1031 mi := rm.ImageMap["machine"] 1032 assert.Equal(t, "blah/ghost", mi.Repository) 1033 } 1034 1035 func TestResolveImageWithUpdatedMismatchedBundle(t *testing.T) { 1036 m := &manifest.Manifest{ 1037 ImageMap: map[string]manifest.MappedImage{ 1038 "machine": manifest.MappedImage{ 1039 Repository: "deislabs/ghost", 1040 Tag: "latest", 1041 Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041", 1042 }, 1043 }, 1044 } 1045 1046 img := bundle.Image{} 1047 img.Image = "blah/ghost:latest" 1048 img.Digest = "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041" 1049 bun := cnab.NewBundle(bundle.Bundle{ 1050 Images: map[string]bundle.Image{ 1051 "ghost": img, 1052 }, 1053 }) 1054 1055 reloMap := relocation.ImageRelocationMap{} 1056 1057 cfg := NewConfigFor(config.NewTestConfig(t).Config) 1058 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 1059 err := rm.ResolveImages(bun, reloMap) 1060 assert.Error(t, err) 1061 assert.EqualError(t, err, fmt.Sprintf("unable to find image in porter manifest: %s", "ghost")) 1062 1063 } 1064 1065 func TestResolveImageWithRelo(t *testing.T) { 1066 m := &manifest.Manifest{ 1067 ImageMap: map[string]manifest.MappedImage{ 1068 "machine": manifest.MappedImage{ 1069 Repository: "gabrtv/microservice", 1070 Tag: "latest", 1071 Digest: "sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687", 1072 }, 1073 }, 1074 } 1075 1076 img := bundle.Image{} 1077 img.Image = "gabrtv/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687" 1078 img.Digest = "sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687" 1079 bun := cnab.NewBundle(bundle.Bundle{ 1080 Images: map[string]bundle.Image{ 1081 "machine": img, 1082 }, 1083 }) 1084 1085 reloMap := relocation.ImageRelocationMap{ 1086 "gabrtv/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687": "my.registry/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687", 1087 } 1088 1089 cfg := NewConfigFor(config.NewTestConfig(t).Config) 1090 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 1091 err := rm.ResolveImages(bun, reloMap) 1092 require.NoError(t, err) 1093 mi := rm.ImageMap["machine"] 1094 assert.Equal(t, "my.registry/microservice", mi.Repository) 1095 } 1096 1097 func TestResolveImageRelocationNoMatch(t *testing.T) { 1098 m := &manifest.Manifest{ 1099 ImageMap: map[string]manifest.MappedImage{ 1100 "machine": manifest.MappedImage{ 1101 Repository: "deislabs/ghost", 1102 Tag: "latest", 1103 Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041", 1104 }, 1105 }, 1106 } 1107 1108 img := bundle.Image{} 1109 img.Image = "deislabs/ghost:latest" 1110 img.Digest = "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041" 1111 bun := cnab.NewBundle(bundle.Bundle{ 1112 Images: map[string]bundle.Image{ 1113 "machine": img, 1114 }, 1115 }) 1116 1117 reloMap := relocation.ImageRelocationMap{ 1118 "deislabs/nogood:latest": "cnabio/ghost:latest", 1119 } 1120 1121 cfg := NewConfigFor(config.NewTestConfig(t).Config) 1122 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 1123 err := rm.ResolveImages(bun, reloMap) 1124 require.NoError(t, err) 1125 assert.Equal(t, "deislabs/ghost", rm.ImageMap["machine"].Repository) 1126 } 1127 1128 func TestResolveStepEncoding(t *testing.T) { 1129 ctx := context.Background() 1130 testConfig := config.NewTestConfig(t) 1131 1132 wantValue := `{"test":"value"}` 1133 testConfig.Setenv("TEST", wantValue) 1134 1135 mContent := `schemaVersion: 1.0.0 1136 parameters: 1137 - name: test 1138 env: TEST 1139 1140 install: 1141 - mymixin: 1142 Flags: 1143 c: '${bundle.parameters.test}' 1144 ` 1145 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 1146 s := rm.Install[0] 1147 1148 err := rm.ResolveStep(ctx, 0, s) 1149 require.NoError(t, err) 1150 1151 require.IsType(t, s.Data["mymixin"], map[string]interface{}{}, "Data.mymixin has the wrong type") 1152 mixin := s.Data["mymixin"].(map[string]interface{}) 1153 require.IsType(t, map[string]interface{}{}, mixin["Flags"], "Data.mymixin.Flags has the wrong type") 1154 flags := mixin["Flags"].(map[string]interface{}) 1155 1156 assert.Equal(t, flags["c"], wantValue) 1157 } 1158 1159 func TestResolveInstallation(t *testing.T) { 1160 ctx := context.Background() 1161 testConfig := config.NewTestConfig(t) 1162 testConfig.Setenv(config.EnvPorterInstallationNamespace, "mynamespace") 1163 testConfig.Setenv(config.EnvPorterInstallationName, "mybun") 1164 1165 mContent := `schemaVersion: 1.0.0 1166 install: 1167 - mymixin: 1168 ns: ${ installation.namespace } 1169 release: ${ installation.name } 1170 ` 1171 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 1172 s := rm.Install[0] 1173 1174 err := rm.ResolveStep(ctx, 0, s) 1175 require.NoError(t, err) 1176 1177 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has the wrong type") 1178 mixin := s.Data["mymixin"].(map[string]interface{}) 1179 1180 assert.Equal(t, "mynamespace", mixin["ns"], "installation.namespace was not rendered") 1181 assert.Equal(t, "mybun", mixin["release"], "installation.name was not rendered") 1182 } 1183 1184 func TestResolveCustomMetadata(t *testing.T) { 1185 ctx := context.Background() 1186 testConfig := config.NewTestConfig(t) 1187 1188 mContent := `schemaVersion: 1.0.0 1189 custom: 1190 foo: foobar 1191 myApp: 1192 featureFlags: 1193 featureA: true 1194 1195 install: 1196 - mymixin: 1197 release: ${ bundle.custom.foo } 1198 featureA: ${ bundle.custom.myApp.featureFlags.featureA } 1199 notabool: "${ bundle.custom.myApp.featureFlags.featureA }" 1200 ` 1201 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 1202 s := rm.Install[0] 1203 1204 err := rm.ResolveStep(ctx, 0, s) 1205 require.NoError(t, err) 1206 1207 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has the wrong type") 1208 mixin := s.Data["mymixin"].(map[string]interface{}) 1209 1210 err = rm.ResolveStep(ctx, 0, s) 1211 require.NoError(t, err, "ResolveStep failed") 1212 1213 assert.Equal(t, "foobar", mixin["release"], "custom metadata was not rendered") 1214 assert.Equal(t, true, mixin["featureA"], "nested custom metadata was not rendered, an unquoted boolean should render as a bool") 1215 assert.Equal(t, "true", mixin["notabool"], "a quoted boolean should render as a string") 1216 } 1217 1218 func TestResolveEnvironmentVariable(t *testing.T) { 1219 ctx := context.Background() 1220 testConfig := config.NewTestConfig(t) 1221 testConfig.Setenv("foo", "foo-value") 1222 testConfig.Setenv("BAR", "bar-value") 1223 1224 mContent := `schemaVersion: 1.0.0 1225 install: 1226 - mymixin: 1227 someInput: ${ env.foo } 1228 moreInput: ${ env.BAR } 1229 ` 1230 rm := runtimeManifestFromStepYaml(t, testConfig, mContent) 1231 s := rm.Install[0] 1232 1233 err := rm.ResolveStep(ctx, 0, s) 1234 require.NoError(t, err) 1235 1236 require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has the wrong type") 1237 mixin := s.Data["mymixin"].(map[string]interface{}) 1238 1239 assert.Equal(t, "foo-value", mixin["someInput"], "expected lower-case foo env var was resolved") 1240 assert.Equal(t, "bar-value", mixin["moreInput"], "expected upper-case BAR env var was resolved") 1241 } 1242 1243 func TestResolveInvocationImage(t *testing.T) { 1244 testcases := []struct { 1245 name string 1246 bundleInvocationImg bundle.BaseImage 1247 relocationMap relocation.ImageRelocationMap 1248 expectedImg string 1249 wantErr string 1250 }{ 1251 {name: "success with no relocation map", 1252 bundleInvocationImg: bundle.BaseImage{Image: "blah/ghost:latest", ImageType: "docker", Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041"}, 1253 expectedImg: "blah/ghost:latest@sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041", 1254 }, 1255 {name: "success with relocation map", 1256 bundleInvocationImg: bundle.BaseImage{Image: "blah/ghost:latest", ImageType: "docker", Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041"}, 1257 relocationMap: relocation.ImageRelocationMap{"blah/ghost:latest@sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041": "relocated-ghost@sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041"}, 1258 expectedImg: "relocated-ghost@sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041", 1259 }, 1260 {name: "success with no update", 1261 expectedImg: "test/image:latest", 1262 }, 1263 {name: "failure with invalid digest", 1264 bundleInvocationImg: bundle.BaseImage{Image: "blah/ghost:latest", ImageType: "docker", Digest: "123"}, 1265 wantErr: "unable to get bundle image reference with digest", 1266 }, 1267 } 1268 1269 cfg := NewConfigFor(config.NewTestConfig(t).Config) 1270 1271 for _, tc := range testcases { 1272 t.Run(tc.name, func(t *testing.T) { 1273 1274 bun := cnab.NewBundle(bundle.Bundle{ 1275 InvocationImages: []bundle.InvocationImage{ 1276 {BaseImage: tc.bundleInvocationImg}, 1277 }, 1278 }) 1279 m := &manifest.Manifest{ 1280 Image: "test/image:latest", 1281 } 1282 rm := NewRuntimeManifest(cfg, cnab.ActionInstall, m) 1283 1284 err := rm.ResolveInvocationImage(bun, tc.relocationMap) 1285 if tc.wantErr != "" { 1286 require.ErrorContains(t, err, tc.wantErr) 1287 return 1288 } 1289 require.NoError(t, err) 1290 require.Equal(t, tc.expectedImg, m.Image) 1291 }) 1292 } 1293 1294 }