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 }