github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/providers/registry.go (about)

     1  // Copyright 2016-2021, 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  	"sync"
    21  
    22  	"github.com/blang/semver"
    23  	uuid "github.com/gofrs/uuid"
    24  
    25  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    26  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
    27  	"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
    28  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    29  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
    30  	"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
    31  )
    32  
    33  const versionKey resource.PropertyKey = "version"
    34  const pluginDownloadKey resource.PropertyKey = "pluginDownloadURL"
    35  
    36  // SetProviderURL sets the provider plugin download server URL in the given property map.
    37  func SetProviderURL(inputs resource.PropertyMap, value string) {
    38  	inputs[pluginDownloadKey] = resource.NewStringProperty(value)
    39  }
    40  
    41  // GetProviderDownloadURL fetches a provider plugin download server URL from the given property map.
    42  // If the server URL is not set, this function returns "".
    43  func GetProviderDownloadURL(inputs resource.PropertyMap) (string, error) {
    44  	url, ok := inputs[pluginDownloadKey]
    45  	if !ok {
    46  		return "", nil
    47  	}
    48  	if !url.IsString() {
    49  		return "", fmt.Errorf("'%s' must be a string", pluginDownloadKey)
    50  	}
    51  	return url.StringValue(), nil
    52  }
    53  
    54  // Sets the provider version in the given property map.
    55  func SetProviderVersion(inputs resource.PropertyMap, value *semver.Version) {
    56  	inputs[versionKey] = resource.NewStringProperty(value.String())
    57  }
    58  
    59  // GetProviderVersion fetches and parses a provider version from the given property map. If the
    60  // version property is not present, this function returns nil.
    61  func GetProviderVersion(inputs resource.PropertyMap) (*semver.Version, error) {
    62  	version, ok := inputs[versionKey]
    63  	if !ok {
    64  		return nil, nil
    65  	}
    66  
    67  	if !version.IsString() {
    68  		return nil, fmt.Errorf("'%s' must be a string", versionKey)
    69  	}
    70  
    71  	sv, err := semver.ParseTolerant(version.StringValue())
    72  	if err != nil {
    73  		return nil, fmt.Errorf("could not parse provider version: %v", err)
    74  	}
    75  	return &sv, nil
    76  }
    77  
    78  // Registry manages the lifecylce of provider resources and their plugins and handles the resolution of provider
    79  // references to loaded plugins.
    80  //
    81  // When a registry is created, it is handed the set of old provider resources that it will manage. Each provider
    82  // resource in this set is loaded and configured as per its recorded inputs and registered under the provider
    83  // reference that corresponds to its URN and ID, both of which must be known. At this point, the created registry is
    84  // prepared to be used to manage the lifecycle of these providers as well as any new provider resources requested by
    85  // invoking the registry's CRUD operations.
    86  //
    87  // In order to fit neatly in to the existing infrastructure for managing resources using Pulumi, a provider regidstry
    88  // itself implements the plugin.Provider interface.
    89  type Registry struct {
    90  	host      plugin.Host
    91  	isPreview bool
    92  	providers map[Reference]plugin.Provider
    93  	builtins  plugin.Provider
    94  	aliases   map[resource.URN]resource.URN
    95  	m         sync.RWMutex
    96  }
    97  
    98  var _ plugin.Provider = (*Registry)(nil)
    99  
   100  func loadProvider(pkg tokens.Package, version *semver.Version, host plugin.Host,
   101  	builtins plugin.Provider) (plugin.Provider, error) {
   102  
   103  	if builtins != nil && pkg == builtins.Pkg() {
   104  		return builtins, nil
   105  	}
   106  
   107  	return host.Provider(pkg, version)
   108  }
   109  
   110  // NewRegistry creates a new provider registry using the given host and old resources. Each provider present in the old
   111  // resources will be loaded, configured, and added to the returned registry under its reference. If any provider is not
   112  // loadable/configurable or has an invalid ID, this function returns an error.
   113  func NewRegistry(host plugin.Host, prev []*resource.State, isPreview bool,
   114  	builtins plugin.Provider) (*Registry, error) {
   115  
   116  	r := &Registry{
   117  		host:      host,
   118  		isPreview: isPreview,
   119  		providers: make(map[Reference]plugin.Provider),
   120  		builtins:  builtins,
   121  		aliases:   make(map[resource.URN]resource.URN),
   122  	}
   123  
   124  	for _, res := range prev {
   125  		urn := res.URN
   126  		if !IsProviderType(urn.Type()) {
   127  			logging.V(7).Infof("provider(%v): %v", urn, res.Provider)
   128  			continue
   129  		}
   130  
   131  		// Ensure that this provider has a known ID.
   132  		if res.ID == "" || res.ID == UnknownID {
   133  			return nil, fmt.Errorf("provider '%v' has an unknown ID", urn)
   134  		}
   135  
   136  		// Ensure that we have no duplicates.
   137  		ref := mustNewReference(urn, res.ID)
   138  		if _, ok := r.providers[ref]; ok {
   139  			return nil, fmt.Errorf("duplicate provider found in old state: '%v'", ref)
   140  		}
   141  
   142  		providerPkg := GetProviderPackage(urn.Type())
   143  
   144  		// Parse the provider version, then load, configure, and register the provider.
   145  		version, err := GetProviderVersion(res.Inputs)
   146  		if err != nil {
   147  			return nil, fmt.Errorf("could not parse version for %v provider '%v': %v", providerPkg, urn, err)
   148  		}
   149  		provider, err := loadProvider(providerPkg, version, host, builtins)
   150  		if err != nil {
   151  			return nil, fmt.Errorf("could not load plugin for %v provider '%v': %v", providerPkg, urn, err)
   152  		}
   153  		if provider == nil {
   154  			return nil, fmt.Errorf("could not find plugin for %v provider '%v' at version %v", providerPkg, urn, version)
   155  		}
   156  		if err := provider.Configure(res.Inputs); err != nil {
   157  			closeErr := host.CloseProvider(provider)
   158  			contract.IgnoreError(closeErr)
   159  			return nil, fmt.Errorf("could not configure provider '%v': %v", urn, err)
   160  		}
   161  
   162  		logging.V(7).Infof("loaded provider %v", ref)
   163  		r.providers[ref] = provider
   164  	}
   165  
   166  	return r, nil
   167  }
   168  
   169  // GetProvider returns the provider plugin that is currently registered under the given reference, if any.
   170  func (r *Registry) GetProvider(ref Reference) (plugin.Provider, bool) {
   171  	r.m.RLock()
   172  	defer r.m.RUnlock()
   173  
   174  	logging.V(7).Infof("GetProvider(%v)", ref)
   175  
   176  	provider, ok := r.providers[ref]
   177  	return provider, ok
   178  }
   179  
   180  func (r *Registry) setProvider(ref Reference, provider plugin.Provider) {
   181  	r.m.Lock()
   182  	defer r.m.Unlock()
   183  
   184  	logging.V(7).Infof("setProvider(%v)", ref)
   185  
   186  	r.providers[ref] = provider
   187  
   188  	if alias, ok := r.aliases[ref.URN()]; ok {
   189  		r.providers[mustNewReference(alias, ref.ID())] = provider
   190  	}
   191  }
   192  
   193  func (r *Registry) deleteProvider(ref Reference) (plugin.Provider, bool) {
   194  	r.m.Lock()
   195  	defer r.m.Unlock()
   196  
   197  	provider, ok := r.providers[ref]
   198  	if !ok {
   199  		return nil, false
   200  	}
   201  	delete(r.providers, ref)
   202  	return provider, true
   203  }
   204  
   205  // The rest of the methods below are the implementation of the plugin.Provider interface methods.
   206  
   207  func (r *Registry) Close() error {
   208  	return nil
   209  }
   210  
   211  func (r *Registry) Pkg() tokens.Package {
   212  	return "pulumi"
   213  }
   214  
   215  func (r *Registry) label() string {
   216  	return "ProviderRegistry"
   217  }
   218  
   219  // GetSchema returns the JSON-serialized schema for the provider.
   220  func (r *Registry) GetSchema(version int) ([]byte, error) {
   221  	contract.Fail()
   222  
   223  	return nil, errors.New("the provider registry has no schema")
   224  }
   225  
   226  // CheckConfig validates the configuration for this resource provider.
   227  func (r *Registry) CheckConfig(urn resource.URN, olds,
   228  	news resource.PropertyMap, allowUnknowns bool) (resource.PropertyMap, []plugin.CheckFailure, error) {
   229  
   230  	contract.Fail()
   231  	return nil, nil, errors.New("the provider registry is not configurable")
   232  }
   233  
   234  // DiffConfig checks what impacts a hypothetical change to this provider's configuration will have on the provider.
   235  func (r *Registry) DiffConfig(urn resource.URN, olds, news resource.PropertyMap,
   236  	allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) {
   237  	contract.Fail()
   238  	return plugin.DiffResult{}, errors.New("the provider registry is not configurable")
   239  }
   240  
   241  func (r *Registry) Configure(props resource.PropertyMap) error {
   242  	contract.Fail()
   243  	return errors.New("the provider registry is not configurable")
   244  }
   245  
   246  // Check validates the configuration for a particular provider resource.
   247  //
   248  // The particulars of Check are a bit subtle for a few reasons:
   249  //   - we need to load the provider for the package indicated by the type name portion provider resource's URN in order
   250  //     to check its config
   251  //   - we need to keep the newly-loaded provider around in case we need to diff its config
   252  //   - if we are running a preview, we need to configure the provider, as its corresponding CRUD operations will not run
   253  //     (we would normally configure the provider in Create or Update).
   254  func (r *Registry) Check(urn resource.URN, olds, news resource.PropertyMap,
   255  	allowUnknowns bool, randomSeed []byte) (resource.PropertyMap, []plugin.CheckFailure, error) {
   256  
   257  	contract.Require(IsProviderType(urn.Type()), "urn")
   258  
   259  	label := fmt.Sprintf("%s.Check(%s)", r.label(), urn)
   260  	logging.V(7).Infof("%s executing (#olds=%d,#news=%d)", label, len(olds), len(news))
   261  
   262  	// Parse the version from the provider properties and load the provider.
   263  	version, err := GetProviderVersion(news)
   264  	if err != nil {
   265  		return nil, []plugin.CheckFailure{{Property: "version", Reason: err.Error()}}, nil
   266  	}
   267  	provider, err := loadProvider(GetProviderPackage(urn.Type()), version, r.host, r.builtins)
   268  	if err != nil {
   269  		return nil, nil, err
   270  	}
   271  	if provider == nil {
   272  		return nil, nil, errors.New("could not find plugin")
   273  	}
   274  
   275  	// Check the provider's config. If the check fails, unload the provider.
   276  	inputs, failures, err := provider.CheckConfig(urn, olds, news, allowUnknowns)
   277  	if len(failures) != 0 || err != nil {
   278  		closeErr := r.host.CloseProvider(provider)
   279  		contract.IgnoreError(closeErr)
   280  		return nil, failures, err
   281  	}
   282  
   283  	// Create a provider reference using the URN and the unknown ID and register the provider.
   284  	r.setProvider(mustNewReference(urn, UnknownID), provider)
   285  
   286  	return inputs, nil, nil
   287  }
   288  
   289  // RegisterAliases informs the registry that the new provider object with the given URN is aliased to the given list
   290  // of URNs.
   291  func (r *Registry) RegisterAlias(providerURN, alias resource.URN) {
   292  	if providerURN != alias {
   293  		r.aliases[providerURN] = alias
   294  	}
   295  }
   296  
   297  // Diff diffs the configuration of the indicated provider. The provider corresponding to the given URN must have
   298  // previously been loaded by a call to Check.
   299  func (r *Registry) Diff(urn resource.URN, id resource.ID, olds, news resource.PropertyMap,
   300  	allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) {
   301  	contract.Require(id != "", "id")
   302  
   303  	label := fmt.Sprintf("%s.Diff(%s,%s)", r.label(), urn, id)
   304  	logging.V(7).Infof("%s: executing (#olds=%d,#news=%d)", label, len(olds), len(news))
   305  
   306  	// Create a reference using the URN and the unknown ID and fetch the provider.
   307  	provider, ok := r.GetProvider(mustNewReference(urn, UnknownID))
   308  	if !ok {
   309  		// If the provider was not found in the registry under its URN and the Unknown ID, then it must have not have
   310  		// been subject to a call to `Check`. This can happen when we are diffing a provider's inputs as part of
   311  		// evaluating the fanout of a delete-before-replace operation. In this case, we can just use the old provider
   312  		// (which must have been loaded when the registry was created), and we will not unload it.
   313  		provider, ok = r.GetProvider(mustNewReference(urn, id))
   314  		contract.Assertf(ok, "Provider must have been registered by NewRegistry for DBR Diff (%v::%v)", urn, id)
   315  
   316  		diff, err := provider.DiffConfig(urn, olds, news, allowUnknowns, ignoreChanges)
   317  		if err != nil {
   318  			return plugin.DiffResult{Changes: plugin.DiffUnknown}, err
   319  		}
   320  		return diff, nil
   321  	}
   322  
   323  	// Diff the properties.
   324  	diff, err := provider.DiffConfig(urn, olds, news, allowUnknowns, ignoreChanges)
   325  	if err != nil {
   326  		return plugin.DiffResult{Changes: plugin.DiffUnknown}, err
   327  	}
   328  	if diff.Changes == plugin.DiffUnknown {
   329  		if olds.DeepEquals(news) {
   330  			diff.Changes = plugin.DiffNone
   331  		} else {
   332  			diff.Changes = plugin.DiffSome
   333  		}
   334  	}
   335  
   336  	// If the diff requires replacement, unload the provider: the engine will reload it during its replacememnt Check.
   337  	if diff.Replace() {
   338  		closeErr := r.host.CloseProvider(provider)
   339  		contract.IgnoreError(closeErr)
   340  	}
   341  
   342  	logging.V(7).Infof("%s: executed (%#v, %#v)", label, diff.Changes, diff.ReplaceKeys)
   343  
   344  	return diff, nil
   345  }
   346  
   347  // Same executes as part of the "Same" step for a provider that has not changed. It exists solely to allow the registry
   348  // to point aliases for a provider to the proper object.
   349  func (r *Registry) Same(ref Reference) {
   350  	r.m.RLock()
   351  	defer r.m.RUnlock()
   352  
   353  	logging.V(7).Infof("Same(%v)", ref)
   354  
   355  	// If this provider is aliased to a different old URN, make sure that it is present under both the old reference and
   356  	// the new reference.
   357  	if alias, ok := r.aliases[ref.URN()]; ok {
   358  		aliasRef := mustNewReference(alias, ref.ID())
   359  		r.providers[ref] = r.providers[aliasRef]
   360  	}
   361  }
   362  
   363  // Create coonfigures the provider with the given URN using the indicated configuration, assigns it an ID, and
   364  // registers it under the assigned (URN, ID).
   365  //
   366  // The provider must have been loaded by a prior call to Check.
   367  func (r *Registry) Create(urn resource.URN, news resource.PropertyMap, timeout float64,
   368  	preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
   369  
   370  	label := fmt.Sprintf("%s.Create(%s)", r.label(), urn)
   371  	logging.V(7).Infof("%s executing (#news=%v)", label, len(news))
   372  
   373  	// Fetch the unconfigured provider, configure it, and register it under a new ID.
   374  	provider, ok := r.GetProvider(mustNewReference(urn, UnknownID))
   375  	contract.Assertf(ok, "'Check' must be called before 'Create' (%v)", urn)
   376  
   377  	if err := provider.Configure(news); err != nil {
   378  		return "", nil, resource.StatusOK, err
   379  	}
   380  
   381  	var id resource.ID
   382  	if !preview {
   383  		// generate a new uuid
   384  		uuid, err := uuid.NewV4()
   385  		if err != nil {
   386  			return "", nil, resource.StatusOK, err
   387  		}
   388  		id = resource.ID(uuid.String())
   389  		contract.Assert(id != UnknownID)
   390  	}
   391  
   392  	r.setProvider(mustNewReference(urn, id), provider)
   393  	return id, news, resource.StatusOK, nil
   394  }
   395  
   396  // Update configures the provider with the given URN and ID using the indicated configuration and registers it at the
   397  // reference indicated by the (URN, ID) pair.
   398  //
   399  // THe provider must have been loaded by a prior call to Check.
   400  func (r *Registry) Update(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
   401  	ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
   402  
   403  	label := fmt.Sprintf("%s.Update(%s,%s)", r.label(), id, urn)
   404  	logging.V(7).Infof("%s executing (#olds=%v,#news=%v)", label, len(olds), len(news))
   405  
   406  	// Fetch the unconfigured provider and configure it.
   407  	provider, ok := r.GetProvider(mustNewReference(urn, UnknownID))
   408  	contract.Assertf(ok, "'Check' and 'Diff' must be called before 'Update' (%v)", urn)
   409  
   410  	if err := provider.Configure(news); err != nil {
   411  		return nil, resource.StatusUnknown, err
   412  	}
   413  
   414  	// Publish the configured provider.
   415  	r.setProvider(mustNewReference(urn, id), provider)
   416  	return news, resource.StatusOK, nil
   417  }
   418  
   419  // Delete unregisters and unloads the provider with the given URN and ID. The provider must have been loaded when the
   420  // registry was created (i.e. it must have been present in the state handed to NewRegistry).
   421  func (r *Registry) Delete(urn resource.URN, id resource.ID, props resource.PropertyMap,
   422  	timeout float64) (resource.Status, error) {
   423  	contract.Assert(!r.isPreview)
   424  
   425  	ref := mustNewReference(urn, id)
   426  	provider, has := r.deleteProvider(ref)
   427  	contract.Assert(has)
   428  
   429  	closeErr := r.host.CloseProvider(provider)
   430  	contract.IgnoreError(closeErr)
   431  	return resource.StatusOK, nil
   432  }
   433  
   434  func (r *Registry) Read(urn resource.URN, id resource.ID,
   435  	inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error) {
   436  	return plugin.ReadResult{}, resource.StatusUnknown, errors.New("provider resources may not be read")
   437  }
   438  
   439  func (r *Registry) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
   440  	inputs resource.PropertyMap, options plugin.ConstructOptions) (plugin.ConstructResult, error) {
   441  	return plugin.ConstructResult{}, errors.New("provider resources may not be constructed")
   442  }
   443  
   444  func (r *Registry) Invoke(tok tokens.ModuleMember,
   445  	args resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) {
   446  
   447  	// It is the responsibility of the eval source to ensure that we never attempt an invoke using the provider
   448  	// registry.
   449  	contract.Fail()
   450  	return nil, nil, errors.New("the provider registry is not invokable")
   451  }
   452  
   453  func (r *Registry) StreamInvoke(
   454  	tok tokens.ModuleMember, args resource.PropertyMap,
   455  	onNext func(resource.PropertyMap) error) ([]plugin.CheckFailure, error) {
   456  
   457  	return nil, fmt.Errorf("the provider registry does not implement streaming invokes")
   458  }
   459  
   460  func (r *Registry) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo,
   461  	options plugin.CallOptions) (plugin.CallResult, error) {
   462  
   463  	// It is the responsibility of the eval source to ensure that we never attempt an call using the provider
   464  	// registry.
   465  	contract.Fail()
   466  	return plugin.CallResult{}, errors.New("the provider registry is not callable")
   467  }
   468  
   469  func (r *Registry) GetPluginInfo() (workspace.PluginInfo, error) {
   470  	// return an error: this should not be called for the provider registry
   471  	return workspace.PluginInfo{}, errors.New("the provider registry does not report plugin info")
   472  }
   473  
   474  func (r *Registry) SignalCancellation() error {
   475  	// At the moment there isn't anything reasonable we can do here. In the future, it might be nice to plumb
   476  	// cancellation through the plugin loader and cancel any outstanding load requests here.
   477  	return nil
   478  }