Compare commits
1 commit
master
...
t0yv0/fix-
Author | SHA1 | Date | |
---|---|---|---|
f223ba0295 |
|
@ -22,17 +22,9 @@ namespace Pulumi
|
||||||
// finished. Otherwise, we might actually read and get the result back *prior* to the
|
// 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
|
// object finishing initializing. Note: this is not a speculative concern. This is
|
||||||
// something that does happen and has to be accounted for.
|
// 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(
|
_runner.RegisterTask(
|
||||||
$"{nameof(IDeploymentInternal.ReadOrRegisterResource)}: {resource.GetResourceType()}-{resource.GetResourceName()}",
|
$"{nameof(IDeploymentInternal.ReadOrRegisterResource)}: {resource.GetResourceType()}-{resource.GetResourceName()}",
|
||||||
CompleteResourceAsync(resource, remote, newDependency, args, options, completionSources));
|
CompleteResourceAsync(resource, remote, newDependency, args, options, resource.CompletionSources));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(string urn, string id, Struct data, ImmutableDictionary<string, ImmutableHashSet<Resource>> dependencies)> ReadOrRegisterResourceAsync(
|
private async Task<(string urn, string id, Struct data, ImmutableDictionary<string, ImmutableHashSet<Resource>> dependencies)> ReadOrRegisterResourceAsync(
|
||||||
|
|
|
@ -20,33 +20,33 @@ namespace Pulumi
|
||||||
/// The child resources of this resource. We use these (only from a ComponentResource) to
|
/// The child resources of this resource. We use these (only from a ComponentResource) to
|
||||||
/// allow code to dependOn a ComponentResource and have that effectively mean that it is
|
/// allow code to dependOn a ComponentResource and have that effectively mean that it is
|
||||||
/// depending on all the CustomResource children of that component.
|
/// depending on all the CustomResource children of that component.
|
||||||
///
|
///
|
||||||
/// Important! We only walk through ComponentResources.They're the only resources that
|
/// Important! We only walk through ComponentResources.They're the only resources that
|
||||||
/// serve as an aggregation of other primitive(i.e.custom) resources.While a custom resource
|
/// serve as an aggregation of other primitive(i.e.custom) resources.While a custom resource
|
||||||
/// can be a parent of other resources, we don't want to ever depend on those child
|
/// can be a parent of other resources, we don't want to ever depend on those child
|
||||||
/// resource. If we do, it's simple to end up in a situation where we end up depending on a
|
/// resource. If we do, it's simple to end up in a situation where we end up depending on a
|
||||||
/// child resource that has a data cycle dependency due to the data passed into it. An
|
/// child resource that has a data cycle dependency due to the data passed into it. An
|
||||||
/// example of how this would be bad is:
|
/// example of how this would be bad is:
|
||||||
///
|
///
|
||||||
/// <c>
|
/// <c>
|
||||||
/// var c1 = new CustomResource("c1");
|
/// var c1 = new CustomResource("c1");
|
||||||
/// var c2 = new CustomResource("c2", { parentId = c1.id }, { parent = c1 });
|
/// var c2 = new CustomResource("c2", { parentId = c1.id }, { parent = c1 });
|
||||||
/// var c3 = new CustomResource("c3", { parentId = c1.id }, { parent = c1 });
|
/// var c3 = new CustomResource("c3", { parentId = c1.id }, { parent = c1 });
|
||||||
/// </c>
|
/// </c>
|
||||||
///
|
///
|
||||||
/// The problem here is that 'c2' has a data dependency on 'c1'. If it tries to wait on
|
/// The problem here is that 'c2' has a data dependency on 'c1'. If it tries to wait on
|
||||||
/// 'c1' it will walk to the children and wait on them.This will mean it will wait on 'c3'.
|
/// 'c1' it will walk to the children and wait on them.This will mean it will wait on 'c3'.
|
||||||
/// But 'c3' will be waiting in the same manner on 'c2', and a cycle forms. This normally
|
/// But 'c3' will be waiting in the same manner on 'c2', and a cycle forms. This normally
|
||||||
/// does not happen with ComponentResources as they do not have any data flowing into
|
/// does not happen with ComponentResources as they do not have any data flowing into
|
||||||
/// them.The only way you would be able to have a problem is if you had this sort of coding
|
/// them.The only way you would be able to have a problem is if you had this sort of coding
|
||||||
/// pattern:
|
/// pattern:
|
||||||
///
|
///
|
||||||
/// <c>
|
/// <c>
|
||||||
/// var c1 = new ComponentResource("c1");
|
/// var c1 = new ComponentResource("c1");
|
||||||
/// var c2 = new CustomResource("c2", { parentId = c1.urn }, { parent: c1 });
|
/// var c2 = new CustomResource("c2", { parentId = c1.urn }, { parent: c1 });
|
||||||
/// var c3 = new CustomResource("c3", { parentId = c1.urn }, { parent: c1 });
|
/// var c3 = new CustomResource("c3", { parentId = c1.urn }, { parent: c1 });
|
||||||
/// </c>
|
/// </c>
|
||||||
///
|
///
|
||||||
/// However, this would be pretty nonsensical as there is zero need for a custom resource to
|
/// However, this would be pretty nonsensical as there is zero need for a custom resource to
|
||||||
/// ever need to reference the urn of a component resource. So it's acceptable if that sort
|
/// ever need to reference the urn of a component resource. So it's acceptable if that sort
|
||||||
/// of pattern failed in practice.
|
/// of pattern failed in practice.
|
||||||
|
@ -103,6 +103,8 @@ namespace Pulumi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly string? _version;
|
internal readonly string? _version;
|
||||||
|
|
||||||
|
internal readonly ImmutableDictionary<string,IOutputCompletionSource> CompletionSources;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and registers a new resource object. <paramref name="type"/> is the fully
|
/// 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
|
/// qualified type token and <paramref name="name"/> is the "name" part to use in creating a
|
||||||
|
@ -128,6 +130,7 @@ namespace Pulumi
|
||||||
_name = "";
|
_name = "";
|
||||||
_protect = false;
|
_protect = false;
|
||||||
_providers = ImmutableDictionary<string, ProviderResource>.Empty;
|
_providers = ImmutableDictionary<string, ProviderResource>.Empty;
|
||||||
|
CompletionSources = ImmutableDictionary<string, IOutputCompletionSource>.Empty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +140,13 @@ namespace Pulumi
|
||||||
if (string.IsNullOrEmpty(name))
|
if (string.IsNullOrEmpty(name))
|
||||||
throw new ArgumentException("'name' cannot be null or empty.", nameof(name));
|
throw new ArgumentException("'name' cannot be null or empty.", nameof(name));
|
||||||
|
|
||||||
|
// Initialize all Output properties crucially including
|
||||||
|
// `Urn` to non-nil Output values, record
|
||||||
|
// `CompletionSources`. This needs to happen before
|
||||||
|
// partially initialized `this` is exposed to other
|
||||||
|
// threads via `parentResource.ChildResources`.
|
||||||
|
CompletionSources = OutputCompletionSource.InitializeOutputs(this);
|
||||||
|
|
||||||
// Before anything else - if there are transformations registered, invoke them in order
|
// Before anything else - if there are transformations registered, invoke them in order
|
||||||
// to transform the properties and options assigned to this resource.
|
// to transform the properties and options assigned to this resource.
|
||||||
var parent = type == Stack._rootPulumiStackTypeName
|
var parent = type == Stack._rootPulumiStackTypeName
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Pulumi.Serialization
|
||||||
|
|
||||||
void TrySetException(Exception exception);
|
void TrySetException(Exception exception);
|
||||||
void TrySetDefaultResult(bool isKnown);
|
void TrySetDefaultResult(bool isKnown);
|
||||||
|
|
||||||
void SetStringValue(string value, bool isKnown);
|
void SetStringValue(string value, bool isKnown);
|
||||||
void SetValue(OutputData<object?> data);
|
void SetValue(OutputData<object?> data);
|
||||||
}
|
}
|
||||||
|
@ -72,13 +72,15 @@ namespace Pulumi.Serialization
|
||||||
if (!propType.IsConstructedGenericType ||
|
if (!propType.IsConstructedGenericType ||
|
||||||
propType.GetGenericTypeDefinition() != typeof(Output<>))
|
propType.GetGenericTypeDefinition() != typeof(Output<>))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"{propFullName} was not an Output<T>");
|
continue;
|
||||||
|
// throw new InvalidOperationException($"{propFullName} was not an Output<T>");
|
||||||
}
|
}
|
||||||
|
|
||||||
var setMethod = prop.DeclaringType!.GetMethod("set_" + prop.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
var setMethod = prop.DeclaringType!.GetMethod("set_" + prop.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||||
if (setMethod == null)
|
if (setMethod == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"{propFullName} did not have a 'set' method");
|
continue;
|
||||||
|
//throw new InvalidOperationException($"{propFullName} did not have a 'set' method");
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputTypeArg = propType.GenericTypeArguments.Single();
|
var outputTypeArg = propType.GenericTypeArguments.Single();
|
||||||
|
|
Loading…
Reference in a new issue