/// <summary> /// Calls <see cref="ReadOrRegisterResourceAsync"/> then completes all the /// <see cref="IOutputCompletionSource"/> sources on the <paramref name="resource"/> with /// the results of it. /// </summary> private async Task CompleteResourceAsync( Resource resource, bool remote, Func <string, Resource> newDependency, ResourceArgs args, ResourceOptions options, ImmutableDictionary <string, IOutputCompletionSource> completionSources) { // Run in a try/catch/finally so that we always resolve all the outputs of the resource // regardless of whether we encounter an errors computing the action. try { var response = await ReadOrRegisterResourceAsync( resource, remote, newDependency, args, options).ConfigureAwait(false); completionSources[Constants.UrnPropertyName].SetStringValue(response.urn, isKnown: true); if (resource is CustomResource customResource) { var id = response.id ?? ""; completionSources[Constants.IdPropertyName].SetStringValue(id, isKnown: id != ""); } // Go through all our output fields and lookup a corresponding value in the response // object. Allow the output field to deserialize the response. foreach (var(fieldName, completionSource) in completionSources) { if (fieldName == Constants.UrnPropertyName || fieldName == Constants.IdPropertyName) { // Already handled specially above. continue; } // We process and deserialize each field (instead of bulk processing // 'response.data' so that each field can have independent isKnown/isSecret // values. We do not want to bubble up isKnown/isSecret from one field to the // rest. if (response.data.Fields.TryGetValue(fieldName, out var value)) { if (!response.dependencies.TryGetValue(fieldName, out var dependencies)) { dependencies = ImmutableHashSet <Resource> .Empty; } var converted = Converter.ConvertValue($"{resource.GetType().FullName}.{fieldName}", value, completionSource.TargetType, dependencies); completionSource.SetValue(converted); } } } catch (Exception e) { // Mark any unresolved output properties with this exception. That way we don't // leave any outstanding tasks sitting around which might cause hangs. foreach (var source in completionSources.Values) { source.TrySetException(e); } throw; } finally { // ensure that we've at least resolved all our completion sources. That way we // don't leave any outstanding tasks sitting around which might cause hangs. foreach (var source in completionSources.Values) { // Didn't get a value for this field. Resolve it with a default value. // If we're in preview, we'll consider this unknown and in a normal // update we'll consider it known. source.TrySetDefaultResult(isKnown: !_isDryRun); } } }
/// <summary> /// Creates and registers a new resource object. <paramref name="type"/> is the fully /// qualified type token and <paramref name="name"/> is the "name" part to use in creating a /// stable and globally unique URN for the object. dependsOn is an optional list of other /// resources that this resource depends on, controlling the order in which we perform /// resource operations. /// </summary> /// <param name="type">The type of the resource.</param> /// <param name="name">The unique name of the resource.</param> /// <param name="custom">True to indicate that this is a custom resource, managed by a plugin.</param> /// <param name="args">The arguments to use to populate the new resource.</param> /// <param name="options">A bag of options that control this resource's behavior.</param> /// <param name="remote">True if this is a remote component resource.</param> /// <param name="dependency">True if this is a synthetic resource used internally for dependency tracking.</param> private protected Resource( string type, string name, bool custom, ResourceArgs args, ResourceOptions options, bool remote = false, bool dependency = false) { if (dependency) { _type = ""; _name = ""; _protect = false; _providers = ImmutableDictionary <string, ProviderResource> .Empty; return; } if (string.IsNullOrEmpty(type)) { throw new ArgumentException("'type' cannot be null or empty.", nameof(type)); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException("'name' cannot be null or empty.", nameof(name)); } // Before anything else - if there are transformations registered, invoke them in order // to transform the properties and options assigned to this resource. var parent = type == Stack._rootPulumiStackTypeName ? null : (options.Parent ?? Deployment.InternalInstance.Stack); _type = type; _name = name; var transformations = ImmutableArray.CreateBuilder <ResourceTransformation>(); transformations.AddRange(options.ResourceTransformations); if (parent != null) { transformations.AddRange(parent._transformations); } this._transformations = transformations.ToImmutable(); foreach (var transformation in this._transformations) { var tres = transformation(new ResourceTransformationArgs(this, args, options)); if (tres != null) { if (tres.Value.Options.Parent != options.Parent) { // This is currently not allowed because the parent tree is needed to // establish what transformation to apply in the first place, and to compute // inheritance of other resource options in the Resource constructor before // transformations are run (so modifying it here would only even partially // take affect). It's theoretically possible this restriction could be // lifted in the future, but for now just disallow re-parenting resources in // transformations to be safe. throw new ArgumentException("Transformations cannot currently be used to change the 'parent' of a resource."); } args = tres.Value.Args; options = tres.Value.Options; } } // Make a shallow clone of options to ensure we don't modify the value passed in. options = options.Clone(); var componentOpts = options as ComponentResourceOptions; var customOpts = options as CustomResourceOptions; if (options.Provider != null && componentOpts?.Providers.Count > 0) { throw new ResourceException("Do not supply both 'provider' and 'providers' options to a ComponentResource.", options.Parent); } // Check the parent type if one exists and fill in any default options. this._providers = ImmutableDictionary <string, ProviderResource> .Empty; if (options.Parent != null) { var parentResource = options.Parent; lock (parentResource.ChildResources) { parentResource.ChildResources.Add(this); } options.Protect ??= options.Parent._protect; // Make a copy of the aliases array, and add to it any implicit aliases inherited from its parent options.Aliases = options.Aliases.ToList(); foreach (var parentAlias in options.Parent._aliases) { options.Aliases.Add(Pulumi.Urn.InheritedChildAlias(name, options.Parent.GetResourceName(), parentAlias, type)); } this._providers = options.Parent._providers; } if (custom) { var provider = customOpts?.Provider; if (provider == null) { if (options.Parent != null) { // If no provider was given, but we have a parent, then inherit the // provider from our parent. options.Provider = options.Parent.GetProvider(type); } } else { // If a provider was specified, add it to the providers map under this type's package so that // any children of this resource inherit its provider. var typeComponents = type.Split(":"); if (typeComponents.Length == 3) { var pkg = typeComponents[0]; this._providers = this._providers.SetItem(pkg, provider); } } } else { // Note: we checked above that at most one of options.provider or options.providers // is set. // If options.provider is set, treat that as if we were given a array of provider // with that single value in it. Otherwise, take the array of providers, convert it // to a map and combine with any providers we've already set from our parent. var providerList = options.Provider != null ? new List <ProviderResource> { options.Provider } : componentOpts?.Providers; this._providers = this._providers.AddRange(ConvertToProvidersMap(providerList)); } this._protect = options.Protect == true; // Collapse any 'Alias'es down to URNs. We have to wait until this point to do so // because we do not know the default 'name' and 'type' to apply until we are inside the // resource constructor. var aliases = ImmutableArray.CreateBuilder <Input <string> >(); foreach (var alias in options.Aliases) { aliases.Add(CollapseAliasToUrn(alias, name, type, options.Parent)); } this._aliases = aliases.ToImmutable(); Deployment.InternalInstance.ReadOrRegisterResource(this, remote, urn => new DependencyResource(urn), args, options); }
Task IDeployment.InvokeAsync(string token, ResourceArgs args, InvokeOptions?options) => InvokeAsync <object>(token, args, options, convertResult: false);
Task <T> IDeployment.InvokeAsync <T>(string token, ResourceArgs args, InvokeOptions?options) => InvokeAsync <T>(token, args, options, convertResult: true);
/// <summary> /// Creates and registers a new provider resource for a particular package. /// </summary> /// <param name="package">The package associated with this provider.</param> /// <param name="name">The unique name of the provider.</param> /// <param name="args">The configuration to use for this provider.</param> /// <param name="options">A bag of options that control this provider's behavior.</param> public ProviderResource(string package, string name, ResourceArgs args, CustomResourceOptions?options = null) : this(package, name, args, options, dependency : false) { }