[sdk/dotnet] Fix deserializing resources (#5921)
This change implements getResource in the mock monitor. This uncovered some issues deserializing resources, which this change also addresses.
This commit is contained in:
parent
afd5ad6a97
commit
160220bc4a
|
@ -19,6 +19,10 @@ CHANGELOG
|
|||
- [sdk/python] Implement getResource in the mock monitor.
|
||||
[#5919](https://github.com/pulumi/pulumi/pull/5919)
|
||||
|
||||
- [sdk/dotnet] Implement getResource in the mock monitor and fix some issues around
|
||||
deserializing resources.
|
||||
[#5921](https://github.com/pulumi/pulumi/pull/5921)
|
||||
|
||||
## 2.15.4 (2020-12-08)
|
||||
|
||||
- Fix a problem where `pulumi import` could panic on an import error due to missing error message.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
@ -18,10 +19,17 @@ namespace Pulumi.Tests.Mocks
|
|||
|
||||
public Task<(string id, object state)> NewResourceAsync(string type, string name, ImmutableDictionary<string, object> inputs, string? provider, string? id)
|
||||
{
|
||||
Assert.Equal("aws:ec2/instance:Instance", type);
|
||||
return Task.FromResult<(string, object)>(("i-1234567890abcdef0", new Dictionary<string, object> {
|
||||
{ "publicIp", "203.0.113.12" },
|
||||
}));
|
||||
switch (type)
|
||||
{
|
||||
case "aws:ec2/instance:Instance":
|
||||
return Task.FromResult<(string, object)>(("i-1234567890abcdef0", new Dictionary<string, object> {
|
||||
{ "publicIp", "203.0.113.12" },
|
||||
}));
|
||||
case "pkg:index:MyCustom":
|
||||
return Task.FromResult<(string, object)>((name + "_id", inputs));
|
||||
default:
|
||||
throw new Exception($"Unknown resource {type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +46,22 @@ namespace Pulumi.Tests.Mocks
|
|||
var ip = await instance.PublicIp.GetValueAsync();
|
||||
Assert.Equal("203.0.113.12", ip);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task TestCustomWithResourceReference()
|
||||
{
|
||||
var resources = await Testing.RunAsync<MyStack>();
|
||||
|
||||
var mycustom = resources.OfType<MyCustom>().FirstOrDefault();
|
||||
Assert.NotNull(mycustom);
|
||||
|
||||
var instance = await mycustom.Instance.GetValueAsync();
|
||||
Assert.IsType<Instance>(instance);
|
||||
|
||||
var ip = await instance.PublicIp.GetValueAsync();
|
||||
Assert.Equal("203.0.113.12", ip);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestStack()
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Pulumi.Tests.Mocks
|
||||
{
|
||||
[ResourceType("aws:ec2/instance:Instance", null)]
|
||||
public partial class Instance : Pulumi.CustomResource
|
||||
{
|
||||
[Output("publicIp")]
|
||||
|
@ -17,6 +18,23 @@ namespace Pulumi.Tests.Mocks
|
|||
{
|
||||
}
|
||||
|
||||
public partial class MyCustom : Pulumi.CustomResource
|
||||
{
|
||||
[Output("instance")]
|
||||
public Output<Instance> Instance { get; private set; } = null!;
|
||||
|
||||
public MyCustom(string name, MyCustomArgs args, CustomResourceOptions? options = null)
|
||||
: base("pkg:index:MyCustom", name, args ?? new MyCustomArgs(), options)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MyCustomArgs : Pulumi.ResourceArgs
|
||||
{
|
||||
[Input("instance")]
|
||||
public Input<Instance>? Instance { get; set; }
|
||||
}
|
||||
|
||||
public class MyStack : Stack
|
||||
{
|
||||
[Output("publicIp")]
|
||||
|
@ -25,6 +43,10 @@ namespace Pulumi.Tests.Mocks
|
|||
public MyStack()
|
||||
{
|
||||
var myInstance = new Instance("instance", new InstanceArgs());
|
||||
var myCustom = new MyCustom("mycustom", new MyCustomArgs
|
||||
{
|
||||
Instance = myInstance,
|
||||
});
|
||||
this.PublicIp = myInstance.PublicIp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,9 @@ namespace Pulumi.Serialization
|
|||
if (targetType == typeof(JsonElement))
|
||||
return TryConvertJsonElement(context, val);
|
||||
|
||||
if (targetType.IsSubclassOf(typeof(Resource)) || targetType == typeof(Resource))
|
||||
return TryEnsureType<Resource>(context, val);
|
||||
|
||||
if (targetType.IsEnum)
|
||||
{
|
||||
var underlyingType = targetType.GetEnumUnderlyingType();
|
||||
|
@ -357,6 +360,11 @@ namespace Pulumi.Serialization
|
|||
return;
|
||||
}
|
||||
|
||||
if (targetType.IsSubclassOf(typeof(Resource)) || targetType == typeof(Resource))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetType == typeof(ImmutableDictionary<string, object>))
|
||||
{
|
||||
// This type is what is generated for things like azure/aws tags. It's an untyped
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Pulumi
|
|||
_resourceTypes ??= DiscoverResourceTypes();
|
||||
}
|
||||
|
||||
var minimalVersion = version != null ? SemVersion.Parse(version) : new SemVersion(0);
|
||||
var minimalVersion = !string.IsNullOrEmpty(version) ? SemVersion.Parse(version) : new SemVersion(0);
|
||||
var yes = _resourceTypes.TryGetValue(name, out var types);
|
||||
if (!yes)
|
||||
{
|
||||
|
@ -45,7 +45,7 @@ namespace Pulumi
|
|||
|
||||
var matches =
|
||||
from vt in types
|
||||
let resourceVersion = vt.Item1 != null ? SemVersion.Parse(vt.Item1) : minimalVersion
|
||||
let resourceVersion = !string.IsNullOrEmpty(vt.Item1) ? SemVersion.Parse(vt.Item1) : minimalVersion
|
||||
where resourceVersion >= minimalVersion
|
||||
where (version == null || vt.Item1 == null || minimalVersion.Major == resourceVersion.Major)
|
||||
orderby resourceVersion descending
|
||||
|
|
|
@ -14,8 +14,9 @@ namespace Pulumi.Testing
|
|||
internal class MockMonitor : IMonitor
|
||||
{
|
||||
private readonly IMocks _mocks;
|
||||
private readonly Serializer _serializer = new Serializer();
|
||||
|
||||
private readonly Serializer _serializer = new Serializer(excessiveDebugOutput: false);
|
||||
private readonly Dictionary<string, object> _registeredResources = new Dictionary<string, object>();
|
||||
|
||||
public readonly List<Resource> Resources = new List<Resource>();
|
||||
|
||||
public MockMonitor(IMocks mocks)
|
||||
|
@ -31,9 +32,25 @@ namespace Pulumi.Testing
|
|||
|
||||
public async Task<InvokeResponse> InvokeAsync(InvokeRequest request)
|
||||
{
|
||||
var result = await _mocks.CallAsync(request.Tok, ToDictionary(request.Args), request.Provider)
|
||||
var args = ToDictionary(request.Args);
|
||||
var urn = (string)args["urn"];
|
||||
|
||||
if (request.Tok == "pulumi:pulumi:getResource")
|
||||
{
|
||||
object? registeredResource;
|
||||
lock (_registeredResources)
|
||||
{
|
||||
if (!_registeredResources.TryGetValue(urn, out registeredResource))
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown resource {urn}");
|
||||
}
|
||||
}
|
||||
return new InvokeResponse { Return = await SerializeAsync(registeredResource).ConfigureAwait(false) };
|
||||
}
|
||||
|
||||
var result = await _mocks.CallAsync(request.Tok, args, request.Provider)
|
||||
.ConfigureAwait(false);
|
||||
return new InvokeResponse {Return = await SerializeAsync(result).ConfigureAwait(false)};
|
||||
return new InvokeResponse { Return = await SerializeAsync(result).ConfigureAwait(false) };
|
||||
}
|
||||
|
||||
public async Task<ReadResourceResponse> ReadResourceAsync(Resource resource, ReadResourceRequest request)
|
||||
|
@ -41,6 +58,18 @@ namespace Pulumi.Testing
|
|||
var (id, state) = await _mocks.NewResourceAsync(request.Type, request.Name,
|
||||
ToDictionary(request.Properties), request.Provider, request.Id).ConfigureAwait(false);
|
||||
|
||||
var urn = NewUrn(request.Parent, request.Type, request.Name);
|
||||
var serializedState = await SerializeToDictionary(state).ConfigureAwait(false);
|
||||
|
||||
lock (_registeredResources)
|
||||
{
|
||||
var builder = ImmutableDictionary.CreateBuilder<string, object>();
|
||||
builder.Add("urn", urn);
|
||||
builder.Add("id", id);
|
||||
builder.Add("state", serializedState);
|
||||
_registeredResources[urn] = builder.ToImmutable();
|
||||
}
|
||||
|
||||
lock (this.Resources)
|
||||
{
|
||||
this.Resources.Add(resource);
|
||||
|
@ -48,8 +77,8 @@ namespace Pulumi.Testing
|
|||
|
||||
return new ReadResourceResponse
|
||||
{
|
||||
Urn = NewUrn(request.Parent, request.Type, request.Name),
|
||||
Properties = await SerializeAsync(state).ConfigureAwait(false)
|
||||
Urn = urn,
|
||||
Properties = Serializer.CreateStruct(serializedState),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -71,12 +100,24 @@ namespace Pulumi.Testing
|
|||
|
||||
var (id, state) = await _mocks.NewResourceAsync(request.Type, request.Name, ToDictionary(request.Object),
|
||||
request.Provider, request.ImportId).ConfigureAwait(false);
|
||||
|
||||
|
||||
var urn = NewUrn(request.Parent, request.Type, request.Name);
|
||||
var serializedState = await SerializeToDictionary(state).ConfigureAwait(false);
|
||||
|
||||
lock (_registeredResources)
|
||||
{
|
||||
var builder = ImmutableDictionary.CreateBuilder<string, object>();
|
||||
builder.Add("urn", urn);
|
||||
builder.Add("id", id ?? request.ImportId);
|
||||
builder.Add("state", serializedState);
|
||||
_registeredResources[urn] = builder.ToImmutable();
|
||||
}
|
||||
|
||||
return new RegisterResourceResponse
|
||||
{
|
||||
Id = id ?? request.ImportId,
|
||||
Urn = NewUrn(request.Parent, request.Type, request.Name),
|
||||
Object = await SerializeAsync(state).ConfigureAwait(false)
|
||||
Urn = urn,
|
||||
Object = Serializer.CreateStruct(serializedState),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -84,7 +125,7 @@ namespace Pulumi.Testing
|
|||
|
||||
private static string NewUrn(string parent, string type, string name)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(parent))
|
||||
if (!string.IsNullOrEmpty(parent))
|
||||
{
|
||||
var qualifiedType = parent.Split("::")[2];
|
||||
var parentType = qualifiedType.Split("$").First();
|
||||
|
@ -107,11 +148,19 @@ namespace Pulumi.Testing
|
|||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
private async Task<ImmutableDictionary<string, object>> SerializeToDictionary(object o)
|
||||
{
|
||||
if (o is IDictionary<string, object> d)
|
||||
{
|
||||
o = d.ToImmutableDictionary();
|
||||
}
|
||||
return await _serializer.SerializeAsync("", o, true).ConfigureAwait(false) as ImmutableDictionary<string, object>
|
||||
?? throw new InvalidOperationException($"{o.GetType().FullName} is not a supported argument type");
|
||||
}
|
||||
|
||||
private async Task<Struct> SerializeAsync(object o)
|
||||
{
|
||||
var dict = (o as IDictionary<string, object>)?.ToImmutableDictionary()
|
||||
?? await _serializer.SerializeAsync("", o, true).ConfigureAwait(false) as ImmutableDictionary<string, object>
|
||||
?? throw new InvalidOperationException($"{o.GetType().FullName} is not a supported argument type");
|
||||
var dict = await SerializeToDictionary(o).ConfigureAwait(false);
|
||||
return Serializer.CreateStruct(dict);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue