github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/engine/lifecycletest/provider_test.go (about)

     1  //nolint:goconst
     2  package lifecycletest
     3  
     4  import (
     5  	"sync"
     6  	"testing"
     7  
     8  	"github.com/blang/semver"
     9  	"github.com/gofrs/uuid"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	. "github.com/pulumi/pulumi/pkg/v3/engine"
    14  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
    15  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy/deploytest"
    16  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
    17  	"github.com/pulumi/pulumi/sdk/v3/go/common/display"
    18  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    19  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
    20  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
    21  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
    22  	"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
    23  )
    24  
    25  func TestSingleResourceDefaultProviderLifecycle(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	loaders := []*deploytest.ProviderLoader{
    29  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
    30  			return &deploytest.Provider{}, nil
    31  		}),
    32  	}
    33  
    34  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
    35  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
    36  		assert.NoError(t, err)
    37  		return nil
    38  	})
    39  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
    40  
    41  	p := &TestPlan{
    42  		Options: UpdateOptions{Host: host},
    43  		Steps:   MakeBasicLifecycleSteps(t, 2),
    44  	}
    45  	p.Run(t, nil)
    46  }
    47  
    48  func TestSingleResourceExplicitProviderLifecycle(t *testing.T) {
    49  	t.Parallel()
    50  
    51  	loaders := []*deploytest.ProviderLoader{
    52  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
    53  			return &deploytest.Provider{}, nil
    54  		}),
    55  	}
    56  
    57  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
    58  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true)
    59  		assert.NoError(t, err)
    60  
    61  		if provID == "" {
    62  			provID = providers.UnknownID
    63  		}
    64  
    65  		provRef, err := providers.NewReference(provURN, provID)
    66  		assert.NoError(t, err)
    67  
    68  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
    69  			Provider: provRef.String(),
    70  		})
    71  		assert.NoError(t, err)
    72  
    73  		return nil
    74  	})
    75  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
    76  
    77  	p := &TestPlan{
    78  		Options: UpdateOptions{Host: host},
    79  		Steps:   MakeBasicLifecycleSteps(t, 2),
    80  	}
    81  	p.Run(t, nil)
    82  }
    83  
    84  func TestSingleResourceDefaultProviderUpgrade(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	loaders := []*deploytest.ProviderLoader{
    88  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
    89  			return &deploytest.Provider{}, nil
    90  		}),
    91  	}
    92  
    93  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
    94  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
    95  		assert.NoError(t, err)
    96  		return nil
    97  	})
    98  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
    99  
   100  	p := &TestPlan{
   101  		Options: UpdateOptions{Host: host},
   102  	}
   103  
   104  	provURN := p.NewProviderURN("pkgA", "default", "")
   105  	resURN := p.NewURN("pkgA:m:typA", "resA", "")
   106  
   107  	// Create an old snapshot with an existing copy of the single resource and no providers.
   108  	old := &deploy.Snapshot{
   109  		Resources: []*resource.State{{
   110  			Type:    resURN.Type(),
   111  			URN:     resURN,
   112  			Custom:  true,
   113  			ID:      "0",
   114  			Inputs:  resource.PropertyMap{},
   115  			Outputs: resource.PropertyMap{},
   116  		}},
   117  	}
   118  
   119  	isRefresh := false
   120  	validate := func(project workspace.Project, target deploy.Target, entries JournalEntries,
   121  		_ []Event, res result.Result) result.Result {
   122  
   123  		// Should see only sames: the default provider should be injected into the old state before the update
   124  		// runs.
   125  		for _, entry := range entries {
   126  			switch urn := entry.Step.URN(); urn {
   127  			case provURN, resURN:
   128  				expect := deploy.OpSame
   129  				if isRefresh {
   130  					expect = deploy.OpRefresh
   131  				}
   132  				assert.Equal(t, expect, entry.Step.Op())
   133  			default:
   134  				t.Fatalf("unexpected resource %v", urn)
   135  			}
   136  		}
   137  		snap, err := entries.Snap(target.Snapshot)
   138  		require.NoError(t, err)
   139  		assert.Len(t, snap.Resources, 2)
   140  		return res
   141  	}
   142  
   143  	// Run a single update step using the base snapshot.
   144  	p.Steps = []TestStep{{Op: Update, Validate: validate}}
   145  	p.Run(t, old)
   146  
   147  	// Run a single refresh step using the base snapshot.
   148  	isRefresh = true
   149  	p.Steps = []TestStep{{Op: Refresh, Validate: validate}}
   150  	p.Run(t, old)
   151  
   152  	// Run a single destroy step using the base snapshot.
   153  	isRefresh = false
   154  	p.Steps = []TestStep{{
   155  		Op: Destroy,
   156  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   157  			_ []Event, res result.Result) result.Result {
   158  
   159  			// Should see two deletes:  the default provider should be injected into the old state before the update
   160  			// runs.
   161  			deleted := make(map[resource.URN]bool)
   162  			for _, entry := range entries {
   163  				switch urn := entry.Step.URN(); urn {
   164  				case provURN, resURN:
   165  					deleted[urn] = true
   166  					assert.Equal(t, deploy.OpDelete, entry.Step.Op())
   167  				default:
   168  					t.Fatalf("unexpected resource %v", urn)
   169  				}
   170  			}
   171  			assert.Len(t, deleted, 2)
   172  			snap, err := entries.Snap(target.Snapshot)
   173  			require.NoError(t, err)
   174  			assert.Len(t, snap.Resources, 0)
   175  			return res
   176  		},
   177  	}}
   178  	p.Run(t, old)
   179  
   180  	// Run a partial lifecycle using the base snapshot, skipping the initial update step.
   181  	p.Steps = MakeBasicLifecycleSteps(t, 2)[1:]
   182  	p.Run(t, old)
   183  }
   184  
   185  func TestSingleResourceDefaultProviderReplace(t *testing.T) {
   186  	t.Parallel()
   187  
   188  	loaders := []*deploytest.ProviderLoader{
   189  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   190  			return &deploytest.Provider{
   191  				DiffConfigF: func(urn resource.URN, olds, news resource.PropertyMap,
   192  					ignoreChanges []string) (plugin.DiffResult, error) {
   193  
   194  					// Always require replacement.
   195  					keys := []resource.PropertyKey{}
   196  					for k := range news {
   197  						keys = append(keys, k)
   198  					}
   199  					return plugin.DiffResult{ReplaceKeys: keys}, nil
   200  				},
   201  			}, nil
   202  		}),
   203  	}
   204  
   205  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   206  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
   207  		assert.NoError(t, err)
   208  		return nil
   209  	})
   210  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   211  
   212  	p := &TestPlan{
   213  		Options: UpdateOptions{Host: host},
   214  		Config: config.Map{
   215  			config.MustMakeKey("pkgA", "foo"): config.NewValue("bar"),
   216  		},
   217  	}
   218  
   219  	// Build a basic lifecycle.
   220  	steps := MakeBasicLifecycleSteps(t, 2)
   221  
   222  	// Run the lifecycle through its no-op update+refresh.
   223  	p.Steps = steps[:4]
   224  	snap := p.Run(t, nil)
   225  
   226  	// Change the config and run an update. We expect everything to require replacement.
   227  	p.Config[config.MustMakeKey("pkgA", "foo")] = config.NewValue("baz")
   228  	p.Steps = []TestStep{{
   229  		Op: Update,
   230  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   231  			_ []Event, res result.Result) result.Result {
   232  
   233  			provURN := p.NewProviderURN("pkgA", "default", "")
   234  			resURN := p.NewURN("pkgA:m:typA", "resA", "")
   235  
   236  			// Look for replace steps on the provider and the resource.
   237  			replacedProvider, replacedResource := false, false
   238  			for _, entry := range entries {
   239  				if entry.Kind != JournalEntrySuccess || entry.Step.Op() != deploy.OpDeleteReplaced {
   240  					continue
   241  				}
   242  
   243  				switch urn := entry.Step.URN(); urn {
   244  				case provURN:
   245  					replacedProvider = true
   246  				case resURN:
   247  					replacedResource = true
   248  				default:
   249  					t.Fatalf("unexpected resource %v", urn)
   250  				}
   251  			}
   252  			assert.True(t, replacedProvider)
   253  			assert.True(t, replacedResource)
   254  
   255  			return res
   256  		},
   257  	}}
   258  
   259  	snap = p.Run(t, snap)
   260  
   261  	// Resume the lifecycle with another no-op update.
   262  	p.Steps = steps[2:]
   263  	p.Run(t, snap)
   264  }
   265  
   266  func TestSingleResourceExplicitProviderReplace(t *testing.T) {
   267  	t.Parallel()
   268  
   269  	loaders := []*deploytest.ProviderLoader{
   270  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   271  			return &deploytest.Provider{
   272  				DiffConfigF: func(urn resource.URN, olds, news resource.PropertyMap,
   273  					ignoreChanges []string) (plugin.DiffResult, error) {
   274  					// Always require replacement.
   275  					keys := []resource.PropertyKey{}
   276  					for k := range news {
   277  						keys = append(keys, k)
   278  					}
   279  					return plugin.DiffResult{ReplaceKeys: keys}, nil
   280  				},
   281  			}, nil
   282  		}),
   283  	}
   284  
   285  	providerInputs := resource.PropertyMap{
   286  		resource.PropertyKey("foo"): resource.NewStringProperty("bar"),
   287  	}
   288  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   289  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true,
   290  			deploytest.ResourceOptions{Inputs: providerInputs})
   291  		assert.NoError(t, err)
   292  
   293  		if provID == "" {
   294  			provID = providers.UnknownID
   295  		}
   296  
   297  		provRef, err := providers.NewReference(provURN, provID)
   298  		assert.NoError(t, err)
   299  
   300  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
   301  			Provider: provRef.String(),
   302  		})
   303  		assert.NoError(t, err)
   304  
   305  		return nil
   306  	})
   307  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   308  
   309  	p := &TestPlan{
   310  		Options: UpdateOptions{Host: host},
   311  	}
   312  
   313  	// Build a basic lifecycle.
   314  	steps := MakeBasicLifecycleSteps(t, 2)
   315  
   316  	// Run the lifecycle through its no-op update+refresh.
   317  	p.Steps = steps[:4]
   318  	snap := p.Run(t, nil)
   319  
   320  	// Change the config and run an update. We expect everything to require replacement.
   321  	providerInputs[resource.PropertyKey("foo")] = resource.NewStringProperty("baz")
   322  	p.Steps = []TestStep{{
   323  		Op: Update,
   324  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   325  			_ []Event, res result.Result) result.Result {
   326  
   327  			provURN := p.NewProviderURN("pkgA", "provA", "")
   328  			resURN := p.NewURN("pkgA:m:typA", "resA", "")
   329  
   330  			// Look for replace steps on the provider and the resource.
   331  			replacedProvider, replacedResource := false, false
   332  			for _, entry := range entries {
   333  				if entry.Kind != JournalEntrySuccess || entry.Step.Op() != deploy.OpDeleteReplaced {
   334  					continue
   335  				}
   336  
   337  				switch urn := entry.Step.URN(); urn {
   338  				case provURN:
   339  					replacedProvider = true
   340  				case resURN:
   341  					replacedResource = true
   342  				default:
   343  					t.Fatalf("unexpected resource %v", urn)
   344  				}
   345  			}
   346  			assert.True(t, replacedProvider)
   347  			assert.True(t, replacedResource)
   348  
   349  			return res
   350  		},
   351  	}}
   352  	snap = p.Run(t, snap)
   353  
   354  	// Resume the lifecycle with another no-op update.
   355  	p.Steps = steps[2:]
   356  	p.Run(t, snap)
   357  }
   358  
   359  type configurableProvider struct {
   360  	id      string
   361  	replace bool
   362  	creates *sync.Map
   363  	deletes *sync.Map
   364  }
   365  
   366  func (p *configurableProvider) configure(news resource.PropertyMap) error {
   367  	p.id = news["id"].StringValue()
   368  	return nil
   369  }
   370  
   371  func (p *configurableProvider) create(urn resource.URN, inputs resource.PropertyMap, timeout float64,
   372  	preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
   373  
   374  	uid, err := uuid.NewV4()
   375  	if err != nil {
   376  		return "", nil, resource.StatusUnknown, err
   377  	}
   378  	id := resource.ID(uid.String())
   379  
   380  	p.creates.Store(id, p.id)
   381  	return id, inputs, resource.StatusOK, nil
   382  }
   383  
   384  func (p *configurableProvider) delete(urn resource.URN, id resource.ID, olds resource.PropertyMap,
   385  	timeout float64) (resource.Status, error) {
   386  	p.deletes.Store(id, p.id)
   387  	return resource.StatusOK, nil
   388  }
   389  
   390  // TestSingleResourceExplicitProviderAliasUpdateDelete verifies that providers respect aliases during updates, and
   391  // that the correct instance of an explicit provider is used to delete a removed resource.
   392  func TestSingleResourceExplicitProviderAliasUpdateDelete(t *testing.T) {
   393  	t.Parallel()
   394  
   395  	var creates, deletes sync.Map
   396  
   397  	loaders := []*deploytest.ProviderLoader{
   398  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   399  			configurable := &configurableProvider{
   400  				creates: &creates,
   401  				deletes: &deletes,
   402  			}
   403  
   404  			return &deploytest.Provider{
   405  				DiffConfigF: func(urn resource.URN, olds, news resource.PropertyMap,
   406  					ignoreChanges []string) (plugin.DiffResult, error) {
   407  					return plugin.DiffResult{}, nil
   408  				},
   409  				ConfigureF: configurable.configure,
   410  				CreateF:    configurable.create,
   411  				DeleteF:    configurable.delete,
   412  			}, nil
   413  		}),
   414  	}
   415  
   416  	providerInputs := resource.PropertyMap{
   417  		resource.PropertyKey("id"): resource.NewStringProperty("first"),
   418  	}
   419  	providerName := "provA"
   420  	aliases := []resource.URN{}
   421  	registerResource := true
   422  	var resourceID resource.ID
   423  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   424  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), providerName, true,
   425  			deploytest.ResourceOptions{
   426  				Inputs:    providerInputs,
   427  				AliasURNs: aliases,
   428  			})
   429  		assert.NoError(t, err)
   430  
   431  		if provID == "" {
   432  			provID = providers.UnknownID
   433  		}
   434  
   435  		provRef, err := providers.NewReference(provURN, provID)
   436  		assert.NoError(t, err)
   437  
   438  		if registerResource {
   439  			_, resourceID, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
   440  				Provider: provRef.String(),
   441  			})
   442  			assert.NoError(t, err)
   443  		}
   444  
   445  		return nil
   446  	})
   447  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   448  
   449  	p := &TestPlan{
   450  		Options: UpdateOptions{Host: host},
   451  	}
   452  
   453  	// Build a basic lifecycle.
   454  	steps := MakeBasicLifecycleSteps(t, 2)
   455  
   456  	// Run the lifecycle through its initial update+refresh.
   457  	p.Steps = steps[:4]
   458  	snap := p.Run(t, nil)
   459  
   460  	// Add a provider alias to the original URN.
   461  	aliases = []resource.URN{
   462  		p.NewProviderURN("pkgA", "provA", ""),
   463  	}
   464  	// Change the provider name and configuration and remove the resource. This will cause an Update for the provider
   465  	// and a Delete for the resource. The updated provider instance should be used to perform the delete.
   466  	providerName = "provB"
   467  	providerInputs[resource.PropertyKey("id")] = resource.NewStringProperty("second")
   468  	registerResource = false
   469  
   470  	p.Steps = []TestStep{{Op: Update}}
   471  	_ = p.Run(t, snap)
   472  
   473  	// Check the identity of the provider that performed the delete.
   474  	deleterID, ok := deletes.Load(resourceID)
   475  	require.True(t, ok)
   476  	assert.Equal(t, "second", deleterID)
   477  }
   478  
   479  // TestSingleResourceExplicitProviderAliasReplace verifies that providers respect aliases,
   480  // and propagate replaces as a result of an aliased provider diff.
   481  func TestSingleResourceExplicitProviderAliasReplace(t *testing.T) {
   482  	t.Parallel()
   483  
   484  	var creates, deletes sync.Map
   485  
   486  	loaders := []*deploytest.ProviderLoader{
   487  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   488  			configurable := &configurableProvider{
   489  				replace: true,
   490  				creates: &creates,
   491  				deletes: &deletes,
   492  			}
   493  
   494  			return &deploytest.Provider{
   495  				DiffConfigF: func(urn resource.URN, olds, news resource.PropertyMap,
   496  					ignoreChanges []string) (plugin.DiffResult, error) {
   497  					keys := []resource.PropertyKey{}
   498  					for k := range news {
   499  						keys = append(keys, k)
   500  					}
   501  					return plugin.DiffResult{ReplaceKeys: keys}, nil
   502  				},
   503  				ConfigureF: configurable.configure,
   504  				CreateF:    configurable.create,
   505  				DeleteF:    configurable.delete,
   506  			}, nil
   507  		}),
   508  	}
   509  
   510  	providerInputs := resource.PropertyMap{
   511  		resource.PropertyKey("id"): resource.NewStringProperty("first"),
   512  	}
   513  	providerName := "provA"
   514  	aliases := []resource.URN{}
   515  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   516  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), providerName, true,
   517  			deploytest.ResourceOptions{
   518  				Inputs:    providerInputs,
   519  				AliasURNs: aliases,
   520  			})
   521  		assert.NoError(t, err)
   522  
   523  		if provID == "" {
   524  			provID = providers.UnknownID
   525  		}
   526  
   527  		provRef, err := providers.NewReference(provURN, provID)
   528  		assert.NoError(t, err)
   529  
   530  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
   531  			Provider: provRef.String(),
   532  		})
   533  		assert.NoError(t, err)
   534  
   535  		return nil
   536  	})
   537  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   538  
   539  	p := &TestPlan{
   540  		Options: UpdateOptions{Host: host},
   541  	}
   542  
   543  	// Build a basic lifecycle.
   544  	steps := MakeBasicLifecycleSteps(t, 2)
   545  
   546  	// Run the lifecycle through its no-op update+refresh.
   547  	p.Steps = steps[:4]
   548  	snap := p.Run(t, nil)
   549  
   550  	// add a provider alias to the original URN
   551  	aliases = []resource.URN{
   552  		p.NewProviderURN("pkgA", "provA", ""),
   553  	}
   554  	// change the provider name
   555  	providerName = "provB"
   556  	// run an update expecting no-op respecting the aliases.
   557  	p.Steps = []TestStep{{
   558  		Op: Update,
   559  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   560  			_ []Event, res result.Result) result.Result {
   561  			for _, entry := range entries {
   562  				if entry.Step.Op() != deploy.OpSame {
   563  					t.Fatalf("update should contain no changes: %v", entry.Step.URN())
   564  				}
   565  			}
   566  			return res
   567  		},
   568  	}}
   569  	snap = p.Run(t, snap)
   570  
   571  	// Change the config and run an update maintaining the alias. We expect everything to require replacement.
   572  	providerInputs[resource.PropertyKey("id")] = resource.NewStringProperty("second")
   573  	p.Steps = []TestStep{{
   574  		Op: Update,
   575  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   576  			_ []Event, res result.Result) result.Result {
   577  
   578  			provURN := p.NewProviderURN("pkgA", providerName, "")
   579  			resURN := p.NewURN("pkgA:m:typA", "resA", "")
   580  
   581  			// Find the delete and create IDs for the resource.
   582  			var createdID, deletedID resource.ID
   583  
   584  			// Look for replace steps on the provider and the resource.
   585  			replacedProvider, replacedResource := false, false
   586  			for _, entry := range entries {
   587  				op := entry.Step.Op()
   588  
   589  				if entry.Step.URN() == resURN {
   590  					switch op {
   591  					case deploy.OpCreateReplacement:
   592  						createdID = entry.Step.New().ID
   593  					case deploy.OpDeleteReplaced:
   594  						deletedID = entry.Step.Old().ID
   595  					}
   596  				}
   597  
   598  				if entry.Kind != JournalEntrySuccess || op != deploy.OpDeleteReplaced {
   599  					continue
   600  				}
   601  
   602  				switch urn := entry.Step.URN(); urn {
   603  				case provURN:
   604  					replacedProvider = true
   605  				case resURN:
   606  					replacedResource = true
   607  				default:
   608  					t.Fatalf("unexpected resource %v", urn)
   609  				}
   610  			}
   611  			assert.True(t, replacedProvider)
   612  			assert.True(t, replacedResource)
   613  
   614  			// Check the identities of the providers that performed the create and delete.
   615  			//
   616  			// For a replacement, the newly-created provider should be used to create the new resource, and the original
   617  			// provider should be used to delete the old resource.
   618  			creatorID, ok := creates.Load(createdID)
   619  			require.True(t, ok)
   620  			assert.Equal(t, "second", creatorID)
   621  
   622  			deleterID, ok := deletes.Load(deletedID)
   623  			require.True(t, ok)
   624  			assert.Equal(t, "first", deleterID)
   625  
   626  			return res
   627  		},
   628  	}}
   629  	snap = p.Run(t, snap)
   630  
   631  	// Resume the lifecycle with another no-op update.
   632  	p.Steps = steps[2:]
   633  	p.Run(t, snap)
   634  }
   635  
   636  func TestSingleResourceExplicitProviderDeleteBeforeReplace(t *testing.T) {
   637  	t.Parallel()
   638  
   639  	loaders := []*deploytest.ProviderLoader{
   640  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   641  			return &deploytest.Provider{
   642  				DiffConfigF: func(urn resource.URN, olds, news resource.PropertyMap,
   643  					ignoreChanges []string) (plugin.DiffResult, error) {
   644  					// Always require replacement.
   645  					keys := []resource.PropertyKey{}
   646  					for k := range news {
   647  						keys = append(keys, k)
   648  					}
   649  					return plugin.DiffResult{ReplaceKeys: keys, DeleteBeforeReplace: true}, nil
   650  				},
   651  			}, nil
   652  		}),
   653  	}
   654  
   655  	providerInputs := resource.PropertyMap{
   656  		resource.PropertyKey("foo"): resource.NewStringProperty("bar"),
   657  	}
   658  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   659  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true,
   660  			deploytest.ResourceOptions{Inputs: providerInputs})
   661  		assert.NoError(t, err)
   662  
   663  		if provID == "" {
   664  			provID = providers.UnknownID
   665  		}
   666  
   667  		provRef, err := providers.NewReference(provURN, provID)
   668  		assert.NoError(t, err)
   669  
   670  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
   671  			Provider: provRef.String(),
   672  		})
   673  		assert.NoError(t, err)
   674  
   675  		return nil
   676  	})
   677  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   678  
   679  	p := &TestPlan{
   680  		Options: UpdateOptions{Host: host},
   681  	}
   682  
   683  	// Build a basic lifecycle.
   684  	steps := MakeBasicLifecycleSteps(t, 2)
   685  
   686  	// Run the lifecycle through its no-op update+refresh.
   687  	p.Steps = steps[:4]
   688  	snap := p.Run(t, nil)
   689  
   690  	// Change the config and run an update. We expect everything to require replacement.
   691  	providerInputs[resource.PropertyKey("foo")] = resource.NewStringProperty("baz")
   692  	p.Steps = []TestStep{{
   693  		Op: Update,
   694  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   695  			_ []Event, res result.Result) result.Result {
   696  
   697  			provURN := p.NewProviderURN("pkgA", "provA", "")
   698  			resURN := p.NewURN("pkgA:m:typA", "resA", "")
   699  
   700  			// Look for replace steps on the provider and the resource.
   701  			createdProvider, createdResource := false, false
   702  			deletedProvider, deletedResource := false, false
   703  			for _, entry := range entries {
   704  				if entry.Kind != JournalEntrySuccess {
   705  					continue
   706  				}
   707  
   708  				switch urn := entry.Step.URN(); urn {
   709  				case provURN:
   710  					if entry.Step.Op() == deploy.OpDeleteReplaced {
   711  						assert.False(t, createdProvider)
   712  						assert.False(t, createdResource)
   713  						assert.True(t, deletedResource)
   714  						deletedProvider = true
   715  					} else if entry.Step.Op() == deploy.OpCreateReplacement {
   716  						assert.True(t, deletedProvider)
   717  						assert.True(t, deletedResource)
   718  						assert.False(t, createdResource)
   719  						createdProvider = true
   720  					}
   721  				case resURN:
   722  					if entry.Step.Op() == deploy.OpDeleteReplaced {
   723  						assert.False(t, deletedProvider)
   724  						assert.False(t, deletedResource)
   725  						deletedResource = true
   726  					} else if entry.Step.Op() == deploy.OpCreateReplacement {
   727  						assert.True(t, deletedProvider)
   728  						assert.True(t, deletedResource)
   729  						assert.True(t, createdProvider)
   730  						createdResource = true
   731  					}
   732  				default:
   733  					t.Fatalf("unexpected resource %v", urn)
   734  				}
   735  			}
   736  			assert.True(t, deletedProvider)
   737  			assert.True(t, deletedResource)
   738  
   739  			return res
   740  		},
   741  	}}
   742  	snap = p.Run(t, snap)
   743  
   744  	// Resume the lifecycle with another no-op update.
   745  	p.Steps = steps[2:]
   746  	p.Run(t, snap)
   747  }
   748  
   749  // TestDefaultProviderDiff tests that the engine can gracefully recover whenever a resource's default provider changes
   750  // and there is no diff in the provider's inputs.
   751  func TestDefaultProviderDiff(t *testing.T) {
   752  	t.Parallel()
   753  
   754  	const resName, resBName = "resA", "resB"
   755  	loaders := []*deploytest.ProviderLoader{
   756  		deploytest.NewProviderLoader("pkgA", semver.MustParse("0.17.10"), func() (plugin.Provider, error) {
   757  			return &deploytest.Provider{}, nil
   758  		}),
   759  		deploytest.NewProviderLoader("pkgA", semver.MustParse("0.17.11"), func() (plugin.Provider, error) {
   760  			return &deploytest.Provider{}, nil
   761  		}),
   762  		deploytest.NewProviderLoader("pkgA", semver.MustParse("0.17.12"), func() (plugin.Provider, error) {
   763  			return &deploytest.Provider{}, nil
   764  		}),
   765  	}
   766  
   767  	runProgram := func(base *deploy.Snapshot, versionA, versionB string, expectedStep display.StepOp) *deploy.Snapshot {
   768  		program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   769  			_, _, _, err := monitor.RegisterResource("pkgA:m:typA", resName, true, deploytest.ResourceOptions{
   770  				Version: versionA,
   771  			})
   772  			assert.NoError(t, err)
   773  			_, _, _, err = monitor.RegisterResource("pkgA:m:typA", resBName, true, deploytest.ResourceOptions{
   774  				Version: versionB,
   775  			})
   776  			assert.NoError(t, err)
   777  			return nil
   778  		})
   779  		host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   780  		p := &TestPlan{
   781  			Options: UpdateOptions{Host: host},
   782  			Steps: []TestStep{
   783  				{
   784  					Op: Update,
   785  					Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   786  						events []Event, res result.Result) result.Result {
   787  						for _, entry := range entries {
   788  							if entry.Kind != JournalEntrySuccess {
   789  								continue
   790  							}
   791  
   792  							switch entry.Step.URN().Name().String() {
   793  							case resName, resBName:
   794  								assert.Equal(t, expectedStep, entry.Step.Op())
   795  							}
   796  						}
   797  						return res
   798  					},
   799  				},
   800  			},
   801  		}
   802  		return p.Run(t, base)
   803  	}
   804  
   805  	// This test simulates the upgrade scenario of old-style default providers to new-style versioned default providers.
   806  	//
   807  	// The first update creates a stack using a language host that does not report a version to the engine. As a result,
   808  	// the engine makes up a default provider for "pkgA" and calls it "default". It then creates the two resources that
   809  	// we are creating and associates them with the default provider.
   810  	snap := runProgram(nil, "", "", deploy.OpCreate)
   811  	for _, res := range snap.Resources {
   812  		switch {
   813  		case providers.IsDefaultProvider(res.URN):
   814  			assert.Equal(t, "default", res.URN.Name().String())
   815  		case res.URN.Name().String() == resName || res.URN.Name().String() == resBName:
   816  			provRef, err := providers.ParseReference(res.Provider)
   817  			assert.NoError(t, err)
   818  			assert.Equal(t, "default", provRef.URN().Name().String())
   819  		}
   820  	}
   821  
   822  	// The second update switches to a language host that does report a version to the engine. As a result, the engine
   823  	// uses this version to make a new provider, with a different URN, and uses that provider to operate on resA and
   824  	// resB.
   825  	//
   826  	// Despite switching out the provider, the engine should still generate a Same step for resA. It is vital that the
   827  	// engine gracefully react to changes in the default provider in this manner. See pulumi/pulumi#2753 for what
   828  	// happens when it doesn't.
   829  	snap = runProgram(snap, "0.17.10", "0.17.10", deploy.OpSame)
   830  	for _, res := range snap.Resources {
   831  		switch {
   832  		case providers.IsDefaultProvider(res.URN):
   833  			assert.Equal(t, "default_0_17_10", res.URN.Name().String())
   834  		case res.URN.Name().String() == resName || res.URN.Name().String() == resBName:
   835  			provRef, err := providers.ParseReference(res.Provider)
   836  			assert.NoError(t, err)
   837  			assert.Equal(t, "default_0_17_10", provRef.URN().Name().String())
   838  		}
   839  	}
   840  
   841  	// The third update changes the version that the language host reports to the engine. This simulates a scenario in
   842  	// which a user updates their SDK to a new version of a provider package. In order to simulate side-by-side
   843  	// packages with different versions, this update requests distinct package versions for resA and resB.
   844  	snap = runProgram(snap, "0.17.11", "0.17.12", deploy.OpSame)
   845  	for _, res := range snap.Resources {
   846  		switch {
   847  		case providers.IsDefaultProvider(res.URN):
   848  			assert.True(t, res.URN.Name().String() == "default_0_17_11" || res.URN.Name().String() == "default_0_17_12")
   849  		case res.URN.Name().String() == resName:
   850  			provRef, err := providers.ParseReference(res.Provider)
   851  			assert.NoError(t, err)
   852  			assert.Equal(t, "default_0_17_11", provRef.URN().Name().String())
   853  		case res.URN.Name().String() == resBName:
   854  			provRef, err := providers.ParseReference(res.Provider)
   855  			assert.NoError(t, err)
   856  			assert.Equal(t, "default_0_17_12", provRef.URN().Name().String())
   857  		}
   858  	}
   859  }
   860  
   861  // TestDefaultProviderDiffReplacement tests that, when replacing a default provider for a resource, the engine will
   862  // replace the resource if DiffConfig on the new provider returns a diff for the provider's new state.
   863  func TestDefaultProviderDiffReplacement(t *testing.T) {
   864  	t.Parallel()
   865  
   866  	const resName, resBName = "resA", "resB"
   867  	loaders := []*deploytest.ProviderLoader{
   868  		deploytest.NewProviderLoader("pkgA", semver.MustParse("0.17.10"), func() (plugin.Provider, error) {
   869  			return &deploytest.Provider{
   870  				// This implementation of DiffConfig always requests replacement.
   871  				DiffConfigF: func(_ resource.URN, olds, news resource.PropertyMap,
   872  					ignoreChanges []string) (plugin.DiffResult, error) {
   873  
   874  					keys := []resource.PropertyKey{}
   875  					for k := range news {
   876  						keys = append(keys, k)
   877  					}
   878  					return plugin.DiffResult{
   879  						Changes:     plugin.DiffSome,
   880  						ReplaceKeys: keys,
   881  					}, nil
   882  				},
   883  			}, nil
   884  		}),
   885  		deploytest.NewProviderLoader("pkgA", semver.MustParse("0.17.11"), func() (plugin.Provider, error) {
   886  			return &deploytest.Provider{}, nil
   887  		}),
   888  	}
   889  
   890  	runProgram := func(base *deploy.Snapshot, versionA, versionB string,
   891  		expectedSteps ...display.StepOp) *deploy.Snapshot {
   892  		program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   893  			_, _, _, err := monitor.RegisterResource("pkgA:m:typA", resName, true, deploytest.ResourceOptions{
   894  				Version: versionA,
   895  			})
   896  			assert.NoError(t, err)
   897  			_, _, _, err = monitor.RegisterResource("pkgA:m:typA", resBName, true, deploytest.ResourceOptions{
   898  				Version: versionB,
   899  			})
   900  			assert.NoError(t, err)
   901  			return nil
   902  		})
   903  		host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   904  		p := &TestPlan{
   905  			Options: UpdateOptions{Host: host},
   906  			Steps: []TestStep{
   907  				{
   908  					Op: Update,
   909  					Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   910  						events []Event, res result.Result) result.Result {
   911  						for _, entry := range entries {
   912  							if entry.Kind != JournalEntrySuccess {
   913  								continue
   914  							}
   915  
   916  							switch entry.Step.URN().Name().String() {
   917  							case resName:
   918  								assert.Subset(t, expectedSteps, []display.StepOp{entry.Step.Op()})
   919  							case resBName:
   920  								assert.Subset(t,
   921  									[]display.StepOp{deploy.OpCreate, deploy.OpSame}, []display.StepOp{entry.Step.Op()})
   922  							}
   923  						}
   924  						return res
   925  					},
   926  				},
   927  			},
   928  		}
   929  		return p.Run(t, base)
   930  	}
   931  
   932  	// This test simulates the upgrade scenario of default providers, except that the requested upgrade results in the
   933  	// provider getting replaced. Because of this, the engine should decide to replace resA. It should not decide to
   934  	// replace resB, as its change does not require replacement.
   935  	snap := runProgram(nil, "", "", deploy.OpCreate)
   936  	for _, res := range snap.Resources {
   937  		switch {
   938  		case providers.IsDefaultProvider(res.URN):
   939  			assert.Equal(t, "default", res.URN.Name().String())
   940  		case res.URN.Name().String() == resName || res.URN.Name().String() == resBName:
   941  			provRef, err := providers.ParseReference(res.Provider)
   942  			assert.NoError(t, err)
   943  			assert.Equal(t, "default", provRef.URN().Name().String())
   944  		}
   945  	}
   946  
   947  	// Upon update, now that the language host is sending a version, DiffConfig reports that there's a diff between the
   948  	// old and new provider and so we must replace resA.
   949  	snap = runProgram(snap, "0.17.10", "0.17.11", deploy.OpCreateReplacement, deploy.OpReplace, deploy.OpDeleteReplaced)
   950  	for _, res := range snap.Resources {
   951  		switch {
   952  		case providers.IsDefaultProvider(res.URN):
   953  			assert.True(t, res.URN.Name().String() == "default_0_17_10" || res.URN.Name().String() == "default_0_17_11")
   954  		case res.URN.Name().String() == resName:
   955  			provRef, err := providers.ParseReference(res.Provider)
   956  			assert.NoError(t, err)
   957  			assert.Equal(t, "default_0_17_10", provRef.URN().Name().String())
   958  		case res.URN.Name().String() == resBName:
   959  			provRef, err := providers.ParseReference(res.Provider)
   960  			assert.NoError(t, err)
   961  			assert.Equal(t, "default_0_17_11", provRef.URN().Name().String())
   962  		}
   963  	}
   964  }
   965  
   966  func TestProviderVersionDefault(t *testing.T) {
   967  	t.Parallel()
   968  
   969  	version := ""
   970  	loaders := []*deploytest.ProviderLoader{
   971  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   972  			version = "1.0.0"
   973  			return &deploytest.Provider{}, nil
   974  		}),
   975  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.5.0"), func() (plugin.Provider, error) {
   976  			version = "1.5.0"
   977  			return &deploytest.Provider{}, nil
   978  		}),
   979  	}
   980  
   981  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   982  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true)
   983  		assert.NoError(t, err)
   984  
   985  		if provID == "" {
   986  			provID = providers.UnknownID
   987  		}
   988  
   989  		provRef, err := providers.NewReference(provURN, provID)
   990  		assert.NoError(t, err)
   991  
   992  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
   993  			Provider: provRef.String(),
   994  		})
   995  		assert.NoError(t, err)
   996  
   997  		return nil
   998  	})
   999  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1000  
  1001  	p := &TestPlan{
  1002  		Options: UpdateOptions{Host: host},
  1003  		Steps:   MakeBasicLifecycleSteps(t, 2),
  1004  	}
  1005  	p.Run(t, nil)
  1006  
  1007  	assert.Equal(t, "1.5.0", version)
  1008  }
  1009  
  1010  func TestProviderVersionOption(t *testing.T) {
  1011  	t.Parallel()
  1012  
  1013  	version := ""
  1014  	loaders := []*deploytest.ProviderLoader{
  1015  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1016  			version = "1.0.0"
  1017  			return &deploytest.Provider{}, nil
  1018  		}),
  1019  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.5.0"), func() (plugin.Provider, error) {
  1020  			version = "1.5.0"
  1021  			return &deploytest.Provider{}, nil
  1022  		}),
  1023  	}
  1024  
  1025  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1026  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true,
  1027  			deploytest.ResourceOptions{
  1028  				Version: "1.0.0",
  1029  			})
  1030  		assert.NoError(t, err)
  1031  
  1032  		if provID == "" {
  1033  			provID = providers.UnknownID
  1034  		}
  1035  
  1036  		provRef, err := providers.NewReference(provURN, provID)
  1037  		assert.NoError(t, err)
  1038  
  1039  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1040  			Provider: provRef.String(),
  1041  		})
  1042  		assert.NoError(t, err)
  1043  
  1044  		return nil
  1045  	})
  1046  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1047  
  1048  	p := &TestPlan{
  1049  		Options: UpdateOptions{Host: host},
  1050  		Steps:   MakeBasicLifecycleSteps(t, 2),
  1051  	}
  1052  	p.Run(t, nil)
  1053  
  1054  	assert.Equal(t, "1.0.0", version)
  1055  }
  1056  
  1057  func TestProviderVersionInput(t *testing.T) {
  1058  	t.Parallel()
  1059  
  1060  	version := ""
  1061  	loaders := []*deploytest.ProviderLoader{
  1062  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1063  			version = "1.0.0"
  1064  			return &deploytest.Provider{}, nil
  1065  		}),
  1066  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.5.0"), func() (plugin.Provider, error) {
  1067  			version = "1.5.0"
  1068  			return &deploytest.Provider{}, nil
  1069  		}),
  1070  	}
  1071  
  1072  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1073  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true,
  1074  			deploytest.ResourceOptions{
  1075  				Inputs: resource.PropertyMap{
  1076  					"version": resource.NewStringProperty("1.0.0"),
  1077  				},
  1078  			})
  1079  		assert.NoError(t, err)
  1080  
  1081  		if provID == "" {
  1082  			provID = providers.UnknownID
  1083  		}
  1084  
  1085  		provRef, err := providers.NewReference(provURN, provID)
  1086  		assert.NoError(t, err)
  1087  
  1088  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1089  			Provider: provRef.String(),
  1090  		})
  1091  		assert.NoError(t, err)
  1092  
  1093  		return nil
  1094  	})
  1095  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1096  
  1097  	p := &TestPlan{
  1098  		Options: UpdateOptions{Host: host},
  1099  		Steps:   MakeBasicLifecycleSteps(t, 2),
  1100  	}
  1101  	p.Run(t, nil)
  1102  
  1103  	assert.Equal(t, "1.0.0", version)
  1104  }
  1105  
  1106  func TestProviderVersionInputAndOption(t *testing.T) {
  1107  	t.Parallel()
  1108  
  1109  	version := ""
  1110  	loaders := []*deploytest.ProviderLoader{
  1111  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1112  			version = "1.0.0"
  1113  			return &deploytest.Provider{}, nil
  1114  		}),
  1115  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.5.0"), func() (plugin.Provider, error) {
  1116  			version = "1.5.0"
  1117  			return &deploytest.Provider{}, nil
  1118  		}),
  1119  	}
  1120  
  1121  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1122  		provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true,
  1123  			deploytest.ResourceOptions{
  1124  				Inputs: resource.PropertyMap{
  1125  					"version": resource.NewStringProperty("1.5.0"),
  1126  				},
  1127  				Version: "1.0.0",
  1128  			})
  1129  		assert.NoError(t, err)
  1130  
  1131  		if provID == "" {
  1132  			provID = providers.UnknownID
  1133  		}
  1134  
  1135  		provRef, err := providers.NewReference(provURN, provID)
  1136  		assert.NoError(t, err)
  1137  
  1138  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1139  			Provider: provRef.String(),
  1140  		})
  1141  		assert.NoError(t, err)
  1142  
  1143  		return nil
  1144  	})
  1145  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1146  
  1147  	p := &TestPlan{
  1148  		Options: UpdateOptions{Host: host},
  1149  		Steps:   MakeBasicLifecycleSteps(t, 2),
  1150  	}
  1151  	p.Run(t, nil)
  1152  
  1153  	assert.Equal(t, "1.0.0", version)
  1154  }
  1155  
  1156  func TestPluginDownloadURLPassthrough(t *testing.T) {
  1157  	t.Parallel()
  1158  
  1159  	loaders := []*deploytest.ProviderLoader{
  1160  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1161  			return &deploytest.Provider{}, nil
  1162  		}),
  1163  	}
  1164  
  1165  	pkgAPluginDownloadURL := "get.pulumi.com/${VERSION}"
  1166  	pkgAType := providers.MakeProviderType("pkgA")
  1167  
  1168  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1169  		provURN, provID, _, err := monitor.RegisterResource(pkgAType, "provA", true, deploytest.ResourceOptions{
  1170  			PluginDownloadURL: pkgAPluginDownloadURL,
  1171  		})
  1172  		assert.NoError(t, err)
  1173  
  1174  		if provID == "" {
  1175  			provID = providers.UnknownID
  1176  		}
  1177  
  1178  		provRef, err := providers.NewReference(provURN, provID)
  1179  		assert.NoError(t, err)
  1180  
  1181  		_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1182  			Provider: provRef.String(),
  1183  		})
  1184  		assert.NoError(t, err)
  1185  
  1186  		return nil
  1187  	})
  1188  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1189  
  1190  	steps := MakeBasicLifecycleSteps(t, 2)
  1191  	steps[0].ValidateAnd(func(project workspace.Project, target deploy.Target, entries JournalEntries,
  1192  		_ []Event, res result.Result) result.Result {
  1193  
  1194  		for _, e := range entries {
  1195  			r := e.Step.New()
  1196  			if r.Type == pkgAType && r.Inputs["pluginDownloadURL"].StringValue() != pkgAPluginDownloadURL {
  1197  				return result.Errorf("Found unexpected value %v", r.Inputs["pluginDownloadURL"])
  1198  			}
  1199  		}
  1200  		return nil
  1201  	})
  1202  	p := &TestPlan{
  1203  		Options: UpdateOptions{Host: host},
  1204  		Steps:   steps,
  1205  	}
  1206  	p.Run(t, nil)
  1207  }
  1208  
  1209  // Check that creating a resource with pluginDownloadURL set will instantiate a default provider with
  1210  // pluginDownloadURL set.
  1211  func TestPluginDownloadURLDefaultProvider(t *testing.T) {
  1212  	t.Parallel()
  1213  
  1214  	loaders := []*deploytest.ProviderLoader{
  1215  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1216  			return &deploytest.Provider{}, nil
  1217  		}),
  1218  	}
  1219  	url := "get.pulumi.com"
  1220  
  1221  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1222  		_, _, _, err := monitor.RegisterResource("pkgA::Foo", "foo", true, deploytest.ResourceOptions{
  1223  			PluginDownloadURL: url,
  1224  		})
  1225  		return err
  1226  	})
  1227  
  1228  	snapshot := (&TestPlan{
  1229  		Options: UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)},
  1230  		// The first step is the update. We don't want the full lifecycle because we want to see the
  1231  		// created resources.
  1232  		Steps: MakeBasicLifecycleSteps(t, 2)[:1],
  1233  	}).Run(t, nil)
  1234  
  1235  	foundDefaultProvider := false
  1236  	for _, r := range snapshot.Resources {
  1237  		if providers.IsDefaultProvider(r.URN) {
  1238  			actualURL, err := providers.GetProviderDownloadURL(r.Inputs)
  1239  			assert.NoError(t, err)
  1240  			assert.Equal(t, url, actualURL)
  1241  			foundDefaultProvider = true
  1242  		}
  1243  	}
  1244  	assert.Truef(t, foundDefaultProvider, "Found resources: %#v", snapshot.Resources)
  1245  }
  1246  
  1247  func TestMultipleResourceDenyDefaultProviderLifecycle(t *testing.T) {
  1248  	t.Parallel()
  1249  
  1250  	cases := []struct {
  1251  		name       string
  1252  		f          deploytest.ProgramFunc
  1253  		disabled   string
  1254  		expectFail bool
  1255  	}{
  1256  		{
  1257  			name: "default-blocked",
  1258  			f: func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1259  				_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
  1260  				assert.NoError(t, err)
  1261  				_, _, _, err = monitor.RegisterResource("pkgB:m:typB", "resB", true)
  1262  				assert.NoError(t, err)
  1263  
  1264  				return nil
  1265  			},
  1266  			disabled:   `["pkgA"]`,
  1267  			expectFail: true,
  1268  		},
  1269  		{
  1270  			name: "explicit-not-blocked",
  1271  			f: func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1272  				provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true)
  1273  				assert.NoError(t, err)
  1274  				provRef, err := providers.NewReference(provURN, provID)
  1275  				assert.NoError(t, err)
  1276  
  1277  				_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1278  					Provider: provRef.String(),
  1279  				})
  1280  				assert.NoError(t, err)
  1281  
  1282  				_, _, _, err = monitor.RegisterResource("pkgB:m:typB", "resB", true)
  1283  				assert.NoError(t, err)
  1284  
  1285  				return nil
  1286  			},
  1287  			disabled:   `["pkgA"]`,
  1288  			expectFail: false,
  1289  		},
  1290  		{
  1291  			name: "wildcard",
  1292  			f: func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1293  				_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
  1294  				assert.NoError(t, err)
  1295  				_, _, _, err = monitor.RegisterResource("pkgB:m:typB", "resB", true)
  1296  				assert.NoError(t, err)
  1297  
  1298  				return nil
  1299  			},
  1300  			disabled:   `["*"]`,
  1301  			expectFail: true,
  1302  		},
  1303  	}
  1304  	for _, tt := range cases {
  1305  		tt := tt
  1306  		t.Run(tt.name, func(t *testing.T) {
  1307  			t.Parallel()
  1308  
  1309  			loaders := []*deploytest.ProviderLoader{
  1310  				deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1311  					return &deploytest.Provider{}, nil
  1312  				}),
  1313  				deploytest.NewProviderLoader("pkgB", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1314  					return &deploytest.Provider{}, nil
  1315  				}),
  1316  			}
  1317  
  1318  			program := deploytest.NewLanguageRuntime(tt.f)
  1319  			host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1320  
  1321  			c := config.Map{}
  1322  			k := config.MustMakeKey("pulumi", "disable-default-providers")
  1323  			c[k] = config.NewValue(tt.disabled)
  1324  
  1325  			expectedCreated := 4
  1326  			if tt.expectFail {
  1327  				expectedCreated = 0
  1328  			}
  1329  			update := MakeBasicLifecycleSteps(t, expectedCreated)[:1]
  1330  			update[0].ExpectFailure = tt.expectFail
  1331  			p := &TestPlan{
  1332  				Options: UpdateOptions{Host: host},
  1333  				Steps:   update,
  1334  				Config:  c,
  1335  			}
  1336  			p.Run(t, nil)
  1337  		})
  1338  	}
  1339  }
  1340  
  1341  func TestProviderVersionAssignment(t *testing.T) {
  1342  	t.Parallel()
  1343  
  1344  	prog := func(opts ...deploytest.ResourceOptions) deploytest.ProgramFunc {
  1345  
  1346  		return func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1347  			_, _, _, err := monitor.RegisterResource("pkgA:r:typA", "resA", true, opts...)
  1348  			if err != nil {
  1349  				return err
  1350  			}
  1351  			_, _, _, err = monitor.RegisterResource("pulumi:providers:pkgA", "provA", true, opts...)
  1352  			if err != nil {
  1353  				return err
  1354  			}
  1355  			return nil
  1356  		}
  1357  	}
  1358  
  1359  	cases := []struct {
  1360  		name     string
  1361  		plugins  []workspace.PluginSpec
  1362  		snapshot *deploy.Snapshot
  1363  		validate func(t *testing.T, r *resource.State)
  1364  		versions []string
  1365  		prog     deploytest.ProgramFunc
  1366  	}{
  1367  		{
  1368  			name:     "empty",
  1369  			versions: []string{"1.0.0"},
  1370  			validate: func(*testing.T, *resource.State) {},
  1371  			prog:     prog(),
  1372  		},
  1373  		{
  1374  			name:     "default-version",
  1375  			versions: []string{"1.0.0", "1.1.0"},
  1376  			plugins: []workspace.PluginSpec{{
  1377  				Name:              "pkgA",
  1378  				Version:           &semver.Version{Major: 1, Minor: 1},
  1379  				PluginDownloadURL: "example.com/default",
  1380  				Kind:              workspace.ResourcePlugin,
  1381  			}},
  1382  			validate: func(t *testing.T, r *resource.State) {
  1383  				if providers.IsProviderType(r.Type) && !providers.IsDefaultProvider(r.URN) {
  1384  					assert.Equal(t, r.Inputs["version"].StringValue(), "1.1.0")
  1385  					assert.Equal(t, r.Inputs["pluginDownloadURL"].StringValue(), "example.com/default")
  1386  				}
  1387  			},
  1388  			prog: prog(),
  1389  		},
  1390  		{
  1391  			name:     "specified-provider",
  1392  			versions: []string{"1.0.0", "1.1.0"},
  1393  			plugins: []workspace.PluginSpec{{
  1394  				Name:    "pkgA",
  1395  				Version: &semver.Version{Major: 1, Minor: 1},
  1396  				Kind:    workspace.ResourcePlugin,
  1397  			}},
  1398  			validate: func(t *testing.T, r *resource.State) {
  1399  				if providers.IsProviderType(r.Type) && !providers.IsDefaultProvider(r.URN) {
  1400  					_, hasVersion := r.Inputs["version"]
  1401  					assert.False(t, hasVersion)
  1402  					assert.Equal(t, r.Inputs["pluginDownloadURL"].StringValue(), "example.com/download")
  1403  				}
  1404  			},
  1405  			prog: prog(deploytest.ResourceOptions{PluginDownloadURL: "example.com/download"}),
  1406  		},
  1407  		{
  1408  			name:     "higher-in-snapshot",
  1409  			versions: []string{"1.3.0", "1.1.0"},
  1410  			prog:     prog(),
  1411  			plugins: []workspace.PluginSpec{{
  1412  				Name:    "pkgA",
  1413  				Version: &semver.Version{Major: 1, Minor: 1},
  1414  				Kind:    workspace.ResourcePlugin,
  1415  			}},
  1416  			snapshot: &deploy.Snapshot{
  1417  				Resources: []*resource.State{
  1418  					{
  1419  						Type: "providers:pulumi:pkgA",
  1420  						URN:  "this:is:a:urn::ofaei",
  1421  						Inputs: map[resource.PropertyKey]resource.PropertyValue{
  1422  							"version": resource.NewPropertyValue("1.3.0"),
  1423  						},
  1424  					},
  1425  				},
  1426  			},
  1427  			validate: func(t *testing.T, r *resource.State) {
  1428  				if providers.IsProviderType(r.Type) && !providers.IsDefaultProvider(r.URN) {
  1429  					assert.Equal(t, r.Inputs["version"].StringValue(), "1.1.0")
  1430  				}
  1431  			},
  1432  		},
  1433  	}
  1434  	for _, c := range cases {
  1435  		c := c
  1436  		t.Run(c.name, func(t *testing.T) {
  1437  			t.Parallel()
  1438  			program := deploytest.NewLanguageRuntime(c.prog, c.plugins...)
  1439  			loaders := []*deploytest.ProviderLoader{}
  1440  			for _, v := range c.versions {
  1441  				loaders = append(loaders,
  1442  					deploytest.NewProviderLoader("pkgA", semver.MustParse(v), func() (plugin.Provider, error) {
  1443  						return &deploytest.Provider{}, nil
  1444  					}))
  1445  			}
  1446  			host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1447  
  1448  			update := []TestStep{{Op: Update, Validate: func(
  1449  				project workspace.Project, target deploy.Target, entries JournalEntries,
  1450  				events []Event, res result.Result) result.Result {
  1451  				snap, err := entries.Snap(target.Snapshot)
  1452  				require.NoError(t, err)
  1453  				assert.Len(t, snap.Resources, 3)
  1454  				for _, r := range snap.Resources {
  1455  					c.validate(t, r)
  1456  				}
  1457  				return nil
  1458  			}}}
  1459  
  1460  			p := &TestPlan{
  1461  				Options: UpdateOptions{Host: host},
  1462  				Steps:   update,
  1463  			}
  1464  			p.Run(t, &deploy.Snapshot{})
  1465  		})
  1466  	}
  1467  }