Skip to content

Commit

Permalink
Wait for resources in the input property dependencies (#444)
Browse files Browse the repository at this point in the history
Besides the explicitly provided resources from the `dependsOn` option,
we need to also take into account the resource dependencies from the
invoke’s arguments.

pulumi/pulumi#17747
  • Loading branch information
julienp authored Jan 17, 2025
1 parent c0cf4c4 commit 67bb690
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 23 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/bug-fixes-444.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
component: sdk
kind: bug-fixes
body: Wait for resources in the input property dependencies
time: 2025-01-09T16:16:01.580923+01:00
custom:
PR: "444"
52 changes: 49 additions & 3 deletions sdk/Pulumi.Tests/Deployment/DeploymentInvokeDependsOnTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;

Expand Down Expand Up @@ -62,8 +63,10 @@ public async Task DeploymentInvokeDependsOnUnknown()
var testOptions = new TestOptions();
var (resources, outputs) = await Deployment.TestAsync(mocks, testOptions, () =>
{
var resource = new MyCustomResource("some-resource", null, new CustomResourceOptions());
var deps = new InputList<Resource>();
var dep_remote = new DependencyResource("some:urn");
deps.Add(dep_remote);
var resource = new MyCustomResource("some-resource", null, new CustomResourceOptions());
deps.Add(resource);

var resultOutput = TestFunction.Invoke(new FunctionArgs(), new InvokeOutputOptions { DependsOn = deps });
Expand All @@ -88,6 +91,41 @@ public async Task DeploymentInvokeDependsOnUnknown()
}


[Fact]
public async Task DeploymentInvokeInputDependencies()
{
var mocks = new InvokeMocks(dryRun: true);
var testOptions = new TestOptions();
var (resources, outputs) = await Deployment.TestAsync(mocks, testOptions, () =>
{
var resource = new MyCustomResource("some-resource", null, new CustomResourceOptions());
var deps = new HashSet<Resource> { resource };
var value = Output.Create("my value").WithDependencies(deps.ToImmutableHashSet());

var resultOutput = TestFunction.Invoke(new()
{
Value = value
});
return new Dictionary<string, object?>
{
["functionResult"] = resultOutput,
};
});


if (outputs["functionResult"] is Output<FunctionResult> functionResult)
{

var dataTask = await functionResult.DataTask.ConfigureAwait(false);
Assert.False(dataTask.IsKnown);
}
else
{
throw new Exception($"Expected result to be of type Output<FunctionResult>");
}
}


public sealed class MyArgs : ResourceArgs { }

[ResourceType("test:DeploymentInvokeDependsOnTests:resource", null)]
Expand All @@ -99,7 +137,16 @@ public MyCustomResource(string name, MyArgs? args, CustomResourceOptions? option
}
}

public sealed class FunctionArgs : global::Pulumi.InvokeArgs { }
public sealed class FunctionArgs : global::Pulumi.InvokeArgs
{
[Input("value", required: false)]
public Input<string> Value { get; set; } = null!;
public FunctionArgs()
{
}
public static new FunctionArgs Empty => new FunctionArgs();

}

[OutputType]
public sealed class FunctionResult
Expand Down Expand Up @@ -144,7 +191,6 @@ public Task<object> CallAsync(MockCallArgs args)
Resolved = true;
if (DryRun)
{

return (null, new Dictionary<string, object>());
}
else
Expand Down
48 changes: 28 additions & 20 deletions sdk/Pulumi/Deployment/Deployment_Invoke.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,35 +156,43 @@ private async Task<OutputData<T>> RawInvoke<T>(

// Resource dependencies that will be added to the Output
var resourceDependencies = new HashSet<Resource>();
// If we depend on any CustomResources, we need to ensure that their
// ID is known before proceeding. If it is not known, we will return
// an unknown result.
// Add the dependencies from the inputs to the set of resources to wait for.
var resourcesToWaitFor = serializedArgs.PropertyToDependentResources.Values.SelectMany(r => r).ToHashSet<Resource>();
if (options is InvokeOutputOptions outputOptions)
{
var deps = outputOptions.DependsOn;
if (deps != null)
{
// The direct dependencies of the invoke.
var resourceList = await GatherExplicitDependenciesAsync(deps).ConfigureAwait(false);
// The expanded set of dependencies, including children of components.
var expandedResourceList = GetAllTransitivelyReferencedResources(resourceList.ToHashSet());
// If we depend on any CustomResources, we need to ensure that their
// ID is known before proceeding. If it is not known, we will return
// an unknown result.
foreach (var resource in expandedResourceList)
resourcesToWaitFor.UnionWith(resourceList);
// Add the resources to the list of dependencies that we'll add to the Output
resourceDependencies.UnionWith(resourceList);
}
}
// The expanded set of dependencies, including children of components.
var expandedResourceList = GetAllTransitivelyReferencedResources(resourcesToWaitFor.ToHashSet());
resourcesToWaitFor.UnionWith(expandedResourceList);
// Ensure that all resource IDs are known before proceeding.
foreach (var resource in resourcesToWaitFor)
{
// Check if it's an instance of CustomResource.
// DependencyResources inherit from CustomResource, but they
// don't set the id. Skip them.
if (resource is CustomResource customResource && customResource.Id != null)
{
var idData = await customResource.Id.DataTask.ConfigureAwait(false);
if (!idData.IsKnown)
{
// check if it's an instance of CustomResource
if (resource is CustomResource customResource)
{
var idData = await customResource.Id.DataTask.ConfigureAwait(false);
if (!idData.IsKnown)
{
return new OutputData<T>(resources: ImmutableHashSet<Resource>.Empty,
value: default!,
isKnown: false,
isSecret: false);
}

}
return new OutputData<T>(resources: ImmutableHashSet<Resource>.Empty,
value: default!,
isKnown: false,
isSecret: false);
}
resourceDependencies.UnionWith(resourceList);

}
}

Expand Down

0 comments on commit 67bb690

Please sign in to comment.