sparse-intern-71089
05/27/2021, 11:49 PMworried-city-86458
05/28/2021, 12:03 AMworried-city-86458
05/28/2021, 12:04 AMbored-activity-40468
05/28/2021, 12:13 AMworried-city-86458
05/28/2021, 12:25 AMTaskCompletionSource
only completes during up and not preview.
So I'd still have to be careful to avoid `await`ing it during preview.worried-city-86458
05/28/2021, 12:28 AMpublic static class OutputExtensions
{
public static Task<T> GetValueAsync<T>(this Output<T> output) =>
(Task<T>)typeof(Output<T>).GetMethod("GetValueAsync", BindingFlags.NonPublic)!.Invoke(output, Array.Empty<object?>())!;
}
worried-city-86458
05/28/2021, 12:29 AMworried-city-86458
05/28/2021, 4:47 AMworried-city-86458
05/28/2021, 6:13 AMWhat if those outputs never resolve and your await never completes? That sounds dangerous to me...Excuse my ignorance but how would that be different from doing
Output.Tuple.Apply
to render the template?
(Which is a lot more awkward to use, hence I'd much prefer to push the await to Scriban)tall-librarian-49374
05/28/2021, 6:38 AMApply
. The callback will just never be invoked. An await
will freeze forever.worried-city-86458
05/28/2021, 6:53 AMOutput.Tuple
will still await all the tuple items before calling apply.
So if the outputs never resolve, what's the difference between awaiting there vs in a Scriban function?
Scriban is async all the way down, so nothing should be "blocking".tall-librarian-49374
05/28/2021, 7:36 AMTupleHelperAsync
in that example, it all stays within outputs that know how to handle unknown values etc. I guess I should come up with a repro of a problematic case. It would involve an await in the main function.tall-librarian-49374
05/28/2021, 7:44 AMawait Deployment.RunAsync(async () =>
{
var resourceGroup = new ResourceGroup("my-rg");
await resourceGroup.Name.GetValueAsync();
return new Dictionary<string, object?>
{
{ "test", "this will never run in the initial preview" }
};
});
worried-city-86458
05/28/2021, 9:40 AMreturn Output.Create(template.RenderAsync(context).AsTask());
tall-librarian-49374
05/28/2021, 9:44 AMawait Deployment.RunAsync(async () =>
{
return new Dictionary<string, object?>
{
{ "test", Output.Create(Task.Run(async () =>
{
var resourceGroup = new ResourceGroup("my-rg");
await resourceGroup.Name.GetValueAsync();
return "this will never run either";
})) }
};
});
worried-city-86458
05/28/2021, 10:15 AMworried-city-86458
05/28/2021, 10:22 AMtall-librarian-49374
05/28/2021, 11:55 AMawait Deployment.RunAsync(async () =>
{
return new Dictionary<string, object?>
{
{ "test", Output.Create(Task.Run(async () =>
{
var resourceGroup = new ResourceGroup("my-rg");
return Output.Tuple(resourceGroup.Name, resourceGroup.Location).Apply(v => v.Item1);
})) }
};
});
The preview will not hang (it won’t show an output).worried-city-86458
05/28/2021, 6:17 PMworried-city-86458
05/28/2021, 6:19 PMworried-city-86458
05/28/2021, 6:39 PMApply
have the gate in ApplyHelperAsync?worried-city-86458
05/28/2021, 6:45 PMworried-city-86458
05/28/2021, 6:51 PMOutput.Create(template.RenderAsync(context).AsTask());
If I understand correctly, what's missing is another building block:
Output.Invoke(template.RenderAsync(context).AsTask());
tall-librarian-49374
05/28/2021, 7:25 PMtall-librarian-49374
05/28/2021, 7:26 PMOutput.Invoke
?worried-city-86458
05/28/2021, 7:32 PMOutput.Prune
would be more descriptive.
Then again maybe it could be baked into Output.Create
since it already knows about Task
.
As for what it is, I was thinking about a helper that "prunes task execution" during preview.
i.e. if it's got a known value, great, use that, otherwise like Apply
it should short circuit and return an unknown value.worried-city-86458
05/28/2021, 7:33 PMworried-city-86458
05/28/2021, 7:34 PMApply
semantics but for the initial Create
worried-city-86458
05/28/2021, 7:42 PMawait Deployment.RunAsync(async () =>
{
return new Dictionary<string, object?>
{
{ "test", Output.Prune(() => Output.Create(Task.Run(async () =>
{
var resourceGroup = new ResourceGroup("my-rg");
await resourceGroup.Name.GetValueAsync();
return "this will never run either ... but it won't hang preview";
}))) }
};
});
worried-city-86458
05/28/2021, 7:49 PMPrune
chains the Create
, the indirection is enough:
await Deployment.RunAsync(async () =>
{
return new Dictionary<string, object?>
{
{ "test", Output.Prune(() => Task.Run(async () =>
{
var resourceGroup = new ResourceGroup("my-rg");
await resourceGroup.Name.GetValueAsync();
return "this will never run either ... but it won't hang preview";
})) }
};
});
worried-city-86458
05/28/2021, 7:50 PMCreate
.
But it probably needs to be more explicit that the Task
will be bypassed during preview.bored-activity-40468
05/28/2021, 7:53 PMtall-librarian-49374
05/28/2021, 9:55 PMworried-city-86458
05/28/2021, 11:41 PMworried-city-86458
05/28/2021, 11:41 PMOutput.Prune(() => template.RenderAsync(context).AsTask());
worried-city-86458
05/28/2021, 11:44 PMOutput.Create(() => template.RenderAsync(context).AsTask());
worried-city-86458
05/29/2021, 12:02 AMApply
semantics, I think it's this:
public static partial class Output
{
public static Output<T> Create<T>(Func<Task<T>> func)
=> Create(default(T)!).Apply(_ => func());
}
worried-city-86458
05/29/2021, 6:01 AMworried-city-86458
05/29/2021, 6:04 AMApply
on an unknown output.tall-librarian-49374
05/29/2021, 6:08 AMOutput.All
your arguments and only then call template.RenderAsync
in the apply?
Q2: If Create(default(T)!).Apply(_ => func())
works for you, why do you need to add stuff to Pulumi core?tall-librarian-49374
05/29/2021, 6:11 AMSafeAwaitable
- it’s going to be hard to explain to folks what that is. It also defies the purpose of hiding GetValueAsync
in OutputUtilities
.worried-city-86458
05/29/2021, 6:16 AMOutput.All
and Output.Tuple
are very clumsy to use in comparison to what I can do otherwise using an anonymous type:
var yaml = RenderTemplate("AwsAuth.yaml", ReadResource, new { deployerRoleArn, nodeRoleArn, k8sFullAccessRoleArn, k8sReadOnlyRoleArn });
... and dealing with pulumi outputs in the template
A2: Create(default(T)!).Apply(_ => func())
didn't work - it would hang since the initial output was known
... the Apply
gate only works when the initial output is unknownworried-city-86458
05/29/2021, 6:19 AMSafeAwaitable
will skip awaiting the specified awaitable during preview.worried-city-86458
05/29/2021, 6:20 AMworried-city-86458
05/29/2021, 6:22 AMworried-city-86458
05/29/2021, 6:25 AMtall-librarian-49374
05/29/2021, 6:26 AMworried-city-86458
05/29/2021, 6:27 AMpreview
(or dry run)worried-city-86458
05/29/2021, 6:28 AMtall-librarian-49374
05/29/2021, 6:29 AMtall-librarian-49374
05/29/2021, 6:31 AMmake some things publicAll you need is
Unknown
, right?worried-city-86458
05/29/2021, 6:32 AMOutputUtilities.GetValueAsync
so I can directly await an output in my awaitable.worried-city-86458
05/29/2021, 6:33 AMUnknown
is used in a few other places so I could refactor those to use it too.tall-librarian-49374
05/29/2021, 6:37 AMUnknown
seems safer to me. I’ll take another look on Monday.worried-city-86458
05/29/2021, 8:24 PMOutput.CreateUnknown
which I now think is a better nameworried-city-86458
05/30/2021, 12:42 AM