-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[sdk/dotnet] Input<T>
eagerly converts values to Output<T>
leading to undesirable preview diffs
#22
Comments
Shouldn't preview diffs print Output's that are known? |
The problem is the way Internally
So when adding an One relatively simple solution is to change
Then, for the most part, we can keep the added values as-is by storing them in |
👍 from the consistency across SDK POV, the other SDKs have the capability to discriminate Input into either T, Promise or Output, so .NET SDK should also have that in the programming model. Fraser raised that perhaps .NET SDK can discriminate Output to find out if it's prompt known T, potentially eventually removing T. It sounds like something that should not block this work, but if we go that way I'd like to figure out how that works with other SDKs, how does imaginary "meta-SDK" code translate into .NET SDK under that API. |
So still 👍 on the main idea listed. Now the subtle implementation point on Lists (I think map follows similarly) - to follow up on the design discussion today. I made a point earlier about Tuple/Map2 interface on Input as a potential implementation path to updated InputList, but it was not very clear. I went ahead and wrote some code that clarified the issue for me at least: https://gist.github.com/t0yv0/84e4f976e190aabbd1ccd666b89d73bd In a world where Input discriminates between The code above works as expected, however there is a problem if
So in addition to changing internals of InputList, to carry this through previews, we either need to change the base type of InputList which is breaking or else we change the way reflective machinery considers InputList values when it encounters them; either way it seems potentially a lot more invasive change than a simple change to InputList internals. If I am wrong in this prediction you Justin can work out a solution that is a purely internal change to InputList I would be very curious to see how that works. This may have been exactly the point @Frassle was making. They are reasoning about this much faster than I am. But I do not want to assume. |
@t0yv0 Yeh these types are a bit confusing and hard to reason about, my first thinking was that either this is really just A) Also all of this is still fine in an SDK where |
I'm still struggling to understand how the PR will solve this example at the top, and I'm running out of time to try it out, guess need to wait for the tests: var bucket = new Bucket("bucket", new BucketArgs {
Tags = {
{ "hello", "world" },
{ "hi", "there" },
{ "foo", randString.Id },
}
}); What's the type of Tags? Looking at def of BucketArgs.Tags I see: /// <summary>
/// A mapping of tags to assign to the bucket.
/// </summary>
public InputMap<string> Tags
{
get => _tags ?? (_tags = new InputMap<string>());
set => _tags = value;
} And this header remains: public sealed class InputMap<V> : Input<ImmutableDictionary<string, V>>, IEnumerable, IAsyncEnumerable<Input<KeyValuePair<string, V>>> So then, if our framework casts it to If our framework however casts it to
|
Love the discussion above and agree with the point on it trying to be
I definitely intend to add a bunch more tests, but the draft does include an output value serialization test that indirectly demonstrates the fix (using a secret rather than unknown). new object[]
{
new InputMap<string> { { "foo", Output.CreateSecret("hello") } },
ImmutableDictionary<string, object>.Empty.Add("foo", CreateOutputValue("hello", isSecret: true))
}, Without the changes in the PR, the serialized value is: {
"4dabf18193072939515e22adb298388d": "d0e6a833031e9bbcd3f4e8bde6ca49a4",
"value": { "foo": "hello" },
"secret": true
} The expected serialized value (with the changes in the PR): {
"foo": {
"4dabf18193072939515e22adb298388d": "d0e6a833031e9bbcd3f4e8bde6ca49a4",
"value": "hello",
"secret": true
}
} The serializer isn't casting to The serializer currently does this: The implementation of The change in the PR changes the serializer to not use So the value that ends up being serialized will either be For the former, it'll then hit the non-generic For the latter, it'll hit the non-generic |
Hitting this issue while developing Pulumi Service Provider - really confusing behavior, forced to rewrite a map into an array of custom objects. Would be great to fix and avoid this. |
### Summary - Fixes: #447 (kinda) - The reason secret was failing to create is due to this c# specific bug - pulumi/pulumi-dotnet#22 - A secret among env vars made the whole object secret and it was skipped - Recent change #467 actually made the issue worse - it is creating the env vars, but NOT secret (Thankfully I never released after that PR! 😅 ) - This PR adds cascading logic to envVars and username values - they are the only ones that are optionally secret ### Testing - Manual test
I had some WIP changes to address this when the .NET SDK was still in the pu/pu repo: pulumi/pulumi@master...justin/dotnet_input |
Fixes #22 InputList and InputMap are types used to make it easier to work with lists/maps where both the overall structure or individual elements can be `Input`s. The current implementation just flattens everything to a top-level input. That is `InputList<T>` is just `Input<ImmutableArray<T>>`. So if you start with a known empty list then add an unknown input element to it you end up with an unknown list structure. This is not ideal for previews (see #22) and doesn't align with the other SDKs where if you add an unknown element to a list the overall list shape is still known and sent to the engine for preview correctly. This PR fixes the above by keeping track of non-flattened data inside InputMap/List. i.e. the internal shape of `InputList<T>` is now `Input<ImmutableArray<Input<T>>>`. This allows us to take a known list and add an unknown element to it and leave the overall shape of the list still known. We do this without changing the public surface of InputList/Map, so they still inherit from `Input<ImmutableArray<T>>`. We add another field for the nested structure and ensure that we just always update both fields (the one in InputList/Map and the one in the base Input class) together. In the property serializer we then use reflection to pull out the fully nested value (e.g. `Input<ImmutableArray<Input<T>>>`) and then recursively serialize that, rather than hitting the standard `IInput` case and getting the flattened value (e.g. `Input<ImmutableArray<T>>`).
This issue has been addressed in PR #449 and shipped in release v3.73.0. |
In the .NET SDK,
Input<T>
(andInputList<T>
,InputMap<V>
,InputUnion<T0, T1>
, andInputJson
) eagerly stores the value ofT
as anOutput<T>
, which leads to undesirable preview diffs.https://github.com/pulumi/pulumi/blob/0a38bc295cfc5ae3c325dcece169da5a5e80fffc/sdk/dotnet/Pulumi/Core/Input.cs#L22-L31
For example, consider this program:
Expected preview diff:
Actual:
This also means that we have the same issue with programs that create multi-lang components, even with output values added in pulumi/pulumi#8316.
To address this, we need to fix
Input<T>
et. al. to not eagerly convert values toOutput<T>
. Instead, internally, it can keep a discriminated union of the plainT
andOutput<T>
and only convert toOutput<T>
when needed.The text was updated successfully, but these errors were encountered: