github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/providers/registry_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 providers
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"github.com/blang/semver"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
    27  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    28  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
    29  	"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
    30  	"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
    31  )
    32  
    33  type testPluginHost struct {
    34  	t             *testing.T
    35  	provider      func(pkg tokens.Package, version *semver.Version) (plugin.Provider, error)
    36  	closeProvider func(provider plugin.Provider) error
    37  }
    38  
    39  func (host *testPluginHost) SignalCancellation() error {
    40  	return nil
    41  }
    42  func (host *testPluginHost) Close() error {
    43  	return nil
    44  }
    45  func (host *testPluginHost) ServerAddr() string {
    46  	host.t.Fatalf("Host RPC address not available")
    47  	return ""
    48  }
    49  func (host *testPluginHost) Log(sev diag.Severity, urn resource.URN, msg string, streamID int32) {
    50  	host.t.Logf("[%v] %v@%v: %v", sev, urn, streamID, msg)
    51  }
    52  func (host *testPluginHost) LogStatus(sev diag.Severity, urn resource.URN, msg string, streamID int32) {
    53  	host.t.Logf("[%v] %v@%v: %v", sev, urn, streamID, msg)
    54  }
    55  func (host *testPluginHost) Analyzer(nm tokens.QName) (plugin.Analyzer, error) {
    56  	return nil, errors.New("unsupported")
    57  }
    58  func (host *testPluginHost) PolicyAnalyzer(name tokens.QName, path string,
    59  	opts *plugin.PolicyAnalyzerOptions) (plugin.Analyzer, error) {
    60  	return nil, errors.New("unsupported")
    61  }
    62  func (host *testPluginHost) ListAnalyzers() []plugin.Analyzer {
    63  	return nil
    64  }
    65  func (host *testPluginHost) Provider(pkg tokens.Package, version *semver.Version) (plugin.Provider, error) {
    66  	return host.provider(pkg, version)
    67  }
    68  func (host *testPluginHost) CloseProvider(provider plugin.Provider) error {
    69  	return host.closeProvider(provider)
    70  }
    71  func (host *testPluginHost) LanguageRuntime(runtime string) (plugin.LanguageRuntime, error) {
    72  	return nil, errors.New("unsupported")
    73  }
    74  func (host *testPluginHost) EnsurePlugins(plugins []workspace.PluginSpec, kinds plugin.Flags) error {
    75  	return nil
    76  }
    77  func (host *testPluginHost) InstallPlugin(plugin workspace.PluginSpec) error {
    78  	return nil
    79  }
    80  func (host *testPluginHost) ResolvePlugin(
    81  	kind workspace.PluginKind, name string, version *semver.Version) (*workspace.PluginInfo, error) {
    82  	return nil, nil
    83  }
    84  
    85  func (host *testPluginHost) GetProjectPlugins() []workspace.ProjectPlugin {
    86  	return nil
    87  }
    88  
    89  func (host *testPluginHost) GetRequiredPlugins(info plugin.ProgInfo,
    90  	kinds plugin.Flags) ([]workspace.PluginInfo, error) {
    91  	return nil, nil
    92  }
    93  
    94  type testProvider struct {
    95  	pkg         tokens.Package
    96  	version     semver.Version
    97  	configured  bool
    98  	checkConfig func(resource.URN, resource.PropertyMap,
    99  		resource.PropertyMap, bool) (resource.PropertyMap, []plugin.CheckFailure, error)
   100  	diffConfig func(resource.URN, resource.PropertyMap, resource.PropertyMap, bool, []string) (plugin.DiffResult, error)
   101  	config     func(resource.PropertyMap) error
   102  }
   103  
   104  func (prov *testProvider) SignalCancellation() error {
   105  	return nil
   106  }
   107  func (prov *testProvider) Close() error {
   108  	return nil
   109  }
   110  func (prov *testProvider) Pkg() tokens.Package {
   111  	return prov.pkg
   112  }
   113  func (prov *testProvider) GetSchema(version int) ([]byte, error) {
   114  	return []byte("{}"), nil
   115  }
   116  func (prov *testProvider) CheckConfig(urn resource.URN, olds,
   117  	news resource.PropertyMap, allowUnknowns bool) (resource.PropertyMap, []plugin.CheckFailure, error) {
   118  	return prov.checkConfig(urn, olds, news, allowUnknowns)
   119  }
   120  func (prov *testProvider) DiffConfig(urn resource.URN, olds, news resource.PropertyMap,
   121  	allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) {
   122  	return prov.diffConfig(urn, olds, news, allowUnknowns, ignoreChanges)
   123  }
   124  func (prov *testProvider) Configure(inputs resource.PropertyMap) error {
   125  	if err := prov.config(inputs); err != nil {
   126  		return err
   127  	}
   128  	prov.configured = true
   129  	return nil
   130  }
   131  func (prov *testProvider) Check(urn resource.URN,
   132  	olds, news resource.PropertyMap, _ bool, _ []byte) (resource.PropertyMap, []plugin.CheckFailure, error) {
   133  	return nil, nil, errors.New("unsupported")
   134  }
   135  func (prov *testProvider) Create(urn resource.URN, props resource.PropertyMap, timeout float64,
   136  	preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
   137  	return "", nil, resource.StatusOK, errors.New("unsupported")
   138  }
   139  func (prov *testProvider) Read(urn resource.URN, id resource.ID,
   140  	inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error) {
   141  	return plugin.ReadResult{}, resource.StatusUnknown, errors.New("unsupported")
   142  }
   143  func (prov *testProvider) Diff(urn resource.URN, id resource.ID,
   144  	olds resource.PropertyMap, news resource.PropertyMap, _ bool, _ []string) (plugin.DiffResult, error) {
   145  	return plugin.DiffResult{}, errors.New("unsupported")
   146  }
   147  func (prov *testProvider) Update(urn resource.URN, id resource.ID,
   148  	olds resource.PropertyMap, news resource.PropertyMap, timeout float64,
   149  	ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
   150  	return nil, resource.StatusOK, errors.New("unsupported")
   151  }
   152  func (prov *testProvider) Delete(urn resource.URN,
   153  	id resource.ID, props resource.PropertyMap, timeout float64) (resource.Status, error) {
   154  	return resource.StatusOK, errors.New("unsupported")
   155  }
   156  func (prov *testProvider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
   157  	inputs resource.PropertyMap, options plugin.ConstructOptions) (plugin.ConstructResult, error) {
   158  	return plugin.ConstructResult{}, errors.New("unsupported")
   159  }
   160  func (prov *testProvider) Invoke(tok tokens.ModuleMember,
   161  	args resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) {
   162  	return nil, nil, errors.New("unsupported")
   163  }
   164  func (prov *testProvider) StreamInvoke(
   165  	tok tokens.ModuleMember, args resource.PropertyMap,
   166  	onNext func(resource.PropertyMap) error) ([]plugin.CheckFailure, error) {
   167  
   168  	return nil, fmt.Errorf("not implemented")
   169  }
   170  func (prov *testProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo,
   171  	options plugin.CallOptions) (plugin.CallResult, error) {
   172  	return plugin.CallResult{}, errors.New("unsupported")
   173  }
   174  func (prov *testProvider) GetPluginInfo() (workspace.PluginInfo, error) {
   175  	return workspace.PluginInfo{
   176  		Name:    "testProvider",
   177  		Version: &prov.version,
   178  	}, nil
   179  }
   180  
   181  type providerLoader struct {
   182  	pkg     tokens.Package
   183  	version semver.Version
   184  	load    func() (plugin.Provider, error)
   185  }
   186  
   187  func newPluginHost(t *testing.T, loaders []*providerLoader) plugin.Host {
   188  	return &testPluginHost{
   189  		t: t,
   190  		provider: func(pkg tokens.Package, ver *semver.Version) (plugin.Provider, error) {
   191  			var best *providerLoader
   192  			for _, l := range loaders {
   193  				if l.pkg != pkg {
   194  					continue
   195  				}
   196  
   197  				if ver != nil && l.version.LT(*ver) {
   198  					continue
   199  				}
   200  				if best == nil || l.version.GT(best.version) {
   201  					best = l
   202  				}
   203  			}
   204  			if best == nil {
   205  				return nil, nil
   206  			}
   207  			return best.load()
   208  		},
   209  		closeProvider: func(provider plugin.Provider) error {
   210  			return nil
   211  		},
   212  	}
   213  }
   214  
   215  func newLoader(t *testing.T, pkg, version string,
   216  	load func(tokens.Package, semver.Version) (plugin.Provider, error)) *providerLoader {
   217  
   218  	var ver semver.Version
   219  	if version != "" {
   220  		v, err := semver.ParseTolerant(version)
   221  		assert.NoError(t, err)
   222  		ver = v
   223  	}
   224  	return &providerLoader{
   225  		pkg:     tokens.Package(pkg),
   226  		version: ver,
   227  		load: func() (plugin.Provider, error) {
   228  			return load(tokens.Package(pkg), ver)
   229  		},
   230  	}
   231  }
   232  
   233  func newSimpleLoader(t *testing.T, pkg, version string, config func(resource.PropertyMap) error) *providerLoader {
   234  	if config == nil {
   235  		config = func(resource.PropertyMap) error {
   236  			return nil
   237  		}
   238  	}
   239  	return newLoader(t, pkg, version, func(pkg tokens.Package, ver semver.Version) (plugin.Provider, error) {
   240  		return &testProvider{
   241  			pkg:     pkg,
   242  			version: ver,
   243  			checkConfig: func(urn resource.URN, olds,
   244  				news resource.PropertyMap, allowUnknowns bool) (resource.PropertyMap, []plugin.CheckFailure, error) {
   245  				return news, nil, nil
   246  			},
   247  			diffConfig: func(urn resource.URN, olds, news resource.PropertyMap,
   248  				allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) {
   249  				return plugin.DiffResult{}, nil
   250  			},
   251  			config: config,
   252  		}, nil
   253  	})
   254  }
   255  
   256  func newProviderState(pkg, name, id string, delete bool, inputs resource.PropertyMap) *resource.State {
   257  	typ := MakeProviderType(tokens.Package(pkg))
   258  	urn := resource.NewURN("test", "test", "", typ, tokens.QName(name))
   259  	if inputs == nil {
   260  		inputs = resource.PropertyMap{}
   261  	}
   262  	return &resource.State{
   263  		Type:   typ,
   264  		URN:    urn,
   265  		Custom: true,
   266  		Delete: delete,
   267  		ID:     resource.ID(id),
   268  		Inputs: inputs,
   269  	}
   270  }
   271  
   272  func TestNewRegistryNoOldState(t *testing.T) {
   273  	t.Parallel()
   274  
   275  	r, err := NewRegistry(&testPluginHost{}, nil, false, nil)
   276  	assert.NoError(t, err)
   277  	assert.NotNil(t, r)
   278  
   279  	r, err = NewRegistry(&testPluginHost{}, nil, true, nil)
   280  	assert.NoError(t, err)
   281  	assert.NotNil(t, r)
   282  }
   283  
   284  func TestNewRegistryOldState(t *testing.T) {
   285  	t.Parallel()
   286  
   287  	olds := []*resource.State{
   288  		// Two providers from package A, each with a unique name and ID
   289  		newProviderState("pkgA", "a", "id1", false, nil),
   290  		newProviderState("pkgA", "b", "id2", false, nil),
   291  		// Two providers from package B, each with a unique name and ID
   292  		newProviderState("pkgB", "a", "id1", false, nil),
   293  		newProviderState("pkgB", "b", "id2", false, nil),
   294  		// Two providers from package C, both with the same name but with unique IDs and one marked for deletion
   295  		newProviderState("pkgC", "a", "id1", false, nil),
   296  		newProviderState("pkgC", "a", "id2", true, nil),
   297  		// One provider from package D with a version
   298  		newProviderState("pkgD", "a", "id1", false, resource.PropertyMap{
   299  			"version": resource.NewStringProperty("1.0.0"),
   300  		}),
   301  	}
   302  	loaders := []*providerLoader{
   303  		newSimpleLoader(t, "pkgA", "", nil),
   304  		newSimpleLoader(t, "pkgB", "", nil),
   305  		newSimpleLoader(t, "pkgC", "", nil),
   306  		newSimpleLoader(t, "pkgD", "1.0.0", nil),
   307  	}
   308  	host := newPluginHost(t, loaders)
   309  
   310  	r, err := NewRegistry(host, olds, false, nil)
   311  	assert.NoError(t, err)
   312  	assert.NotNil(t, r)
   313  
   314  	assert.Equal(t, len(olds), len(r.providers))
   315  
   316  	for _, old := range olds {
   317  		ref, err := NewReference(old.URN, old.ID)
   318  		assert.NoError(t, err)
   319  
   320  		p, ok := r.GetProvider(ref)
   321  		assert.True(t, ok)
   322  		assert.NotNil(t, p)
   323  
   324  		assert.True(t, p.(*testProvider).configured)
   325  
   326  		assert.Equal(t, GetProviderPackage(old.Type), p.Pkg())
   327  
   328  		ver, err := GetProviderVersion(old.Inputs)
   329  		assert.NoError(t, err)
   330  		if ver != nil {
   331  			info, err := p.GetPluginInfo()
   332  			assert.NoError(t, err)
   333  			assert.True(t, info.Version.GTE(*ver))
   334  		}
   335  	}
   336  }
   337  
   338  func TestNewRegistryOldStateNoProviders(t *testing.T) {
   339  	t.Parallel()
   340  
   341  	olds := []*resource.State{
   342  		newProviderState("pkgA", "a", "id1", false, nil),
   343  	}
   344  	host := newPluginHost(t, []*providerLoader{})
   345  
   346  	r, err := NewRegistry(host, olds, false, nil)
   347  	assert.Error(t, err)
   348  	assert.Nil(t, r)
   349  }
   350  
   351  func TestNewRegistryOldStateWrongPackage(t *testing.T) {
   352  	t.Parallel()
   353  
   354  	olds := []*resource.State{
   355  		newProviderState("pkgA", "a", "id1", false, nil),
   356  	}
   357  	loaders := []*providerLoader{
   358  		newSimpleLoader(t, "pkgB", "", nil),
   359  	}
   360  	host := newPluginHost(t, loaders)
   361  
   362  	r, err := NewRegistry(host, olds, false, nil)
   363  	assert.Error(t, err)
   364  	assert.Nil(t, r)
   365  }
   366  
   367  func TestNewRegistryOldStateWrongVersion(t *testing.T) {
   368  	t.Parallel()
   369  
   370  	olds := []*resource.State{
   371  		newProviderState("pkgA", "a", "id1", false, resource.PropertyMap{
   372  			"version": resource.NewStringProperty("1.0.0"),
   373  		}),
   374  	}
   375  	loaders := []*providerLoader{
   376  		newSimpleLoader(t, "pkgA", "0.5.0", nil),
   377  	}
   378  	host := newPluginHost(t, loaders)
   379  
   380  	r, err := NewRegistry(host, olds, false, nil)
   381  	assert.Error(t, err)
   382  	assert.Nil(t, r)
   383  }
   384  
   385  func TestNewRegistryOldStateNoID(t *testing.T) {
   386  	t.Parallel()
   387  
   388  	olds := []*resource.State{
   389  		newProviderState("pkgA", "a", "", false, nil),
   390  	}
   391  	loaders := []*providerLoader{
   392  		newSimpleLoader(t, "pkgA", "", nil),
   393  	}
   394  	host := newPluginHost(t, loaders)
   395  
   396  	r, err := NewRegistry(host, olds, false, nil)
   397  	assert.Error(t, err)
   398  	assert.Nil(t, r)
   399  }
   400  
   401  func TestNewRegistryOldStateUnknownID(t *testing.T) {
   402  	t.Parallel()
   403  
   404  	olds := []*resource.State{
   405  		newProviderState("pkgA", "a", UnknownID, false, nil),
   406  	}
   407  	loaders := []*providerLoader{
   408  		newSimpleLoader(t, "pkgA", "", nil),
   409  	}
   410  	host := newPluginHost(t, loaders)
   411  
   412  	r, err := NewRegistry(host, olds, false, nil)
   413  	assert.Error(t, err)
   414  	assert.Nil(t, r)
   415  }
   416  
   417  func TestNewRegistryOldStateDuplicates(t *testing.T) {
   418  	t.Parallel()
   419  
   420  	olds := []*resource.State{
   421  		newProviderState("pkgA", "a", "id1", false, nil),
   422  		newProviderState("pkgA", "a", "id1", false, nil),
   423  	}
   424  	loaders := []*providerLoader{
   425  		newSimpleLoader(t, "pkgA", "", nil),
   426  	}
   427  	host := newPluginHost(t, loaders)
   428  
   429  	r, err := NewRegistry(host, olds, false, nil)
   430  	assert.Error(t, err)
   431  	assert.Nil(t, r)
   432  }
   433  
   434  func TestCRUD(t *testing.T) {
   435  	t.Parallel()
   436  
   437  	olds := []*resource.State{
   438  		newProviderState("pkgA", "a", "id1", false, nil),
   439  		newProviderState("pkgB", "a", "id1", false, nil),
   440  		newProviderState("pkgC", "a", "id1", false, nil),
   441  	}
   442  	loaders := []*providerLoader{
   443  		newSimpleLoader(t, "pkgA", "", nil),
   444  		newSimpleLoader(t, "pkgB", "", nil),
   445  		newSimpleLoader(t, "pkgC", "", nil),
   446  	}
   447  	host := newPluginHost(t, loaders)
   448  
   449  	r, err := NewRegistry(host, olds, false, nil)
   450  	assert.NoError(t, err)
   451  	assert.NotNil(t, r)
   452  
   453  	assert.Equal(t, len(olds), len(r.providers))
   454  
   455  	for _, old := range olds {
   456  		ref, err := NewReference(old.URN, old.ID)
   457  		assert.NoError(t, err)
   458  
   459  		p, ok := r.GetProvider(ref)
   460  		assert.True(t, ok)
   461  		assert.NotNil(t, p)
   462  
   463  		assert.Equal(t, GetProviderPackage(old.Type), p.Pkg())
   464  	}
   465  
   466  	// Create a new provider for each package.
   467  	for _, l := range loaders {
   468  		typ := MakeProviderType(l.pkg)
   469  		urn := resource.NewURN("test", "test", "", typ, "b")
   470  		olds, news := resource.PropertyMap{}, resource.PropertyMap{}
   471  		timeout := float64(120)
   472  
   473  		// Check
   474  		inputs, failures, err := r.Check(urn, olds, news, false, nil)
   475  		assert.NoError(t, err)
   476  		assert.Equal(t, news, inputs)
   477  		assert.Empty(t, failures)
   478  
   479  		// Since this is not a preview, the provider should not yet be configured.
   480  		p, ok := r.GetProvider(Reference{urn: urn, id: UnknownID})
   481  		assert.True(t, ok)
   482  		assert.False(t, p.(*testProvider).configured)
   483  
   484  		// Create
   485  		id, outs, status, err := r.Create(urn, inputs, timeout, false)
   486  		assert.NoError(t, err)
   487  		assert.NotEqual(t, "", id)
   488  		assert.NotEqual(t, UnknownID, id)
   489  		assert.Equal(t, resource.PropertyMap{}, outs)
   490  		assert.Equal(t, resource.StatusOK, status)
   491  
   492  		p2, ok := r.GetProvider(Reference{urn: urn, id: id})
   493  		assert.True(t, ok)
   494  		assert.Equal(t, p, p2)
   495  		assert.True(t, p2.(*testProvider).configured)
   496  	}
   497  
   498  	// Update the existing provider for the first entry in olds.
   499  	{
   500  		urn, id := olds[0].URN, olds[0].ID
   501  		olds, news := olds[0].Inputs, olds[0].Inputs
   502  		timeout := float64(120)
   503  
   504  		// Fetch the old provider instance.
   505  		old, ok := r.GetProvider(Reference{urn: urn, id: id})
   506  		assert.True(t, ok)
   507  
   508  		// Check
   509  		inputs, failures, err := r.Check(urn, olds, news, false, nil)
   510  		assert.NoError(t, err)
   511  		assert.Equal(t, news, inputs)
   512  		assert.Empty(t, failures)
   513  
   514  		// Since this is not a preview, the provider should not yet be configured.
   515  		p, ok := r.GetProvider(Reference{urn: urn, id: UnknownID})
   516  		assert.True(t, ok)
   517  		assert.False(t, p == old)
   518  		assert.False(t, p.(*testProvider).configured)
   519  
   520  		// Diff
   521  		diff, err := r.Diff(urn, id, olds, news, false, nil)
   522  		assert.NoError(t, err)
   523  		assert.Equal(t, plugin.DiffResult{Changes: plugin.DiffNone}, diff)
   524  
   525  		// The old provider should still be registered.
   526  		p2, ok := r.GetProvider(Reference{urn: urn, id: id})
   527  		assert.True(t, ok)
   528  		assert.Equal(t, old, p2)
   529  
   530  		// Update
   531  		outs, status, err := r.Update(urn, id, olds, inputs, timeout, nil, false)
   532  		assert.NoError(t, err)
   533  		assert.Equal(t, resource.PropertyMap{}, outs)
   534  		assert.Equal(t, resource.StatusOK, status)
   535  
   536  		p3, ok := r.GetProvider(Reference{urn: urn, id: id})
   537  		assert.True(t, ok)
   538  		assert.True(t, p3 == p)
   539  		assert.True(t, p3.(*testProvider).configured)
   540  	}
   541  
   542  	// Delete the existing provider for the last entry in olds.
   543  	{
   544  		urn, id := olds[len(olds)-1].URN, olds[len(olds)-1].ID
   545  		timeout := float64(120)
   546  
   547  		// Fetch the old provider instance.
   548  		_, ok := r.GetProvider(Reference{urn: urn, id: id})
   549  		assert.True(t, ok)
   550  
   551  		// Delete
   552  		status, err := r.Delete(urn, id, resource.PropertyMap{}, timeout)
   553  		assert.NoError(t, err)
   554  		assert.Equal(t, resource.StatusOK, status)
   555  
   556  		_, ok = r.GetProvider(Reference{urn: urn, id: id})
   557  		assert.False(t, ok)
   558  	}
   559  }
   560  
   561  func TestCRUDPreview(t *testing.T) {
   562  	t.Parallel()
   563  
   564  	olds := []*resource.State{
   565  		newProviderState("pkgA", "a", "id1", false, nil),
   566  		newProviderState("pkgB", "a", "id1", false, nil),
   567  		newProviderState("pkgC", "a", "id1", false, nil),
   568  		newProviderState("pkgD", "a", "id1", false, nil),
   569  	}
   570  	loaders := []*providerLoader{
   571  		newSimpleLoader(t, "pkgA", "", nil),
   572  		newSimpleLoader(t, "pkgB", "", nil),
   573  		newSimpleLoader(t, "pkgC", "", nil),
   574  		newLoader(t, "pkgD", "", func(pkg tokens.Package, ver semver.Version) (plugin.Provider, error) {
   575  			return &testProvider{
   576  				pkg:     pkg,
   577  				version: ver,
   578  				checkConfig: func(urn resource.URN, olds,
   579  					news resource.PropertyMap, allowUnknowns bool) (resource.PropertyMap, []plugin.CheckFailure, error) {
   580  					return news, nil, nil
   581  				},
   582  				diffConfig: func(urn resource.URN, olds, news resource.PropertyMap,
   583  					allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) {
   584  					// Always reuquire replacement.
   585  					return plugin.DiffResult{ReplaceKeys: []resource.PropertyKey{"id"}}, nil
   586  				},
   587  				config: func(inputs resource.PropertyMap) error {
   588  					return nil
   589  				},
   590  			}, nil
   591  		}),
   592  	}
   593  	host := newPluginHost(t, loaders)
   594  
   595  	r, err := NewRegistry(host, olds, true, nil)
   596  	assert.NoError(t, err)
   597  	assert.NotNil(t, r)
   598  
   599  	assert.Equal(t, len(olds), len(r.providers))
   600  
   601  	for _, old := range olds {
   602  		ref, err := NewReference(old.URN, old.ID)
   603  		assert.NoError(t, err)
   604  
   605  		p, ok := r.GetProvider(ref)
   606  		assert.True(t, ok)
   607  		assert.NotNil(t, p)
   608  
   609  		assert.Equal(t, GetProviderPackage(old.Type), p.Pkg())
   610  	}
   611  
   612  	// Create a new provider for each package.
   613  	for _, l := range loaders {
   614  		typ := MakeProviderType(l.pkg)
   615  		urn := resource.NewURN("test", "test", "", typ, "b")
   616  		olds, news := resource.PropertyMap{}, resource.PropertyMap{}
   617  
   618  		// Check
   619  		inputs, failures, err := r.Check(urn, olds, news, false, nil)
   620  		assert.NoError(t, err)
   621  		assert.Equal(t, news, inputs)
   622  		assert.Empty(t, failures)
   623  
   624  		// The provider should not be configured: configuration will occur during the previewed Create.
   625  		p, ok := r.GetProvider(Reference{urn: urn, id: UnknownID})
   626  		assert.True(t, ok)
   627  		assert.False(t, p.(*testProvider).configured)
   628  	}
   629  
   630  	// Update the existing provider for the first entry in olds.
   631  	{
   632  		urn, id := olds[0].URN, olds[0].ID
   633  		olds, news := olds[0].Inputs, olds[0].Inputs
   634  
   635  		// Fetch the old provider instance.
   636  		old, ok := r.GetProvider(Reference{urn: urn, id: id})
   637  		assert.True(t, ok)
   638  
   639  		// Check
   640  		inputs, failures, err := r.Check(urn, olds, news, false, nil)
   641  		assert.NoError(t, err)
   642  		assert.Equal(t, news, inputs)
   643  		assert.Empty(t, failures)
   644  
   645  		// The provider should remain unconfigured.
   646  		p, ok := r.GetProvider(Reference{urn: urn, id: UnknownID})
   647  		assert.True(t, ok)
   648  		assert.False(t, p == old)
   649  		assert.False(t, p.(*testProvider).configured)
   650  
   651  		// Diff
   652  		diff, err := r.Diff(urn, id, olds, news, false, nil)
   653  		assert.NoError(t, err)
   654  		assert.Equal(t, plugin.DiffResult{Changes: plugin.DiffNone}, diff)
   655  
   656  		// The original provider should be used because the config did not change.
   657  		p2, ok := r.GetProvider(Reference{urn: urn, id: id})
   658  		assert.True(t, ok)
   659  		assert.True(t, p2 == old)
   660  		assert.False(t, p2 == p)
   661  	}
   662  
   663  	// Replace the existing provider for the last entry in olds.
   664  	{
   665  		urn, id := olds[len(olds)-1].URN, olds[len(olds)-1].ID
   666  		olds, news := olds[len(olds)-1].Inputs, olds[len(olds)-1].Inputs
   667  
   668  		// Fetch the old provider instance.
   669  		old, ok := r.GetProvider(Reference{urn: urn, id: id})
   670  		assert.True(t, ok)
   671  
   672  		// Check
   673  		inputs, failures, err := r.Check(urn, olds, news, false, nil)
   674  		assert.NoError(t, err)
   675  		assert.Equal(t, news, inputs)
   676  		assert.Empty(t, failures)
   677  
   678  		// The provider should remain unconfigured.
   679  		p, ok := r.GetProvider(Reference{urn: urn, id: UnknownID})
   680  		assert.True(t, ok)
   681  		assert.False(t, p == old)
   682  		assert.False(t, p.(*testProvider).configured)
   683  
   684  		// Diff
   685  		diff, err := r.Diff(urn, id, olds, news, false, nil)
   686  		assert.NoError(t, err)
   687  		assert.True(t, diff.Replace())
   688  
   689  		// The new provider should be not be registered; the registered provider should still be the original.
   690  		p2, ok := r.GetProvider(Reference{urn: urn, id: id})
   691  		assert.True(t, ok)
   692  		assert.True(t, p2 == old)
   693  		assert.False(t, p2 == p)
   694  	}
   695  }
   696  
   697  func TestCRUDNoProviders(t *testing.T) {
   698  	t.Parallel()
   699  
   700  	host := newPluginHost(t, []*providerLoader{})
   701  
   702  	r, err := NewRegistry(host, []*resource.State{}, false, nil)
   703  	assert.NoError(t, err)
   704  	assert.NotNil(t, r)
   705  
   706  	typ := MakeProviderType("pkgA")
   707  	urn := resource.NewURN("test", "test", "", typ, "b")
   708  	olds, news := resource.PropertyMap{}, resource.PropertyMap{}
   709  
   710  	// Check
   711  	inputs, failures, err := r.Check(urn, olds, news, false, nil)
   712  	assert.Error(t, err)
   713  	assert.Empty(t, failures)
   714  	assert.Nil(t, inputs)
   715  }
   716  
   717  func TestCRUDWrongPackage(t *testing.T) {
   718  	t.Parallel()
   719  
   720  	loaders := []*providerLoader{
   721  		newSimpleLoader(t, "pkgB", "", nil),
   722  	}
   723  	host := newPluginHost(t, loaders)
   724  
   725  	r, err := NewRegistry(host, []*resource.State{}, false, nil)
   726  	assert.NoError(t, err)
   727  	assert.NotNil(t, r)
   728  
   729  	typ := MakeProviderType("pkgA")
   730  	urn := resource.NewURN("test", "test", "", typ, "b")
   731  	olds, news := resource.PropertyMap{}, resource.PropertyMap{}
   732  
   733  	// Check
   734  	inputs, failures, err := r.Check(urn, olds, news, false, nil)
   735  	assert.Error(t, err)
   736  	assert.Empty(t, failures)
   737  	assert.Nil(t, inputs)
   738  }
   739  
   740  func TestCRUDWrongVersion(t *testing.T) {
   741  	t.Parallel()
   742  
   743  	loaders := []*providerLoader{
   744  		newSimpleLoader(t, "pkgA", "0.5.0", nil),
   745  	}
   746  	host := newPluginHost(t, loaders)
   747  
   748  	r, err := NewRegistry(host, []*resource.State{}, false, nil)
   749  	assert.NoError(t, err)
   750  	assert.NotNil(t, r)
   751  
   752  	typ := MakeProviderType("pkgA")
   753  	urn := resource.NewURN("test", "test", "", typ, "b")
   754  	olds, news := resource.PropertyMap{}, resource.PropertyMap{"version": resource.NewStringProperty("1.0.0")}
   755  
   756  	// Check
   757  	inputs, failures, err := r.Check(urn, olds, news, false, nil)
   758  	assert.Error(t, err)
   759  	assert.Empty(t, failures)
   760  	assert.Nil(t, inputs)
   761  }
   762  
   763  func TestCRUDBadVersionNotString(t *testing.T) {
   764  	t.Parallel()
   765  
   766  	loaders := []*providerLoader{
   767  		newSimpleLoader(t, "pkgA", "1.0.0", nil),
   768  	}
   769  	host := newPluginHost(t, loaders)
   770  
   771  	r, err := NewRegistry(host, []*resource.State{}, false, nil)
   772  	assert.NoError(t, err)
   773  	assert.NotNil(t, r)
   774  
   775  	typ := MakeProviderType("pkgA")
   776  	urn := resource.NewURN("test", "test", "", typ, "b")
   777  	olds, news := resource.PropertyMap{}, resource.PropertyMap{"version": resource.NewBoolProperty(true)}
   778  
   779  	// Check
   780  	inputs, failures, err := r.Check(urn, olds, news, false, nil)
   781  	assert.NoError(t, err)
   782  	assert.Len(t, failures, 1)
   783  	assert.Equal(t, "version", string(failures[0].Property))
   784  	assert.Nil(t, inputs)
   785  }
   786  
   787  func TestCRUDBadVersion(t *testing.T) {
   788  	t.Parallel()
   789  
   790  	loaders := []*providerLoader{
   791  		newSimpleLoader(t, "pkgA", "1.0.0", nil),
   792  	}
   793  	host := newPluginHost(t, loaders)
   794  
   795  	r, err := NewRegistry(host, []*resource.State{}, false, nil)
   796  	assert.NoError(t, err)
   797  	assert.NotNil(t, r)
   798  
   799  	typ := MakeProviderType("pkgA")
   800  	urn := resource.NewURN("test", "test", "", typ, "b")
   801  	olds, news := resource.PropertyMap{}, resource.PropertyMap{"version": resource.NewStringProperty("foo")}
   802  
   803  	// Check
   804  	inputs, failures, err := r.Check(urn, olds, news, false, nil)
   805  	assert.NoError(t, err)
   806  	assert.Len(t, failures, 1)
   807  	assert.Equal(t, "version", string(failures[0].Property))
   808  	assert.Nil(t, inputs)
   809  }