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  // }