mirror of https://github.com/pulumi/pulumi.git
145 lines
5.6 KiB
C#
145 lines
5.6 KiB
C#
// Copyright 2016-2021, Pulumi Corporation
|
|
|
|
using System;
|
|
using System.Collections.Immutable;
|
|
using System.Threading.Tasks;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using Pulumi.Serialization;
|
|
using Pulumirpc;
|
|
|
|
namespace Pulumi
|
|
{
|
|
public sealed partial class Deployment
|
|
{
|
|
void IDeployment.Call(string token, CallArgs args, Resource? self, CallOptions? options)
|
|
=> Call<object>(token, args, self, options, convertResult: false);
|
|
|
|
Output<T> IDeployment.Call<T>(string token, CallArgs args, Resource? self, CallOptions? options)
|
|
=> Call<T>(token, args, self, options, convertResult: true);
|
|
|
|
private Output<T> Call<T>(string token, CallArgs args, Resource? self, CallOptions? options, bool convertResult)
|
|
=> new Output<T>(CallAsync<T>(token, args, self, options, convertResult));
|
|
|
|
private async Task<OutputData<T>> CallAsync<T>(
|
|
string token, CallArgs args, Resource? self, CallOptions? options, bool convertResult)
|
|
{
|
|
var (result, deps) = await CallRawAsync(token, args, self, options).ConfigureAwait(false);
|
|
if (convertResult)
|
|
{
|
|
var converted = Converter.ConvertValue<T>(err => Log.Warn(err, self), $"{token} result", new Value { StructValue = result });
|
|
return new OutputData<T>(deps, converted.Value, converted.IsKnown, converted.IsSecret);
|
|
}
|
|
|
|
return new OutputData<T>(ImmutableHashSet<Resource>.Empty, default!, isKnown: true, isSecret: false);
|
|
}
|
|
|
|
private async Task<(Struct Return, ImmutableHashSet<Resource> Dependencies)> CallRawAsync(
|
|
string token, CallArgs args, Resource? self, CallOptions? options)
|
|
{
|
|
var label = $"Calling function: token={token} asynchronously";
|
|
Log.Debug(label);
|
|
|
|
// Be resilient to misbehaving callers.
|
|
// ReSharper disable once ConstantNullCoalescingCondition
|
|
args ??= CallArgs.Empty;
|
|
|
|
// Wait for all values to be available, and then perform the RPC.
|
|
var argsDict = await args.ToDictionaryAsync().ConfigureAwait(false);
|
|
|
|
// If we have a self arg, include it in the args.
|
|
if (self != null)
|
|
{
|
|
argsDict = argsDict.SetItem("__self__", self);
|
|
}
|
|
|
|
var (serialized, argDependencies) = await SerializeFilteredPropertiesAsync(
|
|
$"call:{token}",
|
|
argsDict, _ => true,
|
|
keepResources: true,
|
|
keepOutputValues: await MonitorSupportsOutputValues().ConfigureAwait(false)).ConfigureAwait(false);
|
|
Log.Debug($"Call RPC prepared: token={token}" +
|
|
(_excessiveDebugOutput ? $", obj={serialized}" : ""));
|
|
|
|
// Determine the provider and version to use.
|
|
ProviderResource? provider;
|
|
string? version;
|
|
string? pluginDownloadURL;
|
|
if (self != null)
|
|
{
|
|
provider = self._provider;
|
|
version = self._version;
|
|
pluginDownloadURL = self._pluginDownloadURL;
|
|
}
|
|
else
|
|
{
|
|
provider = GetProvider(token, options);
|
|
version = options?.Version;
|
|
pluginDownloadURL = options?.PluginDownloadURL;
|
|
}
|
|
var providerReference = await ProviderResource.RegisterAsync(provider).ConfigureAwait(false);
|
|
|
|
// Create the request.
|
|
var request = new CallRequest
|
|
{
|
|
Tok = token,
|
|
Provider = providerReference ?? "",
|
|
Version = version ?? "",
|
|
PluginDownloadURL = pluginDownloadURL ?? "",
|
|
Args = serialized,
|
|
};
|
|
|
|
// Add arg dependencies to the request.
|
|
foreach (var (argName, directDependencies) in argDependencies)
|
|
{
|
|
var urns = await GetAllTransitivelyReferencedResourceUrnsAsync(directDependencies).ConfigureAwait(false);
|
|
var deps = new CallRequest.Types.ArgumentDependencies();
|
|
deps.Urns.AddRange(urns);
|
|
request.ArgDependencies.Add(argName, deps);
|
|
}
|
|
|
|
// Kick off the call.
|
|
var result = await Monitor.CallAsync(request).ConfigureAwait(false);
|
|
|
|
// Handle failures.
|
|
if (result.Failures.Count > 0)
|
|
{
|
|
var reasons = "";
|
|
foreach (var reason in result.Failures)
|
|
{
|
|
if (reasons != "")
|
|
{
|
|
reasons += "; ";
|
|
}
|
|
|
|
reasons += $"{reason.Reason} ({reason.Property})";
|
|
}
|
|
|
|
throw new CallException($"Call of '{token}' failed: {reasons}");
|
|
}
|
|
|
|
// Unmarshal return dependencies.
|
|
var dependencies = ImmutableHashSet.CreateBuilder<Resource>();
|
|
foreach (var (_, returnDependencies) in result.ReturnDependencies)
|
|
{
|
|
foreach (var urn in returnDependencies.Urns)
|
|
{
|
|
dependencies.Add(new DependencyResource(urn));
|
|
}
|
|
}
|
|
|
|
return (result.Return, dependencies.ToImmutable());
|
|
}
|
|
|
|
private static ProviderResource? GetProvider(string token, CallOptions? options)
|
|
=> options?.Provider ?? options?.Parent?.GetProvider(token);
|
|
|
|
private sealed class CallException : Exception
|
|
{
|
|
public CallException(string error)
|
|
: base(error)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|