github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/source_eval_test.go (about) 1 // Copyright 2016-2018, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package deploy 16 17 import ( 18 "context" 19 "encoding/json" 20 "strings" 21 "sync" 22 "sync/atomic" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 27 "github.com/pulumi/pulumi/pkg/v3/resource/deploy/deploytest" 28 "github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers" 29 "github.com/pulumi/pulumi/sdk/v3/go/common/resource" 30 "github.com/pulumi/pulumi/sdk/v3/go/common/resource/config" 31 "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" 32 "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" 33 "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" 34 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 35 "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" 36 ) 37 38 type testRegEvent struct { 39 goal *resource.Goal 40 result *RegisterResult 41 } 42 43 var _ RegisterResourceEvent = (*testRegEvent)(nil) 44 45 func (g *testRegEvent) event() {} 46 47 func (g *testRegEvent) Goal() *resource.Goal { 48 return g.goal 49 } 50 51 func (g *testRegEvent) Done(result *RegisterResult) { 52 contract.Assertf(g.result == nil, "Attempt to invoke testRegEvent.Done more than once") 53 g.result = result 54 } 55 56 func fixedProgram(steps []RegisterResourceEvent) deploytest.ProgramFunc { 57 return func(_ plugin.RunInfo, resmon *deploytest.ResourceMonitor) error { 58 for _, s := range steps { 59 g := s.Goal() 60 urn, id, outs, err := resmon.RegisterResource(g.Type, string(g.Name), g.Custom, deploytest.ResourceOptions{ 61 Parent: g.Parent, 62 Protect: g.Protect, 63 Dependencies: g.Dependencies, 64 Provider: g.Provider, 65 Inputs: g.Properties, 66 PropertyDeps: g.PropertyDependencies, 67 }) 68 if err != nil { 69 return err 70 } 71 s.Done(&RegisterResult{ 72 State: resource.NewState(g.Type, urn, g.Custom, false, id, g.Properties, outs, g.Parent, g.Protect, 73 false, g.Dependencies, nil, g.Provider, g.PropertyDependencies, false, nil, nil, nil, 74 "", false, ""), 75 }) 76 } 77 return nil 78 } 79 } 80 81 func newTestPluginContext(program deploytest.ProgramFunc) (*plugin.Context, error) { 82 sink := cmdutil.Diag() 83 statusSink := cmdutil.Diag() 84 lang := deploytest.NewLanguageRuntime(program) 85 host := deploytest.NewPluginHost(sink, statusSink, lang) 86 return plugin.NewContext(sink, statusSink, host, nil, "", nil, false, nil) 87 } 88 89 type testProviderSource struct { 90 providers map[providers.Reference]plugin.Provider 91 m sync.RWMutex 92 // If nil, do not return a default provider. Otherwise, return this default provider 93 defaultProvider plugin.Provider 94 } 95 96 func (s *testProviderSource) registerProvider(ref providers.Reference, provider plugin.Provider) { 97 s.m.Lock() 98 defer s.m.Unlock() 99 100 s.providers[ref] = provider 101 } 102 103 func (s *testProviderSource) GetProvider(ref providers.Reference) (plugin.Provider, bool) { 104 s.m.RLock() 105 defer s.m.RUnlock() 106 107 provider, ok := s.providers[ref] 108 if !ok && s.defaultProvider != nil && providers.IsDefaultProvider(ref.URN()) { 109 return s.defaultProvider, true 110 } 111 return provider, ok 112 } 113 114 func newProviderEvent(pkg, name string, inputs resource.PropertyMap, parent resource.URN) RegisterResourceEvent { 115 if inputs == nil { 116 inputs = resource.PropertyMap{} 117 } 118 goal := &resource.Goal{ 119 Type: providers.MakeProviderType(tokens.Package(pkg)), 120 Name: tokens.QName(name), 121 Custom: true, 122 Properties: inputs, 123 Parent: parent, 124 } 125 return &testRegEvent{goal: goal} 126 } 127 128 func disableDefaultProviders(runInfo *EvalRunInfo, pkgs ...string) { 129 if runInfo.Target.Config == nil { 130 runInfo.Target.Config = config.Map{} 131 } 132 c := runInfo.Target.Config 133 key := config.MustMakeKey("pulumi", "disable-default-providers") 134 if _, ok, err := c.Get(key, false); err != nil { 135 panic(err) 136 } else if ok { 137 panic("disableDefaultProviders cannot be called twice") 138 } 139 b, err := json.Marshal(pkgs) 140 if err != nil { 141 panic(err) 142 } 143 err = c.Set(key, config.NewValue(string(b)), false) 144 if err != nil { 145 panic(err) 146 } 147 } 148 149 func TestRegisterNoDefaultProviders(t *testing.T) { 150 t.Parallel() 151 152 runInfo := &EvalRunInfo{ 153 Proj: &workspace.Project{Name: "test"}, 154 Target: &Target{Name: "test"}, 155 } 156 157 newURN := func(t tokens.Type, name string, parent resource.URN) resource.URN { 158 var pt tokens.Type 159 if parent != "" { 160 pt = parent.Type() 161 } 162 return resource.NewURN(runInfo.Target.Name.Q(), runInfo.Proj.Name, pt, t, tokens.QName(name)) 163 } 164 165 newProviderURN := func(pkg tokens.Package, name string, parent resource.URN) resource.URN { 166 return newURN(providers.MakeProviderType(pkg), name, parent) 167 } 168 169 componentURN := newURN("component", "component", "") 170 171 providerARef, err := providers.NewReference(newProviderURN("pkgA", "providerA", ""), "id1") 172 assert.NoError(t, err) 173 providerBRef, err := providers.NewReference(newProviderURN("pkgA", "providerB", componentURN), "id2") 174 assert.NoError(t, err) 175 providerCRef, err := providers.NewReference(newProviderURN("pkgC", "providerC", ""), "id1") 176 assert.NoError(t, err) 177 178 steps := []RegisterResourceEvent{ 179 // Register a provider. 180 newProviderEvent("pkgA", "providerA", nil, ""), 181 // Register a component resource. 182 &testRegEvent{ 183 goal: resource.NewGoal(componentURN.Type(), componentURN.Name(), false, resource.PropertyMap{}, "", false, 184 nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 185 }, 186 // Register a couple resources using provider A. 187 &testRegEvent{ 188 goal: resource.NewGoal("pkgA:index:typA", "res1", true, resource.PropertyMap{}, componentURN, false, nil, 189 providerARef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 190 }, 191 &testRegEvent{ 192 goal: resource.NewGoal("pkgA:index:typA", "res2", true, resource.PropertyMap{}, componentURN, false, nil, 193 providerARef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 194 }, 195 // Register two more providers. 196 newProviderEvent("pkgA", "providerB", nil, ""), 197 newProviderEvent("pkgC", "providerC", nil, componentURN), 198 // Register a few resources that use the new providers. 199 &testRegEvent{ 200 goal: resource.NewGoal("pkgB:index:typB", "res3", true, resource.PropertyMap{}, "", false, nil, 201 providerBRef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 202 }, 203 &testRegEvent{ 204 goal: resource.NewGoal("pkgB:index:typC", "res4", true, resource.PropertyMap{}, "", false, nil, 205 providerCRef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 206 }, 207 } 208 209 // Create and iterate an eval source. 210 ctx, err := newTestPluginContext(fixedProgram(steps)) 211 assert.NoError(t, err) 212 213 iter, res := NewEvalSource(ctx, runInfo, nil, false).Iterate(context.Background(), Options{}, &testProviderSource{}) 214 assert.Nil(t, res) 215 216 processed := 0 217 for { 218 event, res := iter.Next() 219 assert.Nil(t, res) 220 221 if event == nil { 222 break 223 } 224 225 reg := event.(RegisterResourceEvent) 226 227 goal := reg.Goal() 228 if providers.IsProviderType(goal.Type) { 229 assert.NotEqual(t, "default", goal.Name) 230 } 231 urn := newURN(goal.Type, string(goal.Name), goal.Parent) 232 id := resource.ID("") 233 if goal.Custom { 234 id = "id" 235 } 236 reg.Done(&RegisterResult{ 237 State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, 238 goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, 239 false, nil, nil, nil, "", false, ""), 240 }) 241 242 processed++ 243 } 244 245 assert.Equal(t, len(steps), processed) 246 } 247 248 func TestRegisterDefaultProviders(t *testing.T) { 249 t.Parallel() 250 251 runInfo := &EvalRunInfo{ 252 Proj: &workspace.Project{Name: "test"}, 253 Target: &Target{Name: "test"}, 254 } 255 256 newURN := func(t tokens.Type, name string, parent resource.URN) resource.URN { 257 var pt tokens.Type 258 if parent != "" { 259 pt = parent.Type() 260 } 261 return resource.NewURN(runInfo.Target.Name.Q(), runInfo.Proj.Name, pt, t, tokens.QName(name)) 262 } 263 264 componentURN := newURN("component", "component", "") 265 266 steps := []RegisterResourceEvent{ 267 // Register a component resource. 268 &testRegEvent{ 269 goal: resource.NewGoal(componentURN.Type(), componentURN.Name(), false, resource.PropertyMap{}, "", false, 270 nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 271 }, 272 // Register a couple resources from package A. 273 &testRegEvent{ 274 goal: resource.NewGoal("pkgA:m:typA", "res1", true, resource.PropertyMap{}, 275 componentURN, false, nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 276 }, 277 &testRegEvent{ 278 goal: resource.NewGoal("pkgA:m:typA", "res2", true, resource.PropertyMap{}, 279 componentURN, false, nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 280 }, 281 // Register a few resources from other packages. 282 &testRegEvent{ 283 goal: resource.NewGoal("pkgB:m:typB", "res3", true, resource.PropertyMap{}, "", false, 284 nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 285 }, 286 &testRegEvent{ 287 goal: resource.NewGoal("pkgB:m:typC", "res4", true, resource.PropertyMap{}, "", false, 288 nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), 289 }, 290 } 291 292 // Create and iterate an eval source. 293 ctx, err := newTestPluginContext(fixedProgram(steps)) 294 assert.NoError(t, err) 295 296 iter, res := NewEvalSource(ctx, runInfo, nil, false).Iterate(context.Background(), Options{}, &testProviderSource{}) 297 assert.Nil(t, res) 298 299 processed, defaults := 0, make(map[string]struct{}) 300 for { 301 event, res := iter.Next() 302 assert.Nil(t, res) 303 304 if event == nil { 305 break 306 } 307 308 reg := event.(RegisterResourceEvent) 309 310 goal := reg.Goal() 311 urn := newURN(goal.Type, string(goal.Name), goal.Parent) 312 id := resource.ID("") 313 if goal.Custom { 314 id = "id" 315 } 316 317 if providers.IsProviderType(goal.Type) { 318 assert.Equal(t, "default", string(goal.Name)) 319 ref, err := providers.NewReference(urn, id) 320 assert.NoError(t, err) 321 _, ok := defaults[ref.String()] 322 assert.False(t, ok) 323 defaults[ref.String()] = struct{}{} 324 } else if goal.Custom { 325 assert.NotEqual(t, "", goal.Provider) 326 _, ok := defaults[goal.Provider] 327 assert.True(t, ok) 328 } 329 330 reg.Done(&RegisterResult{ 331 State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, 332 goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, 333 false, nil, nil, nil, "", false, ""), 334 }) 335 336 processed++ 337 } 338 339 assert.Equal(t, len(steps)+len(defaults), processed) 340 } 341 342 func TestReadInvokeNoDefaultProviders(t *testing.T) { 343 t.Parallel() 344 345 runInfo := &EvalRunInfo{ 346 Proj: &workspace.Project{Name: "test"}, 347 Target: &Target{Name: "test"}, 348 } 349 350 newURN := func(t tokens.Type, name string, parent resource.URN) resource.URN { 351 var pt tokens.Type 352 if parent != "" { 353 pt = parent.Type() 354 } 355 return resource.NewURN(runInfo.Target.Name.Q(), runInfo.Proj.Name, pt, t, tokens.QName(name)) 356 } 357 358 newProviderURN := func(pkg tokens.Package, name string, parent resource.URN) resource.URN { 359 return newURN(providers.MakeProviderType(pkg), name, parent) 360 } 361 362 providerARef, err := providers.NewReference(newProviderURN("pkgA", "providerA", ""), "id1") 363 assert.NoError(t, err) 364 providerBRef, err := providers.NewReference(newProviderURN("pkgA", "providerB", ""), "id2") 365 assert.NoError(t, err) 366 providerCRef, err := providers.NewReference(newProviderURN("pkgC", "providerC", ""), "id1") 367 assert.NoError(t, err) 368 369 invokes := int32(0) 370 noopProvider := &deploytest.Provider{ 371 InvokeF: func(tokens.ModuleMember, resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) { 372 atomic.AddInt32(&invokes, 1) 373 return resource.PropertyMap{}, nil, nil 374 }, 375 } 376 377 providerSource := &testProviderSource{ 378 providers: map[providers.Reference]plugin.Provider{ 379 providerARef: noopProvider, 380 providerBRef: noopProvider, 381 providerCRef: noopProvider, 382 }, 383 } 384 385 expectedReads, expectedInvokes := 3, 3 386 program := func(_ plugin.RunInfo, resmon *deploytest.ResourceMonitor) error { 387 // Perform some reads and invokes with explicit provider references. 388 _, _, perr := resmon.ReadResource("pkgA:m:typA", "resA", "id1", "", nil, providerARef.String(), "") 389 assert.NoError(t, perr) 390 _, _, perr = resmon.ReadResource("pkgA:m:typB", "resB", "id1", "", nil, providerBRef.String(), "") 391 assert.NoError(t, perr) 392 _, _, perr = resmon.ReadResource("pkgC:m:typC", "resC", "id1", "", nil, providerCRef.String(), "") 393 assert.NoError(t, perr) 394 395 _, _, perr = resmon.Invoke("pkgA:m:funcA", nil, providerARef.String(), "") 396 assert.NoError(t, perr) 397 _, _, perr = resmon.Invoke("pkgA:m:funcB", nil, providerBRef.String(), "") 398 assert.NoError(t, perr) 399 _, _, perr = resmon.Invoke("pkgC:m:funcC", nil, providerCRef.String(), "") 400 assert.NoError(t, perr) 401 402 return nil 403 } 404 405 // Create and iterate an eval source. 406 ctx, err := newTestPluginContext(program) 407 assert.NoError(t, err) 408 409 iter, res := NewEvalSource(ctx, runInfo, nil, false).Iterate(context.Background(), Options{}, providerSource) 410 assert.Nil(t, res) 411 412 reads := 0 413 for { 414 event, res := iter.Next() 415 assert.Nil(t, res) 416 if event == nil { 417 break 418 } 419 420 read := event.(ReadResourceEvent) 421 urn := newURN(read.Type(), string(read.Name()), read.Parent()) 422 read.Done(&ReadResult{ 423 State: resource.NewState(read.Type(), urn, true, false, read.ID(), read.Properties(), 424 resource.PropertyMap{}, read.Parent(), false, false, read.Dependencies(), nil, read.Provider(), nil, 425 false, nil, nil, nil, "", false, ""), 426 }) 427 reads++ 428 } 429 430 assert.Equal(t, expectedReads, reads) 431 assert.Equal(t, expectedInvokes, int(invokes)) 432 } 433 434 func TestReadInvokeDefaultProviders(t *testing.T) { 435 t.Parallel() 436 437 runInfo := &EvalRunInfo{ 438 Proj: &workspace.Project{Name: "test"}, 439 Target: &Target{Name: "test"}, 440 } 441 442 newURN := func(t tokens.Type, name string, parent resource.URN) resource.URN { 443 var pt tokens.Type 444 if parent != "" { 445 pt = parent.Type() 446 } 447 return resource.NewURN(runInfo.Target.Name.Q(), runInfo.Proj.Name, pt, t, tokens.QName(name)) 448 } 449 450 invokes := int32(0) 451 noopProvider := &deploytest.Provider{ 452 InvokeF: func(tokens.ModuleMember, resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) { 453 atomic.AddInt32(&invokes, 1) 454 return resource.PropertyMap{}, nil, nil 455 }, 456 } 457 458 expectedReads, expectedInvokes := 3, 3 459 program := func(_ plugin.RunInfo, resmon *deploytest.ResourceMonitor) error { 460 // Perform some reads and invokes with default provider references. 461 _, _, err := resmon.ReadResource("pkgA:m:typA", "resA", "id1", "", nil, "", "") 462 assert.NoError(t, err) 463 _, _, err = resmon.ReadResource("pkgA:m:typB", "resB", "id1", "", nil, "", "") 464 assert.NoError(t, err) 465 _, _, err = resmon.ReadResource("pkgC:m:typC", "resC", "id1", "", nil, "", "") 466 assert.NoError(t, err) 467 468 _, _, err = resmon.Invoke("pkgA:m:funcA", nil, "", "") 469 assert.NoError(t, err) 470 _, _, err = resmon.Invoke("pkgA:m:funcB", nil, "", "") 471 assert.NoError(t, err) 472 _, _, err = resmon.Invoke("pkgC:m:funcC", nil, "", "") 473 assert.NoError(t, err) 474 475 return nil 476 } 477 478 // Create and iterate an eval source. 479 ctx, err := newTestPluginContext(program) 480 assert.NoError(t, err) 481 482 providerSource := &testProviderSource{providers: make(map[providers.Reference]plugin.Provider)} 483 484 iter, res := NewEvalSource(ctx, runInfo, nil, false).Iterate(context.Background(), Options{}, providerSource) 485 assert.Nil(t, res) 486 487 reads, registers := 0, 0 488 for { 489 event, res := iter.Next() 490 assert.Nil(t, res) 491 492 if event == nil { 493 break 494 } 495 496 switch e := event.(type) { 497 case RegisterResourceEvent: 498 goal := e.Goal() 499 urn, id := newURN(goal.Type, string(goal.Name), goal.Parent), resource.ID("id") 500 501 assert.True(t, providers.IsProviderType(goal.Type)) 502 assert.Equal(t, "default", string(goal.Name)) 503 ref, err := providers.NewReference(urn, id) 504 assert.NoError(t, err) 505 _, ok := providerSource.GetProvider(ref) 506 assert.False(t, ok) 507 providerSource.registerProvider(ref, noopProvider) 508 509 e.Done(&RegisterResult{ 510 State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, 511 goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, 512 false, nil, nil, nil, "", false, ""), 513 }) 514 registers++ 515 516 case ReadResourceEvent: 517 urn := newURN(e.Type(), string(e.Name()), e.Parent()) 518 e.Done(&ReadResult{ 519 State: resource.NewState(e.Type(), urn, true, false, e.ID(), e.Properties(), 520 resource.PropertyMap{}, e.Parent(), false, false, e.Dependencies(), nil, e.Provider(), nil, false, 521 nil, nil, nil, "", false, ""), 522 }) 523 reads++ 524 } 525 } 526 527 assert.Equal(t, len(providerSource.providers), registers) 528 assert.Equal(t, expectedReads, reads) 529 assert.Equal(t, expectedInvokes, int(invokes)) 530 } 531 532 // Test that we can run operations with default providers disabled. 533 // 534 // We run against the matrix of 535 // - enabled vs disabled 536 // - explicit vs default 537 // 538 // B exists as a sanity check, to ensure that we can still perform arbitrary 539 // operations that belong to other packages. 540 func TestDisableDefaultProviders(t *testing.T) { 541 t.Parallel() 542 543 type TT struct { 544 disableDefault bool 545 hasExplicit bool 546 expectFail bool 547 } 548 cases := []TT{} 549 for _, disableDefault := range []bool{true, false} { 550 for _, hasExplicit := range []bool{true, false} { 551 cases = append(cases, TT{ 552 disableDefault: disableDefault, 553 hasExplicit: hasExplicit, 554 expectFail: disableDefault && !hasExplicit, 555 }) 556 } 557 } 558 //nolint:paralleltest // false positive because range var isn't used directly in t.Run(name) arg 559 for _, tt := range cases { 560 tt := tt 561 var name []string 562 if tt.disableDefault { 563 name = append(name, "disableDefault") 564 } 565 if tt.hasExplicit { 566 name = append(name, "hasExplicit") 567 } 568 if tt.expectFail { 569 name = append(name, "expectFail") 570 } 571 if len(name) == 0 { 572 name = append(name, "vanilla") 573 } 574 575 t.Run(strings.Join(name, "+"), func(t *testing.T) { 576 t.Parallel() 577 578 runInfo := &EvalRunInfo{ 579 Proj: &workspace.Project{Name: "test"}, 580 Target: &Target{Name: "test"}, 581 } 582 if tt.disableDefault { 583 disableDefaultProviders(runInfo, "pkgA") 584 } 585 586 newURN := func(t tokens.Type, name string, parent resource.URN) resource.URN { 587 var pt tokens.Type 588 if parent != "" { 589 pt = parent.Type() 590 } 591 return resource.NewURN(runInfo.Target.Name.Q(), runInfo.Proj.Name, pt, t, tokens.QName(name)) 592 } 593 594 newProviderURN := func(pkg tokens.Package, name string, parent resource.URN) resource.URN { 595 return newURN(providers.MakeProviderType(pkg), name, parent) 596 } 597 598 providerARef, err := providers.NewReference(newProviderURN("pkgA", "providerA", ""), "id1") 599 assert.NoError(t, err) 600 providerBRef, err := providers.NewReference(newProviderURN("pkgB", "providerB", ""), "id2") 601 assert.NoError(t, err) 602 603 expectedReads, expectedInvokes, expectedRegisters := 3, 3, 1 604 reads, invokes, registers := 0, int32(0), 0 605 606 if tt.expectFail { 607 expectedReads-- 608 expectedInvokes-- 609 } 610 if !tt.hasExplicit && !tt.disableDefault && !tt.expectFail { 611 // The register is creating the default provider 612 expectedRegisters++ 613 } 614 615 noopProvider := &deploytest.Provider{ 616 InvokeF: func(tokens.ModuleMember, resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) { 617 atomic.AddInt32(&invokes, 1) 618 return resource.PropertyMap{}, nil, nil 619 }, 620 } 621 622 providerSource := &testProviderSource{ 623 providers: map[providers.Reference]plugin.Provider{ 624 providerARef: noopProvider, 625 providerBRef: noopProvider, 626 }, 627 defaultProvider: noopProvider, 628 } 629 630 program := func(_ plugin.RunInfo, resmon *deploytest.ResourceMonitor) error { 631 aErrorAssert := assert.NoError 632 if tt.expectFail { 633 aErrorAssert = assert.Error 634 } 635 var aPkgProvider string 636 if tt.hasExplicit { 637 aPkgProvider = providerARef.String() 638 } 639 // Perform some reads and invokes with explicit provider references. 640 _, _, perr := resmon.ReadResource("pkgA:m:typA", "resA", "id1", "", nil, aPkgProvider, "") 641 aErrorAssert(t, perr) 642 _, _, perr = resmon.ReadResource("pkgB:m:typB", "resB", "id1", "", nil, providerBRef.String(), "") 643 assert.NoError(t, perr) 644 _, _, perr = resmon.ReadResource("pkgC:m:typC", "resC", "id1", "", nil, "", "") 645 assert.NoError(t, perr) 646 647 _, _, perr = resmon.Invoke("pkgA:m:funcA", nil, aPkgProvider, "") 648 aErrorAssert(t, perr) 649 _, _, perr = resmon.Invoke("pkgB:m:funcB", nil, providerBRef.String(), "") 650 assert.NoError(t, perr) 651 _, _, perr = resmon.Invoke("pkgC:m:funcC", nil, "", "") 652 assert.NoError(t, perr) 653 654 return nil 655 } 656 657 // Create and iterate an eval source. 658 ctx, err := newTestPluginContext(program) 659 assert.NoError(t, err) 660 661 iter, res := NewEvalSource(ctx, runInfo, nil, false).Iterate(context.Background(), Options{}, providerSource) 662 assert.Nil(t, res) 663 664 for { 665 event, res := iter.Next() 666 assert.Nil(t, res) 667 if event == nil { 668 break 669 } 670 switch event := event.(type) { 671 case ReadResourceEvent: 672 urn := newURN(event.Type(), string(event.Name()), event.Parent()) 673 event.Done(&ReadResult{ 674 State: resource.NewState(event.Type(), urn, true, false, event.ID(), event.Properties(), 675 resource.PropertyMap{}, event.Parent(), false, false, event.Dependencies(), nil, event.Provider(), nil, 676 false, nil, nil, nil, "", false, ""), 677 }) 678 reads++ 679 case RegisterResourceEvent: 680 urn := newURN(event.Goal().Type, string(event.Goal().Name), event.Goal().Parent) 681 event.Done(&RegisterResult{ 682 State: resource.NewState(event.Goal().Type, urn, true, false, event.Goal().ID, event.Goal().Properties, 683 resource.PropertyMap{}, event.Goal().Parent, false, false, event.Goal().Dependencies, nil, 684 event.Goal().Provider, nil, false, nil, nil, nil, "", false, ""), 685 }) 686 registers++ 687 default: 688 panic(event) 689 } 690 } 691 692 assert.Equalf(t, expectedReads, reads, "Reads") 693 assert.Equalf(t, expectedInvokes, int(invokes), "Invokes") 694 assert.Equalf(t, expectedRegisters, registers, "Registers") 695 696 }) 697 } 698 } 699 700 // TODO[pulumi/pulumi#2753]: We should re-enable these tests (and fix them up as needed) once we have a solution 701 // for #2753. 702 // func TestReadResourceAndInvokeVersion(t *testing.T) { 703 // runInfo := &EvalRunInfo{ 704 // Proj: &workspace.Project{Name: "test"}, 705 // Target: &Target{Name: "test"}, 706 // } 707 708 // newURN := func(t tokens.Type, name string, parent resource.URN) resource.URN { 709 // var pt tokens.Type 710 // if parent != "" { 711 // pt = parent.Type() 712 // } 713 // return resource.NewURN(runInfo.Target.Name, runInfo.Proj.Name, pt, t, tokens.QName(name)) 714 // } 715 716 // invokes := int32(0) 717 // noopProvider := &deploytest.Provider{ 718 // InvokeF: func(tokens.ModuleMember, resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) { 719 // atomic.AddInt32(&invokes, 1) 720 // return resource.PropertyMap{}, nil, nil 721 // }, 722 // } 723 724 // // This program is designed to trigger the instantiation of two default providers: 725 // // 1. Provider pkgA, version 0.18.0 726 // // 2. Provider pkgC, version 0.18.0 727 // program := func(_ plugin.RunInfo, resmon *deploytest.ResourceMonitor) error { 728 // // Triggers pkgA, v0.18.0. 729 // _, _, err := resmon.ReadResource("pkgA:m:typA", "resA", "id1", "", nil, "", "0.18.0") 730 // assert.NoError(t, err) 731 // // Uses pkgA's already-instantiated provider. 732 // _, _, err = resmon.ReadResource("pkgA:m:typB", "resB", "id1", "", nil, "", "0.18.0") 733 // assert.NoError(t, err) 734 735 // // Triggers pkgC, v0.18.0. 736 // _, _, err = resmon.ReadResource("pkgC:m:typC", "resC", "id1", "", nil, "", "0.18.0") 737 // assert.NoError(t, err) 738 739 // // Uses pkgA and pkgC's already-instantiated provider. 740 // _, _, err = resmon.Invoke("pkgA:m:funcA", nil, "", "0.18.0") 741 // assert.NoError(t, err) 742 // _, _, err = resmon.Invoke("pkgA:m:funcB", nil, "", "0.18.0") 743 // assert.NoError(t, err) 744 // _, _, err = resmon.Invoke("pkgC:m:funcC", nil, "", "0.18.0") 745 // assert.NoError(t, err) 746 747 // return nil 748 // } 749 750 // ctx, err := newTestPluginContext(program) 751 // assert.NoError(t, err) 752 753 // providerSource := &testProviderSource{providers: make(map[providers.Reference]plugin.Provider)} 754 755 // iter, res := NewEvalSource(ctx, runInfo, nil, false).Iterate(context.Background(), Options{}, providerSource) 756 // assert.Nil(t, res) 757 // registrations, reads := 0, 0 758 // for { 759 // event, res := iter.Next() 760 // assert.Nil(t, res) 761 762 // if event == nil { 763 // break 764 // } 765 766 // switch e := event.(type) { 767 // case RegisterResourceEvent: 768 // goal := e.Goal() 769 // urn, id := newURN(goal.Type, string(goal.Name), goal.Parent), resource.ID("id") 770 771 // assert.True(t, providers.IsProviderType(goal.Type)) 772 // // The name of the provider resource is derived from the version requested. 773 // assert.Equal(t, "default_0_18_0", string(goal.Name)) 774 // ref, err := providers.NewReference(urn, id) 775 // assert.NoError(t, err) 776 // _, ok := providerSource.GetProvider(ref) 777 // assert.False(t, ok) 778 // providerSource.registerProvider(ref, noopProvider) 779 780 // e.Done(&RegisterResult{ 781 // State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, 782 // goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, 783 // false, nil), 784 // }) 785 // registrations++ 786 787 // case ReadResourceEvent: 788 // urn := newURN(e.Type(), string(e.Name()), e.Parent()) 789 // e.Done(&ReadResult{ 790 // State: resource.NewState(e.Type(), urn, true, false, e.ID(), e.Properties(), 791 // resource.PropertyMap{}, e.Parent(), false, false, e.Dependencies(), nil, e.Provider(), nil, false, 792 // nil), 793 // }) 794 // reads++ 795 // } 796 // } 797 798 // assert.Equal(t, 2, registrations) 799 // assert.Equal(t, 3, reads) 800 // assert.Equal(t, int32(3), invokes) 801 // } 802 803 // func TestRegisterResourceWithVersion(t *testing.T) { 804 // runInfo := &EvalRunInfo{ 805 // Proj: &workspace.Project{Name: "test"}, 806 // Target: &Target{Name: "test"}, 807 // } 808 809 // newURN := func(t tokens.Type, name string, parent resource.URN) resource.URN { 810 // var pt tokens.Type 811 // if parent != "" { 812 // pt = parent.Type() 813 // } 814 // return resource.NewURN(runInfo.Target.Name, runInfo.Proj.Name, pt, t, tokens.QName(name)) 815 // } 816 817 // noopProvider := &deploytest.Provider{} 818 819 // // This program is designed to trigger the instantiation of two default providers: 820 // // 1. Provider pkgA, version 0.18.0 821 // // 2. Provider pkgC, version 0.18.0 822 // program := func(_ plugin.RunInfo, resmon *deploytest.ResourceMonitor) error { 823 // // Triggers pkgA, v0.18.1. 824 // _, _, _, err := resmon.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "", 825 // resource.PropertyMap{}, nil, false, "0.18.1", nil) 826 // assert.NoError(t, err) 827 828 // // Re-uses pkgA's already-instantiated provider. 829 // _, _, _, err = resmon.RegisterResource("pkgA:m:typA", "resB", true, "", false, nil, "", 830 // resource.PropertyMap{}, nil, false, "0.18.1", nil) 831 // assert.NoError(t, err) 832 833 // // Triggers pkgA, v0.18.2 834 // _, _, _, err = resmon.RegisterResource("pkgA:m:typA", "resB", true, "", false, nil, "", 835 // resource.PropertyMap{}, nil, false, "0.18.2", nil) 836 // assert.NoError(t, err) 837 // return nil 838 // } 839 840 // ctx, err := newTestPluginContext(program) 841 // assert.NoError(t, err) 842 843 // providerSource := &testProviderSource{providers: make(map[providers.Reference]plugin.Provider)} 844 845 // iter, res := NewEvalSource(ctx, runInfo, nil, false).Iterate(context.Background(), Options{}, providerSource) 846 // assert.Nil(t, res) 847 // registered181, registered182 := false, false 848 // for { 849 // event, res := iter.Next() 850 // assert.Nil(t, res) 851 852 // if event == nil { 853 // break 854 // } 855 856 // switch e := event.(type) { 857 // case RegisterResourceEvent: 858 // goal := e.Goal() 859 // urn, id := newURN(goal.Type, string(goal.Name), goal.Parent), resource.ID("id") 860 861 // if providers.IsProviderType(goal.Type) { 862 // switch goal.Name { 863 // case "default_0_18_1": 864 // assert.False(t, registered181) 865 // registered181 = true 866 // case "default_0_18_2": 867 // assert.False(t, registered182) 868 // registered182 = true 869 // } 870 871 // ref, err := providers.NewReference(urn, id) 872 // assert.NoError(t, err) 873 // _, ok := providerSource.GetProvider(ref) 874 // assert.False(t, ok) 875 // providerSource.registerProvider(ref, noopProvider) 876 // } 877 878 // e.Done(&RegisterResult{ 879 // State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, 880 // goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, 881 // false, nil), 882 // }) 883 // } 884 // } 885 886 // assert.True(t, registered181) 887 // assert.True(t, registered182) 888 // }