private async Task <PrepareResult> PrepareResourceAsync( string label, Resource res, bool custom, bool remote, ResourceArgs args, ResourceOptions options) { // Before we can proceed, all our dependencies must be finished. var type = res.GetResourceType(); var name = res.GetResourceName(); LogExcessive($"Gathering explicit dependencies: t={type}, name={name}, custom={custom}, remote={remote}"); var explicitDirectDependencies = new HashSet <Resource>( await GatherExplicitDependenciesAsync(options.DependsOn).ConfigureAwait(false)); LogExcessive($"Gathered explicit dependencies: t={type}, name={name}, custom={custom}, remote={remote}"); // Serialize out all our props to their final values. In doing so, we'll also collect all // the Resources pointed to by any Dependency objects we encounter, adding them to 'propertyDependencies'. LogExcessive($"Serializing properties: t={type}, name={name}, custom={custom}, remote={remote}"); var dictionary = await args.ToDictionaryAsync().ConfigureAwait(false); var(serializedProps, propertyToDirectDependencies) = await SerializeResourcePropertiesAsync( label, dictionary, await this.MonitorSupportsResourceReferences().ConfigureAwait(false), keepOutputValues : remote && await MonitorSupportsOutputValues().ConfigureAwait(false)).ConfigureAwait(false); LogExcessive($"Serialized properties: t={type}, name={name}, custom={custom}, remote={remote}"); // Wait for the parent to complete. // If no parent was provided, parent to the root resource. LogExcessive($"Getting parent urn: t={type}, name={name}, custom={custom}, remote={remote}"); var parentUrn = options.Parent != null ? await options.Parent.Urn.GetValueAsync(whenUnknown : default !).ConfigureAwait(false)
private async Task <(string urn, string id, Struct data, ImmutableDictionary <string, ImmutableHashSet <Resource> > dependencies)> RegisterResourceAsync( Resource resource, bool remote, Func <string, Resource> newDependency, ResourceArgs args, ResourceOptions options) { var name = resource.GetResourceName(); var type = resource.GetResourceType(); var custom = resource is CustomResource; var label = $"resource:{name}[{type}]"; Log.Debug($"Registering resource start: t={type}, name={name}, custom={custom}, remote={remote}"); var request = CreateRegisterResourceRequest(type, name, custom, remote, options); Log.Debug($"Preparing resource: t={type}, name={name}, custom={custom}, remote={remote}"); var prepareResult = await PrepareResourceAsync(label, resource, custom, remote, args, options).ConfigureAwait(false); Log.Debug($"Prepared resource: t={type}, name={name}, custom={custom}, remote={remote}"); PopulateRequest(request, prepareResult); Log.Debug($"Registering resource monitor start: t={type}, name={name}, custom={custom}, remote={remote}"); var result = await this.Monitor.RegisterResourceAsync(resource, request); Log.Debug($"Registering resource monitor end: t={type}, name={name}, custom={custom}, remote={remote}"); var dependencies = ImmutableDictionary.CreateBuilder <string, ImmutableHashSet <Resource> >(); foreach (var(key, propertyDependencies) in result.PropertyDependencies) { var urns = ImmutableHashSet.CreateBuilder <Resource>(); foreach (var urn in propertyDependencies.Urns) { urns.Add(newDependency(urn)); } dependencies[key] = urns.ToImmutable(); } return(result.Urn, result.Id, result.Object, dependencies.ToImmutable()); }
private async Task <PrepareResult> PrepareResourceAsync( string label, Resource res, bool custom, bool remote, ResourceArgs args, ResourceOptions options) { /* IMPORTANT! We should never await prior to this line, otherwise the Resource will be partly uninitialized. */ // Before we can proceed, all our dependencies must be finished. var type = res.GetResourceType(); var name = res.GetResourceName(); LogExcessive($"Gathering explicit dependencies: t={type}, name={name}, custom={custom}, remote={remote}"); var explicitDirectDependencies = new HashSet <Resource>( await GatherExplicitDependenciesAsync(options.DependsOn).ConfigureAwait(false)); LogExcessive($"Gathered explicit dependencies: t={type}, name={name}, custom={custom}, remote={remote}"); // Serialize out all our props to their final values. In doing so, we'll also collect all // the Resources pointed to by any Dependency objects we encounter, adding them to 'propertyDependencies'. LogExcessive($"Serializing properties: t={type}, name={name}, custom={custom}, remote={remote}"); var dictionary = await args.ToDictionaryAsync().ConfigureAwait(false); var(serializedProps, propertyToDirectDependencies) = await SerializeResourcePropertiesAsync( label, dictionary, await this.MonitorSupportsResourceReferences().ConfigureAwait(false)).ConfigureAwait(false); LogExcessive($"Serialized properties: t={type}, name={name}, custom={custom}, remote={remote}"); // Wait for the parent to complete. // If no parent was provided, parent to the root resource. LogExcessive($"Getting parent urn: t={type}, name={name}, custom={custom}, remote={remote}"); var parentUrn = options.Parent != null ? await options.Parent.Urn.GetValueAsync().ConfigureAwait(false) : await GetRootResourceAsync(type).ConfigureAwait(false); LogExcessive($"Got parent urn: t={type}, name={name}, custom={custom}, remote={remote}"); string?providerRef = null; if (custom) { var customOpts = options as CustomResourceOptions; providerRef = await ProviderResource.RegisterAsync(customOpts?.Provider).ConfigureAwait(false); } var providerRefs = new Dictionary <string, string>(); if (remote && options is ComponentResourceOptions componentOpts) { // If only the Provider opt is set, move it to the Providers list for further processing. if (componentOpts.Provider != null && componentOpts.Providers.Count == 0) { componentOpts.Providers.Add(componentOpts.Provider); componentOpts.Provider = null; } foreach (var provider in componentOpts.Providers) { var pref = await ProviderResource.RegisterAsync(provider).ConfigureAwait(false); if (pref != null) { providerRefs.Add(provider.Package, pref); } } } // Collect the URNs for explicit/implicit dependencies for the engine so that it can understand // the dependency graph and optimize operations accordingly. // The list of all dependencies (implicit or explicit). var allDirectDependencies = new HashSet <Resource>(explicitDirectDependencies); var allDirectDependencyUrns = await GetAllTransitivelyReferencedResourceUrnsAsync(explicitDirectDependencies).ConfigureAwait(false); var propertyToDirectDependencyUrns = new Dictionary <string, HashSet <string> >(); foreach (var(propertyName, directDependencies) in propertyToDirectDependencies) { allDirectDependencies.AddRange(directDependencies); var urns = await GetAllTransitivelyReferencedResourceUrnsAsync(directDependencies).ConfigureAwait(false); allDirectDependencyUrns.AddRange(urns); propertyToDirectDependencyUrns[propertyName] = urns; } // Wait for all aliases. Note that we use 'res._aliases' instead of 'options.aliases' as // the former has been processed in the Resource constructor prior to calling // 'registerResource' - both adding new inherited aliases and simplifying aliases down // to URNs. var aliases = new List <string>(); var uniqueAliases = new HashSet <string>(); foreach (var alias in res._aliases) { var aliasVal = await alias.ToOutput().GetValueAsync().ConfigureAwait(false); if (uniqueAliases.Add(aliasVal)) { aliases.Add(aliasVal); } } return(new PrepareResult( serializedProps, parentUrn ?? "", providerRef ?? "", providerRefs, allDirectDependencyUrns, propertyToDirectDependencyUrns, aliases)); void LogExcessive(string message) { if (_excessiveDebugOutput) { Log.Debug(message); } } }
void IDeploymentInternal.ReadOrRegisterResource( Resource resource, bool remote, Func <string, Resource> newDependency, ResourceArgs args, ResourceOptions options) { // ReadOrRegisterResource is called in a fire-and-forget manner. Make sure we keep // track of this task so that the application will not quit until this async work // completes. // // Also, we can only do our work once the constructor for the resource has actually // finished. Otherwise, we might actually read and get the result back *prior* to the // object finishing initializing. Note: this is not a speculative concern. This is // something that does happen and has to be accounted for. // // IMPORTANT! We have to make sure we run 'OutputCompletionSource.InitializeOutputs' // synchronously directly when `resource`'s constructor runs since this will set all of // the `[Output(...)] Output<T>` properties. We need those properties assigned by the // time the base 'Resource' constructor finishes so that both derived classes and // external consumers can use the Output properties of `resource`. var completionSources = OutputCompletionSource.InitializeOutputs(resource); _runner.RegisterTask( $"{nameof(IDeploymentInternal.ReadOrRegisterResource)}: {resource.GetResourceType()}-{resource.GetResourceName()}", CompleteResourceAsync(resource, remote, newDependency, args, options, completionSources)); }
void IDeploymentInternal.RegisterResourceOutputs(Resource resource, Output <IDictionary <string, object?> > outputs) { // RegisterResourceOutputs is called in a fire-and-forget manner. Make sure we keep track of // this task so that the application will not quit until this async work completes. _runner.RegisterTask( $"{nameof(IDeploymentInternal.RegisterResourceOutputs)}: {resource.GetResourceType()}-{resource.GetResourceName()}", RegisterResourceOutputsAsync(resource, outputs)); }
void IDeploymentInternal.ReadOrRegisterResource( Resource resource, bool remote, Func<string, Resource> newDependency, ResourceArgs args, ResourceOptions options) { // ReadOrRegisterResource is called in a fire-and-forget manner. Make sure we keep // track of this task so that the application will not quit until this async work // completes. // // Also, we can only do our work once the constructor for the resource has actually // finished. Otherwise, we might actually read and get the result back *prior* to the // object finishing initializing. Note: this is not a speculative concern. This is // something that does happen and has to be accounted for. _runner.RegisterTask( $"{nameof(IDeploymentInternal.ReadOrRegisterResource)}: {resource.GetResourceType()}-{resource.GetResourceName()}", CompleteResourceAsync(resource, remote, newDependency, args, options, resource.CompletionSources)); }