/// <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);
                }
            }
        }
Exemplo n.º 2
0
        /// <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);
        }
Exemplo n.º 3
0
 Task IDeployment.InvokeAsync(string token, ResourceArgs args, InvokeOptions?options)
 => InvokeAsync <object>(token, args, options, convertResult: false);
Exemplo n.º 4
0
 Task <T> IDeployment.InvokeAsync <T>(string token, ResourceArgs args, InvokeOptions?options)
 => InvokeAsync <T>(token, args, options, convertResult: true);
Exemplo n.º 5
0
 /// <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)
 {
 }