Skip to content

Commit b74b3ce

Browse files
authored
Bring back the correct fallback behavior for calculating aliases for older Pulumi engines. (#94)
1 parent 1273967 commit b74b3ce

File tree

5 files changed

+139
-92
lines changed

5 files changed

+139
-92
lines changed

CHANGELOG_PENDING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313

1414
- [sdk] Correctly check for alias support in the engine and map fully specified alias urns.
1515
[#88](https://github.com/pulumi/pulumi-dotnet/pull/88)
16+
17+
- [sdk] Bring back the correct fallback behavior for calculating aliases for older Pulumi engines.

sdk/Pulumi/Deployment/Deployment_Prepare.cs

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,9 @@ static async Task<T> Resolve<T>(Input<T>? input, T whenUnknown)
154154
// map each alias in the options to its corresponding Alias proto definition.
155155
foreach (var alias in options.Aliases)
156156
{
157-
var resolvedAlias = await alias.ToOutput().GetValueAsync(whenUnknown: default!).ConfigureAwait(false);
158-
if (resolvedAlias == null)
157+
var defaultAliasWhenUnknown = new Alias();
158+
var resolvedAlias = await alias.ToOutput().GetValueAsync(whenUnknown: defaultAliasWhenUnknown).ConfigureAwait(false);
159+
if (ReferenceEquals(resolvedAlias, defaultAliasWhenUnknown))
159160
{
160161
// alias contains unknowns, skip it.
161162
continue;
@@ -180,12 +181,12 @@ static async Task<T> Resolve<T>(Input<T>? input, T whenUnknown)
180181
Project = await Resolve(resolvedAlias.Project, ""),
181182
};
182183

183-
// Here we specify whether the alias has a parent or not.
184+
// Here we specify whether the alias has a parent or not.
184185
// aliasSpec must only specify one of NoParent or ParentUrn, not both!
185186
// this determines the wire format of the alias which is used by the engine.
186187
if (resolvedAlias.Parent == null && resolvedAlias.ParentUrn == null)
187188
{
188-
aliasSpec.NoParent = true;
189+
aliasSpec.NoParent = resolvedAlias.NoParent;
189190
}
190191
else if (resolvedAlias.Parent != null)
191192
{
@@ -215,15 +216,8 @@ static async Task<T> Resolve<T>(Input<T>? input, T whenUnknown)
215216
// If the engine does not support alias specs, we fall back to the old behavior of
216217
// collapsing all aliases into urns.
217218
var uniqueAliases = new HashSet<string>();
218-
foreach (var alias in options.Aliases)
219+
foreach (var aliasUrn in resource._aliases)
219220
{
220-
var aliasUrn = CollapseAliasToUrn(
221-
alias: alias,
222-
defaultName: resource.GetResourceName(),
223-
defaultType: resource.GetResourceType(),
224-
defaultParent: options.Parent
225-
);
226-
227221
var aliasValue = await Resolve(aliasUrn, "");
228222
if (!string.IsNullOrEmpty(aliasValue) && uniqueAliases.Add(aliasValue))
229223
{
@@ -238,82 +232,6 @@ static async Task<T> Resolve<T>(Input<T>? input, T whenUnknown)
238232
return aliases;
239233
}
240234

241-
private static Output<string> CollapseAliasToUrn(
242-
Input<Alias> alias,
243-
string defaultName,
244-
string defaultType,
245-
Resource? defaultParent)
246-
{
247-
return alias.ToOutput().Apply(a =>
248-
{
249-
if (a.Urn != null)
250-
{
251-
CheckNull(a.Name, nameof(a.Name));
252-
CheckNull(a.Type, nameof(a.Type));
253-
CheckNull(a.Project, nameof(a.Project));
254-
CheckNull(a.Stack, nameof(a.Stack));
255-
CheckNull(a.Parent, nameof(a.Parent));
256-
CheckNull(a.ParentUrn, nameof(a.ParentUrn));
257-
if (a.NoParent)
258-
ThrowAliasPropertyConflict(nameof(a.NoParent));
259-
260-
return Output.Create(a.Urn);
261-
}
262-
263-
var name = a.Name ?? defaultName;
264-
var type = a.Type ?? defaultType;
265-
var project = a.Project ?? Deployment.Instance.ProjectName;
266-
var stack = a.Stack ?? Deployment.Instance.StackName;
267-
268-
var parentCount =
269-
(a.Parent != null ? 1 : 0) +
270-
(a.ParentUrn != null ? 1 : 0) +
271-
(a.NoParent ? 1 : 0);
272-
273-
if (parentCount >= 2)
274-
{
275-
throw new ArgumentException(
276-
$"Only specify one of '{nameof(Alias.Parent)}', '{nameof(Alias.ParentUrn)}' or '{nameof(Alias.NoParent)}' in an {nameof(Alias)}");
277-
}
278-
279-
var (parent, parentUrn) = GetParentInfo(defaultParent, a);
280-
281-
if (name == null)
282-
throw new ArgumentNullException("No valid 'Name' passed in for alias.");
283-
284-
if (type == null)
285-
throw new ArgumentNullException("No valid 'type' passed in for alias.");
286-
287-
return Pulumi.Urn.Create(name, type, parent, parentUrn, project, stack);
288-
});
289-
}
290-
291-
292-
private static void CheckNull<T>(T? value, string name) where T : class
293-
{
294-
if (value != null)
295-
{
296-
ThrowAliasPropertyConflict(name);
297-
}
298-
}
299-
300-
private static void ThrowAliasPropertyConflict(string name)
301-
=> throw new ArgumentException($"{nameof(Alias)} should not specify both {nameof(Alias.Urn)} and {name}");
302-
303-
private static (Resource? parent, Input<string>? urn) GetParentInfo(Resource? defaultParent, Alias alias)
304-
{
305-
if (alias.Parent != null)
306-
return (alias.Parent, null);
307-
308-
if (alias.ParentUrn != null)
309-
return (null, alias.ParentUrn);
310-
311-
if (alias.NoParent)
312-
return (null, null);
313-
314-
return (defaultParent, null);
315-
}
316-
317235
private static Task<ImmutableArray<Resource>> GatherExplicitDependenciesAsync(InputList<Resource> resources)
318236
=> resources.ToOutput().GetValueAsync(whenUnknown: ImmutableArray<Resource>.Empty);
319237

sdk/Pulumi/Deployment/Deployment_RegisterResource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private static void PopulateRequest(RegisterResourceRequest request, PrepareResu
7979
}
8080
}
8181

82-
private async static Task<RegisterResourceRequest> CreateRegisterResourceRequest(
82+
private static async Task<RegisterResourceRequest> CreateRegisterResourceRequest(
8383
string type, string name, bool custom, bool remote, ResourceOptions options)
8484
{
8585
var customOpts = options as CustomResourceOptions;
@@ -137,7 +137,7 @@ private static string TimeoutString(TimeSpan? timeSpan)
137137
// https://golang.org/pkg/time/#ParseDuration.
138138
//
139139
// Simply put, we simply convert our ticks to the integral number of nanoseconds
140-
// corresponding to it. Since each tick is 100ns, this can trivialy be done just by
140+
// corresponding to it. Since each tick is 100ns, this can trivially be done just by
141141
// appending "00" to it.
142142
return timeSpan.Value.Ticks + "00ns";
143143
}

sdk/Pulumi/Pulumi.xml

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/Pulumi/Resources/Resource.cs

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ public class Resource
7171
/// </summary>
7272
private readonly ImmutableArray<ResourceTransformation> _transformations;
7373

74+
/// <summary>
75+
/// A list of aliases applied to this resource.
76+
/// </summary>
77+
internal readonly ImmutableArray<Input<string>> _aliases;
78+
7479
/// <summary>
7580
/// The type assigned to the resource at construction.
7681
/// </summary>
@@ -257,7 +262,7 @@ private protected Resource(
257262
this._provider = custom ? options.Provider : null;
258263
this._version = options.Version;
259264
this._pluginDownloadURL = options.PluginDownloadURL;
260-
265+
this._aliases = AllAliases(options.Aliases.ToList(), name, type, options.Parent);
261266
Deployment.InternalInstance.ReadOrRegisterResource(this, remote, urn => new DependencyResource(urn), args, options);
262267
}
263268

@@ -276,6 +281,115 @@ private protected Resource(
276281
return result;
277282
}
278283

284+
private static Output<string> CollapseAliasToUrn(
285+
Input<Alias> alias,
286+
string defaultName,
287+
string defaultType,
288+
Resource? defaultParent)
289+
{
290+
return alias.ToOutput().Apply(a =>
291+
{
292+
if (a.Urn != null)
293+
{
294+
CheckNull(a.Name, nameof(a.Name));
295+
CheckNull(a.Type, nameof(a.Type));
296+
CheckNull(a.Project, nameof(a.Project));
297+
CheckNull(a.Stack, nameof(a.Stack));
298+
CheckNull(a.Parent, nameof(a.Parent));
299+
CheckNull(a.ParentUrn, nameof(a.ParentUrn));
300+
if (a.NoParent)
301+
ThrowAliasPropertyConflict(nameof(a.NoParent));
302+
303+
return Output.Create(a.Urn);
304+
}
305+
306+
var name = a.Name ?? defaultName;
307+
var type = a.Type ?? defaultType;
308+
var project = a.Project ?? Deployment.Instance.ProjectName;
309+
var stack = a.Stack ?? Deployment.Instance.StackName;
310+
311+
var parentCount =
312+
(a.Parent != null ? 1 : 0) +
313+
(a.ParentUrn != null ? 1 : 0) +
314+
(a.NoParent ? 1 : 0);
315+
316+
if (parentCount >= 2)
317+
{
318+
throw new ArgumentException(
319+
$"Only specify one of '{nameof(Alias.Parent)}', '{nameof(Alias.ParentUrn)}' or '{nameof(Alias.NoParent)}' in an {nameof(Alias)}");
320+
}
321+
322+
var (parent, parentUrn) = GetParentInfo(defaultParent, a);
323+
324+
if (name == null)
325+
throw new ArgumentNullException("No valid 'Name' passed in for alias.");
326+
327+
if (type == null)
328+
throw new ArgumentNullException("No valid 'type' passed in for alias.");
329+
330+
return Pulumi.Urn.Create(name, type, parent, parentUrn, project, stack);
331+
});
332+
}
333+
334+
335+
private static void CheckNull<T>(T? value, string name) where T : class
336+
{
337+
if (value != null)
338+
{
339+
ThrowAliasPropertyConflict(name);
340+
}
341+
}
342+
343+
private static void ThrowAliasPropertyConflict(string name)
344+
=> throw new ArgumentException($"{nameof(Alias)} should not specify both {nameof(Alias.Urn)} and {name}");
345+
346+
private static (Resource? parent, Input<string>? urn) GetParentInfo(Resource? defaultParent, Alias alias)
347+
{
348+
if (alias.Parent != null)
349+
return (alias.Parent, null);
350+
351+
if (alias.ParentUrn != null)
352+
return (null, alias.ParentUrn);
353+
354+
if (alias.NoParent)
355+
return (null, null);
356+
357+
return (defaultParent, null);
358+
}
359+
360+
/// <summary>
361+
/// <see cref="AllAliases"/> makes a copy of the aliases array, and add to it any
362+
/// implicit aliases inherited from its parent. If there are N child aliases, and
363+
/// M parent aliases, there will be (M+1)*(N+1)-1 total aliases, or, as calculated
364+
/// in the logic below, N+(M*(1+N)).
365+
/// </summary>
366+
internal static ImmutableArray<Input<string>> AllAliases(List<Input<Alias>> childAliases, string childName, string childType, Resource? parent)
367+
{
368+
var aliases = ImmutableArray.CreateBuilder<Input<string>>();
369+
foreach (var childAlias in childAliases)
370+
{
371+
aliases.Add(CollapseAliasToUrn(childAlias, childName, childType, parent));
372+
}
373+
if (parent != null)
374+
{
375+
foreach (var parentAlias in parent._aliases)
376+
{
377+
aliases.Add(Pulumi.Urn.InheritedChildAlias(childName, parent._name, parentAlias, childType));
378+
foreach (var childAlias in childAliases)
379+
{
380+
var inheritedAlias = CollapseAliasToUrn(childAlias, childName, childType, parent).Apply(childAliasURN =>
381+
{
382+
var aliasedChildName = Pulumi.Urn.Name(childAliasURN);
383+
var aliasedChildType = Pulumi.Urn.Type(childAliasURN);
384+
return Pulumi.Urn.InheritedChildAlias(aliasedChildName, parent._name, parentAlias, aliasedChildType);
385+
});
386+
aliases.Add(inheritedAlias);
387+
}
388+
}
389+
}
390+
return aliases.ToImmutable();
391+
}
392+
279393
private static ImmutableDictionary<string, ProviderResource> ConvertToProvidersMap(List<ProviderResource>? providers)
280394
{
281395
var result = ImmutableDictionary.CreateBuilder<string, ProviderResource>();

0 commit comments

Comments
 (0)